fairy stuff
fairy quest and attack mode
This commit is contained in:
@@ -30,6 +30,7 @@ Requires Python 3.10+ and `pygame`. Game data (rooms, items, sets, enemies, drop
|
|||||||
| Shatterstep | R |
|
| Shatterstep | R |
|
||||||
| Tether | X |
|
| Tether | X |
|
||||||
| Reality Break | C |
|
| Reality Break | C |
|
||||||
|
| Fairy Mode (once unlocked) | Z |
|
||||||
| Toggle Light/Shade | Left Shift |
|
| Toggle Light/Shade | Left Shift |
|
||||||
| Interact / Talk / Save | F |
|
| Interact / Talk / Save | F |
|
||||||
| Use Butterfly Jar | G |
|
| Use Butterfly Jar | G |
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ Proves the core loop is fun before scaling to the full vision.
|
|||||||
| Region-2 boss + ability pacing | M | Re-align pacing (Ranged already exists). |
|
| Region-2 boss + ability pacing | M | Re-align pacing (Ranged already exists). |
|
||||||
|
|
||||||
### Stretch / core (post-slice, in order)
|
### Stretch / core (post-slice, in order)
|
||||||
|
- Map system (current-room minimap -> world graph -> lantern fast travel) — see below
|
||||||
- Fast travel (rune stones)
|
- Fast travel (rune stones)
|
||||||
- Fairy scout + bond teleport + fairy holes
|
- Fairy scout + bond teleport + fairy holes
|
||||||
- Regions 3-5 per pacing notes
|
- Regions 3-5 per pacing notes
|
||||||
@@ -59,6 +60,27 @@ Proves the core loop is fun before scaling to the full vision.
|
|||||||
### Cut
|
### Cut
|
||||||
- Ascended armor (removed).
|
- Ascended armor (removed).
|
||||||
|
|
||||||
|
## Map system (deferred — brainstorm, not started)
|
||||||
|
|
||||||
|
Feels huge; split into two independent features rather than one.
|
||||||
|
|
||||||
|
1. **Current-room minimap** — cheap and self-contained: scale `room_bounds` + platform rects + a player arrow into a corner/overlay panel. No new data, no persistence. Good first slice.
|
||||||
|
2. **World map / region graph** — nodes = rooms, edges = `exits[]` (derivable automatically from room JSON rather than hand-authored).
|
||||||
|
|
||||||
|
Design notes
|
||||||
|
- **Layout**: add `meta.map_x` / `meta.map_y` (+ optional `meta.region`) per room and place nodes in the editor; fall back to auto-layout from exit topology if absent.
|
||||||
|
- **Discovery**: persist `visited_rooms` and `discovered_links` alongside the existing lantern-only save data. Undiscovered = "?" / fog.
|
||||||
|
- **Theme hook**: map clear in Light, fogged/faded in Shade, or fog dispelled by lanterns — reinforces the light/shade identity (keep it cosmetic).
|
||||||
|
- **Fairy as cartographer**: she fills in rooms and comments on new ones; reuses her room-aware voice lines and mirrors the Boss Diary UI pattern.
|
||||||
|
- **Fast travel**: discovered lanterns become warp nodes; overlaps with the deferred "rune stones" / "fairy bond teleport" items.
|
||||||
|
- **Edge cases**: one-way exits, boss-refight rooms, the missing `room_town_gloomreach.json`, exits with empty `to` (ignore), multiple entry spawns = one node.
|
||||||
|
|
||||||
|
Phasing
|
||||||
|
1. Current-room minimap overlay.
|
||||||
|
2. `visited_rooms` + world map from auto-derived exit links (auto-positioned).
|
||||||
|
3. Manual `map_x/map_y` layout in the editor.
|
||||||
|
4. Lantern fast travel + fairy-bond teleport.
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
- Save is the keystone: death penalty, main menu, and any progress feature hang off it.
|
- Save is the keystone: death penalty, main menu, and any progress feature hang off it.
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
{
|
||||||
|
"talk_range": 80,
|
||||||
|
"bubble_duration": 3.5,
|
||||||
|
"idle_interval": 45.0,
|
||||||
|
"idle_lines": ["idle_1", "idle_2", "idle_3"],
|
||||||
|
"dust_lines": ["dust_1", "dust_2", "dust_3"],
|
||||||
|
"attack_lines": ["attack_1", "attack_2", "attack_3"],
|
||||||
|
"auto": {
|
||||||
|
"room_01.json": { "on_enter": "welcome" }
|
||||||
|
},
|
||||||
|
"lines": {
|
||||||
|
"welcome": {
|
||||||
|
"light": "Wake up, sleepyhead! We have a long way to go.",
|
||||||
|
"shade": "Ooh, you found your dark side already?"
|
||||||
|
},
|
||||||
|
"idle_1": {
|
||||||
|
"light": "The butterflies always know the way.",
|
||||||
|
"shade": "Even in the dark, I can still see you."
|
||||||
|
},
|
||||||
|
"idle_2": {
|
||||||
|
"light": "Rest at a lantern and it will remember this place.",
|
||||||
|
"shade": "Careful - the gloom hides sharp things."
|
||||||
|
},
|
||||||
|
"idle_3": {
|
||||||
|
"light": "I will stay close. I always do.",
|
||||||
|
"shade": "You are not alone down here. I am right here."
|
||||||
|
},
|
||||||
|
"spot_greeting": {
|
||||||
|
"light": "Isn't this place pretty?",
|
||||||
|
"shade": "I can barely make you out in here."
|
||||||
|
},
|
||||||
|
"dust_1": {
|
||||||
|
"light": "Fairy dust! I can feel its sparkle.",
|
||||||
|
"shade": "Ooh, shiny. Even the dark likes this."
|
||||||
|
},
|
||||||
|
"dust_2": {
|
||||||
|
"light": "Another pinch of dust - keep going!",
|
||||||
|
"shade": "The dark tastes sweeter with this."
|
||||||
|
},
|
||||||
|
"dust_3": {
|
||||||
|
"light": "This dust hums when I hold it.",
|
||||||
|
"shade": "Keep collecting; I want all of it."
|
||||||
|
},
|
||||||
|
"quest_intro": {
|
||||||
|
"light": "I scattered my fairy dust across these rooms. Bring me 5 and I will fight beside you!",
|
||||||
|
"shade": "Find my 5 fairy dust. I will lend you my sting."
|
||||||
|
},
|
||||||
|
"quest_reward": {
|
||||||
|
"light": "You found them all! Now I can fight - press the Fairy Mode key!",
|
||||||
|
"shade": "All 5! My sting is yours. Press the Fairy Mode key."
|
||||||
|
},
|
||||||
|
"quest_return": {
|
||||||
|
"light": "That's all 5! Return to me at the fairy spot - I have something to show you!",
|
||||||
|
"shade": "All 5 gathered. Come back to my spot - I am ready to change."
|
||||||
|
},
|
||||||
|
"quest_unlock": {
|
||||||
|
"light": "Watch this - hold still!",
|
||||||
|
"shade": "Observe. I am about to change."
|
||||||
|
},
|
||||||
|
"quest_done": {
|
||||||
|
"light": "I am ready whenever you are!",
|
||||||
|
"shade": "Say the word and I strike."
|
||||||
|
},
|
||||||
|
"combat_on": {
|
||||||
|
"light": "To battle! I will pick off the stragglers.",
|
||||||
|
"shade": "Time to hunt. I like this form of yours."
|
||||||
|
},
|
||||||
|
"combat_off": {
|
||||||
|
"light": "Resting my wings now.",
|
||||||
|
"shade": "Fine, back to lurking."
|
||||||
|
},
|
||||||
|
"combat_empty": {
|
||||||
|
"light": "I am out of charge - I need to rest!",
|
||||||
|
"shade": "Empty. Let me recharge."
|
||||||
|
},
|
||||||
|
"attack_1": {
|
||||||
|
"light": "Huah!",
|
||||||
|
"shade": "Huah!"
|
||||||
|
},
|
||||||
|
"attack_2": {
|
||||||
|
"light": "Take this!",
|
||||||
|
"shade": "Take this!"
|
||||||
|
},
|
||||||
|
"attack_3": {
|
||||||
|
"light": "Hyah!",
|
||||||
|
"shade": "Hyah!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,5 +6,16 @@
|
|||||||
"speed": 650.0,
|
"speed": 650.0,
|
||||||
"lifetime": 1.2,
|
"lifetime": 1.2,
|
||||||
"size": [14, 6]
|
"size": [14, 6]
|
||||||
|
},
|
||||||
|
"fairy": {
|
||||||
|
"charge_cost": 5.0,
|
||||||
|
"cooldown": 0.7,
|
||||||
|
"range": 560.0,
|
||||||
|
"voice_every": 3,
|
||||||
|
"damage_mult": 0.5,
|
||||||
|
"speed": 520.0,
|
||||||
|
"lifetime": 1.4,
|
||||||
|
"size": [16, 16],
|
||||||
|
"spread_deg": 6.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,12 @@
|
|||||||
},
|
},
|
||||||
"quest_point": {
|
"quest_point": {
|
||||||
"type": "quest_giver"
|
"type": "quest_giver"
|
||||||
|
},
|
||||||
|
"fairy_spot": {
|
||||||
|
"type": "fairy_spot"
|
||||||
|
},
|
||||||
|
"fairy_dust": {
|
||||||
|
"type": "fairy_dust"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 175 B |
Binary file not shown.
|
After Width: | Height: | Size: 437 B |
@@ -144,6 +144,23 @@
|
|||||||
"h": 260,
|
"h": 260,
|
||||||
"group": "ground",
|
"group": "ground",
|
||||||
"layer": 0
|
"layer": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 20,
|
||||||
|
"y": 760,
|
||||||
|
"w": 140,
|
||||||
|
"h": 20,
|
||||||
|
"group": "moving",
|
||||||
|
"path": [
|
||||||
|
[
|
||||||
|
20,
|
||||||
|
760
|
||||||
|
],
|
||||||
|
[
|
||||||
|
20,
|
||||||
|
400
|
||||||
|
]
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"enemies": [
|
"enemies": [
|
||||||
@@ -171,7 +188,20 @@
|
|||||||
"radius": 180,
|
"radius": 180,
|
||||||
"object_id": "flow_butterfly_jar",
|
"object_id": "flow_butterfly_jar",
|
||||||
"layer": 0,
|
"layer": 0,
|
||||||
"quest_id": ""
|
"quest_id": "",
|
||||||
|
"dialogue_id": "",
|
||||||
|
"auto": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 600,
|
||||||
|
"y": 140,
|
||||||
|
"type": "fairy_dust",
|
||||||
|
"radius": 40,
|
||||||
|
"object_id": "fairy_dust",
|
||||||
|
"layer": 0,
|
||||||
|
"quest_id": "",
|
||||||
|
"dialogue_id": "",
|
||||||
|
"auto": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"exits": [
|
"exits": [
|
||||||
|
|||||||
@@ -277,6 +277,15 @@
|
|||||||
"object_id": "flow_butterfly",
|
"object_id": "flow_butterfly",
|
||||||
"layer": 0,
|
"layer": 0,
|
||||||
"quest_id": ""
|
"quest_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 440,
|
||||||
|
"y": 400,
|
||||||
|
"type": "fairy_dust",
|
||||||
|
"radius": 40,
|
||||||
|
"object_id": "fairy_dust",
|
||||||
|
"layer": 0,
|
||||||
|
"quest_id": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"exits": [
|
"exits": [
|
||||||
|
|||||||
@@ -136,6 +136,15 @@
|
|||||||
"object_id": "lamp",
|
"object_id": "lamp",
|
||||||
"layer": 0,
|
"layer": 0,
|
||||||
"quest_id": ""
|
"quest_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 740,
|
||||||
|
"y": 180,
|
||||||
|
"type": "fairy_dust",
|
||||||
|
"radius": 40,
|
||||||
|
"object_id": "fairy_dust",
|
||||||
|
"layer": 0,
|
||||||
|
"quest_id": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"exits": [
|
"exits": [
|
||||||
|
|||||||
@@ -79,6 +79,15 @@
|
|||||||
"object_id": "lamp",
|
"object_id": "lamp",
|
||||||
"layer": 0,
|
"layer": 0,
|
||||||
"quest_id": ""
|
"quest_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1100,
|
||||||
|
"y": 520,
|
||||||
|
"type": "fairy_dust",
|
||||||
|
"radius": 40,
|
||||||
|
"object_id": "fairy_dust",
|
||||||
|
"layer": 0,
|
||||||
|
"quest_id": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"exits": [
|
"exits": [
|
||||||
|
|||||||
@@ -14,6 +14,14 @@
|
|||||||
"room9_top_left": [
|
"room9_top_left": [
|
||||||
220,
|
220,
|
||||||
300
|
300
|
||||||
|
],
|
||||||
|
"room9_top_right": [
|
||||||
|
2660,
|
||||||
|
240
|
||||||
|
],
|
||||||
|
"room9_bottom_right": [
|
||||||
|
2660,
|
||||||
|
600
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"platforms": [
|
"platforms": [
|
||||||
@@ -38,42 +46,48 @@
|
|||||||
"y": 440,
|
"y": 440,
|
||||||
"w": 140,
|
"w": 140,
|
||||||
"h": 20,
|
"h": 20,
|
||||||
"group": "ground"
|
"group": "ground",
|
||||||
|
"layer": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x": 620,
|
"x": 620,
|
||||||
"y": 500,
|
"y": 500,
|
||||||
"w": 120,
|
"w": 120,
|
||||||
"h": 20,
|
"h": 20,
|
||||||
"group": "ground"
|
"group": "ground",
|
||||||
|
"layer": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x": 800,
|
"x": 800,
|
||||||
"y": 560,
|
"y": 560,
|
||||||
"w": 80,
|
"w": 80,
|
||||||
"h": 20,
|
"h": 20,
|
||||||
"group": "ground"
|
"group": "ground",
|
||||||
|
"layer": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x": 80,
|
"x": 80,
|
||||||
"y": 660,
|
"y": 660,
|
||||||
"w": 2699,
|
"w": 2699,
|
||||||
"h": 20,
|
"h": 20,
|
||||||
"group": "ground"
|
"group": "ground",
|
||||||
|
"layer": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x": 1580,
|
"x": 1580,
|
||||||
"y": 540,
|
"y": 540,
|
||||||
"w": 240,
|
"w": 240,
|
||||||
"h": 20,
|
"h": 20,
|
||||||
"group": "ground"
|
"group": "ground",
|
||||||
|
"layer": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x": 1980,
|
"x": 1980,
|
||||||
"y": 540,
|
"y": 540,
|
||||||
"w": 100,
|
"w": 100,
|
||||||
"h": 20,
|
"h": 20,
|
||||||
"group": "ground"
|
"group": "ground",
|
||||||
|
"layer": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x": 2260,
|
"x": 2260,
|
||||||
@@ -81,6 +95,7 @@
|
|||||||
"w": 140,
|
"w": 140,
|
||||||
"h": 20,
|
"h": 20,
|
||||||
"group": "moving",
|
"group": "moving",
|
||||||
|
"layer": 0,
|
||||||
"path": [
|
"path": [
|
||||||
[
|
[
|
||||||
2260,
|
2260,
|
||||||
@@ -97,7 +112,24 @@
|
|||||||
"y": 300,
|
"y": 300,
|
||||||
"w": 320,
|
"w": 320,
|
||||||
"h": 20,
|
"h": 20,
|
||||||
"group": "ground"
|
"group": "ground",
|
||||||
|
"layer": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2780,
|
||||||
|
"y": 120,
|
||||||
|
"w": 20,
|
||||||
|
"h": 200,
|
||||||
|
"group": "ground",
|
||||||
|
"layer": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2780,
|
||||||
|
"y": 480,
|
||||||
|
"w": 20,
|
||||||
|
"h": 200,
|
||||||
|
"group": "ground",
|
||||||
|
"layer": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"enemies": [],
|
"enemies": [],
|
||||||
@@ -111,30 +143,32 @@
|
|||||||
{
|
{
|
||||||
"x": 2012,
|
"x": 2012,
|
||||||
"y": 486,
|
"y": 486,
|
||||||
"type": "combat_master"
|
"type": "combat_master",
|
||||||
|
"layer": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"props": [
|
"props": [
|
||||||
{
|
|
||||||
"x": 800,
|
|
||||||
"y": 460,
|
|
||||||
"type": "player",
|
|
||||||
"radius": 180,
|
|
||||||
"object_id": "player"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"x": 1240,
|
"x": 1240,
|
||||||
"y": 660,
|
"y": 660,
|
||||||
"type": "house",
|
"type": "house",
|
||||||
"radius": 180,
|
"radius": 180,
|
||||||
"object_id": "house"
|
"object_id": "house",
|
||||||
|
"layer": 0,
|
||||||
|
"quest_id": "",
|
||||||
|
"dialogue_id": "",
|
||||||
|
"auto": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x": 1660,
|
"x": 1660,
|
||||||
"y": 540,
|
"y": 540,
|
||||||
"type": "house",
|
"type": "house",
|
||||||
"radius": 180,
|
"radius": 180,
|
||||||
"object_id": "house"
|
"object_id": "house",
|
||||||
|
"layer": 0,
|
||||||
|
"quest_id": "",
|
||||||
|
"dialogue_id": "",
|
||||||
|
"auto": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x": 1754,
|
"x": 1754,
|
||||||
@@ -142,7 +176,26 @@
|
|||||||
"type": "diary",
|
"type": "diary",
|
||||||
"radius": 180,
|
"radius": 180,
|
||||||
"object_id": "diary",
|
"object_id": "diary",
|
||||||
"layer": 1
|
"layer": 1,
|
||||||
|
"quest_id": "",
|
||||||
|
"dialogue_id": "",
|
||||||
|
"auto": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 1460,
|
||||||
|
"y": 660,
|
||||||
|
"type": "save_point",
|
||||||
|
"radius": 90,
|
||||||
|
"object_id": "lamp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 840,
|
||||||
|
"y": 560,
|
||||||
|
"type": "fairy_dust",
|
||||||
|
"radius": 40,
|
||||||
|
"object_id": "fairy_dust",
|
||||||
|
"layer": 0,
|
||||||
|
"quest_id": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"exits": [
|
"exits": [
|
||||||
@@ -160,6 +213,36 @@
|
|||||||
"hint": "",
|
"hint": "",
|
||||||
"layer": 0,
|
"layer": 0,
|
||||||
"requires_boss_dead": false
|
"requires_boss_dead": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2700,
|
||||||
|
"y": 560,
|
||||||
|
"w": 80,
|
||||||
|
"h": 100,
|
||||||
|
"to": "",
|
||||||
|
"spawn_id": "",
|
||||||
|
"spawn": [
|
||||||
|
2700,
|
||||||
|
560
|
||||||
|
],
|
||||||
|
"hint": "",
|
||||||
|
"layer": 0,
|
||||||
|
"requires_boss_dead": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 2700,
|
||||||
|
"y": 200,
|
||||||
|
"w": 80,
|
||||||
|
"h": 100,
|
||||||
|
"to": "room_10",
|
||||||
|
"spawn_id": "room10_top_left",
|
||||||
|
"spawn": [
|
||||||
|
140,
|
||||||
|
280
|
||||||
|
],
|
||||||
|
"hint": "",
|
||||||
|
"layer": 0,
|
||||||
|
"requires_boss_dead": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ability_doors": [],
|
"ability_doors": [],
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"name": "room_10",
|
||||||
|
"display_name": "room_10",
|
||||||
|
"meta": {
|
||||||
|
"id": "room_10",
|
||||||
|
"name": "room_10"
|
||||||
|
},
|
||||||
|
"bounds": {},
|
||||||
|
"player_spawn": [
|
||||||
|
160,
|
||||||
|
260
|
||||||
|
],
|
||||||
|
"entry_spawns": {
|
||||||
|
"room10_top_left": [
|
||||||
|
140,
|
||||||
|
280
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"platforms": [
|
||||||
|
{
|
||||||
|
"x": 20,
|
||||||
|
"y": 340,
|
||||||
|
"w": 440,
|
||||||
|
"h": 20,
|
||||||
|
"group": "ground",
|
||||||
|
"layer": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 20,
|
||||||
|
"y": 120,
|
||||||
|
"w": 20,
|
||||||
|
"h": 220,
|
||||||
|
"group": "ground",
|
||||||
|
"layer": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"enemies": [],
|
||||||
|
"npcs": [],
|
||||||
|
"props": [
|
||||||
|
{
|
||||||
|
"x": 340,
|
||||||
|
"y": 300,
|
||||||
|
"type": "fairy_spot",
|
||||||
|
"radius": 180,
|
||||||
|
"object_id": "fairy_spot",
|
||||||
|
"dialogue_id": "spot_greeting",
|
||||||
|
"quest_id": "quest_fairy_dust"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"exits": [
|
||||||
|
{
|
||||||
|
"x": 40,
|
||||||
|
"y": 240,
|
||||||
|
"w": 80,
|
||||||
|
"h": 100,
|
||||||
|
"to": "room_09",
|
||||||
|
"spawn_id": "room9_top_right",
|
||||||
|
"spawn": [
|
||||||
|
2660,
|
||||||
|
240
|
||||||
|
],
|
||||||
|
"hint": "",
|
||||||
|
"layer": 0,
|
||||||
|
"requires_boss_dead": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ability_doors": [],
|
||||||
|
"room_w": 1600,
|
||||||
|
"room_h": 900,
|
||||||
|
"mist": false
|
||||||
|
}
|
||||||
@@ -47,17 +47,18 @@
|
|||||||
"props": [],
|
"props": [],
|
||||||
"exits": [
|
"exits": [
|
||||||
{
|
{
|
||||||
"x": 1460,
|
"x": 1460,
|
||||||
"y": 360,
|
"y": 360,
|
||||||
"w": 80,
|
"w": 80,
|
||||||
"h": 100,
|
"h": 100,
|
||||||
"to": "",
|
"to": "room_09.json",
|
||||||
"spawn_id": "",
|
"spawn_id": "",
|
||||||
"spawn": [
|
"spawn": [
|
||||||
1460,
|
1754,
|
||||||
360
|
540
|
||||||
]
|
],
|
||||||
}
|
"requires_boss_dead": true
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"ability_doors": [],
|
"ability_doors": [],
|
||||||
"room_w": 1600,
|
"room_w": 1600,
|
||||||
|
|||||||
@@ -271,6 +271,8 @@
|
|||||||
diary: { type: "diary" },
|
diary: { type: "diary" },
|
||||||
player: { type: "player" },
|
player: { type: "player" },
|
||||||
quest_point: { type: "quest_giver" },
|
quest_point: { type: "quest_giver" },
|
||||||
|
fairy_spot: { type: "fairy_spot" },
|
||||||
|
fairy_dust: { type: "fairy_dust" },
|
||||||
};
|
};
|
||||||
// Type fallbacks used when no object def provides a radius.
|
// Type fallbacks used when no object def provides a radius.
|
||||||
const TYPE_DEFAULT_RADII = { light_source: 180, save_point: 90 };
|
const TYPE_DEFAULT_RADII = { light_source: 180, save_point: 90 };
|
||||||
@@ -496,7 +498,7 @@
|
|||||||
const type = p.type || p.kind || "light_source";
|
const type = p.type || p.kind || "light_source";
|
||||||
const object_id = p.object_id || p.objectId || null;
|
const object_id = p.object_id || p.objectId || null;
|
||||||
const radius = +p.radius || getDefaultRadiusForType(type, object_id);
|
const radius = +p.radius || getDefaultRadiusForType(type, object_id);
|
||||||
r.props.push({ x, y, type, radius, object_id, layer: +(p.layer||0), quest_id: p.quest_id || p.questId || "", coins: Array.isArray(p.coins) ? deepClone(p.coins) : undefined });
|
r.props.push({ x, y, type, radius, object_id, layer: +(p.layer||0), quest_id: p.quest_id || p.questId || "", dialogue_id: p.dialogue_id || p.dialogueId || "", auto: !!p.auto, coins: Array.isArray(p.coins) ? deepClone(p.coins) : undefined });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2377,8 +2379,9 @@ Cancel = Choose whether to remove without saving`);
|
|||||||
if(!p) return "<div class='muted'>Selection missing.</div>";
|
if(!p) return "<div class='muted'>Selection missing.</div>";
|
||||||
const prDefault = getDefaultRadiusForType(p.type || "light_source", p.object_id || p.objectId || null);
|
const prDefault = getDefaultRadiusForType(p.type || "light_source", p.object_id || p.objectId || null);
|
||||||
const objId = p.object_id || p.objectId || "";
|
const objId = p.object_id || p.objectId || "";
|
||||||
const hint = ({light_source:"Butterfly (charge regen 2x)", save_point:"Lantern (save point - press F to save)", butterfly_jar:"Butterfly-in-jar (collectible)", diary:"Boss diary (interact)", house:"Standalone house", player:"Player size reference (no in-game effect)", quest_giver:"Quest point (name it below)"})[p.type||"light_source"] || "";
|
const hint = ({light_source:"Butterfly (charge regen 2x)", save_point:"Lantern (save point - press F to save)", butterfly_jar:"Butterfly-in-jar (collectible)", diary:"Boss diary (interact)", house:"Standalone house", player:"Player size reference (no in-game effect)", quest_giver:"Quest point (name it below)", fairy_spot:"Fairy spot (press F to hear the fairy; set quest id for a quest)", fairy_dust:"Fairy dust (collect with F)"})[p.type||"light_source"] || "";
|
||||||
const isQuest = (p.type||"") === "quest_giver";
|
const isQuest = (p.type||"") === "quest_giver";
|
||||||
|
const isFairySpot = (p.type||"") === "fairy_spot";
|
||||||
return `
|
return `
|
||||||
<div><b>Prop</b> #${sel.index}${hint ? ` <span class="mini">· ${hint}</span>` : ""}</div>
|
<div><b>Prop</b> #${sel.index}${hint ? ` <span class="mini">· ${hint}</span>` : ""}</div>
|
||||||
<div class="hr"></div>
|
<div class="hr"></div>
|
||||||
@@ -2390,10 +2393,19 @@ Cancel = Choose whether to remove without saving`);
|
|||||||
<div class="mini">type</div>
|
<div class="mini">type</div>
|
||||||
<input id="prtype" type="text" value="${p.type||"light_source"}"/>
|
<input id="prtype" type="text" value="${p.type||"light_source"}"/>
|
||||||
</div>
|
</div>
|
||||||
${isQuest ? `
|
${(isQuest || isFairySpot) ? `
|
||||||
<div style="margin-top:8px;">
|
<div style="margin-top:8px;">
|
||||||
<div class="mini">quest id (name)</div>
|
<div class="mini">quest id (name)</div>
|
||||||
<input id="prqid" type="text" value="${p.quest_id||""}" placeholder="e.g. quest_save_the_well"/>
|
<input id="prqid" type="text" value="${p.quest_id||""}" placeholder="e.g. quest_fairy_dust"/>
|
||||||
|
</div>` : ""}
|
||||||
|
${isFairySpot ? `
|
||||||
|
<div style="margin-top:8px;">
|
||||||
|
<div class="mini">dialogue id (from data/config/fairy.json)</div>
|
||||||
|
<input id="prdialogue" type="text" value="${p.dialogue_id||""}" placeholder="e.g. spot_greeting"/>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top:8px;">
|
||||||
|
<div class="mini">auto trigger</div>
|
||||||
|
<input id="prauto" type="checkbox" ${p.auto ? "checked" : ""}/>
|
||||||
</div>` : ""}
|
</div>` : ""}
|
||||||
<div style="margin-top:8px;">
|
<div style="margin-top:8px;">
|
||||||
<div class="mini">object id</div>
|
<div class="mini">object id</div>
|
||||||
@@ -2648,6 +2660,14 @@ Cancel = Choose whether to remove without saving`);
|
|||||||
const v = prqid.value.trim();
|
const v = prqid.value.trim();
|
||||||
if(v){ p.quest_id = v; } else { delete p.quest_id; }
|
if(v){ p.quest_id = v; } else { delete p.quest_id; }
|
||||||
}));
|
}));
|
||||||
|
const prdialogue=q("prdialogue"), prauto=q("prauto");
|
||||||
|
if(prdialogue) prdialogue.addEventListener("change", ()=>pushAnd(()=>{
|
||||||
|
const v = prdialogue.value.trim();
|
||||||
|
if(v){ p.dialogue_id = v; } else { delete p.dialogue_id; }
|
||||||
|
}));
|
||||||
|
if(prauto) prauto.addEventListener("change", ()=>pushAnd(()=>{
|
||||||
|
if(prauto.checked){ p.auto = true; } else { delete p.auto; }
|
||||||
|
}));
|
||||||
if(probjid) probjid.addEventListener("change", ()=>pushAnd(()=>{
|
if(probjid) probjid.addEventListener("change", ()=>pushAnd(()=>{
|
||||||
const v = probjid.value.trim();
|
const v = probjid.value.trim();
|
||||||
if(v){ p.object_id = v; } else { delete p.object_id; }
|
if(v){ p.object_id = v; } else { delete p.object_id; }
|
||||||
@@ -3492,6 +3512,8 @@ Cancel = Choose whether to remove without saving`);
|
|||||||
if(p.object_id) out.object_id = p.object_id;
|
if(p.object_id) out.object_id = p.object_id;
|
||||||
if(p.layer) out.layer = p.layer;
|
if(p.layer) out.layer = p.layer;
|
||||||
if(p.quest_id) out.quest_id = p.quest_id;
|
if(p.quest_id) out.quest_id = p.quest_id;
|
||||||
|
if(p.dialogue_id) out.dialogue_id = p.dialogue_id;
|
||||||
|
if(p.auto) out.auto = true;
|
||||||
if(Array.isArray(p.coins)) out.coins = p.coins;
|
if(Array.isArray(p.coins)) out.coins = p.coins;
|
||||||
return out;
|
return out;
|
||||||
});
|
});
|
||||||
|
|||||||
+1038
-232
File diff suppressed because it is too large
Load Diff
@@ -1,260 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"saved_at": "2026-10-05T09:57:55",
|
|
||||||
"player": {
|
|
||||||
"room_id": "room_07.json",
|
|
||||||
"x": 680,
|
|
||||||
"y": 520,
|
|
||||||
"health": 10,
|
|
||||||
"charge": 120.0,
|
|
||||||
"shade": false,
|
|
||||||
"coins_light": 40,
|
|
||||||
"coins_dark": 20,
|
|
||||||
"butterfly_jars": 0,
|
|
||||||
"has_ranged": true,
|
|
||||||
"opened_chests": [
|
|
||||||
"room_03.json@90,194",
|
|
||||||
"room_07.json@440,494"
|
|
||||||
],
|
|
||||||
"equipment": {
|
|
||||||
"helmet": {
|
|
||||||
"id": "duskwarrior_helm",
|
|
||||||
"name": "Duskwarrior Helmet",
|
|
||||||
"set": "duskwarrior",
|
|
||||||
"slot": "helmet",
|
|
||||||
"base_stats": {},
|
|
||||||
"rarity_overrides": {
|
|
||||||
"tempered": {
|
|
||||||
"extra_stats": {
|
|
||||||
"charge": 10
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"attuned": {
|
|
||||||
"extra_stats": {
|
|
||||||
"charge": 10,
|
|
||||||
"health": 5
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"awakened": {
|
|
||||||
"extra_stats": {
|
|
||||||
"charge": 15,
|
|
||||||
"health": 5,
|
|
||||||
"charge_regen_pct": 0.1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rarity": "tempered",
|
|
||||||
"tier": 1,
|
|
||||||
"stats": {
|
|
||||||
"charge": 10.0
|
|
||||||
},
|
|
||||||
"abilities": []
|
|
||||||
},
|
|
||||||
"chest": {
|
|
||||||
"id": "vitalweave_chest",
|
|
||||||
"name": "Vitalweave Chestplate",
|
|
||||||
"set": "vitalweave",
|
|
||||||
"slot": "chest",
|
|
||||||
"base_stats": {},
|
|
||||||
"rarity_overrides": {
|
|
||||||
"tempered": {
|
|
||||||
"extra_stats": {
|
|
||||||
"heal_speed_pct": 0.1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"attuned": {
|
|
||||||
"extra_stats": {
|
|
||||||
"heal_speed_pct": 0.15
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"awakened": {
|
|
||||||
"extra_stats": {
|
|
||||||
"heal_speed_pct": 0.15,
|
|
||||||
"health": 5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rarity": "attuned",
|
|
||||||
"tier": 2,
|
|
||||||
"stats": {
|
|
||||||
"heal_speed_pct": 0.15
|
|
||||||
},
|
|
||||||
"abilities": []
|
|
||||||
},
|
|
||||||
"pants": {
|
|
||||||
"id": "gravemark_greaves",
|
|
||||||
"name": "Gravemark Greaves",
|
|
||||||
"set": "gravemark",
|
|
||||||
"slot": "pants",
|
|
||||||
"base_stats": {},
|
|
||||||
"rarity_overrides": {
|
|
||||||
"tempered": {
|
|
||||||
"extra_stats": {
|
|
||||||
"charge": 10
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"attuned": {
|
|
||||||
"extra_stats": {
|
|
||||||
"charge": 10,
|
|
||||||
"health": 5
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"awakened": {
|
|
||||||
"extra_stats": {
|
|
||||||
"charge": 20,
|
|
||||||
"health": 5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rarity": "tempered",
|
|
||||||
"tier": 1,
|
|
||||||
"stats": {
|
|
||||||
"charge": 10.0
|
|
||||||
},
|
|
||||||
"abilities": []
|
|
||||||
},
|
|
||||||
"necklace": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"inventory": [
|
|
||||||
{
|
|
||||||
"id": "dawnrunner_helm",
|
|
||||||
"name": "Dawnrunner Helmet",
|
|
||||||
"set": "dawnrunner",
|
|
||||||
"slot": "helmet",
|
|
||||||
"base_stats": {},
|
|
||||||
"rarity_overrides": {
|
|
||||||
"tempered": {
|
|
||||||
"extra_stats": {
|
|
||||||
"move_speed_light_pct": 0.1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"attuned": {
|
|
||||||
"extra_stats": {
|
|
||||||
"move_speed_light_pct": 0.1,
|
|
||||||
"heal_speed_pct": 0.1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"awakened": {
|
|
||||||
"extra_stats": {
|
|
||||||
"move_speed_light_pct": 0.15,
|
|
||||||
"heal_speed_pct": 0.2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rarity": "attuned",
|
|
||||||
"tier": 2,
|
|
||||||
"stats": {
|
|
||||||
"move_speed_light_pct": 0.1,
|
|
||||||
"heal_speed_pct": 0.1
|
|
||||||
},
|
|
||||||
"abilities": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "paladin_helm",
|
|
||||||
"name": "Paladin Helmet",
|
|
||||||
"set": "paladin",
|
|
||||||
"slot": "helmet",
|
|
||||||
"base_stats": {},
|
|
||||||
"rarity_overrides": {
|
|
||||||
"tempered": {
|
|
||||||
"extra_stats": {
|
|
||||||
"health": 5
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"attuned": {
|
|
||||||
"extra_stats": {
|
|
||||||
"health": 10,
|
|
||||||
"negate_damage_chance": 0.05
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"awakened": {
|
|
||||||
"extra_stats": {
|
|
||||||
"health": 15,
|
|
||||||
"negate_damage_chance": 0.1,
|
|
||||||
"knockback_resistance": 0.25
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rarity": "tempered",
|
|
||||||
"tier": 1,
|
|
||||||
"stats": {
|
|
||||||
"health": 5.0
|
|
||||||
},
|
|
||||||
"abilities": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "dawnrunner_chest",
|
|
||||||
"name": "Dawnrunner Chestplate",
|
|
||||||
"set": "dawnrunner",
|
|
||||||
"slot": "chest",
|
|
||||||
"base_stats": {},
|
|
||||||
"rarity_overrides": {
|
|
||||||
"tempered": {
|
|
||||||
"extra_stats": {
|
|
||||||
"charge": 10
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"attuned": {
|
|
||||||
"extra_stats": {
|
|
||||||
"charge": 15,
|
|
||||||
"move_speed_light_pct": 0.05
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"awakened": {
|
|
||||||
"extra_stats": {
|
|
||||||
"charge": 15,
|
|
||||||
"move_speed_light_pct": 0.05,
|
|
||||||
"heal_plus": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rarity": "attuned",
|
|
||||||
"tier": 2,
|
|
||||||
"stats": {
|
|
||||||
"charge": 15.0,
|
|
||||||
"move_speed_light_pct": 0.05
|
|
||||||
},
|
|
||||||
"abilities": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "necklace_flow",
|
|
||||||
"name": "Charm of Flow",
|
|
||||||
"slot": "necklace",
|
|
||||||
"item_type": "necklace",
|
|
||||||
"base_stats": {
|
|
||||||
"charge": 10
|
|
||||||
},
|
|
||||||
"rarity": "",
|
|
||||||
"tier": 0,
|
|
||||||
"abilities": [],
|
|
||||||
"stats": {
|
|
||||||
"charge": 10
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"quests": {
|
|
||||||
"active": {
|
|
||||||
"first_kills_shade": {
|
|
||||||
"progress": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"completed_one_time": [],
|
|
||||||
"ever_completed": [
|
|
||||||
"quest_first_kills"
|
|
||||||
],
|
|
||||||
"tokens": 0
|
|
||||||
},
|
|
||||||
"boss_diary": {
|
|
||||||
"slotted": {}
|
|
||||||
},
|
|
||||||
"defeated_bosses": [
|
|
||||||
"room_05.json"
|
|
||||||
],
|
|
||||||
"broken_doors": [],
|
|
||||||
"lantern_respawn": {
|
|
||||||
"room_id": "room_07.json",
|
|
||||||
"x": 640,
|
|
||||||
"y": 520
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user