feat: spawn enemies from reusable definitions
This commit is contained in:
@@ -3,8 +3,12 @@ extends RefCounted
|
||||
|
||||
signal changed(state: CombatantStateRecord)
|
||||
|
||||
const SCHEMA_VERSION := 1
|
||||
const SCHEMA_VERSION := 2
|
||||
const LEGACY_SCHEMA_VERSION := 1
|
||||
const NO_NPC_ID := -1
|
||||
const NO_ENEMY_DEFINITION_ID := &""
|
||||
const LEGACY_RAIDER_DEFINITION_ID := &"enemy_raider"
|
||||
const LEGACY_WOLF_DEFINITION_ID := &"enemy_wolf"
|
||||
|
||||
var data: Dictionary
|
||||
|
||||
@@ -26,9 +30,13 @@ static func create(
|
||||
max_health: float,
|
||||
weapon_id: StringName,
|
||||
npc_id: int = NO_NPC_ID,
|
||||
hostile: bool = false
|
||||
hostile: bool = false,
|
||||
enemy_definition_id: StringName = NO_ENEMY_DEFINITION_ID
|
||||
) -> CombatantStateRecord:
|
||||
var snapped_position := Vector3(_snap(position.x), _snap(position.y), _snap(position.z))
|
||||
var canonical_enemy_definition_id := enemy_definition_id
|
||||
if canonical_enemy_definition_id.is_empty():
|
||||
canonical_enemy_definition_id = _legacy_definition_id_for_kind(kind)
|
||||
return (
|
||||
CombatantStateRecord
|
||||
. new(
|
||||
@@ -39,6 +47,7 @@ static func create(
|
||||
"faction_id": String(faction_id),
|
||||
"display_name": display_name,
|
||||
"npc_id": npc_id,
|
||||
"enemy_definition_id": String(canonical_enemy_definition_id),
|
||||
"position": [snapped_position.x, snapped_position.y, snapped_position.z],
|
||||
"health": max_health,
|
||||
"max_health": max_health,
|
||||
@@ -52,10 +61,17 @@ static func create(
|
||||
|
||||
|
||||
static func from_dictionary(record_data: Dictionary) -> CombatantStateRecord:
|
||||
if int(record_data.get("schema_version", -1)) != SCHEMA_VERSION:
|
||||
var version := int(record_data.get("schema_version", -1))
|
||||
var normalized := record_data.duplicate(true)
|
||||
if version == LEGACY_SCHEMA_VERSION:
|
||||
normalized["schema_version"] = SCHEMA_VERSION
|
||||
normalized["enemy_definition_id"] = String(
|
||||
_legacy_definition_id_for_kind(StringName(normalized.get("kind", &"")))
|
||||
)
|
||||
elif version != SCHEMA_VERSION:
|
||||
return null
|
||||
if not (
|
||||
record_data
|
||||
normalized
|
||||
. has_all(
|
||||
[
|
||||
"combatant_id",
|
||||
@@ -63,6 +79,7 @@ static func from_dictionary(record_data: Dictionary) -> CombatantStateRecord:
|
||||
"faction_id",
|
||||
"display_name",
|
||||
"npc_id",
|
||||
"enemy_definition_id",
|
||||
"position",
|
||||
"health",
|
||||
"max_health",
|
||||
@@ -74,12 +91,12 @@ static func from_dictionary(record_data: Dictionary) -> CombatantStateRecord:
|
||||
)
|
||||
):
|
||||
return null
|
||||
var combatant_id := String(record_data["combatant_id"])
|
||||
var display_name := String(record_data["display_name"])
|
||||
var combatant_id := String(normalized["combatant_id"])
|
||||
var display_name := String(normalized["display_name"])
|
||||
if combatant_id.is_empty() or display_name.is_empty():
|
||||
return null
|
||||
if (
|
||||
StringName(record_data["kind"])
|
||||
StringName(normalized["kind"])
|
||||
not in [
|
||||
SimulationIds.COMBATANT_KIND_NPC,
|
||||
SimulationIds.COMBATANT_KIND_RAIDER,
|
||||
@@ -88,15 +105,20 @@ static func from_dictionary(record_data: Dictionary) -> CombatantStateRecord:
|
||||
]
|
||||
):
|
||||
return null
|
||||
var saved_position = record_data["position"]
|
||||
var kind := StringName(normalized["kind"])
|
||||
var enemy_definition_id := StringName(normalized["enemy_definition_id"])
|
||||
var is_enemy := kind in [SimulationIds.COMBATANT_KIND_RAIDER, SimulationIds.COMBATANT_KIND_WOLF]
|
||||
if is_enemy == enemy_definition_id.is_empty():
|
||||
return null
|
||||
var saved_position = normalized["position"]
|
||||
if not saved_position is Array or saved_position.size() != 3:
|
||||
return null
|
||||
for component in saved_position:
|
||||
if not is_finite(float(component)):
|
||||
return null
|
||||
var health := float(record_data["health"])
|
||||
var max_health := float(record_data["max_health"])
|
||||
var npc_id := int(record_data["npc_id"])
|
||||
var health := float(normalized["health"])
|
||||
var max_health := float(normalized["max_health"])
|
||||
var npc_id := int(normalized["npc_id"])
|
||||
if (
|
||||
not is_finite(health)
|
||||
or not is_finite(max_health)
|
||||
@@ -104,16 +126,16 @@ static func from_dictionary(record_data: Dictionary) -> CombatantStateRecord:
|
||||
or max_health <= 0.0
|
||||
or health > max_health
|
||||
or npc_id < NO_NPC_ID
|
||||
or int(record_data["attack_cooldown"]) < 0
|
||||
or int(normalized["attack_cooldown"]) < 0
|
||||
):
|
||||
return null
|
||||
var normalized := record_data.duplicate(true)
|
||||
normalized["schema_version"] = SCHEMA_VERSION
|
||||
normalized["combatant_id"] = combatant_id
|
||||
normalized["kind"] = String(record_data["kind"])
|
||||
normalized["faction_id"] = String(record_data["faction_id"])
|
||||
normalized["kind"] = String(kind)
|
||||
normalized["faction_id"] = String(normalized["faction_id"])
|
||||
normalized["display_name"] = display_name
|
||||
normalized["npc_id"] = npc_id
|
||||
normalized["enemy_definition_id"] = String(enemy_definition_id)
|
||||
normalized["position"] = [
|
||||
_snap(float(saved_position[0])),
|
||||
_snap(float(saved_position[1])),
|
||||
@@ -121,10 +143,10 @@ static func from_dictionary(record_data: Dictionary) -> CombatantStateRecord:
|
||||
]
|
||||
normalized["health"] = health
|
||||
normalized["max_health"] = max_health
|
||||
normalized["weapon_id"] = String(record_data["weapon_id"])
|
||||
normalized["alive"] = bool(record_data["alive"])
|
||||
normalized["hostile"] = bool(record_data["hostile"])
|
||||
normalized["attack_cooldown"] = int(record_data["attack_cooldown"])
|
||||
normalized["weapon_id"] = String(normalized["weapon_id"])
|
||||
normalized["alive"] = bool(normalized["alive"])
|
||||
normalized["hostile"] = bool(normalized["hostile"])
|
||||
normalized["attack_cooldown"] = int(normalized["attack_cooldown"])
|
||||
return CombatantStateRecord.new(normalized)
|
||||
|
||||
|
||||
@@ -148,6 +170,10 @@ func get_npc_id() -> int:
|
||||
return int(data["npc_id"])
|
||||
|
||||
|
||||
func get_enemy_definition_id() -> StringName:
|
||||
return StringName(data["enemy_definition_id"])
|
||||
|
||||
|
||||
func get_position() -> Vector3:
|
||||
var saved_position: Array = data["position"]
|
||||
return Vector3(float(saved_position[0]), float(saved_position[1]), float(saved_position[2]))
|
||||
@@ -235,3 +261,11 @@ func _weapon_cooldown_ticks(tick_interval: float) -> int:
|
||||
|
||||
func to_dictionary() -> Dictionary:
|
||||
return data.duplicate(true)
|
||||
|
||||
|
||||
static func _legacy_definition_id_for_kind(kind: StringName) -> StringName:
|
||||
if kind == SimulationIds.COMBATANT_KIND_RAIDER:
|
||||
return LEGACY_RAIDER_DEFINITION_ID
|
||||
if kind == SimulationIds.COMBATANT_KIND_WOLF:
|
||||
return LEGACY_WOLF_DEFINITION_ID
|
||||
return NO_ENEMY_DEFINITION_ID
|
||||
|
||||
Reference in New Issue
Block a user