@@ -136,40 +136,55 @@ function buildCombatEmbed(state) {
136136 return embed ;
137137}
138138
139- /**
140- * Build combat action rows.
141- * Single enemy → one row (attack, skill, potion, flee).
142- * Multiple enemies → attack row per target, then skill row per target, then utils row.
143- * Returns an array of ActionRowBuilder.
144- */
145- function buildCombatRow ( state ) {
146- const { player, enemies } = state ;
147- const skills = player . activeSkills ?? [ ] ;
148- const cooldowns = player . skillCooldowns ?? { } ;
149- const usedOnce = player . usedOnceSkills ?? [ ] ;
150- const aliveEnemies = enemies . filter ( ( e ) => e . hp > 0 ) ;
151-
152- // Build consumable select menu
153- const availableConsumables = ( player . consumables ?? [ ] ) . filter ( ( c ) => {
139+ function buildConsumableRow ( player ) {
140+ const available = ( player . consumables ?? [ ] ) . filter ( ( c ) => {
154141 const def = ITEMS [ c . itemId ] ;
155142 if ( ! def ?. effect ) return false ;
156- if ( def . effect . type === 'restore_ap' ) return false ; // not applicable in combat
143+ if ( def . effect . type === 'restore_ap' ) return false ;
157144 return c . quantity > 0 || c . quantity === - 1 ;
158145 } ) ;
146+ if ( ! available . length ) return null ;
147+ return new ActionRowBuilder ( ) . addComponents (
148+ new StringSelectMenuBuilder ( )
149+ . setCustomId ( 'combat_consumable' )
150+ . setPlaceholder ( '🎒 Utiliser un consommable…' )
151+ . addOptions ( available . map ( ( c ) => {
152+ const def = ITEMS [ c . itemId ] ;
153+ const qty = c . quantity === - 1 ? '∞' : `×${ c . quantity } ` ;
154+ return { label : `${ def . name } (${ qty } )` , value : c . itemId } ;
155+ } ) ) ,
156+ ) ;
157+ }
159158
160- const consumableRow = availableConsumables . length > 0
161- ? new ActionRowBuilder ( ) . addComponents (
162- new StringSelectMenuBuilder ( )
163- . setCustomId ( 'combat_consumable' )
164- . setPlaceholder ( '🎒 Utiliser un consommable…' )
165- . addOptions ( availableConsumables . map ( ( c ) => {
166- const def = ITEMS [ c . itemId ] ;
167- const qty = c . quantity === - 1 ? '∞' : `×${ c . quantity } ` ;
168- return { label : `${ def . name } (${ qty } )` , value : c . itemId } ;
169- } ) ) ,
170- )
171- : null ;
159+ /**
160+ * Phase 1 — action select + consumable select + flee button (always ≤ 3 rows).
161+ */
162+ function buildCombatRow ( state ) {
163+ const { player } = state ;
164+ const skills = player . activeSkills ?? [ ] ;
165+ const cooldowns = player . skillCooldowns ?? { } ;
166+ const usedOnce = player . usedOnceSkills ?? [ ] ;
167+
168+ const actionRow = new ActionRowBuilder ( ) . addComponents (
169+ new StringSelectMenuBuilder ( )
170+ . setCustomId ( 'combat_action_select' )
171+ . setPlaceholder ( '⚔️ Choisir une action…' )
172+ . addOptions ( [
173+ { label : 'Attaquer' , value : 'attack' , emoji : '⚔️' } ,
174+ ...skills . map ( ( sk ) => {
175+ const cd = cooldowns [ sk . key ] ?? 0 ;
176+ const blocked = cd > 0 || ( sk . oncePerCombat && usedOnce . includes ( sk . key ) ) ;
177+ return {
178+ label : blocked ? `${ sk . name } (${ cd } t)` : sk . name ,
179+ value : `skill_${ sk . key } ` ,
180+ emoji : '🔥' ,
181+ description : blocked ? 'Non disponible ce tour' : undefined ,
182+ } ;
183+ } ) ,
184+ ] ) ,
185+ ) ;
172186
187+ const consumableRow = buildConsumableRow ( player ) ;
173188 const fleeRow = new ActionRowBuilder ( ) . addComponents (
174189 new ButtonBuilder ( )
175190 . setCustomId ( 'combat_flee' )
@@ -178,70 +193,39 @@ function buildCombatRow(state) {
178193 . setStyle ( ButtonStyle . Secondary ) ,
179194 ) ;
180195
181- const targetIdx = enemies . findIndex ( ( e ) => e . hp > 0 ) ;
182- const targetIdx0 = targetIdx >= 0 ? targetIdx : 0 ;
196+ const rows = [ actionRow ] ;
197+ if ( consumableRow ) rows . push ( consumableRow ) ;
198+ rows . push ( fleeRow ) ;
199+ return rows ;
200+ }
183201
184- if ( aliveEnemies . length <= 1 ) {
185- const attackRow = new ActionRowBuilder ( ) . addComponents (
186- new ButtonBuilder ( )
187- . setCustomId ( `combat_attack:${ targetIdx0 } ` )
188- . setLabel ( 'Attaquer' )
189- . setEmoji ( '⚔️' )
190- . setStyle ( ButtonStyle . Danger ) ,
191- ...skills . map ( ( sk ) => {
192- const cd = cooldowns [ sk . key ] ?? 0 ;
193- const blocked = cd > 0 || ( sk . oncePerCombat && usedOnce . includes ( sk . key ) ) ;
194- const label = cd > 0 ? `${ sk . name } (${ cd } t)` : sk . name ;
195- return new ButtonBuilder ( )
196- . setCustomId ( `combat_skill_${ sk . key } :${ targetIdx0 } ` )
197- . setLabel ( label )
198- . setEmoji ( '🔥' )
199- . setStyle ( ButtonStyle . Primary )
200- . setDisabled ( blocked ) ;
201- } ) ,
202- new ButtonBuilder ( )
203- . setCustomId ( 'combat_flee' )
204- . setLabel ( 'Fuir' )
205- . setEmoji ( '🏃' )
206- . setStyle ( ButtonStyle . Secondary ) ,
207- ) ;
208- const rows = [ attackRow ] ;
209- if ( consumableRow ) rows . push ( consumableRow ) ;
210- return rows ;
211- }
202+ /**
203+ * Phase 2 (multi-ennemis) — boutons de cible + consumable + retour (toujours ≤ 3 rows).
204+ * action : 'attack' | 'skill_<key>'
205+ */
206+ function buildTargetRow ( action , state ) {
207+ const { player, enemies } = state ;
208+ const aliveEnemies = enemies . filter ( ( e ) => e . hp > 0 ) ;
212209
213- // Multiple enemies — attack row, one skill row per skill, consumable select, flee
214- const attackRow = new ActionRowBuilder ( ) . addComponents (
210+ const targetRow = new ActionRowBuilder ( ) . addComponents (
215211 ...aliveEnemies . map ( ( e ) => {
216212 const idx = enemies . indexOf ( e ) ;
217213 return new ButtonBuilder ( )
218- . setCustomId ( `combat_attack:${ idx } ` )
219- . setLabel ( `⚔️ ${ e . name } ` )
220- . setStyle ( ButtonStyle . Danger ) ;
214+ . setCustomId ( `combat_${ action } :${ idx } ` )
215+ . setLabel ( e . name )
216+ . setEmoji ( '🎯' )
217+ . setStyle ( ButtonStyle . Primary ) ;
221218 } ) ,
219+ new ButtonBuilder ( )
220+ . setCustomId ( 'combat_action_back' )
221+ . setLabel ( 'Retour' )
222+ . setEmoji ( '↩️' )
223+ . setStyle ( ButtonStyle . Secondary ) ,
222224 ) ;
223225
224- const rows = [ attackRow ] ;
225-
226- for ( const sk of skills ) {
227- const cd = cooldowns [ sk . key ] ?? 0 ;
228- const blocked = cd > 0 || ( sk . oncePerCombat && usedOnce . includes ( sk . key ) ) ;
229- const label = cd > 0 ? `${ sk . name } (${ cd } t)` : sk . name ;
230- const skillRow = new ActionRowBuilder ( ) . addComponents (
231- ...aliveEnemies . map ( ( e ) => {
232- const idx = enemies . indexOf ( e ) ;
233- return new ButtonBuilder ( )
234- . setCustomId ( `combat_skill_${ sk . key } :${ idx } ` )
235- . setLabel ( `🔥 ${ label } → ${ e . name } ` )
236- . setStyle ( ButtonStyle . Primary )
237- . setDisabled ( blocked ) ;
238- } ) ,
239- ) ;
240- rows . push ( skillRow ) ;
241- }
242-
226+ const consumableRow = buildConsumableRow ( player ) ;
227+ const rows = [ targetRow ] ;
243228 if ( consumableRow ) rows . push ( consumableRow ) ;
244- rows . push ( fleeRow ) ;
245229 return rows ;
246230}
247231
@@ -639,6 +623,7 @@ module.exports = {
639623 hpBarAnsi,
640624 buildCombatEmbed,
641625 buildCombatRow,
626+ buildTargetRow,
642627 buildDungeonNextRow,
643628 buildLootEmbed,
644629 buildProfileEmbed,
0 commit comments