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.
- package/dist/assets/manager.js +85 -6
- package/dist/assets/manager.js.map +1 -1
- package/dist/audio/index.d.ts +4 -0
- package/dist/audio/index.js +4 -0
- package/dist/audio/index.js.map +1 -0
- package/dist/audio/music-catalog.d.ts +11 -0
- package/dist/audio/music-catalog.js +96 -0
- package/dist/audio/music-catalog.js.map +1 -0
- package/dist/audio/sfx-generator.d.ts +11 -0
- package/dist/audio/sfx-generator.js +77 -0
- package/dist/audio/sfx-generator.js.map +1 -0
- package/dist/audio/sfx-presets.d.ts +2 -0
- package/dist/audio/sfx-presets.js +286 -0
- package/dist/audio/sfx-presets.js.map +1 -0
- package/dist/audio/types.d.ts +39 -0
- package/dist/audio/types.js +2 -0
- package/dist/audio/types.js.map +1 -0
- package/dist/character/composer.d.ts +4 -1
- package/dist/character/composer.js +41 -7
- package/dist/character/composer.js.map +1 -1
- package/dist/cli.js +92 -24
- package/dist/export/godot.js +78 -19
- package/dist/export/godot.js.map +1 -1
- package/dist/license.js +1 -1
- package/dist/lighting/index.d.ts +16 -0
- package/dist/lighting/index.js +95 -0
- package/dist/lighting/index.js.map +1 -0
- package/dist/lighting/particles.d.ts +2 -0
- package/dist/lighting/particles.js +192 -0
- package/dist/lighting/particles.js.map +1 -0
- package/dist/lighting/presets.d.ts +2 -0
- package/dist/lighting/presets.js +146 -0
- package/dist/lighting/presets.js.map +1 -0
- package/dist/lighting/types.d.ts +36 -0
- package/dist/lighting/types.js +2 -0
- package/dist/lighting/types.js.map +1 -0
- package/dist/systems/day-night.d.ts +2 -0
- package/dist/systems/day-night.js +109 -0
- package/dist/systems/day-night.js.map +1 -0
- package/dist/systems/dialog.d.ts +2 -0
- package/dist/systems/dialog.js +266 -0
- package/dist/systems/dialog.js.map +1 -0
- package/dist/systems/enemy-ai.d.ts +2 -0
- package/dist/systems/enemy-ai.js +204 -0
- package/dist/systems/enemy-ai.js.map +1 -0
- package/dist/systems/hud-full.d.ts +2 -0
- package/dist/systems/hud-full.js +158 -0
- package/dist/systems/hud-full.js.map +1 -0
- package/dist/systems/index.d.ts +10 -0
- package/dist/systems/index.js +59 -0
- package/dist/systems/index.js.map +1 -0
- package/dist/systems/inventory.d.ts +2 -0
- package/dist/systems/inventory.js +324 -0
- package/dist/systems/inventory.js.map +1 -0
- package/dist/systems/loot.d.ts +2 -0
- package/dist/systems/loot.js +109 -0
- package/dist/systems/loot.js.map +1 -0
- package/dist/systems/menu.d.ts +2 -0
- package/dist/systems/menu.js +453 -0
- package/dist/systems/menu.js.map +1 -0
- package/dist/systems/quest.d.ts +2 -0
- package/dist/systems/quest.js +210 -0
- package/dist/systems/quest.js.map +1 -0
- package/dist/systems/save-load.d.ts +2 -0
- package/dist/systems/save-load.js +163 -0
- package/dist/systems/save-load.js.map +1 -0
- package/dist/systems/scene-transition.d.ts +2 -0
- package/dist/systems/scene-transition.js +107 -0
- package/dist/systems/scene-transition.js.map +1 -0
- package/dist/systems/types.d.ts +20 -0
- package/dist/systems/types.js +2 -0
- package/dist/systems/types.js.map +1 -0
- package/dist/systems/writer.d.ts +11 -0
- package/dist/systems/writer.js +31 -0
- package/dist/systems/writer.js.map +1 -0
- package/dist/ui/generator.d.ts +7 -0
- package/dist/ui/generator.js +307 -0
- package/dist/ui/generator.js.map +1 -0
- package/dist/ui/icons.d.ts +10 -0
- package/dist/ui/icons.js +320 -0
- package/dist/ui/icons.js.map +1 -0
- package/dist/ui/index.d.ts +6 -0
- package/dist/ui/index.js +6 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/portrait.d.ts +12 -0
- package/dist/ui/portrait.js +72 -0
- package/dist/ui/portrait.js.map +1 -0
- package/dist/ui/props.d.ts +9 -0
- package/dist/ui/props.js +247 -0
- package/dist/ui/props.js.map +1 -0
- package/dist/ui/themes.d.ts +2 -0
- package/dist/ui/themes.js +69 -0
- package/dist/ui/themes.js.map +1 -0
- package/dist/ui/types.d.ts +58 -0
- package/dist/ui/types.js +2 -0
- package/dist/ui/types.js.map +1 -0
- 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,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"}
|