Skip to content

Commit b4078fb

Browse files
authored
ダンジョン選択ゲートを追加した (#361)
2 parents b022515 + dcbe586 commit b4078fb

File tree

23 files changed

+175
-45
lines changed

23 files changed

+175
-45
lines changed

assets/levels/layouts/town_plaza.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
#..##.......rrr........rrr.......rrr.............#
2626
#rrL........rrr........rrr........rrr.........Lrr#
2727
#rrr........rrr........rrr........rrr.........rrr#
28-
#rrr........rrr........>rr........rrr.........rrr#
28+
#rrr........rrr........rGr........rrr.........rrr#
2929
##..........rrr........rrr........rrr...........##
3030
"""
3131

assets/levels/palettes/town.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"1" = "dirt" # 老兵の下は土
1212
"2" = "dirt" # 怪しい科学者の下は土
1313
"3" = "dirt" # 闇医者の下は土
14-
">" = "dirt" # 次階層ポータルの下は土
14+
"G" = "dirt" # ダンジョンゲートの下は土
1515
"r" = "floor" # 道路
1616

1717
# NPCの定義
@@ -23,4 +23,5 @@
2323

2424
# Propsの定義
2525
[palette.props]
26-
">" = "warp_next" # ダンジョン入口ポータル
26+
">" = "warp_next" # ダンジョン入口ポータル
27+
"G" = "dungeon_gate" # ダンジョン選択門

assets/metadata/entities/raw/raw.toml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2208,6 +2208,48 @@
22082208

22092209
[Props.WarpEscapeTrigger]
22102210

2211+
[[Props]]
2212+
BlockPass = false
2213+
BlockView = false
2214+
Description = "ダンジョンへの門"
2215+
Name = "dungeon_gate"
2216+
2217+
# TODO: スプライトを設定する
2218+
[Props.SpriteRender]
2219+
AnimKeys = [
2220+
"warp_next_0",
2221+
"warp_next_1",
2222+
"warp_next_2",
2223+
"warp_next_3",
2224+
"warp_next_4",
2225+
"warp_next_5",
2226+
"warp_next_6",
2227+
"warp_next_7",
2228+
"warp_next_8",
2229+
"warp_next_9",
2230+
"warp_next_10",
2231+
"warp_next_11",
2232+
"warp_next_12",
2233+
"warp_next_13",
2234+
"warp_next_14",
2235+
"warp_next_15",
2236+
]
2237+
Depth = 1
2238+
SpriteKey = "warp_next_0"
2239+
SpriteSheetName = "field"
2240+
2241+
[Props.LightSource]
2242+
Enabled = true
2243+
Radius = 2
2244+
2245+
[Props.LightSource.Color]
2246+
A = 200
2247+
B = 150
2248+
G = 100
2249+
R = 255
2250+
2251+
[Props.DungeonGateTrigger]
2252+
22112253
# enemy tables ================
22122254

22132255
[[EnemyTables]]

internal/actions/interaction_activate.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ func (ia *InteractionActivateActivity) DoTurn(act *Activity, world w.World) erro
7070
switch content := interactable.Data.(type) {
7171
case gc.PortalInteraction:
7272
err = ia.executePortal(act, world, content)
73+
case gc.DungeonGateInteraction:
74+
err = ia.executeDungeonGate(act, world, content)
7375
case gc.DoorInteraction:
7476
ia.executeDoor(act, world, content)
7577
case gc.TalkInteraction:
@@ -126,6 +128,16 @@ func (ia *InteractionActivateActivity) executePortal(act *Activity, world w.Worl
126128
return nil
127129
}
128130

131+
// executeDungeonGate はダンジョンゲート相互作用を実行する
132+
func (ia *InteractionActivateActivity) executeDungeonGate(act *Activity, world w.World, _ gc.DungeonGateInteraction) error {
133+
// ダンジョン選択メニューを開く
134+
if err := world.Resources.Dungeon.RequestStateChange(resources.OpenDungeonSelectEvent{}); err != nil {
135+
return fmt.Errorf("ダンジョン選択状態変更要求エラー: %w", err)
136+
}
137+
act.Logger.Debug("ダンジョンゲート発動", "actor", act.Actor)
138+
return nil
139+
}
140+
129141
// executeDoor はドア相互作用を実行する
130142
func (ia *InteractionActivateActivity) executeDoor(act *Activity, world w.World, _ gc.DoorInteraction) {
131143
if !ia.InteractableEntity.HasComponent(world.Components.Door) {

internal/components/interactable.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,19 @@ type PortalInteraction struct {
3939
func (p PortalInteraction) Config() InteractionConfig {
4040
return InteractionConfig{
4141
ActivationRange: ActivationRangeSameTile,
42-
ActivationWay: ActivationWayManual, // Enterキーで発動
42+
ActivationWay: ActivationWayManual,
43+
}
44+
}
45+
46+
// DungeonGateInteraction はダンジョン選択門の相互作用
47+
// 発動するとダンジョン選択メニューを開く
48+
type DungeonGateInteraction struct{}
49+
50+
// Config は相互作用設定を返す
51+
func (d DungeonGateInteraction) Config() InteractionConfig {
52+
return InteractionConfig{
53+
ActivationRange: ActivationRangeSameTile,
54+
ActivationWay: ActivationWayManual,
4355
}
4456
}
4557

internal/maingame/game.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,16 @@ func (game *MainGame) Draw(screen *ebiten.Image) {
8989
log.Fatal(err)
9090
}
9191

92-
// 複数stateがあるとき、最初のstate描画後にブラーを適用する
93-
if i == 0 && len(stateList) > 1 {
94-
topState := stateList[len(stateList)-1]
95-
if cfg, ok := topState.(states.Configurable); !ok || cfg.StateConfig().BlurBackground {
96-
applyBlurOverlay(target, state)
92+
// 最初のstate描画後にブラー処理を呼び出す
93+
if i == 0 {
94+
applyBlur := len(stateList) > 1
95+
if applyBlur {
96+
topState := stateList[len(stateList)-1]
97+
if cfg, ok := topState.(states.Configurable); ok && !cfg.StateConfig().BlurBackground {
98+
applyBlur = false
99+
}
97100
}
101+
applyBlurOverlay(target, len(stateList), applyBlur)
98102
}
99103
}
100104

internal/maingame/hooks.go

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,41 @@ import (
44
"image/color"
55

66
"github.com/hajimehoshi/ebiten/v2"
7-
es "github.com/kijimaD/ruins/internal/engine/states"
8-
w "github.com/kijimaD/ruins/internal/world"
97
)
108

11-
// ブラー画像と暗転オーバーレイのキャッシュ
129
var (
1310
cachedBlurImage *ebiten.Image
1411
cachedDarkOverlay *ebiten.Image
15-
cachedBaseState es.State[w.World]
12+
cachedStateCount int
1613
)
1714

1815
// applyBlurOverlay は画面にブラー効果を適用する
19-
// baseStateが変わるとキャッシュを再生成する
20-
func applyBlurOverlay(screen *ebiten.Image, baseState es.State[w.World]) {
21-
// 下層stateが変わったらキャッシュをクリア
22-
if cachedBaseState != baseState {
16+
// stateCountが変わるとキャッシュを再生成する
17+
// applyBlurがfalseの場合はキャッシュ更新のみ行う
18+
func applyBlurOverlay(screen *ebiten.Image, stateCount int, applyBlur bool) {
19+
if stateCount != cachedStateCount {
2320
cachedBlurImage = nil
24-
cachedBaseState = baseState
21+
cachedStateCount = stateCount
22+
}
23+
24+
if !applyBlur {
25+
return
2526
}
2627

2728
bounds := screen.Bounds()
2829

29-
// キャッシュがない場合のみブラー画像を生成する
3030
if cachedBlurImage == nil {
3131
w, h := bounds.Dx(), bounds.Dy()
3232

33-
// 現在の画面をコピーする
3433
src := ebiten.NewImage(w, h)
3534
op := &ebiten.DrawImageOptions{}
3635
op.GeoM.Translate(float64(-bounds.Min.X), float64(-bounds.Min.Y))
3736
src.DrawImage(screen, op)
3837

39-
// ブラー効果を2パス(水平→垂直)で適用する
4038
const blurRadius = 4
4139
kernel := blurRadius*2 + 1
4240

43-
// 水平ブラー
44-
tmp := ebiten.NewImage(bounds.Dx(), bounds.Dy())
41+
tmp := ebiten.NewImage(w, h)
4542
for x := -blurRadius; x <= blurRadius; x++ {
4643
op := &ebiten.DrawImageOptions{}
4744
op.GeoM.Translate(float64(x), 0)
@@ -50,8 +47,7 @@ func applyBlurOverlay(screen *ebiten.Image, baseState es.State[w.World]) {
5047
tmp.DrawImage(src, op)
5148
}
5249

53-
// 垂直ブラー
54-
cachedBlurImage = ebiten.NewImage(bounds.Dx(), bounds.Dy())
50+
cachedBlurImage = ebiten.NewImage(w, h)
5551
for y := -blurRadius; y <= blurRadius; y++ {
5652
op := &ebiten.DrawImageOptions{}
5753
op.GeoM.Translate(0, float64(y))
@@ -61,7 +57,6 @@ func applyBlurOverlay(screen *ebiten.Image, baseState es.State[w.World]) {
6157
}
6258
}
6359

64-
// 黒い半透明オーバーレイをキャッシュから取得または生成する
6560
if cachedDarkOverlay == nil {
6661
cachedDarkOverlay = ebiten.NewImage(bounds.Dx(), bounds.Dy())
6762
cachedDarkOverlay.Fill(color.RGBA{0, 0, 0, 48})

internal/raw/raw.go

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -608,16 +608,17 @@ type TileRaw struct {
608608

609609
// PropRaw は置物のローデータ定義
610610
type PropRaw struct {
611-
Name string
612-
Description string
613-
SpriteRender gc.SpriteRender
614-
AnimKeys []string
615-
BlockPass bool
616-
BlockView bool
617-
LightSource *gc.LightSource
618-
Door *DoorRaw
619-
WarpNextTrigger *WarpNextTriggerRaw // 次階層ワープのトリガー
620-
WarpEscapeTrigger *WarpEscapeTriggerRaw // 脱出ワープのトリガー
611+
Name string
612+
Description string
613+
SpriteRender gc.SpriteRender
614+
AnimKeys []string
615+
BlockPass bool
616+
BlockView bool
617+
LightSource *gc.LightSource
618+
Door *DoorRaw
619+
WarpNextTrigger *WarpNextTriggerRaw // 次階層ワープのトリガー
620+
WarpEscapeTrigger *WarpEscapeTriggerRaw // 脱出ワープのトリガー
621+
DungeonGateTrigger *DungeonGateTriggerRaw // ダンジョン選択ゲートのトリガー
621622
}
622623

623624
// DoorRaw はドアのローデータ
@@ -629,6 +630,9 @@ type WarpNextTriggerRaw struct{}
629630
// WarpEscapeTriggerRaw は脱出ワープトリガーのローデータ
630631
type WarpEscapeTriggerRaw struct{}
631632

633+
// DungeonGateTriggerRaw はダンジョン選択ゲートトリガーのローデータ
634+
type DungeonGateTriggerRaw struct{}
635+
632636
// GetTile は指定された名前のタイルを取得する
633637
// 計画段階でタイルの性質(Walkableなど)を参照する場合に使用する
634638
func (rw *Master) GetTile(name string) (TileRaw, error) {
@@ -747,5 +751,12 @@ func (rw *Master) NewPropSpec(name string) (gc.EntitySpec, error) {
747751
}
748752
}
749753

754+
// ダンジョン選択ゲートトリガー
755+
if propRaw.DungeonGateTrigger != nil {
756+
entitySpec.Interactable = &gc.Interactable{
757+
Data: gc.DungeonGateInteraction{},
758+
}
759+
}
760+
750761
return entitySpec, nil
751762
}

internal/resources/event.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ const (
1818
StateEventTypeGameClear = StateEventType("GAME_CLEAR")
1919
// StateEventTypeShowDialog は会話メッセージの表示を表す
2020
StateEventTypeShowDialog = StateEventType("SHOW_DIALOG")
21+
// StateEventTypeOpenDungeonSelect はダンジョン選択メニューを開くことを表す
22+
StateEventTypeOpenDungeonSelect = StateEventType("OPEN_DUNGEON_SELECT")
2123
)
2224

2325
// StateEvent はフィールド上でのイベント。ステート遷移が発生する
@@ -67,3 +69,11 @@ type ShowDialogEvent struct {
6769
func (e ShowDialogEvent) Type() StateEventType {
6870
return StateEventTypeShowDialog
6971
}
72+
73+
// OpenDungeonSelectEvent はダンジョン選択メニューを開くことを表す
74+
type OpenDungeonSelectEvent struct{}
75+
76+
// Type はイベントタイプを返す
77+
func (e OpenDungeonSelectEvent) Type() StateEventType {
78+
return StateEventTypeOpenDungeonSelect
79+
}

internal/states/action_handlers.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,10 @@ func showTileInteractionMessage(world w.World, playerGrid *gc.GridElement) {
283283
Append("帰還ゲートがある。Enterキーで脱出。").
284284
Log()
285285
}
286+
case gc.DungeonGateInteraction:
287+
gamelog.New(gamelog.FieldLog).
288+
Append("ダンジョンへの門がある。Enterキーで選択。").
289+
Log()
286290
}
287291
}
288292

@@ -348,6 +352,13 @@ func getInteractionActions(world w.World, interactable *gc.Interactable, interac
348352
Activity: &actions.InteractionActivateActivity{InteractableEntity: interactableEntity},
349353
Target: interactableEntity,
350354
})
355+
case gc.DungeonGateInteraction:
356+
// ダンジョン選択アクションを生成
357+
result = append(result, InteractionAction{
358+
Label: "ダンジョンを選ぶ",
359+
Activity: &actions.InteractionActivateActivity{InteractableEntity: interactableEntity},
360+
Target: interactableEntity,
361+
})
351362
}
352363

353364
return result

0 commit comments

Comments
 (0)