feat: define regional world state
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
class_name RegionalStateSupport
|
||||
extends RefCounted
|
||||
|
||||
|
||||
static func normalize_id_array(values: Array, allow_empty: bool = true) -> Variant:
|
||||
var seen: Dictionary = {}
|
||||
var normalized: Array[String] = []
|
||||
for value: Variant in values:
|
||||
if not value is String and not value is StringName:
|
||||
return null
|
||||
var id := String(value)
|
||||
if id.is_empty() or seen.has(id):
|
||||
return null
|
||||
seen[id] = true
|
||||
normalized.append(id)
|
||||
if not allow_empty and normalized.is_empty():
|
||||
return null
|
||||
normalized.sort()
|
||||
return normalized
|
||||
|
||||
|
||||
static func normalize_history_event_ids(values: Array) -> Variant:
|
||||
var seen: Dictionary = {}
|
||||
var normalized: Array[int] = []
|
||||
for value: Variant in values:
|
||||
if not value is int:
|
||||
return null
|
||||
var event_id := int(value)
|
||||
if event_id < 0 or seen.has(event_id):
|
||||
return null
|
||||
seen[event_id] = true
|
||||
normalized.append(event_id)
|
||||
normalized.sort()
|
||||
return normalized
|
||||
|
||||
|
||||
static func normalize_amount_ledger(values: Dictionary) -> Variant:
|
||||
var source_keys: Dictionary = {}
|
||||
for raw_key: Variant in values:
|
||||
if not raw_key is String and not raw_key is StringName:
|
||||
return null
|
||||
var key := String(raw_key)
|
||||
if key.is_empty() or source_keys.has(key):
|
||||
return null
|
||||
var raw_amount: Variant = values[raw_key]
|
||||
if not raw_amount is int and not raw_amount is float:
|
||||
return null
|
||||
var amount := float(raw_amount)
|
||||
if not is_finite(amount) or amount < 0.0:
|
||||
return null
|
||||
source_keys[key] = raw_key
|
||||
var keys: Array = source_keys.keys()
|
||||
keys.sort()
|
||||
var normalized: Dictionary = {}
|
||||
for key: String in keys:
|
||||
normalized[key] = float(values[source_keys[key]])
|
||||
return normalized
|
||||
|
||||
|
||||
static func normalize_count_ledger(values: Dictionary) -> Variant:
|
||||
var source_keys: Dictionary = {}
|
||||
for raw_key: Variant in values:
|
||||
if not raw_key is String and not raw_key is StringName:
|
||||
return null
|
||||
var key := String(raw_key)
|
||||
if key.is_empty() or source_keys.has(key):
|
||||
return null
|
||||
var raw_count: Variant = values[raw_key]
|
||||
if not raw_count is int or int(raw_count) <= 0:
|
||||
return null
|
||||
source_keys[key] = raw_key
|
||||
var keys: Array = source_keys.keys()
|
||||
keys.sort()
|
||||
var normalized: Dictionary = {}
|
||||
for key: String in keys:
|
||||
normalized[key] = int(values[source_keys[key]])
|
||||
return normalized
|
||||
|
||||
|
||||
static func amount_ledger_total(values: Dictionary) -> float:
|
||||
var total := 0.0
|
||||
for value: Variant in values.values():
|
||||
total += float(value)
|
||||
return total
|
||||
|
||||
|
||||
static func normalize_address(value: Variant) -> Variant:
|
||||
var address: SpatialAddress
|
||||
if value is SpatialAddress:
|
||||
address = value as SpatialAddress
|
||||
elif value is Dictionary:
|
||||
address = SpatialAddress.from_dictionary(value)
|
||||
if address == null or not address.is_valid():
|
||||
return null
|
||||
return address.to_dictionary()
|
||||
|
||||
|
||||
static func address_from_data(value: Variant) -> SpatialAddress:
|
||||
if not value is Dictionary:
|
||||
return null
|
||||
return SpatialAddress.from_dictionary(value)
|
||||
|
||||
|
||||
static func address_matches(
|
||||
value: Variant, expected_world_id: StringName, expected_location_id: StringName
|
||||
) -> bool:
|
||||
var address := address_from_data(value)
|
||||
return (
|
||||
address != null
|
||||
and address.get_world_id() == expected_world_id
|
||||
and address.get_location_id() == expected_location_id
|
||||
)
|
||||
|
||||
|
||||
static func copy_string_array(values: Variant) -> Array[StringName]:
|
||||
var copied: Array[StringName] = []
|
||||
if not values is Array:
|
||||
return copied
|
||||
for value: Variant in values:
|
||||
copied.append(StringName(value))
|
||||
return copied
|
||||
|
||||
|
||||
static func copy_int_array(values: Variant) -> Array[int]:
|
||||
var copied: Array[int] = []
|
||||
if not values is Array:
|
||||
return copied
|
||||
for value: Variant in values:
|
||||
copied.append(int(value))
|
||||
return copied
|
||||
|
||||
|
||||
static func pair_key(first_id: StringName, second_id: StringName) -> String:
|
||||
var first := String(first_id)
|
||||
var second := String(second_id)
|
||||
return "%s\u001f%s" % [first, second] if first < second else "%s\u001f%s" % [second, first]
|
||||
|
||||
|
||||
static func is_non_empty_id(value: StringName) -> bool:
|
||||
return not value.is_empty()
|
||||
|
||||
|
||||
static func is_valid_tick(value: int, allow_unset: bool = false) -> bool:
|
||||
return value >= (-1 if allow_unset else 0)
|
||||
@@ -0,0 +1 @@
|
||||
uid://c041d4ms34euf
|
||||
@@ -0,0 +1,858 @@
|
||||
class_name RegionalWorldState
|
||||
extends RefCounted
|
||||
|
||||
const SCHEMA_VERSION := 1
|
||||
|
||||
var _world_id: StringName
|
||||
var _locations: Dictionary = {}
|
||||
var _settlements: Dictionary = {}
|
||||
var _routes: Dictionary = {}
|
||||
var _mobile_groups: Dictionary = {}
|
||||
var _persons: Dictionary = {}
|
||||
var _cohorts: Dictionary = {}
|
||||
var _polities: Dictionary = {}
|
||||
var _diplomatic_relations: Dictionary = {}
|
||||
var _diplomacy_by_pair: Dictionary = {}
|
||||
var _route_ids_by_location: Dictionary = {}
|
||||
var _group_ids_by_location: Dictionary = {}
|
||||
var _group_ids_by_route: Dictionary = {}
|
||||
var _person_ids_by_location: Dictionary = {}
|
||||
var _person_ids_by_group: Dictionary = {}
|
||||
var _cohort_ids_by_location: Dictionary = {}
|
||||
var _cohort_ids_by_group: Dictionary = {}
|
||||
var _settlement_ids_by_location: Dictionary = {}
|
||||
var _global_entity_types: Dictionary = {}
|
||||
var _last_validation_errors: Array[String] = []
|
||||
|
||||
|
||||
static func create(
|
||||
world_id: StringName,
|
||||
locations: Array[LocationStateRecord],
|
||||
settlements: Array[SettlementStateRecord],
|
||||
routes: Array[RouteStateRecord],
|
||||
mobile_groups: Array[MobileGroupStateRecord],
|
||||
persons: Array[PersonStateRecord],
|
||||
cohorts: Array[PopulationCohortRecord],
|
||||
polities: Array[PolityStateRecord],
|
||||
diplomatic_relations: Array[DiplomaticRelationRecord]
|
||||
) -> RegionalWorldState:
|
||||
var state := RegionalWorldState.new()
|
||||
return (
|
||||
state
|
||||
if state.replace_all(
|
||||
world_id,
|
||||
locations,
|
||||
settlements,
|
||||
routes,
|
||||
mobile_groups,
|
||||
persons,
|
||||
cohorts,
|
||||
polities,
|
||||
diplomatic_relations
|
||||
)
|
||||
else null
|
||||
)
|
||||
|
||||
|
||||
static func from_dictionary(record_data: Dictionary) -> RegionalWorldState:
|
||||
var state := RegionalWorldState.new()
|
||||
return state if state.restore_from_dictionary(record_data) else null
|
||||
|
||||
|
||||
func replace_all(
|
||||
world_id: StringName,
|
||||
locations: Array[LocationStateRecord],
|
||||
settlements: Array[SettlementStateRecord],
|
||||
routes: Array[RouteStateRecord],
|
||||
mobile_groups: Array[MobileGroupStateRecord],
|
||||
persons: Array[PersonStateRecord],
|
||||
cohorts: Array[PopulationCohortRecord],
|
||||
polities: Array[PolityStateRecord],
|
||||
diplomatic_relations: Array[DiplomaticRelationRecord]
|
||||
) -> bool:
|
||||
var candidate := RegionalWorldState.new()
|
||||
candidate._world_id = world_id
|
||||
if world_id.is_empty():
|
||||
candidate._add_error("world_id must be non-empty")
|
||||
for source: LocationStateRecord in locations:
|
||||
var record := (
|
||||
LocationStateRecord.from_dictionary(source.to_dictionary()) if source != null else null
|
||||
)
|
||||
if record == null:
|
||||
candidate._add_error("invalid location record")
|
||||
elif not candidate._insert_location(record):
|
||||
pass
|
||||
for source: SettlementStateRecord in settlements:
|
||||
var record := (
|
||||
SettlementStateRecord.from_dictionary(source.to_dictionary())
|
||||
if source != null
|
||||
else null
|
||||
)
|
||||
if record == null:
|
||||
candidate._add_error("invalid settlement record")
|
||||
elif not candidate._insert_settlement(record):
|
||||
pass
|
||||
for source: RouteStateRecord in routes:
|
||||
var record := (
|
||||
RouteStateRecord.from_dictionary(source.to_dictionary()) if source != null else null
|
||||
)
|
||||
if record == null:
|
||||
candidate._add_error("invalid route record")
|
||||
elif not candidate._insert_route(record):
|
||||
pass
|
||||
for source: MobileGroupStateRecord in mobile_groups:
|
||||
var record := (
|
||||
MobileGroupStateRecord.from_dictionary(source.to_dictionary())
|
||||
if source != null
|
||||
else null
|
||||
)
|
||||
if record == null:
|
||||
candidate._add_error("invalid mobile-group record")
|
||||
elif not candidate._insert_mobile_group(record):
|
||||
pass
|
||||
for source: PersonStateRecord in persons:
|
||||
var record := (
|
||||
PersonStateRecord.from_dictionary(source.to_dictionary()) if source != null else null
|
||||
)
|
||||
if record == null:
|
||||
candidate._add_error("invalid person record")
|
||||
elif not candidate._insert_person(record):
|
||||
pass
|
||||
for source: PopulationCohortRecord in cohorts:
|
||||
var record := (
|
||||
PopulationCohortRecord.from_dictionary(source.to_dictionary())
|
||||
if source != null
|
||||
else null
|
||||
)
|
||||
if record == null:
|
||||
candidate._add_error("invalid population-cohort record")
|
||||
elif not candidate._insert_cohort(record):
|
||||
pass
|
||||
for source: PolityStateRecord in polities:
|
||||
var record := (
|
||||
PolityStateRecord.from_dictionary(source.to_dictionary()) if source != null else null
|
||||
)
|
||||
if record == null:
|
||||
candidate._add_error("invalid polity record")
|
||||
elif not candidate._insert_polity(record):
|
||||
pass
|
||||
for source: DiplomaticRelationRecord in diplomatic_relations:
|
||||
var record := (
|
||||
DiplomaticRelationRecord.from_dictionary(source.to_dictionary())
|
||||
if source != null
|
||||
else null
|
||||
)
|
||||
if record == null:
|
||||
candidate._add_error("invalid diplomatic-relation record")
|
||||
elif not candidate._insert_diplomatic_relation(record):
|
||||
pass
|
||||
candidate._validate_cross_references()
|
||||
if not candidate._last_validation_errors.is_empty():
|
||||
candidate._last_validation_errors.sort()
|
||||
_last_validation_errors = candidate._last_validation_errors.duplicate()
|
||||
return false
|
||||
candidate._rebuild_membership_indexes()
|
||||
_adopt(candidate)
|
||||
return true
|
||||
|
||||
|
||||
func restore_from_dictionary(record_data: Dictionary) -> bool:
|
||||
if int(record_data.get("schema_version", -1)) != SCHEMA_VERSION:
|
||||
return _reject("unsupported regional-world schema version")
|
||||
if not (
|
||||
record_data
|
||||
. has_all(
|
||||
[
|
||||
"world_id",
|
||||
"locations",
|
||||
"settlements",
|
||||
"routes",
|
||||
"mobile_groups",
|
||||
"persons",
|
||||
"population_cohorts",
|
||||
"polities",
|
||||
"diplomatic_relations",
|
||||
]
|
||||
)
|
||||
):
|
||||
return _reject("regional-world save is missing required fields")
|
||||
for field: String in [
|
||||
"locations",
|
||||
"settlements",
|
||||
"routes",
|
||||
"mobile_groups",
|
||||
"persons",
|
||||
"population_cohorts",
|
||||
"polities",
|
||||
"diplomatic_relations",
|
||||
]:
|
||||
if not record_data[field] is Array:
|
||||
return _reject("regional-world field '%s' must be an array" % field)
|
||||
var locations: Array[LocationStateRecord] = []
|
||||
for saved_record: Variant in record_data["locations"]:
|
||||
if not saved_record is Dictionary:
|
||||
return _reject("location entry must be a dictionary")
|
||||
var record := LocationStateRecord.from_dictionary(saved_record)
|
||||
if record == null:
|
||||
return _reject("invalid location entry")
|
||||
locations.append(record)
|
||||
var settlements: Array[SettlementStateRecord] = []
|
||||
for saved_record: Variant in record_data["settlements"]:
|
||||
if not saved_record is Dictionary:
|
||||
return _reject("settlement entry must be a dictionary")
|
||||
var record := SettlementStateRecord.from_dictionary(saved_record)
|
||||
if record == null:
|
||||
return _reject("invalid settlement entry")
|
||||
settlements.append(record)
|
||||
var routes: Array[RouteStateRecord] = []
|
||||
for saved_record: Variant in record_data["routes"]:
|
||||
if not saved_record is Dictionary:
|
||||
return _reject("route entry must be a dictionary")
|
||||
var record := RouteStateRecord.from_dictionary(saved_record)
|
||||
if record == null:
|
||||
return _reject("invalid route entry")
|
||||
routes.append(record)
|
||||
var mobile_groups: Array[MobileGroupStateRecord] = []
|
||||
for saved_record: Variant in record_data["mobile_groups"]:
|
||||
if not saved_record is Dictionary:
|
||||
return _reject("mobile-group entry must be a dictionary")
|
||||
var record := MobileGroupStateRecord.from_dictionary(saved_record)
|
||||
if record == null:
|
||||
return _reject("invalid mobile-group entry")
|
||||
mobile_groups.append(record)
|
||||
var persons: Array[PersonStateRecord] = []
|
||||
for saved_record: Variant in record_data["persons"]:
|
||||
if not saved_record is Dictionary:
|
||||
return _reject("person entry must be a dictionary")
|
||||
var record := PersonStateRecord.from_dictionary(saved_record)
|
||||
if record == null:
|
||||
return _reject("invalid person entry")
|
||||
persons.append(record)
|
||||
var cohorts: Array[PopulationCohortRecord] = []
|
||||
for saved_record: Variant in record_data["population_cohorts"]:
|
||||
if not saved_record is Dictionary:
|
||||
return _reject("population-cohort entry must be a dictionary")
|
||||
var record := PopulationCohortRecord.from_dictionary(saved_record)
|
||||
if record == null:
|
||||
return _reject("invalid population-cohort entry")
|
||||
cohorts.append(record)
|
||||
var polities: Array[PolityStateRecord] = []
|
||||
for saved_record: Variant in record_data["polities"]:
|
||||
if not saved_record is Dictionary:
|
||||
return _reject("polity entry must be a dictionary")
|
||||
var record := PolityStateRecord.from_dictionary(saved_record)
|
||||
if record == null:
|
||||
return _reject("invalid polity entry")
|
||||
polities.append(record)
|
||||
var relations: Array[DiplomaticRelationRecord] = []
|
||||
for saved_record: Variant in record_data["diplomatic_relations"]:
|
||||
if not saved_record is Dictionary:
|
||||
return _reject("diplomatic-relation entry must be a dictionary")
|
||||
var record := DiplomaticRelationRecord.from_dictionary(saved_record)
|
||||
if record == null:
|
||||
return _reject("invalid diplomatic-relation entry")
|
||||
relations.append(record)
|
||||
return replace_all(
|
||||
StringName(record_data["world_id"]),
|
||||
locations,
|
||||
settlements,
|
||||
routes,
|
||||
mobile_groups,
|
||||
persons,
|
||||
cohorts,
|
||||
polities,
|
||||
relations
|
||||
)
|
||||
|
||||
|
||||
func get_world_id() -> StringName:
|
||||
return _world_id
|
||||
|
||||
|
||||
func has_global_id(entity_id: StringName) -> bool:
|
||||
return _global_entity_types.has(entity_id)
|
||||
|
||||
|
||||
func get_entity_type(entity_id: StringName) -> StringName:
|
||||
return StringName(_global_entity_types.get(entity_id, &""))
|
||||
|
||||
|
||||
func get_location(location_id: StringName) -> LocationStateRecord:
|
||||
return _locations.get(location_id) as LocationStateRecord
|
||||
|
||||
|
||||
func get_settlement(settlement_id: StringName) -> SettlementStateRecord:
|
||||
return _settlements.get(settlement_id) as SettlementStateRecord
|
||||
|
||||
|
||||
func get_route(route_id: StringName) -> RouteStateRecord:
|
||||
return _routes.get(route_id) as RouteStateRecord
|
||||
|
||||
|
||||
func get_mobile_group(group_id: StringName) -> MobileGroupStateRecord:
|
||||
return _mobile_groups.get(group_id) as MobileGroupStateRecord
|
||||
|
||||
|
||||
func get_person(person_id: StringName) -> PersonStateRecord:
|
||||
return _persons.get(person_id) as PersonStateRecord
|
||||
|
||||
|
||||
func get_population_cohort(cohort_id: StringName) -> PopulationCohortRecord:
|
||||
return _cohorts.get(cohort_id) as PopulationCohortRecord
|
||||
|
||||
|
||||
func get_polity(polity_id: StringName) -> PolityStateRecord:
|
||||
return _polities.get(polity_id) as PolityStateRecord
|
||||
|
||||
|
||||
func get_diplomatic_relation(relation_id: StringName) -> DiplomaticRelationRecord:
|
||||
return _diplomatic_relations.get(relation_id) as DiplomaticRelationRecord
|
||||
|
||||
|
||||
func get_diplomatic_relation_between(
|
||||
first_polity_id: StringName, second_polity_id: StringName
|
||||
) -> DiplomaticRelationRecord:
|
||||
var pair_key := RegionalStateSupport.pair_key(first_polity_id, second_polity_id)
|
||||
var relation_id: StringName = _diplomacy_by_pair.get(pair_key, &"")
|
||||
return get_diplomatic_relation(relation_id)
|
||||
|
||||
|
||||
func get_all_locations() -> Array[LocationStateRecord]:
|
||||
var records: Array[LocationStateRecord] = []
|
||||
for id: StringName in _sorted_ids(_locations):
|
||||
records.append(get_location(id))
|
||||
return records
|
||||
|
||||
|
||||
func get_all_settlements() -> Array[SettlementStateRecord]:
|
||||
var records: Array[SettlementStateRecord] = []
|
||||
for id: StringName in _sorted_ids(_settlements):
|
||||
records.append(get_settlement(id))
|
||||
return records
|
||||
|
||||
|
||||
func get_all_routes() -> Array[RouteStateRecord]:
|
||||
var records: Array[RouteStateRecord] = []
|
||||
for id: StringName in _sorted_ids(_routes):
|
||||
records.append(get_route(id))
|
||||
return records
|
||||
|
||||
|
||||
func get_all_mobile_groups() -> Array[MobileGroupStateRecord]:
|
||||
var records: Array[MobileGroupStateRecord] = []
|
||||
for id: StringName in _sorted_ids(_mobile_groups):
|
||||
records.append(get_mobile_group(id))
|
||||
return records
|
||||
|
||||
|
||||
func get_all_persons() -> Array[PersonStateRecord]:
|
||||
var records: Array[PersonStateRecord] = []
|
||||
for id: StringName in _sorted_ids(_persons):
|
||||
records.append(get_person(id))
|
||||
return records
|
||||
|
||||
|
||||
func get_all_population_cohorts() -> Array[PopulationCohortRecord]:
|
||||
var records: Array[PopulationCohortRecord] = []
|
||||
for id: StringName in _sorted_ids(_cohorts):
|
||||
records.append(get_population_cohort(id))
|
||||
return records
|
||||
|
||||
|
||||
func get_all_polities() -> Array[PolityStateRecord]:
|
||||
var records: Array[PolityStateRecord] = []
|
||||
for id: StringName in _sorted_ids(_polities):
|
||||
records.append(get_polity(id))
|
||||
return records
|
||||
|
||||
|
||||
func get_all_diplomatic_relations() -> Array[DiplomaticRelationRecord]:
|
||||
var records: Array[DiplomaticRelationRecord] = []
|
||||
for id: StringName in _sorted_ids(_diplomatic_relations):
|
||||
records.append(get_diplomatic_relation(id))
|
||||
return records
|
||||
|
||||
|
||||
func get_routes_for_location(location_id: StringName) -> Array[RouteStateRecord]:
|
||||
var records: Array[RouteStateRecord] = []
|
||||
for route_id: StringName in _index_ids(_route_ids_by_location, location_id):
|
||||
records.append(get_route(route_id))
|
||||
return records
|
||||
|
||||
|
||||
func get_mobile_groups_at_location(location_id: StringName) -> Array[MobileGroupStateRecord]:
|
||||
var records: Array[MobileGroupStateRecord] = []
|
||||
for group_id: StringName in _index_ids(_group_ids_by_location, location_id):
|
||||
records.append(get_mobile_group(group_id))
|
||||
return records
|
||||
|
||||
|
||||
func get_mobile_groups_on_route(route_id: StringName) -> Array[MobileGroupStateRecord]:
|
||||
var records: Array[MobileGroupStateRecord] = []
|
||||
for group_id: StringName in _index_ids(_group_ids_by_route, route_id):
|
||||
records.append(get_mobile_group(group_id))
|
||||
return records
|
||||
|
||||
|
||||
func get_persons_at_location(location_id: StringName) -> Array[PersonStateRecord]:
|
||||
var records: Array[PersonStateRecord] = []
|
||||
for person_id: StringName in _index_ids(_person_ids_by_location, location_id):
|
||||
records.append(get_person(person_id))
|
||||
return records
|
||||
|
||||
|
||||
func get_persons_in_group(group_id: StringName) -> Array[PersonStateRecord]:
|
||||
var records: Array[PersonStateRecord] = []
|
||||
for person_id: StringName in _index_ids(_person_ids_by_group, group_id):
|
||||
records.append(get_person(person_id))
|
||||
return records
|
||||
|
||||
|
||||
func get_cohorts_at_location(location_id: StringName) -> Array[PopulationCohortRecord]:
|
||||
var records: Array[PopulationCohortRecord] = []
|
||||
for cohort_id: StringName in _index_ids(_cohort_ids_by_location, location_id):
|
||||
records.append(get_population_cohort(cohort_id))
|
||||
return records
|
||||
|
||||
|
||||
func get_cohorts_in_group(group_id: StringName) -> Array[PopulationCohortRecord]:
|
||||
var records: Array[PopulationCohortRecord] = []
|
||||
for cohort_id: StringName in _index_ids(_cohort_ids_by_group, group_id):
|
||||
records.append(get_population_cohort(cohort_id))
|
||||
return records
|
||||
|
||||
|
||||
func get_settlements_at_location(location_id: StringName) -> Array[SettlementStateRecord]:
|
||||
var records: Array[SettlementStateRecord] = []
|
||||
for settlement_id: StringName in _index_ids(_settlement_ids_by_location, location_id):
|
||||
records.append(get_settlement(settlement_id))
|
||||
return records
|
||||
|
||||
|
||||
func get_last_validation_errors() -> Array[String]:
|
||||
return _last_validation_errors.duplicate()
|
||||
|
||||
|
||||
func replace_settlement_and_mobile_group(
|
||||
settlement: SettlementStateRecord, mobile_group: MobileGroupStateRecord
|
||||
) -> bool:
|
||||
if (
|
||||
settlement == null
|
||||
or mobile_group == null
|
||||
or not _settlements.has(settlement.get_settlement_id())
|
||||
or not _mobile_groups.has(mobile_group.get_group_id())
|
||||
):
|
||||
return false
|
||||
var settlements := get_all_settlements()
|
||||
for index in range(settlements.size()):
|
||||
if settlements[index].get_settlement_id() == settlement.get_settlement_id():
|
||||
settlements[index] = settlement
|
||||
break
|
||||
var mobile_groups := get_all_mobile_groups()
|
||||
for index in range(mobile_groups.size()):
|
||||
if mobile_groups[index].get_group_id() == mobile_group.get_group_id():
|
||||
mobile_groups[index] = mobile_group
|
||||
break
|
||||
return replace_all(
|
||||
_world_id,
|
||||
get_all_locations(),
|
||||
settlements,
|
||||
get_all_routes(),
|
||||
mobile_groups,
|
||||
get_all_persons(),
|
||||
get_all_population_cohorts(),
|
||||
get_all_polities(),
|
||||
get_all_diplomatic_relations()
|
||||
)
|
||||
|
||||
|
||||
func to_dictionary() -> Dictionary:
|
||||
var locations: Array[Dictionary] = []
|
||||
for id: StringName in _sorted_ids(_locations):
|
||||
locations.append(get_location(id).to_dictionary())
|
||||
var settlements: Array[Dictionary] = []
|
||||
for id: StringName in _sorted_ids(_settlements):
|
||||
settlements.append(get_settlement(id).to_dictionary())
|
||||
var routes: Array[Dictionary] = []
|
||||
for id: StringName in _sorted_ids(_routes):
|
||||
routes.append(get_route(id).to_dictionary())
|
||||
var mobile_groups: Array[Dictionary] = []
|
||||
for id: StringName in _sorted_ids(_mobile_groups):
|
||||
mobile_groups.append(get_mobile_group(id).to_dictionary())
|
||||
var persons: Array[Dictionary] = []
|
||||
for id: StringName in _sorted_ids(_persons):
|
||||
persons.append(get_person(id).to_dictionary())
|
||||
var cohorts: Array[Dictionary] = []
|
||||
for id: StringName in _sorted_ids(_cohorts):
|
||||
cohorts.append(get_population_cohort(id).to_dictionary())
|
||||
var polities: Array[Dictionary] = []
|
||||
for id: StringName in _sorted_ids(_polities):
|
||||
polities.append(get_polity(id).to_dictionary())
|
||||
var diplomatic_relations: Array[Dictionary] = []
|
||||
for id: StringName in _sorted_ids(_diplomatic_relations):
|
||||
diplomatic_relations.append(get_diplomatic_relation(id).to_dictionary())
|
||||
return {
|
||||
"schema_version": SCHEMA_VERSION,
|
||||
"world_id": String(_world_id),
|
||||
"locations": locations,
|
||||
"settlements": settlements,
|
||||
"routes": routes,
|
||||
"mobile_groups": mobile_groups,
|
||||
"persons": persons,
|
||||
"population_cohorts": cohorts,
|
||||
"polities": polities,
|
||||
"diplomatic_relations": diplomatic_relations,
|
||||
}
|
||||
|
||||
|
||||
func checksum() -> String:
|
||||
return JSON.stringify(to_dictionary()).sha256_text()
|
||||
|
||||
|
||||
func _insert_location(record: LocationStateRecord) -> bool:
|
||||
var id := record.get_location_id()
|
||||
if not _claim_global_id(id, &"location"):
|
||||
return false
|
||||
_locations[id] = record
|
||||
return true
|
||||
|
||||
|
||||
func _insert_settlement(record: SettlementStateRecord) -> bool:
|
||||
var id := record.get_settlement_id()
|
||||
if not _claim_global_id(id, &"settlement"):
|
||||
return false
|
||||
_settlements[id] = record
|
||||
return true
|
||||
|
||||
|
||||
func _insert_route(record: RouteStateRecord) -> bool:
|
||||
var id := record.get_route_id()
|
||||
if not _claim_global_id(id, &"route"):
|
||||
return false
|
||||
_routes[id] = record
|
||||
return true
|
||||
|
||||
|
||||
func _insert_mobile_group(record: MobileGroupStateRecord) -> bool:
|
||||
var id := record.get_group_id()
|
||||
if not _claim_global_id(id, &"mobile_group"):
|
||||
return false
|
||||
_mobile_groups[id] = record
|
||||
return true
|
||||
|
||||
|
||||
func _insert_person(record: PersonStateRecord) -> bool:
|
||||
var id := record.get_person_id()
|
||||
if not _claim_global_id(id, &"person"):
|
||||
return false
|
||||
_persons[id] = record
|
||||
return true
|
||||
|
||||
|
||||
func _insert_cohort(record: PopulationCohortRecord) -> bool:
|
||||
var id := record.get_cohort_id()
|
||||
if not _claim_global_id(id, &"population_cohort"):
|
||||
return false
|
||||
_cohorts[id] = record
|
||||
return true
|
||||
|
||||
|
||||
func _insert_polity(record: PolityStateRecord) -> bool:
|
||||
var id := record.get_polity_id()
|
||||
if not _claim_global_id(id, &"polity"):
|
||||
return false
|
||||
_polities[id] = record
|
||||
return true
|
||||
|
||||
|
||||
func _insert_diplomatic_relation(record: DiplomaticRelationRecord) -> bool:
|
||||
var id := record.get_relation_id()
|
||||
if not _claim_global_id(id, &"diplomatic_relation"):
|
||||
return false
|
||||
var pair_key := record.get_pair_key()
|
||||
if _diplomacy_by_pair.has(pair_key):
|
||||
_add_error("duplicate diplomatic relation for pair '%s'" % pair_key)
|
||||
return false
|
||||
_diplomatic_relations[id] = record
|
||||
_diplomacy_by_pair[pair_key] = id
|
||||
return true
|
||||
|
||||
|
||||
func _claim_global_id(id: StringName, entity_type: StringName) -> bool:
|
||||
if _global_entity_types.has(id):
|
||||
_add_error(
|
||||
"duplicate global ID '%s' (%s and %s)" % [id, _global_entity_types[id], entity_type]
|
||||
)
|
||||
return false
|
||||
_global_entity_types[id] = entity_type
|
||||
return true
|
||||
|
||||
|
||||
func _validate_cross_references() -> void:
|
||||
for location: LocationStateRecord in _locations.values():
|
||||
if location.get_world_id() != _world_id:
|
||||
_add_error("location '%s' belongs to another world" % location.get_location_id())
|
||||
for settlement: SettlementStateRecord in _settlements.values():
|
||||
_require_id(
|
||||
_locations,
|
||||
settlement.get_location_id(),
|
||||
"settlement location",
|
||||
settlement.get_settlement_id()
|
||||
)
|
||||
_require_id(
|
||||
_polities,
|
||||
settlement.get_polity_id(),
|
||||
"settlement polity",
|
||||
settlement.get_settlement_id()
|
||||
)
|
||||
for route: RouteStateRecord in _routes.values():
|
||||
_require_id(
|
||||
_locations, route.get_from_location_id(), "route endpoint", route.get_route_id()
|
||||
)
|
||||
_require_id(_locations, route.get_to_location_id(), "route endpoint", route.get_route_id())
|
||||
for group: MobileGroupStateRecord in _mobile_groups.values():
|
||||
_validate_mobile_group(group)
|
||||
for person: PersonStateRecord in _persons.values():
|
||||
_validate_person(person)
|
||||
for cohort: PopulationCohortRecord in _cohorts.values():
|
||||
_validate_cohort(cohort)
|
||||
for polity: PolityStateRecord in _polities.values():
|
||||
_validate_polity(polity)
|
||||
for relation: DiplomaticRelationRecord in _diplomatic_relations.values():
|
||||
_require_id(
|
||||
_polities,
|
||||
relation.get_first_polity_id(),
|
||||
"diplomatic polity",
|
||||
relation.get_relation_id()
|
||||
)
|
||||
_require_id(
|
||||
_polities,
|
||||
relation.get_second_polity_id(),
|
||||
"diplomatic polity",
|
||||
relation.get_relation_id()
|
||||
)
|
||||
_validate_group_membership_ownership()
|
||||
|
||||
|
||||
func _validate_mobile_group(group: MobileGroupStateRecord) -> void:
|
||||
_require_id(_polities, group.get_polity_id(), "mobile-group polity", group.get_group_id())
|
||||
if group.is_at_location():
|
||||
var address := group.get_location_address()
|
||||
if address == null or not _locations.has(address.get_location_id()):
|
||||
_add_error("mobile group '%s' has an orphan location" % group.get_group_id())
|
||||
elif not RegionalStateSupport.address_matches(
|
||||
group.data["location_address"], _world_id, address.get_location_id()
|
||||
):
|
||||
_add_error(
|
||||
"mobile group '%s' has a cross-world location address" % group.get_group_id()
|
||||
)
|
||||
return
|
||||
var route := get_route(group.get_route_id())
|
||||
if route == null:
|
||||
_add_error("mobile group '%s' has an orphan route" % group.get_group_id())
|
||||
return
|
||||
if not route.connects(group.get_departure_location_id(), group.get_arrival_location_id()):
|
||||
_add_error(
|
||||
"mobile group '%s' uses invalid route direction/endpoints" % group.get_group_id()
|
||||
)
|
||||
if group.get_arrival_tick() - group.get_departure_tick() != route.get_travel_ticks():
|
||||
_add_error("mobile group '%s' journey duration does not match route" % group.get_group_id())
|
||||
|
||||
|
||||
func _validate_person(person: PersonStateRecord) -> void:
|
||||
_require_id(_polities, person.get_polity_id(), "person polity", person.get_person_id())
|
||||
_require_id(_locations, person.get_home_location_id(), "person home", person.get_person_id())
|
||||
if person.is_at_location():
|
||||
var address := person.get_location_address()
|
||||
if address == null or not _locations.has(address.get_location_id()):
|
||||
_add_error("person '%s' has an orphan location" % person.get_person_id())
|
||||
elif not RegionalStateSupport.address_matches(
|
||||
person.data["location_address"], _world_id, address.get_location_id()
|
||||
):
|
||||
_add_error("person '%s' has a cross-world location address" % person.get_person_id())
|
||||
else:
|
||||
_require_id(
|
||||
_mobile_groups,
|
||||
person.get_mobile_group_id(),
|
||||
"person mobile group",
|
||||
person.get_person_id()
|
||||
)
|
||||
|
||||
|
||||
func _validate_cohort(cohort: PopulationCohortRecord) -> void:
|
||||
_require_id(_polities, cohort.get_polity_id(), "cohort polity", cohort.get_cohort_id())
|
||||
if cohort.is_at_location():
|
||||
_require_id(_locations, cohort.get_location_id(), "cohort location", cohort.get_cohort_id())
|
||||
else:
|
||||
_require_id(
|
||||
_mobile_groups,
|
||||
cohort.get_mobile_group_id(),
|
||||
"cohort mobile group",
|
||||
cohort.get_cohort_id()
|
||||
)
|
||||
|
||||
|
||||
func _validate_polity(polity: PolityStateRecord) -> void:
|
||||
_require_id(
|
||||
_locations, polity.get_capital_location_id(), "polity capital", polity.get_polity_id()
|
||||
)
|
||||
for settlement_id: StringName in polity.get_settlement_ids():
|
||||
var settlement := get_settlement(settlement_id)
|
||||
if settlement == null:
|
||||
_add_error(
|
||||
"polity '%s' lists orphan settlement '%s'" % [polity.get_polity_id(), settlement_id]
|
||||
)
|
||||
elif settlement.get_polity_id() != polity.get_polity_id():
|
||||
_add_error("settlement '%s' belongs to another polity" % settlement_id)
|
||||
for settlement: SettlementStateRecord in _settlements.values():
|
||||
if (
|
||||
settlement.get_polity_id() == polity.get_polity_id()
|
||||
and settlement.get_settlement_id() not in polity.get_settlement_ids()
|
||||
):
|
||||
_add_error(
|
||||
(
|
||||
"polity '%s' omits settlement '%s'"
|
||||
% [polity.get_polity_id(), settlement.get_settlement_id()]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
func _validate_group_membership_ownership() -> void:
|
||||
var person_owner: Dictionary = {}
|
||||
var cohort_owner: Dictionary = {}
|
||||
for group: MobileGroupStateRecord in _mobile_groups.values():
|
||||
for person_id: StringName in group.get_detailed_member_ids():
|
||||
var person := get_person(person_id)
|
||||
if person == null:
|
||||
_add_error(
|
||||
"mobile group '%s' lists orphan person '%s'" % [group.get_group_id(), person_id]
|
||||
)
|
||||
continue
|
||||
if person_owner.has(person_id):
|
||||
_add_error("person '%s' is listed by multiple mobile groups" % person_id)
|
||||
person_owner[person_id] = group.get_group_id()
|
||||
if person.get_mobile_group_id() != group.get_group_id():
|
||||
_add_error("person '%s' has non-reciprocal mobile-group membership" % person_id)
|
||||
if person.get_polity_id() != group.get_polity_id():
|
||||
_add_error("person '%s' and mobile group have different polities" % person_id)
|
||||
for cohort_key: Variant in group.get_cohort_counts():
|
||||
var cohort_id := StringName(cohort_key)
|
||||
var cohort := get_population_cohort(cohort_id)
|
||||
if cohort == null:
|
||||
_add_error(
|
||||
"mobile group '%s' lists orphan cohort '%s'" % [group.get_group_id(), cohort_id]
|
||||
)
|
||||
continue
|
||||
if cohort_owner.has(cohort_id):
|
||||
_add_error("cohort '%s' is listed by multiple mobile groups" % cohort_id)
|
||||
cohort_owner[cohort_id] = group.get_group_id()
|
||||
if cohort.get_mobile_group_id() != group.get_group_id():
|
||||
_add_error("cohort '%s' has non-reciprocal mobile-group membership" % cohort_id)
|
||||
if int(group.get_cohort_counts()[cohort_key]) != cohort.get_count():
|
||||
_add_error("cohort '%s' count disagrees with its mobile group" % cohort_id)
|
||||
if cohort.get_polity_id() != group.get_polity_id():
|
||||
_add_error("cohort '%s' and mobile group have different polities" % cohort_id)
|
||||
for person: PersonStateRecord in _persons.values():
|
||||
if person.is_in_mobile_group() and not person_owner.has(person.get_person_id()):
|
||||
_add_error("person '%s' is omitted by its mobile group" % person.get_person_id())
|
||||
for cohort: PopulationCohortRecord in _cohorts.values():
|
||||
if cohort.is_in_mobile_group() and not cohort_owner.has(cohort.get_cohort_id()):
|
||||
_add_error("cohort '%s' is omitted by its mobile group" % cohort.get_cohort_id())
|
||||
|
||||
|
||||
func _rebuild_membership_indexes() -> void:
|
||||
for route: RouteStateRecord in _routes.values():
|
||||
_index_append(_route_ids_by_location, route.get_from_location_id(), route.get_route_id())
|
||||
_index_append(_route_ids_by_location, route.get_to_location_id(), route.get_route_id())
|
||||
for settlement: SettlementStateRecord in _settlements.values():
|
||||
_index_append(
|
||||
_settlement_ids_by_location,
|
||||
settlement.get_location_id(),
|
||||
settlement.get_settlement_id()
|
||||
)
|
||||
for group: MobileGroupStateRecord in _mobile_groups.values():
|
||||
if group.is_at_location():
|
||||
_index_append(
|
||||
_group_ids_by_location, group.get_current_location_id(), group.get_group_id()
|
||||
)
|
||||
else:
|
||||
_index_append(_group_ids_by_route, group.get_route_id(), group.get_group_id())
|
||||
for person: PersonStateRecord in _persons.values():
|
||||
if person.is_at_location():
|
||||
_index_append(
|
||||
_person_ids_by_location,
|
||||
person.get_location_address().get_location_id(),
|
||||
person.get_person_id()
|
||||
)
|
||||
else:
|
||||
_index_append(
|
||||
_person_ids_by_group, person.get_mobile_group_id(), person.get_person_id()
|
||||
)
|
||||
for cohort: PopulationCohortRecord in _cohorts.values():
|
||||
if cohort.is_at_location():
|
||||
_index_append(_cohort_ids_by_location, cohort.get_location_id(), cohort.get_cohort_id())
|
||||
else:
|
||||
_index_append(
|
||||
_cohort_ids_by_group, cohort.get_mobile_group_id(), cohort.get_cohort_id()
|
||||
)
|
||||
|
||||
|
||||
func _require_id(index: Dictionary, id: StringName, label: String, owner_id: StringName) -> void:
|
||||
if not index.has(id):
|
||||
_add_error("%s '%s' references missing ID '%s'" % [label, owner_id, id])
|
||||
|
||||
|
||||
func _index_append(index: Dictionary, key: StringName, id: StringName) -> void:
|
||||
var ids: Array[StringName] = index.get(key, [] as Array[StringName])
|
||||
ids.append(id)
|
||||
ids.sort_custom(_compare_ids)
|
||||
index[key] = ids
|
||||
|
||||
|
||||
func _index_ids(index: Dictionary, key: StringName) -> Array[StringName]:
|
||||
var ids: Array[StringName] = index.get(key, [] as Array[StringName])
|
||||
return ids.duplicate()
|
||||
|
||||
|
||||
func _sorted_ids(index: Dictionary) -> Array[StringName]:
|
||||
var ids: Array[StringName] = []
|
||||
for id: Variant in index:
|
||||
ids.append(StringName(id))
|
||||
ids.sort_custom(_compare_ids)
|
||||
return ids
|
||||
|
||||
|
||||
static func _compare_ids(first: StringName, second: StringName) -> bool:
|
||||
return String(first) < String(second)
|
||||
|
||||
|
||||
func _reject(message: String) -> bool:
|
||||
_last_validation_errors = [message]
|
||||
return false
|
||||
|
||||
|
||||
func _add_error(message: String) -> void:
|
||||
_last_validation_errors.append(message)
|
||||
|
||||
|
||||
func _adopt(source: RegionalWorldState) -> void:
|
||||
_world_id = source._world_id
|
||||
_locations = source._locations
|
||||
_settlements = source._settlements
|
||||
_routes = source._routes
|
||||
_mobile_groups = source._mobile_groups
|
||||
_persons = source._persons
|
||||
_cohorts = source._cohorts
|
||||
_polities = source._polities
|
||||
_diplomatic_relations = source._diplomatic_relations
|
||||
_diplomacy_by_pair = source._diplomacy_by_pair
|
||||
_route_ids_by_location = source._route_ids_by_location
|
||||
_group_ids_by_location = source._group_ids_by_location
|
||||
_group_ids_by_route = source._group_ids_by_route
|
||||
_person_ids_by_location = source._person_ids_by_location
|
||||
_person_ids_by_group = source._person_ids_by_group
|
||||
_cohort_ids_by_location = source._cohort_ids_by_location
|
||||
_cohort_ids_by_group = source._cohort_ids_by_group
|
||||
_settlement_ids_by_location = source._settlement_ids_by_location
|
||||
_global_entity_types = source._global_entity_types
|
||||
_last_validation_errors.clear()
|
||||
@@ -0,0 +1 @@
|
||||
uid://dkjcccwgeb82g
|
||||
@@ -0,0 +1,129 @@
|
||||
class_name DiplomaticRelationRecord
|
||||
extends RefCounted
|
||||
|
||||
const SCHEMA_VERSION := 1
|
||||
|
||||
var data: Dictionary
|
||||
|
||||
|
||||
func _init(record_data: Dictionary = {}) -> void:
|
||||
data = record_data.duplicate(true)
|
||||
|
||||
|
||||
static func create(
|
||||
relation_id: StringName,
|
||||
first_polity_id: StringName,
|
||||
second_polity_id: StringName,
|
||||
stance_id: StringName,
|
||||
trust: float,
|
||||
started_tick: int,
|
||||
history_event_ids: Array = []
|
||||
) -> DiplomaticRelationRecord:
|
||||
var first := String(first_polity_id)
|
||||
var second := String(second_polity_id)
|
||||
if second < first:
|
||||
var swapped := first
|
||||
first = second
|
||||
second = swapped
|
||||
var normalized_history: Variant = RegionalStateSupport.normalize_history_event_ids(
|
||||
history_event_ids
|
||||
)
|
||||
if normalized_history == null:
|
||||
return null
|
||||
var record := (
|
||||
DiplomaticRelationRecord
|
||||
. new(
|
||||
{
|
||||
"schema_version": SCHEMA_VERSION,
|
||||
"relation_id": String(relation_id),
|
||||
"first_polity_id": first,
|
||||
"second_polity_id": second,
|
||||
"stance_id": String(stance_id),
|
||||
"trust": trust,
|
||||
"started_tick": started_tick,
|
||||
"history_event_ids": normalized_history,
|
||||
}
|
||||
)
|
||||
)
|
||||
return record if record.is_valid() else null
|
||||
|
||||
|
||||
static func from_dictionary(record_data: Dictionary) -> DiplomaticRelationRecord:
|
||||
if int(record_data.get("schema_version", -1)) != SCHEMA_VERSION:
|
||||
return null
|
||||
if not (
|
||||
record_data
|
||||
. has_all(
|
||||
[
|
||||
"relation_id",
|
||||
"first_polity_id",
|
||||
"second_polity_id",
|
||||
"stance_id",
|
||||
"trust",
|
||||
"started_tick",
|
||||
"history_event_ids",
|
||||
]
|
||||
)
|
||||
):
|
||||
return null
|
||||
if not record_data["history_event_ids"] is Array:
|
||||
return null
|
||||
return create(
|
||||
StringName(record_data["relation_id"]),
|
||||
StringName(record_data["first_polity_id"]),
|
||||
StringName(record_data["second_polity_id"]),
|
||||
StringName(record_data["stance_id"]),
|
||||
float(record_data["trust"]),
|
||||
int(record_data["started_tick"]),
|
||||
record_data["history_event_ids"]
|
||||
)
|
||||
|
||||
|
||||
func is_valid() -> bool:
|
||||
return (
|
||||
not get_relation_id().is_empty()
|
||||
and not get_first_polity_id().is_empty()
|
||||
and not get_second_polity_id().is_empty()
|
||||
and String(get_first_polity_id()) < String(get_second_polity_id())
|
||||
and not get_stance_id().is_empty()
|
||||
and is_finite(get_trust())
|
||||
and get_trust() >= -1.0
|
||||
and get_trust() <= 1.0
|
||||
and get_started_tick() >= 0
|
||||
)
|
||||
|
||||
|
||||
func get_relation_id() -> StringName:
|
||||
return StringName(data["relation_id"])
|
||||
|
||||
|
||||
func get_first_polity_id() -> StringName:
|
||||
return StringName(data["first_polity_id"])
|
||||
|
||||
|
||||
func get_second_polity_id() -> StringName:
|
||||
return StringName(data["second_polity_id"])
|
||||
|
||||
|
||||
func get_stance_id() -> StringName:
|
||||
return StringName(data["stance_id"])
|
||||
|
||||
|
||||
func get_trust() -> float:
|
||||
return float(data["trust"])
|
||||
|
||||
|
||||
func get_started_tick() -> int:
|
||||
return int(data["started_tick"])
|
||||
|
||||
|
||||
func get_history_event_ids() -> Array[int]:
|
||||
return RegionalStateSupport.copy_int_array(data["history_event_ids"])
|
||||
|
||||
|
||||
func get_pair_key() -> String:
|
||||
return RegionalStateSupport.pair_key(get_first_polity_id(), get_second_polity_id())
|
||||
|
||||
|
||||
func to_dictionary() -> Dictionary:
|
||||
return data.duplicate(true)
|
||||
@@ -0,0 +1 @@
|
||||
uid://cd7j3wipdurdt
|
||||
@@ -0,0 +1,110 @@
|
||||
class_name LocationStateRecord
|
||||
extends RefCounted
|
||||
|
||||
const SCHEMA_VERSION := 1
|
||||
|
||||
var data: Dictionary
|
||||
|
||||
|
||||
func _init(record_data: Dictionary = {}) -> void:
|
||||
data = record_data.duplicate(true)
|
||||
|
||||
|
||||
static func create(
|
||||
location_id: StringName,
|
||||
world_id: StringName,
|
||||
location_type: StringName,
|
||||
display_name: String,
|
||||
address: SpatialAddress,
|
||||
history_event_ids: Array = []
|
||||
) -> LocationStateRecord:
|
||||
var normalized_history: Variant = RegionalStateSupport.normalize_history_event_ids(
|
||||
history_event_ids
|
||||
)
|
||||
var normalized_address: Variant = RegionalStateSupport.normalize_address(address)
|
||||
if normalized_history == null or normalized_address == null:
|
||||
return null
|
||||
var record := (
|
||||
LocationStateRecord
|
||||
. new(
|
||||
{
|
||||
"schema_version": SCHEMA_VERSION,
|
||||
"location_id": String(location_id),
|
||||
"world_id": String(world_id),
|
||||
"location_type": String(location_type),
|
||||
"display_name": display_name,
|
||||
"address": normalized_address,
|
||||
"history_event_ids": normalized_history,
|
||||
}
|
||||
)
|
||||
)
|
||||
return record if record.is_valid() else null
|
||||
|
||||
|
||||
static func from_dictionary(record_data: Dictionary) -> LocationStateRecord:
|
||||
if int(record_data.get("schema_version", -1)) != SCHEMA_VERSION:
|
||||
return null
|
||||
if not (
|
||||
record_data
|
||||
. has_all(
|
||||
[
|
||||
"location_id",
|
||||
"world_id",
|
||||
"location_type",
|
||||
"display_name",
|
||||
"address",
|
||||
"history_event_ids",
|
||||
]
|
||||
)
|
||||
):
|
||||
return null
|
||||
if not record_data["address"] is Dictionary or not record_data["history_event_ids"] is Array:
|
||||
return null
|
||||
return create(
|
||||
StringName(record_data["location_id"]),
|
||||
StringName(record_data["world_id"]),
|
||||
StringName(record_data["location_type"]),
|
||||
String(record_data["display_name"]),
|
||||
SpatialAddress.from_dictionary(record_data["address"]),
|
||||
record_data["history_event_ids"]
|
||||
)
|
||||
|
||||
|
||||
func is_valid() -> bool:
|
||||
return (
|
||||
not get_location_id().is_empty()
|
||||
and not get_world_id().is_empty()
|
||||
and not get_location_type().is_empty()
|
||||
and not get_display_name().is_empty()
|
||||
and RegionalStateSupport.address_matches(
|
||||
data.get("address"), get_world_id(), get_location_id()
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
func get_location_id() -> StringName:
|
||||
return StringName(data["location_id"])
|
||||
|
||||
|
||||
func get_world_id() -> StringName:
|
||||
return StringName(data["world_id"])
|
||||
|
||||
|
||||
func get_location_type() -> StringName:
|
||||
return StringName(data["location_type"])
|
||||
|
||||
|
||||
func get_display_name() -> String:
|
||||
return String(data["display_name"])
|
||||
|
||||
|
||||
func get_address() -> SpatialAddress:
|
||||
return RegionalStateSupport.address_from_data(data["address"])
|
||||
|
||||
|
||||
func get_history_event_ids() -> Array[int]:
|
||||
return RegionalStateSupport.copy_int_array(data["history_event_ids"])
|
||||
|
||||
|
||||
func to_dictionary() -> Dictionary:
|
||||
return data.duplicate(true)
|
||||
@@ -0,0 +1 @@
|
||||
uid://cu05dxii51mnv
|
||||
@@ -0,0 +1,406 @@
|
||||
class_name MobileGroupStateRecord
|
||||
extends RefCounted
|
||||
|
||||
const SCHEMA_VERSION := 1
|
||||
|
||||
var data: Dictionary
|
||||
|
||||
|
||||
func _init(record_data: Dictionary = {}) -> void:
|
||||
data = record_data.duplicate(true)
|
||||
|
||||
|
||||
static func create_at_location(
|
||||
group_id: StringName,
|
||||
group_type: StringName,
|
||||
display_name: String,
|
||||
polity_id: StringName,
|
||||
address: SpatialAddress,
|
||||
detailed_member_ids: Array = [],
|
||||
cohort_counts: Dictionary = {},
|
||||
cargo_ledger: Dictionary = {},
|
||||
cargo_capacity: float = 0.0,
|
||||
last_cargo_event_id: int = -1,
|
||||
history_event_ids: Array = []
|
||||
) -> MobileGroupStateRecord:
|
||||
return _create(
|
||||
group_id,
|
||||
group_type,
|
||||
display_name,
|
||||
polity_id,
|
||||
address,
|
||||
&"",
|
||||
&"",
|
||||
&"",
|
||||
-1,
|
||||
-1,
|
||||
detailed_member_ids,
|
||||
cohort_counts,
|
||||
cargo_ledger,
|
||||
cargo_capacity,
|
||||
last_cargo_event_id,
|
||||
history_event_ids
|
||||
)
|
||||
|
||||
|
||||
static func create_on_route(
|
||||
group_id: StringName,
|
||||
group_type: StringName,
|
||||
display_name: String,
|
||||
polity_id: StringName,
|
||||
route_id: StringName,
|
||||
departure_location_id: StringName,
|
||||
arrival_location_id: StringName,
|
||||
departure_tick: int,
|
||||
arrival_tick: int,
|
||||
detailed_member_ids: Array = [],
|
||||
cohort_counts: Dictionary = {},
|
||||
cargo_ledger: Dictionary = {},
|
||||
cargo_capacity: float = 0.0,
|
||||
last_cargo_event_id: int = -1,
|
||||
history_event_ids: Array = []
|
||||
) -> MobileGroupStateRecord:
|
||||
return _create(
|
||||
group_id,
|
||||
group_type,
|
||||
display_name,
|
||||
polity_id,
|
||||
null,
|
||||
route_id,
|
||||
departure_location_id,
|
||||
arrival_location_id,
|
||||
departure_tick,
|
||||
arrival_tick,
|
||||
detailed_member_ids,
|
||||
cohort_counts,
|
||||
cargo_ledger,
|
||||
cargo_capacity,
|
||||
last_cargo_event_id,
|
||||
history_event_ids
|
||||
)
|
||||
|
||||
|
||||
static func _create(
|
||||
group_id: StringName,
|
||||
group_type: StringName,
|
||||
display_name: String,
|
||||
polity_id: StringName,
|
||||
address: Variant,
|
||||
route_id: StringName,
|
||||
departure_location_id: StringName,
|
||||
arrival_location_id: StringName,
|
||||
departure_tick: int,
|
||||
arrival_tick: int,
|
||||
detailed_member_ids: Array,
|
||||
cohort_counts: Dictionary,
|
||||
cargo_ledger: Dictionary,
|
||||
cargo_capacity: float,
|
||||
last_cargo_event_id: int,
|
||||
history_event_ids: Array
|
||||
) -> MobileGroupStateRecord:
|
||||
var normalized_members: Variant = RegionalStateSupport.normalize_id_array(detailed_member_ids)
|
||||
var normalized_cohorts: Variant = RegionalStateSupport.normalize_count_ledger(cohort_counts)
|
||||
var normalized_cargo: Variant = RegionalStateSupport.normalize_amount_ledger(cargo_ledger)
|
||||
var normalized_history: Variant = RegionalStateSupport.normalize_history_event_ids(
|
||||
history_event_ids
|
||||
)
|
||||
var normalized_address: Variant = null
|
||||
if address != null:
|
||||
normalized_address = RegionalStateSupport.normalize_address(address)
|
||||
if (
|
||||
normalized_members == null
|
||||
or normalized_cohorts == null
|
||||
or normalized_cargo == null
|
||||
or normalized_history == null
|
||||
or (address != null and normalized_address == null)
|
||||
):
|
||||
return null
|
||||
var record := (
|
||||
MobileGroupStateRecord
|
||||
. new(
|
||||
{
|
||||
"schema_version": SCHEMA_VERSION,
|
||||
"group_id": String(group_id),
|
||||
"group_type": String(group_type),
|
||||
"display_name": display_name,
|
||||
"polity_id": String(polity_id),
|
||||
"location_address": normalized_address,
|
||||
"route_id": String(route_id),
|
||||
"departure_location_id": String(departure_location_id),
|
||||
"arrival_location_id": String(arrival_location_id),
|
||||
"departure_tick": departure_tick,
|
||||
"arrival_tick": arrival_tick,
|
||||
"detailed_member_ids": normalized_members,
|
||||
"cohort_counts": normalized_cohorts,
|
||||
"cargo_ledger": normalized_cargo,
|
||||
"cargo_capacity": cargo_capacity,
|
||||
"last_cargo_event_id": last_cargo_event_id,
|
||||
"history_event_ids": normalized_history,
|
||||
}
|
||||
)
|
||||
)
|
||||
return record if record.is_valid() else null
|
||||
|
||||
|
||||
static func from_dictionary(record_data: Dictionary) -> MobileGroupStateRecord:
|
||||
if int(record_data.get("schema_version", -1)) != SCHEMA_VERSION:
|
||||
return null
|
||||
if not (
|
||||
record_data
|
||||
. has_all(
|
||||
[
|
||||
"group_id",
|
||||
"group_type",
|
||||
"display_name",
|
||||
"polity_id",
|
||||
"location_address",
|
||||
"route_id",
|
||||
"departure_location_id",
|
||||
"arrival_location_id",
|
||||
"departure_tick",
|
||||
"arrival_tick",
|
||||
"detailed_member_ids",
|
||||
"cohort_counts",
|
||||
"cargo_ledger",
|
||||
"cargo_capacity",
|
||||
"last_cargo_event_id",
|
||||
"history_event_ids",
|
||||
]
|
||||
)
|
||||
):
|
||||
return null
|
||||
if (
|
||||
(
|
||||
record_data["location_address"] != null
|
||||
and not record_data["location_address"] is Dictionary
|
||||
)
|
||||
or not record_data["detailed_member_ids"] is Array
|
||||
or not record_data["cohort_counts"] is Dictionary
|
||||
or not record_data["cargo_ledger"] is Dictionary
|
||||
or not record_data["history_event_ids"] is Array
|
||||
):
|
||||
return null
|
||||
return _create(
|
||||
StringName(record_data["group_id"]),
|
||||
StringName(record_data["group_type"]),
|
||||
String(record_data["display_name"]),
|
||||
StringName(record_data["polity_id"]),
|
||||
record_data["location_address"],
|
||||
StringName(record_data["route_id"]),
|
||||
StringName(record_data["departure_location_id"]),
|
||||
StringName(record_data["arrival_location_id"]),
|
||||
int(record_data["departure_tick"]),
|
||||
int(record_data["arrival_tick"]),
|
||||
record_data["detailed_member_ids"],
|
||||
record_data["cohort_counts"],
|
||||
record_data["cargo_ledger"],
|
||||
float(record_data["cargo_capacity"]),
|
||||
int(record_data["last_cargo_event_id"]),
|
||||
record_data["history_event_ids"]
|
||||
)
|
||||
|
||||
|
||||
func is_valid() -> bool:
|
||||
if (
|
||||
get_group_id().is_empty()
|
||||
or get_group_type().is_empty()
|
||||
or get_display_name().is_empty()
|
||||
or get_polity_id().is_empty()
|
||||
or not is_finite(get_cargo_capacity())
|
||||
or get_cargo_capacity() < 0.0
|
||||
or RegionalStateSupport.amount_ledger_total(data["cargo_ledger"]) > get_cargo_capacity()
|
||||
or get_last_cargo_event_id() < -1
|
||||
or (
|
||||
get_last_cargo_event_id() >= 0
|
||||
and get_last_cargo_event_id() not in get_history_event_ids()
|
||||
)
|
||||
):
|
||||
return false
|
||||
if is_at_location():
|
||||
return (
|
||||
get_route_id().is_empty()
|
||||
and get_departure_location_id().is_empty()
|
||||
and get_arrival_location_id().is_empty()
|
||||
and get_departure_tick() == -1
|
||||
and get_arrival_tick() == -1
|
||||
)
|
||||
return (
|
||||
is_on_route()
|
||||
and not get_departure_location_id().is_empty()
|
||||
and not get_arrival_location_id().is_empty()
|
||||
and get_departure_location_id() != get_arrival_location_id()
|
||||
and get_departure_tick() >= 0
|
||||
and get_arrival_tick() > get_departure_tick()
|
||||
)
|
||||
|
||||
|
||||
func is_at_location() -> bool:
|
||||
return data["location_address"] is Dictionary
|
||||
|
||||
|
||||
func is_on_route() -> bool:
|
||||
return data["location_address"] == null and not get_route_id().is_empty()
|
||||
|
||||
|
||||
func get_group_id() -> StringName:
|
||||
return StringName(data["group_id"])
|
||||
|
||||
|
||||
func get_group_type() -> StringName:
|
||||
return StringName(data["group_type"])
|
||||
|
||||
|
||||
func get_display_name() -> String:
|
||||
return String(data["display_name"])
|
||||
|
||||
|
||||
func get_polity_id() -> StringName:
|
||||
return StringName(data["polity_id"])
|
||||
|
||||
|
||||
func get_location_address() -> SpatialAddress:
|
||||
return RegionalStateSupport.address_from_data(data["location_address"])
|
||||
|
||||
|
||||
func get_current_location_id() -> StringName:
|
||||
var address := get_location_address()
|
||||
return address.get_location_id() if address != null else &""
|
||||
|
||||
|
||||
func get_route_id() -> StringName:
|
||||
return StringName(data["route_id"])
|
||||
|
||||
|
||||
func get_departure_location_id() -> StringName:
|
||||
return StringName(data["departure_location_id"])
|
||||
|
||||
|
||||
func get_arrival_location_id() -> StringName:
|
||||
return StringName(data["arrival_location_id"])
|
||||
|
||||
|
||||
func get_departure_tick() -> int:
|
||||
return int(data["departure_tick"])
|
||||
|
||||
|
||||
func get_arrival_tick() -> int:
|
||||
return int(data["arrival_tick"])
|
||||
|
||||
|
||||
func get_detailed_member_ids() -> Array[StringName]:
|
||||
return RegionalStateSupport.copy_string_array(data["detailed_member_ids"])
|
||||
|
||||
|
||||
func get_cohort_counts() -> Dictionary:
|
||||
return data["cohort_counts"].duplicate(true)
|
||||
|
||||
|
||||
func get_cargo_ledger() -> Dictionary:
|
||||
return data["cargo_ledger"].duplicate(true)
|
||||
|
||||
|
||||
func get_cargo_amount(item_id: StringName) -> float:
|
||||
return float(data["cargo_ledger"].get(String(item_id), 0.0))
|
||||
|
||||
|
||||
func get_total_cargo_amount() -> float:
|
||||
return RegionalStateSupport.amount_ledger_total(data["cargo_ledger"])
|
||||
|
||||
|
||||
func get_cargo_capacity() -> float:
|
||||
return float(data["cargo_capacity"])
|
||||
|
||||
|
||||
func get_last_cargo_event_id() -> int:
|
||||
return int(data["last_cargo_event_id"])
|
||||
|
||||
|
||||
func get_history_event_ids() -> Array[int]:
|
||||
return RegionalStateSupport.copy_int_array(data["history_event_ids"])
|
||||
|
||||
|
||||
func get_headcount() -> int:
|
||||
var headcount := get_detailed_member_ids().size()
|
||||
for count: Variant in data["cohort_counts"].values():
|
||||
headcount += int(count)
|
||||
return headcount
|
||||
|
||||
|
||||
func with_location(
|
||||
address: SpatialAddress, cause_event_id: int = -1
|
||||
) -> MobileGroupStateRecord:
|
||||
var history_event_ids := _history_with_event(cause_event_id)
|
||||
return create_at_location(
|
||||
get_group_id(),
|
||||
get_group_type(),
|
||||
get_display_name(),
|
||||
get_polity_id(),
|
||||
address,
|
||||
data["detailed_member_ids"],
|
||||
data["cohort_counts"],
|
||||
data["cargo_ledger"],
|
||||
get_cargo_capacity(),
|
||||
get_last_cargo_event_id(),
|
||||
history_event_ids
|
||||
)
|
||||
|
||||
|
||||
func with_route_journey(
|
||||
route_id: StringName,
|
||||
departure_location_id: StringName,
|
||||
arrival_location_id: StringName,
|
||||
departure_tick: int,
|
||||
arrival_tick: int,
|
||||
cause_event_id: int = -1
|
||||
) -> MobileGroupStateRecord:
|
||||
var history_event_ids := _history_with_event(cause_event_id)
|
||||
return create_on_route(
|
||||
get_group_id(),
|
||||
get_group_type(),
|
||||
get_display_name(),
|
||||
get_polity_id(),
|
||||
route_id,
|
||||
departure_location_id,
|
||||
arrival_location_id,
|
||||
departure_tick,
|
||||
arrival_tick,
|
||||
data["detailed_member_ids"],
|
||||
data["cohort_counts"],
|
||||
data["cargo_ledger"],
|
||||
get_cargo_capacity(),
|
||||
get_last_cargo_event_id(),
|
||||
history_event_ids
|
||||
)
|
||||
|
||||
|
||||
func with_cargo_ledger(cargo_ledger: Dictionary, cargo_event_id: int) -> MobileGroupStateRecord:
|
||||
var history_event_ids := _history_with_event(cargo_event_id)
|
||||
return _create(
|
||||
get_group_id(),
|
||||
get_group_type(),
|
||||
get_display_name(),
|
||||
get_polity_id(),
|
||||
data["location_address"],
|
||||
get_route_id(),
|
||||
get_departure_location_id(),
|
||||
get_arrival_location_id(),
|
||||
get_departure_tick(),
|
||||
get_arrival_tick(),
|
||||
data["detailed_member_ids"],
|
||||
data["cohort_counts"],
|
||||
cargo_ledger,
|
||||
get_cargo_capacity(),
|
||||
cargo_event_id,
|
||||
history_event_ids
|
||||
)
|
||||
|
||||
|
||||
func _history_with_event(event_id: int) -> Array:
|
||||
var history_event_ids: Array = data["history_event_ids"].duplicate()
|
||||
if event_id >= 0 and event_id not in history_event_ids:
|
||||
history_event_ids.append(event_id)
|
||||
return history_event_ids
|
||||
|
||||
|
||||
func to_dictionary() -> Dictionary:
|
||||
return data.duplicate(true)
|
||||
@@ -0,0 +1 @@
|
||||
uid://jyqncykmnbfi
|
||||
@@ -0,0 +1,183 @@
|
||||
class_name PersonStateRecord
|
||||
extends RefCounted
|
||||
|
||||
const SCHEMA_VERSION := 1
|
||||
|
||||
var data: Dictionary
|
||||
|
||||
|
||||
func _init(record_data: Dictionary = {}) -> void:
|
||||
data = record_data.duplicate(true)
|
||||
|
||||
|
||||
static func create_at_location(
|
||||
person_id: StringName,
|
||||
display_name: String,
|
||||
role_id: StringName,
|
||||
polity_id: StringName,
|
||||
home_location_id: StringName,
|
||||
address: SpatialAddress,
|
||||
history_event_ids: Array = []
|
||||
) -> PersonStateRecord:
|
||||
return _create(
|
||||
person_id,
|
||||
display_name,
|
||||
role_id,
|
||||
polity_id,
|
||||
home_location_id,
|
||||
address,
|
||||
&"",
|
||||
history_event_ids
|
||||
)
|
||||
|
||||
|
||||
static func create_in_group(
|
||||
person_id: StringName,
|
||||
display_name: String,
|
||||
role_id: StringName,
|
||||
polity_id: StringName,
|
||||
home_location_id: StringName,
|
||||
mobile_group_id: StringName,
|
||||
history_event_ids: Array = []
|
||||
) -> PersonStateRecord:
|
||||
return _create(
|
||||
person_id,
|
||||
display_name,
|
||||
role_id,
|
||||
polity_id,
|
||||
home_location_id,
|
||||
null,
|
||||
mobile_group_id,
|
||||
history_event_ids
|
||||
)
|
||||
|
||||
|
||||
static func _create(
|
||||
person_id: StringName,
|
||||
display_name: String,
|
||||
role_id: StringName,
|
||||
polity_id: StringName,
|
||||
home_location_id: StringName,
|
||||
address: Variant,
|
||||
mobile_group_id: StringName,
|
||||
history_event_ids: Array
|
||||
) -> PersonStateRecord:
|
||||
var normalized_address: Variant = null
|
||||
if address != null:
|
||||
normalized_address = RegionalStateSupport.normalize_address(address)
|
||||
var normalized_history: Variant = RegionalStateSupport.normalize_history_event_ids(
|
||||
history_event_ids
|
||||
)
|
||||
if normalized_history == null or (address != null and normalized_address == null):
|
||||
return null
|
||||
var record := (
|
||||
PersonStateRecord
|
||||
. new(
|
||||
{
|
||||
"schema_version": SCHEMA_VERSION,
|
||||
"person_id": String(person_id),
|
||||
"display_name": display_name,
|
||||
"role_id": String(role_id),
|
||||
"polity_id": String(polity_id),
|
||||
"home_location_id": String(home_location_id),
|
||||
"location_address": normalized_address,
|
||||
"mobile_group_id": String(mobile_group_id),
|
||||
"history_event_ids": normalized_history,
|
||||
}
|
||||
)
|
||||
)
|
||||
return record if record.is_valid() else null
|
||||
|
||||
|
||||
static func from_dictionary(record_data: Dictionary) -> PersonStateRecord:
|
||||
if int(record_data.get("schema_version", -1)) != SCHEMA_VERSION:
|
||||
return null
|
||||
if not (
|
||||
record_data
|
||||
. has_all(
|
||||
[
|
||||
"person_id",
|
||||
"display_name",
|
||||
"role_id",
|
||||
"polity_id",
|
||||
"home_location_id",
|
||||
"location_address",
|
||||
"mobile_group_id",
|
||||
"history_event_ids",
|
||||
]
|
||||
)
|
||||
):
|
||||
return null
|
||||
if (
|
||||
(
|
||||
record_data["location_address"] != null
|
||||
and not record_data["location_address"] is Dictionary
|
||||
)
|
||||
or not record_data["history_event_ids"] is Array
|
||||
):
|
||||
return null
|
||||
return _create(
|
||||
StringName(record_data["person_id"]),
|
||||
String(record_data["display_name"]),
|
||||
StringName(record_data["role_id"]),
|
||||
StringName(record_data["polity_id"]),
|
||||
StringName(record_data["home_location_id"]),
|
||||
record_data["location_address"],
|
||||
StringName(record_data["mobile_group_id"]),
|
||||
record_data["history_event_ids"]
|
||||
)
|
||||
|
||||
|
||||
func is_valid() -> bool:
|
||||
return (
|
||||
not get_person_id().is_empty()
|
||||
and not get_display_name().is_empty()
|
||||
and not get_role_id().is_empty()
|
||||
and not get_polity_id().is_empty()
|
||||
and not get_home_location_id().is_empty()
|
||||
and (is_at_location() != is_in_mobile_group())
|
||||
)
|
||||
|
||||
|
||||
func get_person_id() -> StringName:
|
||||
return StringName(data["person_id"])
|
||||
|
||||
|
||||
func get_display_name() -> String:
|
||||
return String(data["display_name"])
|
||||
|
||||
|
||||
func get_role_id() -> StringName:
|
||||
return StringName(data["role_id"])
|
||||
|
||||
|
||||
func get_polity_id() -> StringName:
|
||||
return StringName(data["polity_id"])
|
||||
|
||||
|
||||
func get_home_location_id() -> StringName:
|
||||
return StringName(data["home_location_id"])
|
||||
|
||||
|
||||
func is_at_location() -> bool:
|
||||
return data["location_address"] is Dictionary and get_mobile_group_id().is_empty()
|
||||
|
||||
|
||||
func is_in_mobile_group() -> bool:
|
||||
return data["location_address"] == null and not get_mobile_group_id().is_empty()
|
||||
|
||||
|
||||
func get_location_address() -> SpatialAddress:
|
||||
return RegionalStateSupport.address_from_data(data["location_address"])
|
||||
|
||||
|
||||
func get_mobile_group_id() -> StringName:
|
||||
return StringName(data["mobile_group_id"])
|
||||
|
||||
|
||||
func get_history_event_ids() -> Array[int]:
|
||||
return RegionalStateSupport.copy_int_array(data["history_event_ids"])
|
||||
|
||||
|
||||
func to_dictionary() -> Dictionary:
|
||||
return data.duplicate(true)
|
||||
@@ -0,0 +1 @@
|
||||
uid://bfotwdi60mdnm
|
||||
@@ -0,0 +1,98 @@
|
||||
class_name PolityStateRecord
|
||||
extends RefCounted
|
||||
|
||||
const SCHEMA_VERSION := 1
|
||||
|
||||
var data: Dictionary
|
||||
|
||||
|
||||
func _init(record_data: Dictionary = {}) -> void:
|
||||
data = record_data.duplicate(true)
|
||||
|
||||
|
||||
static func create(
|
||||
polity_id: StringName,
|
||||
display_name: String,
|
||||
capital_location_id: StringName,
|
||||
settlement_ids: Array = [],
|
||||
history_event_ids: Array = []
|
||||
) -> PolityStateRecord:
|
||||
var normalized_settlements: Variant = RegionalStateSupport.normalize_id_array(settlement_ids)
|
||||
var normalized_history: Variant = RegionalStateSupport.normalize_history_event_ids(
|
||||
history_event_ids
|
||||
)
|
||||
if normalized_settlements == null or normalized_history == null:
|
||||
return null
|
||||
var record := (
|
||||
PolityStateRecord
|
||||
. new(
|
||||
{
|
||||
"schema_version": SCHEMA_VERSION,
|
||||
"polity_id": String(polity_id),
|
||||
"display_name": display_name,
|
||||
"capital_location_id": String(capital_location_id),
|
||||
"settlement_ids": normalized_settlements,
|
||||
"history_event_ids": normalized_history,
|
||||
}
|
||||
)
|
||||
)
|
||||
return record if record.is_valid() else null
|
||||
|
||||
|
||||
static func from_dictionary(record_data: Dictionary) -> PolityStateRecord:
|
||||
if int(record_data.get("schema_version", -1)) != SCHEMA_VERSION:
|
||||
return null
|
||||
if not (
|
||||
record_data
|
||||
. has_all(
|
||||
[
|
||||
"polity_id",
|
||||
"display_name",
|
||||
"capital_location_id",
|
||||
"settlement_ids",
|
||||
"history_event_ids",
|
||||
]
|
||||
)
|
||||
):
|
||||
return null
|
||||
if not record_data["settlement_ids"] is Array or not record_data["history_event_ids"] is Array:
|
||||
return null
|
||||
return create(
|
||||
StringName(record_data["polity_id"]),
|
||||
String(record_data["display_name"]),
|
||||
StringName(record_data["capital_location_id"]),
|
||||
record_data["settlement_ids"],
|
||||
record_data["history_event_ids"]
|
||||
)
|
||||
|
||||
|
||||
func is_valid() -> bool:
|
||||
return (
|
||||
not get_polity_id().is_empty()
|
||||
and not get_display_name().is_empty()
|
||||
and not get_capital_location_id().is_empty()
|
||||
)
|
||||
|
||||
|
||||
func get_polity_id() -> StringName:
|
||||
return StringName(data["polity_id"])
|
||||
|
||||
|
||||
func get_display_name() -> String:
|
||||
return String(data["display_name"])
|
||||
|
||||
|
||||
func get_capital_location_id() -> StringName:
|
||||
return StringName(data["capital_location_id"])
|
||||
|
||||
|
||||
func get_settlement_ids() -> Array[StringName]:
|
||||
return RegionalStateSupport.copy_string_array(data["settlement_ids"])
|
||||
|
||||
|
||||
func get_history_event_ids() -> Array[int]:
|
||||
return RegionalStateSupport.copy_int_array(data["history_event_ids"])
|
||||
|
||||
|
||||
func to_dictionary() -> Dictionary:
|
||||
return data.duplicate(true)
|
||||
@@ -0,0 +1 @@
|
||||
uid://b1hsgds86deba
|
||||
@@ -0,0 +1,147 @@
|
||||
class_name PopulationCohortRecord
|
||||
extends RefCounted
|
||||
|
||||
const SCHEMA_VERSION := 1
|
||||
|
||||
var data: Dictionary
|
||||
|
||||
|
||||
func _init(record_data: Dictionary = {}) -> void:
|
||||
data = record_data.duplicate(true)
|
||||
|
||||
|
||||
static func create_at_location(
|
||||
cohort_id: StringName,
|
||||
cohort_type: StringName,
|
||||
polity_id: StringName,
|
||||
count: int,
|
||||
location_id: StringName,
|
||||
history_event_ids: Array = []
|
||||
) -> PopulationCohortRecord:
|
||||
return _create(cohort_id, cohort_type, polity_id, count, location_id, &"", history_event_ids)
|
||||
|
||||
|
||||
static func create_in_group(
|
||||
cohort_id: StringName,
|
||||
cohort_type: StringName,
|
||||
polity_id: StringName,
|
||||
count: int,
|
||||
mobile_group_id: StringName,
|
||||
history_event_ids: Array = []
|
||||
) -> PopulationCohortRecord:
|
||||
return _create(
|
||||
cohort_id, cohort_type, polity_id, count, &"", mobile_group_id, history_event_ids
|
||||
)
|
||||
|
||||
|
||||
static func _create(
|
||||
cohort_id: StringName,
|
||||
cohort_type: StringName,
|
||||
polity_id: StringName,
|
||||
count: int,
|
||||
location_id: StringName,
|
||||
mobile_group_id: StringName,
|
||||
history_event_ids: Array
|
||||
) -> PopulationCohortRecord:
|
||||
var normalized_history: Variant = RegionalStateSupport.normalize_history_event_ids(
|
||||
history_event_ids
|
||||
)
|
||||
if normalized_history == null:
|
||||
return null
|
||||
var record := (
|
||||
PopulationCohortRecord
|
||||
. new(
|
||||
{
|
||||
"schema_version": SCHEMA_VERSION,
|
||||
"cohort_id": String(cohort_id),
|
||||
"cohort_type": String(cohort_type),
|
||||
"polity_id": String(polity_id),
|
||||
"count": count,
|
||||
"location_id": String(location_id),
|
||||
"mobile_group_id": String(mobile_group_id),
|
||||
"history_event_ids": normalized_history,
|
||||
}
|
||||
)
|
||||
)
|
||||
return record if record.is_valid() else null
|
||||
|
||||
|
||||
static func from_dictionary(record_data: Dictionary) -> PopulationCohortRecord:
|
||||
if int(record_data.get("schema_version", -1)) != SCHEMA_VERSION:
|
||||
return null
|
||||
if not (
|
||||
record_data
|
||||
. has_all(
|
||||
[
|
||||
"cohort_id",
|
||||
"cohort_type",
|
||||
"polity_id",
|
||||
"count",
|
||||
"location_id",
|
||||
"mobile_group_id",
|
||||
"history_event_ids",
|
||||
]
|
||||
)
|
||||
):
|
||||
return null
|
||||
if not record_data["history_event_ids"] is Array:
|
||||
return null
|
||||
return _create(
|
||||
StringName(record_data["cohort_id"]),
|
||||
StringName(record_data["cohort_type"]),
|
||||
StringName(record_data["polity_id"]),
|
||||
int(record_data["count"]),
|
||||
StringName(record_data["location_id"]),
|
||||
StringName(record_data["mobile_group_id"]),
|
||||
record_data["history_event_ids"]
|
||||
)
|
||||
|
||||
|
||||
func is_valid() -> bool:
|
||||
return (
|
||||
not get_cohort_id().is_empty()
|
||||
and not get_cohort_type().is_empty()
|
||||
and not get_polity_id().is_empty()
|
||||
and get_count() > 0
|
||||
and (is_at_location() != is_in_mobile_group())
|
||||
)
|
||||
|
||||
|
||||
func get_cohort_id() -> StringName:
|
||||
return StringName(data["cohort_id"])
|
||||
|
||||
|
||||
func get_cohort_type() -> StringName:
|
||||
return StringName(data["cohort_type"])
|
||||
|
||||
|
||||
func get_polity_id() -> StringName:
|
||||
return StringName(data["polity_id"])
|
||||
|
||||
|
||||
func get_count() -> int:
|
||||
return int(data["count"])
|
||||
|
||||
|
||||
func is_at_location() -> bool:
|
||||
return not get_location_id().is_empty() and get_mobile_group_id().is_empty()
|
||||
|
||||
|
||||
func is_in_mobile_group() -> bool:
|
||||
return get_location_id().is_empty() and not get_mobile_group_id().is_empty()
|
||||
|
||||
|
||||
func get_location_id() -> StringName:
|
||||
return StringName(data["location_id"])
|
||||
|
||||
|
||||
func get_mobile_group_id() -> StringName:
|
||||
return StringName(data["mobile_group_id"])
|
||||
|
||||
|
||||
func get_history_event_ids() -> Array[int]:
|
||||
return RegionalStateSupport.copy_int_array(data["history_event_ids"])
|
||||
|
||||
|
||||
func to_dictionary() -> Dictionary:
|
||||
return data.duplicate(true)
|
||||
@@ -0,0 +1 @@
|
||||
uid://di6mlijatrbgu
|
||||
@@ -0,0 +1,117 @@
|
||||
class_name RouteStateRecord
|
||||
extends RefCounted
|
||||
|
||||
const SCHEMA_VERSION := 1
|
||||
|
||||
var data: Dictionary
|
||||
|
||||
|
||||
func _init(record_data: Dictionary = {}) -> void:
|
||||
data = record_data.duplicate(true)
|
||||
|
||||
|
||||
static func create(
|
||||
route_id: StringName,
|
||||
from_location_id: StringName,
|
||||
to_location_id: StringName,
|
||||
travel_ticks: int,
|
||||
bidirectional: bool = true,
|
||||
history_event_ids: Array = []
|
||||
) -> RouteStateRecord:
|
||||
var normalized_history: Variant = RegionalStateSupport.normalize_history_event_ids(
|
||||
history_event_ids
|
||||
)
|
||||
if normalized_history == null:
|
||||
return null
|
||||
var record := (
|
||||
RouteStateRecord
|
||||
. new(
|
||||
{
|
||||
"schema_version": SCHEMA_VERSION,
|
||||
"route_id": String(route_id),
|
||||
"from_location_id": String(from_location_id),
|
||||
"to_location_id": String(to_location_id),
|
||||
"travel_ticks": travel_ticks,
|
||||
"bidirectional": bidirectional,
|
||||
"history_event_ids": normalized_history,
|
||||
}
|
||||
)
|
||||
)
|
||||
return record if record.is_valid() else null
|
||||
|
||||
|
||||
static func from_dictionary(record_data: Dictionary) -> RouteStateRecord:
|
||||
if int(record_data.get("schema_version", -1)) != SCHEMA_VERSION:
|
||||
return null
|
||||
if not (
|
||||
record_data
|
||||
. has_all(
|
||||
[
|
||||
"route_id",
|
||||
"from_location_id",
|
||||
"to_location_id",
|
||||
"travel_ticks",
|
||||
"bidirectional",
|
||||
"history_event_ids",
|
||||
]
|
||||
)
|
||||
):
|
||||
return null
|
||||
if not record_data["bidirectional"] is bool or not record_data["history_event_ids"] is Array:
|
||||
return null
|
||||
return create(
|
||||
StringName(record_data["route_id"]),
|
||||
StringName(record_data["from_location_id"]),
|
||||
StringName(record_data["to_location_id"]),
|
||||
int(record_data["travel_ticks"]),
|
||||
bool(record_data["bidirectional"]),
|
||||
record_data["history_event_ids"]
|
||||
)
|
||||
|
||||
|
||||
func is_valid() -> bool:
|
||||
return (
|
||||
not get_route_id().is_empty()
|
||||
and not get_from_location_id().is_empty()
|
||||
and not get_to_location_id().is_empty()
|
||||
and get_from_location_id() != get_to_location_id()
|
||||
and get_travel_ticks() > 0
|
||||
)
|
||||
|
||||
|
||||
func get_route_id() -> StringName:
|
||||
return StringName(data["route_id"])
|
||||
|
||||
|
||||
func get_from_location_id() -> StringName:
|
||||
return StringName(data["from_location_id"])
|
||||
|
||||
|
||||
func get_to_location_id() -> StringName:
|
||||
return StringName(data["to_location_id"])
|
||||
|
||||
|
||||
func get_travel_ticks() -> int:
|
||||
return int(data["travel_ticks"])
|
||||
|
||||
|
||||
func is_bidirectional() -> bool:
|
||||
return bool(data["bidirectional"])
|
||||
|
||||
|
||||
func get_history_event_ids() -> Array[int]:
|
||||
return RegionalStateSupport.copy_int_array(data["history_event_ids"])
|
||||
|
||||
|
||||
func connects(first_location_id: StringName, second_location_id: StringName) -> bool:
|
||||
if first_location_id == get_from_location_id() and second_location_id == get_to_location_id():
|
||||
return true
|
||||
return (
|
||||
is_bidirectional()
|
||||
and first_location_id == get_to_location_id()
|
||||
and second_location_id == get_from_location_id()
|
||||
)
|
||||
|
||||
|
||||
func to_dictionary() -> Dictionary:
|
||||
return data.duplicate(true)
|
||||
@@ -0,0 +1 @@
|
||||
uid://dw73sybt7s5cx
|
||||
@@ -0,0 +1,166 @@
|
||||
class_name SettlementStateRecord
|
||||
extends RefCounted
|
||||
|
||||
const SCHEMA_VERSION := 1
|
||||
|
||||
var data: Dictionary
|
||||
|
||||
|
||||
func _init(record_data: Dictionary = {}) -> void:
|
||||
data = record_data.duplicate(true)
|
||||
|
||||
|
||||
static func create(
|
||||
settlement_id: StringName,
|
||||
location_id: StringName,
|
||||
display_name: String,
|
||||
polity_id: StringName,
|
||||
tier: int,
|
||||
founding_group_id: StringName,
|
||||
founded_tick: int,
|
||||
stockpile_ledger: Dictionary = {},
|
||||
history_event_ids: Array = []
|
||||
) -> SettlementStateRecord:
|
||||
var normalized_stockpile: Variant = RegionalStateSupport.normalize_amount_ledger(
|
||||
stockpile_ledger
|
||||
)
|
||||
var normalized_history: Variant = RegionalStateSupport.normalize_history_event_ids(
|
||||
history_event_ids
|
||||
)
|
||||
if normalized_stockpile == null or normalized_history == null:
|
||||
return null
|
||||
var record := (
|
||||
SettlementStateRecord
|
||||
. new(
|
||||
{
|
||||
"schema_version": SCHEMA_VERSION,
|
||||
"settlement_id": String(settlement_id),
|
||||
"location_id": String(location_id),
|
||||
"display_name": display_name,
|
||||
"polity_id": String(polity_id),
|
||||
"tier": tier,
|
||||
"founding_group_id": String(founding_group_id),
|
||||
"founded_tick": founded_tick,
|
||||
"stockpile_ledger": normalized_stockpile,
|
||||
"history_event_ids": normalized_history,
|
||||
}
|
||||
)
|
||||
)
|
||||
return record if record.is_valid() else null
|
||||
|
||||
|
||||
static func from_dictionary(record_data: Dictionary) -> SettlementStateRecord:
|
||||
if int(record_data.get("schema_version", -1)) != SCHEMA_VERSION:
|
||||
return null
|
||||
if not (
|
||||
record_data
|
||||
. has_all(
|
||||
[
|
||||
"settlement_id",
|
||||
"location_id",
|
||||
"display_name",
|
||||
"polity_id",
|
||||
"tier",
|
||||
"founding_group_id",
|
||||
"founded_tick",
|
||||
"stockpile_ledger",
|
||||
"history_event_ids",
|
||||
]
|
||||
)
|
||||
):
|
||||
return null
|
||||
if (
|
||||
not record_data["stockpile_ledger"] is Dictionary
|
||||
or not record_data["history_event_ids"] is Array
|
||||
):
|
||||
return null
|
||||
return create(
|
||||
StringName(record_data["settlement_id"]),
|
||||
StringName(record_data["location_id"]),
|
||||
String(record_data["display_name"]),
|
||||
StringName(record_data["polity_id"]),
|
||||
int(record_data["tier"]),
|
||||
StringName(record_data["founding_group_id"]),
|
||||
int(record_data["founded_tick"]),
|
||||
record_data["stockpile_ledger"],
|
||||
record_data["history_event_ids"]
|
||||
)
|
||||
|
||||
|
||||
func is_valid() -> bool:
|
||||
return (
|
||||
not get_settlement_id().is_empty()
|
||||
and not get_location_id().is_empty()
|
||||
and not get_display_name().is_empty()
|
||||
and not get_polity_id().is_empty()
|
||||
and not get_founding_group_id().is_empty()
|
||||
and get_tier() >= 0
|
||||
and get_founded_tick() >= 0
|
||||
)
|
||||
|
||||
|
||||
func get_settlement_id() -> StringName:
|
||||
return StringName(data["settlement_id"])
|
||||
|
||||
|
||||
func get_location_id() -> StringName:
|
||||
return StringName(data["location_id"])
|
||||
|
||||
|
||||
func get_display_name() -> String:
|
||||
return String(data["display_name"])
|
||||
|
||||
|
||||
func get_polity_id() -> StringName:
|
||||
return StringName(data["polity_id"])
|
||||
|
||||
|
||||
func get_tier() -> int:
|
||||
return int(data["tier"])
|
||||
|
||||
|
||||
func get_founding_group_id() -> StringName:
|
||||
return StringName(data["founding_group_id"])
|
||||
|
||||
|
||||
func get_founded_tick() -> int:
|
||||
return int(data["founded_tick"])
|
||||
|
||||
|
||||
func get_stockpile_ledger() -> Dictionary:
|
||||
return data["stockpile_ledger"].duplicate(true)
|
||||
|
||||
|
||||
func get_stockpile_amount(item_id: StringName) -> float:
|
||||
return float(data["stockpile_ledger"].get(String(item_id), 0.0))
|
||||
|
||||
|
||||
func get_total_stockpile_amount() -> float:
|
||||
return RegionalStateSupport.amount_ledger_total(data["stockpile_ledger"])
|
||||
|
||||
|
||||
func get_history_event_ids() -> Array[int]:
|
||||
return RegionalStateSupport.copy_int_array(data["history_event_ids"])
|
||||
|
||||
|
||||
func with_stockpile_ledger(
|
||||
stockpile_ledger: Dictionary, cause_event_id: int = -1
|
||||
) -> SettlementStateRecord:
|
||||
var history_event_ids: Array = data["history_event_ids"].duplicate()
|
||||
if cause_event_id >= 0 and cause_event_id not in history_event_ids:
|
||||
history_event_ids.append(cause_event_id)
|
||||
return create(
|
||||
get_settlement_id(),
|
||||
get_location_id(),
|
||||
get_display_name(),
|
||||
get_polity_id(),
|
||||
get_tier(),
|
||||
get_founding_group_id(),
|
||||
get_founded_tick(),
|
||||
stockpile_ledger,
|
||||
history_event_ids
|
||||
)
|
||||
|
||||
|
||||
func to_dictionary() -> Dictionary:
|
||||
return data.duplicate(true)
|
||||
@@ -0,0 +1 @@
|
||||
uid://8ofxqx8bfmyg
|
||||
Reference in New Issue
Block a user