1+ from discord import app_commands , Interaction , Member
2+ from discord .ext import commands
3+ from discord .ui import View , Button , Select
4+ from typing import Dict , Optional , List
5+ from datetime import datetime
6+ import discord
7+ from bot .utils .combat_mechanics_test import (
8+ calculate_hit_chance , calculate_damage ,
9+ calculate_special_attack , calculate_combat_stats ,
10+ apply_prayer_bonuses
11+ )
12+ from bot .utils .combat_views_test import DuelRequestView , CombatActionView , EquipmentView
13+ from bot .utils .DButil_v2 import (
14+ get_user_stats , get_user_equipment ,
15+ get_user_inventory , update_user_stats ,
16+ update_duel_history
17+ )
18+
19+ class DuelState :
20+ """State management for an active duel"""
21+ def __init__ (
22+ self ,
23+ challenger : Member ,
24+ opponent : Member ,
25+ challenger_stats : Dict ,
26+ opponent_stats : Dict ,
27+ challenger_equipment : Dict ,
28+ opponent_equipment : Dict ,
29+ start_time : datetime
30+ ):
31+ self .challenger = challenger
32+ self .opponent = opponent
33+ self .challenger_stats = challenger_stats
34+ self .opponent_stats = opponent_stats
35+ self .challenger_equipment = challenger_equipment
36+ self .opponent_equipment = opponent_equipment
37+ self .start_time = start_time
38+
39+ self .current_turn = challenger
40+ self .winner = None
41+ self .special_attack_cooldown = {}
42+
43+ def get_opponent (self , user : Member ) -> Member :
44+ """Get opponent of given user"""
45+ return self .opponent if user .id == self .challenger .id else self .challenger
46+
47+ def get_health (self , user : Member ) -> int :
48+ """Get current health of user"""
49+ stats = self .challenger_stats if user .id == self .challenger .id else self .opponent_stats
50+ return stats ["current_health" ]
51+
52+ def get_equipment (self , user : Member ) -> Dict :
53+ """Get equipment of user"""
54+ return self .challenger_equipment if user .id == self .challenger .id else self .opponent_equipment
55+
56+ def get_stats (self , user : Member ) -> Dict :
57+ """Get stats of user"""
58+ return self .challenger_stats if user .id == self .challenger .id else self .opponent_stats
59+
60+ def create_status_embed (self ) -> discord .Embed :
61+ """Create status embed for current duel state"""
62+ embed = discord .Embed (title = "Duel Status" , color = 0x00ff00 )
63+
64+ # Add challenger info
65+ challenger_health = self .get_health (self .challenger )
66+ challenger_weapon = self .challenger_equipment .get ("weapon" , {}).get ("name" , "Unarmed" )
67+ embed .add_field (
68+ name = f"{ self .challenger .display_name } " ,
69+ value = f"Health: { challenger_health } \n Weapon: { challenger_weapon } " ,
70+ inline = True
71+ )
72+
73+ # Add opponent info
74+ opponent_health = self .get_health (self .opponent )
75+ opponent_weapon = self .opponent_equipment .get ("weapon" , {}).get ("name" , "Unarmed" )
76+ embed .add_field (
77+ name = f"{ self .opponent .display_name } " ,
78+ value = f"Health: { opponent_health } \n Weapon: { opponent_weapon } " ,
79+ inline = True
80+ )
81+
82+ # Add current turn indicator
83+ embed .add_field (
84+ name = "Current Turn" ,
85+ value = f"{ self .current_turn .display_name } 's turn!" ,
86+ inline = False
87+ )
88+
89+ return embed
90+
91+ class DuelCommand (commands .Cog ):
92+ """Duel command implementation"""
93+ def __init__ (self , bot ):
94+ self .bot = bot
95+ self .active_duels : Dict [int , DuelState ] = {}
96+ self .DUEL_TIMEOUT = 300 # 5 minutes
97+
98+ @app_commands .command (name = "duel" )
99+ async def duel (self , interaction : Interaction , opponent : Member ):
100+ """Challenge another user to a duel"""
101+
102+ # Validate users
103+ if opponent .id == interaction .user .id :
104+ await interaction .response .send_message (
105+ "You can't duel yourself!" ,
106+ ephemeral = True
107+ )
108+ return
109+
110+ if opponent .bot :
111+ await interaction .response .send_message (
112+ "You can't duel a bot!" ,
113+ ephemeral = True
114+ )
115+ return
116+
117+ # Check if either user is in an active duel
118+ if interaction .user .id in self .active_duels :
119+ await interaction .response .send_message (
120+ "You're already in a duel!" ,
121+ ephemeral = True
122+ )
123+ return
124+
125+ if opponent .id in self .active_duels :
126+ await interaction .response .send_message (
127+ f"{ opponent .display_name } is already in a duel!" ,
128+ ephemeral = True
129+ )
130+ return
131+
132+ # Create duel request view
133+ view = DuelRequestView (
134+ challenger = interaction .user ,
135+ opponent = opponent ,
136+ timeout = 60
137+ )
138+
139+ # Send duel request
140+ await interaction .response .send_message (
141+ f"{ interaction .user .mention } has challenged { opponent .mention } to a duel!" ,
142+ view = view
143+ )
144+
145+ # Wait for response
146+ await view .wait ()
147+
148+ if view .accepted :
149+ # Initialize duel state
150+ duel_state = await self ._initialize_duel (interaction .user , opponent )
151+ self .active_duels [interaction .user .id ] = duel_state
152+ self .active_duels [opponent .id ] = duel_state
153+
154+ # Start combat
155+ await self ._start_combat (interaction , duel_state )
156+
157+ elif view .declined :
158+ await interaction .followup .send (
159+ f"{ opponent .display_name } declined the duel!"
160+ )
161+
162+ else :
163+ await interaction .followup .send (
164+ "The duel request timed out."
165+ )
166+
167+ async def _initialize_duel (
168+ self ,
169+ challenger : Member ,
170+ opponent : Member
171+ ) -> DuelState :
172+ """Initialize duel state with user stats and equipment"""
173+ challenger_stats = await get_user_stats (challenger .id )
174+ opponent_stats = await get_user_stats (opponent .id )
175+
176+ challenger_equipment = await get_user_equipment (challenger .id )
177+ opponent_equipment = await get_user_equipment (opponent .id )
178+
179+ return DuelState (
180+ challenger = challenger ,
181+ opponent = opponent ,
182+ challenger_stats = challenger_stats ,
183+ opponent_stats = opponent_stats ,
184+ challenger_equipment = challenger_equipment ,
185+ opponent_equipment = opponent_equipment ,
186+ start_time = datetime .now ()
187+ )
188+
189+ async def _start_combat (
190+ self ,
191+ interaction : Interaction ,
192+ duel_state : DuelState
193+ ):
194+ """Start combat phase of duel"""
195+ embed = duel_state .create_status_embed ()
196+ view = CombatActionView (
197+ attacker = duel_state .current_turn ,
198+ defender = duel_state .get_opponent (duel_state .current_turn ),
199+ duel_state = duel_state
200+ )
201+
202+ await interaction .followup .send (
203+ "Battle begins!" ,
204+ embed = embed ,
205+ view = view
206+ )
207+
208+ async def _handle_duel_completion (self , user_id : int ):
209+ """Handle duel completion and cleanup"""
210+ if user_id in self .active_duels :
211+ duel_state = self .active_duels [user_id ]
212+
213+ # Update user stats
214+ await update_user_stats (
215+ duel_state .winner .id ,
216+ {"total_wins" : "+1" }
217+ )
218+ await update_user_stats (
219+ duel_state .get_opponent (duel_state .winner ).id ,
220+ {"total_losses" : "+1" }
221+ )
222+
223+ # Record duel history
224+ await update_duel_history (
225+ winner_id = duel_state .winner .id ,
226+ loser_id = duel_state .get_opponent (duel_state .winner ).id ,
227+ duration = (datetime .now () - duel_state .start_time ).seconds ,
228+ winner_hp = duel_state .get_health (duel_state .winner ),
229+ loser_hp = 0
230+ )
231+
232+ # Cleanup
233+ challenger_id = duel_state .challenger .id
234+ opponent_id = duel_state .opponent .id
235+ self .active_duels .pop (challenger_id , None )
236+ self .active_duels .pop (opponent_id , None )
237+
238+ async def setup (bot : commands .Bot ):
239+ await bot .add_cog (DuelCommand (bot ))
0 commit comments