Add event inventory pickups
This commit is contained in:
@@ -96,7 +96,7 @@ Godot 4 tactical RPG prototype inspired by classic turn-based Romance of the Thr
|
||||
- Scenario post-battle dialogue can play before the victory result panel.
|
||||
- Basic battle EXP, level-ups, and core first-tier class promotion routes.
|
||||
- Victory results summarize saved player level-ups and promotions.
|
||||
- Scenario events for battle start, battle-begin dialogue, turn start, movement-triggered ambushes, objective updates, and reinforcements.
|
||||
- Scenario events for battle start, battle-begin dialogue, turn start, movement-triggered ambushes, objective updates, inventory pickups, and reinforcements.
|
||||
- Scenario briefing before battle with a campaign battle header and objective summary.
|
||||
- Victory rewards and consumed inventory saved into a campaign state.
|
||||
- Scenario-defined victory and defeat conditions.
|
||||
|
||||
@@ -84,7 +84,8 @@
|
||||
"once": true,
|
||||
"when": { "type": "battle_start" },
|
||||
"actions": [
|
||||
{ "type": "log", "text": "Cao Cao engages the Yellow Turbans." }
|
||||
{ "type": "log", "text": "Cao Cao engages the Yellow Turbans." },
|
||||
{ "type": "log", "text": "A small supply cache is hidden in the northern woods." }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -101,6 +102,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "northern_woods_cache",
|
||||
"once": true,
|
||||
"when": { "type": "unit_reaches_tile", "team": "player", "pos": [3, 2] },
|
||||
"actions": [
|
||||
{ "type": "log", "text": "The northern woods cache is recovered." },
|
||||
{ "type": "grant_item", "item_id": "bean" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "turn_2_warning",
|
||||
"once": true,
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
8. Player ends turn.
|
||||
9. Enemy AI moves, attacks, and can cast damage, healing, support, debuff, movement-modifier, accuracy/evasion-modifier, damage-over-time, or action-lock tactics through the same combat and skill resolvers.
|
||||
10. Battle checks scenario-defined defeat conditions first, including commander loss and turn limits, then victory conditions such as enemy defeat or reaching a marked destination tile.
|
||||
11. Turn-start and movement-triggered scenario events run as phases change or units enter marked cells, and may update objectives or deploy reinforcements.
|
||||
11. Turn-start and movement-triggered scenario events run as phases change or units enter marked cells, and may update objectives, grant inventory pickups, or deploy reinforcements.
|
||||
12. On first-time victory, `CampaignState` snapshots player progression and the battle inventory, applies rewards once, advances `current_scenario_id`, and writes `user://campaign_save.json`.
|
||||
13. If the scenario defines post-battle dialogue, it plays once before the victory result panel opens.
|
||||
14. The victory result panel summarizes rewards plus saved player level-ups and promotions.
|
||||
@@ -56,8 +56,8 @@
|
||||
|
||||
Joined officers gate whether `requires_joined` player deployments are loaded at all. Rewards and post-battle choices can add or remove joined officers without deleting their saved roster snapshot, so a later rejoin can preserve progression. When a save loads, `CampaignState` scans completed scenarios in campaign order and reapplies only officer join/leave transitions from rewards and the saved `applied_post_battle_choices` ledger, allowing added recruitment rewards to reach older saves without replaying gold or items. Old saves without the ledger can backfill a choice id only when exactly one completed scenario choice matches saved flags. Roster entries are keyed by `officer_id` when available, so future scenarios can deploy the same joined officer under different scenario-specific `unit_id` values. Saved progression overlays final battle stats for now; a later equipment refactor should split base stats from equipment bonuses before adding item leveling or stat recalculation. New battles currently start player units healed to their saved maximum HP/MP. Skill access is carried in roster snapshots, with class defaults still applied for old saves that do not include skills. Promoted class ids are saved in the roster, and the next battle rehydrates class-derived movement, growth, skills, and range from the saved class id before equipment range is recalculated.
|
||||
|
||||
Inventory and campaign flags are copied from `CampaignState` into `BattleState` when a scenario starts. The pre-battle Chapters overview reads `CampaignState` chapter progress and can load completed or current battles without mutating the save. The pre-battle Save menu can write the current campaign state to `user://campaign_manual_save.json`, and loading that checkpoint restores it into `user://campaign_save.json` before re-entering the same pending-choice, completion, or current-briefing branch as startup. Pre-battle shop stock comes from the loaded scenario's `shop.items` list plus matching `shop.conditional_items` blocks, with optional finite limits from item entries or `stock` maps. Shop purchases and 50% sell-back are campaign transactions: they update saved gold, unequipped inventory, and finite-stock purchase counts immediately, write the save file, and refresh the already-loaded battle inventory before the player begins the battle. Pre-battle Armory equipment changes use the same equipment rules as battle HUD equipment changes, then immediately save merged roster equipment/stat snapshots and inventory stock. Pre-battle Roster uses scenario `roster.max_units`, `roster.required_officers`, and `roster.required_units` to mark optional player deployments as sortie or reserve; reserve units remain in the candidate list but are excluded from board occupancy, drawing, selection, AI targeting, and living-unit victory checks. Non-controllable player units can act as protected escort targets: enemies can attack them and conditions can reference them, but they are skipped by player selection, Armory, Formation, counterattacks, and campaign roster progression snapshots when `persist_progression` is false. Scenario `ai_target_priority` nudges enemy movement and damage-skill scoring toward important targets such as envoys without overriding defeat bonuses or range checks. Pre-battle Formation uses the loaded scenario's `formation.cells` and only mutates current battle unit positions before battle-begin events fire. Destination victory cells from `unit_reaches_tile` conditions are exposed to the scene for objective overlays and tile info. Movement-triggered `unit_reaches_tile` events receive the unit that just moved, so ambushes fire on entry rather than from units already standing on a marker. Briefing data can append matching `briefing.conditional_lines`, and scenario events can use `when.campaign_flags` to branch dialogue, objective changes, and reinforcements from saved campaign choices. Consumable use can restore HP or MP or cure matching poison/seal/snare/disarm statuses, and in-battle item use plus equipment swaps mutate only the battle copy until victory; defeats and restarts do not spend saved items or preserve in-battle gear changes. Post-battle choices write campaign `flags` only after the result-panel button is pressed; if the player reloads after rewards are saved but before selecting, the pending scenario id restores the victory choice panel without replaying rewards. The next battle button stays locked until the campaign choice save succeeds. Equipment rewards share the same `item_id -> count` inventory stock, cover weapon, armor, and accessory slots, can be equipped from the side HUD before the selected unit moves or acts, and do not appear in the consumable action menu. Already completed scenarios can be replayed without granting duplicate rewards, choices, replay inventory consumption, save writes, current-scenario advancement, or finite-stock purchases; pre-battle Shop, Armory, Roster, Formation, and Save are disabled on completed-scenario replays to avoid side effects.
|
||||
Inventory and campaign flags are copied from `CampaignState` into `BattleState` when a scenario starts. The pre-battle Chapters overview reads `CampaignState` chapter progress and can load completed or current battles without mutating the save. The pre-battle Save menu can write the current campaign state to `user://campaign_manual_save.json`, and loading that checkpoint restores it into `user://campaign_save.json` before re-entering the same pending-choice, completion, or current-briefing branch as startup. Pre-battle shop stock comes from the loaded scenario's `shop.items` list plus matching `shop.conditional_items` blocks, with optional finite limits from item entries or `stock` maps. Shop purchases and 50% sell-back are campaign transactions: they update saved gold, unequipped inventory, and finite-stock purchase counts immediately, write the save file, and refresh the already-loaded battle inventory before the player begins the battle. Pre-battle Armory equipment changes use the same equipment rules as battle HUD equipment changes, then immediately save merged roster equipment/stat snapshots and inventory stock. Pre-battle Roster uses scenario `roster.max_units`, `roster.required_officers`, and `roster.required_units` to mark optional player deployments as sortie or reserve; reserve units remain in the candidate list but are excluded from board occupancy, drawing, selection, AI targeting, and living-unit victory checks. Non-controllable player units can act as protected escort targets: enemies can attack them and conditions can reference them, but they are skipped by player selection, Armory, Formation, counterattacks, and campaign roster progression snapshots when `persist_progression` is false. Scenario `ai_target_priority` nudges enemy movement and damage-skill scoring toward important targets such as envoys without overriding defeat bonuses or range checks. Pre-battle Formation uses the loaded scenario's `formation.cells` and only mutates current battle unit positions before battle-begin events fire. Destination victory cells from `unit_reaches_tile` conditions are exposed to the scene for objective overlays and tile info. Movement-triggered `unit_reaches_tile` events receive the unit that just moved, so ambushes and pickup events fire on entry rather than from units already standing on a marker. Briefing data can append matching `briefing.conditional_lines`, and scenario events can use `when.campaign_flags` to branch dialogue, objective changes, item pickups, and reinforcements from saved campaign choices. Consumable use can restore HP or MP or cure matching poison/seal/snare/disarm statuses, and in-battle item use, event pickup grants, plus equipment swaps mutate only the battle copy until victory; defeats and restarts do not spend saved items, keep picked-up items, or preserve in-battle gear changes. Post-battle choices write campaign `flags` only after the result-panel button is pressed; if the player reloads after rewards are saved but before selecting, the pending scenario id restores the victory choice panel without replaying rewards. The next battle button stays locked until the campaign choice save succeeds. Equipment rewards share the same `item_id -> count` inventory stock, cover weapon, armor, and accessory slots, can be equipped from the side HUD before the selected unit moves or acts, and do not appear in the consumable action menu. Already completed scenarios can be replayed without granting duplicate rewards, choices, replay inventory consumption, save writes, current-scenario advancement, or finite-stock purchases; pre-battle Shop, Armory, Roster, Formation, and Save are disabled on completed-scenario replays to avoid side effects.
|
||||
|
||||
The battle scene owns presentation feedback: briefing and result states use menu BGM, briefing headers combine `CampaignState` chapter ranges, campaign order, and title data with `BattleState` briefing and objective text, active battles use battle BGM, dialogue lines can show optional portrait textures or speaker-initial fallback panels and can place the portrait on the left or right side of the dialogue panel, board units render low-HP warning rings, HP bar color states, and active support/debuff/poison/seal/snare/disarm status pips from battle state fields, hover previews are rendered as target badges from existing `BattleState` forecast APIs, log/result hooks trigger placeholder SFX, support, debuff, poison, seal, snare, and disarm effects use distinct floating feedback colors, `BattleState.unit_motion_requested` asks the scene to interpolate a unit's draw position during movement, and `BattleState.combat_feedback_requested` asks the scene to draw transient floating combat text without changing battle rules.
|
||||
The battle scene owns presentation feedback: briefing and result states use menu BGM, briefing headers combine `CampaignState` chapter ranges, campaign order, and title data with `BattleState` briefing and objective text, active battles use battle BGM, dialogue lines can show optional portrait textures or speaker-initial fallback panels and can place the portrait on the left or right side of the dialogue panel, board units render low-HP warning rings, HP bar color states, and active support/debuff/poison/seal/snare/disarm status pips from battle state fields, hover previews are rendered as target badges from existing `BattleState` forecast APIs, log/result hooks trigger placeholder SFX, support, debuff, poison, seal, snare, disarm, and item pickup effects use distinct floating feedback colors, `BattleState.unit_motion_requested` asks the scene to interpolate a unit's draw position during movement, and `BattleState.combat_feedback_requested` asks the scene to draw transient floating combat text without changing battle rules.
|
||||
|
||||
The `New Campaign` button clears `user://campaign_save.json`, resets campaign state to the first scenario, and reloads the opening briefing.
|
||||
|
||||
@@ -444,7 +444,7 @@ Conditions may include `after_event` to stay inactive until a one-shot event has
|
||||
|
||||
`post_battle_choices` are shown on the victory result panel after first-time scenario rewards are applied. Each choice needs a unique stable lowercase `id`, a non-empty `label`, and a `set_flags` object whose keys are stable lowercase flag ids. Choices may also grant positive `gold`, known `items`, `join_officers`, and `leave_officers`. When rewards are saved but the player has not selected a choice yet, `CampaignState.pending_post_battle_choice_scenario_id` records that scenario id so reloading the game returns to the victory choice panel instead of skipping the branch. The selected choice is saved to `CampaignState.flags`, `CampaignState.applied_post_battle_choices`, and campaign membership only when the player presses its result-panel button; completed-scenario replays do not show choices again. Save-load migration for older saves uses flags as a conservative choice signal, so avoid designing multiple choices in one scenario that can all match the same saved flag state.
|
||||
|
||||
Scenario `events` support `battle_start`, `battle_begin`, `turn_start`, and movement-triggered `unit_reaches_tile` triggers. `battle_begin` runs after the briefing is closed. `unit_reaches_tile` events run when a moved unit enters the zero-based `pos` cell; use `team`, `unit_ids`, or `officer_ids` to limit who can trigger the event. Actions currently include `log`, `dialogue`, `set_objective`, `spawn_deployment`, and `spawn_deployments`. Event `dialogue` actions use the same dialogue line format, portrait default rules, and optional `side` placement as `post_battle_dialogue`.
|
||||
Scenario `events` support `battle_start`, `battle_begin`, `turn_start`, and movement-triggered `unit_reaches_tile` triggers. `battle_begin` runs after the briefing is closed. `unit_reaches_tile` events run when a moved unit enters the zero-based `pos` cell; use `team`, `unit_ids`, or `officer_ids` to limit who can trigger the event. Actions currently include `log`, `dialogue`, `set_objective`, `grant_item`, `grant_items`, `spawn_deployment`, and `spawn_deployments`. Event `dialogue` actions use the same dialogue line format, portrait default rules, and optional `side` placement as `post_battle_dialogue`. Event item grants add to the battle inventory immediately; they persist to the campaign save only when the battle is won.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -465,6 +465,7 @@ Scenario `events` support `battle_start`, `battle_begin`, `turn_start`, and move
|
||||
]
|
||||
},
|
||||
{ "type": "set_objective", "victory": "Defeat the vanguard and reinforcements." },
|
||||
{ "type": "grant_item", "item_id": "bean" },
|
||||
{
|
||||
"type": "spawn_deployment",
|
||||
"deployment": {
|
||||
@@ -505,6 +506,20 @@ Movement-triggered ambushes use the same event action shape:
|
||||
}
|
||||
```
|
||||
|
||||
Movement-triggered pickups can use `grant_item` for one item or `grant_items` for a mixed list. `grant_items.items` accepts item ids or objects with `item_id`/`id` and a positive `count`.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "northern_woods_cache",
|
||||
"once": true,
|
||||
"when": { "type": "unit_reaches_tile", "team": "player", "pos": [3, 2] },
|
||||
"actions": [
|
||||
{ "type": "log", "text": "The northern woods cache is recovered." },
|
||||
{ "type": "grant_items", "items": ["bean", { "item_id": "antidote", "count": 2 }] }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`briefing.conditional_lines`, `shop.conditional_items`, and `when.campaign_flags` all use exact flag matching. When present, every listed flag must match the saved campaign flag value for that block or event to apply. Use separate blocks or events for OR-style branches. The validator requires referenced flags to have been introduced by an earlier `post_battle_choices.set_flags` entry in campaign order.
|
||||
|
||||
Event-spawned units use the same compact deployment shape as scenario starting units. Spawn attempts are skipped if the unit id already exists, the target cell is occupied, the cell is outside the map, or the unit cannot stand on that terrain. Reinforcements that arrive during their own team's phase wait until their next phase before acting.
|
||||
@@ -517,7 +532,7 @@ Event-spawned units use the same compact deployment shape as scenario starting u
|
||||
|
||||
After a first-time victory, `CampaignState` advances `current_scenario_id` to the next entry in `data/campaign/campaign.json`. Completed-scenario replays return a result summary without rewriting the save or moving `current_scenario_id`. The battle scene passes `CampaignState.get_roster_overrides()`, `CampaignState.get_inventory_snapshot()`, `CampaignState.get_flags_snapshot()`, and `CampaignState.get_joined_officers_snapshot()` into `BattleState.load_battle()`, so officers can keep level/EXP/stat progression, equipped gear, consumable/equipment stock counts, recruitment gates, and branch flags into the next scenario.
|
||||
|
||||
During a battle, item consumption and equipment swaps affect `BattleState.battle_inventory`. `CampaignState.apply_battle_result()` commits that inventory snapshot only for a first-time victory, then adds the victory rewards. Equipped gear is stored in the player roster snapshot together with the current derived stats; a later stat-model refactor should split base stats, growth, and equipment bonuses.
|
||||
During a battle, item consumption, event item pickups, and equipment swaps affect `BattleState.battle_inventory`. `CampaignState.apply_battle_result()` commits that inventory snapshot only for a first-time victory, then adds the victory rewards. Equipped gear is stored in the player roster snapshot together with the current derived stats; a later stat-model refactor should split base stats, growth, and equipment bonuses.
|
||||
|
||||
`BattleState` records player-only `progression_events` when a unit levels up or promotes. `CampaignState.apply_battle_result()` includes those events in the victory summary only when the first-time victory save succeeds, so completed-scenario replays and failed saves do not present unsaved growth as campaign progress.
|
||||
|
||||
@@ -527,4 +542,4 @@ Before a battle starts, `BattleScene` can buy priced items through `CampaignStat
|
||||
|
||||
When every campaign scenario id is present in `completed_scenarios`, the battle scene shows a campaign completion panel. Starting a new campaign clears the automatic save file and resets `current_scenario_id` to `start_scenario`; the manual checkpoint is left alone until overwritten from the Save menu.
|
||||
|
||||
Run `tools/validate_data.ps1` after data edits to check campaign paths, chapter ranges, map dimensions, terrain keys, class/officer/item/skill references, skill status/action-lock effect shapes, item effect names and cure statuses, equipment slot compatibility including accessory types, promotion routes and promotion equipment compatibility, condition/event names, condition unit references, destination condition coordinates, campaign flag references, joined-officer gates, briefing line blocks, deployment ids, deployment bounds, impassable spawn cells, shop stock, roster rules, formation cells, post-battle dialogue, post-battle choices, and reward item ids.
|
||||
Run `tools/validate_data.ps1` after data edits to check campaign paths, chapter ranges, map dimensions, terrain keys, class/officer/item/skill references, skill status/action-lock effect shapes, item effect names and cure statuses, equipment slot compatibility including accessory types, promotion routes and promotion equipment compatibility, condition/event names, event item grants, condition unit references, destination condition coordinates, campaign flag references, joined-officer gates, briefing line blocks, deployment ids, deployment bounds, impassable spawn cells, shop stock, roster rules, formation cells, post-battle dialogue, post-battle choices, and reward item ids.
|
||||
|
||||
@@ -46,6 +46,7 @@ powershell -NoProfile -ExecutionPolicy Bypass -File tools\check_godot46_readines
|
||||
- Finite-stock Shop entries show remaining counts and disable once sold out.
|
||||
- Manual checkpoint save/load restores the expected current briefing without script errors.
|
||||
- A player unit can move, attack, wait, use a tactic, use an item, and equip compatible gear.
|
||||
- Movement-triggered supply cache events grant battle inventory items with pickup feedback, and the items persist only after victory.
|
||||
- March Order and Hamper Order adjust movement range while active and expire with the normal support/debuff timing.
|
||||
- Focus Order and Feint Order adjust physical hit chance through AGI bonuses or penalties and update attack forecasts while active.
|
||||
- Poison Mist applies poison, poison ticks at the target team's phase start, and poison defeat immediately ends the battle when it satisfies a victory or defeat condition.
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
- Mid-battle map/action events have a first action system with movement ambushes.
|
||||
- Opening battle dialogue exists through event actions.
|
||||
- Post-battle dialogue exists before the victory result panel.
|
||||
- Reinforcements and objective changes exist in a basic form.
|
||||
- Reinforcements, objective changes, and inventory pickup events exist in a basic form.
|
||||
- Post-battle reward save, campaign choice flags, and first flag-driven briefing/shop/event branches exist in a basic form. Rich reward screens and wider branch support are still planned.
|
||||
|
||||
## Milestone 4: Campaign Progression
|
||||
@@ -133,7 +133,7 @@
|
||||
|
||||
- Portrait pipeline. Officer definitions can provide default portrait paths, dialogue lines can override them, and missing art falls back to speaker initials.
|
||||
- Unit sprites and animations. Board movement now has a short slide animation, and board units show low-HP warning rings plus small status pips for active support, debuff, poison, seal, snare, and disarm effects.
|
||||
- Battle effects. First floating combat text now covers damage, recovery, misses, support effects, poison ticks, level-ups, and promotions. Hover target preview badges summarize attacks, tactics, and items.
|
||||
- Battle effects. First floating combat text now covers damage, recovery, misses, support effects, poison ticks, item pickups, level-ups, and promotions. Hover target preview badges summarize attacks, tactics, and items.
|
||||
- Music and sound. Basic placeholder BGM and SFX now play for menus, battle flow, unit actions, tactics, items, and result states.
|
||||
- Chapter UI and visual novel scenes. Pre-battle briefing now uses campaign chapter ranges for chapter-aware numbering, has a chapter progress/replay overview, and dialogue lines can choose left or right portrait placement.
|
||||
|
||||
|
||||
@@ -2776,7 +2776,7 @@ func _run_events(trigger_type: String, team := "", turn := -1, trigger_unit: Dic
|
||||
continue
|
||||
if not _campaign_flags_match(when.get("campaign_flags", {})):
|
||||
continue
|
||||
_execute_event_actions(event.get("actions", []))
|
||||
_execute_event_actions(event.get("actions", []), trigger_unit)
|
||||
if bool(event.get("once", false)) and not event_id.is_empty():
|
||||
fired_event_ids[event_id] = true
|
||||
_check_battle_status()
|
||||
@@ -2807,14 +2807,14 @@ func _campaign_flags_match(required_flags) -> bool:
|
||||
return true
|
||||
|
||||
|
||||
func _execute_event_actions(actions: Array) -> void:
|
||||
func _execute_event_actions(actions: Array, trigger_unit: Dictionary = {}) -> void:
|
||||
for action in actions:
|
||||
if typeof(action) != TYPE_DICTIONARY:
|
||||
continue
|
||||
_execute_event_action(action)
|
||||
_execute_event_action(action, trigger_unit)
|
||||
|
||||
|
||||
func _execute_event_action(action: Dictionary) -> void:
|
||||
func _execute_event_action(action: Dictionary, trigger_unit: Dictionary = {}) -> void:
|
||||
var action_type := str(action.get("type", ""))
|
||||
if action_type == "log":
|
||||
_emit_log(str(action.get("text", "")))
|
||||
@@ -2824,6 +2824,10 @@ func _execute_event_action(action: Dictionary) -> void:
|
||||
dialogue_requested.emit(lines)
|
||||
elif action_type == "set_objective":
|
||||
_apply_objective_event(action)
|
||||
elif action_type == "grant_item":
|
||||
_grant_event_item(action, trigger_unit)
|
||||
elif action_type == "grant_items":
|
||||
_grant_event_items(action.get("items", []), trigger_unit)
|
||||
elif action_type == "spawn_deployment":
|
||||
_spawn_event_deployment(action.get("deployment", {}))
|
||||
elif action_type == "spawn_deployments":
|
||||
@@ -2835,6 +2839,38 @@ func _execute_event_action(action: Dictionary) -> void:
|
||||
_spawn_event_deployment(deployment)
|
||||
|
||||
|
||||
func _grant_event_items(item_entries, trigger_unit: Dictionary = {}) -> void:
|
||||
if typeof(item_entries) != TYPE_ARRAY:
|
||||
push_error("Skipping malformed event item grants in %s." % battle_id)
|
||||
return
|
||||
for entry in item_entries:
|
||||
if typeof(entry) == TYPE_STRING:
|
||||
_grant_event_item({"item_id": str(entry)}, trigger_unit)
|
||||
elif typeof(entry) == TYPE_DICTIONARY:
|
||||
_grant_event_item(entry, trigger_unit)
|
||||
else:
|
||||
push_error("Skipping malformed event item grant in %s." % battle_id)
|
||||
|
||||
|
||||
func _grant_event_item(action: Dictionary, trigger_unit: Dictionary = {}) -> bool:
|
||||
var item_id := str(action.get("item_id", action.get("id", ""))).strip_edges()
|
||||
if item_id.is_empty():
|
||||
push_error("Skipping event item grant with no item_id in %s." % battle_id)
|
||||
return false
|
||||
var item := get_item_def(item_id)
|
||||
if item.is_empty():
|
||||
push_error("Skipping unknown event item grant %s in %s." % [item_id, battle_id])
|
||||
return false
|
||||
var count: int = maxi(1, int(action.get("count", 1)))
|
||||
battle_inventory[item_id] = int(battle_inventory.get(item_id, 0)) + count
|
||||
var item_name: String = str(item.get("name", item_id))
|
||||
var count_text: String = "" if count == 1 else " x%d" % count
|
||||
_emit_log("Received %s%s." % [item_name, count_text])
|
||||
if not trigger_unit.is_empty() and bool(trigger_unit.get("alive", false)):
|
||||
_emit_combat_feedback(trigger_unit, "+%s" % item_name, "item")
|
||||
return true
|
||||
|
||||
|
||||
func _apply_objective_event(action: Dictionary) -> void:
|
||||
if action.has("victory"):
|
||||
objectives["victory"] = str(action["victory"])
|
||||
|
||||
@@ -731,7 +731,7 @@ func _play_log_sfx(message: String) -> void:
|
||||
_play_sfx("fire_skill")
|
||||
elif message.contains(" moved to ") or message.contains(" advances to ") or message.contains(" takes formation at ") or message.contains(" arrives at "):
|
||||
_play_sfx("footstep")
|
||||
elif message.contains(" uses ") or message.contains(" equips ") or message.begins_with("Bought ") or message.begins_with("Sold "):
|
||||
elif message.contains(" uses ") or message.contains(" equips ") or message.begins_with("Bought ") or message.begins_with("Sold ") or message.begins_with("Received "):
|
||||
_play_sfx("item_pickup")
|
||||
elif message.contains(" reaches Lv.") or message.contains(" promotes to "):
|
||||
_play_sfx("menu_confirm")
|
||||
@@ -1115,6 +1115,8 @@ func _floating_text_color(kind: String) -> Color:
|
||||
return Color(0.36, 1.0, 0.46)
|
||||
if kind == "mp":
|
||||
return Color(0.48, 0.74, 1.0)
|
||||
if kind == "item":
|
||||
return Color(0.98, 0.76, 0.28)
|
||||
if kind == "support":
|
||||
return Color(1.0, 0.88, 0.28)
|
||||
if kind == "debuff":
|
||||
|
||||
@@ -93,7 +93,7 @@ $validConditionTypes = @(
|
||||
"any"
|
||||
)
|
||||
$validTriggers = @("battle_start", "battle_begin", "turn_start", "unit_reaches_tile")
|
||||
$validActions = @("log", "dialogue", "set_objective", "spawn_deployment", "spawn_deployments")
|
||||
$validActions = @("log", "dialogue", "set_objective", "grant_item", "grant_items", "spawn_deployment", "spawn_deployments")
|
||||
$validEffects = @("heal_hp", "heal_mp", "cure_status")
|
||||
$validItemKinds = @("weapon", "armor", "accessory", "consumable")
|
||||
$validBonusStats = @("hp", "mp", "atk", "def", "int", "agi")
|
||||
@@ -494,6 +494,32 @@ function Find-Item-Def([string]$ItemId) {
|
||||
return $prop.Value
|
||||
}
|
||||
|
||||
function Resolve-Event-Grant-Item-Id($Entry, [string]$Context) {
|
||||
if ($Entry -is [string]) {
|
||||
return [string]$Entry
|
||||
}
|
||||
if ($null -eq $Entry -or $Entry -is [System.Array]) {
|
||||
Fail "$Context has malformed item grant."
|
||||
}
|
||||
return [string](Get-Prop $Entry "item_id" (Get-Prop $Entry "id" ""))
|
||||
}
|
||||
|
||||
function Check-Event-Grant-Item($Entry, [string]$Context) {
|
||||
$itemId = Resolve-Event-Grant-Item-Id $Entry $Context
|
||||
if ([string]::IsNullOrWhiteSpace($itemId)) {
|
||||
Fail "$Context has empty item id."
|
||||
}
|
||||
if (-not $itemIds.Contains($itemId)) {
|
||||
Fail "$Context references unknown item: $itemId"
|
||||
}
|
||||
if (-not ($Entry -is [string]) -and (Has-Prop $Entry "count")) {
|
||||
$count = [int](Get-Prop $Entry "count" 0)
|
||||
if ($count -le 0) {
|
||||
Fail "$Context has invalid count for item $itemId."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Check-Class-Equipment-Slots() {
|
||||
$weaponTypes = @()
|
||||
$armorTypes = @()
|
||||
@@ -1556,6 +1582,19 @@ foreach ($scenario in $campaign.scenarios) {
|
||||
Check-Dialogue-Line $action "Scenario $scenarioId event $eventId dialogue"
|
||||
}
|
||||
}
|
||||
if ($actionType -eq "grant_item") {
|
||||
Check-Event-Grant-Item $action "Scenario $scenarioId event $eventId grant_item"
|
||||
}
|
||||
if ($actionType -eq "grant_items") {
|
||||
$rawItems = Get-Prop $action "items" $null
|
||||
$grantItems = @($rawItems)
|
||||
if ($null -eq $rawItems -or $grantItems.Count -le 0) {
|
||||
Fail "Scenario $scenarioId event $eventId grant_items action has malformed items."
|
||||
}
|
||||
foreach ($entry in $grantItems) {
|
||||
Check-Event-Grant-Item $entry "Scenario $scenarioId event $eventId grant_items"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user