diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index 428cfae8239d9..7f82faf40d0eb 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -102,6 +102,9 @@
/// A define used in ritual priority for heretics.
#define MAX_KNOWLEDGE_PRIORITY 100
+/// The maximum (and optimal) number of sacrifice targets a heretic should roll.
+#define HERETIC_MAX_SAC_TARGETS 4
+
/// How much does it cost to reroll strains?
#define BLOB_REROLL_COST 40
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index fd7679d468711..893fc6f3d00c6 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -758,6 +758,22 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
. = orange(distance,center)
return
+/**
+ * Gets the mind from a variable, whether it be a mob, or a mind itself.
+ * If [include_last] is true, then it will also return last_mind for carbons if there isn't a current mind.
+ */
+/proc/get_mind(target, include_last = FALSE)
+ if(istype(target, /datum/mind))
+ return target
+ if(ismob(target))
+ var/mob/mob_target = target
+ if(!QDELETED(mob_target.mind))
+ return mob_target.mind
+ if(include_last && iscarbon(mob_target))
+ var/mob/living/carbon/carbon_target = mob_target
+ if(!QDELETED(carbon_target.last_mind))
+ return carbon_target.last_mind
+
#undef FACING_SAME_DIR
#undef FACING_EACHOTHER
#undef FACING_INIT_FACING_TARGET_TARGET_FACING_PERPENDICULAR
diff --git a/code/__HELPERS/turfs.dm b/code/__HELPERS/turfs.dm
index a1875bde3727d..1b900a65ac0a1 100644
--- a/code/__HELPERS/turfs.dm
+++ b/code/__HELPERS/turfs.dm
@@ -369,3 +369,26 @@ Turf and target are separate in case you want to teleport some distance from a t
if(rail.dir == test_dir || is_fulltile)
return FALSE
return TRUE
+
+/proc/is_turf_safe(turf/open/floor/floor)
+ // It's probably not safe if it's not a floor.
+ if(!istype(floor))
+ return FALSE
+ var/datum/gas_mixture/air = floor.air
+ // Certainly unsafe if it completely lacks air.
+ if(QDELETED(air))
+ return FALSE
+ // Can most things breathe?
+ for(var/id in air.get_gases())
+ if(id in GLOB.hardcoded_gases)
+ continue
+ return FALSE
+ if(air.get_moles(GAS_O2) < 16 || air.get_moles(GAS_PLASMA) || air.get_moles(GAS_CO2) >= 10)
+ return FALSE
+ var/temperature = air.return_temperature()
+ if(temperature <= 270 || temperature >= 360)
+ return FALSE
+ var/pressure = air.return_pressure()
+ if(pressure <= 20 || pressure >= 550)
+ return FALSE
+ return TRUE
diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm
index e12efe3ec4363..4a5b3d4225629 100644
--- a/code/datums/helper_datums/teleport.dm
+++ b/code/datums/helper_datums/teleport.dm
@@ -137,32 +137,7 @@
if(!isfloorturf(random_location))
continue
var/turf/open/floor/F = random_location
- if(!F.air)
- continue
-
- var/datum/gas_mixture/A = F.air
- var/trace_gases
- for(var/id in A.get_gases())
- if(id in GLOB.hardcoded_gases)
- continue
- trace_gases = TRUE
- break
-
- // Can most things breathe?
- if(trace_gases)
- continue
- if(A.get_moles(GAS_O2) < 16)
- continue
- if(A.get_moles(GAS_PLASMA))
- continue
- if(A.get_moles(GAS_CO2) >= 10)
- continue
-
- // Aim for goldilocks temperatures and pressure
- if((A.return_temperature() <= 270) || (A.return_temperature() >= 360))
- continue
- var/pressure = A.return_pressure()
- if((pressure <= 20) || (pressure >= 550))
+ if(!is_turf_safe(F))
continue
if(extended_safety_checks)
diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm
index fddceb7f3869d..b3d49c66426a7 100644
--- a/code/modules/antagonists/heretic/heretic_antag.dm
+++ b/code/modules/antagonists/heretic/heretic_antag.dm
@@ -19,6 +19,7 @@
antag_moodlet = /datum/mood_event/heretics
banning_key = ROLE_HERETIC
required_living_playtime = 4
+ ui_name = "AntagInfoHeretic"
/// Whether we've ascended! (Completed one of the final rituals)
var/ascended = FALSE
/// The path our heretic has chosen. Mostly used for flavor.
@@ -33,20 +34,25 @@
var/total_sacrifices = 0
/// A list of TOTAL how many high value sacrifices completed.
var/high_value_sacrifices = 0
- /// Lazy assoc list of [weakrefs to humans] to [image previews of the human]. Humans that we have as sacrifice targets.
+ /// Weakrefs to the minds of monsters have been successfully summoned. Includes ghouls.
+ var/list/datum/weakref/monsters_summoned
+ /// Lazy assoc list of [weakrefs to minds] to [image previews of the human]. Humans that we have as sacrifice targets.
var/list/datum/weakref/sac_targets
+ /// A list of minds we won't pick as targets.
+ var/list/datum/mind/target_blacklist
/// Whether we're drawing a rune or not
var/drawing_rune = FALSE
/// A static typecache of all tools we can scribe with.
var/static/list/scribing_tools = typecacheof(list(/obj/item/pen, /obj/item/toy/crayon))
/// A blacklist of turfs we cannot scribe on.
var/static/list/blacklisted_rune_turfs = typecacheof(list(/turf/open/space, /turf/open/openspace, /turf/open/lava, /turf/open/chasm))
- var/datum/action/innate/hereticmenu/menu
- ui_name = "AntagInfoHeretic"
+ var/datum/action/antag_info/heretic/menu
-/datum/antagonist/heretic/ui_data(mob/user)
- var/list/data = list()
+/datum/antagonist/heretic/Destroy()
+ . = ..()
+ LAZYCLEARLIST(target_blacklist)
+/datum/antagonist/heretic/ui_data(mob/user)
var/static/list/path_to_color = list(
HERETIC_PATH_START = "grey",
HERETIC_PATH_SIDE = "green",
@@ -56,7 +62,8 @@
HERETIC_PATH_VOID = "blue",
)
- data["charges"] = knowledge_points
+ var/list/available = list()
+ var/list/researched = list()
for(var/datum/heretic_knowledge/knowledge as anything in get_researchable_knowledge())
var/list/knowledge_data = list()
@@ -74,7 +81,7 @@
knowledge_data["hereticPath"] = initial(knowledge.route)
knowledge_data["color"] = path_to_color[initial(knowledge.route)] || "grey"
- data["learnableKnowledge"] += list(knowledge_data)
+ available += list(knowledge_data)
for(var/path in researched_knowledge)
var/list/knowledge_data = list()
@@ -86,18 +93,29 @@
knowledge_data["hereticPath"] = found_knowledge.route
knowledge_data["color"] = path_to_color[found_knowledge.route] || "grey"
- data["learnedKnowledge"] += list(knowledge_data)
-
- return data
+ researched += list(knowledge_data)
+
+ return list(
+ "sacrifices" = list(
+ "total" = total_sacrifices,
+ "command" = high_value_sacrifices
+ ),
+ "ascended" = ascended,
+ "points" = knowledge_points,
+ "knowledge" = list(
+ "available" = available,
+ "researched" = researched
+ )
+ )
/datum/antagonist/heretic/ui_static_data(mob/user)
- var/list/data = list()
-
- data["total_sacrifices"] = total_sacrifices
- data["ascended"] = ascended
- data["objectives"] = get_objectives()
+ return list("objectives" = get_objectives())
- return data
+/datum/antagonist/heretic/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, ui_name, name)
+ ui.open()
/datum/antagonist/heretic/make_info_button()
return // we already handle this with our own button
@@ -106,19 +124,16 @@
. = ..()
if(.)
return
-
switch(action)
if("research")
var/datum/heretic_knowledge/researched_path = text2path(params["path"])
if(!ispath(researched_path))
CRASH("Heretic attempted to learn non-heretic_knowledge path! (Got: [researched_path])")
-
if(initial(researched_path.cost) > knowledge_points)
return
if(!gain_knowledge(researched_path))
return
-
- knowledge_points -= initial(researched_path.cost)
+ adjust_knowledge_points(-initial(researched_path.cost))
return TRUE
/datum/antagonist/heretic/ui_status(mob/user, datum/ui_state/state)
@@ -214,18 +229,15 @@
*/
/datum/antagonist/heretic/proc/on_spell_cast(mob/living/source, obj/effect/proc_holder/spell/spell)
SIGNAL_HANDLER
-
// Non-Heretic spells, we don't care
if(!spell.requires_heretic_focus)
return
-
// If we've got the trait, we don't care
if(HAS_TRAIT(source, TRAIT_ALLOW_HERETIC_CASTING))
return
// All powerful, don't care
if(ascended)
return
-
// We shouldn't be able to cast this! Cancel it.
source.balloon_alert(source, "You need a focus")
return COMPONENT_CANCEL_SPELL
@@ -239,16 +251,13 @@
*/
/datum/antagonist/heretic/proc/on_item_afterattack(mob/living/source, atom/target, obj/item/weapon, proximity_flag, click_parameters)
SIGNAL_HANDLER
-
if(!is_type_in_typecache(weapon, scribing_tools))
return
if(!isturf(target) || !isliving(source) || !proximity_flag)
return
-
var/obj/item/offhand = source.get_inactive_held_item()
if(QDELETED(offhand) || !istype(offhand, /obj/item/melee/touch_attack/mansus_fist))
return
-
try_draw_rune(source, target, additional_checks = CALLBACK(src, PROC_REF(check_mansus_grasp_offhand), source))
return COMPONENT_CANCEL_ATTACK_CHAIN
@@ -266,15 +275,12 @@
if(!isopenturf(nearby_turf) || is_type_in_typecache(nearby_turf, blacklisted_rune_turfs))
target_turf.balloon_alert(user, "Invalid placement for rune")
return
-
if(locate(/obj/effect/heretic_rune) in range(3, target_turf))
target_turf.balloon_alert(user, "Too close to another rune")
return
-
if(drawing_rune)
target_turf.balloon_alert(user, "Already drawing a rune")
return
-
INVOKE_ASYNC(src, PROC_REF(draw_rune), user, target_turf, drawing_time, additional_checks)
/**
@@ -288,13 +294,19 @@
*/
/datum/antagonist/heretic/proc/draw_rune(mob/living/user, turf/target_turf, drawing_time = 30 SECONDS, additional_checks)
drawing_rune = TRUE
-
target_turf.balloon_alert(user, "You start drawing a rune")
+ for(var/knowledge_index in researched_knowledge)
+ var/datum/heretic_knowledge/limited_amount/knowledge = researched_knowledge[knowledge_index]
+ if(!istype(knowledge))
+ continue
+ var/length_mul = knowledge.rune_draw_time_multiplier(user, target_turf)
+ if(isnum_safe(length_mul) && length_mul > 0)
+ drawing_time *= length_mul
+ break
if(!do_after(user, drawing_time, target = target_turf, extra_checks = additional_checks))
target_turf.balloon_alert(user, "Interrupted")
drawing_rune = FALSE
return
-
target_turf.balloon_alert(user, "Rune created")
new /obj/effect/heretic_rune/big(target_turf)
drawing_rune = FALSE
@@ -318,55 +330,109 @@
*/
/datum/antagonist/heretic/proc/fix_influence_network(mob/source)
SIGNAL_HANDLER
-
GLOB.reality_smash_track.rework_network()
/**
* Create our objectives for our heretic.
*/
/datum/antagonist/heretic/proc/forge_objectives()
+ // Give research objective
var/datum/objective/heretic_research/research_objective = new()
research_objective.owner = owner
objectives += research_objective
-
+ // Calculate how many command members there are
var/num_heads = 0
for(var/mob/player in SSticker.mode.current_players[CURRENT_LIVING_PLAYERS])
- if(player.mind.assigned_role in list("Captain", "Head of Personnel", "Chief Engineer", "Head of Security", "Research Director", "Chief Medical Officer"))
+ if((player.mind.assigned_role in GLOB.command_positions) && player.ckey && player.client)
num_heads++
-
+ // Give normal sacrifice objective
var/datum/objective/minor_sacrifice/sac_objective = new()
sac_objective.owner = owner
if(num_heads < 2) // They won't get major sacrifice, so bump up minor sacrifice a bit
sac_objective.target_amount += 2
sac_objective.update_explanation_text()
objectives += sac_objective
-
+ // Give command sacrifice objective (if there's at least 2 command staff)
if(num_heads >= 2)
var/datum/objective/major_sacrifice/other_sac_objective = new()
other_sac_objective.owner = owner
objectives += other_sac_objective
+ update_static_data_for_all_viewers()
/**
* Add [target] as a sacrifice target for the heretic.
- * Generates a preview image and associates it with a weakref of the mob.
+ * Generates a preview image and associates it with a weakref of the mob's mind.
*/
-/datum/antagonist/heretic/proc/add_sacrifice_target(mob/living/carbon/human/target)
+/datum/antagonist/heretic/proc/add_sacrifice_target(target)
+ var/datum/mind/target_mind = get_mind(target, TRUE)
+ if(QDELETED(target_mind))
+ return FALSE
+ var/mob/living/carbon/target_body = target_mind.current
+ if(!istype(target_body))
+ return FALSE
+ RegisterSignal(target_mind, COMSIG_MIND_CRYOED, PROC_REF(on_sac_target_cryoed))
+ LAZYSET(sac_targets, WEAKREF(target_mind), getFlatIcon(target_body, defdir = SOUTH))
+ return TRUE
- var/image/target_image = image(icon = target.icon, icon_state = target.icon_state)
- target_image.overlays = target.overlays
+/**
+ * Remove [target] as a sacrifice target for the heretic.
+ */
+/datum/antagonist/heretic/proc/remove_sacrifice_target(target)
+ var/datum/mind/target_mind = get_mind(target, TRUE)
+ if(!QDELETED(target_mind))
+ UnregisterSignal(target_mind, COMSIG_MIND_CRYOED)
+ LAZYREMOVE(sac_targets, WEAKREF(target_mind))
- LAZYSET(sac_targets, WEAKREF(target), target_image)
+/**
+ * Returns a list of valid sacrifice targets from the current living players.
+ */
+/datum/antagonist/heretic/proc/possible_sacrifice_targets(include_current_targets = TRUE)
+ . = list()
+ for(var/mob/living/carbon/human/player in SSticker.mode.current_players[CURRENT_LIVING_PLAYERS])
+ if(!player.mind || !player.client || player.client.is_afk())
+ continue
+ var/datum/mind/possible_target = player.mind
+ if(!include_current_targets && (WEAKREF(possible_target) in sac_targets))
+ continue
+ if(possible_target == src)
+ continue
+ if(possible_target in target_blacklist)
+ continue
+ if(player.stat == DEAD || player.InFullCritical())
+ continue
+ . += possible_target
+
+/datum/antagonist/heretic/proc/on_sac_target_cryoed(datum/mind/sac_mind)
+ SIGNAL_HANDLER
+ if(!istype(sac_mind) || !LAZYLEN(sac_targets))
+ return
+ remove_sacrifice_target(sac_mind)
+ var/list/candidates = possible_sacrifice_targets(include_current_targets = FALSE)
+ if(!length(candidates))
+ to_chat(owner, "You feel one of your sacrifice targets leave your reach... but the Mansus remains silent.")
+ return
+ var/datum/mind/new_target = pick(candidates)
+ add_sacrifice_target(new_target)
+ to_chat(owner, "The Mansus whispers to you a new name as one of your previous sacrifice targets exits your grasp... [new_target.name]. Go forth and sacrifice [new_target.current.p_them()]!")
/**
* Increments knowledge by one.
* Used in callbacks for passive gain over time.
*/
/datum/antagonist/heretic/proc/passive_influence_gain()
- knowledge_points++
+ adjust_knowledge_points(1)
if(owner.current.stat <= SOFT_CRIT)
to_chat(owner.current, "You hear a whisper... [pick(strings(HERETIC_INFLUENCE_FILE, "drain_message"))]")
addtimer(CALLBACK(src, PROC_REF(passive_influence_gain)), passive_gain_timer)
+/datum/antagonist/heretic/proc/adjust_knowledge_points(amount)
+ knowledge_points += amount
+ ui_update()
+
+/datum/antagonist/heretic/proc/add_menu_action()
+ menu = new /datum/action/antag_info/heretic(src)
+ menu.Grant(owner.current)
+
/datum/antagonist/heretic/roundend_report()
var/list/parts = list()
@@ -378,14 +444,13 @@
if(length(objectives))
var/count = 1
for(var/datum/objective/objective as anything in objectives)
- parts += "Objective #[count]: [objective.get_completion_message()]"
+ parts += "Objective #[count]: [objective.get_completion_message()]"
if(!objective.check_completion())
succeeded = FALSE
count++
if(ascended)
parts += "THE HERETIC ASCENDED!"
-
else
if(succeeded)
parts += "The heretic was successful, but did not ascend!"
@@ -395,87 +460,71 @@
parts += "Knowledge Researched: "
var/list/string_of_knowledge = list()
-
for(var/knowledge_index in researched_knowledge)
var/datum/heretic_knowledge/knowledge = researched_knowledge[knowledge_index]
string_of_knowledge += knowledge.name
-
parts += english_list(string_of_knowledge)
-
return parts.Join("
")
/datum/antagonist/heretic/get_admin_commands()
. = ..()
-
switch(has_living_heart())
if(HERETIC_NO_LIVING_HEART)
- .["Give Living Heart"] = CALLBACK(src, PROC_REF(give_living_heart))
+ .["Give Living Heart"] = CALLBACK(src, PROC_REF(admin_give_living_heart))
if(HERETIC_HAS_LIVING_HEART)
- .["Add Heart Target (Marked Mob)"] = CALLBACK(src, PROC_REF(add_marked_as_target))
- .["Remove Heart Target"] = CALLBACK(src, PROC_REF(remove_target))
-
+ .["Add Heart Target (Marked Mob)"] = CALLBACK(src, PROC_REF(admin_add_marked_target))
+ .["Remove Heart Target"] = CALLBACK(src, PROC_REF(admin_remove_target))
.["Adjust Knowledge Points"] = CALLBACK(src, PROC_REF(admin_change_points))
/*
* Admin proc for giving a heretic a Living Heart easily.
*/
-/datum/antagonist/heretic/proc/give_living_heart(mob/admin)
+/datum/antagonist/heretic/proc/admin_give_living_heart(mob/admin)
if(!admin.client?.holder)
to_chat(admin, "You shouldn't be using this!")
return
-
var/datum/heretic_knowledge/living_heart/heart_knowledge = get_knowledge(/datum/heretic_knowledge/living_heart)
if(!heart_knowledge)
to_chat(admin, "The heretic doesn't have a living heart knowledge for some reason. What?")
return
-
heart_knowledge.on_research(owner.current)
/*
* Admin proc for adding a marked mob to a heretic's sac list.
*/
-/datum/antagonist/heretic/proc/add_marked_as_target(mob/admin)
+/datum/antagonist/heretic/proc/admin_add_marked_target(mob/admin)
if(!admin.client?.holder)
to_chat(admin, "You shouldn't be using this!")
return
-
var/mob/living/carbon/human/new_target = admin.client?.holder.marked_datum
if(!istype(new_target))
to_chat(admin, "You need to mark a human to do this!")
return
-
- if(alert(admin, "Let them know their targets have been updated?", "Whispers of the Mansus", "Yes", "No") == "Yes")
+ if(tgui_alert(admin, "Let them know their targets have been updated?", "Whispers of the Mansus", list("Yes", "No")) == "Yes")
to_chat(owner.current, "The Mansus has modified your targets. Go find them!")
to_chat(owner.current, "[new_target.real_name], the [new_target.mind?.assigned_role || "human"].")
-
add_sacrifice_target(new_target)
/*
* Admin proc for removing a mob from a heretic's sac list.
*/
-/datum/antagonist/heretic/proc/remove_target(mob/admin)
+/datum/antagonist/heretic/proc/admin_remove_target(mob/admin)
if(!admin.client?.holder)
to_chat(admin, "You shouldn't be using this!")
return
-
var/list/removable = list()
for(var/datum/weakref/ref as anything in sac_targets)
- var/mob/living/carbon/human/old_target = ref.resolve()
+ var/datum/mind/old_target = ref.resolve()
if(!QDELETED(old_target))
removable[old_target.name] = old_target
-
- var/name_of_removed = input(admin, "Choose a human to remove", "Who to Spare") as null|anything in removable
+ var/name_of_removed = tgui_input_list(admin, "Choose a human to remove", "Who to Spare", removable)
if(QDELETED(src) || !admin.client?.holder || isnull(name_of_removed))
return
- var/mob/living/carbon/human/chosen_target = removable[name_of_removed]
- if(QDELETED(chosen_target) || !ishuman(chosen_target))
+ var/datum/mind/chosen_target = removable[name_of_removed]
+ if(!istype(chosen_target) || !(WEAKREF(chosen_target) in sac_targets))
return
- if(!(WEAKREF(chosen_target) in sac_targets))
- return
-
LAZYREMOVE(sac_targets, WEAKREF(chosen_target))
-
- if(alert(admin, "Let them know their targets have been updated?", "Whispers of the Mansus", "Yes", "No") == "Yes")
+ if(tgui_alert(admin, "Let them know their targets have been updated?", "Whispers of the Mansus", list("Yes", "No")) == "Yes")
to_chat(owner.current, "The Mansus has modified your targets.")
/*
@@ -486,10 +535,10 @@
to_chat(admin, "You shouldn't be using this!")
return
- var/change_num = input(admin, "Add or remove knowledge points", "Points") as null|num
+ var/change_num = tgui_input_number(admin, "Add or remove knowledge points", "Points", 0)
if(!change_num || QDELETED(src))
return
- knowledge_points += change_num
+ adjust_knowledge_points(change_num)
message_admins("[admin] modified [src]'s knowledge points by [change_num].")
/datum/antagonist/heretic/antag_panel_data()
@@ -506,15 +555,14 @@
/datum/antagonist/heretic/antag_panel_objectives()
. = ..()
-
. += "
"
. += "Current Targets:
"
if(LAZYLEN(sac_targets))
for(var/datum/weakref/ref as anything in sac_targets)
- var/mob/living/carbon/human/actual_target = ref.resolve()
- if(QDELETED(actual_target))
+ var/datum/mind/actual_target = ref.resolve()
+ if(istype(actual_target))
continue
- . += " - [actual_target.real_name], the [actual_target.mind?.assigned_role || "Unknown"].
"
+ . += " - [actual_target.name], the [actual_target.assigned_role || "Unknown"].
"
else
. += "None!
"
. += "
"
@@ -553,8 +601,28 @@
/*
* Check if the wanted type-path is in the list of research knowledge.
*/
-/datum/antagonist/heretic/proc/get_knowledge(wanted)
- return researched_knowledge[wanted]
+/datum/antagonist/heretic/proc/get_knowledge(wanted, subtypes = FALSE)
+ if(subtypes)
+ for(var/knowledge_path in typesof(wanted))
+ if(researched_knowledge[knowledge_path])
+ return researched_knowledge[knowledge_path]
+ else
+ return researched_knowledge[wanted]
+
+/**
+ * Check to see if the given mob can be sacrificed
+ */
+/datum/antagonist/heretic/proc/can_sacrifice(target)
+ . = FALSE
+ var/datum/mind/target_mind = get_mind(target, TRUE)
+ if(!istype(target_mind))
+ return
+ if(LAZYACCESS(sac_targets, WEAKREF(target_mind)))
+ return TRUE
+ // You can ALWAYS sacrifice heads of staff if you need to do so.
+ var/datum/objective/major_sacrifice/major_sacc_objective = locate() in objectives
+ if(major_sacc_objective && !major_sacc_objective.check_completion() && (target_mind.assigned_role in GLOB.command_positions))
+ return TRUE
/*
* Get a list of all rituals this heretic can invoke on a rune.
@@ -564,13 +632,11 @@
*/
/datum/antagonist/heretic/proc/get_rituals()
var/list/rituals = list()
-
for(var/knowledge_index in researched_knowledge)
var/datum/heretic_knowledge/knowledge = researched_knowledge[knowledge_index]
if(!knowledge.can_be_invoked(src))
continue
rituals[knowledge.name] = knowledge
-
return sortTim(rituals, /proc/cmp_heretic_knowledge, associative = TRUE)
/*
@@ -598,10 +664,8 @@
var/obj/item/organ/our_living_heart = owner.current?.getorganslot(ORGAN_SLOT_HEART)
if(!our_living_heart)
return HERETIC_NO_HEART_ORGAN
-
if(!HAS_TRAIT(our_living_heart, TRAIT_LIVING_HEART))
return HERETIC_NO_LIVING_HEART
-
return HERETIC_HAS_LIVING_HEART
/// Heretic's minor sacrifice objective. "Minor sacrifices" includes anyone.
@@ -619,9 +683,7 @@
/datum/objective/minor_sacrifice/check_completion()
var/datum/antagonist/heretic/heretic_datum = owner?.has_antag_datum(/datum/antagonist/heretic)
- if(!heretic_datum)
- return FALSE
- return heretic_datum.total_sacrifices >= target_amount
+ return ..() || (heretic_datum?.total_sacrifices >= target_amount)
/// Heretic's major sacrifice objective. "Major sacrifices" are heads of staff.
/datum/objective/major_sacrifice
@@ -631,9 +693,7 @@
/datum/objective/major_sacrifice/check_completion()
var/datum/antagonist/heretic/heretic_datum = owner?.has_antag_datum(/datum/antagonist/heretic)
- if(!heretic_datum)
- return FALSE
- return heretic_datum.high_value_sacrifices >= target_amount
+ return ..() || (heretic_datum?.high_value_sacrifices >= target_amount)
/// Heretic's research objective. "Research" is heretic knowledge nodes (You start with some).
/datum/objective/heretic_research
@@ -643,7 +703,6 @@
/datum/objective/heretic_research/New(text)
. = ..()
-
if(!main_path_length)
// Let's find the length of a main path. We'll use rust because it's the coolest.
// (All the main paths are (should be) the same length, so it doesn't matter.)
@@ -651,9 +710,7 @@
for(var/datum/heretic_knowledge/knowledge as anything in subtypesof(/datum/heretic_knowledge))
if(initial(knowledge.route) == HERETIC_PATH_RUST)
rust_paths_found++
-
main_path_length = rust_paths_found
-
// Factor in the length of the main path first.
target_amount = main_path_length
// Add in the base research we spawn with, otherwise it'd be too easy.
@@ -668,9 +725,7 @@
/datum/objective/heretic_research/check_completion()
var/datum/antagonist/heretic/heretic_datum = owner?.has_antag_datum(/datum/antagonist/heretic)
- if(!heretic_datum)
- return FALSE
- return length(heretic_datum.researched_knowledge) >= target_amount
+ return completed || (length(heretic_datum?.researched_knowledge) >= target_amount)
/datum/objective/heretic_summon
name = "summon monsters"
@@ -678,19 +733,8 @@
explanation_text = "Summon 2 monsters from the Mansus into this realm."
/datum/objective/heretic_summon/check_completion()
-
- var/num_we_have = 0
- for(var/datum/antagonist/heretic_monster/monster in GLOB.antagonists)
- if(!monster.master)
- continue
- if(ishuman(monster.owner.current))
- continue
- if(monster.master != owner)
- continue
-
- num_we_have++
-
- return completed || (num_we_have >= target_amount)
+ var/datum/antagonist/heretic/heretic_datum = owner?.has_antag_datum(/datum/antagonist/heretic)
+ return ..() || (LAZYLEN(heretic_datum?.monsters_summoned) >= target_amount)
/datum/action/antag_info/heretic
name = "Forbidden Knowledge"
diff --git a/code/modules/antagonists/heretic/heretic_knowledge.dm b/code/modules/antagonists/heretic/heretic_knowledge.dm
index ed9eb138decdd..c3bf08c16466c 100644
--- a/code/modules/antagonists/heretic/heretic_knowledge.dm
+++ b/code/modules/antagonists/heretic/heretic_knowledge.dm
@@ -183,7 +183,7 @@
/// A list of weakrefs to all items we've created.
var/list/datum/weakref/created_items
-/datum/heretic_knowledge/limited_amount/Destroy(force, ...)
+/datum/heretic_knowledge/limited_amount/Destroy()
LAZYCLEARLIST(created_items)
return ..()
@@ -192,11 +192,9 @@
var/atom/real_thing = ref.resolve()
if(QDELETED(real_thing))
LAZYREMOVE(created_items, ref)
-
if(LAZYLEN(created_items) >= limit)
loc.balloon_alert(user, "ritual failed, at limit!")
return FALSE
-
return TRUE
/datum/heretic_knowledge/limited_amount/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
@@ -205,49 +203,60 @@
LAZYADD(created_items, WEAKREF(created_thing))
return TRUE
+/*
+ * Returns a multiplier for the time required to draw a rune.
+ */
+/datum/heretic_knowledge/limited_amount/proc/rune_draw_time_multiplier(mob/living/user, turf/target_turf)
+ var/datum/antagonist/heretic/our_heretic = IS_HERETIC(user)
+ if(our_heretic.ascended)
+ return 0.2
+ if(our_heretic.can_sacrifice(user.pulling))
+ return 0.5
+ for(var/mob/living/target as anything in range(1, target_turf) + range(1, user))
+ if(istype(target) && our_heretic.can_sacrifice(target))
+ return 0.5
+ return 1
+
/*
* A knowledge subtype lets the heretic curse someone with a ritual.
*/
/datum/heretic_knowledge/curse
/// The duration of the curse
var/duration = 5 MINUTES
- /// Cache list of fingerprints (actual fingerprint strings) we have from our current ritual
- var/list/fingerprints
+ /// Cache list of DNA (UI strings) we have from our current ritual
+ var/list/dna
/datum/heretic_knowledge/curse/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc)
- fingerprints = list()
+ dna = list()
for(var/atom/requirements as anything in atoms)
- fingerprints[requirements.return_fingerprints()] = 1
- list_clear_nulls(fingerprints)
-
- // No fingerprints? No ritual
- if(!length(fingerprints))
- loc.balloon_alert(user, "ritual failed, no fingerprints!")
+ for(var/fingerprint in requirements.return_fingerprints())
+ dna[fingerprint] = TRUE
+ for(var/blood in requirements.return_blood_DNA())
+ dna[blood] = TRUE
+ list_clear_nulls(dna)
+ // No DNA? No ritual
+ if(!length(dna))
+ loc.balloon_alert(user, "ritual failed, no DNA!")
return FALSE
-
return TRUE
/datum/heretic_knowledge/curse/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
-
var/list/compiled_list = list()
-
- for(var/mob/living/carbon/carbon_to_check as anything in GLOB.carbon_list)
- if(!istype(carbon_to_check, /mob/living/carbon/human))
+ for(var/mob/living/carbon/human/candidate in GLOB.carbon_list)
+ if(candidate == user || QDELETED(candidate.mind) || !length(candidate.dna?.uni_identity))
continue
- var/mob/living/carbon/human/human_to_check = carbon_to_check
- if(fingerprints[md5(human_to_check.dna.uni_identity)])
- compiled_list |= human_to_check.real_name
- compiled_list[human_to_check.real_name] = human_to_check
+ var/dna_string = rustg_hash_string(RUSTG_HASH_MD5, candidate.dna.uni_identity)
+ if(dna[dna_string])
+ compiled_list |= candidate.real_name
+ compiled_list[candidate.real_name] = candidate
if(!length(compiled_list))
- loc.balloon_alert(user, "Ritual failed, no fingerprints found")
+ loc.balloon_alert(user, "Ritual failed, no DNA found")
return FALSE
- var/chosen_mob = input(user, "Select the person you wish to curse", "Eldritch Curse") as null|anything in sort_list(compiled_list, GLOBAL_PROC_REF(cmp_mob_realname_dsc))
- if(isnull(chosen_mob))
+ var/mob/living/carbon/human/to_curse = tgui_input_list(user, "Select the person you wish to curse", "Eldritch Curse", sort_list(compiled_list, GLOBAL_PROC_REF(cmp_mob_realname_dsc)))
+ if(isnull(to_curse))
return FALSE
-
- var/mob/living/carbon/human/to_curse = compiled_list[chosen_mob]
if(QDELETED(to_curse))
loc.balloon_alert(user, "Ritual failed, invalid choice")
return FALSE
@@ -307,6 +316,9 @@
var/datum/antagonist/heretic_monster/heretic_monster = summoned.mind.add_antag_datum(/datum/antagonist/heretic_monster)
heretic_monster.set_owner(user.mind)
+ var/datum/antagonist/heretic/our_heretic = IS_HERETIC(user)
+ LAZYOR(our_heretic.monsters_summoned, WEAKREF(summoned.mind))
+
return TRUE
/// The amount of knowledge points the knowledge ritual gives on success.
@@ -328,36 +340,36 @@
. = ..()
var/static/list/potential_organs = list(
/obj/item/organ/appendix,
- /obj/item/organ/tail,
- /obj/item/organ/eyes,
- /obj/item/organ/tongue,
/obj/item/organ/ears,
+ /obj/item/organ/eyes,
/obj/item/organ/heart,
/obj/item/organ/liver,
- /obj/item/organ/stomach,
/obj/item/organ/lungs,
+ /obj/item/organ/stomach,
+ /obj/item/organ/tail,
+ /obj/item/organ/tongue
)
var/static/list/potential_easy_items = list(
- /obj/item/shard,
- /obj/item/candle,
/obj/item/book,
- /obj/item/pen,
- /obj/item/paper,
- /obj/item/toy/crayon,
- /obj/item/flashlight,
+ /obj/item/candle,
/obj/item/clipboard,
+ /obj/item/flashlight,
+ /obj/item/paper,
+ /obj/item/pen,
+ /obj/item/shard,
+ /obj/item/toy/crayon
)
var/static/list/potential_uncommoner_items = list(
- /obj/item/restraints/legcuffs/beartrap,
- /obj/item/restraints/handcuffs/cable/zipties,
- /obj/item/circular_saw,
- /obj/item/scalpel,
/obj/item/binoculars,
+ /obj/item/circular_saw,
+ /obj/item/clothing/glasses/sunglasses,
/obj/item/clothing/gloves/color/yellow,
/obj/item/melee/baton,
- /obj/item/clothing/glasses/sunglasses,
+ /obj/item/restraints/handcuffs/cable/zipties,
+ /obj/item/restraints/legcuffs/beartrap,
+ /obj/item/scalpel
)
required_atoms = list()
@@ -392,7 +404,7 @@
/datum/heretic_knowledge/knowledge_ritual/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
var/datum/antagonist/heretic/our_heretic = IS_HERETIC(user)
- our_heretic.knowledge_points += KNOWLEDGE_RITUAL_POINTS
+ our_heretic.adjust_knowledge_points(KNOWLEDGE_RITUAL_POINTS)
was_completed = TRUE
var/drain_message = pick(strings(HERETIC_INFLUENCE_FILE, "drain_message"))
@@ -419,25 +431,17 @@
total_points += knowledge.cost
/datum/heretic_knowledge/final/can_be_invoked(datum/antagonist/heretic/invoker)
- if(invoker.ascended)
- return FALSE
-
- if(!invoker.can_ascend())
- return FALSE
-
- return TRUE
+ return !invoker.ascended && invoker.can_ascend()
/datum/heretic_knowledge/final/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc)
var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
if(!can_be_invoked(heretic_datum))
return FALSE
-
// Remove all non-dead humans from the atoms list.
// (We only want to sacrifice dead folk.)
for(var/mob/living/carbon/human/sacrifice in atoms)
if(!is_valid_sacrifice(sacrifice))
atoms -= sacrifice
-
// All the non-dead humans are removed in this proc.
// We handle checking if we have enough humans in the ritual itself.
return TRUE
@@ -463,5 +467,4 @@
for(var/mob/living/carbon/human/sacrifice in selected_atoms)
selected_atoms -= sacrifice
sacrifice.gib()
-
return ..()
diff --git a/code/modules/antagonists/heretic/heretic_living_heart.dm b/code/modules/antagonists/heretic/heretic_living_heart.dm
index acf2182aaa7b8..6459989cb2af7 100644
--- a/code/modules/antagonists/heretic/heretic_living_heart.dm
+++ b/code/modules/antagonists/heretic/heretic_living_heart.dm
@@ -72,26 +72,22 @@
desc = "Track a Sacrifice Target"
check_flags = AB_CHECK_CONSCIOUS
background_icon_state = "bg_ecult"
- /// The real name of the last mob we tracked
- var/last_tracked_name
/// Whether the target radial is currently opened.
var/radial_open = FALSE
/// How long we have to wait between tracking uses.
- var/track_cooldown_lenth = 8 SECONDS
+ var/track_cooldown_length = 8 SECONDS
/// The cooldown between button uses.
COOLDOWN_DECLARE(track_cooldown)
/datum/action/item_action/organ_action/track_target/Grant(mob/granted)
if(!IS_HERETIC(granted))
return
-
return ..()
/datum/action/item_action/organ_action/track_target/IsAvailable()
. = ..()
if(!.)
return
-
if(!IS_HERETIC(owner))
return FALSE
if(!HAS_TRAIT(target, TRAIT_LIVING_HEART))
@@ -107,22 +103,21 @@
return
var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(owner)
- if(!LAZYLEN(heretic_datum.sac_targets))
+ if(!LAZYLEN(heretic_datum.sac_targets) && !length(GLOB.reality_smash_track.smashes))
owner.balloon_alert(owner, "No targets, visit a rune")
return TRUE
var/list/targets_to_choose = list()
- var/list/mob/living/carbon/human/human_targets = list()
+ var/list/mob/living/carbon/tracked_targets = list()
for(var/datum/weakref/target_ref as anything in heretic_datum.sac_targets)
- var/mob/living/carbon/human/real_target = target_ref.resolve()
- if(QDELETED(real_target))
+ var/datum/mind/target_mind = target_ref.resolve()
+ if(!istype(target_mind) || !iscarbon(target_mind.current))
continue
-
- human_targets[real_target.real_name] = real_target
- targets_to_choose[real_target.real_name] = heretic_datum.sac_targets[target_ref]
+ tracked_targets[target_mind.name] = target_mind.current
+ targets_to_choose[target_mind.name] = heretic_datum.sac_targets[target_ref]
radial_open = TRUE
- last_tracked_name = show_radial_menu(
+ var/tracked = show_radial_menu(
owner,
owner,
targets_to_choose,
@@ -132,42 +127,50 @@
tooltips = TRUE,
)
radial_open = FALSE
-
// If our last tracked name is still null, skip the trigger
- if(isnull(last_tracked_name))
+ if(isnull(tracked))
return FALSE
- var/mob/living/carbon/human/tracked_mob = human_targets[last_tracked_name]
+ var/mob/living/carbon/tracked_mob = tracked_targets[tracked]
if(QDELETED(tracked_mob))
- last_tracked_name = null
return FALSE
+ . = track_sacrifice_target(tracked_mob)
- COOLDOWN_START(src, track_cooldown, track_cooldown_lenth)
- var/balloon_message = "Your target is "
+ if(.)
+ COOLDOWN_START(src, track_cooldown, track_cooldown_length)
+ playsound(owner, 'sound/effects/singlebeat.ogg', vol = 50, vary = TRUE, extrarange = SILENCED_SOUND_EXTRARANGE)
- playsound(owner, 'sound/effects/singlebeat.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
- if(isturf(tracked_mob.loc) && owner.z != tracked_mob.z)
- balloon_message += "on another plane"
+/datum/action/item_action/organ_action/track_target/proc/track_sacrifice_target(mob/living/carbon/tracked)
+ var/turf/owner_turf = get_turf(owner)
+ var/turf/tracked_turf = get_turf(tracked)
+ var/balloon_message = "Your target is "
+ if(tracked.stat == DEAD)
+ balloon_message += "dead and "
+ else if(!tracked.ckey)
+ balloon_message += "catatonic and "
+ if(owner_turf.get_virtual_z_level() != tracked_turf.get_virtual_z_level())
+ if(is_reserved_level(tracked_turf.z))
+ balloon_message += "traveling through space"
+ else
+ balloon_message += "on another plane"
else
- var/dist = get_dist(get_turf(owner), get_turf(tracked_mob))
- var/dir = get_dir(get_turf(owner), get_turf(tracked_mob))
-
- switch(dist)
- if(0 to 15)
- balloon_message += "very near, [dir2text(dir)]"
- if(16 to 31)
- balloon_message += "near, [dir2text(dir)]"
- if(32 to 127)
- balloon_message += "far, [dir2text(dir)]"
- else
- balloon_message += "very far"
-
- if(tracked_mob.stat == DEAD)
- balloon_message += "dead, they're " + balloon_message
-
+ balloon_message += distance_hint(owner_turf, tracked_turf)
owner.balloon_alert(owner, balloon_message)
return TRUE
+/datum/action/item_action/organ_action/track_target/proc/distance_hint(turf/source, turf/target)
+ var/dist = get_dist(source, target)
+ var/dir = get_dir(source, target)
+ switch(dist)
+ if(0 to 15)
+ return "very near, [dir2text(dir)]"
+ if(16 to 31)
+ return "near, [dir2text(dir)]"
+ if(32 to 127)
+ return "far away, [dir2text(dir)]"
+ else
+ return "very far away"
+
/// Callback for the radial to ensure it's closed when not allowed.
/datum/action/item_action/organ_action/track_target/proc/check_menu()
if(QDELETED(src))
diff --git a/code/modules/antagonists/heretic/influences.dm b/code/modules/antagonists/heretic/influences.dm
index 8b394c5d71477..edfb744df3112 100644
--- a/code/modules/antagonists/heretic/influences.dm
+++ b/code/modules/antagonists/heretic/influences.dm
@@ -1,6 +1,11 @@
-
/// The number of influences spawned per heretic
#define NUM_INFLUENCES_PER_HERETIC 4
+/// The minimum amount of time until midround influences begin to spawn
+#define MIDROUND_INFLUENCE_MIN_TIME 25 MINUTES
+/// The maximum amount of time until midround influences begin to spawn
+#define MIDROUND_INFLUENCE_MAX_TIME 40 MINUTES
+/// How many tiles to "fuzz" the influence spawn location by.
+#define HERETIC_INFLUENCE_SPAWN_FUZZ 5
/**
* #Reality smash tracker
@@ -18,10 +23,13 @@
var/num_drained = 0
/// List of tracked influences (reality smashes)
var/list/obj/effect/heretic_influence/smashes = list()
+ /// List of visible influences (harvested reality smashes)
+ var/list/obj/effect/visible_heretic_influence/visible_smashes = list()
/// List of minds with the ability to see influences
var/list/datum/mind/tracked_heretics = list()
-/datum/reality_smash_tracker/Destroy(force, ...)
+
+/datum/reality_smash_tracker/Destroy()
if(GLOB.reality_smash_track == src)
stack_trace("[type] was deleted. Heretics may no longer access any influences. Fix it, or call coder support.")
message_admins("The [type] was deleted. Heretics may no longer access any influences. Fix it, or call coder support.")
@@ -29,6 +37,39 @@
tracked_heretics.Cut()
return ..()
+/datum/reality_smash_tracker/proc/start_midround_influence_timer()
+ var/time = FLOOR(rand(MIDROUND_INFLUENCE_MIN_TIME, MIDROUND_INFLUENCE_MAX_TIME), 15 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(spawn_midround_influences)), time, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_DELETE_ME)
+
+/**
+ * Generates some random new influences in the middle of the round for each heretic,
+ * regardless of pre-existing heretics.
+ */
+/datum/reality_smash_tracker/proc/spawn_midround_influences()
+ start_midround_influence_timer()
+ var/living_heretics = 0
+ for(var/datum/mind/heretic_mind as anything in tracked_heretics)
+ var/mob/living/carbon/heretic_body = heretic_mind.current
+ var/datum/antagonist/heretic/heretic_datum = heretic_mind.has_antag_datum(/datum/antagonist/heretic)
+ if(QDELETED(heretic_datum))
+ continue
+ if(heretic_datum.ascended)
+ continue
+ if(heretic_datum.can_ascend() && heretic_datum.get_knowledge(/datum/heretic_knowledge/final, subtypes = TRUE))
+ continue
+ if(QDELETED(heretic_body) || !istype(heretic_body))
+ continue
+ if(!heretic_body.ckey || heretic_body.stat == DEAD || heretic_body.InFullCritical())
+ continue
+ living_heretics++
+ if(!living_heretics)
+ return
+ var/influences_to_spawn = rand(1, 3)
+ for(var/heretic_number = 1 to living_heretics)
+ influences_to_spawn += max(NUM_INFLUENCES_PER_HERETIC - heretic_number, 1)
+ log_game("Spawning [influences_to_spawn] new midround influences for heretics")
+ INVOKE_ASYNC(src, PROC_REF(generate_new_influences), influences_to_spawn)
+
/**
* Automatically fixes the target and smash network
*
@@ -64,28 +105,149 @@
* based on the number of already existing smashes
* and the number of minds we're tracking.
*/
-/datum/reality_smash_tracker/proc/generate_new_influences()
- // 1 heretic = 4 influences
- // 2 heretics = 7 influences
- // 3 heretics = 9 influences
- // 4 heretics = 10 influences, +1 for each onwards.
- var/how_many_can_we_make = 0
- for(var/heretic_number in 1 to length(tracked_heretics))
- how_many_can_we_make += max(NUM_INFLUENCES_PER_HERETIC - heretic_number + 1, 1)
+/datum/reality_smash_tracker/proc/generate_new_influences(amount)
+ /// Static list of station areas which would otherwise be valid, but we don't want to spawn influences in.
+ var/static/list/station_area_blacklist = typecacheof(list(
+ /area/chapel/office,
+ /area/commons/dorms/barracks,
+ /area/crew_quarters/theatre/abandoned,
+ /area/crew_quarters/theatre/backstage,
+ /area/hallway/secondary/asteroid,
+ /area/hallway/secondary/command,
+ /area/hallway/secondary/construction,
+ /area/hallway/secondary/service,
+ /area/hallway/upper/secondary/command,
+ /area/hallway/upper/secondary/construction,
+ /area/hallway/upper/secondary/service,
+ /area/holodeck/rec_center/offstation_one,
+ /area/hydroponics/garden/abandoned,
+ /area/library/abandoned
+ ))
+ /// Static list of station areas we will attempt to spawn influences in the general vicinity of.
+ var/static/list/public_station_areas = typecacheof(list(
+ /area/cargo/lobby,
+ /area/chapel,
+ /area/commons/dorms,
+ /area/crew_quarters/cafeteria,
+ /area/crew_quarters/fitness,
+ /area/crew_quarters/locker,
+ /area/crew_quarters/lounge,
+ /area/crew_quarters/park,
+ /area/crew_quarters/theatre,
+ /area/crew_quarters/toilet,
+ /area/hallway,
+ /area/holodeck/rec_center,
+ /area/hydroponics/garden,
+ /area/library,
+ /area/medical/medbay/central,
+ /area/medical/medbay/lobby,
+ /area/medical/sleeper,
+ /area/science/lobby,
+ /area/storage/art,
+ /area/storage/primary,
+ /area/storage/tools
+ )) - station_area_blacklist
+ /// Static typecache of 'secondary' areas that should be weighted less.
+ var/static/list/low_weight_areas = typecacheof(/area/security/checkpoint)
+ /// Static typecache of the areas we will NEVER spawn influences in.
+ var/static/list/forbidden_influence_areas = (typecacheof(list(
+ /area/ai_monitored,
+ /area/asteroid,
+ /area/bridge,
+ /area/comms,
+ /area/crew_quarters/heads,
+ /area/docking,
+ /area/drydock,
+ /area/engine/atmospherics_engine,
+ /area/engine/engine_room,
+ /area/engine/gravity_generator,
+ /area/engine/transit_tube,
+ /area/gateway,
+ /area/maintenance,
+ /area/medical/abandoned,
+ /area/quartermaster/qm,
+ /area/quartermaster/qm_bedroom,
+ /area/science/mixing/chamber,
+ /area/security,
+ /area/server,
+ /area/shuttle,
+ /area/solar,
+ /area/space,
+ /area/tcommsat,
+ /area/teleporter
+ )) + station_area_blacklist) - low_weight_areas
+
+ if(!isnum_safe(amount) || amount <= 0)
+ // 1 heretic = 4 influences
+ // 2 heretics = 7 influences
+ // 3 heretics = 9 influences
+ // 4 heretics = 10 influences, +1 for each onwards.
+ var/max_amount = 0
+ for(var/heretic_number in 1 to length(tracked_heretics))
+ max_amount += max(NUM_INFLUENCES_PER_HERETIC - heretic_number + 1, 1)
+ amount = max_amount - (length(smashes) + num_drained)
+ // Don't bother doing all this stuff if we've made enough influences already.
+ if(amount <= 0)
+ rework_network()
+ return
var/location_sanity = 0
- while((length(smashes) + num_drained) < how_many_can_we_make && location_sanity < 100)
- var/turf/chosen_location = get_safe_random_station_turfs()
-
- // We don't want them close to each other - at least 1 tile of seperation
- var/list/nearby_things = range(1, chosen_location)
- var/obj/effect/heretic_influence/what_if_i_have_one = locate() in nearby_things
- var/obj/effect/visible_heretic_influence/what_if_i_had_one_but_its_used = locate() in nearby_things
- if(what_if_i_have_one || what_if_i_had_one_but_its_used)
+ var/list/primary_turfs = list()
+ var/list/banned_turfs = list()
+ // Ensure at least 1 tile of seperation between other influences.
+ for(var/obj/smash as anything in smashes + visible_smashes)
+ var/turf/smash_loc = get_turf(smash)
+ for(var/near_smash in RANGE_TURFS(1, smash_loc))
+ banned_turfs[near_smash] = TRUE
+ for(var/area/area_to_check as anything in GLOB.areas)
+ if(!is_type_in_typecache(area_to_check, public_station_areas))
+ continue
+ for(var/turf/open/floor/floor in area_to_check.get_contained_turfs())
+ if(banned_turfs[floor])
+ continue
+ primary_turfs |= floor
+ if(!length(primary_turfs))
+ CRASH("Could not find any valid turfs to spawn heretic influences on!")
+ var/spawned = 0
+ while(length(primary_turfs) && spawned < amount && location_sanity < 100)
+ var/turf/chosen_location = pick_n_take(primary_turfs)
+ var/list/eligible_locations = list()
+ var/list/tested_turfs = list()
+ view_loop:
+ for(var/turf/open/floor/possibility in view(HERETIC_INFLUENCE_SPAWN_FUZZ, chosen_location))
+ // Ensure we don't waste any time re-checking the same turf (or a banned turf)
+ if(tested_turfs[possibility] || banned_turfs[possibility])
+ continue
+ tested_turfs[possibility] = TRUE
+ var/area/area = get_area(possibility)
+ // Ensure the turf isn't in an outright forbidden area
+ if(is_type_in_typecache(area, forbidden_influence_areas))
+ continue
+ // Ensure all adjacent turfs are open (i.e no tiny rooms)
+ for(var/turf/nearby_turf as anything in RANGE_TURFS(1, possibility))
+ if(!isfloorturf(nearby_turf))
+ continue view_loop
+ // Ensure there's no dense objects on this turf
+ for(var/obj/thingymajig as anything in possibility) // Minor optimization: don't filter the list for objs beforehand, as we're going to break on the first dense obj anyways.
+ if(istype(thingymajig) && (thingymajig.density || initial(thingymajig.density)))
+ continue view_loop
+ // Ensure the turf is safe
+ if(!is_turf_safe(possibility))
+ continue
+ // Lower weight for certain areas, and add it to our list of eligible locations
+ eligible_locations[possibility] = is_type_in_typecache(area, low_weight_areas) ? 1 : 5
+ if(!length(eligible_locations))
location_sanity++
continue
-
- new /obj/effect/heretic_influence(chosen_location)
+ var/turf/chosen_turf = pick_weight(eligible_locations)
+ if(QDELETED(chosen_turf))
+ location_sanity++
+ continue
+ for(var/near_chosen in RANGE_TURFS(1, chosen_turf))
+ banned_turfs[near_chosen] = TRUE
+ primary_turfs -= near_chosen
+ new /obj/effect/heretic_influence(chosen_turf)
+ spawned++
rework_network()
@@ -95,6 +257,9 @@
* Use this whenever you want to add someone to the list
*/
/datum/reality_smash_tracker/proc/add_tracked_mind(datum/mind/heretic)
+ // First heretic? Set up our midround timer!
+ if(!length(tracked_heretics))
+ start_midround_influence_timer()
tracked_heretics |= heretic
// If our heretic's on station, generate some new influences
@@ -103,7 +268,6 @@
add_to_smashes(heretic)
-
/**
* Removes a mind from the list of people that can see the reality smashes
*
@@ -123,14 +287,20 @@
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
alpha = 0
-/obj/effect/visible_heretic_influence/Initialize(mapload)
+/obj/effect/visible_heretic_influence/Initialize()
. = ..()
+ GLOB.reality_smash_track.visible_smashes += src
addtimer(CALLBACK(src, PROC_REF(show_presence)), 15 SECONDS)
var/image/silicon_image = image('icons/effects/heretic.dmi', src, null, OBJ_LAYER)
silicon_image.override = TRUE
add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/silicons, "pierced_reality", silicon_image)
- addtimer(CALLBACK(src,PROC_REF(dissipate)), 60 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(dissipate)), 1 MINUTES)
+
+/obj/effect/visible_heretic_influence/Destroy()
+ GLOB.reality_smash_track.visible_smashes -= src
+ return ..()
+
/*
* Makes the influence fade in after 15 seconds.
*/
@@ -138,22 +308,19 @@
animate(src, alpha = 255, time = 15 SECONDS)
/obj/effect/visible_heretic_influence/proc/dissipate()
- animate(src,alpha = 0,time = 15 SECONDS)
+ animate(src,alpha = 0, time = 15 SECONDS)
QDEL_IN(src, 15 SECONDS)
/obj/effect/visible_heretic_influence/attack_hand(mob/living/user, list/modifiers)
. = ..()
- if(.)
- return
- if(!ishuman(user))
+ if(. || !ishuman(user))
return
-
- if(IS_HERETIC(user))
+ if(IS_HERETIC_OR_MONSTER(user))
to_chat(user, "You know better than to tempt forces out of your control!")
return TRUE
-
var/mob/living/carbon/human/human_user = user
var/obj/item/bodypart/their_poor_arm = human_user.get_active_hand()
+ // om nom nom
if(prob(25))
to_chat(human_user, "An otherwordly presence tears and atomizes your [their_poor_arm.name] as you try to touch the hole in the very fabric of reality!")
their_poor_arm.dismember()
@@ -165,38 +332,32 @@
/obj/effect/visible_heretic_influence/attack_tk(mob/user)
if(!ishuman(user))
return
-
. = COMPONENT_CANCEL_ATTACK_CHAIN
-
- if(IS_HERETIC(user))
+ if(IS_HERETIC_OR_MONSTER(user))
to_chat(user, "You know better than to tempt forces out of your control!")
return
-
var/mob/living/carbon/human/human_user = user
-
// A very elaborate way to suicide
to_chat(human_user, "Eldritch energy lashes out, piercing your fragile mind, tearing it to pieces!")
human_user.ghostize()
+ // Your head asplode!
var/obj/item/bodypart/head/head = locate() in human_user.get_bodypart(BODY_ZONE_HEAD)
if(head)
head.dismember()
qdel(head)
else
human_user.gib()
-
var/datum/effect_system/reagents_explosion/explosion = new()
explosion.set_up(1, get_turf(human_user), TRUE, 0)
explosion.start(src)
-/obj/effect/visible_heretic_influence/examine(mob/user)
+/obj/effect/visible_heretic_influence/examine(mob/living/carbon/human/user)
. = ..()
- if(IS_HERETIC(user) || !ishuman(user))
+ if(!istype(user) || IS_HERETIC_OR_MONSTER(user))
return
-
- var/mob/living/carbon/human/human_user = user
- to_chat(human_user, "Your mind burns as you stare at the tear!")
- human_user.adjustOrganLoss(ORGAN_SLOT_BRAIN, 10, 190)
- SEND_SIGNAL(human_user, COMSIG_ADD_MOOD_EVENT, "gates_of_mansus", /datum/mood_event/gates_of_mansus)
+ to_chat(user, "Your mind burns as you stare at the tear!")
+ user.adjustOrganLoss(ORGAN_SLOT_BRAIN, 10, 190)
+ SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "gates_of_mansus", /datum/mood_event/gates_of_mansus)
/obj/effect/heretic_influence
name = "reality smash"
@@ -224,26 +385,21 @@
GLOB.reality_smash_track.smashes -= src
for(var/datum/mind/heretic in minds)
remove_mind(heretic)
-
heretic_image = null
return ..()
/obj/effect/heretic_influence/attack_hand(mob/user, list/modifiers)
if(!IS_HERETIC(user)) // Shouldn't be able to do this, but just in case
return
-
if(being_drained)
balloon_alert(user, "Already being drained")
else
INVOKE_ASYNC(src, PROC_REF(drain_influence), user, 1)
- return
-
/obj/effect/heretic_influence/attackby(obj/item/weapon, mob/user, params)
. = ..()
if(.)
return
-
// Using a codex will give you two knowledge points for draining.
if(!being_drained && istype(weapon, /obj/item/codex_cicatrix))
var/obj/item/codex_cicatrix/codex = weapon
@@ -259,24 +415,20 @@
* If successful, the influence is drained and deleted.
*/
/obj/effect/heretic_influence/proc/drain_influence(mob/living/user, knowledge_to_gain)
-
being_drained = TRUE
balloon_alert(user, "You begin draining the influence")
RegisterSignal(user, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine))
-
if(!do_after(user, 10 SECONDS, target = src))
being_drained = FALSE
balloon_alert(user, "Interrupted")
UnregisterSignal(user, COMSIG_PARENT_EXAMINE)
return
-
// We don't need to set being_drained back since we delete after anyways
UnregisterSignal(user, COMSIG_PARENT_EXAMINE)
balloon_alert(user, "Influence drained")
-
+ // Actually grant the heretic their well-earned knowledge
var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
- heretic_datum.knowledge_points += knowledge_to_gain
-
+ heretic_datum.adjust_knowledge_points(knowledge_to_gain)
// Aaand now we delete it
after_drain(user)
@@ -287,10 +439,8 @@
if(user)
to_chat(user, "[pick(strings(HERETIC_INFLUENCE_FILE, "drain_message"))]")
to_chat(user, "[src] begins to fade into reality!")
-
var/obj/effect/visible_heretic_influence/illusion = new /obj/effect/visible_heretic_influence(drop_location())
illusion.name = "\improper" + pick(strings(HERETIC_INFLUENCE_FILE, "drained")) + " " + format_text(name)
-
GLOB.reality_smash_track.num_drained++
qdel(src)
@@ -299,13 +449,11 @@
*
* Gives a chance for examiners to see that the heretic is interacting with an infuence.
*/
-/obj/effect/heretic_influence/proc/on_examine(atom/source, mob/user, list/examine_list)
+/obj/effect/heretic_influence/proc/on_examine(mob/living/source, mob/user, list/examine_list)
SIGNAL_HANDLER
-
- if(prob(50))
+ if(!IS_HERETIC_OR_MONSTER(user) && prob(50))
return
-
- examine_list += "[source]'s hand seems to be glowing a ["strange purple"]..."
+ examine_list += "[source.p_their(TRUE)] hand seems to be glowing a ominous, cosmic purple..."
/*
* Add a mind to the list of tracked minds,
@@ -322,7 +470,6 @@
/obj/effect/heretic_influence/proc/remove_mind(datum/mind/heretic)
if(!(heretic in minds))
CRASH("[type] - remove_mind called with a mind not present in the minds list!")
-
minds -= heretic
heretic.current?.client?.images -= heretic_image
@@ -332,4 +479,7 @@
/obj/effect/heretic_influence/proc/generate_name()
name = "\improper" + pick(strings(HERETIC_INFLUENCE_FILE, "prefix")) + " " + pick(strings(HERETIC_INFLUENCE_FILE, "postfix"))
+#undef HERETIC_INFLUENCE_SPAWN_FUZZ
+#undef MIDROUND_INFLUENCE_MAX_TIME
+#undef MIDROUND_INFLUENCE_MIN_TIME
#undef NUM_INFLUENCES_PER_HERETIC
diff --git a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
index 2a3c0d73eeb41..e7e9167832abe 100644
--- a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
@@ -62,6 +62,7 @@
var/datum/objective/heretic_summon/summon_objective = new()
summon_objective.owner = our_heretic.owner
our_heretic.objectives += summon_objective
+ our_heretic.update_static_data_for_all_viewers()
to_chat(user, "Undertaking the Path of Flesh, you are given another objective.")
our_heretic.owner.announce_objectives()
@@ -119,6 +120,9 @@
var/datum/mind/human_target_mind = human_target.mind
INVOKE_ASYNC(human_target_mind, TYPE_PROC_REF(/datum/mind, add_antag_datum), /datum/antagonist/heretic_monster)
+ var/datum/antagonist/heretic/our_heretic = IS_HERETIC(source)
+ LAZYOR(our_heretic.monsters_summoned, WEAKREF(human_target_mind))
+
/datum/heretic_knowledge/limited_amount/flesh_grasp/proc/remove_ghoul(mob/living/carbon/human/source)
SIGNAL_HANDLER
@@ -196,6 +200,9 @@
var/datum/antagonist/heretic_monster/heretic_monster = soon_to_be_ghoul.mind.add_antag_datum(/datum/antagonist/heretic_monster)
heretic_monster.set_owner(user.mind)
+ var/datum/antagonist/heretic/our_heretic = IS_HERETIC(user)
+ LAZYOR(our_heretic.monsters_summoned, WEAKREF(soon_to_be_ghoul.mind))
+
selected_atoms -= soon_to_be_ghoul
LAZYADD(created_items, WEAKREF(soon_to_be_ghoul))
diff --git a/code/modules/antagonists/heretic/knowledge/rust_lore.dm b/code/modules/antagonists/heretic/knowledge/rust_lore.dm
index 0b4d1a6452156..b497789255aa8 100644
--- a/code/modules/antagonists/heretic/knowledge/rust_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/rust_lore.dm
@@ -54,6 +54,15 @@
var/datum/antagonist/heretic/our_heretic = IS_HERETIC(user)
our_heretic.heretic_path = route
+/datum/heretic_knowledge/limited_amount/base_rust/rune_draw_time_multiplier(mob/living/user, turf/target_turf)
+ . = ..()
+ var/rust = 0
+ for(var/turf/open/floor in RANGE_TURFS(1, target_turf))
+ if(HAS_TRAIT(floor, TRAIT_RUSTY))
+ rust++
+ if(rust >= 4)
+ . = min(., 0.65)
+
/datum/heretic_knowledge/rust_fist
name = "Grasp of Rust"
desc = "Your Mansus Grasp will deal 500 damage to non-living matter and rust any surface it touches. \
diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
index 9d696dd4f9f4b..cb95207dc4521 100644
--- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
+++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
@@ -22,14 +22,11 @@
var/skip_this_ritual = FALSE
/// A weakref to the mind of our heretic.
var/datum/mind/heretic_mind
- /// Lazylist of minds that we won't pick as targets.
- var/list/datum/mind/target_blacklist
/// An assoc list of [ref] to [timers] - a list of all the timers of people in the shadow realm currently
var/return_timers
/datum/heretic_knowledge/hunt_and_sacrifice/Destroy(force, ...)
heretic_mind = null
- LAZYCLEARLIST(target_blacklist)
return ..()
/datum/heretic_knowledge/hunt_and_sacrifice/on_research(mob/user, regained = FALSE)
@@ -67,7 +64,7 @@
// Let's remove any humans in our atoms list that aren't a sac target
for(var/mob/living/carbon/human/sacrifice in atoms)
// If the mob's not in soft crit or worse, or isn't one of the sacrifices, remove it from the list
- if(sacrifice.stat < SOFT_CRIT || !(WEAKREF(sacrifice) in heretic_datum.sac_targets))
+ if(sacrifice.stat < SOFT_CRIT || !heretic_datum.can_sacrifice(sacrifice))
atoms -= sacrifice
// Finally, return TRUE if we have a target in the list
@@ -97,24 +94,10 @@
* Returns FALSE if no targets are found, TRUE if the targets list was populated.
*/
/datum/heretic_knowledge/hunt_and_sacrifice/proc/obtain_targets(mob/living/user, silent = FALSE)
+ var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
// First construct a list of minds that are valid objective targets.
- var/list/datum/mind/valid_targets = list()
- for(var/mob/player in SSticker.mode.current_players[CURRENT_LIVING_PLAYERS])
- if(player.mind)
- var/datum/mind/possible_target = player.mind
- if(possible_target == user.mind)
- continue
- if(possible_target in target_blacklist)
- continue
- if(!ishuman(player))
- continue
- if(player.stat == DEAD)
- continue
- valid_targets += possible_target
- else
- continue
-
+ var/list/datum/mind/valid_targets = heretic_datum.possible_sacrifice_targets()
if(!length(valid_targets))
if(!silent)
to_chat(user,"No sacrifice targets could be found!")
@@ -129,14 +112,14 @@
// First target, any command.
for(var/datum/mind/head_mind as anything in shuffle_inplace(valid_targets))
- if(head_mind.assigned_role in list("Captain", "Head of Personnel", "Chief Engineer", "Head of Security", "Research Director", "Chief Medical Officer"))
+ if(head_mind.assigned_role in GLOB.command_positions)
final_targets += head_mind
valid_targets -= head_mind
break
// Second target, any security
for(var/datum/mind/sec_mind as anything in shuffle_inplace(valid_targets))
- if(sec_mind.assigned_role in list("Security Officer", "Warden", "Detective", "Head of Security", "Brig Physician", "Deputy"))
+ if(sec_mind.assigned_role in GLOB.security_positions)
final_targets += sec_mind
valid_targets -= sec_mind
break
@@ -154,12 +137,10 @@
// If any of our targets failed to aquire,
// Let's run a loop until we get four total, grabbing random targets.
var/target_sanity = 0
- while(length(final_targets) < 4 && length(valid_targets) > 4 && target_sanity < 25)
+ while(length(final_targets) < HERETIC_MAX_SAC_TARGETS && length(valid_targets) > HERETIC_MAX_SAC_TARGETS && target_sanity < 25)
final_targets += pick_n_take(valid_targets)
target_sanity++
- var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
-
if(!silent)
to_chat(user, "Your targets have been determined. Your Living Heart will allow you to track their position. Go forth, and sacrifice them!")
@@ -184,21 +165,21 @@
var/mob/living/carbon/human/sacrifice = locate() in selected_atoms
if(!sacrifice)
CRASH("[type] sacrifice_process didn't have a human in the atoms list. How'd it make it so far?")
- if(!(WEAKREF(sacrifice) in heretic_datum.sac_targets))
+ var/datum/mind/sacrifice_mind = get_mind(sacrifice, TRUE)
+ if(!heretic_datum.can_sacrifice(sacrifice_mind))
CRASH("[type] sacrifice_process managed to get a non-target human. This is incorrect.")
- if(sacrifice.mind)
- LAZYADD(target_blacklist, sacrifice.mind)
- LAZYREMOVE(heretic_datum.sac_targets, WEAKREF(sacrifice))
+ LAZYADD(heretic_datum.target_blacklist, sacrifice_mind)
+ heretic_datum.remove_sacrifice_target(sacrifice_mind)
to_chat(user, "Your patron accepts your offer.")
- if(sacrifice.mind?.assigned_role in list("Captain", "Head of Personnel", "Chief Engineer", "Head of Security", "Research Director", "Chief Medical Officer"))
- heretic_datum.knowledge_points++
+ if(sacrifice_mind.assigned_role in GLOB.command_positions)
+ heretic_datum.adjust_knowledge_points(1)
heretic_datum.high_value_sacrifices++
heretic_datum.total_sacrifices++
- heretic_datum.knowledge_points += 2
+ heretic_datum.adjust_knowledge_points(2)
if(!begin_sacrifice(sacrifice))
disembowel_target(sacrifice)
@@ -243,6 +224,8 @@
addtimer(CALLBACK(sac_target, TYPE_PROC_REF(/mob/living/carbon, do_jitter_animation), 100), SACRIFICE_SLEEP_DURATION * (1/3))
addtimer(CALLBACK(sac_target, TYPE_PROC_REF(/mob/living/carbon, do_jitter_animation), 100), SACRIFICE_SLEEP_DURATION * (2/3))
+ // Grab their ghost, just in case they're dead or something.
+ sac_target.grab_ghost()
// If our target is dead, try to revive them
// and if we fail to revive them, don't proceede the chain
if(!sac_target.heal_and_revive(50, "[sac_target]'s heart begins to beat with an unholy force as they return from death!"))
@@ -274,6 +257,8 @@
if(QDELETED(sac_target))
return
+ // Grab ghost again, just to be safe.
+ sac_target.grab_ghost()
// The target disconnected or something, we shouldn't bother sending them along.
if(!sac_target.client || !sac_target.mind)
disembowel_target(sac_target)
diff --git a/code/modules/antagonists/heretic/knowledge/void_lore.dm b/code/modules/antagonists/heretic/knowledge/void_lore.dm
index 1a77d2a8409a1..5f53a18aeb823 100644
--- a/code/modules/antagonists/heretic/knowledge/void_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/void_lore.dm
@@ -52,16 +52,19 @@
var/datum/antagonist/heretic/our_heretic = IS_HERETIC(user)
our_heretic.heretic_path = route
+/datum/heretic_knowledge/limited_amount/base_void/rune_draw_time_multiplier(mob/living/user, turf/open/target_turf)
+ . = ..()
+ if(istype(target_turf) && target_turf.GetTemperature() <= T0C)
+ . = min(., 0.65)
+
/datum/heretic_knowledge/limited_amount/base_void/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc)
if(!isopenturf(loc))
loc.balloon_alert(user, "ritual failed, invalid location!")
return FALSE
-
var/turf/open/our_turf = loc
if(our_turf.GetTemperature() > T0C)
loc.balloon_alert(user, "ritual failed, not cold enough!")
return FALSE
-
return ..()
/datum/heretic_knowledge/void_grasp
diff --git a/tgui/packages/tgui/interfaces/AntagInfoHeretic.tsx b/tgui/packages/tgui/interfaces/AntagInfoHeretic.tsx
index 06987f3b8e436..84a15094c5d2e 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoHeretic.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoHeretic.tsx
@@ -27,6 +27,11 @@ const hereticYellow = {
color: 'yellow',
};
+enum CurrentTab {
+ Info,
+ Research,
+}
+
type Knowledge = {
path: string;
name: string;
@@ -39,8 +44,8 @@ type Knowledge = {
};
type KnowledgeInfo = {
- learnableKnowledge: Knowledge[];
- learnedKnowledge: Knowledge[];
+ available: Knowledge[];
+ researched: Knowledge[];
};
type Objective = {
@@ -49,11 +54,17 @@ type Objective = {
explanation: string;
};
+type SacrificeInfo = {
+ total: number;
+ command: number;
+};
+
type Info = {
- charges: number;
- total_sacrifices: number;
+ points: number;
+ sacrifices: SacrificeInfo;
ascended: BooleanLike;
objectives: Objective[];
+ knowledge: KnowledgeInfo;
};
const IntroductionSection = () => {
@@ -153,8 +164,13 @@ const GuideSection = () => {
};
const InformationSection = (props, context) => {
- const { data } = useBackend(context);
- const { charges, total_sacrifices, ascended } = data;
+ const {
+ data: {
+ points,
+ ascended,
+ sacrifices: { total },
+ },
+ } = useBackend(context);
return (
@@ -172,12 +188,12 @@ const InformationSection = (props, context) => {
)}
- You have {charges || 0}
- knowledge point{charges !== 1 ? 's' : ''}.
+ You have {points || 0}
+ knowledge point{points !== 1 ? 's' : ''}.
You have made a total of
- {total_sacrifices || 0}
+ {total || 0}
sacrifices.
@@ -186,8 +202,9 @@ const InformationSection = (props, context) => {
};
const ObjectivePrintout = (props, context) => {
- const { data } = useBackend(context);
- const { objectives } = data;
+ const {
+ data: { objectives },
+ } = useBackend(context);
return (
@@ -206,15 +223,18 @@ const ObjectivePrintout = (props, context) => {
};
const ResearchedKnowledge = (props, context) => {
- const { data } = useBackend(context);
- const { learnedKnowledge } = data;
+ const {
+ data: {
+ knowledge: { researched },
+ },
+ } = useBackend(context);
return (
- {(!learnedKnowledge.length && 'None!') ||
- learnedKnowledge.map((learned) => (
+ {(!researched.length && 'None!') ||
+ researched.map((learned) => (