feat: unify authored activity commands
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
class_name ContentCatalog
|
||||
extends RefCounted
|
||||
# gdlint: disable=max-file-lines
|
||||
|
||||
const CORE_PACK_PATH := "res://simulation/definitions/packs/core.tres"
|
||||
const DEFAULT_SITUATION_EVENT_PREDICATE_IDS: Array[StringName] = [
|
||||
@@ -11,6 +12,7 @@ const DEFAULT_SITUATION_STATE_PREDICATE_IDS: Array[StringName] = [
|
||||
const DEFAULT_SITUATION_EXPIRY_PREDICATE_IDS: Array[StringName] = [
|
||||
&"state_number_gt", &"age_reached"
|
||||
]
|
||||
const BUILTIN_HANDLER_IDS: Array[StringName] = [SimulationIds.ACTION_HANDLER_ACTIVITY_METRIC_DELTA]
|
||||
|
||||
var _valid := false
|
||||
var _errors: Array[String] = []
|
||||
@@ -81,11 +83,14 @@ func rebuild(
|
||||
var pack_list := content_packs.duplicate()
|
||||
pack_list.sort_custom(_sort_packs)
|
||||
var supported_handlers := {}
|
||||
for handler_id in BUILTIN_HANDLER_IDS:
|
||||
supported_handlers[handler_id] = true
|
||||
for handler_id in supported_handler_ids:
|
||||
if handler_id.is_empty():
|
||||
errors.append("Supported handler ID is empty")
|
||||
elif supported_handlers.has(handler_id):
|
||||
errors.append("Duplicate supported handler '%s'" % handler_id)
|
||||
if handler_id not in BUILTIN_HANDLER_IDS:
|
||||
errors.append("Duplicate supported handler '%s'" % handler_id)
|
||||
else:
|
||||
supported_handlers[handler_id] = true
|
||||
var cue_ids := (
|
||||
@@ -202,7 +207,9 @@ func rebuild(
|
||||
actions_by_id,
|
||||
professions_by_id,
|
||||
items_by_id,
|
||||
storages_by_id,
|
||||
capability_tags_by_id,
|
||||
event_types_by_id,
|
||||
supported_handlers,
|
||||
supported_cues,
|
||||
errors
|
||||
@@ -670,7 +677,9 @@ static func _validate_action_references(
|
||||
actions_by_id: Dictionary,
|
||||
professions_by_id: Dictionary,
|
||||
items_by_id: Dictionary,
|
||||
storages_by_id: Dictionary,
|
||||
capability_tags_by_id: Dictionary,
|
||||
event_types_by_id: Dictionary,
|
||||
supported_handlers: Dictionary,
|
||||
supported_cues: Dictionary,
|
||||
errors: Array[String]
|
||||
@@ -696,6 +705,16 @@ static func _validate_action_references(
|
||||
supported_cues,
|
||||
errors
|
||||
)
|
||||
if (
|
||||
not definition.event_type_id.is_empty()
|
||||
and not event_types_by_id.has(definition.event_type_id)
|
||||
):
|
||||
errors.append(
|
||||
(
|
||||
"Action '%s' references unknown event type '%s'"
|
||||
% [definition.action_id, definition.event_type_id]
|
||||
)
|
||||
)
|
||||
for handler_field in [
|
||||
["selection policy", definition.selection_policy_id],
|
||||
["target policy", definition.target_policy_id],
|
||||
@@ -769,6 +788,64 @@ static func _validate_action_references(
|
||||
% [definition.action_id, scheduled_action_id]
|
||||
)
|
||||
)
|
||||
_validate_builtin_action_handler_contract(definition, items_by_id, storages_by_id, errors)
|
||||
|
||||
|
||||
static func _validate_builtin_action_handler_contract(
|
||||
definition: ActionDefinition,
|
||||
items_by_id: Dictionary,
|
||||
storages_by_id: Dictionary,
|
||||
errors: Array[String]
|
||||
) -> void:
|
||||
if definition.completion_handler_id != SimulationIds.ACTION_HANDLER_ACTIVITY_METRIC_DELTA:
|
||||
return
|
||||
var label := "Action '%s' activity_metric_delta" % definition.action_id
|
||||
if definition.target_type != SimulationIds.TARGET_ACTIVITY:
|
||||
errors.append("%s handler requires an activity target" % label)
|
||||
if definition.effects.size() != 1:
|
||||
errors.append("%s handler requires exactly one effect" % label)
|
||||
return
|
||||
var effect := definition.effects[0] as ActionEffect
|
||||
if effect == null or effect.effect_kind != ActionEffect.KIND_METRIC_DELTA:
|
||||
errors.append("%s handler requires one metric_delta effect" % label)
|
||||
return
|
||||
if effect.subject_key not in [&"safety", &"knowledge"]:
|
||||
errors.append("%s metric must be safety or knowledge" % label)
|
||||
if not is_finite(effect.value) or effect.value <= 0.0:
|
||||
errors.append("%s effect value must be finite and positive" % label)
|
||||
var allowed_parameters := {"player_multiplier": true, "npc_productivity": true}
|
||||
for raw_key in effect.parameters:
|
||||
var key := String(raw_key)
|
||||
if not allowed_parameters.has(key):
|
||||
errors.append("%s has unsupported effect parameter '%s'" % [label, key])
|
||||
var player_multiplier: Variant = effect.parameters.get("player_multiplier", 1.0)
|
||||
if player_multiplier is not int and player_multiplier is not float:
|
||||
errors.append("%s player_multiplier must be numeric" % label)
|
||||
elif not is_finite(float(player_multiplier)) or float(player_multiplier) <= 0.0:
|
||||
errors.append("%s player_multiplier must be finite and positive" % label)
|
||||
var npc_productivity: Variant = effect.parameters.get("npc_productivity", false)
|
||||
if npc_productivity is not bool:
|
||||
errors.append("%s npc_productivity must be a bool" % label)
|
||||
if definition.event_type_id.is_empty():
|
||||
errors.append("%s handler requires a nonempty event_type_id" % label)
|
||||
if not definition.has_completion_cost():
|
||||
return
|
||||
var cost_item := items_by_id.get(definition.completion_cost_resource_id) as ItemDefinition
|
||||
if cost_item == null:
|
||||
return
|
||||
var has_storage_route := false
|
||||
for storage_value in storages_by_id.values():
|
||||
var storage := storage_value as StorageDefinition
|
||||
if storage != null and storage.accepts_item(cost_item):
|
||||
has_storage_route = true
|
||||
break
|
||||
if not has_storage_route:
|
||||
errors.append(
|
||||
(
|
||||
"%s completion cost item '%s' has no authored storage route"
|
||||
% [label, definition.completion_cost_resource_id]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
static func _validate_item_references(
|
||||
|
||||
@@ -28,6 +28,7 @@ const PROFESSION_PATHS := [
|
||||
static var _action_list: Array[ActionDefinition] = []
|
||||
static var _profession_list: Array[ProfessionDefinition] = []
|
||||
static var _action_map: Dictionary = {}
|
||||
static var _action_event_type_ids: Dictionary = {}
|
||||
static var _profession_map: Dictionary = {}
|
||||
static var _profession_id_list: Array[StringName] = []
|
||||
static var _catalog_errors: Array[String] = []
|
||||
@@ -40,6 +41,7 @@ static func _ensure_cache() -> void:
|
||||
_action_list = []
|
||||
_profession_list = []
|
||||
_action_map = {}
|
||||
_action_event_type_ids = {}
|
||||
_profession_map = {}
|
||||
_profession_id_list = []
|
||||
_catalog_errors = []
|
||||
@@ -52,6 +54,8 @@ static func _ensure_cache() -> void:
|
||||
_profession_list = catalog.get_professions()
|
||||
for definition in _action_list:
|
||||
_action_map[definition.action_id] = definition
|
||||
if not definition.event_type_id.is_empty():
|
||||
_action_event_type_ids[definition.event_type_id] = true
|
||||
for definition in _profession_list:
|
||||
_profession_map[definition.profession_id] = definition
|
||||
_profession_id_list.append(definition.profession_id)
|
||||
@@ -78,6 +82,11 @@ static func get_action(action_id: StringName) -> ActionDefinition:
|
||||
return _action_map.get(action_id)
|
||||
|
||||
|
||||
static func has_action_event_type(event_type_id: StringName) -> bool:
|
||||
_ensure_cache()
|
||||
return _action_event_type_ids.has(event_type_id)
|
||||
|
||||
|
||||
static func get_profession(profession_id: StringName) -> ProfessionDefinition:
|
||||
_ensure_cache()
|
||||
return _profession_map.get(profession_id)
|
||||
|
||||
@@ -6,6 +6,13 @@ const PLAYER_INVENTORY_ID := &"player_inventory"
|
||||
|
||||
const WORLD_CORE := &"world_core"
|
||||
const LOCATION_JAJCE := &"settlement_jajce"
|
||||
const REGIONAL_WORLD_BOSNIA := &"regional_bosnia"
|
||||
const REGIONAL_LOCATION_JAJCE := &"location_jajce"
|
||||
const ACTIVITY_GUARD_POST := &"guard_post"
|
||||
const ACTIVITY_STUDY_DESK := &"study_desk"
|
||||
const ACTION_HANDLER_ACTIVITY_METRIC_DELTA := &"activity_metric_delta"
|
||||
const BLOCKED_CONTRACT_ACTION_COST := &"action_cost"
|
||||
const BLOCKED_CONTRACT_LEGACY_UNSTRUCTURED := &"legacy_unstructured"
|
||||
|
||||
const ENTITY_PERSON := &"person"
|
||||
const ENTITY_PLAYER := &"player"
|
||||
@@ -72,6 +79,7 @@ const EVENT_COMMITMENT_FULFILLED := &"commitment_fulfilled"
|
||||
const EVENT_COMMITMENT_BROKEN := &"commitment_broken"
|
||||
const EVENT_COMMITMENT_RELEASED := &"commitment_released"
|
||||
const EVENT_COMMITMENT_SUPERSEDED := &"commitment_superseded"
|
||||
const EVENT_ACTIVITY_COMPLETED := &"activity_completed"
|
||||
|
||||
const OPPORTUNITY_RESTOCK_EMPTY_PANTRY := &"restock_empty_pantry"
|
||||
const OPPORTUNITY_SUPPLY_MISSING_WOOD := &"supply_missing_wood"
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
[gd_resource type="Resource" script_class="ActionDefinition" load_steps=2 format=3]
|
||||
[gd_resource type="Resource" script_class="ActionDefinition" load_steps=3 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://simulation/definitions/ActionDefinition.gd" id="1"]
|
||||
[ext_resource type="Script" path="res://simulation/commands/ActionEffect.gd" id="2"]
|
||||
|
||||
[sub_resource type="Resource" id="Effect_patrol_safety"]
|
||||
script = ExtResource("2")
|
||||
effect_id = &"patrol_safety"
|
||||
effect_kind = &"metric_delta"
|
||||
subject_key = &"safety"
|
||||
value = 1.0
|
||||
parameters = {"npc_productivity": true, "player_multiplier": 3.0}
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1")
|
||||
@@ -11,3 +20,6 @@ preferred_profession_id = &"guard"
|
||||
target_type = &"activity"
|
||||
completion_cost_resource_id = &"wood"
|
||||
completion_cost_amount = 1.0
|
||||
completion_handler_id = &"activity_metric_delta"
|
||||
effects = [SubResource("Effect_patrol_safety")]
|
||||
event_type_id = &"activity_completed"
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
[gd_resource type="Resource" script_class="ActionDefinition" load_steps=2 format=3]
|
||||
[gd_resource type="Resource" script_class="ActionDefinition" load_steps=3 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://simulation/definitions/ActionDefinition.gd" id="1"]
|
||||
[ext_resource type="Script" path="res://simulation/commands/ActionEffect.gd" id="2"]
|
||||
|
||||
[sub_resource type="Resource" id="Effect_study_knowledge"]
|
||||
script = ExtResource("2")
|
||||
effect_id = &"study_knowledge"
|
||||
effect_kind = &"metric_delta"
|
||||
subject_key = &"knowledge"
|
||||
value = 1.0
|
||||
parameters = {"npc_productivity": true, "player_multiplier": 2.0}
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1")
|
||||
@@ -11,3 +20,6 @@ preferred_profession_id = &"scholar"
|
||||
target_type = &"activity"
|
||||
completion_cost_resource_id = &"wood"
|
||||
completion_cost_amount = 1.0
|
||||
completion_handler_id = &"activity_metric_delta"
|
||||
effects = [SubResource("Effect_study_knowledge")]
|
||||
event_type_id = &"activity_completed"
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
[gd_resource type="Resource" script_class="EventTypeDefinition" load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://simulation/definitions/EventTypeDefinition.gd" id="1"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1")
|
||||
event_type_id = &"activity_completed"
|
||||
display_name = "Activity Completed"
|
||||
description = "An actor completed a world-backed activity and changed authoritative state."
|
||||
category = "activity"
|
||||
@@ -1,4 +1,4 @@
|
||||
[gd_resource type="Resource" script_class="SimulationContentPack" load_steps=93 format=3]
|
||||
[gd_resource type="Resource" script_class="SimulationContentPack" load_steps=94 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://simulation/definitions/SimulationContentPack.gd" id="1_pack"]
|
||||
[ext_resource type="Resource" path="res://simulation/definitions/actions/defend.tres" id="2_defend"]
|
||||
@@ -81,6 +81,7 @@
|
||||
[ext_resource type="Resource" path="res://simulation/definitions/event_types/war_resolved.tres" id="79_war_resolved"]
|
||||
[ext_resource type="Resource" path="res://simulation/definitions/event_types/war_aborted.tres" id="80_war_aborted"]
|
||||
[ext_resource type="Resource" path="res://simulation/definitions/event_types/wolf_hunt.tres" id="81_wolf_hunt"]
|
||||
[ext_resource type="Resource" path="res://simulation/definitions/event_types/activity_completed.tres" id="92_activity_completed"]
|
||||
[ext_resource type="Resource" path="res://simulation/definitions/social_consequences/storage_food_aid_trust.tres" id="82_storage_food_aid"]
|
||||
[ext_resource type="Resource" path="res://simulation/definitions/social_consequences/player_food_aid_trust.tres" id="83_player_food_aid"]
|
||||
[ext_resource type="Resource" path="res://simulation/definitions/social_consequences/commitment_accepted_obligation.tres" id="84_accepted_obligation"]
|
||||
@@ -96,6 +97,7 @@
|
||||
script = ExtResource("1_pack")
|
||||
pack_id = &"core"
|
||||
display_name = "The Steward Core"
|
||||
required_handler_ids = [&"activity_metric_delta"]
|
||||
actions = [ExtResource("2_defend"), ExtResource("3_deposit_food"), ExtResource("4_deposit_wood"), ExtResource("5_eat"), ExtResource("6_feed_animal"), ExtResource("7_gather_food"), ExtResource("8_gather_wood"), ExtResource("9_patrol"), ExtResource("10_rest"), ExtResource("11_sleep"), ExtResource("12_study"), ExtResource("13_wander"), ExtResource("14_withdraw_food")]
|
||||
professions = [ExtResource("15_farmer"), ExtResource("16_guard"), ExtResource("17_scholar"), ExtResource("18_wanderer"), ExtResource("19_woodcutter")]
|
||||
capability_tags = [ExtResource("28_item_food"), ExtResource("29_item_medicine"), ExtResource("30_item_wood"), ExtResource("31_resource_harvestable"), ExtResource("32_storage_village")]
|
||||
@@ -107,5 +109,5 @@ animals = [ExtResource("39_goat"), ExtResource("40_sheep")]
|
||||
situations = [ExtResource("41_pantry_situation")]
|
||||
dialogue_intents = [ExtResource("42_accept"), ExtResource("43_acknowledge"), ExtResource("44_wellbeing"), ExtResource("45_happened"), ExtResource("46_who"), ExtResource("47_work"), ExtResource("48_decline"), ExtResource("49_need"), ExtResource("50_goodbye"), ExtResource("51_greet"), ExtResource("52_offer"), ExtResource("53_renegotiate"), ExtResource("54_progress"), ExtResource("55_reproach"), ExtResource("56_share"), ExtResource("57_thank")]
|
||||
dialogue_template_catalogs = [ExtResource("58_templates")]
|
||||
event_types = [ExtResource("59_resource_extracted"), ExtResource("60_storage_deposited"), ExtResource("61_storage_withdrawn"), ExtResource("62_item_consumed"), ExtResource("63_npc_slept"), ExtResource("64_npc_died"), ExtResource("65_task_started"), ExtResource("66_task_blocked"), ExtResource("67_resource_depleted"), ExtResource("68_animal_fed"), ExtResource("69_villager_weak"), ExtResource("70_home_damaged"), ExtResource("71_commitment_accepted"), ExtResource("72_commitment_fulfilled"), ExtResource("73_commitment_broken"), ExtResource("74_commitment_released"), ExtResource("75_commitment_superseded"), ExtResource("76_combatant_hurt"), ExtResource("77_combatant_killed"), ExtResource("78_raid_started"), ExtResource("79_war_resolved"), ExtResource("80_war_aborted"), ExtResource("81_wolf_hunt")]
|
||||
event_types = [ExtResource("59_resource_extracted"), ExtResource("60_storage_deposited"), ExtResource("61_storage_withdrawn"), ExtResource("62_item_consumed"), ExtResource("63_npc_slept"), ExtResource("64_npc_died"), ExtResource("65_task_started"), ExtResource("66_task_blocked"), ExtResource("67_resource_depleted"), ExtResource("68_animal_fed"), ExtResource("69_villager_weak"), ExtResource("70_home_damaged"), ExtResource("71_commitment_accepted"), ExtResource("72_commitment_fulfilled"), ExtResource("73_commitment_broken"), ExtResource("74_commitment_released"), ExtResource("75_commitment_superseded"), ExtResource("76_combatant_hurt"), ExtResource("77_combatant_killed"), ExtResource("78_raid_started"), ExtResource("79_war_resolved"), ExtResource("80_war_aborted"), ExtResource("81_wolf_hunt"), ExtResource("92_activity_completed")]
|
||||
social_consequences = [ExtResource("82_storage_food_aid"), ExtResource("83_player_food_aid"), ExtResource("84_accepted_obligation"), ExtResource("85_fulfilled_obligation"), ExtResource("86_fulfilled_trust"), ExtResource("87_broken_obligation"), ExtResource("88_broken_trust"), ExtResource("89_broken_hostility"), ExtResource("90_released_obligation"), ExtResource("91_superseded_obligation")]
|
||||
|
||||
Reference in New Issue
Block a user