299 lines
9.1 KiB
GDScript
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
|