feat: unify authored activity commands

This commit is contained in:
Rijad Zuzo
2026-08-13 00:08:30 +02:00
parent e5aecee0de
commit b3b98c6456
36 changed files with 3013 additions and 96 deletions
+24 -1
View File
@@ -4,6 +4,8 @@ extends RefCounted
const SCHEMA_VERSION := 3
const POSITION_LEGACY_SCHEMA_VERSION := 2
const LEGACY_SCHEMA_VERSION := 1
const LEGACY_BLOCKED_WORLD_SCHEMA_MIN := 3
const LEGACY_BLOCKED_WORLD_SCHEMA_MAX := 15
var data: Dictionary
@@ -117,7 +119,17 @@ static func from_dictionary(record_data: Dictionary) -> EconomicEventRecord:
and not normalized["item_id"].is_empty()
and normalized["required_amount"] > 0.0
)
if version == SCHEMA_VERSION and not has_stable_contract:
var has_legacy_marker := (
(
StringName(normalized.get("blocked_contract_kind", &""))
== SimulationIds.BLOCKED_CONTRACT_LEGACY_UNSTRUCTURED
)
and (
int(normalized.get("legacy_world_schema_version", -1))
in range(LEGACY_BLOCKED_WORLD_SCHEMA_MIN, LEGACY_BLOCKED_WORLD_SCHEMA_MAX + 1)
)
)
if version == SCHEMA_VERSION and not has_stable_contract and not has_legacy_marker:
return null
return EconomicEventRecord.new(normalized)
@@ -160,5 +172,16 @@ func description(npc_names: Dictionary = {}) -> String:
return "%s was depleted" % source
"animal_fed":
return "%s fed %s %.0f %s" % [actor_name, destination, amount, item]
"activity_completed":
return (
"%s completed %s at %s (+%.1f %s)"
% [
actor_name,
String(data.get("action_id", "activity")).capitalize(),
source,
float(data.get("metric_delta", 0.0)),
String(data.get("metric_id", "benefit")),
]
)
_:
return "tick %d: %s" % [int(data["tick"]), event_type]
+72 -1
View File
@@ -1,8 +1,9 @@
class_name SimulationStateRecord
extends RefCounted
# gdlint: disable=max-file-lines
const SCHEMA_NAME := "the_steward.simulation"
const SCHEMA_VERSION := 15
const SCHEMA_VERSION := 16
const LEGACY_SCHEMA_VERSION := 1
const EVENT_LEGACY_SCHEMA_VERSION := 2
const RELATIONSHIP_LEGACY_SCHEMA_VERSION := 3
@@ -16,6 +17,7 @@ const PLAYER_RELATIONSHIP_SCHEMA_VERSION := 11
const PLAYER_NEEDS_SCHEMA_VERSION := 12
const CONFLICT_SCHEMA_VERSION := 13
const PRE_EMERGENT_SCHEMA_VERSION := 14
const EMERGENT_SCHEMA_VERSION := 15
const PREVIOUS_SCHEMA_VERSION := 7
var simulation: Dictionary
@@ -137,6 +139,7 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
PLAYER_NEEDS_SCHEMA_VERSION,
CONFLICT_SCHEMA_VERSION,
PRE_EMERGENT_SCHEMA_VERSION,
EMERGENT_SCHEMA_VERSION,
]
):
record_data = _migrate_legacy(record_data, version)
@@ -187,6 +190,8 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
"next_journal_entry_id",
"next_commitment_id",
"next_conversation_act_id",
"world_id",
"location_id",
]
)
):
@@ -200,6 +205,8 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
var saved_next_journal_entry_id := int(simulation_data["next_journal_entry_id"])
var saved_next_commitment_id := int(simulation_data["next_commitment_id"])
var saved_next_conversation_act_id := int(simulation_data["next_conversation_act_id"])
var saved_world_id := StringName(simulation_data["world_id"])
var saved_location_id := StringName(simulation_data["location_id"])
if (
not is_finite(saved_tick_interval)
or saved_tick_interval <= 0.0
@@ -212,6 +219,8 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
or saved_next_journal_entry_id < 0
or saved_next_commitment_id < 0
or saved_next_conversation_act_id < 0
or saved_world_id.is_empty()
or saved_location_id.is_empty()
):
return null
if simulation_data.has("cycle_duration_seconds"):
@@ -447,6 +456,25 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
int(latest_feed_ticks.get(fed_animal_id, AnimalStateRecord.NEVER_FED_TICK)),
int(event_record.data["tick"])
)
elif ActivityEventValidator.is_candidate(event_record):
if not ActivityEventValidator.is_valid_for_state(
event_record,
npc_ids,
int(record.simulation["tick_count"]),
saved_world_id,
saved_location_id
):
return null
elif ActivityEventValidator.is_blocked_candidate(event_record):
if not ActivityEventValidator.is_valid_blocked_for_state(
event_record,
npc_ids,
storage_records_by_id,
int(record.simulation["tick_count"]),
saved_world_id,
saved_location_id
):
return null
record.economic_events.append(event_record)
if int(record.simulation["next_event_id"]) <= highest_event_id:
return null
@@ -932,6 +960,13 @@ static func _is_valid_supply_resolution(
static func _migrate_legacy(legacy_data: Dictionary, version: int) -> Dictionary:
var migrated := legacy_data.duplicate(true)
migrated["schema_version"] = SCHEMA_VERSION
if version == EMERGENT_SCHEMA_VERSION:
var scoped_simulation_data: Dictionary = migrated.get("simulation", {})
scoped_simulation_data["world_id"] = String(SimulationIds.REGIONAL_WORLD_BOSNIA)
scoped_simulation_data["location_id"] = String(SimulationIds.REGIONAL_LOCATION_JAJCE)
migrated["simulation"] = scoped_simulation_data
_migrate_blocked_event_scope(migrated, scoped_simulation_data, version)
return migrated
migrated["situations"] = []
migrated["quest_journal"] = []
migrated["commitments"] = []
@@ -941,7 +976,10 @@ static func _migrate_legacy(legacy_data: Dictionary, version: int) -> Dictionary
emergent_simulation_data["next_journal_entry_id"] = 0
emergent_simulation_data["next_commitment_id"] = 0
emergent_simulation_data["next_conversation_act_id"] = 0
emergent_simulation_data["world_id"] = String(SimulationIds.REGIONAL_WORLD_BOSNIA)
emergent_simulation_data["location_id"] = String(SimulationIds.REGIONAL_LOCATION_JAJCE)
migrated["simulation"] = emergent_simulation_data
_migrate_blocked_event_scope(migrated, emergent_simulation_data, version)
if not migrated.has("player"):
migrated["player"] = PlayerStateRecord.create_default().to_dictionary()
if version < CONFLICT_SCHEMA_VERSION:
@@ -1031,6 +1069,39 @@ static func _migrate_legacy(legacy_data: Dictionary, version: int) -> Dictionary
return migrated
static func _migrate_blocked_event_scope(
migrated: Dictionary, simulation_data: Dictionary, source_world_schema_version: int
) -> void:
var migrated_events: Variant = migrated.get("economic_events", [])
if migrated_events is not Array:
return
for event_value in migrated_events:
if (
event_value is Dictionary
and StringName(event_value.get("event_type", &"")) == SimulationIds.EVENT_TASK_BLOCKED
):
var event := event_value as Dictionary
var raw_required_amount: Variant = event.get("required_amount", 0.0)
var has_stable_contract := (
not String(event.get("action_id", "")).is_empty()
and not String(event.get("item_id", "")).is_empty()
and (raw_required_amount is int or raw_required_amount is float)
and is_finite(float(raw_required_amount))
and float(raw_required_amount) > 0.0
)
event["blocked_contract_kind"] = String(
(
SimulationIds.BLOCKED_CONTRACT_ACTION_COST
if has_stable_contract
else SimulationIds.BLOCKED_CONTRACT_LEGACY_UNSTRUCTURED
)
)
if not has_stable_contract:
event["legacy_world_schema_version"] = source_world_schema_version
event["world_id"] = String(simulation_data["world_id"])
event["location_id"] = String(simulation_data["location_id"])
static func _migrate_npc_familiarity(npc_data: Variant) -> Array[Dictionary]:
var migrated_relationships: Array[Dictionary] = []
if not npc_data is Array: