Files
gamedev-the-steward/simulation/quests/PlayerQuestSystem.gd
T

204 lines
5.9 KiB
GDScript

class_name PlayerQuestSystem
extends RefCounted
const PANTRY_STANDING_REWARD := 8.0
const WOOD_STANDING_REWARD := 8.0
const ANIMAL_CARE_STANDING_REWARD := 6.0
const PERSONAL_TRUST_THRESHOLD := 0.5
const MIN_PERSONAL_REQUEST_TIER := PlayerStandingRecord.TIER_KNOWN_HAND
var quests: Array[PlayerQuestRecord] = []
var next_quest_id := 0
var standing := PlayerStandingRecord.create()
func consider_animal_care(
animal_state: AnimalStateRecord,
requester_npc_id: int,
current_tick: int,
pantry: StorageStateRecord
) -> PlayerQuestRecord:
if (
animal_state == null
or requester_npc_id < 0
or current_tick < 0
or pantry == null
or not animal_state.needs_feed()
or not animal_state.can_player_feed_animal()
):
return null
var definition := SimulationDefinitions.get_action(SimulationIds.ACTION_FEED_ANIMAL)
if (
definition == null
or not definition.has_completion_cost()
or (
pantry.get_amount(definition.completion_cost_resource_id)
< definition.completion_cost_amount
)
):
return null
if _find_open_quest_for_animal(animal_state.get_animal_id()) != null:
return null
var quest := PlayerQuestRecord.create(
next_quest_id,
PlayerQuestRecord.NO_OPPORTUNITY,
requester_npc_id,
SimulationIds.OPPORTUNITY_FEED_HUNGRY_ANIMAL,
definition.completion_cost_resource_id,
animal_state.get_animal_id(),
definition.completion_cost_amount,
current_tick,
ANIMAL_CARE_STANDING_REWARD,
animal_state.get_animal_id()
)
next_quest_id += 1
quests.append(quest)
return quest
func on_animal_fed(
animal_state: AnimalStateRecord, resolution_event: EconomicEventRecord, current_tick: int
) -> Dictionary:
if animal_state == null or resolution_event == null:
return {}
var quest := _find_open_quest_for_animal(animal_state.get_animal_id())
if quest == null:
return {}
var event_id := int(resolution_event.data["event_id"])
var actor_id := int(resolution_event.data["actor_id"])
if actor_id < 0:
if not quest.complete(event_id, current_tick):
return {}
standing.grant_standing(quest.get_standing_reward(), quest.get_requester_npc_id())
return {"quest": quest, "completed": true}
if quest.expire(current_tick):
return {"quest": quest, "completed": false}
return {}
func consider_opportunity_opened(
opportunity: OpportunityStateRecord,
player_response: OpportunityPlayerResponseResult,
current_tick: int,
personal_request_eligible := false
) -> PlayerQuestRecord:
if opportunity == null or not opportunity.is_open() or current_tick < 0:
return null
if player_response == null and not personal_request_eligible:
return null
if _find_open_quest_for_opportunity(opportunity.get_opportunity_id()) != null:
return null
var reward := _standing_reward_for(opportunity)
if reward <= 0.0:
return null
var quest := PlayerQuestRecord.create(
next_quest_id,
opportunity.get_opportunity_id(),
opportunity.get_interested_npc_id(),
opportunity.get_opportunity_type(),
opportunity.get_resource_id(),
opportunity.get_target_id(),
opportunity.get_target_amount(),
current_tick,
reward
)
next_quest_id += 1
quests.append(quest)
return quest
func can_request_personally(opportunity: OpportunityStateRecord) -> bool:
if (
opportunity == null
or not opportunity.is_open()
or standing.get_tier() < MIN_PERSONAL_REQUEST_TIER
):
return false
return standing.get_gratitude(opportunity.get_interested_npc_id()) >= PERSONAL_TRUST_THRESHOLD
func on_opportunity_resolved(
opportunity: OpportunityStateRecord, resolution_event: EconomicEventRecord, current_tick: int
) -> Dictionary:
var quest := _find_open_quest_for_opportunity(opportunity.get_opportunity_id())
if quest == null or resolution_event == null:
return {}
var event_id := int(resolution_event.data["event_id"])
var actor_id := int(resolution_event.data["actor_id"])
if actor_id < 0:
if not quest.complete(event_id, current_tick):
return {}
standing.grant_standing(quest.get_standing_reward(), quest.get_requester_npc_id())
return {"quest": quest, "completed": true}
if quest.expire(current_tick):
return {"quest": quest, "completed": false}
return {}
func on_opportunity_invalidated(
opportunity: OpportunityStateRecord, current_tick: int
) -> PlayerQuestRecord:
var quest := _find_open_quest_for_opportunity(opportunity.get_opportunity_id())
if quest == null:
return null
if quest.expire(current_tick):
return quest
return null
func get_active_quest() -> PlayerQuestRecord:
for quest in quests:
if quest.is_open():
return quest
return null
func get_all_sorted() -> Array[PlayerQuestRecord]:
var sorted := quests.duplicate()
sorted.sort_custom(_sort_by_id)
return sorted
func get_latest_for_requester(requester_npc_id: int) -> PlayerQuestRecord:
for index in range(quests.size() - 1, -1, -1):
var quest := quests[index]
if quest.get_requester_npc_id() == requester_npc_id:
return quest
return null
func restore(
records: Array[PlayerQuestRecord], restored_next_id: int, standing_record: PlayerStandingRecord
) -> void:
quests = records.duplicate()
quests.sort_custom(_sort_by_id)
next_quest_id = maxi(restored_next_id, 0)
standing = standing_record if standing_record != null else PlayerStandingRecord.create()
func _find_open_quest_for_opportunity(opportunity_id: int) -> PlayerQuestRecord:
for quest in quests:
if quest.is_open() and quest.get_opportunity_id() == opportunity_id:
return quest
return null
func _find_open_quest_for_animal(animal_id: StringName) -> PlayerQuestRecord:
for quest in quests:
if quest.is_open() and quest.has_animal_target() and quest.get_animal_id() == animal_id:
return quest
return null
func _standing_reward_for(opportunity: OpportunityStateRecord) -> float:
match opportunity.get_opportunity_type():
SimulationIds.OPPORTUNITY_RESTOCK_EMPTY_PANTRY:
return PANTRY_STANDING_REWARD
SimulationIds.OPPORTUNITY_SUPPLY_MISSING_WOOD:
return WOOD_STANDING_REWARD
return 0.0
static func _sort_by_id(first: PlayerQuestRecord, second: PlayerQuestRecord) -> bool:
return first.get_quest_id() < second.get_quest_id()