Skip to content

Commit 5c204e2

Browse files
authored
テーブルUIを追加した (#369)
2 parents 3e97c6e + 5c281ca commit 5c204e2

File tree

13 files changed

+507
-203
lines changed

13 files changed

+507
-203
lines changed

internal/consts/icons.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ const (
2626
IconMenu = "\uf0c9"
2727
IconSearch = "\uf002"
2828

29+
// 単位
30+
IconDegree = "\u2103" // 摂氏記号 ℃
31+
2932
// ゲーム
3033
IconHeart = "\uf004"
3134
IconStar = "\uf005"

internal/states/equip_menu.go

Lines changed: 145 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,19 @@ type EquipMenuState struct {
5050
equipTargetMember ecs.Entity // 装備対象のメンバー
5151
}
5252

53+
// equipSlotItem は装備スロット項目の表示データ
54+
type equipSlotItem struct {
55+
SlotLabel string // スロット名
56+
ItemName string // 装備名
57+
UserData map[string]interface{} // 元のUserData
58+
}
59+
60+
// equipSelectItem は装備選択モードでの項目データ
61+
type equipSelectItem struct {
62+
Entity ecs.Entity
63+
Name string
64+
}
65+
5366
func (st EquipMenuState) String() string {
5467
return "EquipMenu"
5568
}
@@ -197,14 +210,14 @@ func (st *EquipMenuState) initUI(world w.World) *ebitenui.UI {
197210
st.SetTransition(es.Transition[w.World]{Type: es.TransPop})
198211
},
199212
OnTabChange: func(_, _ int, _ tabmenu.TabItem) {
200-
st.menuView.UpdateTabDisplayContainer(st.tabDisplayContainer)
213+
st.updateTabDisplayAsTable(world)
201214
st.reloadAbilityContainer(world)
202215
},
203216
OnItemChange: func(_ int, _, _ int, item tabmenu.Item) error {
204217
if err := st.handleItemChange(world, item); err != nil {
205218
return err
206219
}
207-
st.menuView.UpdateTabDisplayContainer(st.tabDisplayContainer)
220+
st.updateTabDisplayAsTable(world)
208221
return nil
209222
},
210223
}
@@ -285,8 +298,8 @@ func (st *EquipMenuState) createTabs(world w.World) []tabmenu.TabItem {
285298

286299
// createAllSlotItems は武器と防具の全スロットのMenuItemを作成する
287300
func (st *EquipMenuState) createAllSlotItems(world w.World, member ecs.Entity, _ int) []tabmenu.Item {
288-
// 武器スロット5つ + 防具スロット4つ = 9つ
289-
items := make([]tabmenu.Item, 0, 9)
301+
// 武器スロット5つ + 防具スロット7つ = 12つ
302+
items := make([]tabmenu.Item, 0, 12)
290303

291304
// 武器スロットを追加する
292305
weapons := worldhelper.GetWeapons(world, member)
@@ -299,20 +312,25 @@ func (st *EquipMenuState) createAllSlotItems(world w.World, member ecs.Entity, _
299312
gc.SlotWeapon5,
300313
}
301314
for i, weapon := range weapons {
302-
var name string
315+
slotLabel := weaponLabels[i]
316+
itemName := ""
303317
if weapon != nil {
304-
name = fmt.Sprintf("%s: %s", weaponLabels[i], world.Components.Name.Get(*weapon).(*gc.Name).Name)
305-
} else {
306-
name = fmt.Sprintf("%s: -", weaponLabels[i])
318+
itemName = world.Components.Name.Get(*weapon).(*gc.Name).Name
319+
}
320+
321+
userData := map[string]interface{}{
322+
"member": member,
323+
"slotNumber": weaponSlotNumbers[i],
324+
"entity": weapon,
307325
}
308326

309327
items = append(items, tabmenu.Item{
310328
ID: fmt.Sprintf("weapon_slot_%d", i),
311-
Label: name,
312-
UserData: map[string]interface{}{
313-
"member": member,
314-
"slotNumber": weaponSlotNumbers[i],
315-
"entity": weapon,
329+
Label: slotLabel,
330+
UserData: equipSlotItem{
331+
SlotLabel: slotLabel,
332+
ItemName: itemName,
333+
UserData: userData,
316334
},
317335
})
318336
}
@@ -322,20 +340,25 @@ func (st *EquipMenuState) createAllSlotItems(world w.World, member ecs.Entity, _
322340
armorLabels := []string{"防具(頭)", "防具(胴)", "防具(腕)", "防具(手)", "防具(脚)", "防具(足)", "防具(装飾)"}
323341
armorSlotNumbers := []gc.EquipmentSlotNumber{gc.SlotHead, gc.SlotTorso, gc.SlotArms, gc.SlotHands, gc.SlotLegs, gc.SlotFeet, gc.SlotJewelry}
324342
for i, slot := range armorSlots {
325-
var name string
343+
slotLabel := armorLabels[i]
344+
itemName := ""
326345
if slot != nil {
327-
name = fmt.Sprintf("%s: %s", armorLabels[i], world.Components.Name.Get(*slot).(*gc.Name).Name)
328-
} else {
329-
name = fmt.Sprintf("%s: -", armorLabels[i])
346+
itemName = world.Components.Name.Get(*slot).(*gc.Name).Name
347+
}
348+
349+
userData := map[string]interface{}{
350+
"member": member,
351+
"slotNumber": armorSlotNumbers[i],
352+
"entity": slot,
330353
}
331354

332355
items = append(items, tabmenu.Item{
333356
ID: fmt.Sprintf("wear_slot_%d", i),
334-
Label: name,
335-
UserData: map[string]interface{}{
336-
"member": member,
337-
"slotNumber": armorSlotNumbers[i],
338-
"entity": slot,
357+
Label: slotLabel,
358+
UserData: equipSlotItem{
359+
SlotLabel: slotLabel,
360+
ItemName: itemName,
361+
UserData: userData,
339362
},
340363
})
341364
}
@@ -351,12 +374,12 @@ func (st *EquipMenuState) handleItemSelection(world w.World, _ tabmenu.TabItem,
351374
}
352375

353376
// スロット選択モードの場合
354-
userData, ok := item.UserData.(map[string]interface{})
377+
slotItem, ok := item.UserData.(equipSlotItem)
355378
if !ok {
356379
return fmt.Errorf("unexpected item UserData")
357380
}
358381

359-
st.showActionWindow(world, userData)
382+
st.showActionWindow(world, slotItem.UserData)
360383
return nil
361384
}
362385

@@ -371,23 +394,25 @@ func (st *EquipMenuState) handleItemChange(world w.World, item tabmenu.Item) err
371394

372395
if st.isEquipMode {
373396
// 装備選択モードの場合
374-
entity, ok := item.UserData.(ecs.Entity)
397+
selectItem, ok := item.UserData.(equipSelectItem)
375398
if !ok {
376399
return fmt.Errorf("unexpected item UserData")
377400
}
378401

402+
entity := selectItem.Entity
379403
if entity.HasComponent(world.Components.Description) {
380404
desc := world.Components.Description.Get(entity).(*gc.Description)
381405
st.itemDesc.Label = desc.Description
382406
}
383407
views.UpdateSpec(world, st.specContainer, entity)
384408
} else {
385409
// スロット選択モードの場合
386-
userData, ok := item.UserData.(map[string]interface{})
410+
slotItem, ok := item.UserData.(equipSlotItem)
387411
if !ok {
388412
return fmt.Errorf("unexpected item UserData")
389413
}
390414

415+
userData := slotItem.UserData
391416
slotEntity := userData["entity"].(*ecs.Entity)
392417
if slotEntity != nil {
393418
if (*slotEntity).HasComponent(world.Components.Description) {
@@ -409,8 +434,65 @@ func (st *EquipMenuState) handleItemChange(world w.World, item tabmenu.Item) err
409434
}
410435

411436
// createTabDisplayUI はタブ表示UIを作成する
412-
func (st *EquipMenuState) createTabDisplayUI(_ w.World) {
413-
st.menuView.UpdateTabDisplayContainer(st.tabDisplayContainer)
437+
func (st *EquipMenuState) createTabDisplayUI(world w.World) {
438+
st.updateTabDisplayAsTable(world)
439+
}
440+
441+
// updateTabDisplayAsTable はタブ表示コンテナをテーブル形式で更新する
442+
func (st *EquipMenuState) updateTabDisplayAsTable(world w.World) {
443+
st.tabDisplayContainer.RemoveChildren()
444+
res := world.Resources.UIResources
445+
446+
currentTab := st.menuView.GetCurrentTab()
447+
currentItemIndex := st.menuView.GetCurrentItemIndex()
448+
449+
if st.isEquipMode {
450+
// 装備選択モード: カーソル、アイテム名の2列
451+
columnWidths := []int{20, 150}
452+
453+
table := styled.NewTableContainer(columnWidths, res)
454+
455+
for i, item := range currentTab.Items {
456+
isSelected := i == currentItemIndex
457+
458+
selectItem, ok := item.UserData.(equipSelectItem)
459+
name := item.Label
460+
if ok {
461+
name = selectItem.Name
462+
}
463+
464+
styled.NewTableRow(table, columnWidths, []string{"", name}, nil, &isSelected, res)
465+
}
466+
467+
st.tabDisplayContainer.AddChild(table)
468+
} else {
469+
// スロット選択モード: カーソル、スロット名、装備名の3列
470+
columnWidths := []int{20, 80, 120}
471+
472+
table := styled.NewTableContainer(columnWidths, res)
473+
474+
for i, item := range currentTab.Items {
475+
isSelected := i == currentItemIndex
476+
477+
slotItem, ok := item.UserData.(equipSlotItem)
478+
slotLabel := item.Label
479+
itemName := ""
480+
if ok {
481+
slotLabel = slotItem.SlotLabel
482+
itemName = slotItem.ItemName
483+
}
484+
485+
styled.NewTableRow(table, columnWidths, []string{"", slotLabel, itemName}, nil, &isSelected, res)
486+
}
487+
488+
st.tabDisplayContainer.AddChild(table)
489+
}
490+
491+
// アイテムがない場合の表示
492+
if len(currentTab.Items) == 0 {
493+
emptyText := styled.NewDescriptionText("(装備なし)", res)
494+
st.tabDisplayContainer.AddChild(emptyText)
495+
}
414496
}
415497

416498
// updateInitialItemDisplay は初期状態のアイテム表示を更新する
@@ -430,6 +512,7 @@ func (st *EquipMenuState) updateInitialItemDisplay(world w.World) error {
430512
// reloadAbilityContainer はメンバーの能力表示コンテナを更新する
431513
func (st *EquipMenuState) reloadAbilityContainer(world w.World) {
432514
st.abilityContainer.RemoveChildren()
515+
res := world.Resources.UIResources
433516

434517
var player ecs.Entity
435518
var found bool
@@ -447,13 +530,20 @@ func (st *EquipMenuState) reloadAbilityContainer(world w.World) {
447530
// プレイヤーの基本情報を表示
448531
views.AddMemberStatusText(st.abilityContainer, player, world)
449532

533+
// 能力値をテーブル形式で表示
534+
columnWidths := []int{50, 30, 40}
535+
aligns := []styled.TextAlign{styled.AlignLeft, styled.AlignRight, styled.AlignRight}
536+
450537
attrs := world.Components.Attributes.Get(player).(*gc.Attributes)
451-
st.abilityContainer.AddChild(styled.NewBodyText(fmt.Sprintf("%s %2d(%+d)", consts.VitalityLabel, attrs.Vitality.Total, attrs.Vitality.Modifier), consts.TextColor, world.Resources.UIResources))
452-
st.abilityContainer.AddChild(styled.NewBodyText(fmt.Sprintf("%s %2d(%+d)", consts.StrengthLabel, attrs.Strength.Total, attrs.Strength.Modifier), consts.TextColor, world.Resources.UIResources))
453-
st.abilityContainer.AddChild(styled.NewBodyText(fmt.Sprintf("%s %2d(%+d)", consts.SensationLabel, attrs.Sensation.Total, attrs.Sensation.Modifier), consts.TextColor, world.Resources.UIResources))
454-
st.abilityContainer.AddChild(styled.NewBodyText(fmt.Sprintf("%s %2d(%+d)", consts.DexterityLabel, attrs.Dexterity.Total, attrs.Dexterity.Modifier), consts.TextColor, world.Resources.UIResources))
455-
st.abilityContainer.AddChild(styled.NewBodyText(fmt.Sprintf("%s %2d(%+d)", consts.AgilityLabel, attrs.Agility.Total, attrs.Agility.Modifier), consts.TextColor, world.Resources.UIResources))
456-
st.abilityContainer.AddChild(styled.NewBodyText(fmt.Sprintf("%s %2d(%+d)", consts.DefenseLabel, attrs.Defense.Total, attrs.Defense.Modifier), consts.TextColor, world.Resources.UIResources))
538+
539+
table := styled.NewTableContainer(columnWidths, res)
540+
styled.NewTableRow(table, columnWidths, []string{consts.VitalityLabel, fmt.Sprintf("%d", attrs.Vitality.Total), fmt.Sprintf("(%+d)", attrs.Vitality.Modifier)}, aligns, nil, res)
541+
styled.NewTableRow(table, columnWidths, []string{consts.StrengthLabel, fmt.Sprintf("%d", attrs.Strength.Total), fmt.Sprintf("(%+d)", attrs.Strength.Modifier)}, aligns, nil, res)
542+
styled.NewTableRow(table, columnWidths, []string{consts.SensationLabel, fmt.Sprintf("%d", attrs.Sensation.Total), fmt.Sprintf("(%+d)", attrs.Sensation.Modifier)}, aligns, nil, res)
543+
styled.NewTableRow(table, columnWidths, []string{consts.DexterityLabel, fmt.Sprintf("%d", attrs.Dexterity.Total), fmt.Sprintf("(%+d)", attrs.Dexterity.Modifier)}, aligns, nil, res)
544+
styled.NewTableRow(table, columnWidths, []string{consts.AgilityLabel, fmt.Sprintf("%d", attrs.Agility.Total), fmt.Sprintf("(%+d)", attrs.Agility.Modifier)}, aligns, nil, res)
545+
styled.NewTableRow(table, columnWidths, []string{consts.DefenseLabel, fmt.Sprintf("%d", attrs.Defense.Total), fmt.Sprintf("(%+d)", attrs.Defense.Modifier)}, aligns, nil, res)
546+
st.abilityContainer.AddChild(table)
457547
}
458548

459549
// queryEquipableItemsForSlot はスロット番号に応じた装備可能なアイテムを取得する
@@ -521,7 +611,14 @@ func (st *EquipMenuState) showActionWindow(world w.World, userData map[string]in
521611
if hasEquipment && slotEntity != nil {
522612
st.actionItems = append(st.actionItems, "外す")
523613
}
524-
st.actionItems = append(st.actionItems, "装備する")
614+
615+
// 装備可能なアイテムがあるかチェック
616+
slotNumber := userData["slotNumber"].(gc.EquipmentSlotNumber)
617+
equipableItems := st.queryEquipableItemsForSlot(world, slotNumber)
618+
if len(equipableItems) > 0 {
619+
st.actionItems = append(st.actionItems, "装備する")
620+
}
621+
525622
st.actionItems = append(st.actionItems, TextClose)
526623

527624
st.actionFocusIndex = 0
@@ -589,11 +686,12 @@ func (st *EquipMenuState) executeActionItem(world w.World) {
589686
return
590687
}
591688

592-
userData, ok := currentTab.Items[currentItemIndex].UserData.(map[string]interface{})
689+
slotItem, ok := currentTab.Items[currentItemIndex].UserData.(equipSlotItem)
593690
if !ok {
594691
st.closeActionWindow()
595692
return
596693
}
694+
userData := slotItem.UserData
597695

598696
switch selectedAction {
599697
case "装備する":
@@ -632,7 +730,7 @@ func (st *EquipMenuState) startEquipMode(world w.World, userData map[string]inte
632730
}
633731

634732
st.menuView.UpdateTabs(newTabs)
635-
st.menuView.UpdateTabDisplayContainer(st.tabDisplayContainer)
733+
st.updateTabDisplayAsTable(world)
636734
st.closeActionWindow()
637735
}
638736

@@ -643,9 +741,12 @@ func (st *EquipMenuState) createEquipMenuItems(world w.World, entities []ecs.Ent
643741
for i, entity := range entities {
644742
name := world.Components.Name.Get(entity).(*gc.Name).Name
645743
items[i] = tabmenu.Item{
646-
ID: fmt.Sprintf("equip_entity_%d", entity),
647-
Label: name,
648-
UserData: entity,
744+
ID: fmt.Sprintf("equip_entity_%d", entity),
745+
Label: name,
746+
UserData: equipSelectItem{
747+
Entity: entity,
748+
Name: name,
749+
},
649750
}
650751
}
651752

@@ -654,7 +755,7 @@ func (st *EquipMenuState) createEquipMenuItems(world w.World, entities []ecs.Ent
654755

655756
// handleEquipItemSelection は装備選択時の処理
656757
func (st *EquipMenuState) handleEquipItemSelection(world w.World, item tabmenu.Item) error {
657-
entity, ok := item.UserData.(ecs.Entity)
758+
selectItem, ok := item.UserData.(equipSelectItem)
658759
if !ok {
659760
return fmt.Errorf("unexpected item UserData")
660761
}
@@ -665,7 +766,7 @@ func (st *EquipMenuState) handleEquipItemSelection(world w.World, item tabmenu.I
665766
}
666767

667768
// 保存されたメンバーに新しい装備を装着
668-
worldhelper.MoveToEquip(world, entity, st.equipTargetMember, st.equipSlotNumber)
769+
worldhelper.MoveToEquip(world, selectItem.Entity, st.equipTargetMember, st.equipSlotNumber)
669770

670771
// 装備モードを終了して元の表示に戻る
671772
return st.exitEquipMode(world)
@@ -678,7 +779,7 @@ func (st *EquipMenuState) unequipItem(world w.World, userData map[string]interfa
678779
if hasEquipment && slotEntity != nil {
679780
worldhelper.MoveToBackpack(world, *slotEntity, member)
680781
st.reloadTabs(world)
681-
st.menuView.UpdateTabDisplayContainer(st.tabDisplayContainer)
782+
st.updateTabDisplayAsTable(world)
682783
st.reloadAbilityContainer(world)
683784
}
684785
st.closeActionWindow()
@@ -702,7 +803,7 @@ func (st *EquipMenuState) exitEquipMode(world w.World) error {
702803
}
703804
}
704805

705-
st.menuView.UpdateTabDisplayContainer(st.tabDisplayContainer)
806+
st.updateTabDisplayAsTable(world)
706807
st.reloadAbilityContainer(world)
707808
return nil
708809
}
@@ -711,5 +812,4 @@ func (st *EquipMenuState) exitEquipMode(world w.World) error {
711812
func (st *EquipMenuState) reloadTabs(world w.World) {
712813
newTabs := st.createTabs(world)
713814
st.menuView.UpdateTabs(newTabs)
714-
st.menuView.UpdateTabDisplayContainer(st.tabDisplayContainer)
715815
}

0 commit comments

Comments
 (0)