mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-04 14:01:22 +00:00
* ONLY SKYRAT CHANGES * ACTUALLY SKYRAT CHANGES * yolo, revert later * Update alternate_byond_versions.txt Co-authored-by: AnturK <AnturK@users.noreply.github.com>
537 lines
20 KiB
Plaintext
537 lines
20 KiB
Plaintext
// json field definitions bit verbose but i've had it with the typos
|
|
#define ADVENTURE_NAME_FIELD "adventure_name"
|
|
#define ADVENTURE_STARTING_NODE_FIELD "starting_node"
|
|
#define ADVENTURE_REQUIRED_SITE_TRAITS_FIELD "required_site_traits"
|
|
#define ADVENTURE_SCAN_BAND_MODS_FIELD "scan_band_mods"
|
|
#define ADVENTURE_LOOT_FIELD "loot_categories"
|
|
#define ADVENTURE_STARTING_QUALITIES_FIELD "starting_qualities"
|
|
#define ADVENTURE_DEEP_SCAN_DESCRIPTION "deep_scan_description"
|
|
#define ADVENTURE_NODES_FIELD "nodes"
|
|
#define ADVENTURE_TRIGGERS_FIELD "triggers"
|
|
#define ADVENTURE_VERSION_FIELD "version"
|
|
|
|
#define NODE_NAME_FIELD "name"
|
|
#define NODE_DESCRIPTION_FIELD "description"
|
|
#define NODE_IMAGE_FIELD "image"
|
|
#define NODE_RAW_IMAGE_FIELD "raw_image"
|
|
#define NODE_CHOICES_FIELD "choices"
|
|
#define NODE_ON_ENTER_EFFECTS_FIELD "on_enter_effects"
|
|
#define NODE_ON_EXIT_EFFECTS_FIELD "on_exit_effects"
|
|
|
|
#define CHOICE_KEY_FIELD "key"
|
|
#define CHOICE_NAME_FIELD "name"
|
|
#define CHOICE_ON_SELECTION_EFFECT_FIELD "on_selection_effects"
|
|
#define CHOICE_REQUIREMENTS_FIELD "requirements"
|
|
#define CHOICE_EXIT_NODE_FIELD "exit_node"
|
|
#define CHOICE_DELAY_FIELD "delay"
|
|
#define CHOICE_DELAY_MESSAGE_FIELD "delay_message"
|
|
|
|
#define EFFECT_TYPE_FIELD "effect_type"
|
|
#define EFFECT_QUALITY_FIELD "quality"
|
|
#define EFFECT_VALUE_FIELD "value"
|
|
#define EFFECT_VALUE_VALUE_TYPE_FIELD "value_type"
|
|
#define TRIGGER_NAME_FIELD "name"
|
|
#define TRIGGER_REQUIREMENTS_FIELD "requirements"
|
|
#define TRIGGER_ON_TRIGGER_EFFECTS_FIELD "on_trigger_effects"
|
|
#define TRIGGER_TARGET_NODE_FIELD "target_node"
|
|
|
|
#define REQ_GROUP_REQUIREMENTS_FIELD "requirements"
|
|
#define REQ_GROUP_GROUP_TYPE_FIELD "group_type"
|
|
|
|
#define REQ_QUALITY_FIELD "quality"
|
|
#define REQ_VALUE_FIELD "value"
|
|
#define REQ_OPERATOR_FIELD "operator"
|
|
|
|
#define CURRENT_ADVENTURE_VERSION 1
|
|
|
|
/// All possible adventures in raw form
|
|
GLOBAL_LIST_EMPTY(explorer_drone_adventure_db_entries)
|
|
|
|
/// Loads all adventures from DB
|
|
/proc/load_adventures()
|
|
. = list()
|
|
if(!SSdbcore.Connect())
|
|
GLOB.explorer_drone_adventure_db_entries = .
|
|
return
|
|
var/datum/db_query/Query = SSdbcore.NewQuery("SELECT id,adventure_data,uploader,timestamp,approved FROM [format_table_name("text_adventures")]")
|
|
if(!Query.Execute())
|
|
qdel(Query)
|
|
return
|
|
while(Query.NextRow())
|
|
var/datum/adventure_db_entry/entry = new()
|
|
entry.id = Query.item[1]
|
|
entry.raw_json = Query.item[2]
|
|
entry.uploader = Query.item[3]
|
|
entry.timestamp = Query.item[4]
|
|
entry.approved = Query.item[5]
|
|
entry.extract_metadata()
|
|
. += entry
|
|
qdel(Query)
|
|
GLOB.explorer_drone_adventure_db_entries = .
|
|
|
|
/datum/adventure_db_entry
|
|
/// db id or null for freshly created adventures
|
|
var/id
|
|
/// actual adventure json string
|
|
var/raw_json
|
|
/// ckey of last change user.
|
|
var/uploader
|
|
/// Time of last change.
|
|
var/timestamp
|
|
/// Unapproved adventures won't be used for exploration sites.
|
|
var/approved = FALSE
|
|
/// Was the adventure used for exploration site this round.
|
|
var/placed = FALSE
|
|
|
|
//Variables below are extracted from the JSON
|
|
|
|
/// json version
|
|
var/version
|
|
/// adventure name
|
|
var/name
|
|
/// required site traits to use this adventure
|
|
var/list/required_site_traits
|
|
|
|
/// Check if the adventure usable for given exploration site traits
|
|
/datum/adventure_db_entry/proc/valid_for_use(list/site_traits)
|
|
if(!raw_json || version != CURRENT_ADVENTURE_VERSION || placed)
|
|
return FALSE
|
|
if(required_site_traits && length(required_site_traits - site_traits) != 0)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/// Updates this entry from db, if possible.
|
|
/datum/adventure_db_entry/proc/refresh()
|
|
if(id)
|
|
//Check if our timestamp is fresh, if not update local and stop
|
|
var/datum/db_query/SelectQuery = SSdbcore.NewQuery("SELECT adventure_data,uploader,timestamp,approved FROM [format_table_name("text_adventures")] WHERE id = :id",list("id" = id))
|
|
if(!SelectQuery.warn_execute() || !SelectQuery.NextRow())
|
|
qdel(SelectQuery)
|
|
return
|
|
raw_json = SelectQuery.item[1]
|
|
uploader = SelectQuery.item[2]
|
|
timestamp = SelectQuery.item[3]
|
|
approved = SelectQuery.item[4]
|
|
extract_metadata()
|
|
qdel(SelectQuery)
|
|
return
|
|
// No ID, nothing to be done.
|
|
|
|
/// Pushes this entry changes to DB
|
|
/datum/adventure_db_entry/proc/save()
|
|
if(id)
|
|
//We're up to date, update db instead
|
|
var/datum/db_query/UpdateQuery = SSdbcore.NewQuery("UPDATE [format_table_name("text_adventures")] SET adventure_data = :adventure_data,uploader = :uploader,approved = :approved WHERE id = :id AND timestamp < NOW()",
|
|
list("id" = id, "adventure_data" = raw_json, "uploader" = usr.ckey, "approved" = approved))
|
|
UpdateQuery.warn_execute()
|
|
qdel(UpdateQuery)
|
|
else
|
|
// Create new entry
|
|
var/datum/db_query/InsertQuery = SSdbcore.NewQuery("INSERT INTO [format_table_name("text_adventures")] (adventure_data, uploader) VALUES (:raw_json, :uploader)", list("raw_json" = raw_json, "uploader" = usr.ckey))
|
|
if(!InsertQuery.warn_execute())
|
|
qdel(InsertQuery)
|
|
return FALSE
|
|
id = InsertQuery.last_insert_id
|
|
qdel(InsertQuery)
|
|
refresh()
|
|
|
|
/// Deletes the local AND db entry.
|
|
/datum/adventure_db_entry/proc/remove()
|
|
if(id)
|
|
var/datum/db_query/DelQuery = SSdbcore.NewQuery("DELETE FROM [format_table_name("text_adventures")] WHERE id = :id", list("id" = id))
|
|
if(!DelQuery.warn_execute())
|
|
qdel(DelQuery)
|
|
return FALSE
|
|
log_admin("[key_name(usr)] deleted text adventure with id : [id], name : [name]")
|
|
qdel(DelQuery)
|
|
GLOB.explorer_drone_adventure_db_entries -= src
|
|
qdel(src)
|
|
return TRUE
|
|
|
|
/// Extracts fields that are used by adventure browser / generation before instantiating
|
|
/datum/adventure_db_entry/proc/extract_metadata()
|
|
if(!raw_json)
|
|
CRASH("Trying to extract metadata from empty adventure")
|
|
var/list/json_data = json_decode(raw_json)
|
|
if(!islist(json_data))
|
|
CRASH("Invalid JSON for adventure with db id:[id]")
|
|
version = json_data[ADVENTURE_VERSION_FIELD] || 0
|
|
name = json_data[ADVENTURE_NAME_FIELD]
|
|
required_site_traits = json_data[ADVENTURE_REQUIRED_SITE_TRAITS_FIELD]
|
|
|
|
/// Creates new adventure instance
|
|
/datum/adventure_db_entry/proc/create_adventure()
|
|
if(version != CURRENT_ADVENTURE_VERSION)
|
|
CRASH("Trying to instance outdated adventure version")
|
|
return try_loading_adventure()
|
|
|
|
/// Parses adventure JSON and returns /datum/adventure instance on success
|
|
/datum/adventure_db_entry/proc/try_loading_adventure()
|
|
var/list/json_data = json_decode(raw_json)
|
|
if(!islist(json_data))
|
|
CRASH("Invalid JSON in adventure with id:[id], name:[name]")
|
|
|
|
//Basic validation of required fields, don't even bother loading if they are missing.
|
|
var/static/list/required_fields = list(ADVENTURE_NAME_FIELD,ADVENTURE_STARTING_NODE_FIELD,ADVENTURE_NODES_FIELD)
|
|
for(var/field in required_fields)
|
|
if(!json_data[field])
|
|
CRASH("Adventure id:[id], name:[name] missing [field] value")
|
|
|
|
var/datum/adventure/loaded_adventure = new
|
|
//load properties
|
|
loaded_adventure.starting_node = json_data[ADVENTURE_STARTING_NODE_FIELD]
|
|
loaded_adventure.name = json_data[ADVENTURE_NAME_FIELD]
|
|
loaded_adventure.required_site_traits = json_data[ADVENTURE_REQUIRED_SITE_TRAITS_FIELD]
|
|
loaded_adventure.band_modifiers = json_data[ADVENTURE_SCAN_BAND_MODS_FIELD]
|
|
loaded_adventure.loot_categories = json_data[ADVENTURE_LOOT_FIELD]
|
|
loaded_adventure.starting_qualities = json_data[ADVENTURE_STARTING_QUALITIES_FIELD]
|
|
loaded_adventure.deep_scan_description = json_data[ADVENTURE_DEEP_SCAN_DESCRIPTION]
|
|
|
|
for(var/list/node_data in json_data[ADVENTURE_NODES_FIELD])
|
|
var/datum/adventure_node/node = try_loading_node(node_data)
|
|
if(node)
|
|
if(loaded_adventure.nodes[node.id])
|
|
CRASH("Duplicate [node.id] node in id:[id], name:[name] adventure")
|
|
loaded_adventure.nodes[node.id] = node
|
|
loaded_adventure.triggers = json_data[ADVENTURE_TRIGGERS_FIELD]
|
|
if(!loaded_adventure.validate())
|
|
CRASH("Validation failed for id:[id], name:[name] adventure")
|
|
return loaded_adventure
|
|
|
|
/datum/adventure_db_entry/proc/try_loading_node(node_data)
|
|
if(!islist(node_data))
|
|
CRASH("Invalid adventure node data in id:[id], name:[name] adventure.")
|
|
var/datum/adventure_node/fresh_node = new
|
|
fresh_node.id = node_data[NODE_NAME_FIELD]
|
|
fresh_node.description = node_data[NODE_DESCRIPTION_FIELD]
|
|
fresh_node.image_name = node_data[NODE_IMAGE_FIELD]
|
|
fresh_node.raw_image = node_data[NODE_RAW_IMAGE_FIELD]
|
|
fresh_node.choices = list()
|
|
for(var/list/choice_data in node_data[NODE_CHOICES_FIELD])
|
|
fresh_node.choices[choice_data[CHOICE_KEY_FIELD]] = choice_data
|
|
fresh_node.on_enter_effects = node_data[NODE_ON_ENTER_EFFECTS_FIELD]
|
|
fresh_node.on_exit_effects = node_data[NODE_ON_EXIT_EFFECTS_FIELD]
|
|
return fresh_node
|
|
|
|
/// text adventure instance, holds data about nodes/choices/etc and of current play state.
|
|
/datum/adventure
|
|
/// Adventure name, this organization only, not visible to users
|
|
var/name
|
|
/// Node the adventure will start at
|
|
var/starting_node
|
|
/// Required site traits for the adventure to appear
|
|
var/list/required_site_traits = list()
|
|
/// Modifiers to band scan values
|
|
var/list/band_modifiers = list()
|
|
/// Loot table ids used as reward for finishing the adventure succesfully.
|
|
var/list/loot_categories = list()
|
|
/// Nodes for this adventure, represent single scene.
|
|
var/list/nodes = list()
|
|
/// Triggers for this adventure, checked after quality changes to cause instantenous results
|
|
var/list/triggers = list()
|
|
/// List of starting quality values, these will be set before first node is encountered.
|
|
var/list/starting_qualities = list()
|
|
///Keeps track firing of triggers until stop state to prevent loops
|
|
var/list/trigger_loop_safety = list()
|
|
/// Opional description shown after site deep scan
|
|
var/deep_scan_description
|
|
|
|
// State tracking variables
|
|
/// Current active adventure node
|
|
var/datum/adventure_node/current_node
|
|
/// Last other node than this one. Used by GO_BACK_NODE
|
|
var/previous_node_id
|
|
/// Assoc list of quality name = value
|
|
var/list/qualities
|
|
/// Delayed state properties. If not null, means adventure is in delayed action state and will contain list(delay_time,delay_message)
|
|
var/list/delayed_action
|
|
|
|
/// Basic sanity checks to ensure broken adventures are not used.
|
|
/datum/adventure/proc/validate()
|
|
///Check all nodes have choices
|
|
for(var/node_id in nodes)
|
|
var/datum/adventure_node/node = nodes[node_id]
|
|
if(!length(node.choices))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/datum/adventure/proc/start_adventure()
|
|
initialize_qualities()
|
|
previous_node_id = starting_node
|
|
navigate_to_node(starting_node)
|
|
|
|
/// Finish adventure
|
|
/datum/adventure/proc/end_adventure(result)
|
|
SEND_SIGNAL(src,COMSIG_ADVENTURE_FINISHED,result)
|
|
|
|
/datum/adventure/proc/initialize_qualities()
|
|
qualities = starting_qualities || list()
|
|
SEND_SIGNAL(src,COMSIG_ADVENTURE_QUALITY_INIT,qualities)
|
|
|
|
/datum/adventure/proc/navigate_to_node(node_id)
|
|
if(current_node)
|
|
if(current_node.on_exit(src)) //Trigger on exit caused node change <- I don't really see much use for this so might want to warn about it ?
|
|
return
|
|
if(current_node.id != previous_node_id)
|
|
previous_node_id = current_node.id
|
|
if(handle_special_nodes(node_id))
|
|
return
|
|
if(!nodes[node_id])
|
|
stack_trace("Invalid adventure node navigation from node [current_node.id]")
|
|
current_node = nodes[node_id]
|
|
current_node.on_enter(src)
|
|
|
|
/// Handles special node ID's
|
|
/datum/adventure/proc/handle_special_nodes(node_id)
|
|
switch(node_id)
|
|
if(FAIL_NODE)
|
|
end_adventure(ADVENTURE_RESULT_DAMAGE)
|
|
return TRUE
|
|
if(FAIL_DEATH_NODE)
|
|
end_adventure(ADVENTURE_RESULT_DEATH)
|
|
return TRUE
|
|
if(WIN_NODE)
|
|
end_adventure(ADVENTURE_RESULT_SUCCESS)
|
|
return TRUE
|
|
if(GO_BACK_NODE)
|
|
if(previous_node_id)
|
|
navigate_to_node(previous_node_id)
|
|
return TRUE
|
|
else
|
|
return FALSE
|
|
else
|
|
return FALSE
|
|
|
|
/datum/adventure/proc/select_choice(choice_id)
|
|
if(!current_node || !islist(current_node.choices[choice_id]))
|
|
return
|
|
var/list/choice_data = current_node.choices[choice_id]
|
|
if(!check_requirements(choice_data[CHOICE_REQUIREMENTS_FIELD]))
|
|
return
|
|
if(choice_data[CHOICE_ON_SELECTION_EFFECT_FIELD])
|
|
if(apply_adventure_effect(choice_data[CHOICE_ON_SELECTION_EFFECT_FIELD],src))
|
|
return //Trigger forced node change.
|
|
var/exit_id = choice_data[CHOICE_EXIT_NODE_FIELD]
|
|
if(!exit_id)
|
|
CRASH("No exit node for choice [choice_id] in adventure [name]")
|
|
if(choice_data[CHOICE_DELAY_FIELD])
|
|
var/delay_message = choice_data[CHOICE_DELAY_MESSAGE_FIELD]
|
|
var/delay_time = choice_data[CHOICE_DELAY_FIELD]
|
|
if(!isnum(delay_time))
|
|
CRASH("Invalid delay in adventure [name]")
|
|
SEND_SIGNAL(src,COMSIG_ADVENTURE_DELAY_START,delay_time,delay_message)
|
|
delayed_action = list(delay_time,delay_message)
|
|
addtimer(CALLBACK(src, PROC_REF(finish_delay),exit_id),delay_time)
|
|
return
|
|
navigate_to_node(exit_id)
|
|
|
|
/datum/adventure/proc/finish_delay(exit_id)
|
|
delayed_action = null
|
|
navigate_to_node(exit_id)
|
|
SEND_SIGNAL(src,COMSIG_ADVENTURE_DELAY_END)
|
|
|
|
/datum/adventure/ui_data(mob/user)
|
|
. = ..()
|
|
.["description"] = current_node?.description
|
|
.["image"] = current_node?.image_name
|
|
.["raw_image"] = current_node?.raw_image
|
|
.["choices"] = current_node?.get_available_choices(src)
|
|
|
|
|
|
/datum/adventure_node
|
|
/// Unique identifier for this node
|
|
var/id
|
|
/// The actual displayed text
|
|
var/description
|
|
/// Preset image name, exclusive with raw_image
|
|
var/image_name
|
|
/// Image in base64 form. Exclusive with image_name
|
|
var/raw_image
|
|
/// All possible choices from this node, associative list of choice_id -> choice_data
|
|
var/list/choices
|
|
/// Effects fired when navigating to this node.
|
|
var/list/on_enter_effects
|
|
/// Effects fired when leaving this node.
|
|
var/list/on_exit_effects
|
|
/// Pauses adventure for this long after the choice
|
|
var/delay
|
|
/// This will show when the delay is happening.
|
|
var/delay_message
|
|
|
|
|
|
/datum/adventure_node/proc/on_enter(datum/adventure/context)
|
|
if(on_enter_effects)
|
|
if(context.apply_adventure_effect(on_enter_effects))
|
|
return TRUE
|
|
|
|
|
|
/datum/adventure_node/proc/on_exit(datum/adventure/context)
|
|
if(on_exit_effects)
|
|
if(context.apply_adventure_effect(on_exit_effects))
|
|
return TRUE
|
|
|
|
|
|
/datum/adventure_node/proc/get_available_choices(datum/adventure/context)
|
|
. = list()
|
|
for(var/choice_key in choices)
|
|
var/list/choice_data = choices[choice_key]
|
|
if(context.check_requirements(choice_data[CHOICE_REQUIREMENTS_FIELD]))
|
|
. += list(list("key" = choice_key,"text" = choice_data[CHOICE_NAME_FIELD]))
|
|
|
|
///Applies changes encoded in effect data and processes triggers, returns TRUE if the change forced node change.
|
|
/datum/adventure/proc/apply_adventure_effect(list/effect_data,process_triggers=TRUE)
|
|
if(!islist(effect_data))
|
|
CRASH("Invalid effect data [json_encode(effect_data)] in adventure [name]")
|
|
for(var/list/effect_group in effect_data)
|
|
var/effect_keyword = effect_group[EFFECT_TYPE_FIELD]
|
|
var/list/quality_name = effect_group[EFFECT_QUALITY_FIELD]
|
|
var/value = process_adventure_value(effect_group[EFFECT_VALUE_FIELD])
|
|
switch(effect_keyword)
|
|
if(ADVENTURE_EFFECT_TYPE_REMOVE) //remove quality doesn't care about value for now
|
|
qualities -= quality_name
|
|
if(ADVENTURE_EFFECT_TYPE_ADD)
|
|
if(!isnum(value))
|
|
CRASH("Invalid add quality effect value in effect [json_encode(effect_data)] in adventure [name]")
|
|
if(!qualities[quality_name])
|
|
qualities[quality_name] = 0
|
|
qualities[quality_name] += value
|
|
if(ADVENTURE_EFFECT_TYPE_SET)
|
|
qualities[quality_name] = value
|
|
else
|
|
CRASH("Invalid effect keyword in effect [json_encode(effect_data)] in adventure [name]")
|
|
///Check Triggers
|
|
if(process_triggers)
|
|
for(var/list/trigger_data in triggers)
|
|
if(!check_requirements(trigger_data[TRIGGER_REQUIREMENTS_FIELD]))
|
|
continue
|
|
if(LAZYACCESS(trigger_loop_safety,trigger_data[TRIGGER_NAME_FIELD]))
|
|
stack_trace("Loop in trigger processing detected in adventure [name]")
|
|
continue
|
|
LAZYADD(trigger_loop_safety,trigger_data[TRIGGER_NAME_FIELD])
|
|
if(trigger_data[TRIGGER_ON_TRIGGER_EFFECTS_FIELD])
|
|
apply_adventure_effect(trigger_data[TRIGGER_ON_TRIGGER_EFFECTS_FIELD],FALSE) //Let's keep this simple
|
|
if(trigger_data[TRIGGER_TARGET_NODE_FIELD])
|
|
navigate_to_node(trigger_data[TRIGGER_TARGET_NODE_FIELD])
|
|
return TRUE
|
|
//We're out of trigger processing
|
|
LAZYCLEARLIST(trigger_loop_safety)
|
|
return FALSE
|
|
|
|
/// Extracts raw value from special value objects
|
|
/datum/adventure/proc/process_adventure_value(raw_value)
|
|
if(islist(raw_value))
|
|
var/list/value_as_list = raw_value
|
|
switch(value_as_list[EFFECT_VALUE_VALUE_TYPE_FIELD])
|
|
if(ADVENTURE_QUALITY_TYPE_RANDOM)
|
|
return rand(value_as_list[ADVENTURE_RANDOM_QUALITY_LOW_FIELD],value_as_list[ADVENTURE_RANDOM_QUALITY_HIGH_FIELD])
|
|
else
|
|
CRASH("Invalid special value type in adventure [name]")
|
|
else
|
|
return raw_value
|
|
|
|
/// Checks if current qualities satisfy passed in requirements
|
|
/datum/adventure/proc/check_requirements(raw_requirements)
|
|
if(!islist(raw_requirements))
|
|
return TRUE
|
|
var/list/req_groups = raw_requirements
|
|
// Top level list - can contain either req groups or single requirements and is AND type group
|
|
for(var/list/group_data in req_groups)
|
|
if(group_data[REQ_GROUP_REQUIREMENTS_FIELD]) //It's a group
|
|
if(!check_requirement_group(group_data))
|
|
return FALSE
|
|
else //It's a single requirement
|
|
if(!check_single_requirement(group_data))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/// Recursively validates group requirements.
|
|
/datum/adventure/proc/check_requirement_group(raw_group_data)
|
|
if(!islist(raw_group_data))
|
|
CRASH("Invalid group requirement in adventure [name]")
|
|
var/list/group_data = raw_group_data
|
|
var/group_type = group_data[REQ_GROUP_GROUP_TYPE_FIELD]
|
|
var/list/group_elements = group_data[REQ_GROUP_REQUIREMENTS_FIELD]
|
|
switch(group_type)
|
|
if("OR") //Just one out of subgroups/reqs need to be true for this to return true
|
|
for(var/list/subgroup_data in group_elements)
|
|
if(subgroup_data[REQ_GROUP_REQUIREMENTS_FIELD]) //It's a group
|
|
if(check_requirement_group(subgroup_data))
|
|
return TRUE
|
|
else //It's a single requirement
|
|
if(check_single_requirement(subgroup_data))
|
|
return TRUE
|
|
return FALSE
|
|
if("AND") //All subgroups/reqs need to be true for this to return true
|
|
for(var/list/subgroup_data in group_elements)
|
|
if(subgroup_data[REQ_GROUP_REQUIREMENTS_FIELD]) //It's a group
|
|
if(!check_requirement_group(subgroup_data))
|
|
return FALSE
|
|
else //It's a single requirement
|
|
if(!check_single_requirement(subgroup_data))
|
|
return FALSE
|
|
return TRUE
|
|
else
|
|
CRASH("Invalid requirement group in adventure [name]")
|
|
|
|
//Checks if unit requirement {"quality": "a","op": "==","value": "something"} is met.
|
|
/datum/adventure/proc/check_single_requirement(raw_requirement)
|
|
var/qkey = raw_requirement[REQ_QUALITY_FIELD]
|
|
var/qval = raw_requirement[REQ_VALUE_FIELD]
|
|
switch(raw_requirement[REQ_OPERATOR_FIELD])
|
|
if("==")
|
|
return qualities[qkey] == qval
|
|
if("!=")
|
|
return qualities[qkey] != qval
|
|
if(">")
|
|
return qualities[qkey] > qval
|
|
if(">=")
|
|
return qualities[qkey] >= qval
|
|
if("<=")
|
|
return qualities[qkey] <= qval
|
|
if("<")
|
|
return qualities[qkey] < qval
|
|
if("exists")
|
|
return qkey in qualities
|
|
|
|
#undef ADVENTURE_NAME_FIELD
|
|
#undef ADVENTURE_STARTING_NODE_FIELD
|
|
#undef ADVENTURE_REQUIRED_SITE_TRAITS_FIELD
|
|
#undef ADVENTURE_SCAN_BAND_MODS_FIELD
|
|
#undef ADVENTURE_LOOT_FIELD
|
|
#undef ADVENTURE_STARTING_QUALITIES_FIELD
|
|
#undef ADVENTURE_DEEP_SCAN_DESCRIPTION
|
|
#undef ADVENTURE_NODES_FIELD
|
|
#undef ADVENTURE_TRIGGERS_FIELD
|
|
|
|
#undef NODE_NAME_FIELD
|
|
#undef NODE_DESCRIPTION_FIELD
|
|
#undef NODE_IMAGE_FIELD
|
|
#undef NODE_RAW_IMAGE_FIELD
|
|
#undef NODE_CHOICES_FIELD
|
|
#undef NODE_ON_ENTER_EFFECTS_FIELD
|
|
#undef NODE_ON_EXIT_EFFECTS_FIELD
|
|
|
|
#undef CHOICE_KEY_FIELD
|
|
#undef CHOICE_NAME_FIELD
|
|
#undef CHOICE_ON_SELECTION_EFFECT_FIELD
|
|
#undef CHOICE_REQUIREMENTS_FIELD
|
|
#undef CHOICE_EXIT_NODE_FIELD
|
|
#undef CHOICE_DELAY_FIELD
|
|
#undef CHOICE_DELAY_MESSAGE_FIELD
|
|
|
|
#undef EFFECT_TYPE_FIELD
|
|
#undef EFFECT_QUALITY_FIELD
|
|
#undef EFFECT_VALUE_FIELD
|
|
#undef EFFECT_VALUE_VALUE_TYPE_FIELD
|
|
#undef TRIGGER_NAME_FIELD
|
|
#undef TRIGGER_REQUIREMENTS_FIELD
|
|
#undef TRIGGER_ON_TRIGGER_EFFECTS_FIELD
|
|
#undef TRIGGER_TARGET_NODE_FIELD
|
|
|
|
#undef REQ_GROUP_REQUIREMENTS_FIELD
|
|
#undef REQ_GROUP_GROUP_TYPE_FIELD
|
|
|
|
#undef REQ_QUALITY_FIELD
|
|
#undef REQ_VALUE_FIELD
|
|
#undef REQ_OPERATOR_FIELD
|