lpc-forge 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/assets/manager.js +85 -6
  2. package/dist/assets/manager.js.map +1 -1
  3. package/dist/audio/index.d.ts +4 -0
  4. package/dist/audio/index.js +4 -0
  5. package/dist/audio/index.js.map +1 -0
  6. package/dist/audio/music-catalog.d.ts +11 -0
  7. package/dist/audio/music-catalog.js +96 -0
  8. package/dist/audio/music-catalog.js.map +1 -0
  9. package/dist/audio/sfx-generator.d.ts +11 -0
  10. package/dist/audio/sfx-generator.js +77 -0
  11. package/dist/audio/sfx-generator.js.map +1 -0
  12. package/dist/audio/sfx-presets.d.ts +2 -0
  13. package/dist/audio/sfx-presets.js +286 -0
  14. package/dist/audio/sfx-presets.js.map +1 -0
  15. package/dist/audio/types.d.ts +39 -0
  16. package/dist/audio/types.js +2 -0
  17. package/dist/audio/types.js.map +1 -0
  18. package/dist/character/composer.d.ts +4 -1
  19. package/dist/character/composer.js +41 -7
  20. package/dist/character/composer.js.map +1 -1
  21. package/dist/cli.js +92 -24
  22. package/dist/export/godot.js +78 -19
  23. package/dist/export/godot.js.map +1 -1
  24. package/dist/license.js +1 -1
  25. package/dist/lighting/index.d.ts +16 -0
  26. package/dist/lighting/index.js +95 -0
  27. package/dist/lighting/index.js.map +1 -0
  28. package/dist/lighting/particles.d.ts +2 -0
  29. package/dist/lighting/particles.js +192 -0
  30. package/dist/lighting/particles.js.map +1 -0
  31. package/dist/lighting/presets.d.ts +2 -0
  32. package/dist/lighting/presets.js +146 -0
  33. package/dist/lighting/presets.js.map +1 -0
  34. package/dist/lighting/types.d.ts +36 -0
  35. package/dist/lighting/types.js +2 -0
  36. package/dist/lighting/types.js.map +1 -0
  37. package/dist/systems/day-night.d.ts +2 -0
  38. package/dist/systems/day-night.js +109 -0
  39. package/dist/systems/day-night.js.map +1 -0
  40. package/dist/systems/dialog.d.ts +2 -0
  41. package/dist/systems/dialog.js +266 -0
  42. package/dist/systems/dialog.js.map +1 -0
  43. package/dist/systems/enemy-ai.d.ts +2 -0
  44. package/dist/systems/enemy-ai.js +204 -0
  45. package/dist/systems/enemy-ai.js.map +1 -0
  46. package/dist/systems/hud-full.d.ts +2 -0
  47. package/dist/systems/hud-full.js +158 -0
  48. package/dist/systems/hud-full.js.map +1 -0
  49. package/dist/systems/index.d.ts +10 -0
  50. package/dist/systems/index.js +59 -0
  51. package/dist/systems/index.js.map +1 -0
  52. package/dist/systems/inventory.d.ts +2 -0
  53. package/dist/systems/inventory.js +324 -0
  54. package/dist/systems/inventory.js.map +1 -0
  55. package/dist/systems/loot.d.ts +2 -0
  56. package/dist/systems/loot.js +109 -0
  57. package/dist/systems/loot.js.map +1 -0
  58. package/dist/systems/menu.d.ts +2 -0
  59. package/dist/systems/menu.js +453 -0
  60. package/dist/systems/menu.js.map +1 -0
  61. package/dist/systems/quest.d.ts +2 -0
  62. package/dist/systems/quest.js +210 -0
  63. package/dist/systems/quest.js.map +1 -0
  64. package/dist/systems/save-load.d.ts +2 -0
  65. package/dist/systems/save-load.js +163 -0
  66. package/dist/systems/save-load.js.map +1 -0
  67. package/dist/systems/scene-transition.d.ts +2 -0
  68. package/dist/systems/scene-transition.js +107 -0
  69. package/dist/systems/scene-transition.js.map +1 -0
  70. package/dist/systems/types.d.ts +20 -0
  71. package/dist/systems/types.js +2 -0
  72. package/dist/systems/types.js.map +1 -0
  73. package/dist/systems/writer.d.ts +11 -0
  74. package/dist/systems/writer.js +31 -0
  75. package/dist/systems/writer.js.map +1 -0
  76. package/dist/ui/generator.d.ts +7 -0
  77. package/dist/ui/generator.js +307 -0
  78. package/dist/ui/generator.js.map +1 -0
  79. package/dist/ui/icons.d.ts +10 -0
  80. package/dist/ui/icons.js +320 -0
  81. package/dist/ui/icons.js.map +1 -0
  82. package/dist/ui/index.d.ts +6 -0
  83. package/dist/ui/index.js +6 -0
  84. package/dist/ui/index.js.map +1 -0
  85. package/dist/ui/portrait.d.ts +12 -0
  86. package/dist/ui/portrait.js +72 -0
  87. package/dist/ui/portrait.js.map +1 -0
  88. package/dist/ui/props.d.ts +9 -0
  89. package/dist/ui/props.js +247 -0
  90. package/dist/ui/props.js.map +1 -0
  91. package/dist/ui/themes.d.ts +2 -0
  92. package/dist/ui/themes.js +69 -0
  93. package/dist/ui/themes.js.map +1 -0
  94. package/dist/ui/types.d.ts +58 -0
  95. package/dist/ui/types.js +2 -0
  96. package/dist/ui/types.js.map +1 -0
  97. package/package.json +5 -1
@@ -0,0 +1,453 @@
1
+ export function generateMenuSystem() {
2
+ const mainMenuGd = `extends Control
3
+ ## Main menu screen. Set as main scene or load via SceneTransition.
4
+
5
+ @onready var new_game_btn: Button = $VBoxContainer/NewGameButton
6
+ @onready var load_game_btn: Button = $VBoxContainer/LoadGameButton
7
+ @onready var settings_btn: Button = $VBoxContainer/SettingsButton
8
+ @onready var quit_btn: Button = $VBoxContainer/QuitButton
9
+ @onready var title_label: Label = $TitleLabel
10
+ @onready var save_slots: VBoxContainer = $SaveSlotsPanel/VBoxContainer
11
+
12
+ var _showing_saves := false
13
+
14
+ func _ready() -> void:
15
+ \tnew_game_btn.pressed.connect(_on_new_game)
16
+ \tload_game_btn.pressed.connect(_on_load_game)
17
+ \tsettings_btn.pressed.connect(_on_settings)
18
+ \tquit_btn.pressed.connect(_on_quit)
19
+ \t$SaveSlotsPanel.visible = false
20
+
21
+ func _on_new_game() -> void:
22
+ \tget_tree().change_scene_to_file("res://main.tscn")
23
+
24
+ func _on_load_game() -> void:
25
+ \t_showing_saves = !_showing_saves
26
+ \t$SaveSlotsPanel.visible = _showing_saves
27
+ \tif _showing_saves:
28
+ \t\t_refresh_save_slots()
29
+
30
+ func _on_settings() -> void:
31
+ \tvar settings_scene := load("res://scenes/settings_menu.tscn")
32
+ \tif settings_scene:
33
+ \t\tvar settings := settings_scene.instantiate()
34
+ \t\tadd_child(settings)
35
+
36
+ func _on_quit() -> void:
37
+ \tget_tree().quit()
38
+
39
+ func _refresh_save_slots() -> void:
40
+ \tfor child in save_slots.get_children():
41
+ \t\tchild.queue_free()
42
+ \tvar save_mgr := get_node_or_null("/root/SaveManager")
43
+ \tif not save_mgr:
44
+ \t\treturn
45
+ \tfor i in range(SaveManager.MAX_SLOTS):
46
+ \t\tvar info := SaveManager.get_save_info(i)
47
+ \t\tvar btn := Button.new()
48
+ \t\tif info.is_empty():
49
+ \t\t\tbtn.text = "Slot %d — Empty" % (i + 1)
50
+ \t\t\tbtn.disabled = true
51
+ \t\telse:
52
+ \t\t\tbtn.text = "Slot %d — Lv.%d — %s" % [i + 1, info.get("level", 1), info.get("timestamp", "")]
53
+ \t\tbtn.pressed.connect(SaveManager.load_game.bind(i))
54
+ \t\tsave_slots.add_child(btn)
55
+ `;
56
+ const mainMenuTscn = `[gd_scene load_steps=2 format=3]
57
+
58
+ [ext_resource type="Script" path="res://scripts/main_menu.gd" id="1"]
59
+
60
+ [node name="MainMenu" type="Control"]
61
+ layout_mode = 3
62
+ anchors_preset = 15
63
+ anchor_right = 1.0
64
+ anchor_bottom = 1.0
65
+ script = ExtResource("1")
66
+
67
+ [node name="ColorRect" type="ColorRect" parent="."]
68
+ layout_mode = 1
69
+ anchors_preset = 15
70
+ anchor_right = 1.0
71
+ anchor_bottom = 1.0
72
+ color = Color(0.1, 0.1, 0.15, 1)
73
+
74
+ [node name="TitleLabel" type="Label" parent="."]
75
+ layout_mode = 1
76
+ anchors_preset = 5
77
+ anchor_left = 0.5
78
+ anchor_right = 0.5
79
+ offset_left = -200.0
80
+ offset_top = 80.0
81
+ offset_right = 200.0
82
+ offset_bottom = 140.0
83
+ text = "My RPG"
84
+ horizontal_alignment = 1
85
+ theme_override_font_sizes/font_size = 48
86
+
87
+ [node name="VBoxContainer" type="VBoxContainer" parent="."]
88
+ layout_mode = 1
89
+ anchors_preset = 8
90
+ anchor_left = 0.5
91
+ anchor_top = 0.5
92
+ anchor_right = 0.5
93
+ anchor_bottom = 0.5
94
+ offset_left = -100.0
95
+ offset_top = -60.0
96
+ offset_right = 100.0
97
+ offset_bottom = 60.0
98
+
99
+ [node name="NewGameButton" type="Button" parent="VBoxContainer"]
100
+ layout_mode = 2
101
+ text = "New Game"
102
+
103
+ [node name="LoadGameButton" type="Button" parent="VBoxContainer"]
104
+ layout_mode = 2
105
+ text = "Load Game"
106
+
107
+ [node name="SettingsButton" type="Button" parent="VBoxContainer"]
108
+ layout_mode = 2
109
+ text = "Settings"
110
+
111
+ [node name="QuitButton" type="Button" parent="VBoxContainer"]
112
+ layout_mode = 2
113
+ text = "Quit"
114
+
115
+ [node name="SaveSlotsPanel" type="PanelContainer" parent="."]
116
+ layout_mode = 1
117
+ anchors_preset = 8
118
+ anchor_left = 0.5
119
+ anchor_top = 0.5
120
+ anchor_right = 0.5
121
+ anchor_bottom = 0.5
122
+ offset_left = -120.0
123
+ offset_top = 80.0
124
+ offset_right = 120.0
125
+ offset_bottom = 200.0
126
+ visible = false
127
+
128
+ [node name="VBoxContainer" type="VBoxContainer" parent="SaveSlotsPanel"]
129
+ layout_mode = 2
130
+ `;
131
+ const pauseMenuGd = `extends Control
132
+ ## Pause menu. Toggle with "pause" input action.
133
+
134
+ @onready var resume_btn: Button = $Panel/VBoxContainer/ResumeButton
135
+ @onready var save_btn: Button = $Panel/VBoxContainer/SaveButton
136
+ @onready var settings_btn: Button = $Panel/VBoxContainer/SettingsButton
137
+ @onready var main_menu_btn: Button = $Panel/VBoxContainer/MainMenuButton
138
+
139
+ func _ready() -> void:
140
+ \tvisible = false
141
+ \tresume_btn.pressed.connect(_on_resume)
142
+ \tsave_btn.pressed.connect(_on_save)
143
+ \tsettings_btn.pressed.connect(_on_settings)
144
+ \tmain_menu_btn.pressed.connect(_on_main_menu)
145
+
146
+ func _input(event: InputEvent) -> void:
147
+ \tif event.is_action_pressed("pause"):
148
+ \t\ttoggle_pause()
149
+
150
+ func toggle_pause() -> void:
151
+ \tvisible = !visible
152
+ \tget_tree().paused = visible
153
+
154
+ func _on_resume() -> void:
155
+ \ttoggle_pause()
156
+
157
+ func _on_save() -> void:
158
+ \tvar slot := SaveManager.current_slot
159
+ \tif slot < 0:
160
+ \t\tslot = 0
161
+ \tSaveManager.save_game(slot)
162
+
163
+ func _on_settings() -> void:
164
+ \tvar settings_scene := load("res://scenes/settings_menu.tscn")
165
+ \tif settings_scene:
166
+ \t\tvar settings := settings_scene.instantiate()
167
+ \t\tadd_child(settings)
168
+
169
+ func _on_main_menu() -> void:
170
+ \tget_tree().paused = false
171
+ \tget_tree().change_scene_to_file("res://scenes/main_menu.tscn")
172
+ `;
173
+ const pauseMenuTscn = `[gd_scene load_steps=2 format=3]
174
+
175
+ [ext_resource type="Script" path="res://scripts/pause_menu.gd" id="1"]
176
+
177
+ [node name="PauseMenu" type="Control"]
178
+ layout_mode = 3
179
+ anchors_preset = 15
180
+ anchor_right = 1.0
181
+ anchor_bottom = 1.0
182
+ process_mode = 3
183
+ visible = false
184
+ script = ExtResource("1")
185
+
186
+ [node name="ColorRect" type="ColorRect" parent="."]
187
+ layout_mode = 1
188
+ anchors_preset = 15
189
+ anchor_right = 1.0
190
+ anchor_bottom = 1.0
191
+ color = Color(0, 0, 0, 0.5)
192
+
193
+ [node name="Panel" type="PanelContainer" parent="."]
194
+ layout_mode = 1
195
+ anchors_preset = 8
196
+ anchor_left = 0.5
197
+ anchor_top = 0.5
198
+ anchor_right = 0.5
199
+ anchor_bottom = 0.5
200
+ offset_left = -100.0
201
+ offset_top = -80.0
202
+ offset_right = 100.0
203
+ offset_bottom = 80.0
204
+
205
+ [node name="VBoxContainer" type="VBoxContainer" parent="Panel"]
206
+ layout_mode = 2
207
+
208
+ [node name="PauseLabel" type="Label" parent="Panel/VBoxContainer"]
209
+ layout_mode = 2
210
+ text = "PAUSED"
211
+ horizontal_alignment = 1
212
+
213
+ [node name="ResumeButton" type="Button" parent="Panel/VBoxContainer"]
214
+ layout_mode = 2
215
+ text = "Resume"
216
+
217
+ [node name="SaveButton" type="Button" parent="Panel/VBoxContainer"]
218
+ layout_mode = 2
219
+ text = "Save Game"
220
+
221
+ [node name="SettingsButton" type="Button" parent="Panel/VBoxContainer"]
222
+ layout_mode = 2
223
+ text = "Settings"
224
+
225
+ [node name="MainMenuButton" type="Button" parent="Panel/VBoxContainer"]
226
+ layout_mode = 2
227
+ text = "Main Menu"
228
+ `;
229
+ const settingsMenuGd = `extends Control
230
+ ## Settings menu with volume controls and fullscreen toggle.
231
+
232
+ @onready var master_slider: HSlider = $Panel/VBoxContainer/MasterVolume/Slider
233
+ @onready var music_slider: HSlider = $Panel/VBoxContainer/MusicVolume/Slider
234
+ @onready var sfx_slider: HSlider = $Panel/VBoxContainer/SFXVolume/Slider
235
+ @onready var fullscreen_check: CheckButton = $Panel/VBoxContainer/FullscreenCheck
236
+ @onready var back_btn: Button = $Panel/VBoxContainer/BackButton
237
+
238
+ func _ready() -> void:
239
+ \tmaster_slider.value = db_to_linear(AudioServer.get_bus_volume_db(0))
240
+ \tvar music_idx := AudioServer.get_bus_index("Music")
241
+ \tif music_idx >= 0:
242
+ \t\tmusic_slider.value = db_to_linear(AudioServer.get_bus_volume_db(music_idx))
243
+ \tvar sfx_idx := AudioServer.get_bus_index("SFX")
244
+ \tif sfx_idx >= 0:
245
+ \t\tsfx_slider.value = db_to_linear(AudioServer.get_bus_volume_db(sfx_idx))
246
+ \tfullscreen_check.button_pressed = DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN
247
+
248
+ \tmaster_slider.value_changed.connect(_on_master_changed)
249
+ \tmusic_slider.value_changed.connect(_on_music_changed)
250
+ \tsfx_slider.value_changed.connect(_on_sfx_changed)
251
+ \tfullscreen_check.toggled.connect(_on_fullscreen_toggled)
252
+ \tback_btn.pressed.connect(_on_back)
253
+
254
+ func _on_master_changed(value: float) -> void:
255
+ \tAudioServer.set_bus_volume_db(0, linear_to_db(value))
256
+
257
+ func _on_music_changed(value: float) -> void:
258
+ \tvar idx := AudioServer.get_bus_index("Music")
259
+ \tif idx >= 0:
260
+ \t\tAudioServer.set_bus_volume_db(idx, linear_to_db(value))
261
+
262
+ func _on_sfx_changed(value: float) -> void:
263
+ \tvar idx := AudioServer.get_bus_index("SFX")
264
+ \tif idx >= 0:
265
+ \t\tAudioServer.set_bus_volume_db(idx, linear_to_db(value))
266
+
267
+ func _on_fullscreen_toggled(pressed: bool) -> void:
268
+ \tif pressed:
269
+ \t\tDisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
270
+ \telse:
271
+ \t\tDisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
272
+
273
+ func _on_back() -> void:
274
+ \tqueue_free()
275
+ `;
276
+ const settingsMenuTscn = `[gd_scene load_steps=2 format=3]
277
+
278
+ [ext_resource type="Script" path="res://scripts/settings_menu.gd" id="1"]
279
+
280
+ [node name="SettingsMenu" type="Control"]
281
+ layout_mode = 3
282
+ anchors_preset = 15
283
+ anchor_right = 1.0
284
+ anchor_bottom = 1.0
285
+ script = ExtResource("1")
286
+
287
+ [node name="ColorRect" type="ColorRect" parent="."]
288
+ layout_mode = 1
289
+ anchors_preset = 15
290
+ anchor_right = 1.0
291
+ anchor_bottom = 1.0
292
+ color = Color(0, 0, 0, 0.7)
293
+
294
+ [node name="Panel" type="PanelContainer" parent="."]
295
+ layout_mode = 1
296
+ anchors_preset = 8
297
+ anchor_left = 0.5
298
+ anchor_top = 0.5
299
+ anchor_right = 0.5
300
+ anchor_bottom = 0.5
301
+ offset_left = -160.0
302
+ offset_top = -120.0
303
+ offset_right = 160.0
304
+ offset_bottom = 120.0
305
+
306
+ [node name="VBoxContainer" type="VBoxContainer" parent="Panel"]
307
+ layout_mode = 2
308
+
309
+ [node name="Title" type="Label" parent="Panel/VBoxContainer"]
310
+ layout_mode = 2
311
+ text = "Settings"
312
+ horizontal_alignment = 1
313
+
314
+ [node name="MasterVolume" type="HBoxContainer" parent="Panel/VBoxContainer"]
315
+ layout_mode = 2
316
+
317
+ [node name="Label" type="Label" parent="Panel/VBoxContainer/MasterVolume"]
318
+ layout_mode = 2
319
+ text = "Master"
320
+ custom_minimum_size = Vector2(80, 0)
321
+
322
+ [node name="Slider" type="HSlider" parent="Panel/VBoxContainer/MasterVolume"]
323
+ layout_mode = 2
324
+ size_flags_horizontal = 3
325
+ min_value = 0.0
326
+ max_value = 1.0
327
+ step = 0.05
328
+ value = 1.0
329
+
330
+ [node name="MusicVolume" type="HBoxContainer" parent="Panel/VBoxContainer"]
331
+ layout_mode = 2
332
+
333
+ [node name="Label" type="Label" parent="Panel/VBoxContainer/MusicVolume"]
334
+ layout_mode = 2
335
+ text = "Music"
336
+ custom_minimum_size = Vector2(80, 0)
337
+
338
+ [node name="Slider" type="HSlider" parent="Panel/VBoxContainer/MusicVolume"]
339
+ layout_mode = 2
340
+ size_flags_horizontal = 3
341
+ min_value = 0.0
342
+ max_value = 1.0
343
+ step = 0.05
344
+ value = 0.8
345
+
346
+ [node name="SFXVolume" type="HBoxContainer" parent="Panel/VBoxContainer"]
347
+ layout_mode = 2
348
+
349
+ [node name="Label" type="Label" parent="Panel/VBoxContainer/SFXVolume"]
350
+ layout_mode = 2
351
+ text = "SFX"
352
+ custom_minimum_size = Vector2(80, 0)
353
+
354
+ [node name="Slider" type="HSlider" parent="Panel/VBoxContainer/SFXVolume"]
355
+ layout_mode = 2
356
+ size_flags_horizontal = 3
357
+ min_value = 0.0
358
+ max_value = 1.0
359
+ step = 0.05
360
+ value = 0.8
361
+
362
+ [node name="FullscreenCheck" type="CheckButton" parent="Panel/VBoxContainer"]
363
+ layout_mode = 2
364
+ text = "Fullscreen"
365
+
366
+ [node name="BackButton" type="Button" parent="Panel/VBoxContainer"]
367
+ layout_mode = 2
368
+ text = "Back"
369
+ `;
370
+ const gameOverGd = `extends Control
371
+ ## Game over screen. Show when player dies.
372
+
373
+ @onready var retry_btn: Button = $Panel/VBoxContainer/RetryButton
374
+ @onready var main_menu_btn: Button = $Panel/VBoxContainer/MainMenuButton
375
+
376
+ func _ready() -> void:
377
+ \tretry_btn.pressed.connect(_on_retry)
378
+ \tmain_menu_btn.pressed.connect(_on_main_menu)
379
+
380
+ func _on_retry() -> void:
381
+ \tget_tree().reload_current_scene()
382
+
383
+ func _on_main_menu() -> void:
384
+ \tget_tree().change_scene_to_file("res://scenes/main_menu.tscn")
385
+ `;
386
+ const gameOverTscn = `[gd_scene load_steps=2 format=3]
387
+
388
+ [ext_resource type="Script" path="res://scripts/game_over.gd" id="1"]
389
+
390
+ [node name="GameOver" type="Control"]
391
+ layout_mode = 3
392
+ anchors_preset = 15
393
+ anchor_right = 1.0
394
+ anchor_bottom = 1.0
395
+ visible = false
396
+ script = ExtResource("1")
397
+
398
+ [node name="ColorRect" type="ColorRect" parent="."]
399
+ layout_mode = 1
400
+ anchors_preset = 15
401
+ anchor_right = 1.0
402
+ anchor_bottom = 1.0
403
+ color = Color(0.1, 0, 0, 0.8)
404
+
405
+ [node name="Panel" type="PanelContainer" parent="."]
406
+ layout_mode = 1
407
+ anchors_preset = 8
408
+ anchor_left = 0.5
409
+ anchor_top = 0.5
410
+ anchor_right = 0.5
411
+ anchor_bottom = 0.5
412
+ offset_left = -100.0
413
+ offset_top = -60.0
414
+ offset_right = 100.0
415
+ offset_bottom = 60.0
416
+
417
+ [node name="VBoxContainer" type="VBoxContainer" parent="Panel"]
418
+ layout_mode = 2
419
+
420
+ [node name="Label" type="Label" parent="Panel/VBoxContainer"]
421
+ layout_mode = 2
422
+ text = "GAME OVER"
423
+ horizontal_alignment = 1
424
+ theme_override_font_sizes/font_size = 32
425
+ theme_override_colors/font_color = Color(0.8, 0.2, 0.2, 1)
426
+
427
+ [node name="RetryButton" type="Button" parent="Panel/VBoxContainer"]
428
+ layout_mode = 2
429
+ text = "Retry"
430
+
431
+ [node name="MainMenuButton" type="Button" parent="Panel/VBoxContainer"]
432
+ layout_mode = 2
433
+ text = "Main Menu"
434
+ `;
435
+ return {
436
+ name: 'menu',
437
+ description: 'Main menu, pause menu, settings (volume + fullscreen), and game over screens',
438
+ files: [
439
+ { path: 'scripts/main_menu.gd', content: mainMenuGd },
440
+ { path: 'scenes/main_menu.tscn', content: mainMenuTscn },
441
+ { path: 'scripts/pause_menu.gd', content: pauseMenuGd },
442
+ { path: 'scenes/pause_menu.tscn', content: pauseMenuTscn },
443
+ { path: 'scripts/settings_menu.gd', content: settingsMenuGd },
444
+ { path: 'scenes/settings_menu.tscn', content: settingsMenuTscn },
445
+ { path: 'scripts/game_over.gd', content: gameOverGd },
446
+ { path: 'scenes/game_over.tscn', content: gameOverTscn },
447
+ ],
448
+ dependencies: ['save_load'],
449
+ autoloads: [],
450
+ inputActions: ['pause'],
451
+ };
452
+ }
453
+ //# sourceMappingURL=menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu.js","sourceRoot":"","sources":["../../src/systems/menu.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,kBAAkB;IAChC,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqDpB,CAAC;IAEA,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0EtB,CAAC;IAEA,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyCrB,CAAC;IAEA,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuDvB,CAAC;IAEA,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8CxB,CAAC;IAEA,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6F1B,CAAC;IAEA,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;CAepB,CAAC;IAEA,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgDtB,CAAC;IAEA,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,WAAW,EAAE,8EAA8E;QAC3F,KAAK,EAAE;YACL,EAAE,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,UAAU,EAAE;YACrD,EAAE,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,YAAY,EAAE;YACxD,EAAE,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,WAAW,EAAE;YACvD,EAAE,IAAI,EAAE,wBAAwB,EAAE,OAAO,EAAE,aAAa,EAAE;YAC1D,EAAE,IAAI,EAAE,0BAA0B,EAAE,OAAO,EAAE,cAAc,EAAE;YAC7D,EAAE,IAAI,EAAE,2BAA2B,EAAE,OAAO,EAAE,gBAAgB,EAAE;YAChE,EAAE,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,UAAU,EAAE;YACrD,EAAE,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,YAAY,EAAE;SACzD;QACD,YAAY,EAAE,CAAC,WAAW,CAAC;QAC3B,SAAS,EAAE,EAAE;QACb,YAAY,EAAE,CAAC,OAAO,CAAC;KACxB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { GameSystem } from './types.js';
2
+ export declare function generateQuest(): GameSystem;
@@ -0,0 +1,210 @@
1
+ export function generateQuest() {
2
+ const questManagerGd = `extends Node
3
+ ## Quest manager autoload. Tracks active quests, objectives, and completion.
4
+
5
+ signal quest_started(quest_id: String)
6
+ signal quest_completed(quest_id: String)
7
+ signal objective_updated(quest_id: String, objective_id: String, current: int, target: int)
8
+
9
+ var active_quests: Dictionary = {} # quest_id → quest data
10
+ var completed_quests: Array = []
11
+ var _quest_database: Dictionary = {}
12
+
13
+ func _ready() -> void:
14
+ \t_load_quest_database()
15
+
16
+ func _load_quest_database() -> void:
17
+ \t_quest_database = {
18
+ \t\t"tutorial_01": {
19
+ \t\t\t"name": "First Steps",
20
+ \t\t\t"description": "Talk to the village elder to learn about the world.",
21
+ \t\t\t"objectives": [
22
+ \t\t\t\t{"id": "talk_elder", "description": "Talk to the Elder", "type": "interact", "target": 1},
23
+ \t\t\t],
24
+ \t\t\t"rewards": {"xp": 50, "items": [{"id": "health_potion", "amount": 2}]},
25
+ \t\t},
26
+ \t\t"kill_slimes": {
27
+ \t\t\t"name": "Slime Trouble",
28
+ \t\t\t"description": "Clear the slimes threatening the village.",
29
+ \t\t\t"objectives": [
30
+ \t\t\t\t{"id": "kill_slime", "description": "Defeat Slimes", "type": "kill", "target": 5},
31
+ \t\t\t\t{"id": "return_elder", "description": "Return to the Elder", "type": "interact", "target": 1},
32
+ \t\t\t],
33
+ \t\t\t"rewards": {"xp": 150, "items": [{"id": "coin", "amount": 25}, {"id": "iron_sword", "amount": 1}]},
34
+ \t\t},
35
+ \t\t"fetch_herbs": {
36
+ \t\t\t"name": "Healing Herbs",
37
+ \t\t\t"description": "Gather herbs for the village healer.",
38
+ \t\t\t"objectives": [
39
+ \t\t\t\t{"id": "gather_herb", "description": "Collect Herbs", "type": "collect", "target": 3},
40
+ \t\t\t],
41
+ \t\t\t"rewards": {"xp": 100, "items": [{"id": "health_potion", "amount": 5}]},
42
+ \t\t},
43
+ \t}
44
+
45
+ func start_quest(quest_id: String) -> bool:
46
+ \tif active_quests.has(quest_id) or quest_id in completed_quests:
47
+ \t\treturn false
48
+ \tif not _quest_database.has(quest_id):
49
+ \t\treturn false
50
+ \tvar quest: Dictionary = _quest_database[quest_id].duplicate(true)
51
+ \tvar objectives := {}
52
+ \tfor obj in quest["objectives"]:
53
+ \t\tobjectives[obj["id"]] = 0
54
+ \tquest["progress"] = objectives
55
+ \tactive_quests[quest_id] = quest
56
+ \tquest_started.emit(quest_id)
57
+ \treturn true
58
+
59
+ func update_objective(quest_id: String, objective_id: String, amount: int = 1) -> void:
60
+ \tif not active_quests.has(quest_id):
61
+ \t\treturn
62
+ \tvar quest: Dictionary = active_quests[quest_id]
63
+ \tif not quest["progress"].has(objective_id):
64
+ \t\treturn
65
+ \tvar target := 0
66
+ \tfor obj in quest["objectives"]:
67
+ \t\tif obj["id"] == objective_id:
68
+ \t\t\ttarget = obj["target"]
69
+ \t\t\tbreak
70
+ \tvar current: int = quest["progress"][objective_id]
71
+ \tquest["progress"][objective_id] = mini(current + amount, target)
72
+ \tobjective_updated.emit(quest_id, objective_id, quest["progress"][objective_id], target)
73
+ \t_check_completion(quest_id)
74
+
75
+ func _check_completion(quest_id: String) -> void:
76
+ \tvar quest: Dictionary = active_quests[quest_id]
77
+ \tfor obj in quest["objectives"]:
78
+ \t\tvar current: int = quest["progress"].get(obj["id"], 0)
79
+ \t\tif current < obj["target"]:
80
+ \t\t\treturn
81
+ \tcompleted_quests.append(quest_id)
82
+ \t_grant_rewards(quest)
83
+ \tactive_quests.erase(quest_id)
84
+ \tquest_completed.emit(quest_id)
85
+
86
+ func _grant_rewards(quest: Dictionary) -> void:
87
+ \tvar rewards: Dictionary = quest.get("rewards", {})
88
+ \tvar inv := get_node_or_null("/root/Inventory")
89
+ \tif inv and rewards.has("items"):
90
+ \t\tfor item in rewards["items"]:
91
+ \t\t\tinv.add_item(item["id"], item.get("amount", 1))
92
+
93
+ func is_quest_active(quest_id: String) -> bool:
94
+ \treturn active_quests.has(quest_id)
95
+
96
+ func is_quest_complete(quest_id: String) -> bool:
97
+ \treturn quest_id in completed_quests
98
+
99
+ func get_quest_info(quest_id: String) -> Dictionary:
100
+ \tif active_quests.has(quest_id):
101
+ \t\treturn active_quests[quest_id]
102
+ \tif _quest_database.has(quest_id):
103
+ \t\treturn _quest_database[quest_id]
104
+ \treturn {}
105
+
106
+ func save_data() -> Dictionary:
107
+ \treturn {
108
+ \t\t"active": active_quests.duplicate(true),
109
+ \t\t"completed": completed_quests.duplicate(),
110
+ \t}
111
+
112
+ func load_data(data: Dictionary) -> void:
113
+ \tactive_quests = data.get("active", {})
114
+ \tcompleted_quests = data.get("completed", [])
115
+ `;
116
+ const questUIGd = `extends Control
117
+ ## Quest tracker UI. Shows active quests and objectives.
118
+
119
+ @onready var quest_list: VBoxContainer = $Panel/MarginContainer/VBoxContainer
120
+
121
+ func _ready() -> void:
122
+ \tQuestManager.quest_started.connect(func(_id: String) -> void: _refresh())
123
+ \tQuestManager.quest_completed.connect(func(_id: String) -> void: _refresh())
124
+ \tQuestManager.objective_updated.connect(func(_a: String, _b: String, _c: int, _d: int) -> void: _refresh())
125
+ \t_refresh()
126
+
127
+ func _refresh() -> void:
128
+ \tfor child in quest_list.get_children():
129
+ \t\tchild.queue_free()
130
+
131
+ \tif QuestManager.active_quests.is_empty():
132
+ \t\tvar empty := Label.new()
133
+ \t\tempty.text = "No active quests"
134
+ \t\tempty.add_theme_color_override("font_color", Color(0.5, 0.5, 0.5))
135
+ \t\tquest_list.add_child(empty)
136
+ \t\treturn
137
+
138
+ \tfor quest_id in QuestManager.active_quests:
139
+ \t\tvar quest: Dictionary = QuestManager.active_quests[quest_id]
140
+ \t\tvar title := Label.new()
141
+ \t\ttitle.text = "◆ " + quest.get("name", quest_id)
142
+ \t\ttitle.add_theme_font_size_override("font_size", 16)
143
+ \t\tquest_list.add_child(title)
144
+
145
+ \t\tfor obj in quest["objectives"]:
146
+ \t\t\tvar current: int = quest["progress"].get(obj["id"], 0)
147
+ \t\t\tvar target: int = obj["target"]
148
+ \t\t\tvar complete := current >= target
149
+ \t\t\tvar obj_label := Label.new()
150
+ \t\t\tvar check := "✓" if complete else "○"
151
+ \t\t\tobj_label.text = " %s %s (%d/%d)" % [check, obj["description"], current, target]
152
+ \t\t\tif complete:
153
+ \t\t\t\tobj_label.add_theme_color_override("font_color", Color(0.4, 0.8, 0.4))
154
+ \t\t\tquest_list.add_child(obj_label)
155
+
156
+ func _input(event: InputEvent) -> void:
157
+ \tif event.is_action_pressed("quest_log"):
158
+ \t\tvisible = !visible
159
+ `;
160
+ const questUITscn = `[gd_scene load_steps=2 format=3]
161
+
162
+ [ext_resource type="Script" path="res://scripts/quest_ui.gd" id="1"]
163
+
164
+ [node name="QuestUI" type="Control"]
165
+ layout_mode = 3
166
+ anchors_preset = 15
167
+ anchor_right = 1.0
168
+ anchor_bottom = 1.0
169
+ script = ExtResource("1")
170
+
171
+ [node name="Panel" type="PanelContainer" parent="."]
172
+ layout_mode = 1
173
+ anchors_preset = 1
174
+ anchor_left = 1.0
175
+ anchor_right = 1.0
176
+ offset_left = -240.0
177
+ offset_top = 16.0
178
+ offset_right = -16.0
179
+ offset_bottom = 300.0
180
+
181
+ [node name="MarginContainer" type="MarginContainer" parent="Panel"]
182
+ layout_mode = 2
183
+ theme_override_constants/margin_left = 8
184
+ theme_override_constants/margin_top = 8
185
+ theme_override_constants/margin_right = 8
186
+ theme_override_constants/margin_bottom = 8
187
+
188
+ [node name="VBoxContainer" type="VBoxContainer" parent="Panel/MarginContainer"]
189
+ layout_mode = 2
190
+
191
+ [node name="Title" type="Label" parent="Panel/MarginContainer/VBoxContainer"]
192
+ layout_mode = 2
193
+ text = "Quest Log"
194
+ horizontal_alignment = 1
195
+ theme_override_font_sizes/font_size = 18
196
+ `;
197
+ return {
198
+ name: 'quest',
199
+ description: 'Quest system with objectives, progress tracking, rewards, and quest log UI',
200
+ files: [
201
+ { path: 'scripts/quest_manager.gd', content: questManagerGd },
202
+ { path: 'scripts/quest_ui.gd', content: questUIGd },
203
+ { path: 'scenes/quest_ui.tscn', content: questUITscn },
204
+ ],
205
+ dependencies: ['inventory'],
206
+ autoloads: [{ name: 'QuestManager', path: 'res://scripts/quest_manager.gd' }],
207
+ inputActions: ['quest_log'],
208
+ };
209
+ }
210
+ //# sourceMappingURL=quest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quest.js","sourceRoot":"","sources":["../../src/systems/quest.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,aAAa;IAC3B,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiHxB,CAAC;IAEA,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2CnB,CAAC;IAEA,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCrB,CAAC;IAEA,OAAO;QACL,IAAI,EAAE,OAAO;QACb,WAAW,EAAE,4EAA4E;QACzF,KAAK,EAAE;YACL,EAAE,IAAI,EAAE,0BAA0B,EAAE,OAAO,EAAE,cAAc,EAAE;YAC7D,EAAE,IAAI,EAAE,qBAAqB,EAAE,OAAO,EAAE,SAAS,EAAE;YACnD,EAAE,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,WAAW,EAAE;SACvD;QACD,YAAY,EAAE,CAAC,WAAW,CAAC;QAC3B,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,gCAAgC,EAAE,CAAC;QAC7E,YAAY,EAAE,CAAC,WAAW,CAAC;KAC5B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { GameSystem } from './types.js';
2
+ export declare function generateSaveLoad(): GameSystem;