merge: integrate remote player quest slices
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
class_name PlayerQuestRecord
|
||||
extends RefCounted
|
||||
|
||||
const SCHEMA_VERSION := 2
|
||||
const LEGACY_SCHEMA_VERSION := 1
|
||||
const STATUS_OPEN := &"open"
|
||||
const STATUS_COMPLETED := &"completed"
|
||||
const STATUS_EXPIRED := &"expired"
|
||||
const NO_OPPORTUNITY := -1
|
||||
|
||||
var data: Dictionary
|
||||
|
||||
|
||||
func _init(record_data: Dictionary = {}) -> void:
|
||||
data = record_data.duplicate(true)
|
||||
|
||||
|
||||
static func create(
|
||||
quest_id: int,
|
||||
opportunity_id: int,
|
||||
requester_npc_id: int,
|
||||
quest_type: StringName,
|
||||
resource_id: StringName,
|
||||
target_id: StringName,
|
||||
target_amount: float,
|
||||
created_tick: int,
|
||||
standing_reward: float,
|
||||
animal_id: StringName = &""
|
||||
) -> PlayerQuestRecord:
|
||||
return (
|
||||
PlayerQuestRecord
|
||||
. new(
|
||||
{
|
||||
"schema_version": SCHEMA_VERSION,
|
||||
"quest_id": quest_id,
|
||||
"opportunity_id": opportunity_id,
|
||||
"requester_npc_id": requester_npc_id,
|
||||
"quest_type": String(quest_type),
|
||||
"resource_id": String(resource_id),
|
||||
"target_id": String(target_id),
|
||||
"target_amount": target_amount,
|
||||
"status": String(STATUS_OPEN),
|
||||
"created_tick": created_tick,
|
||||
"standing_reward": standing_reward,
|
||||
"resolution_event_id": -1,
|
||||
"resolved_tick": -1,
|
||||
"animal_id": String(animal_id),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
static func from_dictionary(record_data: Dictionary) -> PlayerQuestRecord:
|
||||
var version := int(record_data.get("schema_version", -1))
|
||||
if version not in [LEGACY_SCHEMA_VERSION, SCHEMA_VERSION]:
|
||||
return null
|
||||
var normalized := record_data.duplicate(true)
|
||||
if version == LEGACY_SCHEMA_VERSION:
|
||||
normalized["schema_version"] = SCHEMA_VERSION
|
||||
normalized["animal_id"] = ""
|
||||
if not (
|
||||
normalized
|
||||
. has_all(
|
||||
[
|
||||
"quest_id",
|
||||
"opportunity_id",
|
||||
"requester_npc_id",
|
||||
"quest_type",
|
||||
"resource_id",
|
||||
"target_id",
|
||||
"target_amount",
|
||||
"status",
|
||||
"created_tick",
|
||||
"standing_reward",
|
||||
"resolution_event_id",
|
||||
"resolved_tick",
|
||||
"animal_id",
|
||||
]
|
||||
)
|
||||
):
|
||||
return null
|
||||
var quest_id := int(normalized["quest_id"])
|
||||
var opportunity_id := int(normalized["opportunity_id"])
|
||||
var requester_npc_id := int(normalized["requester_npc_id"])
|
||||
var quest_type := StringName(normalized["quest_type"])
|
||||
var resource_id := StringName(normalized["resource_id"])
|
||||
var target_id := StringName(normalized["target_id"])
|
||||
var target_amount := float(normalized["target_amount"])
|
||||
var status := StringName(normalized["status"])
|
||||
var created_tick := int(normalized["created_tick"])
|
||||
var standing_reward := float(normalized["standing_reward"])
|
||||
var resolution_event_id := int(normalized["resolution_event_id"])
|
||||
var resolved_tick := int(normalized["resolved_tick"])
|
||||
var animal_id := StringName(normalized["animal_id"])
|
||||
if (
|
||||
quest_id < 0
|
||||
or requester_npc_id < 0
|
||||
or quest_type.is_empty()
|
||||
or resource_id.is_empty()
|
||||
or target_id.is_empty()
|
||||
or not is_finite(target_amount)
|
||||
or target_amount <= 0.0
|
||||
or created_tick < 0
|
||||
or not is_finite(standing_reward)
|
||||
or standing_reward <= 0.0
|
||||
or resolution_event_id < -1
|
||||
or (opportunity_id < 0 and animal_id.is_empty())
|
||||
or (not animal_id.is_empty() and opportunity_id != NO_OPPORTUNITY)
|
||||
):
|
||||
return null
|
||||
match status:
|
||||
STATUS_OPEN:
|
||||
if resolution_event_id != -1 or resolved_tick != -1:
|
||||
return null
|
||||
STATUS_COMPLETED:
|
||||
if resolution_event_id < 0 or resolved_tick < created_tick:
|
||||
return null
|
||||
STATUS_EXPIRED:
|
||||
if resolution_event_id != -1 or resolved_tick < created_tick:
|
||||
return null
|
||||
_:
|
||||
return null
|
||||
return (
|
||||
PlayerQuestRecord
|
||||
. new(
|
||||
{
|
||||
"schema_version": SCHEMA_VERSION,
|
||||
"quest_id": quest_id,
|
||||
"opportunity_id": opportunity_id,
|
||||
"requester_npc_id": requester_npc_id,
|
||||
"quest_type": String(quest_type),
|
||||
"resource_id": String(resource_id),
|
||||
"target_id": String(target_id),
|
||||
"target_amount": target_amount,
|
||||
"status": String(status),
|
||||
"created_tick": created_tick,
|
||||
"standing_reward": standing_reward,
|
||||
"resolution_event_id": resolution_event_id,
|
||||
"resolved_tick": resolved_tick,
|
||||
"animal_id": String(animal_id),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
func get_quest_id() -> int:
|
||||
return int(data["quest_id"])
|
||||
|
||||
|
||||
func get_opportunity_id() -> int:
|
||||
return int(data["opportunity_id"])
|
||||
|
||||
|
||||
func get_requester_npc_id() -> int:
|
||||
return int(data["requester_npc_id"])
|
||||
|
||||
|
||||
func get_quest_type() -> StringName:
|
||||
return StringName(data["quest_type"])
|
||||
|
||||
|
||||
func get_resource_id() -> StringName:
|
||||
return StringName(data["resource_id"])
|
||||
|
||||
|
||||
func get_target_id() -> StringName:
|
||||
return StringName(data["target_id"])
|
||||
|
||||
|
||||
func get_target_amount() -> float:
|
||||
return float(data["target_amount"])
|
||||
|
||||
|
||||
func get_status() -> StringName:
|
||||
return StringName(data["status"])
|
||||
|
||||
|
||||
func get_created_tick() -> int:
|
||||
return int(data["created_tick"])
|
||||
|
||||
|
||||
func get_standing_reward() -> float:
|
||||
return float(data["standing_reward"])
|
||||
|
||||
|
||||
func get_resolution_event_id() -> int:
|
||||
return int(data["resolution_event_id"])
|
||||
|
||||
|
||||
func get_resolved_tick() -> int:
|
||||
return int(data["resolved_tick"])
|
||||
|
||||
|
||||
func get_animal_id() -> StringName:
|
||||
return StringName(data["animal_id"])
|
||||
|
||||
|
||||
func has_animal_target() -> bool:
|
||||
return not get_animal_id().is_empty()
|
||||
|
||||
|
||||
func is_open() -> bool:
|
||||
return get_status() == STATUS_OPEN
|
||||
|
||||
|
||||
func complete(resolution_event_id: int, resolved_tick: int) -> bool:
|
||||
if not is_open() or resolution_event_id < 0 or resolved_tick < get_created_tick():
|
||||
return false
|
||||
data["status"] = String(STATUS_COMPLETED)
|
||||
data["resolution_event_id"] = resolution_event_id
|
||||
data["resolved_tick"] = resolved_tick
|
||||
return true
|
||||
|
||||
|
||||
func expire(resolved_tick: int) -> bool:
|
||||
if not is_open() or resolved_tick < get_created_tick():
|
||||
return false
|
||||
data["status"] = String(STATUS_EXPIRED)
|
||||
data["resolved_tick"] = resolved_tick
|
||||
return true
|
||||
|
||||
|
||||
func to_dictionary() -> Dictionary:
|
||||
return data.duplicate(true)
|
||||
@@ -0,0 +1 @@
|
||||
uid://rix0e3rygdoo
|
||||
@@ -0,0 +1,120 @@
|
||||
class_name PlayerStandingRecord
|
||||
extends RefCounted
|
||||
|
||||
const SCHEMA_VERSION := 1
|
||||
const MAX_STANDING := 100.0
|
||||
const TIER_STRANGER := 0
|
||||
const TIER_KNOWN_HAND := 1
|
||||
const TIER_TRUSTED := 2
|
||||
const TIER_VILLAGE_STEWARD := 3
|
||||
const TIER_VOICE_OF_JAJCE := 4
|
||||
|
||||
const TIER_THRESHOLDS := {
|
||||
TIER_STRANGER: 0.0,
|
||||
TIER_KNOWN_HAND: 15.0,
|
||||
TIER_TRUSTED: 35.0,
|
||||
TIER_VILLAGE_STEWARD: 60.0,
|
||||
TIER_VOICE_OF_JAJCE: 85.0,
|
||||
}
|
||||
|
||||
const TIER_NAMES := {
|
||||
TIER_STRANGER: "Stranger",
|
||||
TIER_KNOWN_HAND: "Known Hand",
|
||||
TIER_TRUSTED: "Trusted",
|
||||
TIER_VILLAGE_STEWARD: "Village Steward",
|
||||
TIER_VOICE_OF_JAJCE: "Voice of Jajce",
|
||||
}
|
||||
|
||||
var data: Dictionary
|
||||
|
||||
|
||||
func _init(record_data: Dictionary = {}) -> void:
|
||||
data = record_data.duplicate(true)
|
||||
|
||||
|
||||
static func create() -> PlayerStandingRecord:
|
||||
return (
|
||||
PlayerStandingRecord
|
||||
. new(
|
||||
{
|
||||
"schema_version": SCHEMA_VERSION,
|
||||
"standing": 0.0,
|
||||
"resolved_needs": 0,
|
||||
"gratitude": {},
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
static func from_dictionary(record_data: Dictionary) -> PlayerStandingRecord:
|
||||
if int(record_data.get("schema_version", -1)) != SCHEMA_VERSION:
|
||||
return null
|
||||
if not record_data.has_all(["standing", "resolved_needs", "gratitude"]):
|
||||
return null
|
||||
var standing := float(record_data["standing"])
|
||||
var resolved_needs := int(record_data["resolved_needs"])
|
||||
var gratitude = record_data["gratitude"]
|
||||
if (
|
||||
not is_finite(standing)
|
||||
or standing < 0.0
|
||||
or standing > MAX_STANDING
|
||||
or resolved_needs < 0
|
||||
or not gratitude is Dictionary
|
||||
):
|
||||
return null
|
||||
var normalized_gratitude := {}
|
||||
for raw_npc_id in gratitude:
|
||||
var npc_id := int(raw_npc_id)
|
||||
var value := float(gratitude[raw_npc_id])
|
||||
if npc_id < 0 or not is_finite(value) or value < 0.0 or value > 1.0:
|
||||
return null
|
||||
normalized_gratitude[npc_id] = value
|
||||
return (
|
||||
PlayerStandingRecord
|
||||
. new(
|
||||
{
|
||||
"schema_version": SCHEMA_VERSION,
|
||||
"standing": standing,
|
||||
"resolved_needs": resolved_needs,
|
||||
"gratitude": normalized_gratitude,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
func grant_standing(amount: float, requester_npc_id: int) -> void:
|
||||
if not is_finite(amount) or amount <= 0.0 or requester_npc_id < 0:
|
||||
return
|
||||
data["standing"] = minf(get_standing() + amount, MAX_STANDING)
|
||||
data["resolved_needs"] = int(data["resolved_needs"]) + 1
|
||||
var gratitude := get_gratitude(requester_npc_id)
|
||||
data["gratitude"][requester_npc_id] = minf(gratitude + 0.1, 1.0)
|
||||
|
||||
|
||||
func get_standing() -> float:
|
||||
return float(data["standing"])
|
||||
|
||||
|
||||
func get_resolved_needs() -> int:
|
||||
return int(data["resolved_needs"])
|
||||
|
||||
|
||||
func get_gratitude(npc_id: int) -> float:
|
||||
return float(data["gratitude"].get(npc_id, 0.0))
|
||||
|
||||
|
||||
func get_tier() -> int:
|
||||
var current := TIER_STRANGER
|
||||
for tier in [TIER_VOICE_OF_JAJCE, TIER_VILLAGE_STEWARD, TIER_TRUSTED, TIER_KNOWN_HAND]:
|
||||
if get_standing() >= float(TIER_THRESHOLDS[tier]):
|
||||
current = tier
|
||||
break
|
||||
return current
|
||||
|
||||
|
||||
func get_tier_name() -> String:
|
||||
return String(TIER_NAMES[get_tier()])
|
||||
|
||||
|
||||
func to_dictionary() -> Dictionary:
|
||||
return data.duplicate(true)
|
||||
@@ -0,0 +1 @@
|
||||
uid://v827brnqruus
|
||||
@@ -19,6 +19,8 @@ const CONFLICT_SCHEMA_VERSION := 13
|
||||
const PRE_EMERGENT_SCHEMA_VERSION := 14
|
||||
const EMERGENT_SCHEMA_VERSION := 15
|
||||
const PREVIOUS_SCHEMA_VERSION := 7
|
||||
const PLAYER_LEGACY_SCHEMA_VERSION := 11
|
||||
const PLAYER_STATE_LEGACY_SCHEMA_VERSION := 12
|
||||
|
||||
var simulation: Dictionary
|
||||
var village: VillageStateRecord
|
||||
@@ -37,6 +39,9 @@ var conversation_history: Array[ConversationActStateRecord] = []
|
||||
var player: PlayerStateRecord
|
||||
var combatants: Array[CombatantStateRecord] = []
|
||||
var factions: Array[FactionStateRecord] = []
|
||||
var player_standing: PlayerStandingRecord
|
||||
var player_quests: Array[PlayerQuestRecord] = []
|
||||
var player_state: PlayerStateRecord
|
||||
|
||||
|
||||
func to_dictionary() -> Dictionary:
|
||||
@@ -83,6 +88,9 @@ func to_dictionary() -> Dictionary:
|
||||
var faction_data: Array[Dictionary] = []
|
||||
for faction_record in factions:
|
||||
faction_data.append(faction_record.to_dictionary())
|
||||
var player_quest_data: Array[Dictionary] = []
|
||||
for player_quest_record in player_quests:
|
||||
player_quest_data.append(player_quest_record.to_dictionary())
|
||||
|
||||
return {
|
||||
"schema": SCHEMA_NAME,
|
||||
@@ -103,7 +111,10 @@ func to_dictionary() -> Dictionary:
|
||||
"conversation_history": conversation_history_data,
|
||||
"player": player.to_dictionary(),
|
||||
"combatants": combatant_data,
|
||||
"factions": faction_data
|
||||
"factions": faction_data,
|
||||
"player_standing": player_standing.to_dictionary(),
|
||||
"player_quests": player_quest_data,
|
||||
"player_state": player_state.to_dictionary()
|
||||
}
|
||||
|
||||
|
||||
@@ -140,6 +151,8 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
||||
CONFLICT_SCHEMA_VERSION,
|
||||
PRE_EMERGENT_SCHEMA_VERSION,
|
||||
EMERGENT_SCHEMA_VERSION,
|
||||
PLAYER_LEGACY_SCHEMA_VERSION,
|
||||
PLAYER_STATE_LEGACY_SCHEMA_VERSION,
|
||||
]
|
||||
):
|
||||
record_data = _migrate_legacy(record_data, version)
|
||||
@@ -166,6 +179,9 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
||||
"player",
|
||||
"combatants",
|
||||
"factions",
|
||||
"player_standing",
|
||||
"player_quests",
|
||||
"player_state",
|
||||
]
|
||||
)
|
||||
):
|
||||
@@ -551,6 +567,7 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
||||
record.relationships.append(relationship_record)
|
||||
|
||||
var opportunity_ids := {}
|
||||
var opportunity_records_by_id := {}
|
||||
var trigger_event_ids := {}
|
||||
var resolution_event_ids := {}
|
||||
var has_open_opportunity := false
|
||||
@@ -585,6 +602,7 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
||||
):
|
||||
return null
|
||||
opportunity_ids[opportunity_id] = true
|
||||
opportunity_records_by_id[opportunity_id] = opportunity_record
|
||||
trigger_event_ids[trigger_event_id] = true
|
||||
highest_opportunity_id = maxi(highest_opportunity_id, opportunity_id)
|
||||
record.opportunities.append(opportunity_record)
|
||||
@@ -620,6 +638,72 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
||||
record.conversation_history.append(conversation_act)
|
||||
if saved_next_conversation_act_id <= highest_conversation_act_id:
|
||||
return null
|
||||
var standing_data = record_data["player_standing"]
|
||||
if not standing_data is Dictionary:
|
||||
return null
|
||||
var standing_record := PlayerStandingRecord.from_dictionary(standing_data)
|
||||
if standing_record == null:
|
||||
return null
|
||||
record.player_standing = standing_record
|
||||
|
||||
var player_quest_data = record_data["player_quests"]
|
||||
if not player_quest_data is Array:
|
||||
return null
|
||||
var quest_ids := {}
|
||||
var quest_opportunity_ids := {}
|
||||
var highest_quest_id := -1
|
||||
for item in player_quest_data:
|
||||
if not item is Dictionary:
|
||||
return null
|
||||
var quest_record := PlayerQuestRecord.from_dictionary(item)
|
||||
if quest_record == null:
|
||||
return null
|
||||
var quest_id := quest_record.get_quest_id()
|
||||
var quest_opportunity_id := quest_record.get_opportunity_id()
|
||||
var requester_id := quest_record.get_requester_npc_id()
|
||||
var has_valid_requester := npc_ids.has(requester_id)
|
||||
var has_valid_opportunity := (
|
||||
quest_record.has_animal_target() or opportunity_ids.has(quest_opportunity_id)
|
||||
)
|
||||
if (
|
||||
quest_ids.has(quest_id)
|
||||
or quest_opportunity_ids.has(quest_opportunity_id)
|
||||
or not has_valid_requester
|
||||
or not has_valid_opportunity
|
||||
):
|
||||
return null
|
||||
if (
|
||||
quest_record.get_status() == PlayerQuestRecord.STATUS_OPEN
|
||||
and (
|
||||
(not quest_record.has_animal_target())
|
||||
and not opportunity_records_by_id.has(quest_opportunity_id)
|
||||
)
|
||||
):
|
||||
return null
|
||||
if (
|
||||
quest_record.get_resolution_event_id() >= 0
|
||||
and not event_ids.has(quest_record.get_resolution_event_id())
|
||||
):
|
||||
return null
|
||||
var dedup_key := (
|
||||
"animal:%s" % String(quest_record.get_animal_id())
|
||||
if quest_record.has_animal_target()
|
||||
else "opp:%d" % quest_opportunity_id
|
||||
)
|
||||
if quest_ids.has(quest_id) or quest_opportunity_ids.has(dedup_key):
|
||||
return null
|
||||
quest_ids[quest_id] = true
|
||||
quest_opportunity_ids[dedup_key] = true
|
||||
highest_quest_id = maxi(highest_quest_id, quest_id)
|
||||
record.player_quests.append(quest_record)
|
||||
|
||||
var player_state_data = record_data["player_state"]
|
||||
if not player_state_data is Dictionary:
|
||||
return null
|
||||
var player_state_record := PlayerStateRecord.from_dictionary(player_state_data)
|
||||
if player_state_record == null:
|
||||
return null
|
||||
record.player_state = player_state_record
|
||||
|
||||
return record
|
||||
|
||||
@@ -940,7 +1024,10 @@ static func _is_valid_supply_resolution(
|
||||
return false
|
||||
if event_type == SimulationIds.EVENT_STORAGE_DEPOSITED:
|
||||
if actor_id == SimulationIds.PLAYER_ACTOR_ID:
|
||||
return StringName(event.data["source_id"]) == SimulationIds.PLAYER_INVENTORY_ID
|
||||
return (
|
||||
StringName(event.data["source_id"])
|
||||
in [SimulationIds.PLAYER_INVENTORY_ID, &"player_carry"]
|
||||
)
|
||||
return (
|
||||
npc_ids.has(actor_id)
|
||||
and StringName(event.data["source_id"]) == SimulationIds.npc_inventory_id(actor_id)
|
||||
@@ -1066,6 +1153,14 @@ static func _migrate_legacy(legacy_data: Dictionary, version: int) -> Dictionary
|
||||
var opportunity_simulation_data: Dictionary = migrated.get("simulation", {})
|
||||
opportunity_simulation_data["next_opportunity_id"] = 0
|
||||
migrated["simulation"] = opportunity_simulation_data
|
||||
if version <= PLAYER_LEGACY_SCHEMA_VERSION:
|
||||
migrated["player_standing"] = PlayerStandingRecord.create().to_dictionary()
|
||||
migrated["player_quests"] = []
|
||||
var quest_simulation_data: Dictionary = migrated.get("simulation", {})
|
||||
quest_simulation_data["next_quest_id"] = 0
|
||||
migrated["simulation"] = quest_simulation_data
|
||||
if version <= PLAYER_STATE_LEGACY_SCHEMA_VERSION:
|
||||
migrated["player_state"] = PlayerStateRecord.create().to_dictionary()
|
||||
return migrated
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,12 @@ extends RefCounted
|
||||
const SCHEMA_VERSION := 2
|
||||
const LEGACY_SCHEMA_VERSION := 1
|
||||
const MAX_HEALTH := 100.0
|
||||
const RESET_HUNGER := 40.0
|
||||
const RESET_ENERGY := 100.0
|
||||
const STACK_THRESHOLD := 80.0
|
||||
const DEATH_STANDING_PENALTY := 0.85
|
||||
const DEFAULT_STARVATION_DEATH_THRESHOLD := 1
|
||||
const DEFAULT_CARRY_CAPACITY := 3.0
|
||||
|
||||
var data: Dictionary
|
||||
|
||||
@@ -18,18 +24,30 @@ static func create_default() -> PlayerStateRecord:
|
||||
. new(
|
||||
{
|
||||
"schema_version": SCHEMA_VERSION,
|
||||
"hunger": 40.0,
|
||||
"energy": 100.0,
|
||||
"hunger": RESET_HUNGER,
|
||||
"energy": RESET_ENERGY,
|
||||
"health": MAX_HEALTH,
|
||||
"max_health": MAX_HEALTH,
|
||||
"downed": false,
|
||||
"downed_ticks": 0,
|
||||
"dead": false,
|
||||
"deaths": 0,
|
||||
"starvation_ticks": 0,
|
||||
"starvation_death_threshold": DEFAULT_STARVATION_DEATH_THRESHOLD,
|
||||
"carry_capacity": DEFAULT_CARRY_CAPACITY,
|
||||
"inventory": {},
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# Compatibility constructor for the first embodied-player slice. New code
|
||||
# should use create_default(), but keeping this alias lets older quest/needs
|
||||
# resources load without introducing a second player-state schema.
|
||||
static func create() -> PlayerStateRecord:
|
||||
return create_default()
|
||||
|
||||
|
||||
static func from_dictionary(record_data: Dictionary) -> PlayerStateRecord:
|
||||
var version := int(record_data.get("schema_version", -1))
|
||||
if version == LEGACY_SCHEMA_VERSION:
|
||||
@@ -76,6 +94,20 @@ static func from_dictionary(record_data: Dictionary) -> PlayerStateRecord:
|
||||
"max_health": max_health,
|
||||
"downed": bool(record_data["downed"]),
|
||||
"downed_ticks": int(record_data["downed_ticks"]),
|
||||
"dead": bool(record_data.get("dead", false)),
|
||||
"deaths": maxi(int(record_data.get("deaths", 0)), 0),
|
||||
"starvation_ticks": maxi(int(record_data.get("starvation_ticks", 0)), 0),
|
||||
"starvation_death_threshold":
|
||||
maxi(
|
||||
int(
|
||||
record_data.get(
|
||||
"starvation_death_threshold", DEFAULT_STARVATION_DEATH_THRESHOLD
|
||||
)
|
||||
),
|
||||
1
|
||||
),
|
||||
"carry_capacity":
|
||||
maxf(float(record_data.get("carry_capacity", DEFAULT_CARRY_CAPACITY)), 0.0),
|
||||
"inventory": normalized_inventory,
|
||||
}
|
||||
)
|
||||
@@ -89,6 +121,11 @@ static func _migrate_v1(legacy_data: Dictionary) -> Dictionary:
|
||||
migrated["max_health"] = MAX_HEALTH
|
||||
migrated["downed"] = false
|
||||
migrated["downed_ticks"] = 0
|
||||
migrated["dead"] = false
|
||||
migrated["deaths"] = 0
|
||||
migrated["starvation_ticks"] = 0
|
||||
migrated["starvation_death_threshold"] = DEFAULT_STARVATION_DEATH_THRESHOLD
|
||||
migrated["carry_capacity"] = DEFAULT_CARRY_CAPACITY
|
||||
return migrated
|
||||
|
||||
|
||||
@@ -112,6 +149,18 @@ func is_downed() -> bool:
|
||||
return bool(data["downed"])
|
||||
|
||||
|
||||
func is_dead() -> bool:
|
||||
return bool(data.get("dead", false))
|
||||
|
||||
|
||||
func is_starving() -> bool:
|
||||
return get_hunger() >= STACK_THRESHOLD and not is_dead()
|
||||
|
||||
|
||||
func get_deaths() -> int:
|
||||
return maxi(int(data.get("deaths", 0)), 0)
|
||||
|
||||
|
||||
func get_downed_ticks() -> int:
|
||||
return int(data["downed_ticks"])
|
||||
|
||||
@@ -124,6 +173,54 @@ func set_energy(amount: float) -> void:
|
||||
data["energy"] = clampf(amount, 0.0, 100.0)
|
||||
|
||||
|
||||
func advance(hunger_rate: float, energy_drain: float) -> bool:
|
||||
if is_dead():
|
||||
return true
|
||||
set_hunger(get_hunger() + maxf(hunger_rate, 0.0))
|
||||
set_energy(get_energy() - maxf(energy_drain, 0.0))
|
||||
if get_hunger() < STACK_THRESHOLD:
|
||||
data["starvation_ticks"] = 0
|
||||
return false
|
||||
data["starvation_ticks"] = int(data.get("starvation_ticks", 0)) + 1
|
||||
if int(data["starvation_ticks"]) >= int(data.get("starvation_death_threshold", 1)):
|
||||
die()
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func eat(amount: float) -> float:
|
||||
if not is_finite(amount) or amount <= 0.0 or is_dead():
|
||||
return 0.0
|
||||
var consumed := minf(amount, get_hunger())
|
||||
set_hunger(get_hunger() - consumed)
|
||||
set_energy(get_energy() + consumed * 0.5)
|
||||
data["starvation_ticks"] = 0
|
||||
return consumed
|
||||
|
||||
|
||||
func die() -> void:
|
||||
if is_dead():
|
||||
return
|
||||
data["dead"] = true
|
||||
data["deaths"] = get_deaths() + 1
|
||||
|
||||
|
||||
func respawn() -> void:
|
||||
data["dead"] = false
|
||||
data["downed"] = false
|
||||
data["downed_ticks"] = 0
|
||||
data["hunger"] = RESET_HUNGER
|
||||
data["energy"] = RESET_ENERGY
|
||||
data["health"] = get_max_health()
|
||||
data["starvation_ticks"] = 0
|
||||
|
||||
|
||||
func get_available_carry() -> float:
|
||||
return maxf(
|
||||
float(data.get("carry_capacity", DEFAULT_CARRY_CAPACITY)) - get_carried_total(), 0.0
|
||||
)
|
||||
|
||||
|
||||
func take_damage(amount: float) -> float:
|
||||
if not is_finite(amount) or amount <= 0.0 or is_downed():
|
||||
return 0.0
|
||||
@@ -157,10 +254,14 @@ func get_carried_total() -> float:
|
||||
return total
|
||||
|
||||
|
||||
func add_inventory(item_id: StringName, amount: float) -> void:
|
||||
func add_inventory(item_id: StringName, amount: float) -> float:
|
||||
if item_id.is_empty() or not is_finite(amount) or amount <= 0.0:
|
||||
return
|
||||
data["inventory"][String(item_id)] = get_inventory_amount(item_id) + amount
|
||||
return 0.0
|
||||
var added := minf(amount, get_available_carry())
|
||||
if added <= 0.0:
|
||||
return 0.0
|
||||
data["inventory"][String(item_id)] = get_inventory_amount(item_id) + added
|
||||
return added
|
||||
|
||||
|
||||
func remove_inventory(item_id: StringName, requested_amount: float) -> float:
|
||||
|
||||
Reference in New Issue
Block a user