"
+ parts += "
You managed to survive the events on [station_name()] as [M.real_name]."
+
+ else
+ parts += "
"
+ parts += "
You did not survive the events on [station_name()]..."
+ else
+ parts += "
"
+ parts += "
"
+ if(GLOB.survivor_report)
+ parts += GLOB.survivor_report
+ else
+ parts += survivor_report()
+
+ parts += "
"
+
+ return parts.Join()
+
+/datum/controller/subsystem/ticker/proc/display_report()
+ GLOB.common_report = build_roundend_report()
+ for(var/client/C in GLOB.clients)
+ show_roundend_report(C,GLOB.common_report)
+ give_show_report_button(C)
+ CHECK_TICK
+
+/datum/controller/subsystem/ticker/proc/law_report()
+ var/list/parts = list()
+ //Silicon laws report
+ for (var/i in GLOB.ai_list)
+ var/mob/living/silicon/ai/aiPlayer = i
+ if(aiPlayer.mind)
+ parts += "
[aiPlayer.name] (Played by: [aiPlayer.mind.key])'s laws [aiPlayer.stat != DEAD ? "at the end of the round" : "when it was deactivated"] were:"
+ parts += aiPlayer.laws.get_law_list(include_zeroth=TRUE)
+
+ parts += "
Total law changes: [aiPlayer.law_change_counter]"
+
+ if (aiPlayer.connected_robots.len)
+ var/robolist = "
[aiPlayer.real_name]'s minions were: "
+ for(var/mob/living/silicon/robot/robo in aiPlayer.connected_robots)
+ if(robo.mind)
+ robolist += "[robo.name][robo.stat?" (Deactivated) (Played by: [robo.mind.key]), ":" (Played by: [robo.mind.key]), "]"
+ parts += "[robolist]"
+
+ for (var/mob/living/silicon/robot/robo in GLOB.silicon_mobs)
+ if (!robo.connected_ai && robo.mind)
+ if (robo.stat != DEAD)
+ parts += "
[robo.name] (Played by: [robo.mind.key]) survived as an AI-less borg! Its laws were:"
+ else
+ parts += "
[robo.name] (Played by: [robo.mind.key]) was unable to survive the rigors of being a cyborg without an AI. Its laws were:"
+
+ if(robo) //How the hell do we lose robo between here and the world messages directly above this?
+ parts += robo.laws.get_law_list(include_zeroth=TRUE)
+ if(parts.len)
+ return "
[parts.Join("
")]
"
+ else
+ return ""
+
+/datum/controller/subsystem/ticker/proc/goal_report()
+ var/list/parts = list()
+ if(mode.station_goals.len)
+ for(var/V in mode.station_goals)
+ var/datum/station_goal/G = V
+ parts += G.get_result()
+ return "
"
+
+/datum/controller/subsystem/ticker/proc/medal_report()
+ if(GLOB.commendations.len)
+ var/list/parts = list()
+ parts += ""
+ for (var/com in GLOB.commendations)
+ parts += com
+ return "
[parts.Join("
")]
"
+ return ""
+
+/datum/controller/subsystem/ticker/proc/antag_report()
+ var/list/result = list()
+ var/list/all_teams = list()
+ var/list/all_antagonists = list()
+
+ for(var/datum/antagonist/A in GLOB.antagonists)
+ all_teams |= A.get_team()
+ all_antagonists += A
+
+ for(var/datum/objective_team/T in all_teams)
+ result += T.roundend_report()
+ for(var/datum/antagonist/X in all_antagonists)
+ if(X.get_team() == T)
+ all_antagonists -= X
+ result += " "//newline between teams
+
+ var/currrent_category
+ var/datum/antagonist/previous_category
+
+ sortTim(all_antagonists, /proc/cmp_antag_category)
+
+ for(var/datum/antagonist/A in all_antagonists)
+ if(!A.show_in_roundend)
+ continue
+ if(A.roundend_category != currrent_category)
+ if(previous_category)
+ result += previous_category.roundend_report_footer()
+ result += "
"
+ result += "
"
+ result += A.roundend_report_header()
+ currrent_category = A.roundend_category
+ previous_category = A
+ result += A.roundend_report()
+ result += "
"
+
+ if(all_antagonists.len)
+ var/datum/antagonist/last = all_antagonists[all_antagonists.len]
+ result += last.roundend_report_footer()
+ result += "
"
+
+ return result.Join()
+
+/proc/cmp_antag_category(datum/antagonist/A,datum/antagonist/B)
+ return sorttext(B.roundend_category,A.roundend_category)
+
+
+/datum/controller/subsystem/ticker/proc/give_show_report_button(client/C)
+ var/datum/action/report/R = new
+ C.player_details.player_actions += R
+ R.Grant(C.mob)
+ to_chat(C,"
Show roundend report again")
+
+/datum/action/report
+ name = "Show roundend report"
+ button_icon_state = "vote"
+
+/datum/action/report/Trigger()
+ if(owner && GLOB.common_report && SSticker.current_state == GAME_STATE_FINISHED)
+ SSticker.show_roundend_report(owner.client,GLOB.common_report)
+
+/datum/action/report/IsAvailable()
+ return 1
+
+/datum/action/report/Topic(href,href_list)
+ if(usr != owner)
+ return
+ if(href_list["report"])
+ Trigger()
+ return
+
+
+/proc/printplayer(datum/mind/ply, fleecheck)
+ var/text = "
[ply.key] was
[ply.name] the
[ply.assigned_role] and"
+ if(ply.current)
+ if(ply.current.stat == DEAD)
+ text += "
died"
+ else
+ text += "
survived"
+ if(fleecheck)
+ var/turf/T = get_turf(ply.current)
+ if(!T || !(T.z in GLOB.station_z_levels))
+ text += " while
fleeing the station"
+ if(ply.current.real_name != ply.name)
+ text += " as
[ply.current.real_name]"
+ else
+ text += "
had their body destroyed"
+ return text
+
+/proc/printplayerlist(list/players,fleecheck)
+ var/list/parts = list()
+
+ parts += "
"
+ for(var/datum/mind/M in players)
+ parts += "- [printplayer(M,fleecheck)]
"
+ parts += "
"
+ return parts.Join()
+
+
+/proc/printobjectives(datum/mind/ply)
+ var/list/objective_parts = list()
+ var/count = 1
+ for(var/datum/objective/objective in ply.objectives)
+ if(objective.check_completion())
+ objective_parts += "
Objective #[count]: [objective.explanation_text]
Success!"
+ else
+ objective_parts += "
Objective #[count]: [objective.explanation_text]
Fail."
+ count++
+ return objective_parts.Join("
")
\ No newline at end of file
diff --git a/code/_globalvars/game_modes.dm b/code/_globalvars/game_modes.dm
index 9c3af923f1..3822f7077d 100644
--- a/code/_globalvars/game_modes.dm
+++ b/code/_globalvars/game_modes.dm
@@ -1,18 +1,12 @@
GLOBAL_VAR_INIT(master_mode, "traitor") //"extended"
GLOBAL_VAR_INIT(secret_force_mode, "secret") // if this is anything but "secret", the secret rotation will forceably choose this mode
+GLOBAL_VAR(common_report) //Contains commmon part of roundend report
+GLOBAL_VAR(survivor_report) //Contains shared surivor report for roundend report (part of personal report)
+
GLOBAL_VAR_INIT(wavesecret, 0) // meteor mode, delays wave progression, terrible name
GLOBAL_DATUM(start_state, /datum/station_state) // Used in round-end report
-// Cult, needs to be global so admin cultists are functional
-GLOBAL_VAR_INIT(blood_target, null) // Cult Master's target or Construct's Master
-GLOBAL_DATUM(blood_target_image, /image)
-GLOBAL_VAR_INIT(blood_target_reset_timer, null)
-GLOBAL_DATUM(sac_mind, /datum/mind)
-GLOBAL_VAR_INIT(sac_image, null)
-GLOBAL_VAR_INIT(cult_vote_called, FALSE)
-GLOBAL_VAR_INIT(cult_mastered, FALSE)
-GLOBAL_VAR_INIT(reckoning_complete, FALSE)
-GLOBAL_VAR_INIT(sac_complete, FALSE)
-GLOBAL_DATUM(cult_narsie, /obj/singularity/narsie/large/cult)
-GLOBAL_LIST_EMPTY(summon_spots)
\ No newline at end of file
+
+//TODO clear this one up too
+GLOBAL_DATUM(cult_narsie, /obj/singularity/narsie/large/cult)
\ No newline at end of file
diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm
index bb86b4cbb0..8b8b817586 100644
--- a/code/_globalvars/misc.dm
+++ b/code/_globalvars/misc.dm
@@ -16,3 +16,5 @@ GLOBAL_VAR_INIT(CHARGELEVEL, 0.001) // Cap for how fast cells charge, as a perce
GLOBAL_LIST_EMPTY(powernets)
GLOBAL_VAR_INIT(bsa_unlock, FALSE) //BSA unlocked by head ID swipes
+
+GLOBAL_LIST_EMPTY(player_details) // ckey -> /datum/player_details
\ No newline at end of file
diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm
index 0006d72d3b..2ad2eb76f5 100644
--- a/code/_onclick/hud/alert.dm
+++ b/code/_onclick/hud/alert.dm
@@ -302,32 +302,41 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
/obj/screen/alert/bloodsense/process()
var/atom/blood_target
- if(GLOB.blood_target)
- if(!get_turf(GLOB.blood_target))
- GLOB.blood_target = null
+
+ var/datum/antagonist/cult/antag = mob_viewer.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
+ if(!antag)
+ return
+ var/datum/objective/sacrifice/sac_objective = locate() in antag.cult_team.objectives
+
+ if(antag.cult_team.blood_target)
+ if(!get_turf(antag.cult_team.blood_target))
+ antag.cult_team.blood_target = null
else
- blood_target = GLOB.blood_target
+ blood_target = antag.cult_team.blood_target
if(Cviewer && Cviewer.seeking && Cviewer.master)
blood_target = Cviewer.master
desc = "Your blood sense is leading you to [Cviewer.master]"
if(!blood_target)
- if(!GLOB.sac_complete)
+ if(sac_objective && !sac_objective.check_completion())
if(icon_state == "runed_sense0")
return
animate(src, transform = null, time = 1, loop = 0)
angle = 0
cut_overlays()
icon_state = "runed_sense0"
- desc = "Nar-Sie demands that [GLOB.sac_mind] be sacrificed before the summoning ritual can begin."
- add_overlay(GLOB.sac_image)
+ desc = "Nar-Sie demands that [sac_objective.target] be sacrificed before the summoning ritual can begin."
+ add_overlay(sac_objective.sac_image)
else
+ var/datum/objective/eldergod/summon_objective = locate() in antag.cult_team.objectives
+ if(!summon_objective)
+ return
if(icon_state == "runed_sense1")
return
animate(src, transform = null, time = 1, loop = 0)
angle = 0
cut_overlays()
icon_state = "runed_sense1"
- desc = "The sacrifice is complete, summon Nar-Sie! The summoning can only take place in [english_list(GLOB.summon_spots)]!"
+ desc = "The sacrifice is complete, summon Nar-Sie! The summoning can only take place in [english_list(summon_objective.summon_spots)]!"
add_overlay(narnar)
return
var/turf/P = get_turf(blood_target)
@@ -388,11 +397,13 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
desc = "
CHETR
NYY
HAGEHUGF-NAQ-UBABE
RATVAR."
else
var/servants = 0
- var/list/textlist
+ var/list/textlist = list()
for(var/mob/living/L in GLOB.alive_mob_list)
if(is_servant_of_ratvar(L))
servants++
- textlist = list("[SSticker.mode.eminence ? "There is an Eminence." : "
There is no Eminence! Get one ASAP!"]
")
+ var/datum/antagonist/clockcult/C = mob_viewer.mind.has_antag_datum(/datum/antagonist/clockcult,TRUE)
+ if(C && C.clock_team)
+ textlist += "[C.clock_team.eminence ? "There is an Eminence." : "
There is no Eminence! Get one ASAP!"]
"
textlist += "There are currently
[servants] servant[servants > 1 ? "s" : ""] of Ratvar.
"
for(var/i in SSticker.scripture_states)
if(i != SCRIPTURE_DRIVER) //ignore the always-unlocked stuff
diff --git a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm
index 941fcbbcd6..e638de668e 100644
--- a/code/controllers/subsystem/blackbox.dm
+++ b/code/controllers/subsystem/blackbox.dm
@@ -10,7 +10,8 @@ SUBSYSTEM_DEF(blackbox)
var/sealed = FALSE //time to stop tracking stats?
var/list/research_levels = list() //list of highest tech levels attained that isn't lost lost by destruction of RD computers
var/list/versions = list("time_dilation_current" = 2,
- "science_techweb_unlock" = 2) //associative list of any feedback variables that have had their format changed since creation and their current version, remember to update this
+ "science_techweb_unlock" = 2,
+ "antagonists" = 3) //associative list of any feedback variables that have had their format changed since creation and their current version, remember to update this
/datum/controller/subsystem/blackbox/Initialize()
@@ -225,7 +226,10 @@ Versioning
var/pos = length(FV.json["data"]) + 1
FV.json["data"]["[pos]"] = list() //in 512 "pos" can be replaced with "[FV.json["data"].len+1]"
for(var/i in data)
- FV.json["data"]["[pos]"]["[i]"] = "[data[i]]" //and here with "[FV.json["data"].len]"
+ if(islist(data[i]))
+ FV.json["data"]["[pos]"]["[i]"] = data[i] //and here with "[FV.json["data"].len]"
+ else
+ FV.json["data"]["[pos]"]["[i]"] = "[data[i]]"
else
CRASH("Invalid feedback key_type: [key_type]")
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 881bb7fcb7..f178a1bd24 100755
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -398,6 +398,7 @@ SUBSYSTEM_DEF(ticker)
var/mob/living/L = I
L.notransform = FALSE
+<<<<<<< HEAD
/datum/controller/subsystem/ticker/proc/declare_completion()
set waitfor = FALSE
var/station_evacuated = EMERGENCY_ESCAPED_OR_ENDGAMED
@@ -596,6 +597,8 @@ SUBSYSTEM_DEF(ticker)
else
CRASH("Attempted standard reboot without ticker roundend completion")
+=======
+>>>>>>> 3d81385... Roundend report refactor (#33246)
/datum/controller/subsystem/ticker/proc/send_tip_of_the_round()
var/m
if(selected_tip)
diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm
index 55624a866c..0ce136f83b 100644
--- a/code/controllers/subsystem/vote.dm
+++ b/code/controllers/subsystem/vote.dm
@@ -206,6 +206,7 @@ SUBSYSTEM_DEF(vote)
var/datum/action/vote/V = new
if(question)
V.name = "Vote: [question]"
+ C.player_details.player_actions += V
V.Grant(C.mob)
generated_actions += V
return 1
@@ -299,6 +300,7 @@ SUBSYSTEM_DEF(vote)
for(var/v in generated_actions)
var/datum/action/vote/V = v
if(!QDELETED(V))
+ V.remove_from_client()
V.Remove(V.owner)
generated_actions = list()
@@ -318,7 +320,16 @@ SUBSYSTEM_DEF(vote)
/datum/action/vote/Trigger()
if(owner)
owner.vote()
+ remove_from_client()
Remove(owner)
/datum/action/vote/IsAvailable()
return 1
+
+/datum/action/vote/proc/remove_from_client()
+ if(owner.client)
+ owner.client.player_details.player_actions -= src
+ else if(owner.ckey)
+ var/datum/player_details/P = GLOB.player_details[owner.ckey]
+ if(P)
+ P.player_actions -= src
\ No newline at end of file
diff --git a/code/datums/ai_laws.dm b/code/datums/ai_laws.dm
index 8d86507271..c12a08a792 100644
--- a/code/datums/ai_laws.dm
+++ b/code/datums/ai_laws.dm
@@ -373,32 +373,9 @@
ion = list()
/datum/ai_laws/proc/show_laws(who)
-
- if (devillaws && devillaws.len) //Yes, devil laws go in FRONT of zeroth laws, as the devil must still obey it's ban/obligation.
- for(var/i in devillaws)
- to_chat(who, "666. [i]")
-
- if (zeroth)
- to_chat(who, "0. [zeroth]")
-
- for (var/index = 1, index <= ion.len, index++)
- var/law = ion[index]
- var/num = ionnum()
- to_chat(who, "[num]. [law]")
-
- var/number = 1
- for (var/index = 1, index <= inherent.len, index++)
- var/law = inherent[index]
-
- if (length(law) > 0)
- to_chat(who, "[number]. [law]")
- number++
-
- for (var/index = 1, index <= supplied.len, index++)
- var/law = supplied[index]
- if (length(law) > 0)
- to_chat(who, "[number]. [law]")
- number++
+ var/list/printable_laws = get_law_list(include_zeroth = TRUE)
+ for(var/law in printable_laws)
+ to_chat(who,law)
/datum/ai_laws/proc/clear_zeroth_law(force) //only removes zeroth from antag ai if force is 1
if(force)
diff --git a/code/datums/antagonists/abductor.dm b/code/datums/antagonists/abductor.dm
index 8d13c48e7c..3b8935d9fa 100644
--- a/code/datums/antagonists/abductor.dm
+++ b/code/datums/antagonists/abductor.dm
@@ -1,5 +1,6 @@
/datum/antagonist/abductor
name = "Abductor"
+ roundend_category = "abductors"
job_rank = ROLE_ABDUCTOR
var/datum/objective_team/abductor_team/team
var/sub_role
@@ -70,3 +71,65 @@
var/mob/living/carbon/human/H = owner.current
var/datum/species/abductor/A = H.dna.species
A.scientist = TRUE
+
+
+/datum/objective_team/abductor_team
+ member_name = "abductor"
+ var/team_number
+ var/list/datum/mind/abductees = list()
+
+/datum/objective_team/abductor_team/is_solo()
+ return FALSE
+
+/datum/objective_team/abductor_team/proc/add_objective(datum/objective/O)
+ O.team = src
+ O.update_explanation_text()
+ objectives += O
+
+/datum/objective_team/abductor_team/roundend_report()
+ var/list/result = list()
+
+ var/won = TRUE
+ for(var/datum/objective/O in objectives)
+ if(!O.check_completion())
+ won = FALSE
+ if(won)
+ result += "
[name] team fulfilled its mission!"
+ else
+ result += "
[name] team failed its mission."
+
+ result += ""
+ for(var/datum/mind/abductor_mind in members)
+ result += printplayer(abductor_mind)
+ result += printobjectives(abductor_mind)
+
+ return result.Join("
")
+
+
+/datum/antagonist/abductee
+ name = "Abductee"
+ roundend_category = "abductees"
+
+/datum/antagonist/abductee/on_gain()
+ give_objective()
+ . = ..()
+
+/datum/antagonist/abductee/greet()
+ to_chat(owner, "
Your mind snaps!")
+ to_chat(owner, "
You can't remember how you got here...")
+ owner.announce_objectives()
+
+/datum/antagonist/abductee/proc/give_objective()
+ var/mob/living/carbon/human/H = owner.current
+ if(istype(H))
+ H.gain_trauma_type(BRAIN_TRAUMA_MILD)
+ var/objtype = (prob(75) ? /datum/objective/abductee/random : pick(subtypesof(/datum/objective/abductee/) - /datum/objective/abductee/random))
+ var/datum/objective/abductee/O = new objtype()
+ objectives += O
+ owner.objectives += objectives
+
+/datum/antagonist/abductee/apply_innate_effects(mob/living/mob_override)
+ SSticker.mode.update_abductor_icons_added(mob_override ? mob_override.mind : owner)
+
+/datum/antagonist/abductee/remove_innate_effects(mob/living/mob_override)
+ SSticker.mode.update_abductor_icons_removed(mob_override ? mob_override.mind : owner)
\ No newline at end of file
diff --git a/code/datums/antagonists/antag_datum.dm b/code/datums/antagonists/antag_datum.dm
index 0a7b2aa22f..6b5a573eff 100644
--- a/code/datums/antagonists/antag_datum.dm
+++ b/code/datums/antagonists/antag_datum.dm
@@ -2,6 +2,8 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist
var/name = "Antagonist"
+ var/roundend_category = "other antagonists" //Section of roundend report, datums with same category will be displayed together, also default header for the section
+ var/show_in_roundend = TRUE //Set to false to hide the antagonists from roundend report
var/datum/mind/owner //Mind that owns this datum
var/silent = FALSE //Silent will prevent the gain/lose texts to show
var/can_coexist_with_others = TRUE //Whether or not the person will be able to have more than one datum
@@ -9,6 +11,7 @@ GLOBAL_LIST_EMPTY(antagonists)
var/delete_on_mind_deletion = TRUE
var/job_rank
var/replace_banned = TRUE //Should replace jobbaned player with ghosts if granted.
+ var/list/objectives = list()
/datum/antagonist/New(datum/mind/new_owner)
GLOB.antagonists += src
@@ -96,9 +99,62 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/proc/get_team()
return
+//Individual roundend report
+/datum/antagonist/proc/roundend_report()
+ var/list/report = list()
+
+ if(!owner)
+ CRASH("antagonist datum without owner")
+
+ report += printplayer(owner)
+
+ var/objectives_complete = TRUE
+ if(owner.objectives.len)
+ report += printobjectives(owner)
+ for(var/datum/objective/objective in owner.objectives)
+ if(!objective.check_completion())
+ objectives_complete = FALSE
+ break
+
+ if(owner.objectives.len == 0 || objectives_complete)
+ report += "
The [name] was successful!"
+ else
+ report += "
The [name] has failed!"
+
+ return report.Join("
")
+
+//Displayed at the start of roundend_category section, default to roundend_category header
+/datum/antagonist/proc/roundend_report_header()
+ return "
"
+
+//Displayed at the end of roundend_category section
+/datum/antagonist/proc/roundend_report_footer()
+ return
+
//Should probably be on ticker or job ss ?
/proc/get_antagonists(antag_type,specific = FALSE)
. = list()
for(var/datum/antagonist/A in GLOB.antagonists)
if(!specific && istype(A,antag_type) || specific && A.type == antag_type)
- . += A.owner
\ No newline at end of file
+ . += A.owner
+
+
+
+//This datum will autofill the name with special_role
+//Used as placeholder for minor antagonists, please create proper datums for these
+/datum/antagonist/auto_custom
+
+/datum/antagonist/auto_custom/on_gain()
+ ..()
+ name = owner.special_role
+ //Add all objectives not already owned by other datums to this one.
+ var/list/already_registered_objectives = list()
+ for(var/datum/antagonist/A in owner.antag_datums)
+ if(A == src)
+ continue
+ else
+ already_registered_objectives |= A.objectives
+ objectives = owner.objectives - already_registered_objectives
+
+//This one is created by admin tools for custom objectives
+/datum/antagonist/custom
\ No newline at end of file
diff --git a/code/datums/antagonists/brother.dm b/code/datums/antagonists/brother.dm
index 6458e6da09..dd3bdef9d2 100644
--- a/code/datums/antagonists/brother.dm
+++ b/code/datums/antagonists/brother.dm
@@ -55,3 +55,71 @@
/datum/antagonist/brother/proc/finalize_brother()
SSticker.mode.update_brother_icons_added(owner)
+
+
+/datum/objective_team/brother_team
+ name = "brotherhood"
+ member_name = "blood brother"
+ var/meeting_area
+
+/datum/objective_team/brother_team/is_solo()
+ return FALSE
+
+/datum/objective_team/brother_team/proc/update_name()
+ var/list/last_names = list()
+ for(var/datum/mind/M in members)
+ var/list/split_name = splittext(M.name," ")
+ last_names += split_name[split_name.len]
+
+ name = last_names.Join(" & ")
+
+/datum/objective_team/brother_team/roundend_report()
+ var/list/parts = list()
+
+ parts += ""
+ for(var/datum/mind/M in members)
+ parts += printplayer(M)
+ var/win = TRUE
+ var/objective_count = 1
+ for(var/datum/objective/objective in objectives)
+ if(objective.check_completion())
+ parts += "
Objective #[objective_count]: [objective.explanation_text]
Success!"
+ else
+ parts += "
Objective #[objective_count]: [objective.explanation_text]
Fail."
+ win = FALSE
+ objective_count++
+ if(win)
+ parts += "
The blood brothers were successful!"
+ else
+ parts += "
The blood brothers have failed!"
+
+ return "
[parts.Join("
")]
"
+
+/datum/objective_team/brother_team/proc/add_objective(datum/objective/O, needs_target = FALSE)
+ O.team = src
+ if(needs_target)
+ O.find_target()
+ O.update_explanation_text()
+ objectives += O
+
+/datum/objective_team/brother_team/proc/forge_brother_objectives()
+ objectives = list()
+ var/is_hijacker = prob(10)
+ for(var/i = 1 to max(1, CONFIG_GET(number/brother_objectives_amount) + (members.len > 2) - is_hijacker))
+ forge_single_objective()
+ if(is_hijacker)
+ if(!locate(/datum/objective/hijack) in objectives)
+ add_objective(new/datum/objective/hijack)
+ else if(!locate(/datum/objective/escape) in objectives)
+ add_objective(new/datum/objective/escape)
+
+/datum/objective_team/brother_team/proc/forge_single_objective()
+ if(prob(50))
+ if(LAZYLEN(active_ais()) && prob(100/GLOB.joined_player_list.len))
+ add_objective(new/datum/objective/destroy, TRUE)
+ else if(prob(30))
+ add_objective(new/datum/objective/maroon, TRUE)
+ else
+ add_objective(new/datum/objective/assassinate, TRUE)
+ else
+ add_objective(new/datum/objective/steal, TRUE)
\ No newline at end of file
diff --git a/code/datums/antagonists/changeling.dm b/code/datums/antagonists/changeling.dm
index e98bfed782..4f8af2dc8a 100644
--- a/code/datums/antagonists/changeling.dm
+++ b/code/datums/antagonists/changeling.dm
@@ -4,11 +4,11 @@
/datum/antagonist/changeling
name = "Changeling"
+ roundend_category = "changelings"
job_rank = ROLE_CHANGELING
var/you_are_greet = TRUE
var/give_objectives = TRUE
- var/list/objectives = list()
var/team_mode = FALSE //Should assign team objectives ?
//Changeling Stuff
@@ -478,4 +478,35 @@
/datum/antagonist/changeling/xenobio
name = "Xenobio Changeling"
give_objectives = FALSE
+ show_in_roundend = FALSE //These are here for admin tracking purposes only
you_are_greet = FALSE
+
+/datum/antagonist/changeling/roundend_report()
+ var/list/parts = list()
+
+ var/changelingwin = 1
+ if(!owner.current)
+ changelingwin = 0
+
+ parts += printplayer(owner)
+
+ //Removed sanity if(changeling) because we -want- a runtime to inform us that the changelings list is incorrect and needs to be fixed.
+ parts += "
Changeling ID: [changelingID]."
+ parts += "
Genomes Extracted: [absorbedcount]"
+ parts += " "
+ if(objectives.len)
+ var/count = 1
+ for(var/datum/objective/objective in objectives)
+ if(objective.check_completion())
+ parts += "
Objective #[count]: [objective.explanation_text]
Success!"
+ else
+ parts += "
Objective #[count]: [objective.explanation_text]
Fail."
+ changelingwin = 0
+ count++
+
+ if(changelingwin)
+ parts += "
The changeling was successful!"
+ else
+ parts += "
The changeling has failed."
+
+ return parts.Join("
")
\ No newline at end of file
diff --git a/code/datums/antagonists/clockcult.dm b/code/datums/antagonists/clockcult.dm
index 8cc1c9e9a7..5f99ccc5dd 100644
--- a/code/datums/antagonists/clockcult.dm
+++ b/code/datums/antagonists/clockcult.dm
@@ -1,8 +1,11 @@
//CLOCKCULT PROOF OF CONCEPT
/datum/antagonist/clockcult
name = "Clock Cultist"
- var/datum/action/innate/hierophant/hierophant_network = new()
+ roundend_category = "clock cultists"
job_rank = ROLE_SERVANT_OF_RATVAR
+ var/datum/action/innate/hierophant/hierophant_network = new()
+ var/datum/objective_team/clockcult/clock_team
+ var/make_team = TRUE //This should be only false for tutorial scarabs
/datum/antagonist/clockcult/silent
silent = TRUE
@@ -11,6 +14,22 @@
qdel(hierophant_network)
return ..()
+/datum/antagonist/clockcult/get_team()
+ return clock_team
+
+/datum/antagonist/clockcult/create_team(datum/objective_team/clockcult/new_team)
+ if(!new_team && make_team)
+ //TODO blah blah same as the others, allow multiple
+ for(var/datum/antagonist/clockcult/H in GLOB.antagonists)
+ if(H.clock_team)
+ clock_team = H.clock_team
+ return
+ clock_team = new /datum/objective_team/clockcult
+ return
+ if(make_team && !istype(new_team))
+ stack_trace("Wrong team type passed to [type] initialization.")
+ clock_team = new_team
+
/datum/antagonist/clockcult/can_be_owned(datum/mind/new_owner)
. = ..()
if(.)
@@ -164,3 +183,35 @@
if(iscyborg(owner.current))
to_chat(owner.current, "
Despite your freedom from Ratvar's influence, you are still irreparably damaged and no longer possess certain functions such as AI linking.")
. = ..()
+
+
+/datum/objective_team/clockcult
+ name = "Clockcult"
+ var/list/objective
+ var/datum/mind/eminence
+
+/datum/objective_team/clockcult/proc/check_clockwork_victory()
+ if(GLOB.clockwork_gateway_activated)
+ return TRUE
+ return FALSE
+
+/datum/objective_team/clockcult/roundend_report()
+ var/list/parts = list()
+
+ if(check_clockwork_victory())
+ parts += "
Ratvar's servants defended the Ark until its activation!"
+ else
+ parts += "
The Ark was destroyed! Ratvar will rust away for all eternity!"
+ parts += " "
+ parts += "
The servants' objective was: [CLOCKCULT_OBJECTIVE]."
+ parts += "
Construction Value(CV) was:
[GLOB.clockwork_construction_value]"
+ for(var/i in SSticker.scripture_states)
+ if(i != SCRIPTURE_DRIVER)
+ parts += "
[i] scripture was:
[SSticker.scripture_states[i] ? "UN":""]LOCKED"
+ if(eminence)
+ parts += " [printplayer(eminence)]"
+ if(members.len)
+ parts += ""
+ parts += printplayerlist(members - eminence)
+
+ return "
[parts.Join("
")]
"
\ No newline at end of file
diff --git a/code/datums/antagonists/cult.dm b/code/datums/antagonists/cult.dm
index c26b0f8108..bce9123fb3 100644
--- a/code/datums/antagonists/cult.dm
+++ b/code/datums/antagonists/cult.dm
@@ -2,84 +2,103 @@
/datum/antagonist/cult
name = "Cultist"
+ roundend_category = "cultists"
var/datum/action/innate/cult/comm/communion = new
var/datum/action/innate/cult/mastervote/vote = new
job_rank = ROLE_CULTIST
var/ignore_implant = FALSE
+ var/give_equipment = FALSE
+
+ var/datum/objective_team/cult/cult_team
+
+/datum/antagonist/cult/get_team()
+ return cult_team
+
+/datum/antagonist/cult/create_team(datum/objective_team/cult/new_team)
+ if(!new_team)
+ //todo remove this and allow admin buttons to create more than one cult
+ for(var/datum/antagonist/cult/H in GLOB.antagonists)
+ if(H.cult_team)
+ cult_team = H.cult_team
+ return
+ cult_team = new /datum/objective_team/cult
+ cult_team.setup_objectives()
+ return
+ if(!istype(new_team))
+ stack_trace("Wrong team type passed to [type] initialization.")
+ cult_team = new_team
+
+/datum/antagonist/cult/proc/add_objectives()
+ objectives |= cult_team.objectives
+ owner.objectives |= objectives
+
+/datum/antagonist/cult/proc/remove_objectives()
+ owner.objectives -= objectives
/datum/antagonist/cult/Destroy()
QDEL_NULL(communion)
QDEL_NULL(vote)
return ..()
-/datum/antagonist/cult/proc/add_objectives()
- var/list/target_candidates = list()
- for(var/mob/living/carbon/human/player in GLOB.player_list)
- if(player.mind && !player.mind.has_antag_datum(ANTAG_DATUM_CULT) && !is_convertable_to_cult(player) && (player != owner) && player.stat != DEAD)
- target_candidates += player.mind
- if(target_candidates.len == 0)
- message_admins("Cult Sacrifice: Could not find unconvertable target, checking for convertable target.")
- for(var/mob/living/carbon/human/player in GLOB.player_list)
- if(player.mind && !player.mind.has_antag_datum(ANTAG_DATUM_CULT) && (player != owner) && player.stat != DEAD)
- target_candidates += player.mind
- listclearnulls(target_candidates)
- if(LAZYLEN(target_candidates))
- GLOB.sac_mind = pick(target_candidates)
- if(!GLOB.sac_mind)
- message_admins("Cult Sacrifice: ERROR - Null target chosen!")
- else
- var/datum/job/sacjob = SSjob.GetJob(GLOB.sac_mind.assigned_role)
- var/datum/preferences/sacface = GLOB.sac_mind.current.client.prefs
- var/icon/reshape = get_flat_human_icon(null, sacjob, sacface)
- reshape.Shift(SOUTH, 4)
- reshape.Shift(EAST, 1)
- reshape.Crop(7,4,26,31)
- reshape.Crop(-5,-3,26,30)
- GLOB.sac_image = reshape
- else
- message_admins("Cult Sacrifice: Could not find unconvertable or convertable target. WELP!")
- GLOB.sac_complete = TRUE
- SSticker.mode.cult_objectives += "sacrifice"
- if(!GLOB.summon_spots.len)
- while(GLOB.summon_spots.len < SUMMON_POSSIBILITIES)
- var/area/summon = pick(GLOB.sortedAreas - GLOB.summon_spots)
- if(summon && (summon.z in GLOB.station_z_levels) && summon.valid_territory)
- GLOB.summon_spots += summon
- SSticker.mode.cult_objectives += "eldergod"
-
-/datum/antagonist/cult/proc/cult_memorization(datum/mind/cult_mind)
- var/mob/living/current = cult_mind.current
- for(var/obj_count = 1,obj_count <= SSticker.mode.cult_objectives.len,obj_count++)
- var/explanation
- switch(SSticker.mode.cult_objectives[obj_count])
- if("sacrifice")
- if(GLOB.sac_mind)
- explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role] via invoking a Sacrifice rune with them on it and three acolytes around it."
- else
- explanation = "The veil has already been weakened here, proceed to the final objective."
- GLOB.sac_complete = TRUE
- if("eldergod")
- explanation = "Summon Nar-Sie by invoking the rune 'Summon Nar-Sie'.
The summoning can only be accomplished in [english_list(GLOB.summon_spots)] - where the veil is weak enough for the ritual to begin."
- if(!silent)
- to_chat(current, "
Objective #[obj_count]: [explanation]")
- cult_mind.memory += "
Objective #[obj_count]: [explanation]
"
-
/datum/antagonist/cult/can_be_owned(datum/mind/new_owner)
. = ..()
if(. && !ignore_implant)
- . = is_convertable_to_cult(new_owner.current)
+ . = is_convertable_to_cult(new_owner.current,cult_team)
+
+/datum/antagonist/cult/greet()
+ to_chat(owner, "
You are a member of the cult!")
+ owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/bloodcult.ogg', 100, FALSE, pressure_affected = FALSE)//subject to change
+ owner.announce_objectives()
/datum/antagonist/cult/on_gain()
. = ..()
var/mob/living/current = owner.current
- if(!LAZYLEN(SSticker.mode.cult_objectives))
- add_objectives()
+ add_objectives()
+ if(give_equipment)
+ equip_cultist()
SSticker.mode.cult += owner // Only add after they've been given objectives
- cult_memorization(owner)
SSticker.mode.update_cult_icons_added(owner)
current.log_message("
Has been converted to the cult of Nar'Sie!", INDIVIDUAL_ATTACK_LOG)
- if(GLOB.blood_target && GLOB.blood_target_image && current.client)
- current.client.images += GLOB.blood_target_image
+
+ if(cult_team.blood_target && cult_team.blood_target_image && current.client)
+ current.client.images += cult_team.blood_target_image
+
+
+/datum/antagonist/cult/proc/equip_cultist(tome=FALSE)
+ var/mob/living/carbon/H = owner.current
+ if(!istype(H))
+ return
+ if (owner.assigned_role == "Clown")
+ to_chat(owner, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
+ H.dna.remove_mutation(CLOWNMUT)
+
+ if(tome)
+ . += cult_give_item(/obj/item/tome, H)
+ else
+ . += cult_give_item(/obj/item/paper/talisman/supply, H)
+ to_chat(owner, "These will help you start the cult on this station. Use them well, and remember - you are not the only one.")
+
+
+/datum/antagonist/cult/proc/cult_give_item(obj/item/item_path, mob/living/carbon/human/mob)
+ var/list/slots = list(
+ "backpack" = slot_in_backpack,
+ "left pocket" = slot_l_store,
+ "right pocket" = slot_r_store
+ )
+
+ var/T = new item_path(mob)
+ var/item_name = initial(item_path.name)
+ var/where = mob.equip_in_one_of_slots(T, slots)
+ if(!where)
+ to_chat(mob, "
Unfortunately, you weren't able to get a [item_name]. This is very bad and you should adminhelp immediately (press F1).")
+ return 0
+ else
+ to_chat(mob, "
You have a [item_name] in your [where].")
+ if(where == "backpack")
+ var/obj/item/storage/B = mob.back
+ B.orient2hud(mob)
+ B.show_to(mob)
+ return 1
/datum/antagonist/cult/apply_innate_effects(mob/living/mob_override)
. = ..()
@@ -89,7 +108,7 @@
current.faction |= "cult"
current.grant_language(/datum/language/narsie)
current.verbs += /mob/living/proc/cult_help
- if(!GLOB.cult_mastered)
+ if(!cult_team.cult_mastered)
vote.Grant(current)
communion.Grant(current)
current.throw_alert("bloodsense", /obj/screen/alert/bloodsense)
@@ -107,6 +126,7 @@
current.clear_alert("bloodsense")
/datum/antagonist/cult/on_removal()
+ remove_objectives()
owner.wipe_memory()
SSticker.mode.cult -= owner
SSticker.mode.update_cult_icons_removed(owner)
@@ -114,8 +134,8 @@
owner.current.visible_message("
[owner.current] looks like [owner.current.p_they()] just reverted to their old faith!", ignored_mob = owner.current)
to_chat(owner.current, "
An unfamiliar white light flashes through your mind, cleansing the taint of the Geometer and all your memories as her servant.")
owner.current.log_message("
Has renounced the cult of Nar'Sie!", INDIVIDUAL_ATTACK_LOG)
- if(GLOB.blood_target && GLOB.blood_target_image && owner.current.client)
- owner.current.client.images -= GLOB.blood_target_image
+ if(cult_team.blood_target && cult_team.blood_target_image && owner.current.client)
+ owner.current.client.images -= cult_team.blood_target_image
. = ..()
/datum/antagonist/cult/master
@@ -145,7 +165,7 @@
var/mob/living/current = owner.current
if(mob_override)
current = mob_override
- if(!GLOB.reckoning_complete)
+ if(!cult_team.reckoning_complete)
reckoning.Grant(current)
bloodmark.Grant(current)
throwing.Grant(current)
@@ -162,3 +182,118 @@
throwing.Remove(current)
current.update_action_buttons_icon()
current.remove_status_effect(/datum/status_effect/cult_master)
+
+/datum/objective_team/cult
+ name = "Cult"
+
+ var/blood_target
+ var/image/blood_target_image
+ var/blood_target_reset_timer
+
+ var/cult_vote_called = FALSE
+ var/cult_mastered = FALSE
+ var/reckoning_complete = FALSE
+
+
+/datum/objective_team/cult/proc/setup_objectives()
+ //SAC OBJECTIVE , todo: move this to objective internals
+ var/list/target_candidates = list()
+ var/datum/objective/sacrifice/sac_objective = new
+ sac_objective.team = src
+
+ for(var/mob/living/carbon/human/player in GLOB.player_list)
+ if(player.mind && !player.mind.has_antag_datum(ANTAG_DATUM_CULT) && !is_convertable_to_cult(player) && player.stat != DEAD)
+ target_candidates += player.mind
+
+ if(target_candidates.len == 0)
+ message_admins("Cult Sacrifice: Could not find unconvertable target, checking for convertable target.")
+ for(var/mob/living/carbon/human/player in GLOB.player_list)
+ if(player.mind && !player.mind.has_antag_datum(ANTAG_DATUM_CULT) && player.stat != DEAD)
+ target_candidates += player.mind
+ listclearnulls(target_candidates)
+ if(LAZYLEN(target_candidates))
+ sac_objective.target = pick(target_candidates)
+ sac_objective.update_explanation_text()
+
+ var/datum/job/sacjob = SSjob.GetJob(sac_objective.target.assigned_role)
+ var/datum/preferences/sacface = sac_objective.target.current.client.prefs
+ var/icon/reshape = get_flat_human_icon(null, sacjob, sacface)
+ reshape.Shift(SOUTH, 4)
+ reshape.Shift(EAST, 1)
+ reshape.Crop(7,4,26,31)
+ reshape.Crop(-5,-3,26,30)
+ sac_objective.sac_image = reshape
+
+ objectives += sac_objective
+ else
+ message_admins("Cult Sacrifice: Could not find unconvertable or convertable target. WELP!")
+
+
+ //SUMMON OBJECTIVE
+
+ var/datum/objective/eldergod/summon_objective = new()
+ summon_objective.team = src
+ objectives += summon_objective
+
+/datum/objective/sacrifice
+ var/sacced = FALSE
+ var/sac_image
+
+/datum/objective/sacrifice/check_completion()
+ return sacced || completed
+
+/datum/objective/sacrifice/update_explanation_text()
+ if(target && !sacced)
+ explanation_text = "Sacrifice [target], the [target.assigned_role] via invoking a Sacrifice rune with them on it and three acolytes around it."
+ else
+ explanation_text = "The veil has already been weakened here, proceed to the final objective."
+
+/datum/objective/eldergod
+ var/summoned = FALSE
+ var/list/summon_spots = list()
+
+/datum/objective/eldergod/New()
+ ..()
+ var/sanity = 0
+ while(summon_spots.len < SUMMON_POSSIBILITIES && sanity < 100)
+ var/area/summon = pick(GLOB.sortedAreas - summon_spots)
+ if(summon && (summon.z in GLOB.station_z_levels) && summon.valid_territory)
+ summon_spots += summon
+ sanity++
+ update_explanation_text()
+
+/datum/objective/eldergod/update_explanation_text()
+ explanation_text = "Summon Nar-Sie by invoking the rune 'Summon Nar-Sie'.
The summoning can only be accomplished in [english_list(summon_spots)] - where the veil is weak enough for the ritual to begin."
+
+/datum/objective/eldergod/check_completion()
+ return summoned || completed
+
+/datum/objective_team/cult/proc/check_cult_victory()
+ for(var/datum/objective/O in objectives)
+ if(!O.check_completion())
+ return FALSE
+ return TRUE
+
+/datum/objective_team/cult/roundend_report()
+ var/list/parts = list()
+
+ if(check_cult_victory())
+ parts += "
The cult has succeeded! Nar-sie has snuffed out another torch in the void!"
+ else
+ parts += "
The staff managed to stop the cult! Dark words and heresy are no match for Nanotrasen's finest!"
+
+ if(objectives.len)
+ parts += "
The cultists' objectives were:"
+ var/count = 1
+ for(var/datum/objective/objective in objectives)
+ if(objective.check_completion())
+ parts += "
Objective #[count]: [objective.explanation_text]
Success!"
+ else
+ parts += "
Objective #[count]: [objective.explanation_text]
Fail."
+ count++
+
+ if(members.len)
+ parts += ""
+ parts += printplayerlist(members)
+
+ return "
[parts.Join("
")]
"
\ No newline at end of file
diff --git a/code/datums/antagonists/datum_traitor.dm b/code/datums/antagonists/datum_traitor.dm
index d386c79c25..3ccdc7ddfb 100644
--- a/code/datums/antagonists/datum_traitor.dm
+++ b/code/datums/antagonists/datum_traitor.dm
@@ -1,5 +1,6 @@
/datum/antagonist/traitor
name = "Traitor"
+ roundend_category = "traitors"
job_rank = ROLE_TRAITOR
var/should_specialise = FALSE //do we split into AI and human, set to true on inital assignment only
var/ai_datum = ANTAG_DATUM_TRAITOR_AI
@@ -8,7 +9,6 @@
var/employer = "The Syndicate"
var/give_objectives = TRUE
var/should_give_codewords = TRUE
- var/list/objectives_given = list()
/datum/antagonist/traitor/human
var/should_equip = TRUE
@@ -52,9 +52,9 @@
if(should_specialise)
return ..()//we never did any of this anyway
SSticker.mode.traitors -= owner
- for(var/O in objectives_given)
+ for(var/O in objectives)
owner.objectives -= O
- objectives_given = list()
+ objectives = list()
if(!silent && owner.current)
to_chat(owner.current,"
You are no longer the [special_role]! ")
owner.special_role = null
@@ -71,11 +71,11 @@
/datum/antagonist/traitor/proc/add_objective(var/datum/objective/O)
owner.objectives += O
- objectives_given += O
+ objectives += O
/datum/antagonist/traitor/proc/remove_objective(var/datum/objective/O)
owner.objectives -= O
- objectives_given -= O
+ objectives -= O
/datum/antagonist/traitor/proc/forge_traitor_objectives()
return
@@ -294,3 +294,53 @@
where = "In your [equipped_slot]"
to_chat(mob, "
[where] is a folder containing secret documents that another Syndicate group wants. We have set up a meeting with one of their agents on station to make an exchange. Exercise extreme caution as they cannot be trusted and may be hostile.")
+//TODO Collate
+/datum/antagonist/traitor/roundend_report()
+ var/list/result = list()
+
+ var/traitorwin = TRUE
+
+ result += printplayer(owner)
+
+ var/TC_uses = 0
+ var/uplink_true = FALSE
+ var/purchases = ""
+ for(var/datum/component/uplink/H in GLOB.uplinks)
+ if(H && H.owner && H.owner == owner.key)
+ TC_uses += H.spent_telecrystals
+ uplink_true = TRUE
+ purchases += H.purchase_log.generate_render(FALSE)
+
+ var/objectives_text = ""
+ if(objectives.len)//If the traitor had no objectives, don't need to process this.
+ var/count = 1
+ for(var/datum/objective/objective in objectives)
+ if(objective.check_completion())
+ objectives_text += "
Objective #[count]: [objective.explanation_text]
Success!"
+ else
+ objectives_text += "
Objective #[count]: [objective.explanation_text]
Fail."
+ traitorwin = FALSE
+ count++
+
+ if(uplink_true)
+ var/uplink_text = "(used [TC_uses] TC) [purchases]"
+ if(TC_uses==0 && traitorwin)
+ var/static/icon/badass = icon('icons/badass.dmi', "badass")
+ uplink_text += "
[icon2html(badass, world)]"
+ result += uplink_text
+
+ result += objectives_text
+
+ var/special_role_text = lowertext(name)
+
+ if(traitorwin)
+ result += "
The [special_role_text] was successful!"
+ else
+ result += "
The [special_role_text] has failed!"
+ SEND_SOUND(owner.current, 'sound/ambience/ambifailure.ogg')
+
+ return result.Join("
")
+
+/datum/antagonist/traitor/roundend_report_footer()
+ return "
The code phrases were: [GLOB.syndicate_code_phrase]\
+
The code responses were: [GLOB.syndicate_code_response]"
\ No newline at end of file
diff --git a/code/datums/antagonists/devil.dm b/code/datums/antagonists/devil.dm
index 416a8f3752..97e0d8c18a 100644
--- a/code/datums/antagonists/devil.dm
+++ b/code/datums/antagonists/devil.dm
@@ -86,6 +86,7 @@ GLOBAL_LIST_INIT(devil_syllable, list("hal", "ve", "odr", "neit", "ci", "quon",
GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master", ", the Lord of all things", ", Jr."))
/datum/antagonist/devil
name = "Devil"
+ roundend_category = "devils"
job_rank = ROLE_DEVIL
//Don't delete upon mind destruction, otherwise soul re-selling will break.
delete_on_mind_deletion = FALSE
@@ -508,6 +509,35 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
owner.RemoveSpell(S)
.=..()
+/datum/antagonist/devil/proc/printdevilinfo()
+ var/list/parts = list()
+ parts += "The devil's true name is: [truename]"
+ parts += "The devil's bans were:"
+ parts += "[GLOB.TAB][GLOB.lawlorify[LORE][ban]]"
+ parts += "[GLOB.TAB][GLOB.lawlorify[LORE][bane]]"
+ parts += "[GLOB.TAB][GLOB.lawlorify[LORE][obligation]]"
+ parts += "[GLOB.TAB][GLOB.lawlorify[LORE][banish]]"
+ return parts.Join("
")
+
+/datum/antagonist/devil/roundend_report()
+ var/list/parts = list()
+ parts += printplayer(owner)
+ parts += printdevilinfo()
+ parts += printobjectives(owner)
+ return parts.Join("
")
+
+/datum/antagonist/devil/roundend_report_footer()
+ //sintouched go here for now as a hack , TODO proper antag datum for these
+ var/list/parts = list()
+ if(SSticker.mode.sintouched.len)
+ parts += ""
+ var/list/sintouchedUnique = uniqueList(SSticker.mode.sintouched)
+ for(var/S in sintouchedUnique)
+ var/datum/mind/sintouched_mind = S
+ parts += printplayer(sintouched_mind)
+ parts += printobjectives(sintouched_mind)
+ return parts.Join("
")
+
//A simple super light weight datum for the codex gigas.
/datum/fakeDevil
var/truename
diff --git a/code/datums/antagonists/ninja.dm b/code/datums/antagonists/ninja.dm
index ab4822dd79..8201cd879d 100644
--- a/code/datums/antagonists/ninja.dm
+++ b/code/datums/antagonists/ninja.dm
@@ -37,19 +37,20 @@
else if(M.assigned_role in GLOB.command_positions)
possible_targets[M] = 1 //good-guy
- var/list/objectives = list(1,2,3,4)
- while(owner.objectives.len < quantity)
- switch(pick_n_take(objectives))
+ var/list/possible_objectives = list(1,2,3,4)
+
+ while(objectives.len < quantity)
+ switch(pick_n_take(possible_objectives))
if(1) //research
var/datum/objective/download/O = new /datum/objective/download()
O.owner = owner
O.gen_amount_goal()
- owner.objectives += O
+ objectives += O
if(2) //steal
var/datum/objective/steal/special/O = new /datum/objective/steal/special()
O.owner = owner
- owner.objectives += O
+ objectives += O
if(3) //protect/kill
if(!possible_targets.len) continue
@@ -63,13 +64,13 @@
O.owner = owner
O.target = M
O.explanation_text = "Slay \the [M.current.real_name], the [M.assigned_role]."
- owner.objectives += O
+ objectives += O
else //protect
var/datum/objective/protect/O = new /datum/objective/protect()
O.owner = owner
O.target = M
O.explanation_text = "Protect \the [M.current.real_name], the [M.assigned_role], from harm."
- owner.objectives += O
+ objectives += O
if(4) //debrain/capture
if(!possible_targets.len) continue
var/selected = rand(1,possible_targets.len)
@@ -82,17 +83,17 @@
O.owner = owner
O.target = M
O.explanation_text = "Steal the brain of [M.current.real_name]."
- owner.objectives += O
+ objectives += O
else //capture
var/datum/objective/capture/O = new /datum/objective/capture()
O.owner = owner
O.gen_amount_goal()
- owner.objectives += O
+ objectives += O
else
break
var/datum/objective/O = new /datum/objective/survive()
O.owner = owner
- owner.objectives += O
+ owner.objectives |= objectives
/proc/remove_ninja(mob/living/L)
diff --git a/code/datums/antagonists/nukeop.dm b/code/datums/antagonists/nukeop.dm
new file mode 100644
index 0000000000..d0f24e7d7b
--- /dev/null
+++ b/code/datums/antagonists/nukeop.dm
@@ -0,0 +1,322 @@
+#define NUKE_RESULT_FLUKE 0
+#define NUKE_RESULT_NUKE_WIN 1
+#define NUKE_RESULT_CREW_WIN 2
+#define NUKE_RESULT_CREW_WIN_SYNDIES_DEAD 3
+#define NUKE_RESULT_DISK_LOST 4
+#define NUKE_RESULT_DISK_STOLEN 5
+#define NUKE_RESULT_NOSURVIVORS 6
+#define NUKE_RESULT_WRONG_STATION 7
+#define NUKE_RESULT_WRONG_STATION_DEAD 8
+
+/datum/antagonist/nukeop
+ name = "Nuclear Operative"
+ roundend_category = "syndicate operatives" //just in case
+ job_rank = ROLE_OPERATIVE
+ var/datum/objective_team/nuclear/nuke_team
+ var/always_new_team = FALSE //If not assigned a team by default ops will try to join existing ones, set this to TRUE to always create new team.
+ var/send_to_spawnpoint = TRUE //Should the user be moved to default spawnpoint.
+ var/nukeop_outfit = /datum/outfit/syndicate
+
+/datum/antagonist/nukeop/proc/update_synd_icons_added(mob/living/M)
+ var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS]
+ opshud.join_hud(M)
+ set_antag_hud(M, "synd")
+
+/datum/antagonist/nukeop/proc/update_synd_icons_removed(mob/living/M)
+ var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS]
+ opshud.leave_hud(M)
+ set_antag_hud(M, null)
+
+/datum/antagonist/nukeop/apply_innate_effects(mob/living/mob_override)
+ var/mob/living/M = mob_override || owner.current
+ update_synd_icons_added(M)
+
+/datum/antagonist/nukeop/remove_innate_effects(mob/living/mob_override)
+ var/mob/living/M = mob_override || owner.current
+ update_synd_icons_removed(M)
+
+/datum/antagonist/nukeop/proc/equip_op()
+ if(!ishuman(owner.current))
+ return
+ var/mob/living/carbon/human/H = owner.current
+
+ H.set_species(/datum/species/human) //Plasamen burn up otherwise, and lizards are vulnerable to asimov AIs
+
+ H.equipOutfit(nukeop_outfit)
+ return TRUE
+
+/datum/antagonist/nukeop/greet()
+ owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ops.ogg',100,0)
+ to_chat(owner, "
You are a [nuke_team ? nuke_team.syndicate_name : "syndicate"] agent!")
+ owner.announce_objectives()
+ return
+
+/datum/antagonist/nukeop/on_gain()
+ give_alias()
+ forge_objectives()
+ . = ..()
+ equip_op()
+ memorize_code()
+ if(send_to_spawnpoint)
+ move_to_spawnpoint()
+
+/datum/antagonist/nukeop/get_team()
+ return nuke_team
+
+/datum/antagonist/nukeop/proc/assign_nuke()
+ if(nuke_team && !nuke_team.tracked_nuke)
+ nuke_team.memorized_code = random_nukecode()
+ var/obj/machinery/nuclearbomb/nuke = locate("syndienuke") in GLOB.nuke_list
+ if(nuke)
+ nuke_team.tracked_nuke = nuke
+ if(nuke.r_code == "ADMIN")
+ nuke.r_code = nuke_team.memorized_code
+ else //Already set by admins/something else?
+ nuke_team.memorized_code = nuke.r_code
+ else
+ stack_trace("Syndicate nuke not found during nuke team creation.")
+ nuke_team.memorized_code = null
+
+/datum/antagonist/nukeop/proc/give_alias()
+ if(nuke_team && nuke_team.syndicate_name)
+ var/number = 1
+ number = nuke_team.members.Find(owner)
+ owner.current.real_name = "[nuke_team.syndicate_name] Operative #[number]"
+
+/datum/antagonist/nukeop/proc/memorize_code()
+ if(nuke_team && nuke_team.tracked_nuke && nuke_team.memorized_code)
+ owner.store_memory("
[nuke_team.tracked_nuke] Code: [nuke_team.memorized_code]", 0, 0)
+ to_chat(owner, "The nuclear authorization code is:
[nuke_team.memorized_code]")
+ else
+ to_chat(owner, "Unfortunately the syndicate was unable to provide you with nuclear authorization code.")
+
+/datum/antagonist/nukeop/proc/forge_objectives()
+ if(nuke_team)
+ owner.objectives |= nuke_team.objectives
+
+/datum/antagonist/nukeop/proc/move_to_spawnpoint()
+ var/team_number = 1
+ if(nuke_team)
+ team_number = nuke_team.members.Find(owner)
+ owner.current.forceMove(GLOB.nukeop_start[((team_number - 1) % GLOB.nukeop_start.len) + 1])
+
+/datum/antagonist/nukeop/leader/move_to_spawnpoint()
+ owner.current.forceMove(pick(GLOB.nukeop_leader_start))
+
+/datum/antagonist/nukeop/create_team(datum/objective_team/nuclear/new_team)
+ if(!new_team)
+ if(!always_new_team)
+ for(var/datum/antagonist/nukeop/N in GLOB.antagonists)
+ if(N.nuke_team)
+ nuke_team = N.nuke_team
+ return
+ nuke_team = new /datum/objective_team/nuclear
+ nuke_team.update_objectives()
+ assign_nuke() //This is bit ugly
+ return
+ if(!istype(new_team))
+ stack_trace("Wrong team type passed to [type] initialization.")
+ nuke_team = new_team
+
+/datum/antagonist/nukeop/leader
+ name = "Nuclear Operative Leader"
+ nukeop_outfit = /datum/outfit/syndicate/leader
+ always_new_team = TRUE
+ var/title
+
+/datum/antagonist/nukeop/leader/memorize_code()
+ ..()
+ if(nuke_team && nuke_team.memorized_code)
+ var/obj/item/paper/P = new
+ P.info = "The nuclear authorization code is:
[nuke_team.memorized_code]"
+ P.name = "nuclear bomb code"
+ var/mob/living/carbon/human/H = owner.current
+ if(!istype(H))
+ P.forceMove(get_turf(H))
+ else
+ H.put_in_hands(P, TRUE)
+ H.update_icons()
+
+/datum/antagonist/nukeop/leader/give_alias()
+ title = pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord")
+ if(nuke_team && nuke_team.syndicate_name)
+ owner.current.real_name = "[nuke_team.syndicate_name] [title]"
+ else
+ owner.current.real_name = "Syndicate [title]"
+
+/datum/antagonist/nukeop/leader/greet()
+ owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ops.ogg',100,0)
+ to_chat(owner, "
You are the Syndicate [title] for this mission. You are responsible for the distribution of telecrystals and your ID is the only one who can open the launch bay doors.")
+ to_chat(owner, "
If you feel you are not up to this task, give your ID to another operative.")
+ to_chat(owner, "
In your hand you will find a special item capable of triggering a greater challenge for your team. Examine it carefully and consult with your fellow operatives before activating it.")
+ owner.announce_objectives()
+ addtimer(CALLBACK(src, .proc/nuketeam_name_assign), 1)
+
+
+/datum/antagonist/nukeop/leader/proc/nuketeam_name_assign()
+ if(!nuke_team)
+ return
+ nuke_team.rename_team(ask_name())
+
+/datum/objective_team/nuclear/proc/rename_team(new_name)
+ syndicate_name = new_name
+ name = "[syndicate_name] Team"
+ for(var/I in members)
+ var/datum/mind/synd_mind = I
+ var/mob/living/carbon/human/H = synd_mind.current
+ if(!istype(H))
+ continue
+ var/chosen_name = H.dna.species.random_name(H.gender,0,syndicate_name)
+ H.fully_replace_character_name(H.real_name,chosen_name)
+
+/datum/antagonist/nukeop/leader/proc/ask_name()
+ var/randomname = pick(GLOB.last_names)
+ var/newname = stripped_input(owner.current,"You are the nuke operative [title]. Please choose a last name for your family.", "Name change",randomname)
+ if (!newname)
+ newname = randomname
+ else
+ newname = reject_bad_name(newname)
+ if(!newname)
+ newname = randomname
+
+ return capitalize(newname)
+
+/datum/antagonist/nukeop/lone
+ name = "Lone Operative"
+ always_new_team = TRUE
+ send_to_spawnpoint = FALSE //Handled by event
+ nukeop_outfit = /datum/outfit/syndicate/full
+
+/datum/antagonist/nukeop/lone/assign_nuke()
+ if(nuke_team && !nuke_team.tracked_nuke)
+ nuke_team.memorized_code = random_nukecode()
+ var/obj/machinery/nuclearbomb/selfdestruct/nuke = locate() in GLOB.nuke_list
+ if(nuke)
+ nuke_team.tracked_nuke = nuke
+ if(nuke.r_code == "ADMIN")
+ nuke.r_code = nuke_team.memorized_code
+ else //Already set by admins/something else?
+ nuke_team.memorized_code = nuke.r_code
+ else
+ stack_trace("Station self destruct ot found during lone op team creation.")
+ nuke_team.memorized_code = null
+
+/datum/objective_team/nuclear
+ var/syndicate_name
+ var/obj/machinery/nuclearbomb/tracked_nuke
+ var/core_objective = /datum/objective/nuclear
+ var/memorized_code
+
+/datum/objective_team/nuclear/New()
+ ..()
+ syndicate_name = syndicate_name()
+
+/datum/objective_team/nuclear/proc/update_objectives()
+ if(core_objective)
+ var/datum/objective/O = new core_objective
+ O.team = src
+ objectives += O
+
+/datum/objective_team/nuclear/proc/disk_rescued()
+ for(var/obj/item/disk/nuclear/D in GLOB.poi_list)
+ if(!D.onCentCom())
+ return FALSE
+ return TRUE
+
+/datum/objective_team/nuclear/proc/operatives_dead()
+ for(var/I in members)
+ var/datum/mind/operative_mind = I
+ if(ishuman(operative_mind.current) && (operative_mind.current.stat != DEAD))
+ return FALSE
+ return TRUE
+
+/datum/objective_team/nuclear/proc/syndies_escaped()
+ var/obj/docking_port/mobile/S = SSshuttle.getShuttle("syndicate")
+ return (S && (S.z == ZLEVEL_CENTCOM || S.z == ZLEVEL_TRANSIT))
+
+/datum/objective_team/nuclear/proc/get_result()
+ var/evacuation = SSshuttle.emergency.mode == SHUTTLE_ENDGAME
+ var/disk_rescued = disk_rescued()
+ var/syndies_didnt_escape = !syndies_escaped()
+ var/station_was_nuked = SSticker.mode.station_was_nuked
+ var/nuke_off_station = SSticker.mode.nuke_off_station
+
+ if(nuke_off_station == NUKE_SYNDICATE_BASE)
+ return NUKE_RESULT_FLUKE
+ else if(!disk_rescued && station_was_nuked && !syndies_didnt_escape)
+ return NUKE_RESULT_NUKE_WIN
+ else if (!disk_rescued && station_was_nuked && syndies_didnt_escape)
+ return NUKE_RESULT_NOSURVIVORS
+ else if (!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape)
+ return NUKE_RESULT_WRONG_STATION
+ else if (!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape)
+ return NUKE_RESULT_WRONG_STATION_DEAD
+ else if ((disk_rescued || evacuation) && operatives_dead())
+ return NUKE_RESULT_CREW_WIN_SYNDIES_DEAD
+ else if (disk_rescued)
+ return NUKE_RESULT_CREW_WIN
+ else if (!disk_rescued && operatives_dead())
+ return NUKE_RESULT_DISK_LOST
+ else if (!disk_rescued && evacuation)
+ return NUKE_RESULT_DISK_STOLEN
+ else
+ return //Undefined result
+
+/datum/objective_team/nuclear/roundend_report()
+ var/list/parts = list()
+ parts += ""
+
+ switch(get_result())
+ if(NUKE_RESULT_FLUKE)
+ parts += "
Humiliating Syndicate Defeat"
+ parts += "
The crew of [station_name()] gave [syndicate_name] operatives back their bomb! The syndicate base was destroyed! Next time, don't lose the nuke!"
+ if(NUKE_RESULT_NUKE_WIN)
+ parts += "
Syndicate Major Victory!"
+ parts += "
[syndicate_name] operatives have destroyed [station_name()]!"
+ if(NUKE_RESULT_NOSURVIVORS)
+ parts += "
Total Annihilation"
+ parts += "
[syndicate_name] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion. Next time, don't lose the disk!"
+ if(NUKE_RESULT_WRONG_STATION)
+ parts += "
Crew Minor Victory"
+ parts += "
[syndicate_name] operatives secured the authentication disk but blew up something that wasn't [station_name()]. Next time, don't do that!"
+ if(NUKE_RESULT_WRONG_STATION_DEAD)
+ parts += "
[syndicate_name] operatives have earned Darwin Award!"
+ parts += "
[syndicate_name] operatives blew up something that wasn't [station_name()] and got caught in the explosion. Next time, don't do that!"
+ if(NUKE_RESULT_CREW_WIN_SYNDIES_DEAD)
+ parts += "
Crew Major Victory!"
+ parts += "
The Research Staff has saved the disk and killed the [syndicate_name] Operatives"
+ if(NUKE_RESULT_CREW_WIN)
+ parts += "
Crew Major Victory"
+ parts += "
The Research Staff has saved the disk and stopped the [syndicate_name] Operatives!"
+ if(NUKE_RESULT_DISK_LOST)
+ parts += "
Neutral Victory!"
+ parts += "
The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name] Operatives!"
+ if(NUKE_RESULT_DISK_STOLEN)
+ parts += "
Syndicate Minor Victory!"
+ parts += "
[syndicate_name] operatives survived the assault but did not achieve the destruction of [station_name()]. Next time, don't lose the disk!"
+ else
+ parts += "
Neutral Victory"
+ parts += "
Mission aborted!"
+
+ var/text = "
"
+ var/purchases = ""
+ var/TC_uses = 0
+ for(var/I in members)
+ var/datum/mind/syndicate = I
+ for(var/U in GLOB.uplinks)
+ var/datum/component/uplink/H = U
+ if(H.owner == syndicate.key)
+ TC_uses += H.spent_telecrystals
+ if(H.purchase_log)
+ purchases += H.purchase_log.generate_render(show_key = FALSE)
+ else
+ stack_trace("WARNING: Nuke Op uplink with no purchase_log Owner: [H.owner]")
+ text += printplayerlist(members)
+ text += "
"
+ text += "(Syndicates used [TC_uses] TC) [purchases]"
+ if(TC_uses == 0 && SSticker.mode.station_was_nuked && !operatives_dead())
+ text += "
[icon2html('icons/badass.dmi', world, "badass")]"
+
+ parts += text
+
+ return "
[parts.Join("
")]
"
diff --git a/code/datums/antagonists/pirate.dm b/code/datums/antagonists/pirate.dm
index ad32e09151..e0ce38c1e4 100644
--- a/code/datums/antagonists/pirate.dm
+++ b/code/datums/antagonists/pirate.dm
@@ -1,6 +1,7 @@
/datum/antagonist/pirate
name = "Space Pirate"
job_rank = ROLE_TRAITOR
+ roundend_category = "space pirates"
var/datum/objective_team/pirate/crew
/datum/antagonist/pirate/greet()
@@ -36,7 +37,6 @@
/datum/objective_team/pirate
name = "Pirate crew"
- var/list/objectives = list()
/datum/objective_team/pirate/proc/forge_objectives()
var/datum/objective/loot/getbooty = new()
@@ -84,11 +84,11 @@ GLOBAL_LIST_INIT(pirate_loot_cache, typecacheof(list(
loot_table[lootname] = count
else
loot_table[lootname] += count
- var/text = ""
+ var/list/loot_texts = list()
for(var/key in loot_table)
var/amount = loot_table[key]
- text += "[amount] [key][amount > 1 ? "s":""], "
- return text
+ loot_texts += "[amount] [key][amount > 1 ? "s":""]"
+ return loot_texts.Join(", ")
/datum/objective/loot/proc/get_loot_value()
if(!storage_area)
@@ -105,31 +105,26 @@ GLOBAL_LIST_INIT(pirate_loot_cache, typecacheof(list(
return ..() || get_loot_value() >= target_value
-//These need removal ASAP as everything is converted to datum antags.
-/datum/game_mode/proc/auto_declare_completion_pirates()
- var/list/datum/mind/pirates = get_antagonists(/datum/antagonist/pirate)
- var/datum/objective_team/pirate/crew
- var/text = ""
- if(pirates.len)
- text += "
Space Pirates were:"
- for(var/datum/mind/M in pirates)
- text += printplayer(M)
- if(!crew)
- var/datum/antagonist/pirate/P = M.has_antag_datum(/datum/antagonist/pirate)
- crew = P.crew
- if(crew)
- text += "
Loot stolen: "
- var/datum/objective/loot/L = locate() in crew.objectives
- text += L.loot_listing()
- text += "
Total loot value : [L.get_loot_value()]/[L.target_value] credits"
- var/all_dead = TRUE
- for(var/datum/mind/M in crew.members)
- if(considered_alive(M))
- all_dead = FALSE
- break
- if(L.check_completion() && !all_dead)
- text += "
The pirate crew was successful!"
- else
- text += "
The pirate crew has failed."
- to_chat(world, text)
\ No newline at end of file
+/datum/objective_team/pirate/roundend_report()
+ var/list/parts = list()
+
+ parts += ""
+
+ var/all_dead = TRUE
+ for(var/datum/mind/M in members)
+ if(considered_alive(M))
+ all_dead = FALSE
+ parts += printplayerlist(members)
+
+ parts += "Loot stolen: "
+ var/datum/objective/loot/L = locate() in objectives
+ parts += L.loot_listing()
+ parts += "Total loot value : [L.get_loot_value()]/[L.target_value] credits"
+
+ if(L.check_completion() && !all_dead)
+ parts += "
The pirate crew was successful!"
+ else
+ parts += "
The pirate crew has failed."
+
+ return parts.Join("
")
\ No newline at end of file
diff --git a/code/datums/antagonists/revolution.dm b/code/datums/antagonists/revolution.dm
index 9db86bdf08..14c6d1ab0e 100644
--- a/code/datums/antagonists/revolution.dm
+++ b/code/datums/antagonists/revolution.dm
@@ -3,6 +3,7 @@
/datum/antagonist/rev
name = "Revolutionary"
+ roundend_category = "revolutionaries" // if by some miracle revolutionaries without revolution happen
job_rank = ROLE_REV
var/hud_type = "rev"
var/datum/objective_team/revolution/rev_team
@@ -184,7 +185,6 @@
/datum/objective_team/revolution
name = "Revolution"
- var/list/objectives = list()
var/max_headrevs = 3
/datum/objective_team/revolution/proc/update_objectives(initial = FALSE)
@@ -227,3 +227,56 @@
rev.promote()
addtimer(CALLBACK(src,.proc/update_heads),HEAD_UPDATE_PERIOD,TIMER_UNIQUE)
+
+
+/datum/objective_team/revolution/roundend_report()
+ if(!members.len)
+ return
+
+ var/list/result = list()
+
+ result += "
"
+
+ var/num_revs = 0
+ var/num_survivors = 0
+ for(var/mob/living/carbon/survivor in GLOB.alive_mob_list)
+ if(survivor.ckey)
+ num_survivors++
+ if(survivor.mind)
+ if(is_revolutionary(survivor))
+ num_revs++
+ if(num_survivors)
+ result += "Command's Approval Rating:
[100 - round((num_revs/num_survivors)*100, 0.1)]%"
+
+
+ var/list/targets = list()
+ var/list/datum/mind/headrevs = get_antagonists(/datum/antagonist/rev/head)
+ var/list/datum/mind/revs = get_antagonists(/datum/antagonist/rev,TRUE)
+ if(headrevs.len)
+ var/list/headrev_part = list()
+ headrev_part += ""
+ headrev_part += printplayerlist(headrevs,TRUE)
+ result += headrev_part.Join("
")
+
+ if(revs.len)
+ var/list/rev_part = list()
+ rev_part += ""
+ rev_part += printplayerlist(revs,TRUE)
+ result += rev_part.Join("
")
+
+ var/list/heads = SSjob.get_all_heads()
+ if(heads.len)
+ var/head_text = ""
+ head_text += "
"
+ for(var/datum/mind/head in heads)
+ var/target = (head in targets)
+ head_text += "- "
+ if(target)
+ head_text += "Target"
+ head_text += "[printplayer(head, 1)]
"
+ head_text += "
"
+ result += head_text
+
+ result += "
"
+
+ return result.Join()
\ No newline at end of file
diff --git a/code/datums/antagonists/wizard.dm b/code/datums/antagonists/wizard.dm
index e1e6dc09c8..7f23215d6c 100644
--- a/code/datums/antagonists/wizard.dm
+++ b/code/datums/antagonists/wizard.dm
@@ -5,13 +5,13 @@
/datum/antagonist/wizard
name = "Space Wizard"
+ roundend_category = "wizards/witches"
job_rank = ROLE_WIZARD
var/give_objectives = TRUE
var/strip = TRUE //strip before equipping
var/allow_rename = TRUE
var/hud_version = "wizard"
var/datum/objective_team/wizard/wiz_team //Only created if wizard summons apprentices
- var/list/objectives = list() //this should be base datum antag proc and list, todo make lazy
var/move_to_lair = TRUE
var/outfit_type = /datum/outfit/wizard
var/wiz_age = WIZARD_AGE_MIN /* Wizards by nature cannot be too young. */
@@ -45,10 +45,12 @@
/datum/objective_team/wizard
name = "wizard team"
+ var/datum/antagonist/wizard/master_wizard
/datum/antagonist/wizard/proc/create_wiz_team()
wiz_team = new(owner)
wiz_team.name = "[owner.current.real_name] team"
+ wiz_team.master_wizard = src
update_wiz_icons_added(owner.current)
/datum/antagonist/wizard/proc/send_to_lair()
@@ -283,4 +285,46 @@
var/datum/objective/new_objective = new("Protect Wizard Academy from the intruders")
new_objective.owner = owner
owner.objectives += new_objective
- objectives += new_objective
\ No newline at end of file
+ objectives += new_objective
+
+//Solo wizard report
+/datum/antagonist/wizard/roundend_report()
+ var/list/parts = list()
+
+ parts += printplayer(owner)
+
+ var/count = 1
+ var/wizardwin = 1
+ for(var/datum/objective/objective in objectives)
+ if(objective.check_completion())
+ parts += "
Objective #[count]: [objective.explanation_text]
Success!"
+ else
+ parts += "
Objective #[count]: [objective.explanation_text]
Fail."
+ wizardwin = 0
+ count++
+
+ if(wizardwin)
+ parts += "
The wizard was successful!"
+ else
+ parts += "
The wizard has failed!"
+
+ if(owner.spell_list.len>0)
+ parts += "
[owner.name] used the following spells: "
+ var/list/spell_names = list()
+ for(var/obj/effect/proc_holder/spell/S in owner.spell_list)
+ spell_names += S.name
+ parts += spell_names.Join(", ")
+
+ return parts.Join("
")
+
+//Wizard with apprentices report
+/datum/objective_team/wizard/roundend_report()
+ var/list/parts = list()
+
+ parts += ""
+ parts += master_wizard.roundend_report()
+ parts += " "
+ parts += ""
+ parts += printplayerlist(members - master_wizard.owner)
+
+ return "
[parts.Join("
")]
"
\ No newline at end of file
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index c0ab1dc015..14d619c917 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -185,12 +185,6 @@
objectives, uplinks, powers etc are all handled.
*/
-/datum/mind/proc/remove_objectives()
- if(objectives.len)
- for(var/datum/objective/O in objectives)
- objectives -= O
- qdel(O)
-
/datum/mind/proc/remove_changeling()
var/datum/antagonist/changeling/C = has_antag_datum(/datum/antagonist/changeling)
if(C)
@@ -223,7 +217,6 @@
if(src in SSticker.mode.cult)
SSticker.mode.remove_cultist(src, 0, 0)
special_role = null
- remove_objectives()
remove_antag_equip()
/datum/mind/proc/remove_rev()
@@ -769,17 +762,44 @@
var/objective_pos
var/def_value
+
+
+ var/datum/antagonist/target_antag
+
if (href_list["obj_edit"])
objective = locate(href_list["obj_edit"])
if (!objective)
return
- objective_pos = objectives.Find(objective)
+
+ for(var/datum/antagonist/A in antag_datums)
+ if(objective in A.objectives)
+ target_antag = A
+ objective_pos = A.objectives.Find(objective)
+ break
+
+ if(!target_antag) //Shouldn't happen
+ stack_trace("objective without antagonist found")
+ objective_pos = objectives.Find(objective)
//Text strings are easy to manipulate. Revised for simplicity.
var/temp_obj_type = "[objective.type]"//Convert path into a text string.
def_value = copytext(temp_obj_type, 19)//Convert last part of path into an objective keyword.
if(!def_value)//If it's a custom objective, it will be an empty string.
def_value = "custom"
+ else
+ switch(antag_datums.len)
+ if(0)
+ target_antag = add_antag_datum(/datum/antagonist/custom)
+ if(1)
+ target_antag = antag_datums[1]
+ else
+ var/datum/antagonist/target = input("Which antagonist gets the objective:", "Antagonist", def_value) as null|anything in antag_datums + "(new custom antag)"
+ if (QDELETED(target))
+ return
+ else if(target == "(new custom antag)")
+ target_antag = add_antag_datum(/datum/antagonist/custom)
+ else
+ target_antag = target
var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "late-assassinate", "maroon", "debrain", "protect", "destroy", "prevent", "hijack", "escape", "survive", "martyr", "steal", "download", "nuclear", "capture", "absorb", "custom")
if (!new_obj_type)
@@ -901,11 +921,15 @@
return
if (objective)
+ if(target_antag)
+ target_antag.objectives -= objective
objectives -= objective
- objectives.Insert(objective_pos, new_objective)
+ target_antag.objectives.Insert(objective_pos, new_objective)
message_admins("[key_name_admin(usr)] edited [current]'s objective to [new_objective.explanation_text]")
log_admin("[key_name(usr)] edited [current]'s objective to [new_objective.explanation_text]")
else
+ if(target_antag)
+ target_antag.objectives += new_objective
objectives += new_objective
message_admins("[key_name_admin(usr)] added a new objective for [current]: [new_objective.explanation_text]")
log_admin("[key_name(usr)] added a new objective for [current]: [new_objective.explanation_text]")
@@ -914,6 +938,11 @@
var/datum/objective/objective = locate(href_list["obj_delete"])
if(!istype(objective))
return
+
+ for(var/datum/antagonist/A in antag_datums)
+ if(objective in A.objectives)
+ A.objectives -= objective
+ break
objectives -= objective
message_admins("[key_name_admin(usr)] removed an objective for [current]: [objective.explanation_text]")
log_admin("[key_name(usr)] removed an objective for [current]: [objective.explanation_text]")
@@ -996,11 +1025,13 @@
message_admins("[key_name_admin(usr)] has cult'ed [current].")
log_admin("[key_name(usr)] has cult'ed [current].")
if("tome")
- if (!SSticker.mode.equip_cultist(current,1))
+ var/datum/antagonist/cult/C = has_antag_datum(/datum/antagonist/cult,TRUE)
+ if (C.equip_cultist(current,1))
to_chat(usr, "
Spawning tome failed!")
if("amulet")
- if (!SSticker.mode.equip_cultist(current))
+ var/datum/antagonist/cult/C = has_antag_datum(/datum/antagonist/cult,TRUE)
+ if (C.equip_cultist(current))
to_chat(usr, "
Spawning amulet failed!")
else if(href_list["clockcult"])
@@ -1407,16 +1438,11 @@
/datum/mind/proc/make_Cultist()
- if(!(src in SSticker.mode.cult))
- SSticker.mode.add_cultist(src,FALSE)
+ if(!has_antag_datum(/datum/antagonist/cult,TRUE))
+ SSticker.mode.add_cultist(src,FALSE,equip=TRUE)
special_role = "Cultist"
to_chat(current, "
You catch a glimpse of the Realm of Nar-Sie, The Geometer of Blood. You now see how flimsy your world is, you see that it should be open to the knowledge of Nar-Sie.")
to_chat(current, "
Assist your new bretheren in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back.")
- var/datum/antagonist/cult/C
- C.cult_memorization(src)
- var/mob/living/carbon/human/H = current
- if (!SSticker.mode.equip_cultist(current))
- to_chat(H, "Spawning an amulet from your Master failed.")
/datum/mind/proc/make_Rev()
var/datum/antagonist/rev/head/head = new(src)
diff --git a/code/game/gamemodes/antag_spawner.dm b/code/game/gamemodes/antag_spawner.dm
index 7456b8f5eb..d0ab4347ff 100644
--- a/code/game/gamemodes/antag_spawner.dm
+++ b/code/game/gamemodes/antag_spawner.dm
@@ -242,6 +242,7 @@
new_objective2.owner = S.mind
new_objective2.explanation_text = "[objective_verb] everyone[usr ? " else while you're at it":""]."
S.mind.objectives += new_objective2
+ S.mind.add_antag_datum(/datum/antagonist/auto_custom)
to_chat(S, S.playstyle_string)
to_chat(S, "
You are currently not currently in the same plane of existence as the station. \
Ctrl+Click a blood pool to manifest.")
diff --git a/code/game/gamemodes/brother/traitor_bro.dm b/code/game/gamemodes/brother/traitor_bro.dm
index c1af1601ce..34373da5c8 100644
--- a/code/game/gamemodes/brother/traitor_bro.dm
+++ b/code/game/gamemodes/brother/traitor_bro.dm
@@ -1,41 +1,3 @@
-/datum/objective_team/brother_team
- name = "brotherhood"
- member_name = "blood brother"
- var/list/objectives = list()
- var/meeting_area
-
-/datum/objective_team/brother_team/is_solo()
- return FALSE
-
-/datum/objective_team/brother_team/proc/add_objective(datum/objective/O, needs_target = FALSE)
- O.team = src
- if(needs_target)
- O.find_target()
- O.update_explanation_text()
- objectives += O
-
-/datum/objective_team/brother_team/proc/forge_brother_objectives()
- objectives = list()
- var/is_hijacker = prob(10)
- for(var/i = 1 to max(1, CONFIG_GET(number/brother_objectives_amount) + (members.len > 2) - is_hijacker))
- forge_single_objective()
- if(is_hijacker)
- if(!locate(/datum/objective/hijack) in objectives)
- add_objective(new/datum/objective/hijack)
- else if(!locate(/datum/objective/escape) in objectives)
- add_objective(new/datum/objective/escape)
-
-/datum/objective_team/brother_team/proc/forge_single_objective()
- if(prob(50))
- if(LAZYLEN(active_ais()) && prob(100/GLOB.joined_player_list.len))
- add_objective(new/datum/objective/destroy, TRUE)
- else if(prob(30))
- add_objective(new/datum/objective/maroon, TRUE)
- else
- add_objective(new/datum/objective/assassinate, TRUE)
- else
- add_objective(new/datum/objective/steal, TRUE)
-
/datum/game_mode
var/list/datum/mind/brothers = list()
var/list/datum/objective_team/brother_team/brother_teams = list()
@@ -54,6 +16,7 @@
var/list/datum/objective_team/brother_team/pre_brother_teams = list()
var/const/team_amount = 2 //hard limit on brother teams if scaling is turned off
var/const/min_team_size = 2
+ traitors_required = FALSE //Only teams are possible
var/meeting_areas = list("The Bar", "Dorms", "Escape Dock", "Arrivals", "Holodeck", "Primary Tool Storage", "Recreation Area", "Chapel", "Library")
@@ -92,12 +55,14 @@
team.forge_brother_objectives()
for(var/datum/mind/M in team.members)
M.add_antag_datum(ANTAG_DATUM_BROTHER, team)
+ team.update_name()
brother_teams += pre_brother_teams
return ..()
/datum/game_mode/traitor/bros/generate_report()
return "It's Syndicate recruiting season. Be alert for potential Syndicate infiltrators, but also watch out for disgruntled employees trying to defect. Unlike Nanotrasen, the Syndicate prides itself in teamwork and will only recruit pairs that share a brotherly trust."
+<<<<<<< HEAD
/datum/game_mode/proc/auto_declare_completion_brother()
if(!LAZYLEN(brother_teams))
return
@@ -130,6 +95,8 @@
text += "
"
to_chat(world, text)
+=======
+>>>>>>> 3d81385... Roundend report refactor (#33246)
/datum/game_mode/proc/update_brother_icons_added(datum/mind/brother_mind)
var/datum/atom_hud/antag/brotherhud = GLOB.huds[ANTAG_HUD_BROTHER]
brotherhud.join_hud(brother_mind.current)
diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm
index a70f392d4f..6fc06f7bca 100644
--- a/code/game/gamemodes/changeling/changeling.dm
+++ b/code/game/gamemodes/changeling/changeling.dm
@@ -94,6 +94,7 @@ GLOBAL_VAR(changeling_team_objective_type) //If this is not null, we hand our th
of the Thing being sent to a station in this sector is highly likely. It may be in the guise of any crew member. Trust nobody - suspect everybody. Do not announce this to the crew, \
as paranoia may spread and inhibit workplace efficiency."
+<<<<<<< HEAD
/datum/game_mode/proc/auto_declare_completion_changeling()
var/list/changelings = get_antagonists(/datum/antagonist/changeling,TRUE) //Only real lings get a mention
if(changelings.len)
@@ -135,6 +136,8 @@ GLOBAL_VAR(changeling_team_objective_type) //If this is not null, we hand our th
return 1
+=======
+>>>>>>> 3d81385... Roundend report refactor (#33246)
/proc/changeling_transform(mob/living/carbon/human/user, datum/changelingprofile/chosen_prof)
var/datum/dna/chosen_dna = chosen_prof.dna
user.real_name = chosen_prof.name
diff --git a/code/game/gamemodes/clock_cult/clock_cult.dm b/code/game/gamemodes/clock_cult/clock_cult.dm
index 61bd9bb496..778cf41022 100644
--- a/code/game/gamemodes/clock_cult/clock_cult.dm
+++ b/code/game/gamemodes/clock_cult/clock_cult.dm
@@ -65,13 +65,16 @@ Credit where due:
return TRUE
return FALSE
-/proc/add_servant_of_ratvar(mob/L, silent = FALSE)
+/proc/add_servant_of_ratvar(mob/L, silent = FALSE, create_team = TRUE)
if(!L || !L.mind)
return
var/update_type = ANTAG_DATUM_CLOCKCULT
if(silent)
update_type = ANTAG_DATUM_CLOCKCULT_SILENT
- . = L.mind.add_antag_datum(update_type)
+ var/datum/antagonist/clockcult/C = new update_type(L.mind)
+ C.make_team = create_team
+ C.show_in_roundend = create_team //tutorial scarabs begone
+ . = L.mind.add_antag_datum(C)
/proc/remove_servant_of_ratvar(mob/L, silent = FALSE)
if(!L || !L.mind)
@@ -88,7 +91,6 @@ Credit where due:
///////////////
/datum/game_mode
- var/datum/mind/eminence //The clockwork Eminence
var/list/servants_of_ratvar = list() //The Enlightened servants of Ratvar
var/clockwork_explanation = "Defend the Ark of the Clockwork Justiciar and free Ratvar." //The description of the current objective
@@ -110,6 +112,8 @@ Credit where due:
var/servants_to_serve = list()
var/roundstart_player_count
var/ark_time //In minutes, how long the Ark waits before activation; this is equal to 30 + (number of players / 5) (max 40 mins.)
+
+ var/datum/objective_team/clockcult/main_clockcult
/datum/game_mode/clockwork_cult/pre_setup()
if(CONFIG_GET(flag/protect_roles_from_antagonist))
@@ -191,16 +195,16 @@ Credit where due:
. = ..()
/datum/game_mode/clockwork_cult/proc/check_clockwork_victory()
+ return main_clockcult.check_clockwork_victory()
+
+/datum/game_mode/clock_cult/set_round_result()
+ ..()
if(GLOB.clockwork_gateway_activated)
SSticker.news_report = CLOCK_SUMMON
- return TRUE
+ SSticker.mode_result = "win - servants completed their objective (summon ratvar)"
else
SSticker.news_report = CULT_FAILURE
- return FALSE
-
-/datum/game_mode/clockwork_cult/declare_completion()
- ..()
- return //Doesn't end until the round does
+ SSticker.mode_result = "loss - servants failed their objective (summon ratvar)"
/datum/game_mode/clockwork_cult/generate_report()
return "Bluespace monitors near your sector have detected a continuous stream of patterned fluctuations since the station was completed. It is most probable that a powerful entity \
@@ -210,30 +214,6 @@ Credit where due:
working for this entity and utilizing highly-advanced technology to cross the great distance at will. If they should turn out to be a credible threat, the task falls on you and \
your crew to dispatch it in a timely manner."
-/datum/game_mode/proc/auto_declare_completion_clockwork_cult()
- var/text = ""
- if(istype(SSticker.mode, /datum/game_mode/clockwork_cult)) //Possibly hacky?
- var/datum/game_mode/clockwork_cult/C = SSticker.mode
- if(C.check_clockwork_victory())
- text += "
Ratvar's servants defended the Ark until its activation!"
- SSticker.mode_result = "win - servants completed their objective (summon ratvar)"
- else
- text += "
The Ark was destroyed! Ratvar will rust away for all eternity!"
- SSticker.mode_result = "loss - servants failed their objective (summon ratvar)"
- text += "
The servants' objective was: [CLOCKCULT_OBJECTIVE]."
- text += "
Ratvar's servants had
[GLOB.clockwork_caches] Tinkerer's Caches."
- text += "
Construction Value(CV) was:
[GLOB.clockwork_construction_value]"
- for(var/i in SSticker.scripture_states)
- if(i != SCRIPTURE_DRIVER)
- text += "
[i] scripture was:
[SSticker.scripture_states[i] ? "UN":""]LOCKED"
- if(SSticker.mode.eminence)
- text += "
The Eminence was: [printplayer(SSticker.mode.eminence)]"
- if(servants_of_ratvar.len)
- text += "
Ratvar's servants were:"
- for(var/datum/mind/M in servants_of_ratvar - SSticker.mode.eminence)
- text += printplayer(M)
- to_chat(world, text)
-
/datum/game_mode/proc/update_servant_icons_added(datum/mind/M)
var/datum/atom_hud/antag/A = GLOB.huds[ANTAG_HUD_CLOCKWORK]
A.join_hud(M.current)
diff --git a/code/game/gamemodes/clock_cult/clock_items/construct_chassis.dm b/code/game/gamemodes/clock_cult/clock_items/construct_chassis.dm
index acf6e43974..1b2b867aa5 100644
--- a/code/game/gamemodes/clock_cult/clock_items/construct_chassis.dm
+++ b/code/game/gamemodes/clock_cult/clock_items/construct_chassis.dm
@@ -91,7 +91,8 @@
/obj/item/clockwork/construct_chassis/cogscarab/pre_spawn()
if(infinite_resources)
- construct_type = /mob/living/simple_animal/drone/cogscarab/ratvar //During rounds where they can't interact with the station, let them experiment with builds
+ //During rounds where they can't interact with the station, let them experiment with builds
+ construct_type = /mob/living/simple_animal/drone/cogscarab/ratvar
/obj/item/clockwork/construct_chassis/cogscarab/post_spawn(mob/living/construct)
if(infinite_resources) //Allow them to build stuff and recite scripture
diff --git a/code/game/gamemodes/clock_cult/clock_mobs/_eminence.dm b/code/game/gamemodes/clock_cult/clock_mobs/_eminence.dm
index cc91feeb98..8416e4651d 100644
--- a/code/game/gamemodes/clock_cult/clock_mobs/_eminence.dm
+++ b/code/game/gamemodes/clock_cult/clock_mobs/_eminence.dm
@@ -14,16 +14,6 @@
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
var/static/superheated_walls = 0
-/mob/camera/eminence/Initialize()
- if(SSticker.mode.eminence)
- return INITIALIZE_HINT_QDEL
- . = ..()
-
-/mob/camera/eminence/Destroy(force)
- if(!force && mind && SSticker.mode.eminence == mind)
- return QDEL_HINT_LETMELIVE
- return ..()
-
/mob/camera/eminence/CanPass(atom/movable/mover, turf/target)
return TRUE
@@ -39,12 +29,20 @@
/mob/camera/eminence/Login()
..()
- add_servant_of_ratvar(src, TRUE)
+ var/datum/antagonist/clockcult/C = mind.has_antag_datum(/datum/antagonist/clockcult,TRUE)
+ if(!C)
+ add_servant_of_ratvar(src, TRUE)
+ C = mind.has_antag_datum(/datum/antagonist/clockcult,TRUE)
+ if(C && C.clock_team)
+ if(C.clock_team.eminence)
+ remove_servant_of_ratvar(src,TRUE)
+ qdel(src)
+ else
+ C.clock_team.eminence = src
to_chat(src, "
You have been selected as the Eminence!")
to_chat(src, "
As the Eminence, you lead the servants. Anything you say will be heard by the entire cult.")
to_chat(src, "
Though you can move through walls, you're also incorporeal, and largely can't interact with the world except for a few ways.")
to_chat(src, "
Additionally, unless the herald's beacon is activated, you can't understand any speech while away from Reebe.")
- SSticker.mode.eminence = mind
eminence_help()
for(var/V in actions)
var/datum/action/A = V
diff --git a/code/game/gamemodes/clock_cult/clock_structures/eminence_spire.dm b/code/game/gamemodes/clock_cult/clock_structures/eminence_spire.dm
index 8d4e936658..495bfaeaa8 100644
--- a/code/game/gamemodes/clock_cult/clock_structures/eminence_spire.dm
+++ b/code/game/gamemodes/clock_cult/clock_structures/eminence_spire.dm
@@ -17,7 +17,11 @@
return
if(kingmaking)
return
- if(SSticker.mode.eminence)
+
+ var/datum/antagonist/clockcult/C = user.mind.has_antag_datum(/datum/antagonist/clockcult)
+ if(!C || !C.clock_team)
+ return
+ if(C.clock_team.eminence)
to_chat(user, "
There's already an Eminence!")
return
if(!GLOB.servants_active)
@@ -34,7 +38,9 @@
/obj/structure/destructible/clockwork/eminence_spire/attack_ghost(mob/user)
if(!IsAdminGhost(user))
return
- if(SSticker.mode.eminence)
+
+ var/datum/antagonist/clockcult/random_cultist = locate() in GLOB.antagonists //if theres no cultists new team without eminence will be created anyway.
+ if(random_cultist && random_cultist.clock_team && random_cultist.clock_team.eminence)
to_chat(user, "
There's already an Eminence - too late!")
return
if(!GLOB.servants_active)
diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm
index f20dd25802..67e2225772 100644
--- a/code/game/gamemodes/cult/cult.dm
+++ b/code/game/gamemodes/cult/cult.dm
@@ -2,24 +2,23 @@
/datum/game_mode
var/list/datum/mind/cult = list()
- var/list/cult_objectives = list()
- var/eldergod = 1 //for the summon god objective
/proc/iscultist(mob/living/M)
return istype(M) && M.mind && M.mind.has_antag_datum(ANTAG_DATUM_CULT)
-/proc/is_sacrifice_target(datum/mind/mind)
- if(mind == GLOB.sac_mind)
- return TRUE
+/datum/objective_team/cult/proc/is_sacrifice_target(datum/mind/mind)
+ for(var/datum/objective/sacrifice/sac_objective in objectives)
+ if(mind == sac_objective.target)
+ return TRUE
return FALSE
-
-/proc/is_convertable_to_cult(mob/living/M)
+
+/proc/is_convertable_to_cult(mob/living/M,datum/objective_team/cult/specific_cult)
if(!istype(M))
return FALSE
if(M.mind)
if(ishuman(M) && (M.mind.assigned_role in list("Captain", "Chaplain")))
return FALSE
- if(is_sacrifice_target(M.mind))
+ if(specific_cult && specific_cult.is_sacrifice_target(M.mind))
return FALSE
if(M.mind.enslaved_to && !iscultist(M.mind.enslaved_to))
return FALSE
@@ -55,10 +54,10 @@
var/list/cultists_to_cult = list() //the cultists we'll convert
+ var/datum/objective_team/cult/main_cult
+
/datum/game_mode/cult/pre_setup()
- cult_objectives += "sacrifice"
-
if(CONFIG_GET(flag/protect_roles_from_antagonist))
restricted_jobs += protected_jobs
@@ -86,82 +85,19 @@
/datum/game_mode/cult/post_setup()
- if("sacrifice" in cult_objectives)
- var/list/possible_targets = get_unconvertables()
- if(!possible_targets.len)
- message_admins("Cult Sacrifice: Could not find unconvertable target, checking for convertable target.")
- for(var/mob/living/carbon/human/player in GLOB.player_list)
- if(player.mind && !(player.mind in cultists_to_cult))
- possible_targets += player.mind
- if(possible_targets.len > 0)
- GLOB.sac_mind = pick(possible_targets)
- if(!GLOB.sac_mind)
- message_admins("Cult Sacrifice: ERROR - Null target chosen!")
- else
- var/datum/job/sacjob = SSjob.GetJob(GLOB.sac_mind.assigned_role)
- var/datum/preferences/sacface = GLOB.sac_mind.current.client.prefs
- var/icon/reshape = get_flat_human_icon(null, sacjob, sacface)
- reshape.Shift(SOUTH, 4)
- reshape.Shift(EAST, 1)
- reshape.Crop(7,4,26,31)
- reshape.Crop(-5,-3,26,30)
- GLOB.sac_image = reshape
- else
- message_admins("Cult Sacrifice: Could not find unconvertable or convertable target. WELP!")
- if(!GLOB.summon_spots.len)
- while(GLOB.summon_spots.len < SUMMON_POSSIBILITIES)
- var/area/summon = pick(GLOB.sortedAreas - GLOB.summon_spots)
- if((summon.z in GLOB.station_z_levels) && summon.valid_territory)
- GLOB.summon_spots += summon
- cult_objectives += "eldergod"
-
for(var/datum/mind/cult_mind in cultists_to_cult)
- equip_cultist(cult_mind.current)
- update_cult_icons_added(cult_mind)
- to_chat(cult_mind.current, "
You are a member of the cult!")
- cult_mind.current.playsound_local(get_turf(cult_mind.current), 'sound/ambience/antag/bloodcult.ogg', 100, FALSE, pressure_affected = FALSE)//subject to change
- add_cultist(cult_mind, 0)
+ add_cultist(cult_mind, 0, equip=TRUE)
..()
-/datum/game_mode/proc/equip_cultist(mob/living/carbon/human/mob,tome = 0)
- if(!istype(mob))
- return
- if (mob.mind)
- if (mob.mind.assigned_role == "Clown")
- to_chat(mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
- mob.dna.remove_mutation(CLOWNMUT)
- if(tome)
- . += cult_give_item(/obj/item/tome, mob)
- else
- . += cult_give_item(/obj/item/paper/talisman/supply, mob)
- to_chat(mob, "These will help you start the cult on this station. Use them well, and remember - you are not the only one.")
-
-/datum/game_mode/proc/cult_give_item(obj/item/item_path, mob/living/carbon/human/mob)
- var/list/slots = list(
- "backpack" = slot_in_backpack,
- "left pocket" = slot_l_store,
- "right pocket" = slot_r_store
- )
-
- var/T = new item_path(mob)
- var/item_name = initial(item_path.name)
- var/where = mob.equip_in_one_of_slots(T, slots)
- if(!where)
- to_chat(mob, "
Unfortunately, you weren't able to get a [item_name]. This is very bad and you should adminhelp immediately (press F1).")
- return 0
- else
- to_chat(mob, "
You have a [item_name] in your [where].")
- if(where == "backpack")
- var/obj/item/storage/B = mob.back
- B.orient2hud(mob)
- B.show_to(mob)
- return 1
-
-/datum/game_mode/proc/add_cultist(datum/mind/cult_mind, stun) //BASE
+/datum/game_mode/proc/add_cultist(datum/mind/cult_mind, stun , equip = FALSE) //BASE
if (!istype(cult_mind))
return 0
- if(cult_mind.add_antag_datum(ANTAG_DATUM_CULT))
+
+ var/datum/antagonist/cult/new_cultist = new(cult_mind)
+ new_cultist.give_equipment = equip
+
+ if(cult_mind.add_antag_datum(new_cultist))
if(stun)
cult_mind.current.Unconscious(100)
return 1
@@ -187,25 +123,19 @@
culthud.leave_hud(cult_mind.current)
set_antag_hud(cult_mind.current, null)
-/datum/game_mode/cult/proc/get_unconvertables()
- var/list/ucs = list()
- for(var/mob/living/carbon/human/player in GLOB.player_list)
- if(player.mind && !is_convertable_to_cult(player) && !(player.mind in cultists_to_cult))
- ucs += player.mind
- return ucs
-
/datum/game_mode/cult/proc/check_cult_victory()
- var/cult_fail = 0
- if(cult_objectives.Find("survive"))
- cult_fail += check_survive() //the proc returns 1 if there are not enough cultists on the shuttle, 0 otherwise
- if(cult_objectives.Find("eldergod"))
- cult_fail += eldergod //1 by default, 0 if the elder god has been summoned at least once
- if(cult_objectives.Find("sacrifice"))
- if(GLOB.sac_mind && !GLOB.sac_complete) //if the target has been GLOB.sacrificed, ignore this step. otherwise, add 1 to cult_fail
- cult_fail++
- return cult_fail //if any objectives aren't met, failure
+ return main_cult.check_cult_victory()
+/datum/game_mode/cult/set_round_result()
+ ..()
+ if(check_cult_victory())
+ SSticker.mode_result = "win - cult win"
+ SSticker.news_report = CULT_SUMMON
+ else
+ SSticker.mode_result = "loss - staff stopped the cult"
+ SSticker.news_report = CULT_FAILURE
+
/datum/game_mode/cult/proc/check_survive()
var/acolytes_survived = 0
for(var/datum/mind/cult_mind in cult)
@@ -218,57 +148,6 @@
return 1
-/datum/game_mode/cult/declare_completion()
-
- if(!check_cult_victory())
- SSticker.mode_result = "win - cult win"
- to_chat(world, "
The cult has succeeded! Nar-sie has snuffed out another torch in the void!")
- else
- SSticker.mode_result = "loss - staff stopped the cult"
- to_chat(world, "
The staff managed to stop the cult! Dark words and heresy are no match for Nanotrasen's finest!")
-
- var/text = ""
-
- if(cult_objectives.len)
- text += "
The cultists' objectives were:"
- for(var/obj_count=1, obj_count <= cult_objectives.len, obj_count++)
- var/explanation
- switch(cult_objectives[obj_count])
- if("survive")
- if(!check_survive())
- explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. ([acolytes_survived] escaped)
Success!"
- SSblackbox.record_feedback("nested tally", "cult_objective", 1, list("cult_survive", "SUCCESS"))
- SSticker.news_report = CULT_ESCAPE
- else
- explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. ([acolytes_survived] escaped)
Fail."
- SSblackbox.record_feedback("nested tally", "cult_objective", 1, list("cult_survive", "FAIL"))
- SSticker.news_report = CULT_FAILURE
- if("sacrifice")
- if(GLOB.sac_complete)
- explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role].
Success!"
- SSblackbox.record_feedback("nested tally", "cult_objective", 1, list("cult_sacrifice", "SUCCESS"))
- else
- explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role].
Fail."
- SSblackbox.record_feedback("nested tally", "cult_objective", 1, list("cult_sacrifice", "FAIL"))
- if("eldergod")
- if(!eldergod)
- explanation = "Summon Nar-Sie. The summoning can only be accomplished in [english_list(GLOB.summon_spots)].
Success!"
- SSblackbox.record_feedback("nested tally", "cult_objective", 1, list("cult_narsie", "SUCCESS"))
- SSticker.news_report = CULT_SUMMON
- else
- explanation = "Summon Nar-Sie. The summoning can only be accomplished in [english_list(GLOB.summon_spots)]
Fail."
- SSblackbox.record_feedback("nested tally", "cult_objective", 1, list("cult_narsie", "FAIL"))
- SSticker.news_report = CULT_FAILURE
-
- text += "
Objective #[obj_count]: [explanation]"
- if(cult.len)
- text += "
The cultists were:"
- for(var/datum/mind/M in cult)
- text += printplayer(M)
- to_chat(world, text)
- ..()
- return 1
-
/datum/game_mode/cult/generate_report()
return "Some stations in your sector have reported evidence of blood sacrifice and strange magic. Ties to the Wizards' Federation have been proven not to exist, and many employees \
have disappeared; even Central Command employees light-years away have felt strange presences and at times hysterical compulsions. Interrogations point towards this being the work of \
@@ -276,41 +155,4 @@
devoted to stopping this cult. Note that holy water seems to weaken and eventually return the minds of cultists that ingest it, and mindshield implants will prevent conversion \
altogether."
-/datum/game_mode/proc/datum_cult_completion()
- var/text = ""
- var/cult_fail = 0
- cult_fail += eldergod
- if(!GLOB.sac_complete)
- cult_fail++
- if(!cult_fail)
- SSticker.mode_result = "win - cult win"
- to_chat(world, "
The cult has succeeded! Nar-sie has snuffed out another torch in the void!")
- else
- SSticker.mode_result = "loss - staff stopped the cult"
- to_chat(world, "
The staff managed to stop the cult! Dark words and heresy are no match for Nanotrasen's finest!")
- if(cult_objectives.len)
- text += "
The cultists' objectives were:"
- for(var/obj_count in 1 to 2)
- var/explanation
- switch(cult_objectives[obj_count])
- if("sacrifice")
- if(GLOB.sac_mind)
- if(GLOB.sac_complete)
- explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role].
Success!"
- SSblackbox.record_feedback("nested tally", "cult_objective", 1, list("cult_sacrifice", "SUCCESS"))
- else
- explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role].
Fail."
- SSblackbox.record_feedback("nested tally", "cult_objective", 1, list("cult_sacrifice", "FAIL"))
- if("eldergod")
- if(!eldergod)
- explanation = "Summon Nar-Sie.
Success!"
- SSblackbox.record_feedback("nested tally", "cult_objective", 1, list("cult_narsie", "SUCCESS"))
- SSticker.news_report = CULT_SUMMON
- else
- explanation = "Summon Nar-Sie.
Fail."
- SSblackbox.record_feedback("nested tally", "cult_objective", 1, list("cult_narsie", "FAIL"))
- SSticker.news_report = CULT_FAILURE
- text += "
Objective #[obj_count]: [explanation]"
- to_chat(world, text)
-
#undef CULT_SCALING_COEFFICIENT
diff --git a/code/game/gamemodes/cult/cult_comms.dm b/code/game/gamemodes/cult/cult_comms.dm
index 530b6ebb20..2938f5abf4 100644
--- a/code/game/gamemodes/cult/cult_comms.dm
+++ b/code/game/gamemodes/cult/cult_comms.dm
@@ -88,19 +88,21 @@
button_icon_state = "cultvote"
/datum/action/innate/cult/mastervote/IsAvailable()
- if(GLOB.cult_vote_called || !ishuman(owner))
+ var/datum/antagonist/cult/C = owner.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
+ if(!C || C.cult_team.cult_vote_called || !ishuman(owner))
return FALSE
return ..()
/datum/action/innate/cult/mastervote/Activate()
- pollCultists(owner)
+ var/datum/antagonist/cult/C = owner.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
+ pollCultists(owner,C.cult_team)
-/proc/pollCultists(var/mob/living/Nominee) //Cult Master Poll
+/proc/pollCultists(var/mob/living/Nominee,datum/objective_team/cult/team) //Cult Master Poll
if(world.time < CULT_POLL_WAIT)
to_chat(Nominee, "It would be premature to select a leader while everyone is still settling in, try again in [DisplayTimeText(CULT_POLL_WAIT-world.time)].")
return
- GLOB.cult_vote_called = TRUE //somebody's trying to be a master, make sure we don't let anyone else try
- for(var/datum/mind/B in SSticker.mode.cult)
+ team.cult_vote_called = TRUE //somebody's trying to be a master, make sure we don't let anyone else try
+ for(var/datum/mind/B in team.members)
if(B.current)
B.current.update_action_buttons_icon()
if(!B.current.incapacitated())
@@ -108,39 +110,39 @@
to_chat(B.current, "
Acolyte [Nominee] has asserted that they are worthy of leading the cult. A vote will be called shortly.")
sleep(100)
var/list/asked_cultists = list()
- for(var/datum/mind/B in SSticker.mode.cult)
+ for(var/datum/mind/B in team.members)
if(B.current && B.current != Nominee && !B.current.incapacitated())
SEND_SOUND(B.current, 'sound/magic/exit_blood.ogg')
asked_cultists += B.current
var/list/yes_voters = pollCandidates("[Nominee] seeks to lead your cult, do you support [Nominee.p_them()]?", poll_time = 300, group = asked_cultists)
if(QDELETED(Nominee) || Nominee.incapacitated())
- GLOB.cult_vote_called = FALSE
- for(var/datum/mind/B in SSticker.mode.cult)
+ team.cult_vote_called = FALSE
+ for(var/datum/mind/B in team.members)
if(B.current)
B.current.update_action_buttons_icon()
if(!B.current.incapacitated())
to_chat(B.current,"
[Nominee] has died in the process of attempting to win the cult's support!")
return FALSE
if(!Nominee.mind)
- GLOB.cult_vote_called = FALSE
- for(var/datum/mind/B in SSticker.mode.cult)
+ team.cult_vote_called = FALSE
+ for(var/datum/mind/B in team.members)
if(B.current)
B.current.update_action_buttons_icon()
if(!B.current.incapacitated())
to_chat(B.current,"
[Nominee] has gone catatonic in the process of attempting to win the cult's support!")
return FALSE
if(LAZYLEN(yes_voters) <= LAZYLEN(asked_cultists) * 0.5)
- GLOB.cult_vote_called = FALSE
- for(var/datum/mind/B in SSticker.mode.cult)
+ team.cult_vote_called = FALSE
+ for(var/datum/mind/B in team.members)
if(B.current)
B.current.update_action_buttons_icon()
if(!B.current.incapacitated())
to_chat(B.current, "
[Nominee] could not win the cult's support and shall continue to serve as an acolyte.")
return FALSE
- GLOB.cult_mastered = TRUE
+ team.cult_mastered = TRUE
SSticker.mode.remove_cultist(Nominee.mind, TRUE)
Nominee.mind.add_antag_datum(ANTAG_DATUM_CULT_MASTER)
- for(var/datum/mind/B in SSticker.mode.cult)
+ for(var/datum/mind/B in team.members)
if(B.current)
for(var/datum/action/innate/cult/mastervote/vote in B.current.actions)
vote.Remove(B.current)
@@ -159,6 +161,9 @@
button_icon_state = "sintouch"
/datum/action/innate/cult/master/finalreck/Activate()
+ var/datum/antagonist/cult/antag = owner.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
+ if(!antag)
+ return
for(var/i in 1 to 4)
chant(i)
var/list/destinations = list()
@@ -169,7 +174,7 @@
to_chat(owner, "
You need more space to summon the cult!")
return
if(do_after(owner, 30, target = owner))
- for(var/datum/mind/B in SSticker.mode.cult)
+ for(var/datum/mind/B in antag.cult_team.members)
if(B.current && B.current.stat != DEAD)
var/turf/mobloc = get_turf(B.current)
switch(i)
@@ -194,7 +199,7 @@
addtimer(CALLBACK(B.current, /mob/.proc/reckon, final), 10)
else
return
- GLOB.reckoning_complete = TRUE
+ antag.cult_team.reckoning_complete = TRUE
Remove(owner)
/mob/proc/reckon(turf/final)
@@ -269,34 +274,37 @@
var/turf/T = get_turf(ranged_ability_user)
if(!isturf(T))
return FALSE
+
+ var/datum/antagonist/cult/C = caller.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
+
if(target in view(7, get_turf(ranged_ability_user)))
- GLOB.blood_target = target
+ C.cult_team.blood_target = target
var/area/A = get_area(target)
attached_action.cooldown = world.time + attached_action.base_cooldown
addtimer(CALLBACK(attached_action.owner, /mob.proc/update_action_buttons_icon), attached_action.base_cooldown)
- GLOB.blood_target_image = image('icons/effects/cult_target.dmi', target, "glow", ABOVE_MOB_LAYER)
- GLOB.blood_target_image.appearance_flags = RESET_COLOR
- GLOB.blood_target_image.pixel_x = -target.pixel_x
- GLOB.blood_target_image.pixel_y = -target.pixel_y
+ C.cult_team.blood_target_image = image('icons/effects/cult_target.dmi', target, "glow", ABOVE_MOB_LAYER)
+ C.cult_team.blood_target_image.appearance_flags = RESET_COLOR
+ C.cult_team.blood_target_image.pixel_x = -target.pixel_x
+ C.cult_team.blood_target_image.pixel_y = -target.pixel_y
for(var/datum/mind/B in SSticker.mode.cult)
if(B.current && B.current.stat != DEAD && B.current.client)
- to_chat(B.current, "
Master [ranged_ability_user] has marked [GLOB.blood_target] in the [A.name] as the cult's top priority, get there immediately!")
+ to_chat(B.current, "
Master [ranged_ability_user] has marked [C.cult_team.blood_target] in the [A.name] as the cult's top priority, get there immediately!")
SEND_SOUND(B.current, sound(pick('sound/hallucinations/over_here2.ogg','sound/hallucinations/over_here3.ogg'),0,1,75))
- B.current.client.images += GLOB.blood_target_image
+ B.current.client.images += C.cult_team.blood_target_image
attached_action.owner.update_action_buttons_icon()
remove_ranged_ability("
The marking rite is complete! It will last for 90 seconds.")
- GLOB.blood_target_reset_timer = addtimer(CALLBACK(GLOBAL_PROC, .proc/reset_blood_target), 900, TIMER_STOPPABLE)
+ C.cult_team.blood_target_reset_timer = addtimer(CALLBACK(GLOBAL_PROC, .proc/reset_blood_target,C.cult_team), 900, TIMER_STOPPABLE)
return TRUE
return FALSE
-/proc/reset_blood_target()
- for(var/datum/mind/B in SSticker.mode.cult)
+/proc/reset_blood_target(datum/objective_team/cult/team)
+ for(var/datum/mind/B in team.members)
if(B.current && B.current.stat != DEAD && B.current.client)
- if(GLOB.blood_target)
+ if(team.blood_target)
to_chat(B.current,"
The blood mark has expired!")
- B.current.client.images -= GLOB.blood_target_image
- QDEL_NULL(GLOB.blood_target_image)
- GLOB.blood_target = null
+ B.current.client.images -= team.blood_target_image
+ QDEL_NULL(team.blood_target_image)
+ team.blood_target = null
diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm
index 2adaa57a2f..f0934b524f 100644
--- a/code/game/gamemodes/cult/ritual.dm
+++ b/code/game/gamemodes/cult/ritual.dm
@@ -179,6 +179,13 @@ This file contains the arcane tome files.
var/list/shields = list()
var/area/A = get_area(src)
+ var/datum/antagonist/cult/user_antag = user.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
+ if(!user_antag)
+ return
+
+ var/datum/objective/eldergod/summon_objective = locate() in user_antag.cult_team.objectives
+ var/datum/objective/sacrifice/sac_objective = locate() in user_antag.cult_team.objectives
+
if(!check_rune_turf(Turf, user))
return
entered_rune_name = input(user, "Choose a rite to scribe.", "Sigils of Power") as null|anything in GLOB.rune_types
@@ -196,18 +203,20 @@ This file contains the arcane tome files.
A = get_area(src)
if(!src || QDELETED(src) || !Adjacent(user) || user.incapacitated() || !check_rune_turf(Turf, user))
return
+
+ //AAAAAAAAAAAAAAAH, i'm rewriting enough for now so TODO: remove this shit
if(ispath(rune_to_scribe, /obj/effect/rune/narsie))
- if(!("eldergod" in SSticker.mode.cult_objectives))
+ if(!summon_objective)
to_chat(user, "
Nar-Sie does not wish to be summoned!")
return
- if(!GLOB.sac_complete)
+ if(sac_objective && !sac_objective.check_completion())
to_chat(user, "
The sacrifice is not complete. The portal would lack the power to open if you tried!")
return
- if(!SSticker.mode.eldergod)
+ if(summon_objective.check_completion())
to_chat(user, "
\"I am already here. There is no need to try to summon me now.\"")
return
- if(!(A in GLOB.summon_spots))
- to_chat(user, "
The Geometer can only be summoned where the veil is weak - in [english_list(GLOB.summon_spots)]!")
+ if(!(A in summon_objective.summon_spots))
+ to_chat(user, "
The Geometer can only be summoned where the veil is weak - in [english_list(summon_objective.summon_spots)]!")
return
var/confirm_final = alert(user, "This is the FINAL step to summon Nar-Sie; it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for Nar-Sie!", "No")
if(confirm_final == "No")
@@ -215,8 +224,8 @@ This file contains the arcane tome files.
return
Turf = get_turf(user)
A = get_area(src)
- if(!(A in GLOB.summon_spots)) // Check again to make sure they didn't move
- to_chat(user, "
The Geometer can only be summoned where the veil is weak - in [english_list(GLOB.summon_spots)]!")
+ if(!(A in summon_objective.summon_spots)) // Check again to make sure they didn't move
+ to_chat(user, "
The Geometer can only be summoned where the veil is weak - in [english_list(summon_objective.summon_spots)]!")
return
priority_announce("Figments from an eldritch god are being summoned by [user] into [A.map_name] from an unknown dimension. Disrupt the ritual at all costs!","Central Command Higher Dimensional Affairs", 'sound/ai/spanomalies.ogg')
for(var/B in spiral_range_turfs(1, user, 1))
diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm
index be1c2ccaff..1bab1d90f4 100644
--- a/code/game/gamemodes/cult/runes.dm
+++ b/code/game/gamemodes/cult/runes.dm
@@ -349,7 +349,11 @@ structure_check() searches for nearby cultist structures required for the invoca
color = RUNE_COLOR_DARKRED
var/mob/living/L = pick(myriad_targets)
var/is_clock = is_servant_of_ratvar(L)
- var/is_convertable = is_convertable_to_cult(L)
+
+ var/mob/living/F = invokers[1]
+ var/datum/antagonist/cult/C = F.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
+
+ var/is_convertable = is_convertable_to_cult(L,C.cult_team)
if(L.stat != DEAD && (is_clock || is_convertable))
invocation = "Mah'weyh pleggh at e'ntrath!"
..()
@@ -397,17 +401,27 @@ structure_check() searches for nearby cultist structures required for the invoca
return 1
/obj/effect/rune/convert/proc/do_sacrifice(mob/living/sacrificial, list/invokers)
+ var/mob/living/first_invoker = invokers[1]
+ if(!first_invoker)
+ return FALSE
+ var/datum/antagonist/cult/C = first_invoker.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
+ if(!C)
+ return
+
+
var/big_sac = FALSE
- if((((ishuman(sacrificial) || iscyborg(sacrificial)) && sacrificial.stat != DEAD) || is_sacrifice_target(sacrificial.mind)) && invokers.len < 3)
+ if((((ishuman(sacrificial) || iscyborg(sacrificial)) && sacrificial.stat != DEAD) || C.cult_team.is_sacrifice_target(sacrificial.mind)) && invokers.len < 3)
for(var/M in invokers)
to_chat(M, "
[sacrificial] is too greatly linked to the world! You need three acolytes!")
log_game("Offer rune failed - not enough acolytes and target is living or sac target")
return FALSE
if(sacrificial.mind)
GLOB.sacrificed += sacrificial.mind
- if(is_sacrifice_target(sacrificial.mind))
- GLOB.sac_complete = TRUE
- big_sac = TRUE
+ for(var/datum/objective/sacrifice/sac_objective in C.cult_team.objectives)
+ if(sac_objective.target == sacrificial.mind)
+ sac_objective.sacced = TRUE
+ sac_objective.update_explanation_text()
+ big_sac = TRUE
else
GLOB.sacrificed += sacrificial
@@ -481,7 +495,6 @@ structure_check() searches for nearby cultist structures required for the invoca
sleep(40)
if(src)
color = RUNE_COLOR_RED
- SSticker.mode.eldergod = FALSE
new /obj/singularity/narsie/large/cult(T) //Causes Nar-Sie to spawn even if the rune has been removed
/obj/effect/rune/narsie/attackby(obj/I, mob/user, params) //Since the narsie rune takes a long time to make, add logging to removal.
diff --git a/code/game/gamemodes/devil/game_mode.dm b/code/game/gamemodes/devil/game_mode.dm
index 957e261933..44f3368feb 100644
--- a/code/game/gamemodes/devil/game_mode.dm
+++ b/code/game/gamemodes/devil/game_mode.dm
@@ -3,32 +3,6 @@
var/list/datum/mind/devils = list()
var/devil_ascended = 0 // Number of arch devils on station
-/datum/game_mode/proc/auto_declare_completion_sintouched()
- var/text = ""
- if(sintouched.len)
- text += "
The sintouched were:"
- var/list/sintouchedUnique = uniqueList(sintouched)
- for(var/S in sintouchedUnique)
- var/datum/mind/sintouched_mind = S
- text += printplayer(sintouched_mind)
- text += printobjectives(sintouched_mind)
- text += "
"
- text += "
"
- to_chat(world, text)
-
-/datum/game_mode/proc/auto_declare_completion_devils()
- /var/text = ""
- if(devils.len)
- text += "
The devils were:"
- for(var/D in devils)
- var/datum/mind/devil = D
- text += printplayer(devil)
- text += printdevilinfo(devil.current)
- text += printobjectives(devil)
- text += "
"
- text += "
"
- to_chat(world, text)
-
/datum/game_mode/proc/add_devil_objectives(datum/mind/devil_mind, quantity)
var/list/validtypes = list(/datum/objective/devil/soulquantity, /datum/objective/devil/soulquality, /datum/objective/devil/sintouch, /datum/objective/devil/buy_target)
for(var/i = 1 to quantity)
@@ -41,18 +15,6 @@
else
objective.find_target()
-/datum/game_mode/proc/printdevilinfo(mob/living/ply)
- var/datum/antagonist/devil/devilinfo = is_devil(ply)
- if(!devilinfo)
- return "Target is not a devil."
- var/text = "The devil's true name is: [devilinfo.truename]"
- text += "The devil's bans were:"
- text += " [GLOB.lawlorify[LORE][devilinfo.ban]]"
- text += " [GLOB.lawlorify[LORE][devilinfo.bane]]"
- text += " [GLOB.lawlorify[LORE][devilinfo.obligation]]"
- text += " [GLOB.lawlorify[LORE][devilinfo.banish]]"
- return text
-
/datum/game_mode/proc/update_devil_icons_added(datum/mind/devil_mind)
var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_DEVIL]
hud.join_hud(devil_mind.current)
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 803e73b9e0..0c6a8fd1e0 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -73,7 +73,6 @@
/datum/game_mode/proc/pre_setup()
return 1
-
///Everyone should now be on the station and have their normal gear. This is the place to give the special roles extra things
/datum/game_mode/proc/post_setup(report) //Gamemodes can override the intercept report. Passing TRUE as the argument will force a report.
if(!report)
@@ -241,55 +240,9 @@
return 0
-/datum/game_mode/proc/declare_completion()
- var/clients = 0
- var/surviving_humans = 0
- var/surviving_total = 0
- var/ghosts = 0
- var/escaped_humans = 0
- var/escaped_total = 0
-
- for(var/mob/M in GLOB.player_list)
- if(M.client)
- clients++
- if(ishuman(M))
- if(!M.stat)
- surviving_humans++
- if(M.z == ZLEVEL_CENTCOM)
- escaped_humans++
- if(!M.stat)
- surviving_total++
- if(M.z == ZLEVEL_CENTCOM)
- escaped_total++
-
-
- if(isobserver(M))
- ghosts++
-
- if(clients)
- SSblackbox.record_feedback("nested tally", "round_end_stats", clients, list("clients"))
- if(ghosts)
- SSblackbox.record_feedback("nested tally", "round_end_stats", ghosts, list("ghosts"))
- if(surviving_humans)
- SSblackbox.record_feedback("nested tally", "round_end_stats", surviving_humans, list("survivors", "human"))
- if(surviving_total)
- SSblackbox.record_feedback("nested tally", "round_end_stats", surviving_total, list("survivors", "total"))
- if(escaped_humans)
- SSblackbox.record_feedback("nested tally", "round_end_stats", escaped_humans, list("escapees", "human"))
- if(escaped_total)
- SSblackbox.record_feedback("nested tally", "round_end_stats", escaped_total, list("escapees", "total"))
-
- send2irc("Server", "Round just ended.")
- if(cult.len && !istype(SSticker.mode, /datum/game_mode/cult))
- datum_cult_completion()
-
- return 0
-
-
/datum/game_mode/proc/check_win() //universal trigger to be called at mob death, nuke explosion, etc. To be called from everywhere.
return 0
-
/datum/game_mode/proc/send_intercept()
var/intercepttext = "
Central Command Status Summary
"
intercepttext += "
Central Command has intercepted and partially decoded a Syndicate transmission with vital information regarding their movements. The following report outlines the most \
@@ -451,34 +404,6 @@
for (var/C in GLOB.admins)
to_chat(C, msg)
-/datum/game_mode/proc/printplayer(datum/mind/ply, fleecheck)
- var/text = "
[ply.key] was [ply.name] the [ply.assigned_role] and"
- if(ply.current)
- if(ply.current.stat == DEAD)
- text += " died"
- else
- text += " survived"
- if(fleecheck)
- var/turf/T = get_turf(ply.current)
- if(!T || !(T.z in GLOB.station_z_levels))
- text += " while fleeing the station"
- if(ply.current.real_name != ply.name)
- text += " as [ply.current.real_name]"
- else
- text += " had their body destroyed"
- return text
-
-/datum/game_mode/proc/printobjectives(datum/mind/ply)
- var/text = ""
- var/count = 1
- for(var/datum/objective/objective in ply.objectives)
- if(objective.check_completion())
- text += "
Objective #[count]: [objective.explanation_text] Success!"
- else
- text += "
Objective #[count]: [objective.explanation_text] Fail."
- count++
- return text
-
//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1
/datum/game_mode/proc/age_check(client/C)
if(get_remaining_days(C) == 0)
@@ -518,11 +443,6 @@
station_goals += new picked
-/datum/game_mode/proc/declare_station_goal_completion()
- for(var/V in station_goals)
- var/datum/station_goal/G = V
- G.print_result()
-
/datum/game_mode/proc/generate_report() //Generates a small text blurb for the gamemode in centcom report
return "Gamemode report for [name] not set. Contact a coder."
@@ -530,3 +450,20 @@
/datum/game_mode/proc/OnNukeExplosion(off_station)
if(off_station < 2)
station_was_nuked = TRUE //Will end the round on next check.
+<<<<<<< HEAD
+=======
+
+//Additional report section in roundend report
+/datum/game_mode/proc/special_report()
+ return
+
+//Set result and news report here
+/datum/game_mode/proc/set_round_result()
+ SSticker.mode_result = "undefined"
+ if(station_was_nuked)
+ SSticker.news_report = STATION_DESTROYED_NUKE
+ if(EMERGENCY_ESCAPED_OR_ENDGAMED)
+ SSticker.news_report = STATION_EVACUATED
+ if(SSshuttle.emergency.is_hijacked())
+ SSticker.news_report = SHUTTLE_HIJACK
+>>>>>>> 3d81385... Roundend report refactor (#33246)
diff --git a/code/game/gamemodes/meteor/meteor.dm b/code/game/gamemodes/meteor/meteor.dm
index b7a6580570..f8e0666015 100644
--- a/code/game/gamemodes/meteor/meteor.dm
+++ b/code/game/gamemodes/meteor/meteor.dm
@@ -30,30 +30,29 @@
spawn_meteors(ramp_up_final, wavetype)
-/datum/game_mode/meteor/declare_completion()
- var/text
+/datum/game_mode/meteor/special_report()
var/survivors = 0
+ var/list/survivor_list = list()
for(var/mob/living/player in GLOB.player_list)
if(player.stat != DEAD)
++survivors
if(player.onCentCom())
- text += "
[player.real_name] escaped to the safety of CentCom."
+ survivor_list += "[player.real_name] escaped to the safety of CentCom."
else if(player.onSyndieBase())
- text += "
[player.real_name] escaped to the (relative) safety of Syndicate Space."
+ survivor_list += "[player.real_name] escaped to the (relative) safety of Syndicate Space."
else
- text += "
[player.real_name] survived but is stranded without any hope of rescue."
-
+ survivor_list += "[player.real_name] survived but is stranded without any hope of rescue."
if(survivors)
- to_chat(world, "The following survived the meteor storm:[text]")
+ return "
[survivor_list.Join("
")]"
else
- to_chat(world, "Nobody survived the meteor storm!")
+ return "Nobody survived the meteor storm!"
- SSticker.mode_result = "end - evacuation"
+/datum/game_mode/meteor/set_round_result()
..()
- return 1
+ SSticker.mode_result = "end - evacuation"
/datum/game_mode/meteor/generate_report()
return "[pick("Asteroids have", "Meteors have", "Large rocks have", "Stellar minerals have", "Space hail has", "Debris has")] been detected near your station, and a collision is possible, \
diff --git a/code/game/gamemodes/miniantags/abduction/abduction.dm b/code/game/gamemodes/miniantags/abduction/abduction.dm
index 4eb8642c02..050cac443d 100644
--- a/code/game/gamemodes/miniantags/abduction/abduction.dm
+++ b/code/game/gamemodes/miniantags/abduction/abduction.dm
@@ -1,19 +1,5 @@
-/datum/objective_team/abductor_team
- member_name = "abductor"
- var/list/objectives = list()
- var/team_number
-
-/datum/objective_team/abductor_team/is_solo()
- return FALSE
-
-/datum/objective_team/abductor_team/proc/add_objective(datum/objective/O)
- O.team = src
- O.update_explanation_text()
- objectives += O
-
/datum/game_mode
var/list/datum/mind/abductors = list()
- var/list/datum/mind/abductees = list()
/datum/game_mode/abduction
name = "abduction"
@@ -99,35 +85,6 @@
return ..()
return ..()
-/datum/game_mode/abduction/declare_completion()
- for(var/datum/objective_team/abductor_team/team in abductor_teams)
- var/won = TRUE
- for(var/datum/objective/O in team.objectives)
- if(!O.check_completion())
- won = FALSE
- if(won)
- to_chat(world, "[team.name] team fulfilled its mission!")
- else
- to_chat(world, "[team.name] team failed its mission.")
- ..()
- return TRUE
-
-/datum/game_mode/proc/auto_declare_completion_abduction()
- var/text = ""
- if(abductors.len)
- text += "
The abductors were:"
- for(var/datum/mind/abductor_mind in abductors)
- text += printplayer(abductor_mind)
- text += printobjectives(abductor_mind)
- text += "
"
- if(abductees.len)
- text += "
The abductees were:"
- for(var/datum/mind/abductee_mind in abductees)
- text += printplayer(abductee_mind)
- text += printobjectives(abductee_mind)
- text += "
"
- to_chat(world, text)
-
// LANDMARKS
/obj/effect/landmark/abductor
var/team_number = 1
diff --git a/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm b/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm
index c3a591a278..cf1329aec9 100644
--- a/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm
+++ b/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm
@@ -151,13 +151,18 @@
var/mob/living/mob_occupant = occupant
if(mob_occupant.stat != DEAD)
if(href_list["experiment"])
- flash = Experiment(occupant,href_list["experiment"])
+ flash = Experiment(occupant,href_list["experiment"],usr)
updateUsrDialog()
add_fingerprint(usr)
-/obj/machinery/abductor/experiment/proc/Experiment(mob/occupant,type)
+/obj/machinery/abductor/experiment/proc/Experiment(mob/occupant,type,mob/user)
LAZYINITLIST(history)
var/mob/living/carbon/human/H = occupant
+
+ var/datum/antagonist/abductor/user_abductor = user.mind.has_antag_datum(/datum/antagonist/abductor)
+ if(!user_abductor)
+ return "Authorization failure. Contact mothership immidiately."
+
var/point_reward = 0
if(H in history)
return "Specimen already in database."
@@ -182,15 +187,8 @@
if(3)
to_chat(H, "You feel intensely watched.")
sleep(5)
- to_chat(H, "Your mind snaps!")
- H.gain_trauma_type(BRAIN_TRAUMA_MILD)
- to_chat(H, "You can't remember how you got here...")
- var/objtype = (prob(75) ? /datum/objective/abductee/random : pick(subtypesof(/datum/objective/abductee/) - /datum/objective/abductee/random))
- var/datum/objective/abductee/O = new objtype()
- SSticker.mode.abductees += H.mind
- H.mind.objectives += O
- H.mind.announce_objectives()
- SSticker.mode.update_abductor_icons_added(H.mind)
+ user_abductor.team.abductees += H.mind
+ H.mind.add_antag_datum(/datum/antagonist/abductee)
for(var/obj/item/organ/heart/gland/G in H.internal_organs)
G.Start()
diff --git a/code/game/gamemodes/miniantags/monkey/monkey.dm b/code/game/gamemodes/miniantags/monkey/monkey.dm
index 379031dce9..8e02d41441 100644
--- a/code/game/gamemodes/miniantags/monkey/monkey.dm
+++ b/code/game/gamemodes/miniantags/monkey/monkey.dm
@@ -105,13 +105,18 @@
monkey_mind.special_role = null
-/datum/game_mode/monkey/declare_completion()
+/datum/game_mode/monkey/set_round_result()
+ ..()
if(check_monkey_victory())
SSticker.mode_result = "win - monkey win"
- to_chat(world, "The monkeys have overthrown their captors! Eeek eeeek!!")
else
SSticker.mode_result = "loss - staff stopped the monkeys"
- to_chat(world, "The staff managed to contain the monkey infestation!")
+
+/datum/game_mode/monkey/special_report()
+ if(check_monkey_victory())
+ return "The monkeys have overthrown their captors! Eeek eeeek!!"
+ else
+ return "The staff managed to contain the monkey infestation!"
/datum/game_mode/monkey/generate_report()
return "Reports of an ancient [pick("retrovirus", "flesh eating bacteria", "disease", "magical curse blamed on viruses", "banana blight")] outbreak that turn humans into monkeys has been reported in your quadrant. Any such infections may be treated with banana juice. If an outbreak occurs, ensure the station is quarantined to prevent a largescale outbreak at CentCom."
diff --git a/code/game/gamemodes/miniantags/morph/morph.dm b/code/game/gamemodes/miniantags/morph/morph.dm
index 5e1a97675b..2ebc21adac 100644
--- a/code/game/gamemodes/miniantags/morph/morph.dm
+++ b/code/game/gamemodes/miniantags/morph/morph.dm
@@ -227,6 +227,7 @@
player_mind.assigned_role = "Morph"
player_mind.special_role = "Morph"
SSticker.mode.traitors |= player_mind
+ player_mind.add_antag_datum(/datum/antagonist/auto_custom)
to_chat(S, S.playstyle_string)
SEND_SOUND(S, sound('sound/magic/mutate.ogg'))
message_admins("[key_name_admin(S)] has been made into a morph by an event.")
diff --git a/code/game/gamemodes/miniantags/revenant/revenant.dm b/code/game/gamemodes/miniantags/revenant/revenant.dm
index 727203b150..78d39673eb 100644
--- a/code/game/gamemodes/miniantags/revenant/revenant.dm
+++ b/code/game/gamemodes/miniantags/revenant/revenant.dm
@@ -87,6 +87,7 @@
mind.assigned_role = "revenant"
mind.special_role = "Revenant"
SSticker.mode.traitors |= mind //Necessary for announcing
+ mind.add_antag_datum(/datum/antagonist/auto_custom)
AddSpell(new /obj/effect/proc_holder/spell/targeted/night_vision/revenant(null))
AddSpell(new /obj/effect/proc_holder/spell/targeted/revenant_transmit(null))
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/defile(null))
diff --git a/code/game/gamemodes/miniantags/slaughter/slaughterevent.dm b/code/game/gamemodes/miniantags/slaughter/slaughterevent.dm
index f1f9624380..e62665f21b 100644
--- a/code/game/gamemodes/miniantags/slaughter/slaughterevent.dm
+++ b/code/game/gamemodes/miniantags/slaughter/slaughterevent.dm
@@ -38,6 +38,7 @@
player_mind.assigned_role = "Slaughter Demon"
player_mind.special_role = "Slaughter Demon"
SSticker.mode.traitors |= player_mind
+ player_mind.add_antag_datum(/datum/antagonist/auto_custom)
to_chat(S, S.playstyle_string)
to_chat(S, "You are currently not currently in the same plane of existence as the station. Blood Crawl near a blood pool to manifest.")
SEND_SOUND(S, 'sound/magic/demon_dies.ogg')
diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm
index 94c372ef23..c7fc1bfdc8 100644
--- a/code/game/gamemodes/nuclear/nuclear.dm
+++ b/code/game/gamemodes/nuclear/nuclear.dm
@@ -171,6 +171,7 @@
return FALSE //its a static var btw
..()
+<<<<<<< HEAD
/datum/game_mode/nuclear/declare_completion()
var/disk_rescued = 1
for(var/obj/item/disk/nuclear/D in GLOB.poi_list)
@@ -251,12 +252,49 @@
..()
return
+=======
+/datum/game_mode/nuclear/set_round_result()
+ var result = nuke_team.get_result()
+ switch(result)
+ if(NUKE_RESULT_FLUKE)
+ SSticker.mode_result = "loss - syndicate nuked - disk secured"
+ SSticker.news_report = NUKE_SYNDICATE_BASE
+ if(NUKE_RESULT_NUKE_WIN)
+ SSticker.mode_result = "win - syndicate nuke"
+ SSticker.news_report = STATION_NUKED
+ if(NUKE_RESULT_NOSURVIVORS)
+ SSticker.mode_result = "halfwin - syndicate nuke - did not evacuate in time"
+ SSticker.news_report = STATION_NUKED
+ if(NUKE_RESULT_WRONG_STATION)
+ SSticker.mode_result = "halfwin - blew wrong station"
+ SSticker.news_report = NUKE_MISS
+ if(NUKE_RESULT_WRONG_STATION_DEAD)
+ SSticker.mode_result = "halfwin - blew wrong station - did not evacuate in time"
+ SSticker.news_report = NUKE_MISS
+ if(NUKE_RESULT_CREW_WIN_SYNDIES_DEAD)
+ SSticker.mode_result = "loss - evacuation - disk secured - syndi team dead"
+ SSticker.news_report = OPERATIVES_KILLED
+ if(NUKE_RESULT_CREW_WIN)
+ SSticker.mode_result = "loss - evacuation - disk secured"
+ SSticker.news_report = OPERATIVES_KILLED
+ if(NUKE_RESULT_DISK_LOST)
+ SSticker.mode_result = "halfwin - evacuation - disk not secured"
+ SSticker.news_report = OPERATIVE_SKIRMISH
+ if(NUKE_RESULT_DISK_STOLEN)
+ SSticker.mode_result = "halfwin - detonation averted"
+ SSticker.news_report = OPERATIVE_SKIRMISH
+ else
+ SSticker.mode_result = "halfwin - interrupted"
+ SSticker.news_report = OPERATIVE_SKIRMISH
+ return ..()
+>>>>>>> 3d81385... Roundend report refactor (#33246)
/datum/game_mode/nuclear/generate_report()
return "One of Central Command's trading routes was recently disrupted by a raid carried out by the Gorlex Marauders. They seemed to only be after one ship - a highly-sensitive \
transport containing a nuclear fission explosive, although it is useless without the proper code and authorization disk. While the code was likely found in minutes, the only disk that \
can activate this explosive is on your station. Ensure that it is protected at all times, and remain alert for possible intruders."
+<<<<<<< HEAD
/datum/game_mode/proc/auto_declare_completion_nuclear()
if( syndicates.len || (SSticker && istype(SSticker.mode, /datum/game_mode/nuclear)) )
var/text = "
The syndicate operatives were:"
@@ -298,6 +336,8 @@
synd_mind.current.real_name = synd_mind.name
return
+=======
+>>>>>>> 3d81385... Roundend report refactor (#33246)
/proc/is_nuclear_operative(mob/M)
return M && istype(M) && M.mind && SSticker && SSticker.mode && M.mind in SSticker.mode.syndicates
diff --git a/code/game/gamemodes/objective_team.dm b/code/game/gamemodes/objective_team.dm
index 7789a167c7..1483f356d2 100644
--- a/code/game/gamemodes/objective_team.dm
+++ b/code/game/gamemodes/objective_team.dm
@@ -3,6 +3,7 @@
var/list/datum/mind/members = list()
var/name = "team"
var/member_name = "member"
+ var/list/objectives = list() //common objectives, these won't be added or removed automatically, subtypes handle this, this is here for bookkeeping purposes.
/datum/objective_team/New(starting_members)
. = ..()
@@ -12,7 +13,6 @@
add_member(M)
else
add_member(starting_members)
- members += starting_members
/datum/objective_team/proc/is_solo()
return members.len == 1
@@ -21,4 +21,14 @@
members |= new_member
/datum/objective_team/proc/remove_member(datum/mind/member)
- members -= member
\ No newline at end of file
+ members -= member
+
+//Display members/victory/failure/objectives for the team
+/datum/objective_team/proc/roundend_report()
+ var/list/report = list()
+
+ report += "[name]:"
+ report += "The [member_name]s were:"
+ report += printplayerlist(members)
+
+ return report.Join("
")
\ No newline at end of file
diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm
index 4fa9532b42..cbfdb98c69 100644
--- a/code/game/gamemodes/revolution/revolution.dm
+++ b/code/game/gamemodes/revolution/revolution.dm
@@ -169,62 +169,22 @@
return FALSE
return TRUE
-//////////////////////////////////////////////////////////////////////
-//Announces the end of the game with all relavent information stated//
-//////////////////////////////////////////////////////////////////////
-/datum/game_mode/revolution/declare_completion()
+
+/datum/game_mode/revolution/set_round_result()
+ ..()
if(finished == 1)
SSticker.mode_result = "win - heads killed"
- to_chat(world, "The heads of staff were killed or exiled! The revolutionaries win!")
-
SSticker.news_report = REVS_WIN
-
else if(finished == 2)
SSticker.mode_result = "loss - rev heads killed"
- to_chat(world, "The heads of staff managed to stop the revolution!")
-
SSticker.news_report = REVS_LOSE
- ..()
- return TRUE
-/datum/game_mode/proc/auto_declare_completion_revolution()
- var/list/targets = list()
- var/list/datum/mind/headrevs = get_antagonists(/datum/antagonist/rev/head)
- var/list/datum/mind/revs = get_antagonists(/datum/antagonist/rev,TRUE)
- if(headrevs.len)
- var/num_revs = 0
- var/num_survivors = 0
- for(var/mob/living/carbon/survivor in GLOB.alive_mob_list)
- if(survivor.ckey)
- num_survivors++
- if(survivor.mind)
- if(is_revolutionary(survivor))
- num_revs++
- if(num_survivors)
- to_chat(world, "[GLOB.TAB]Command's Approval Rating: [100 - round((num_revs/num_survivors)*100, 0.1)]%" )
- var/text = "
The head revolutionaries were:"
- for(var/datum/mind/headrev in headrevs)
- text += printplayer(headrev, 1)
- text += "
"
- to_chat(world, text)
-
- if(revs.len)
- var/text = "
The revolutionaries were:"
- for(var/datum/mind/rev in revs)
- text += printplayer(rev, 1)
- text += "
"
- to_chat(world, text)
-
- if(revs.len || headrevs.len)
- var/text = "
The heads of staff were:"
- var/list/heads = SSjob.get_all_heads()
- for(var/datum/mind/head in heads)
- var/target = (head in targets)
- if(target)
- text += "Target"
- text += printplayer(head, 1)
- text += "
"
- to_chat(world, text)
+//TODO What should be displayed for revs in non-rev rounds
+/datum/game_mode/revolution/special_report()
+ if(finished == 1)
+ return "The heads of staff were killed or exiled! The revolutionaries win!"
+ else if(finished == 2)
+ return "The heads of staff managed to stop the revolution!"
/datum/game_mode/revolution/generate_report()
return "Employee unrest has spiked in recent weeks, with several attempted mutinies on heads of staff. Some crew have been observed using flashbulb devices to blind their colleagues, \
diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm
index 7c93a89a36..468725074d 100644
--- a/code/game/gamemodes/traitor/traitor.dm
+++ b/code/game/gamemodes/traitor/traitor.dm
@@ -27,6 +27,7 @@
var/traitors_possible = 4 //hard limit on traitors if scaling is turned off
var/num_modifier = 0 // Used for gamemodes, that are a child of traitor, that need more than the usual.
var/antag_datum = ANTAG_DATUM_TRAITOR //what type of antag to create
+ var/traitors_required = TRUE //Will allow no traitors
/datum/game_mode/traitor/pre_setup()
@@ -55,7 +56,7 @@
log_game("[traitor.key] (ckey) has been selected as a [traitor_name]")
antag_candidates.Remove(traitor)
- return pre_traitors.len > 0
+ return !traitors_required || pre_traitors.len > 0
/datum/game_mode/traitor/post_setup()
@@ -85,6 +86,7 @@
new_antag.should_specialise = TRUE
character.add_antag_datum(new_antag)
+<<<<<<< HEAD
/datum/game_mode/traitor/declare_completion()
@@ -154,11 +156,12 @@
return TRUE
+=======
+>>>>>>> 3d81385... Roundend report refactor (#33246)
/datum/game_mode/traitor/generate_report()
return "Although more specific threats are commonplace, you should always remain vigilant for Syndicate agents aboard your station. Syndicate communications have implied that many \
Nanotrasen employees are Syndicate agents with hidden memories that may be activated at a moment's notice, so it's possible that these agents might not even know their positions."
-
/datum/game_mode/proc/update_traitor_icons_added(datum/mind/traitor_mind)
var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR]
traitorhud.join_hud(traitor_mind.current)
diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm
index 484961b6f8..e0b83f4d0a 100644
--- a/code/game/gamemodes/wizard/soulstone.dm
+++ b/code/game/gamemodes/wizard/soulstone.dm
@@ -143,7 +143,8 @@
if("VICTIM")
var/mob/living/carbon/human/T = target
- if(is_sacrifice_target(T.mind))
+ var/datum/antagonist/cult/C = user.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
+ if(C && C.cult_team.is_sacrifice_target(T.mind))
if(iscultist(user))
to_chat(user, "\"This soul is mine. SACRIFICE THEM!\"")
else
diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm
index 5302ebd39b..6dd38e9d58 100644
--- a/code/game/gamemodes/wizard/wizard.dm
+++ b/code/game/gamemodes/wizard/wizard.dm
@@ -58,20 +58,13 @@
return TRUE
-/datum/game_mode/wizard/declare_completion()
+/datum/game_mode/wizard/set_round_result()
+ ..()
if(finished)
SSticker.mode_result = "loss - wizard killed"
- to_chat(world, "The wizard[(wizards.len>1)?"s":""] has been killed by the crew! The Space Wizards Federation has been taught a lesson they will not soon forget!")
-
SSticker.news_report = WIZARD_KILLED
- ..()
- return 1
-
-
-/datum/game_mode/proc/auto_declare_completion_wizard()
- if(wizards.len)
- var/text = "
the wizards/witches were:"
+<<<<<<< HEAD
for(var/datum/mind/wizard in wizards)
text += "
[wizard.key] was [wizard.name] ("
@@ -114,9 +107,12 @@
text += ", "
i++
text += "
"
+=======
+/datum/game_mode/wizard/special_report()
+ if(finished)
+ return "The wizard[(wizards.len>1)?"s":""] has been killed by the crew! The Space Wizards Federation has been taught a lesson they will not soon forget!"
+>>>>>>> 3d81385... Roundend report refactor (#33246)
- to_chat(world, text)
- return 1
//returns whether the mob is a wizard (or apprentice)
/proc/iswizard(mob/living/M)
return M.mind && M.mind.has_antag_datum(/datum/antagonist/wizard,TRUE)
diff --git a/code/game/machinery/wishgranter.dm b/code/game/machinery/wishgranter.dm
index fdb481401e..59465cf5b2 100644
--- a/code/game/machinery/wishgranter.dm
+++ b/code/game/machinery/wishgranter.dm
@@ -45,6 +45,7 @@
var/datum/objective/hijack/hijack = new
hijack.owner = user.mind
user.mind.objectives += hijack
+ user.mind.add_antag_datum(/datum/antagonist/auto_custom)
user.mind.announce_objectives()
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 22365c4584..c0859cc318 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -705,15 +705,17 @@
/datum/admins/proc/output_all_devil_info()
var/devil_number = 0
- for(var/D in SSticker.mode.devils)
+ for(var/datum/mind/D in SSticker.mode.devils)
devil_number++
- to_chat(usr, "Devil #[devil_number]:
" + SSticker.mode.printdevilinfo(D))
+ var/datum/antagonist/devil/devil = D.has_antag_datum(/datum/antagonist/devil)
+ to_chat(usr, "Devil #[devil_number]:
" + devil.printdevilinfo())
if(!devil_number)
to_chat(usr, "No Devils located" )
/datum/admins/proc/output_devil_info(mob/living/M)
if(is_devil(M))
- to_chat(usr, SSticker.mode.printdevilinfo(M))
+ var/datum/antagonist/devil/devil = M.mind.has_antag_datum(/datum/antagonist/devil)
+ to_chat(usr, devil.printdevilinfo())
else
to_chat(usr, "[M] is not a devil.")
diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm
index 8a18dc9ac5..75e43adf51 100644
--- a/code/modules/admin/verbs/one_click_antag.dm
+++ b/code/modules/admin/verbs/one_click_antag.dm
@@ -317,11 +317,14 @@
//Assign antag status and the mission
SSticker.mode.traitors += Commando.mind
Commando.mind.special_role = "deathsquad"
+
var/datum/objective/missionobj = new
missionobj.owner = Commando.mind
missionobj.explanation_text = mission
missionobj.completed = 1
Commando.mind.objectives += missionobj
+
+ Commando.mind.add_antag_datum(/datum/antagonist/auto_custom)
//Greet the commando
to_chat(Commando, "You are the [numagents==1?"Deathsquad Officer":"Death Commando"].")
@@ -369,11 +372,14 @@
//Assign antag status and the mission
SSticker.mode.traitors += newmob.mind
newmob.mind.special_role = "official"
+
var/datum/objective/missionobj = new
missionobj.owner = newmob.mind
missionobj.explanation_text = mission
missionobj.completed = 1
newmob.mind.objectives += missionobj
+
+ newmob.mind.add_antag_datum(/datum/antagonist/auto_custom)
if(CONFIG_GET(flag/enforce_human_authority))
newmob.set_species(/datum/species/human)
@@ -474,12 +480,15 @@
//Assign antag status and the mission
SSticker.mode.traitors += ERTOperative.mind
ERTOperative.mind.special_role = "ERT"
+
var/datum/objective/missionobj = new
missionobj.owner = ERTOperative.mind
missionobj.explanation_text = mission
missionobj.completed = 1
ERTOperative.mind.objectives += missionobj
+ ERTOperative.mind.add_antag_datum(/datum/antagonist/auto_custom)
+
//Greet the commando
to_chat(ERTOperative, "You are [numagents==1?"the Emergency Response Team Commander":"an Emergency Response Officer"].")
var/missiondesc = "Your squad is being sent on a Code [alert] mission to [station_name()] by Nanotrasen's Security Division."
diff --git a/code/modules/admin/verbs/onlyone.dm b/code/modules/admin/verbs/onlyone.dm
index b675815602..d09041aaf3 100644
--- a/code/modules/admin/verbs/onlyone.dm
+++ b/code/modules/admin/verbs/onlyone.dm
@@ -28,6 +28,7 @@ GLOBAL_VAR_INIT(highlander, FALSE)
/mob/living/carbon/human/proc/make_scottish()
SSticker.mode.traitors += mind
mind.special_role = "highlander"
+
dna.species.species_traits |= NOGUNS //nice try jackass
var/datum/objective/steal/steal_objective = new
@@ -40,6 +41,8 @@ GLOBAL_VAR_INIT(highlander, FALSE)
hijack_objective.owner = mind
mind.objectives += hijack_objective
+ mind.add_antag_datum(/datum/antagonist/auto_custom)
+
mind.announce_objectives()
for(var/obj/item/I in get_equipped_items())
diff --git a/code/modules/awaymissions/mission_code/wildwest.dm b/code/modules/awaymissions/mission_code/wildwest.dm
index 3031936d43..1fe8baba01 100644
--- a/code/modules/awaymissions/mission_code/wildwest.dm
+++ b/code/modules/awaymissions/mission_code/wildwest.dm
@@ -115,9 +115,11 @@
to_chat(user, "The Wish Granter punishes you for your wickedness, claiming your soul and warping your body to match the darkness in your heart.")
SSticker.mode.traitors += user.mind
user.mind.special_role = "traitor"
+
var/datum/objective/hijack/hijack = new
hijack.owner = user.mind
user.mind.objectives += hijack
+ user.mind.add_antag_datum(/datum/antagonist/auto_custom)
to_chat(user, "Your inhibitions are swept away, the bonds of loyalty broken, you are free to murder as you please!")
user.mind.announce_objectives()
user.set_species(/datum/species/shadow)
diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm
index dbbe6ea512..a7e3b20e7a 100644
--- a/code/modules/client/client_defines.dm
+++ b/code/modules/client/client_defines.dm
@@ -68,3 +68,9 @@
var/datum/chatOutput/chatOutput
+<<<<<<< HEAD
+=======
+ var/list/credits //lazy list of all credit object bound to this client
+
+ var/datum/player_details/player_details //these persist between logins/logouts during the same round.
+>>>>>>> 3d81385... Roundend report refactor (#33246)
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 43ae694b84..fc00ae3828 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -222,6 +222,13 @@ GLOBAL_LIST(external_rsc_urls)
message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(C)] (no longer logged in). ")
log_access("Notice: [key_name(src)] has the same [matches] as [key_name(C)] (no longer logged in).")
+ if(GLOB.player_details[ckey])
+ player_details = GLOB.player_details[ckey]
+ else
+ player_details = new
+ GLOB.player_details[ckey] = player_details
+
+
. = ..() //calls mob.Login()
chatOutput.start() // Starts the chat
diff --git a/code/modules/client/player_details.dm b/code/modules/client/player_details.dm
new file mode 100644
index 0000000000..a842607235
--- /dev/null
+++ b/code/modules/client/player_details.dm
@@ -0,0 +1,2 @@
+/datum/player_details
+ var/list/player_actions = list()
\ No newline at end of file
diff --git a/code/modules/clothing/under/accessories.dm b/code/modules/clothing/under/accessories.dm
index f983ca2563..7ec7d75bac 100644
--- a/code/modules/clothing/under/accessories.dm
+++ b/code/modules/clothing/under/accessories.dm
@@ -135,7 +135,7 @@
"You pin \the [src] on [M]'s chest.")
if(input)
SSblackbox.record_feedback("associative", "commendation", 1, list("commender" = "[user.real_name]", "commendee" = "[M.real_name]", "medal" = "[src]", "reason" = input))
- GLOB.commendations += "[user.real_name] awarded [M.real_name] the [name]! \n- [input]"
+ GLOB.commendations += "[user.real_name] awarded [M.real_name] the [name]! \n- [input]"
commended = TRUE
log_game("[key_name(M)] was given the following commendation by [key_name(user)]: [input]")
message_admins("[key_name(M)] was given the following commendation by [key_name(user)]: [input]")
diff --git a/code/modules/events/holiday/vday.dm b/code/modules/events/holiday/vday.dm
index 41ff90407d..abb5a0639d 100644
--- a/code/modules/events/holiday/vday.dm
+++ b/code/modules/events/holiday/vday.dm
@@ -45,19 +45,26 @@
to_chat(L, "You didn't get a date! They're all having fun without you! you'll show them though...")
var/datum/objective/martyr/normiesgetout = new
normiesgetout.owner = L.mind
+ L.mind.special_role = "heartbreaker"
SSticker.mode.traitors |= L.mind
L.mind.objectives += normiesgetout
+ L.mind.add_antag_datum(/datum/antagonist/auto_custom)
+
/proc/forge_valentines_objective(mob/living/lover,mob/living/date)
SSticker.mode.traitors |= lover.mind
lover.mind.special_role = "valentine"
+
var/datum/objective/protect/protect_objective = new /datum/objective/protect
protect_objective.owner = lover.mind
protect_objective.target = date.mind
protect_objective.explanation_text = "Protect [date.real_name], your date."
lover.mind.objectives += protect_objective
+
+ lover.mind.add_antag_datum(/datum/antagonist/auto_custom)
+
to_chat(lover, "You're on a date with [date]! Protect them at all costs. This takes priority over all other loyalties.")
diff --git a/code/modules/events/nightmare.dm b/code/modules/events/nightmare.dm
index 073ef8aea3..76188cb5b7 100644
--- a/code/modules/events/nightmare.dm
+++ b/code/modules/events/nightmare.dm
@@ -35,6 +35,7 @@
player_mind.assigned_role = "Nightmare"
player_mind.special_role = "Nightmare"
SSticker.mode.traitors += player_mind
+ player_mind.add_antag_datum(/datum/antagonist/auto_custom)
S.set_species(/datum/species/shadow/nightmare)
playsound(S, 'sound/magic/ethereal_exit.ogg', 50, 1, -1)
message_admins("[key_name_admin(S)] has been made into a Nightmare by an event.")
diff --git a/code/modules/events/wizard/departmentrevolt.dm b/code/modules/events/wizard/departmentrevolt.dm
index 618c746a65..3227f81d58 100644
--- a/code/modules/events/wizard/departmentrevolt.dm
+++ b/code/modules/events/wizard/departmentrevolt.dm
@@ -44,6 +44,7 @@
citizens += H
SSticker.mode.traitors += M
M.special_role = "separatist"
+ M.add_antag_datum(/datum/antagonist/auto_custom)
H.log_message("Was made into a separatist, long live [nation]!", INDIVIDUAL_ATTACK_LOG)
to_chat(H, "You are a separatist! [nation] forever! Protect the sovereignty of your newfound land with your comrades in arms!")
if(citizens.len)
diff --git a/code/modules/events/wizard/greentext.dm b/code/modules/events/wizard/greentext.dm
index 6ee000249b..1cf4858ce7 100644
--- a/code/modules/events/wizard/greentext.dm
+++ b/code/modules/events/wizard/greentext.dm
@@ -67,6 +67,7 @@
O.completed = 1 //YES!
O.owner = new_holder.mind
new_holder.mind.objectives += O
+ new_holder.mind.add_antag_datum(/datum/antagonist/auto_custom)
new_holder.log_message("Won with greentext!!!", INDIVIDUAL_ATTACK_LOG)
color_altered_mobs -= new_holder
resistance_flags |= ON_FIRE
diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm
index aab1f204f6..d09f8cd4a2 100644
--- a/code/modules/mob/living/silicon/robot/life.dm
+++ b/code/modules/mob/living/silicon/robot/life.dm
@@ -50,6 +50,7 @@
if(!mind.special_role)
mind.special_role = "traitor"
SSticker.mode.traitors += mind
+ mind.add_antag_datum(/datum/antagonist/auto_custom) // ????
/mob/living/silicon/robot/update_health_hud()
diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm
index 0e6ecf1672..8046daf55d 100644
--- a/code/modules/mob/living/simple_animal/constructs.dm
+++ b/code/modules/mob/living/simple_animal/constructs.dm
@@ -360,8 +360,14 @@
..()
/datum/action/innate/seek_master/Activate()
- if(!SSticker.mode.eldergod)
- the_construct.master = GLOB.blood_target
+ var/datum/antagonist/cult/C = owner.mind.has_antag_datum(/datum/antagonist/cult)
+ if(!C)
+ return
+ var/datum/objective/eldergod/summon_objective = locate() in C.cult_team.objectives
+
+ if(summon_objective.check_completion())
+ the_construct.master = C.cult_team.blood_target
+
if(!the_construct.master)
to_chat(the_construct, "You have no master to seek!")
the_construct.seeking = FALSE
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm
index 3aa1153133..65ef28baee 100644
--- a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm
+++ b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm
@@ -153,7 +153,7 @@
/mob/living/simple_animal/drone/cogscarab/Login()
..()
- add_servant_of_ratvar(src, TRUE)
+ add_servant_of_ratvar(src, TRUE, GLOB.servants_active)
to_chat(src,"You yourself are one of these servants, and will be able to utilize almost anything they can[GLOB.ratvar_awakens ? "":", excluding a clockwork slab"].") // this can't go with flavortext because i'm assuming it requires them to be ratvar'd
/mob/living/simple_animal/drone/cogscarab/binarycheck()
diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm
index e7608f524b..5a10b3183b 100644
--- a/code/modules/mob/login.dm
+++ b/code/modules/mob/login.dm
@@ -48,6 +48,10 @@
client.change_view(CONFIG_GET(string/default_view)) // Resets the client.view in case it was changed.
+ if(client.player_details.player_actions.len)
+ for(var/datum/action/A in client.player_details.player_actions)
+ A.Grant(src)
+
if(!GLOB.individual_log_list[ckey])
GLOB.individual_log_list[ckey] = logging
else
diff --git a/code/modules/ninja/ninja_event.dm b/code/modules/ninja/ninja_event.dm
index 1656a43505..b26d01d880 100644
--- a/code/modules/ninja/ninja_event.dm
+++ b/code/modules/ninja/ninja_event.dm
@@ -95,7 +95,7 @@ Contents:
var/datum/mind/Mind = new /datum/mind(key)
Mind.assigned_role = "Space Ninja"
Mind.special_role = "Space Ninja"
- SSticker.mode.traitors |= Mind //Adds them to current traitor list. Which is really the extra antagonist list.
+ SSticker.mode.traitors |= Mind //Adds them to current traitor list. TODO : Remove this in admin tools refeactor.
return Mind
diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm
index 22712e970d..9801e98614 100644
--- a/code/modules/power/singularity/narsie.dm
+++ b/code/modules/power/singularity/narsie.dm
@@ -47,8 +47,15 @@
/obj/singularity/narsie/large/cult/Initialize()
. = ..()
GLOB.cult_narsie = src
- deltimer(GLOB.blood_target_reset_timer)
- GLOB.blood_target = src
+ var/list/all_cults = list()
+ for(var/datum/antagonist/cult/C in GLOB.antagonists)
+ all_cults |= C.cult_team
+ for(var/datum/objective_team/cult/T in all_cults)
+ deltimer(T.blood_target_reset_timer)
+ T.blood_target = src
+ var/datum/objective/eldergod/summon_objective = locate() in T.objectives
+ if(summon_objective)
+ summon_objective.summoned = TRUE
for(var/datum/mind/cult_mind in SSticker.mode.cult)
if(isliving(cult_mind.current))
var/mob/living/L = cult_mind.current
diff --git a/code/modules/spells/spell_types/rightandwrong.dm b/code/modules/spells/spell_types/rightandwrong.dm
index b992871004..7ab3121a3a 100644
--- a/code/modules/spells/spell_types/rightandwrong.dm
+++ b/code/modules/spells/spell_types/rightandwrong.dm
@@ -22,12 +22,14 @@
guns.owner = H.mind
H.mind.objectives += guns
H.mind.special_role = "survivalist"
+ H.mind.add_antag_datum(/datum/antagonist/auto_custom)
to_chat(H, "You are the survivalist! Your own safety matters above all else, and the only way to ensure your safety is to stockpile weapons! Grab as many guns as possible, by any means necessary. Kill anyone who gets in your way.")
else
var/datum/objective/steal_five_of_type/summon_magic/magic = new
magic.owner = H.mind
H.mind.objectives += magic
H.mind.special_role = "amateur magician"
+ H.mind.add_antag_datum(/datum/antagonist/auto_custom)
to_chat(H, "You are the amateur magician! Grow your newfound talent! Grab as many magical artefacts as possible, by any means necessary. Kill anyone who gets in your way.")
var/datum/objective/survive/survive = new
survive.owner = H.mind
diff --git a/code/modules/station_goals/station_goal.dm b/code/modules/station_goals/station_goal.dm
index 98ec01f641..88377455c6 100644
--- a/code/modules/station_goals/station_goal.dm
+++ b/code/modules/station_goals/station_goal.dm
@@ -26,11 +26,11 @@
/datum/station_goal/proc/check_completion()
return completed
-/datum/station_goal/proc/print_result()
+/datum/station_goal/proc/get_result()
if(check_completion())
- to_chat(world, "Station Goal : [name] : Completed!")
+ return "[name] : Completed!"
else
- to_chat(world, "Station Goal : [name] : Failed!")
+ return "[name] : Failed!"
/datum/station_goal/Destroy()
SSticker.mode.station_goals -= src
diff --git a/html/browser/roundend.css b/html/browser/roundend.css
new file mode 100644
index 0000000000..82235f1273
--- /dev/null
+++ b/html/browser/roundend.css
@@ -0,0 +1,67 @@
+.greentext {
+ color: #90ee90;
+ font-weight: bold;
+}
+
+.greentext_alt {
+ color: green;
+}
+.redtext {
+ color: #ef2f3c;
+ font-weight: bold;
+}
+.neutraltext {
+ font-weight: bold; /* If you feel these should have some color feel free to change */
+}
+
+.marooned {
+ color: rgb(109, 109, 255); font-weight: bold;
+}
+
+.header {
+ font-size: 24px; font-weight: bold;
+}
+
+.big {
+ font-size: 24px;
+}
+
+.medaltext {
+ color: #add8e6;
+}
+
+.codephrase {
+ color : #ef2f3c;
+}
+
+.redborder {
+ border-bottom: 2px solid #ef2f3c;
+}
+
+.greenborder {
+ border-bottom: 2px solid #90ee90;
+}
+
+.clockborder {
+ border-bottom: 2px solid #B18B25;
+}
+
+.stationborder {
+ border-bottom: 2px solid #add8e6;
+}
+
+li {
+ margin-bottom: 0.2rem;
+}
+
+.panel {
+ background-color: #313131;
+ padding: 10px;
+ border-radius: 10px;
+ margin-bottom: 5px;
+}
+
+body {
+ background-color: #272727;
+ color: #efefef;
+}
\ No newline at end of file
diff --git a/tgstation.dme b/tgstation.dme
index 79e277a91b..44c5e2dd93 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -107,6 +107,7 @@
#include "code\__HELPERS\pronouns.dm"
#include "code\__HELPERS\radiation.dm"
#include "code\__HELPERS\radio.dm"
+#include "code\__HELPERS\roundend.dm"
#include "code\__HELPERS\sanitize_values.dm"
#include "code\__HELPERS\shell.dm"
#include "code\__HELPERS\stat_tracking.dm"
@@ -1301,6 +1302,7 @@
#include "code\modules\client\client_colour.dm"
#include "code\modules\client\client_defines.dm"
#include "code\modules\client\client_procs.dm"
+#include "code\modules\client\player_details.dm"
#include "code\modules\client\message.dm"
#include "code\modules\client\preferences.dm"
#include "code\modules\client\preferences_savefile.dm"