Files
aetherbound-guild/runtime/domain/p9_1/session_state.gd
T

299 lines
9.1 KiB
GDScript

class_name FirstSessionState
extends RefCounted
const Contracts = preload("res://domain/p9_1/contracts.gd")
var revision := 0
var legacy_page := 0
var screen: StringName = Contracts.SCREEN_BOOT
var front_choice := ""
var rear_choice := ""
var selected_recruits: Array[String] = []
var starting_pair: Array[String] = []
var initial_second_id := ""
var marked_id := ""
var field_order: Array[String] = []
var member_hp: Dictionary = {}
var member_levels: Dictionary = {}
var formation_commit_count := 0
var notice := ""
var restart_confirmation := false
var battle_phase := 0
var battle_playing := false
var front_hp := 100
var rear_hp := 100
var enemy_hp := 100
var guard_events := 0
var rear_events := 0
var victory_events := 0
var phase_impact_committed: Dictionary = {}
var coin := 0
var first_reward_committed := false
var adaptation_choice := ""
var adaptation_committed := false
var guild_response: String:
get: return _guild_response
set(value):
if value.is_empty() or value in Contracts.GUILD_RESPONSE_IDS:
_guild_response = value
var _guild_response := ""
var guild_committed := false
var retained_item := ""
var added_rear := ""
var route_choice := ""
var route_committed := false
var route_reward_committed := false
var training_choice := ""
var growth_member_id := ""
var training_committed := false
var trained_member := ""
var trained_member_id := ""
var fifth_reward_committed := false
var encounter_elapsed := 0.0
var result_times: Dictionary = {}
var facts: Array[Dictionary] = []
func set_legacy_page(value: int) -> void:
legacy_page = value
screen = Contracts.screen_for_legacy_page(value)
func compat_patch(fields: Dictionary) -> bool:
for field in fields:
if field not in _compat_patch_fields():
return false
if field == &"guild_response" and not String(fields[field]).is_empty() and fields[field] not in Contracts.GUILD_RESPONSE_IDS:
return false
for field in fields:
set(field, fields[field])
return invariants_hold()
func _compat_patch_fields() -> Array[StringName]:
return [
&"legacy_page", &"screen", &"front_choice", &"rear_choice", &"selected_recruits",
&"starting_pair", &"initial_second_id", &"marked_id", &"field_order",
&"member_hp", &"member_levels",
&"formation_commit_count", &"notice", &"restart_confirmation", &"battle_phase",
&"battle_playing", &"front_hp", &"rear_hp", &"enemy_hp", &"guard_events",
&"rear_events", &"victory_events", &"phase_impact_committed", &"coin",
&"first_reward_committed", &"adaptation_choice", &"adaptation_committed",
&"guild_response", &"guild_committed", &"retained_item", &"added_rear",
&"route_choice", &"route_committed", &"route_reward_committed", &"training_choice",
&"growth_member_id", &"training_committed", &"trained_member", &"trained_member_id",
&"fifth_reward_committed", &"encounter_elapsed", &"result_times",
]
func set_screen(value: StringName) -> void:
screen = value
var mapped_page := Contracts.legacy_page_for_screen(value)
if mapped_page >= 0:
legacy_page = mapped_page
func reset_transient() -> void:
front_choice = ""
rear_choice = ""
selected_recruits.clear()
starting_pair.clear()
initial_second_id = ""
marked_id = ""
field_order.clear()
member_hp.clear()
member_levels.clear()
formation_commit_count = 0
notice = ""
restart_confirmation = false
battle_phase = 0
battle_playing = false
front_hp = 100
rear_hp = 100
enemy_hp = 100
guard_events = 0
rear_events = 0
victory_events = 0
phase_impact_committed.clear()
coin = 0
first_reward_committed = false
adaptation_choice = ""
adaptation_committed = false
guild_response = ""
guild_committed = false
retained_item = ""
added_rear = ""
route_choice = ""
route_committed = false
route_reward_committed = false
training_choice = ""
growth_member_id = ""
training_committed = false
trained_member = ""
trained_member_id = ""
fifth_reward_committed = false
encounter_elapsed = 0.0
result_times.clear()
facts.clear()
func available_actions() -> Array[StringName]:
match screen:
Contracts.SCREEN_RECRUIT_SELECTION:
return [&"toggle_recruit", &"review_formation", &"home"]
Contracts.SCREEN_FORMATION_REVIEW:
return [&"revise_formation", &"commit_formation", &"home"]
Contracts.SCREEN_ADAPTATION:
return [&"choose_adaptation", &"commit_adaptation"]
Contracts.SCREEN_GUILD_RESPONSE:
return [&"choose_guild_response", &"commit_guild_response"]
Contracts.SCREEN_ROUTE_BOARD:
return [&"choose_route", &"review_route"]
Contracts.SCREEN_ROUTE_REVIEW:
return [&"cancel_route_review", &"commit_route"]
Contracts.SCREEN_GROWTH:
return [&"choose_growth", &"review_growth"]
Contracts.SCREEN_GROWTH_REVIEW:
return [&"cancel_growth_review", &"commit_growth"]
return []
func snapshot() -> Dictionary:
var ordered: Array[String] = []
for member_id in Contracts.AUTO_FORMATION_ORDER:
if selected_recruits.has(member_id):
ordered.append(member_id)
return {
"schema": Contracts.SNAPSHOT_SCHEMA,
"revision": revision,
"screen": screen,
"party": {
"selected_ids": selected_recruits.duplicate(),
"starting_pair": starting_pair.duplicate(),
"ordered_ids": field_order.duplicate() if not field_order.is_empty() else ordered,
"lead_id": lead_id(),
"second_id": field_order[1] if field_order.size() > 1 else rear_choice,
"initial_second_id": initial_second_id,
"marked_id": marked_id,
"added_id": added_rear,
"member_hp": member_hp.duplicate(true),
"member_levels": member_levels.duplicate(true),
"formation_commits": formation_commit_count,
},
"resources": {"coin": coin},
"decisions": {
"adaptation": adaptation_choice,
"guild_response": guild_response,
"route": route_choice,
"growth_member": trained_member_id,
},
"locks": {
"adaptation": adaptation_committed,
"guild_response": guild_committed,
"route": route_committed,
"growth": training_committed,
},
"encounter": {
"id": encounter_id(),
"phase": battle_phase,
"playing": battle_playing,
"front_hp": front_hp,
"rear_hp": rear_hp,
"enemy_hp": enemy_hp,
"committed_impacts": committed_impact_ids(),
"elapsed": encounter_elapsed,
},
"result_times": result_times.duplicate(true),
"rewards": {
"battle_1": first_reward_committed,
"route": route_reward_committed,
"battle_5": fifth_reward_committed,
},
"facts": facts.duplicate(true),
"available_actions": available_actions(),
}.duplicate(true)
func lead_id() -> String:
return field_order[0] if not field_order.is_empty() else front_choice
func marked_member_id() -> String:
if not marked_id.is_empty():
return marked_id
if not initial_second_id.is_empty():
return initial_second_id
return rear_choice
func current_member_ids() -> Array[String]:
if not field_order.is_empty():
return field_order.duplicate()
var result: Array[String] = []
for member_id in [front_choice, rear_choice, added_rear]:
if not member_id.is_empty() and member_id not in result:
result.append(member_id)
return result
func reset_member_health(value := 100) -> void:
var clamped_value := clampi(value, 0, 100)
member_hp.clear()
for member_id in current_member_ids():
member_hp[member_id] = clamped_value
front_hp = clamped_value
rear_hp = clamped_value
_sync_legacy_health()
func set_member_health(member_id: String, value: int) -> void:
if member_id.is_empty():
return
member_hp[member_id] = clampi(value, 0, 100)
_sync_legacy_health()
func member_health(member_id: String) -> int:
if member_hp.has(member_id):
return int(member_hp[member_id])
if member_id == lead_id():
return front_hp
if member_id == marked_member_id():
return rear_hp
return 100
func _sync_legacy_health() -> void:
if not lead_id().is_empty():
front_hp = member_health(lead_id())
if not marked_member_id().is_empty():
rear_hp = member_health(marked_member_id())
func encounter_id() -> StringName:
match screen:
Contracts.SCREEN_BATTLE_1: return Contracts.ENCOUNTER_1
Contracts.SCREEN_BATTLE_2: return Contracts.ENCOUNTER_2
Contracts.SCREEN_BATTLE_3: return Contracts.ENCOUNTER_3
Contracts.SCREEN_BATTLE_4: return Contracts.ENCOUNTER_4
Contracts.SCREEN_BATTLE_5: return Contracts.ENCOUNTER_5
return &""
func committed_impact_ids() -> Array[StringName]:
var result: Array[StringName] = []
for key in phase_impact_committed:
result.append(StringName(str(key)))
return result
func invariants_hold() -> bool:
if selected_recruits.size() > 2:
return false
if starting_pair.size() not in [0, 2]:
return false
var seen := {}
for member_id in field_order:
if member_id not in Contracts.MEMBER_IDS or seen.has(member_id):
return false
seen[member_id] = true
if field_order.size() > 3:
return false
if formation_commit_count > 1:
return false
if not guild_response.is_empty() and guild_response not in Contracts.GUILD_RESPONSE_IDS:
return false
var current_members := current_member_ids()
for member_id in member_hp:
if member_id not in current_members or int(member_hp[member_id]) < 0 or int(member_hp[member_id]) > 100:
return false
for member_id in member_levels:
if member_id not in current_members or int(member_levels[member_id]) not in [1, 2]:
return false
if front_hp < 0 or rear_hp < 0 or enemy_hp < 0 or coin < 0:
return false
return true