\
diff --git a/code/__HELPERS/matrices.dm b/code/__HELPERS/matrices.dm
index 2e34624df5..8b77b170e0 100644
--- a/code/__HELPERS/matrices.dm
+++ b/code/__HELPERS/matrices.dm
@@ -24,7 +24,6 @@
//doesn't have an object argument because this is "Stacking" with the animate call above
//3 billion% intentional
-
//Dumps the matrix data in format a-f
/matrix/proc/tolist()
. = list()
diff --git a/code/__HELPERS/text_vr.dm b/code/__HELPERS/text_vr.dm
index 06aadb6708..7cd683f456 100644
--- a/code/__HELPERS/text_vr.dm
+++ b/code/__HELPERS/text_vr.dm
@@ -24,4 +24,4 @@ GLOBAL_LIST_EMPTY(whitelisted_species_list)
/proc/log_mentor(text)
GLOB.mentor_log.Add(text)
- GLOB.diary << "\[[time_stamp()]]MENTOR: [text]"
\ No newline at end of file
+ GLOB.world_game_log << "\[[time_stamp()]]MENTOR: [text]"
\ No newline at end of file
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 1da1b50aa3..d0fcb3bcbe 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -1342,6 +1342,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
//This prevents RCEs from badmins
//kevinz000 if you touch this I will hunt you down
GLOBAL_VAR_INIT(valid_HTTPSGet, FALSE)
+GLOBAL_PROTECT(valid_HTTPSGet)
/proc/HTTPSGet(url)
if(findtext(url, "\""))
GLOB.valid_HTTPSGet = FALSE
diff --git a/code/_globalvars/game_modes.dm b/code/_globalvars/game_modes.dm
index 153ebbf089..299113795d 100644
--- a/code/_globalvars/game_modes.dm
+++ b/code/_globalvars/game_modes.dm
@@ -3,3 +3,12 @@ GLOBAL_VAR_INIT(secret_force_mode, "secret") // if this is anything but "secret"
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_DATUM(sac_mind, /datum/mind)
+GLOBAL_VAR_INIT(sac_image, null)
+GLOBAL_VAR_INIT(cult_mastered, FALSE)
+GLOBAL_VAR_INIT(reckoning_complete, FALSE)
+GLOBAL_VAR_INIT(sac_complete, FALSE)
\ No newline at end of file
diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm
index 849c5248a0..59885b3432 100644
--- a/code/_globalvars/lists/maintenance_loot.dm
+++ b/code/_globalvars/lists/maintenance_loot.dm
@@ -104,5 +104,6 @@ GLOBAL_LIST_INIT(maintenance_loot, list(
/obj/item/weapon/storage/secure/briefcase = 3,
/obj/item/weapon/storage/toolbox/artistic = 2,
/obj/item/toy/eightball = 1,
+ /obj/item/weapon/storage/daki = 3,
"" = 3
))
diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm
index 32969dd5d8..acb60ee68e 100644
--- a/code/_globalvars/lists/mobs.dm
+++ b/code/_globalvars/lists/mobs.dm
@@ -11,6 +11,7 @@ GLOBAL_LIST_EMPTY(stealthminID) //reference list with IDs that store ckeys,
GLOBAL_LIST_EMPTY(player_list) //all mobs **with clients attached**. Excludes /mob/dead/new_player
GLOBAL_LIST_EMPTY(mob_list) //all mobs, including clientless
+GLOBAL_LIST_EMPTY(mob_directory) //mob_id -> mob
GLOBAL_LIST_EMPTY(living_mob_list) //all alive mobs, including clientless. Excludes /mob/dead/new_player
GLOBAL_LIST_EMPTY(dead_mob_list) //all dead mobs, including clientless. Excludes /mob/dead/new_player
GLOBAL_LIST_EMPTY(joined_player_list) //all clients that have joined the game at round-start or as a latejoin.
@@ -18,5 +19,7 @@ GLOBAL_LIST_EMPTY(silicon_mobs) //all silicon mobs
GLOBAL_LIST_EMPTY(ai_list)
GLOBAL_LIST_EMPTY(pai_list)
GLOBAL_LIST_EMPTY(available_ai_shells)
-GLOBAL_LIST_EMPTY(language_datums)
-GLOBAL_LIST_EMPTY(simple_animals)
\ No newline at end of file
+GLOBAL_LIST_EMPTY(simple_animals)
+
+GLOBAL_LIST_EMPTY(language_datum_instances)
+GLOBAL_LIST_EMPTY(all_languages)
diff --git a/code/_globalvars/lists/names.dm b/code/_globalvars/lists/names.dm
index 15a0867667..5726a1501d 100644
--- a/code/_globalvars/lists/names.dm
+++ b/code/_globalvars/lists/names.dm
@@ -1,23 +1,23 @@
-GLOBAL_LIST_INIT(ai_names, world.file2list("config/names/ai.txt"))
-GLOBAL_LIST_INIT(wizard_first, world.file2list("config/names/wizardfirst.txt"))
-GLOBAL_LIST_INIT(wizard_second, world.file2list("config/names/wizardsecond.txt"))
-GLOBAL_LIST_INIT(ninja_titles, world.file2list("config/names/ninjatitle.txt"))
-GLOBAL_LIST_INIT(ninja_names, world.file2list("config/names/ninjaname.txt"))
-GLOBAL_LIST_INIT(commando_names, world.file2list("config/names/death_commando.txt"))
-GLOBAL_LIST_INIT(first_names_male, world.file2list("config/names/first_male.txt"))
-GLOBAL_LIST_INIT(first_names_female, world.file2list("config/names/first_female.txt"))
-GLOBAL_LIST_INIT(last_names, world.file2list("config/names/last.txt"))
-GLOBAL_LIST_INIT(lizard_names_male, world.file2list("config/names/lizard_male.txt"))
-GLOBAL_LIST_INIT(lizard_names_female, world.file2list("config/names/lizard_female.txt"))
-GLOBAL_LIST_INIT(clown_names, world.file2list("config/names/clown.txt"))
-GLOBAL_LIST_INIT(mime_names, world.file2list("config/names/mime.txt"))
-GLOBAL_LIST_INIT(carp_names, world.file2list("config/names/carp.txt"))
-GLOBAL_LIST_INIT(golem_names, world.file2list("config/names/golem.txt"))
-GLOBAL_LIST_INIT(plasmaman_names, world.file2list("config/names/plasmaman.txt"))
-GLOBAL_LIST_INIT(posibrain_names, list("PBU","HIU","SINA","ARMA","OSI","HBL","MSO","RR","CHRI","CDB","HG","XSI","ORNG","GUN","KOR","MET","FRE","XIS","SLI","PKP","HOG","RZH","GOOF","MRPR","JJR","FIRC","INC","PHL","BGB","ANTR","MIW","WJ","JRD","CHOC","ANCL","JLLO","JNLG","KOS","TKRG","XAL","STLP","CBOS","DUNC","FXMC","DRSD","COI"))
+GLOBAL_LIST_INIT(ai_names, world.file2list("strings/names/ai.txt"))
+GLOBAL_LIST_INIT(wizard_first, world.file2list("strings/names/wizardfirst.txt"))
+GLOBAL_LIST_INIT(wizard_second, world.file2list("strings/names/wizardsecond.txt"))
+GLOBAL_LIST_INIT(ninja_titles, world.file2list("strings/names/ninjatitle.txt"))
+GLOBAL_LIST_INIT(ninja_names, world.file2list("strings/names/ninjaname.txt"))
+GLOBAL_LIST_INIT(commando_names, world.file2list("strings/names/death_commando.txt"))
+GLOBAL_LIST_INIT(first_names_male, world.file2list("strings/names/first_male.txt"))
+GLOBAL_LIST_INIT(first_names_female, world.file2list("strings/names/first_female.txt"))
+GLOBAL_LIST_INIT(last_names, world.file2list("strings/names/last.txt"))
+GLOBAL_LIST_INIT(lizard_names_male, world.file2list("strings/names/lizard_male.txt"))
+GLOBAL_LIST_INIT(lizard_names_female, world.file2list("strings/names/lizard_female.txt"))
+GLOBAL_LIST_INIT(clown_names, world.file2list("strings/names/clown.txt"))
+GLOBAL_LIST_INIT(mime_names, world.file2list("strings/names/mime.txt"))
+GLOBAL_LIST_INIT(carp_names, world.file2list("strings/names/carp.txt"))
+GLOBAL_LIST_INIT(golem_names, world.file2list("strings/names/golem.txt"))
+GLOBAL_LIST_INIT(plasmaman_names, world.file2list("strings/names/plasmaman.txt"))
+GLOBAL_LIST_INIT(posibrain_names, world.file2list("strings/names/posibrain.txt"))
-GLOBAL_LIST_INIT(verbs, world.file2list("config/names/verbs.txt"))
-GLOBAL_LIST_INIT(adjectives, world.file2list("config/names/adjectives.txt"))
+GLOBAL_LIST_INIT(verbs, world.file2list("strings/names/verbs.txt"))
+GLOBAL_LIST_INIT(adjectives, world.file2list("strings/names/adjectives.txt"))
//loaded on startup because of "
//would include in rsc if ' was used
diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm
index be771e0676..e82976d4a3 100644
--- a/code/_globalvars/lists/objects.dm
+++ b/code/_globalvars/lists/objects.dm
@@ -29,6 +29,7 @@ GLOBAL_LIST_EMPTY(zombie_infection_list) // A list of all zombie_infection org
GLOBAL_LIST_EMPTY(meteor_list) // List of all meteors.
GLOBAL_LIST_EMPTY(active_jammers) // List of active radio jammers
GLOBAL_LIST_EMPTY(ladders)
+GLOBAL_LIST_EMPTY(trophy_cases)
GLOBAL_LIST_EMPTY(wire_color_directory)
GLOBAL_LIST_EMPTY(wire_name_directory)
\ No newline at end of file
diff --git a/code/_globalvars/lists/poll_ignore.dm b/code/_globalvars/lists/poll_ignore.dm
index ff6a4edd40..fadd68526d 100644
--- a/code/_globalvars/lists/poll_ignore.dm
+++ b/code/_globalvars/lists/poll_ignore.dm
@@ -5,5 +5,6 @@
#define POLL_IGNORE_POSSESSED_BLADE "possessed_blade"
#define POLL_IGNORE_ALIEN_LARVA "alien_larva"
#define POLL_IGNORE_CLOCKWORK_MARAUDER "clockwork_marauder"
+#define POLL_IGNORE_SYNDICATE "syndicate"
GLOBAL_LIST_EMPTY(poll_ignore)
diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm
index 35adcef36c..00665bdfe2 100644
--- a/code/_globalvars/logging.dm
+++ b/code/_globalvars/logging.dm
@@ -1,28 +1,34 @@
-GLOBAL_VAR(diary)
-GLOBAL_PROTECT(diary)
-GLOBAL_VAR(runtime_diary)
-GLOBAL_PROTECT(runtime_diary)
-GLOBAL_VAR(diaryofmeanpeople)
-GLOBAL_PROTECT(diaryofmeanpeople)
-GLOBAL_VAR(href_logfile)
-GLOBAL_PROTECT(href_logfile)
-
-GLOBAL_LIST_EMPTY(bombers)
-GLOBAL_PROTECT(bombers)
-GLOBAL_LIST_EMPTY(admin_log)
-GLOBAL_PROTECT(admin_log)
-GLOBAL_LIST_EMPTY(lastsignalers) //keeps last 100 signals here in format: "[src] used \ref[src] @ location [src.loc]: [freq]/[code]"
-GLOBAL_PROTECT(lastsignalers)
-GLOBAL_LIST_EMPTY(lawchanges) //Stores who uploaded laws to which silicon-based lifeform, and what the law was
-GLOBAL_PROTECT(lawchanges)
-
-GLOBAL_LIST_EMPTY(combatlog)
-GLOBAL_PROTECT(combatlog)
-GLOBAL_LIST_EMPTY(IClog)
-GLOBAL_PROTECT(IClog)
-GLOBAL_LIST_EMPTY(OOClog)
-GLOBAL_PROTECT(OOClog)
-GLOBAL_LIST_EMPTY(adminlog)
-GLOBAL_PROTECT(adminlog)
-
+GLOBAL_VAR(log_directory)
+GLOBAL_PROTECT(log_directory)
+GLOBAL_VAR(world_game_log)
+GLOBAL_PROTECT(world_game_log)
+GLOBAL_VAR(world_runtime_log)
+GLOBAL_PROTECT(world_runtime_log)
+GLOBAL_VAR(world_attack_log)
+GLOBAL_PROTECT(world_attack_log)
+GLOBAL_VAR(world_href_log)
+GLOBAL_PROTECT(world_href_log)
+GLOBAL_VAR(round_id)
+GLOBAL_PROTECT(round_id)
+GLOBAL_VAR(config_error_log)
+GLOBAL_PROTECT(config_error_log)
+
+GLOBAL_LIST_EMPTY(bombers)
+GLOBAL_PROTECT(bombers)
+GLOBAL_LIST_EMPTY(admin_log)
+GLOBAL_PROTECT(admin_log)
+GLOBAL_LIST_EMPTY(lastsignalers) //keeps last 100 signals here in format: "[src] used \ref[src] @ location [src.loc]: [freq]/[code]"
+GLOBAL_PROTECT(lastsignalers)
+GLOBAL_LIST_EMPTY(lawchanges) //Stores who uploaded laws to which silicon-based lifeform, and what the law was
+GLOBAL_PROTECT(lawchanges)
+
+GLOBAL_LIST_EMPTY(combatlog)
+GLOBAL_PROTECT(combatlog)
+GLOBAL_LIST_EMPTY(IClog)
+GLOBAL_PROTECT(IClog)
+GLOBAL_LIST_EMPTY(OOClog)
+GLOBAL_PROTECT(OOClog)
+GLOBAL_LIST_EMPTY(adminlog)
+GLOBAL_PROTECT(adminlog)
+
GLOBAL_LIST_EMPTY(active_turfs_startlist)
\ No newline at end of file
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 0fe37a9286..9272abbdb6 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -107,64 +107,112 @@
var/obj/item/W = get_active_held_item()
-
if(W == A)
W.attack_self(src)
update_inv_hands()
return
-
- // operate three levels deep here (item in backpack in src; item in box in backpack in src, not any deeper)
- if(A.ClickAccessible(src, depth=INVENTORY_DEPTH))
- // No adjacency needed
+
+ //These are always reachable.
+ //User itself, current loc, and user inventory
+ if(DirectAccess(A))
if(W)
- melee_item_attack_chain(src, W, A, params)
+ melee_item_attack_chain(src,W,A,params)
else
if(ismob(A))
changeNext_move(CLICK_CD_MELEE)
UnarmedAttack(A)
return
-
- if(!isturf(loc)) // This is going to stop you from telekinesing from inside a closet, but I don't shed many tears for that
+
+ //Can't reach anything else in lockers or other weirdness
+ if(!loc.AllowClick())
return
- // Allows you to click on a box's contents, if that box is on the ground, but no deeper than that
- if(isturf(A) || isturf(A.loc) || (A.loc && isturf(A.loc.loc)))
- if(Adjacent(A) || (W && CheckReach(src, A, W.reach))) //Adjacent or reaching attacks
- if(W)
- melee_item_attack_chain(src, W, A, params)
- else
- if(ismob(A))
- changeNext_move(CLICK_CD_MELEE)
- UnarmedAttack(A, 1)
- return
- else // non-adjacent click
- if(W)
- W.afterattack(A,src,0,params) // 0: not Adjacent
- else
- RangedAttack(A, params)
+ //Standard reach turf to turf or reaching inside storage
+ if(CanReach(A,W))
+ if(W)
+ melee_item_attack_chain(src,W,A,params)
+ else
+ if(ismob(A))
+ changeNext_move(CLICK_CD_MELEE)
+ UnarmedAttack(A,1)
+ else
+ if(W)
+ W.afterattack(A,src,0,params)
+ else
+ RangedAttack(A,params)
+
+/atom/movable/proc/CanReach(atom/target,obj/item/tool,view_only = FALSE)
+ if(isturf(target) || isturf(target.loc) || DirectAccess(target)) //Directly accessible atoms
+ if(Adjacent(target) || (tool && CheckToolReach(src, target, tool.reach))) //Adjacent or reaching attacks
+ return TRUE
+ else
+ //Things inside storage insde another storage
+ //Eg Contents of a box in a backpack
+ var/atom/outer_storage = get_atom_on_turf(target)
+ if(outer_storage == target) //whatever that is we don't want infinite loop.
+ return FALSE
+ if(outer_storage && CanReach(outer_storage,tool) && outer_storage.CanReachStorage(target,src,view_only ? STORAGE_VIEW_DEPTH : INVENTORY_DEPTH))
+ return TRUE
+ return FALSE
+
+//Can [target] in this container be reached by [user], can't be more than [depth] levels deep
+/atom/proc/CanReachStorage(atom/target,user,depth)
+ return FALSE
+
+/obj/item/weapon/storage/CanReachStorage(atom/target,user,depth)
+ while(target && depth > 0)
+ target = target.loc
+ depth--
+ if(target == src)
+ return TRUE
+ return FALSE
+
+/atom/movable/proc/DirectAccess(atom/target)
+ if(target == src)
+ return TRUE
+ if(target == loc)
+ return TRUE
+
+/mob/DirectAccess(atom/target)
+ if(..())
+ return TRUE
+ if(target in contents) //This could probably use moving down and restricting to inventory only
+ return TRUE
+ return FALSE
+
+/mob/living/DirectAccess(atom/target)
+ if(..()) //Lightweight checks first
+ return TRUE
+ if(target in GetAllContents())
+ return TRUE
+
+/atom/proc/AllowClick()
+ return FALSE
+
+/turf/AllowClick()
+ return TRUE
-/proc/CheckReach(atom/movable/here, atom/movable/there, reach)
+/proc/CheckToolReach(atom/movable/here, atom/movable/there, reach)
if(!here || !there)
return
switch(reach)
if(0)
- return here.loc == there.loc
+ return FALSE
if(1)
- return here.Adjacent(there)
+ return FALSE //here.Adjacent(there)
if(2 to INFINITY)
- var/obj/dummy = new(get_turf(here)) //We'll try to move this every tick, failing if we can't
+ var/obj/dummy = new(get_turf(here))
dummy.pass_flags |= PASSTABLE
+ dummy.invisibility = INVISIBILITY_ABSTRACT
for(var/i in 1 to reach) //Limit it to that many tries
var/turf/T = get_step(dummy, get_dir(dummy, there))
- if(dummy.loc == there.loc)
+ if(dummy.CanReach(there))
qdel(dummy)
- return 1
- if(there.density && dummy in range(1, there)) //For windows and suchlike
- qdel(dummy)
- return 1
+ return TRUE
if(!dummy.Move(T)) //we're blocked!
qdel(dummy)
return
+ qdel(dummy)
// Default behavior: ignore double clicks (the second click that makes the doubleclick call already calls for a normal click)
/mob/proc/DblClickOn(atom/A, params)
@@ -304,21 +352,6 @@
/atom/proc/CtrlShiftClick(mob/user)
return
-/*
- Helper to check can the mob click/access an item.
- Used by mob inventory and storage items.
-*/
-/atom/proc/ClickAccessible(mob/user, depth=1)
- if(src == user.loc || (src in user.contents))
- return TRUE
-
- if(loc && depth > 1)
- return loc.ClickAccessible(user, depth-1)
-
-/turf/ClickAccessible(mob/user, depth=1)
- return
-
-
/*
Misc helpers
diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm
index 90c953a110..ce7014d765 100644
--- a/code/_onclick/hud/_defines.dm
+++ b/code/_onclick/hud/_defines.dm
@@ -21,7 +21,7 @@
#define ui_inventory "WEST:6,SOUTH:5"
//Middle left indicators
-#define ui_lingchemdisplay "WEST:6,CENTER-1:15"
+#define ui_lingchemdisplay "WEST,CENTER-1:15"
#define ui_lingstingdisplay "WEST:6,CENTER-3:11"
#define ui_crafting "12:-10,1:5"
#define ui_building "12:-10,1:21"
@@ -104,12 +104,17 @@
#define ui_health "EAST-1:28,CENTER-1:15"
#define ui_internal "EAST-1:28,CENTER:17"
-//borgs and aliens
-#define ui_alien_nightvision "EAST-1:28,CENTER:17"
+//borgs
#define ui_borg_health "EAST-1:28,CENTER-1:15" //borgs have the health display where humans have the pressure damage indicator.
-#define ui_alien_health "EAST-1:28,CENTER-1:15" //aliens have the health display where humans have the pressure damage indicator.
-#define ui_alienplasmadisplay "EAST-1:28,CENTER-2:15"
-#define ui_alien_queen_finder "EAST-1:28,CENTER-3:15"
+
+//aliens
+#define ui_alien_health "EAST,CENTER-1:15" //aliens have the health display where humans have the pressure damage indicator.
+#define ui_alienplasmadisplay "EAST,CENTER-2:15"
+#define ui_alien_queen_finder "EAST,CENTER-3:15"
+
+//constructs
+#define ui_construct_pull "EAST,CENTER-2:15"
+#define ui_construct_health "EAST,CENTER:15" //same as borgs and humans
// AI
diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm
index 9c8fe9750a..7cbd997db6 100644
--- a/code/_onclick/hud/alert.dm
+++ b/code/_onclick/hud/alert.dm
@@ -42,6 +42,7 @@
thealert.override_alerts = override
if(override)
thealert.timeout = null
+ thealert.mob_viewer = src
if(new_master)
var/old_layer = new_master.layer
@@ -96,6 +97,7 @@
var/severity = 0
var/alerttooltipstyle = ""
var/override_alerts = FALSE //If it is overriding other alerts of the same type
+ var/mob/mob_viewer //the mob viewing this alert
/obj/screen/alert/MouseEntered(location,control,params)
@@ -256,6 +258,102 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
icon_state = "blobbernaut_nofactory"
alerttooltipstyle = "blob"
+// BLOODCULT
+
+/obj/screen/alert/bloodsense
+ name = "Blood Sense"
+ desc = "Allows you to sense blood that is manipulated by dark magicks."
+ icon_state = "cult_sense"
+ alerttooltipstyle = "cult"
+ var/static/image/narnar
+ var/angle = 0
+ var/mob/living/simple_animal/hostile/construct/Cviewer = null
+
+/obj/screen/alert/bloodsense/Initialize()
+ . = ..()
+ if(!narnar)
+ narnar = new('icons/mob/screen_alert.dmi', "mini_nar")
+ START_PROCESSING(SSprocessing, src)
+
+/obj/screen/alert/bloodsense/Destroy()
+ Cviewer = null
+ STOP_PROCESSING(SSprocessing, src)
+ return ..()
+
+/obj/screen/alert/bloodsense/process()
+ var/atom/blood_target
+ if(GLOB.blood_target)
+ if(!get_turf(GLOB.blood_target))
+ GLOB.blood_target = null
+ else
+ blood_target = GLOB.blood_target
+ if(Cviewer)
+ if(Cviewer.seeking && Cviewer.master)
+ blood_target = Cviewer.master
+ if(!blood_target && !GLOB.sac_complete)
+ 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)
+ return
+ if(!blood_target && GLOB.sac_complete)
+ 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, prepare to summon Nar-Sie!"
+ add_overlay(narnar)
+ return
+ if(!blood_target)
+ return
+ var/turf/P = get_turf(blood_target)
+ var/turf/Q = get_turf(mob_viewer)
+ var/area/A = get_area(P)
+ if(P.z != Q.z) //The target is on a different Z level, we cannot sense that far.
+ return
+ desc = "You are currently tracking [blood_target] in [A.name]."
+ var/target_angle = Get_Angle(Q, P)
+ var/target_dist = get_dist(P, Q)
+ cut_overlays()
+ switch(target_dist)
+ if(0 to 1)
+ icon_state = "runed_sense2"
+ if(2 to 8)
+ icon_state = "arrow8"
+ if(9 to 15)
+ icon_state = "arrow7"
+ if(16 to 22)
+ icon_state = "arrow6"
+ if(23 to 29)
+ icon_state = "arrow5"
+ if(30 to 36)
+ icon_state = "arrow4"
+ if(37 to 43)
+ icon_state = "arrow3"
+ if(44 to 50)
+ icon_state = "arrow2"
+ if(51 to 57)
+ icon_state = "arrow1"
+ if(58 to 64)
+ icon_state = "arrow0"
+ if(65 to 400)
+ icon_state = "arrow"
+ var/difference = target_angle - angle
+ angle = target_angle
+ if(!difference)
+ return
+ var/matrix/final = matrix(transform)
+ final.Turn(difference)
+ animate(src, transform = final, time = 5, loop = 0)
+
+
+
// CLOCKCULT
/obj/screen/alert/clockwork
alerttooltipstyle = "clockcult"
@@ -393,6 +491,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
textlist += "
"
else
textlist += "Seconds until Ratvar's arrival: [G.get_arrival_text(TRUE)]
"
+ break
if(unconverted_ais_exist)
if(unconverted_ais_exist > 1)
textlist += "[unconverted_ais_exist] unconverted AIs exist!
"
diff --git a/code/_onclick/hud/constructs.dm b/code/_onclick/hud/constructs.dm
new file mode 100644
index 0000000000..dc3cea7f99
--- /dev/null
+++ b/code/_onclick/hud/constructs.dm
@@ -0,0 +1,18 @@
+
+/datum/hud/construct
+ ui_style_icon = 'icons/mob/screen_construct.dmi'
+
+/datum/hud/construct/New(mob/owner)
+ ..()
+ pull_icon = new /obj/screen/pull()
+ pull_icon.icon = ui_style_icon
+ pull_icon.update_icon(mymob)
+ pull_icon.screen_loc = ui_pull_resist
+ static_inventory += pull_icon
+
+ healths = new /obj/screen/healths/construct()
+ infodisplay += healths
+
+/mob/living/simple_animal/hostile/construct/create_mob_hud()
+ if(client && !hud_used)
+ hud_used = new /datum/hud/construct(src)
diff --git a/code/_onclick/hud/ghost.dm b/code/_onclick/hud/ghost.dm
index 04655315b7..c2448ceb87 100644
--- a/code/_onclick/hud/ghost.dm
+++ b/code/_onclick/hud/ghost.dm
@@ -68,6 +68,10 @@
using.screen_loc = ui_ghost_pai
static_inventory += using
+ using = new /obj/screen/language_menu
+ using.icon = ui_style
+ static_inventory += using
+
/datum/hud/ghost/show_hud(version = 0, mob/viewmob)
..()
if(!mymob.client.prefs.ghost_hud)
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index b1b4a1cbb3..f6a3688626 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -208,7 +208,6 @@
mymob.reload_fullscreen()
update_parallax_pref(screenmob)
-
/datum/hud/human/show_hud(version = 0,mob/viewmob)
..()
hidden_inventory_update(viewmob)
@@ -266,3 +265,6 @@
E.screen_loc = ui_equip_position(mymob)
if(mymob.hud_used)
show_hud(HUD_STYLE_STANDARD,mymob)
+
+/datum/hud/proc/update_locked_slots()
+ return
\ No newline at end of file
diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm
index f35579c16b..8453b12085 100644
--- a/code/_onclick/hud/human.dm
+++ b/code/_onclick/hud/human.dm
@@ -124,7 +124,6 @@
inv_box.icon = ui_style
inv_box.slot_id = slot_w_uniform
inv_box.icon_state = "uniform"
-// inv_box.icon_full = "template"
inv_box.screen_loc = ui_iclothing
toggleable_inventory += inv_box
@@ -133,7 +132,6 @@
inv_box.icon = ui_style
inv_box.slot_id = slot_wear_suit
inv_box.icon_state = "suit"
-// inv_box.icon_full = "template"
inv_box.screen_loc = ui_oclothing
toggleable_inventory += inv_box
@@ -155,7 +153,6 @@
inv_box.name = "id"
inv_box.icon = ui_style
inv_box.icon_state = "id"
-// inv_box.icon_full = "template_small"
inv_box.screen_loc = ui_id
inv_box.slot_id = slot_wear_id
static_inventory += inv_box
@@ -164,7 +161,6 @@
inv_box.name = "mask"
inv_box.icon = ui_style
inv_box.icon_state = "mask"
-// inv_box.icon_full = "template"
inv_box.screen_loc = ui_mask
inv_box.slot_id = slot_wear_mask
toggleable_inventory += inv_box
@@ -173,7 +169,6 @@
inv_box.name = "neck"
inv_box.icon = ui_style
inv_box.icon_state = "neck"
-// inv_box.icon_full = "template"
inv_box.screen_loc = ui_neck
inv_box.slot_id = slot_neck
toggleable_inventory += inv_box
@@ -182,7 +177,6 @@
inv_box.name = "back"
inv_box.icon = ui_style
inv_box.icon_state = "back"
-// inv_box.icon_full = "template_small"
inv_box.screen_loc = ui_back
inv_box.slot_id = slot_back
static_inventory += inv_box
@@ -191,7 +185,6 @@
inv_box.name = "storage1"
inv_box.icon = ui_style
inv_box.icon_state = "pocket"
-// inv_box.icon_full = "template_small"
inv_box.screen_loc = ui_storage1
inv_box.slot_id = slot_l_store
static_inventory += inv_box
@@ -200,7 +193,6 @@
inv_box.name = "storage2"
inv_box.icon = ui_style
inv_box.icon_state = "pocket"
-// inv_box.icon_full = "template_small"
inv_box.screen_loc = ui_storage2
inv_box.slot_id = slot_r_store
static_inventory += inv_box
@@ -209,7 +201,6 @@
inv_box.name = "suit storage"
inv_box.icon = ui_style
inv_box.icon_state = "suit_storage"
-// inv_box.icon_full = "template"
inv_box.screen_loc = ui_sstore1
inv_box.slot_id = slot_s_store
static_inventory += inv_box
@@ -233,7 +224,6 @@
inv_box.name = "gloves"
inv_box.icon = ui_style
inv_box.icon_state = "gloves"
-// inv_box.icon_full = "template"
inv_box.screen_loc = ui_gloves
inv_box.slot_id = slot_gloves
toggleable_inventory += inv_box
@@ -242,7 +232,6 @@
inv_box.name = "eyes"
inv_box.icon = ui_style
inv_box.icon_state = "glasses"
-// inv_box.icon_full = "template"
inv_box.screen_loc = ui_glasses
inv_box.slot_id = slot_glasses
toggleable_inventory += inv_box
@@ -251,7 +240,6 @@
inv_box.name = "ears"
inv_box.icon = ui_style
inv_box.icon_state = "ears"
-// inv_box.icon_full = "template"
inv_box.screen_loc = ui_ears
inv_box.slot_id = slot_ears
toggleable_inventory += inv_box
@@ -260,7 +248,6 @@
inv_box.name = "head"
inv_box.icon = ui_style
inv_box.icon_state = "head"
-// inv_box.icon_full = "template"
inv_box.screen_loc = ui_head
inv_box.slot_id = slot_head
toggleable_inventory += inv_box
@@ -269,7 +256,6 @@
inv_box.name = "shoes"
inv_box.icon = ui_style
inv_box.icon_state = "shoes"
-// inv_box.icon_full = "template"
inv_box.screen_loc = ui_shoes
inv_box.slot_id = slot_shoes
toggleable_inventory += inv_box
@@ -327,6 +313,18 @@
inv_slots[inv.slot_id] = inv
inv.update_icon()
+/datum/hud/human/update_locked_slots()
+ if(!mymob)
+ return
+ var/mob/living/carbon/human/H = mymob
+ var/datum/species/S = H.dna.species
+ for(var/obj/screen/inventory/inv in (static_inventory + toggleable_inventory))
+ if(inv.slot_id)
+ if(inv.slot_id in S.no_equip)
+ inv.alpha = 128
+ else
+ inv.alpha = initial(inv.alpha)
+
/datum/hud/human/hidden_inventory_update(mob/viewer)
if(!mymob)
return
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index 81754efc93..3cd0d5bdbd 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -91,10 +91,9 @@
screen_loc = ui_language_menu
/obj/screen/language_menu/Click()
- var/mob/living/L = usr
- if(!istype(L))
- return
- L.open_language_menu(usr)
+ var/mob/M = usr
+ var/datum/language_holder/H = M.get_language_holder()
+ H.open_language_menu(usr)
/obj/screen/inventory
var/slot_id // The indentifier for the slot. It has nothing to do with ID cards.
@@ -532,6 +531,12 @@
screen_loc = ui_health
mouse_opacity = 0
+/obj/screen/healths/construct
+ icon = 'icons/mob/screen_construct.dmi'
+ icon_state = "artificer_health0"
+ screen_loc = ui_construct_health
+ mouse_opacity = 0
+
/obj/screen/healthdoll
name = "health doll"
screen_loc = ui_healthdoll
diff --git a/code/citadel/_cit_helpers.dm b/code/citadel/_cit_helpers.dm
index 6e4ea762f1..db0626fa9c 100644
--- a/code/citadel/_cit_helpers.dm
+++ b/code/citadel/_cit_helpers.dm
@@ -117,7 +117,7 @@ GLOBAL_VAR_INIT(dlooc_allowed, 1)
prefs.chat_toggles ^= CHAT_LOOC
prefs.save_preferences()
src << "You will [(prefs.chat_toggles & CHAT_LOOC) ? "now" : "no longer"] see messages on the LOOC channel."
- feedback_add_details("admin_verb","TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ SSblackbox.add_details("admin_verb","TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/togglelooc()
set category = "Server"
@@ -126,7 +126,7 @@ GLOBAL_VAR_INIT(dlooc_allowed, 1)
toggle_looc()
log_admin("[key_name(usr)] toggled LOOC.")
message_admins("[key_name_admin(usr)] toggled LOOC.")
- feedback_add_details("admin_verb","TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ SSblackbox.add_details("admin_verb","TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/proc/toggle_looc(toggle = null)
if(toggle != null) //if we're specifically en/disabling ooc
@@ -146,7 +146,7 @@ GLOBAL_VAR_INIT(dlooc_allowed, 1)
log_admin("[key_name(usr)] toggled Dead LOOC.")
message_admins("[key_name_admin(usr)] toggled Dead LOOC.")
- feedback_add_details("admin_verb","TDLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ SSblackbox.add_details("admin_verb","TDLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/mob/living/carbon/proc/has_penis()
diff --git a/code/citadel/organs/genitals.dm b/code/citadel/organs/genitals.dm
index 4066295350..c129d8e8fa 100644
--- a/code/citadel/organs/genitals.dm
+++ b/code/citadel/organs/genitals.dm
@@ -16,7 +16,8 @@
/obj/item/organ/genital/Initialize()
. = ..()
- reagents = create_reagents(fluid_max_volume)
+ if(!reagents)
+ create_reagents(fluid_max_volume)
update()
/obj/item/organ/genital/Destroy()
diff --git a/code/controllers/admin.dm b/code/controllers/admin.dm
index 2be7139a27..3565620c88 100644
--- a/code/controllers/admin.dm
+++ b/code/controllers/admin.dm
@@ -45,9 +45,9 @@ INITIALIZE_IMMEDIATE(/obj/effect/statclick)
switch(controller)
if("Master")
Recreate_MC()
- feedback_add_details("admin_verb","Restart Master Controller")
+ SSblackbox.add_details("admin_verb","Restart Master Controller")
if("Failsafe")
new /datum/controller/failsafe()
- feedback_add_details("admin_verb","Restart Failsafe Controller")
+ SSblackbox.add_details("admin_verb","Restart Failsafe Controller")
message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.")
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index fd3fe6d685..4d9c819ef9 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -43,10 +43,8 @@
var/log_attack = 0 // log attack messages
var/log_adminchat = 0 // log admin chat messages
var/log_pda = 0 // log pda messages
- var/log_hrefs = 0 // log all links clicked in-game. Could be used for debugging and tracking down exploits
var/log_twitter = 0 // log certain expliotable parrots and other such fun things in a JSON file of twitter valid phrases.
var/log_world_topic = 0 // log all world.Topic() calls
- var/log_runtimes = FALSE // log runtimes into a file
var/sql_enabled = 0 // for sql switching
var/allow_admin_ooccolor = 0 // Allows admins with relevant permissions to have their own ooc colour
var/allow_vote_restart = 0 // allow votes to restart
@@ -66,9 +64,9 @@
var/respawn = 1
var/guest_jobban = 1
var/usewhitelist = 0
- var/inactivity_period = 3000 //time in ds until a player is considered inactive
- var/afk_period = 6000 //time in ds until a player is considered afk and kickable
- var/kick_inactive = FALSE //force disconnect for inactive players
+ var/inactivity_period = 3000 //time in ds until a player is considered inactive
+ var/afk_period = 6000 //time in ds until a player is considered afk and kickable
+ var/kick_inactive = FALSE //force disconnect for inactive players
var/load_jobs_from_txt = 0
var/automute_on = 0 //enables automuting/spam prevention
var/minimal_access_threshold = 0 //If the number of players is larger than this threshold, minimal access will be turned on.
@@ -129,6 +127,7 @@
var/forbid_peaceborg = 0
var/panic_bunker = 0 // prevents new people it hasn't seen before from connecting
var/notify_new_player_age = 0 // how long do we notify admins of a new player
+ var/notify_new_player_account_age = 0 // how long do we notify admins of a new byond account
var/irc_first_connection_alert = 0 // do we notify the irc channel when somebody is connecting for the first time?
var/traitor_scaling_coeff = 6 //how much does the amount of players get divided by to determine traitors
@@ -269,7 +268,7 @@
if(M.config_tag)
if(!(M.config_tag in modes)) // ensure each mode is added only once
- GLOB.diary << "Adding game mode [M.name] ([M.config_tag]) to configuration."
+ GLOB.config_error_log << "Adding game mode [M.name] ([M.config_tag]) to configuration."
modes += M.config_tag
mode_names[M.config_tag] = M.name
probabilities[M.config_tag] = M.probability
@@ -291,7 +290,7 @@
GLOB.abandon_allowed = respawn
/datum/configuration/proc/load(filename, type = "config") //the type can also be game_options, in which case it uses a different switch. not making it separate to not copypaste code - Urist
- var/list/Lines = world.file2list(filename)
+ var/list/Lines = world.file2list(filename)
for(var/t in Lines)
if(!t)
@@ -358,8 +357,6 @@
log_adminchat = 1
if("log_pda")
log_pda = 1
- if("log_hrefs")
- log_hrefs = 1
if("log_twitter")
log_twitter = 1
if("log_world_topic")
@@ -410,12 +407,12 @@
usewhitelist = TRUE
if("allow_metadata")
allow_Metadata = 1
- if("inactivity_period")
- inactivity_period = text2num(value) * 10 //documented as seconds in config.txt
- if("afk_period")
- afk_period = text2num(value) * 10 // ^^^
+ if("inactivity_period")
+ inactivity_period = text2num(value) * 10 //documented as seconds in config.txt
+ if("afk_period")
+ afk_period = text2num(value) * 10 // ^^^
if("kick_inactive")
- kick_inactive = TRUE
+ kick_inactive = TRUE
if("load_jobs_from_txt")
load_jobs_from_txt = 1
if("forbid_singulo_possession")
@@ -476,6 +473,8 @@
panic_bunker = 1
if("notify_new_player_age")
notify_new_player_age = text2num(value)
+ if("notify_new_player_account_age")
+ notify_new_player_account_age = text2num(value)
if("irc_first_connection_alert")
irc_first_connection_alert = 1
if("check_randomizer")
@@ -493,12 +492,6 @@
ipintel_save_bad = text2num(value)
if("aggressive_changelog")
aggressive_changelog = 1
- if("log_runtimes")
- log_runtimes = TRUE
- var/newlog = file("data/logs/runtimes/runtime-[time2text(world.realtime, "YYYY-MM-DD")].log")
- if(GLOB.runtime_diary != newlog)
- world.log << "Now logging runtimes to data/logs/runtimes/runtime-[time2text(world.realtime, "YYYY-MM-DD")].log"
- GLOB.runtime_diary = newlog
if("autoconvert_notes")
autoconvert_notes = 1
if("allow_webclient")
@@ -542,7 +535,7 @@
if("error_msg_delay")
error_msg_delay = text2num(value)
else
- GLOB.diary << "Unknown setting in configuration: '[name]'"
+ GLOB.config_error_log << "Unknown setting in configuration: '[name]'"
else if(type == "game_options")
switch(name)
@@ -605,13 +598,13 @@
if(mode_name in modes)
continuous[mode_name] = 1
else
- GLOB.diary << "Unknown continuous configuration definition: [mode_name]."
+ GLOB.config_error_log << "Unknown continuous configuration definition: [mode_name]."
if("midround_antag")
var/mode_name = lowertext(value)
if(mode_name in modes)
midround_antag[mode_name] = 1
else
- GLOB.diary << "Unknown midround antagonist configuration definition: [mode_name]."
+ GLOB.config_error_log << "Unknown midround antagonist configuration definition: [mode_name]."
if("midround_antag_time_check")
midround_antag_time_check = text2num(value)
if("midround_antag_life_check")
@@ -627,9 +620,9 @@
if(mode_name in modes)
min_pop[mode_name] = text2num(mode_value)
else
- GLOB.diary << "Unknown minimum population configuration definition: [mode_name]."
+ GLOB.config_error_log << "Unknown minimum population configuration definition: [mode_name]."
else
- GLOB.diary << "Incorrect minimum population configuration definition: [mode_name] [mode_value]."
+ GLOB.config_error_log << "Incorrect minimum population configuration definition: [mode_name] [mode_value]."
if("max_pop")
var/pop_pos = findtext(value, " ")
var/mode_name = null
@@ -641,9 +634,9 @@
if(mode_name in modes)
max_pop[mode_name] = text2num(mode_value)
else
- GLOB.diary << "Unknown maximum population configuration definition: [mode_name]."
+ GLOB.config_error_log << "Unknown maximum population configuration definition: [mode_name]."
else
- GLOB.diary << "Incorrect maximum population configuration definition: [mode_name] [mode_value]."
+ GLOB.config_error_log << "Incorrect maximum population configuration definition: [mode_name] [mode_value]."
if("shuttle_refuel_delay")
shuttle_refuel_delay = text2num(value)
if("show_game_type_odds")
@@ -671,9 +664,9 @@
if(prob_name in modes)
probabilities[prob_name] = text2num(prob_value)
else
- GLOB.diary << "Unknown game mode probability configuration definition: [prob_name]."
+ GLOB.config_error_log << "Unknown game mode probability configuration definition: [prob_name]."
else
- GLOB.diary << "Incorrect probability configuration definition: [prob_name] [prob_value]."
+ GLOB.config_error_log << "Incorrect probability configuration definition: [prob_name] [prob_value]."
if("protect_roles_from_antagonist")
protect_roles_from_antagonist = 1
@@ -720,7 +713,7 @@
// Value is in the form "LAWID,NUMBER"
var/list/L = splittext(value, ",")
if(L.len != 2)
- GLOB.diary << "Invalid LAW_WEIGHT: " + t
+ GLOB.config_error_log << "Invalid LAW_WEIGHT: " + t
continue
var/lawid = L[1]
var/weight = text2num(L[2])
@@ -776,8 +769,8 @@
mentors_mobname_only = 1
if ("mentor_legacy_system")
mentor_legacy_system = 1
- else
- GLOB.diary << "Unknown setting in configuration: '[name]'"
+ // else
+ // GLOB.config_error_log << "Adding game mode [M.name] ([M.config_tag]) to configuration."
fps = round(fps)
if(fps <= 0)
@@ -785,7 +778,7 @@
/datum/configuration/proc/loadmaplist(filename)
- var/list/Lines = world.file2list(filename)
+ var/list/Lines = world.file2list(filename)
var/datum/map_config/currentmap = null
for(var/t in Lines)
@@ -831,11 +824,11 @@
maplist[currentmap.map_name] = currentmap
currentmap = null
else
- GLOB.diary << "Unknown command in map vote config: '[command]'"
+ GLOB.config_error_log << "Unknown command in map vote config: '[command]'"
/datum/configuration/proc/loadsql(filename)
- var/list/Lines = world.file2list(filename)
+ var/list/Lines = world.file2list(filename)
for(var/t in Lines)
if(!t)
continue
@@ -875,7 +868,7 @@
if("feedback_tableprefix")
global.sqlfdbktableprefix = value
else
- GLOB.diary << "Unknown setting in configuration: '[name]'"
+ GLOB.config_error_log << "Unknown setting in configuration: '[name]'"
/datum/configuration/proc/pick_mode(mode_name)
// I wish I didn't have to instance the game modes in order to look up
diff --git a/code/controllers/globals.dm b/code/controllers/globals.dm
index cd86e0d97c..89f8a81f20 100644
--- a/code/controllers/globals.dm
+++ b/code/controllers/globals.dm
@@ -52,6 +52,12 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars)
/datum/controller/global_vars/Initialize()
gvars_datum_init_order = list()
gvars_datum_protected_varlist = list("gvars_datum_protected_varlist")
+
+ //See https://github.com/tgstation/tgstation/issues/26954
+ for(var/I in typesof(/datum/controller/global_vars/proc))
+ var/CLEANBOT_RETURNS = "[I]"
+ pass(CLEANBOT_RETURNS)
+
for(var/I in vars - gvars_datum_in_built_vars)
var/start_tick = world.time
call(src, "InitGlobal[I]")()
diff --git a/code/controllers/master.dm b/code/controllers/master.dm
index ab4ff43b04..0df2c8cc73 100644
--- a/code/controllers/master.dm
+++ b/code/controllers/master.dm
@@ -48,7 +48,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
var/initializations_finished_with_no_players_logged_in //I wonder what this could be?
// Has round started? (So we know what subsystems to run)
- var/round_started = 0
+ var/local_round_started = FALSE //Don't read this var, use SSticker.HasRoundStarted() instead
// The type of the last subsystem to be process()'d.
var/last_type_processed
@@ -189,7 +189,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
// Notify the MC that the round has started.
/datum/controller/master/proc/RoundStart()
- round_started = 1
+ local_round_started = TRUE
var/timer = world.time
for (var/datum/controller/subsystem/SS in subsystems)
if (SS.flags & SS_FIRE_IN_LOBBY || SS.flags & SS_TICKER)
@@ -222,7 +222,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
// local vars rock
// Schedule the first run of the Subsystems.
- round_started = world.has_round_started()
+ local_round_started = world.has_round_started()
//all this shit is here so that flag edits can be refreshed by restarting the MC. (and for speed)
var/list/tickersubsystems = list()
var/list/normalsubsystems = list()
@@ -245,7 +245,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
lobbysubsystems += SS
timer += world.tick_lag * rand(1, 5)
SS.next_fire = timer
- else if (round_started)
+ else if (local_round_started)
timer += world.tick_lag * rand(1, 5)
SS.next_fire = timer
normalsubsystems += SS
@@ -280,7 +280,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
// because sleeps are processed in the order received, so longer sleeps are more likely to run first
if (world.tick_usage > TICK_LIMIT_MC)
sleep_delta += 2
- GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING - (TICK_LIMIT_RUNNING * 0.5)
+ GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING * 0.5
sleep(world.tick_lag * (processing + sleep_delta))
continue
@@ -296,7 +296,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
if (!Failsafe || (Failsafe.processing_interval > 0 && (Failsafe.lasttick+(Failsafe.processing_interval*5)) < world.time))
new/datum/controller/failsafe() // (re)Start the failsafe.
if (!queue_head || !(iteration % 3))
- if (round_started)
+ if (local_round_started)
subsystems_to_check = normalsubsystems
else
subsystems_to_check = lobbysubsystems
@@ -421,26 +421,31 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
else
tick_precentage = tick_remaining
- GLOB.CURRENT_TICKLIMIT = world.tick_usage + tick_precentage
+ tick_precentage = max(tick_precentage*0.5, tick_precentage-queue_node.tick_overrun)
+
+ GLOB.CURRENT_TICKLIMIT = round(world.tick_usage + tick_precentage)
if (!(queue_node_flags & SS_TICKER))
ran_non_ticker = TRUE
ran = TRUE
- tick_usage = world.tick_usage
+
queue_node_paused = (queue_node.state == SS_PAUSED || queue_node.state == SS_PAUSING)
last_type_processed = queue_node
queue_node.state = SS_RUNNING
+ tick_usage = world.tick_usage
var/state = queue_node.ignite(queue_node_paused)
+ tick_usage = world.tick_usage - tick_usage
+
if (state == SS_RUNNING)
state = SS_IDLE
current_tick_budget -= queue_node_priority
- tick_usage = world.tick_usage - tick_usage
+
if (tick_usage < 0)
tick_usage = 0
-
+ queue_node.tick_overrun = max(0, MC_AVG_FAST_UP_SLOW_DOWN(queue_node.tick_overrun, tick_usage-tick_precentage))
queue_node.state = state
if (state == SS_PAUSED)
@@ -467,13 +472,13 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
queue_node.times_fired++
if (queue_node_flags & SS_TICKER)
- queue_node.next_fire = world.time + (world.tick_lag * queue_node.wait)
+ queue_node.next_fire = world.time + (world.tick_lag * (queue_node.wait + (queue_node.tick_overrun/100)))
else if (queue_node_flags & SS_POST_FIRE_TIMING)
- queue_node.next_fire = world.time + queue_node.wait
+ queue_node.next_fire = world.time + queue_node.wait + (world.tick_lag * (queue_node.tick_overrun/100))
else if (queue_node_flags & SS_KEEP_TIMING)
queue_node.next_fire += queue_node.wait
else
- queue_node.next_fire = queue_node.queued_time + queue_node.wait
+ queue_node.next_fire = queue_node.queued_time + queue_node.wait + (world.tick_lag * (queue_node.tick_overrun/100))
queue_node.queued_time = 0
diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm
index f37a9159ef..3332b1c45d 100644
--- a/code/controllers/subsystem.dm
+++ b/code/controllers/subsystem.dm
@@ -17,6 +17,7 @@
var/next_fire = 0 //scheduled world.time for next fire()
var/cost = 0 //average time to execute
var/tick_usage = 0 //average tick usage
+ var/tick_overrun = 0 //average tick overrun
var/state = SS_IDLE //tracks the current state of the ss, running, paused, etc.
var/paused_ticks = 0 //ticks this ss is taking to run right now.
var/paused_tick_usage //total tick_usage of all of our runs while pausing this run
@@ -168,7 +169,7 @@
if(can_fire && !(SS_NO_FIRE in flags))
- msg = "[round(cost,1)]ms|[round(tick_usage,1)]%|[round(ticks,0.1)]\t[msg]"
+ msg = "[round(cost,1)]ms|[round(tick_usage,1)]%([round(tick_overrun,1)]%)|[round(ticks,0.1)]\t[msg]"
else
msg = "OFFLINE\t[msg]"
diff --git a/code/controllers/subsystem/atoms.dm.rej b/code/controllers/subsystem/atoms.dm.rej
deleted file mode 100644
index 4d7225164f..0000000000
--- a/code/controllers/subsystem/atoms.dm.rej
+++ /dev/null
@@ -1,9 +0,0 @@
-diff a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm (rejected hunks)
-@@ -12,6 +12,7 @@ SUBSYSTEM_DEF(atoms)
- var/old_initialized
-
- var/list/late_loaders
-+ var/list/created_atoms
-
- var/list/BadInitializeCalls = list()
-
diff --git a/code/orphaned_procs/statistics.dm b/code/controllers/subsystem/blackbox.dm
similarity index 55%
rename from code/orphaned_procs/statistics.dm
rename to code/controllers/subsystem/blackbox.dm
index d6cdcda6c1..a807202c9e 100644
--- a/code/orphaned_procs/statistics.dm
+++ b/code/controllers/subsystem/blackbox.dm
@@ -1,9 +1,7 @@
-GLOBAL_DATUM_INIT(blackbox, /datum/feedback, new)
-
-//the feedback datum; stores all feedback
-/datum/feedback
- var/list/messages = list()
- var/list/messages_admin = list()
+SUBSYSTEM_DEF(blackbox)
+ name = "Blackbox"
+ wait = 6000
+ flags = SS_NO_TICK_CHECK
var/list/msg_common = list()
var/list/msg_science = list()
@@ -15,21 +13,48 @@ GLOBAL_DATUM_INIT(blackbox, /datum/feedback, new)
var/list/msg_syndicate = list()
var/list/msg_service = list()
var/list/msg_cargo = list()
+ var/list/msg_other = list()
- var/list/datum/feedback_variable/feedback = new()
+ var/list/feedback = list() //list of datum/feedback_variable
-/datum/feedback/proc/find_feedback_datum(variable)
- for (var/datum/feedback_variable/FV in feedback)
- if (FV.get_variable() == variable)
- return FV
- var/datum/feedback_variable/FV = new(variable)
- feedback += FV
- return FV
+//poll population
+/datum/controller/subsystem/blackbox/fire()
+ if(!SSdbcore.Connect())
+ return
+ var/playercount = 0
+ for(var/mob/M in GLOB.player_list)
+ if(M.client)
+ playercount += 1
+ var/admincount = GLOB.admins.len
+ var/datum/DBQuery/query_record_playercount = SSdbcore.NewQuery("INSERT INTO [format_table_name("legacy_population")] (playercount, admincount, time, server_ip, server_port) VALUES ([playercount], [admincount], '[SQLtime()]', INET_ATON('[world.internet_address]'), '[world.port]')")
+ query_record_playercount.Execute()
-/datum/feedback/proc/get_round_feedback()
- return feedback
+/datum/controller/subsystem/blackbox/Recover()
+ msg_common = SSblackbox.msg_common
+ msg_science = SSblackbox.msg_science
+ msg_command = SSblackbox.msg_command
+ msg_medical = SSblackbox.msg_medical
+ msg_engineering = SSblackbox.msg_engineering
+ msg_security = SSblackbox.msg_security
+ msg_deathsquad = SSblackbox.msg_deathsquad
+ msg_syndicate = SSblackbox.msg_syndicate
+ msg_service = SSblackbox.msg_service
+ msg_cargo = SSblackbox.msg_cargo
+ msg_other = SSblackbox.msg_other
-/datum/feedback/proc/round_end_data_gathering()
+
+ feedback = SSblackbox.feedback
+
+//no touchie
+/datum/controller/subsystem/blackbox/can_vv_get(var_name)
+ if(var_name == "feedback")
+ return FALSE
+ return ..()
+
+/datum/controller/subsystem/blackbox/vv_edit_var(var_name, var_value)
+ return FALSE
+
+/datum/controller/subsystem/blackbox/Shutdown()
var/pda_msg_amt = 0
var/rc_msg_amt = 0
@@ -39,41 +64,26 @@ GLOBAL_DATUM_INIT(blackbox, /datum/feedback, new)
if (MS.rc_msgs.len > rc_msg_amt)
rc_msg_amt = MS.rc_msgs.len
- feedback_set_details("radio_usage","")
+ set_details("radio_usage","")
- feedback_add_details("radio_usage","COM-[msg_common.len]")
- feedback_add_details("radio_usage","SCI-[msg_science.len]")
- feedback_add_details("radio_usage","HEA-[msg_command.len]")
- feedback_add_details("radio_usage","MED-[msg_medical.len]")
- feedback_add_details("radio_usage","ENG-[msg_engineering.len]")
- feedback_add_details("radio_usage","SEC-[msg_security.len]")
- feedback_add_details("radio_usage","DTH-[msg_deathsquad.len]")
- feedback_add_details("radio_usage","SYN-[msg_syndicate.len]")
- feedback_add_details("radio_usage","SRV-[msg_service.len]")
- feedback_add_details("radio_usage","CAR-[msg_cargo.len]")
- feedback_add_details("radio_usage","OTH-[messages.len]")
- feedback_add_details("radio_usage","PDA-[pda_msg_amt]")
- feedback_add_details("radio_usage","RC-[rc_msg_amt]")
+ add_details("radio_usage","COM-[msg_common.len]")
+ add_details("radio_usage","SCI-[msg_science.len]")
+ add_details("radio_usage","HEA-[msg_command.len]")
+ add_details("radio_usage","MED-[msg_medical.len]")
+ add_details("radio_usage","ENG-[msg_engineering.len]")
+ add_details("radio_usage","SEC-[msg_security.len]")
+ add_details("radio_usage","DTH-[msg_deathsquad.len]")
+ add_details("radio_usage","SYN-[msg_syndicate.len]")
+ add_details("radio_usage","SRV-[msg_service.len]")
+ add_details("radio_usage","CAR-[msg_cargo.len]")
+ add_details("radio_usage","OTH-[msg_other.len]")
+ add_details("radio_usage","PDA-[pda_msg_amt]")
+ add_details("radio_usage","RC-[rc_msg_amt]")
- feedback_set_details("round_end","[time2text(world.realtime)]") //This one MUST be the last one that gets set.
+ set_details("round_end","[time2text(world.realtime)]") //This one MUST be the last one that gets set.
-//This proc is only to be called at round end.
-/datum/feedback/proc/save_all_data_to_sql()
- if (!feedback) return
-
- round_end_data_gathering() //round_end time logging and some other data processing
- if (!SSdbcore.Connect()) return
- var/round_id
-
- var/datum/DBQuery/query_feedback_max_id = SSdbcore.NewQuery("SELECT MAX(round_id) AS round_id FROM [format_table_name("feedback")]")
- if(!query_feedback_max_id.Execute())
+ if (!SSdbcore.Connect())
return
- while (query_feedback_max_id.NextRow())
- round_id = query_feedback_max_id.item[1]
-
- if (!isnum(round_id))
- round_id = text2num(round_id)
- round_id++
var/sqlrowlist = ""
@@ -81,7 +91,7 @@ GLOBAL_DATUM_INIT(blackbox, /datum/feedback, new)
if (sqlrowlist != "")
sqlrowlist += ", " //a comma (,) at the start of the first row to insert will trigger a SQL error
- sqlrowlist += "(null, Now(), [round_id], \"[sanitizeSQL(FV.get_variable())]\", [FV.get_value()], \"[sanitizeSQL(FV.get_details())]\")"
+ sqlrowlist += "(null, Now(), [GLOB.round_id], \"[sanitizeSQL(FV.get_variable())]\", [FV.get_value()], \"[sanitizeSQL(FV.get_details())]\")"
if (sqlrowlist == "")
return
@@ -89,69 +99,99 @@ GLOBAL_DATUM_INIT(blackbox, /datum/feedback, new)
var/datum/DBQuery/query_feedback_save = SSdbcore.NewQuery("INSERT DELAYED IGNORE INTO [format_table_name("feedback")] VALUES " + sqlrowlist)
query_feedback_save.Execute()
+/datum/controller/subsystem/blackbox/proc/LogBroadcast(blackbox_msg, freq)
+ switch(freq)
+ if(1459)
+ msg_common += blackbox_msg
+ if(1351)
+ msg_science += blackbox_msg
+ if(1353)
+ msg_command += blackbox_msg
+ if(1355)
+ msg_medical += blackbox_msg
+ if(1357)
+ msg_engineering += blackbox_msg
+ if(1359)
+ msg_security += blackbox_msg
+ if(1441)
+ msg_deathsquad += blackbox_msg
+ if(1213)
+ msg_syndicate += blackbox_msg
+ if(1349)
+ msg_service += blackbox_msg
+ if(1347)
+ msg_cargo += blackbox_msg
+ else
+ msg_other += blackbox_msg
-/proc/feedback_set(variable,value)
- if(!GLOB.blackbox)
- return
+/datum/controller/subsystem/blackbox/proc/find_feedback_datum(variable)
+ for(var/datum/feedback_variable/FV in feedback)
+ if(FV.get_variable() == variable)
+ return FV
- var/datum/feedback_variable/FV = GLOB.blackbox.find_feedback_datum(variable)
-
- if(!FV)
- return
+ var/datum/feedback_variable/FV = new(variable)
+ feedback += FV
+ return FV
+/datum/controller/subsystem/blackbox/proc/set_val(variable, value)
+ var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.set_value(value)
-/proc/feedback_inc(variable,value)
- if(!GLOB.blackbox)
- return
-
- var/datum/feedback_variable/FV = GLOB.blackbox.find_feedback_datum(variable)
-
- if(!FV)
- return
-
+/datum/controller/subsystem/blackbox/proc/inc(variable, value)
+ var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.inc(value)
-/proc/feedback_dec(variable,value)
- if(!GLOB.blackbox)
- return
-
- var/datum/feedback_variable/FV = GLOB.blackbox.find_feedback_datum(variable)
-
- if(!FV)
- return
-
+/datum/controller/subsystem/blackbox/proc/dec(variable,value)
+ var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.dec(value)
-/proc/feedback_set_details(variable,details)
- if(!GLOB.blackbox)
- return
-
- var/datum/feedback_variable/FV = GLOB.blackbox.find_feedback_datum(variable)
-
- if(!FV)
- return
-
+/datum/controller/subsystem/blackbox/proc/set_details(variable,details)
+ var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.set_details(details)
-/proc/feedback_add_details(variable,details)
- if(!GLOB.blackbox)
- return
-
- var/datum/feedback_variable/FV = GLOB.blackbox.find_feedback_datum(variable)
-
- if(!FV)
- return
-
+/datum/controller/subsystem/blackbox/proc/add_details(variable,details)
+ var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.add_details(details)
+/datum/controller/subsystem/blackbox/proc/ReportDeath(mob/living/L)
+ if(!SSdbcore.Connect())
+ return
+ if(!L || !L.key || !L.mind)
+ return
+ var/turf/T = get_turf(L)
+ var/area/placeofdeath = get_area(T.loc)
+ var/sqlname = sanitizeSQL(L.real_name)
+ var/sqlkey = sanitizeSQL(L.ckey)
+ var/sqljob = sanitizeSQL(L.mind.assigned_role)
+ var/sqlspecial = sanitizeSQL(L.mind.special_role)
+ var/sqlpod = sanitizeSQL(placeofdeath.name)
+ var/laname
+ var/lakey
+ if(L.lastattacker && ismob(L.lastattacker))
+ var/mob/LA = L.lastattacker
+ laname = sanitizeSQL(LA.real_name)
+ lakey = sanitizeSQL(LA.key)
+ var/sqlgender = sanitizeSQL(L.gender)
+ var/sqlbrute = sanitizeSQL(L.getBruteLoss())
+ var/sqlfire = sanitizeSQL(L.getFireLoss())
+ var/sqlbrain = sanitizeSQL(L.getBrainLoss())
+ var/sqloxy = sanitizeSQL(L.getOxyLoss())
+ var/sqltox = sanitizeSQL(L.getStaminaLoss())
+ var/sqlclone = sanitizeSQL(L.getStaminaLoss())
+ var/sqlstamina = sanitizeSQL(L.getStaminaLoss())
+ var/coord = sanitizeSQL("[L.x], [L.y], [L.z]")
+ var/map = sanitizeSQL(SSmapping.config.map_name)
+ var/datum/DBQuery/query_report_death = SSdbcore.NewQuery("INSERT INTO [format_table_name("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, toxloss, cloneloss, staminaloss, coord, mapname, server_ip, server_port) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[SQLtime()]', '[laname]', '[lakey]', '[sqlgender]', [sqlbrute], [sqlfire], [sqlbrain], [sqloxy], [sqltox], [sqlclone], [sqlstamina], '[coord]', '[map]', INET_ATON('[world.internet_address]'), '[world.port]')")
+ query_report_death.Execute()
+
+
//feedback variable datum, for storing all kinds of data
/datum/feedback_variable
var/variable
var/value
var/details
-/datum/feedback_variable/New(var/param_variable,var/param_value = 0)
+/datum/feedback_variable/New(param_variable, param_value = 0)
variable = param_variable
value = param_value
@@ -204,50 +244,3 @@ GLOBAL_DATUM_INIT(blackbox, /datum/feedback, new)
/datum/feedback_variable/proc/get_parsed()
return list(variable,value,details)
-
-//sql reporting procs
-/proc/sql_poll_population()
- if(!config.sql_enabled)
- return
- if(!SSdbcore.Connect())
- return
- var/playercount = 0
- for(var/mob/M in GLOB.player_list)
- if(M.client)
- playercount += 1
- var/admincount = GLOB.admins.len
- var/datum/DBQuery/query_record_playercount = SSdbcore.NewQuery("INSERT INTO [format_table_name("legacy_population")] (playercount, admincount, time, server_ip, server_port) VALUES ([playercount], [admincount], '[SQLtime()]', INET_ATON('[world.internet_address]'), '[world.port]')")
- query_record_playercount.Execute()
-
-/proc/sql_report_death(mob/living/L)
- if(!config.sql_enabled)
- return
- if(!SSdbcore.Connect())
- return
- if(!L || !L.key || !L.mind)
- return
- var/turf/T = get_turf(L)
- var/area/placeofdeath = get_area(T.loc)
- var/sqlname = sanitizeSQL(L.real_name)
- var/sqlkey = sanitizeSQL(L.ckey)
- var/sqljob = sanitizeSQL(L.mind.assigned_role)
- var/sqlspecial = sanitizeSQL(L.mind.special_role)
- var/sqlpod = sanitizeSQL(placeofdeath.name)
- var/laname
- var/lakey
- if(L.lastattacker && ismob(L.lastattacker))
- var/mob/LA = L.lastattacker
- laname = sanitizeSQL(LA.real_name)
- lakey = sanitizeSQL(LA.key)
- var/sqlgender = sanitizeSQL(L.gender)
- var/sqlbrute = sanitizeSQL(L.getBruteLoss())
- var/sqlfire = sanitizeSQL(L.getFireLoss())
- var/sqlbrain = sanitizeSQL(L.getBrainLoss())
- var/sqloxy = sanitizeSQL(L.getOxyLoss())
- var/sqltox = sanitizeSQL(L.getStaminaLoss())
- var/sqlclone = sanitizeSQL(L.getStaminaLoss())
- var/sqlstamina = sanitizeSQL(L.getStaminaLoss())
- var/coord = sanitizeSQL("[L.x], [L.y], [L.z]")
- var/map = sanitizeSQL(SSmapping.config.map_name)
- var/datum/DBQuery/query_report_death = SSdbcore.NewQuery("INSERT INTO [format_table_name("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, toxloss, cloneloss, staminaloss, coord, mapname, server_ip, server_port) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[SQLtime()]', '[laname]', '[lakey]', '[sqlgender]', [sqlbrute], [sqlfire], [sqlbrain], [sqloxy], [sqltox], [sqlclone], [sqlstamina], '[coord]', '[map]', INET_ATON('[world.internet_address]'), '[world.port]')")
- query_report_death.Execute()
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index 137bd2a2c1..c37e10b28c 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -308,7 +308,6 @@ SUBSYSTEM_DEF(job)
Debug("DO non-human failed, Player: [player], Job:[job.title]")
continue
-
// If the player wants that job on this level, then try give it to him.
if(player.client.prefs.GetJobDepartment(job, level) & job.flag)
@@ -319,6 +318,7 @@ SUBSYSTEM_DEF(job)
unassigned -= player
break
+
// Hand out random jobs to the people who didn't get any in the last check
// Also makes sure that they got their preference correct
for(var/mob/dead/new_player/player in unassigned)
@@ -483,7 +483,7 @@ SUBSYSTEM_DEF(job)
else level4++ //not selected
tmp_str += "HIGH=[level1]|MEDIUM=[level2]|LOW=[level3]|NEVER=[level4]|BANNED=[level5]|YOUNG=[level6]|-"
- feedback_add_details("job_preferences",tmp_str)
+ SSblackbox.add_details("job_preferences",tmp_str)
/datum/controller/subsystem/job/proc/PopcapReached()
if(config.hard_popcap || config.extreme_popcap)
diff --git a/code/controllers/subsystem/language.dm b/code/controllers/subsystem/language.dm
new file mode 100644
index 0000000000..e80a7096d8
--- /dev/null
+++ b/code/controllers/subsystem/language.dm
@@ -0,0 +1,18 @@
+SUBSYSTEM_DEF(language)
+ name = "Language"
+ init_order = INIT_ORDER_LANGUAGE
+ flags = SS_NO_FIRE
+
+/datum/controller/subsystem/language/Initialize(timeofday)
+ for(var/L in subtypesof(/datum/language))
+ var/datum/language/language = L
+ if(!initial(language.key))
+ continue
+
+ GLOB.all_languages += language
+
+ var/datum/language/instance = new language
+
+ GLOB.language_datum_instances[language] = instance
+
+ return ..()
diff --git a/code/controllers/subsystem/lighting.dm b/code/controllers/subsystem/lighting.dm
index eb61e5ef39..35d8a3d08f 100644
--- a/code/controllers/subsystem/lighting.dm
+++ b/code/controllers/subsystem/lighting.dm
@@ -36,18 +36,10 @@ SUBSYSTEM_DEF(lighting)
for (i in 1 to GLOB.lighting_update_lights.len)
var/datum/light_source/L = GLOB.lighting_update_lights[i]
- if (L.check() || QDELETED(L) || L.force_update)
- L.remove_lum()
- if (!QDELETED(L))
- L.apply_lum()
+ L.update_corners()
- else if (L.vis_update) //We smartly update only tiles that became (in) visible to use.
- L.smart_vis_update()
+ L.needs_update = LIGHTING_NO_UPDATE
- L.vis_update = FALSE
- L.force_update = FALSE
- L.needs_update = FALSE
-
if(init_tick_checks)
CHECK_TICK
else if (MC_TICK_CHECK)
diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm
index a27235c592..85c580f4d9 100644
--- a/code/controllers/subsystem/mapping.dm
+++ b/code/controllers/subsystem/mapping.dm
@@ -119,7 +119,7 @@ SUBSYSTEM_DEF(mapping)
INIT_ANNOUNCE("Loading [config.map_name]...")
TryLoadZ(config.GetFullMapPath(), FailedZs, ZLEVEL_STATION)
INIT_ANNOUNCE("Loaded station in [(REALTIMEOFDAY - start_time)/10]s!")
- feedback_add_details("map_name", config.map_name)
+ SSblackbox.add_details("map_name", config.map_name)
if(config.minetype != "lavaland")
INIT_ANNOUNCE("WARNING: A map without lavaland set as it's minetype was loaded! This is being ignored! Update the maploader code!")
diff --git a/code/controllers/subsystem/pai.dm b/code/controllers/subsystem/pai.dm
index dfb41c8905..ea6d57088f 100644
--- a/code/controllers/subsystem/pai.dm
+++ b/code/controllers/subsystem/pai.dm
@@ -150,7 +150,7 @@ SUBSYSTEM_DEF(pai)
if(!(ROLE_PAI in G.client.prefs.be_special))
continue
//G << 'sound/misc/server-ready.ogg' //Alerting them to their consideration
- to_chat(G, "Someone is requesting a pAI personality! Use the pAI button to submit yourself as one.")
+ to_chat(G, "[user] is requesting a pAI personality! Use the pAI button to submit yourself as one.")
addtimer(CALLBACK(src, .proc/spam_again), spam_delay)
var/list/available = list()
for(var/datum/paiCandidate/c in SSpai.candidates)
diff --git a/code/controllers/subsystem/persistence.dm b/code/controllers/subsystem/persistence.dm
index e1cf992e8b..ec76d623b1 100644
--- a/code/controllers/subsystem/persistence.dm
+++ b/code/controllers/subsystem/persistence.dm
@@ -9,12 +9,15 @@ SUBSYSTEM_DEF(persistence)
var/list/obj/structure/chisel_message/chisel_messages = list()
var/list/saved_messages = list()
- var/savefile/chisel_messages_sav
+
+ var/savefile/trophy_sav
+ var/list/saved_trophies = list()
/datum/controller/subsystem/persistence/Initialize()
LoadSatchels()
LoadPoly()
LoadChiselMessages()
+ LoadTrophies()
..()
/datum/controller/subsystem/persistence/proc/LoadSatchels()
@@ -71,14 +74,14 @@ SUBSYSTEM_DEF(persistence)
break //Who's been duping the bird?!
/datum/controller/subsystem/persistence/proc/LoadChiselMessages()
- chisel_messages_sav = new /savefile("data/npc_saves/ChiselMessages.sav")
+ var/savefile/chisel_messages_sav = new /savefile("data/npc_saves/ChiselMessages.sav")
var/saved_json
chisel_messages_sav[SSmapping.config.map_name] >> saved_json
if(!saved_json)
return
- var/saved_messages = json_decode(saved_json)
+ var/list/saved_messages = json_decode(saved_json)
for(var/item in saved_messages)
if(!islist(item))
@@ -100,15 +103,57 @@ SUBSYSTEM_DEF(persistence)
var/obj/structure/chisel_message/M = new(T)
- M.unpack(item)
- if(!M.loc)
- M.persists = FALSE
- qdel(M)
+ if(!QDELETED(M))
+ M.unpack(item)
+
+ log_world("Loaded [saved_messages.len] engraved messages on map [SSmapping.config.map_name]")
+
+/datum/controller/subsystem/persistence/proc/LoadTrophies()
+ trophy_sav = new /savefile("data/npc_saves/TrophyItems.sav")
+ var/saved_json
+ trophy_sav >> saved_json
+
+ if(!saved_json)
+ return
+
+ var/decoded_json = json_decode(saved_json)
+
+ if(!islist(decoded_json))
+ return
+
+ saved_trophies = decoded_json
+
+ SetUpTrophies(saved_trophies.Copy())
+
+/datum/controller/subsystem/persistence/proc/SetUpTrophies(list/trophy_items)
+ for(var/A in GLOB.trophy_cases)
+ var/obj/structure/displaycase/trophy/T = A
+ T.added_roundstart = TRUE
+
+ var/trophy_data = pick_n_take(trophy_items)
+
+ if(!islist(trophy_data))
+ continue
+
+ var/list/chosen_trophy = trophy_data
+
+ if(!chosen_trophy || isemptylist(chosen_trophy)) //Malformed
+ continue
+
+ var/path = text2path(chosen_trophy["path"]) //If the item no longer exist, this returns null
+ if(!path)
+ continue
+
+ T.showpiece = new /obj/item/showpiece_dummy(T, path)
+ T.trophy_message = chosen_trophy["message"]
+ T.placer_key = chosen_trophy["placer_key"]
+ T.update_icon()
/datum/controller/subsystem/persistence/proc/CollectData()
CollectChiselMessages()
CollectSecretSatchels()
+ CollectTrophies()
/datum/controller/subsystem/persistence/proc/CollectSecretSatchels()
for(var/A in new_secret_satchels)
@@ -129,10 +174,26 @@ SUBSYSTEM_DEF(persistence)
secret_satchels[SSmapping.config.map_name] << old_secret_satchels
/datum/controller/subsystem/persistence/proc/CollectChiselMessages()
+ var/savefile/chisel_messages_sav = new /savefile("data/npc_saves/ChiselMessages.sav")
+
for(var/obj/structure/chisel_message/M in chisel_messages)
saved_messages += list(M.pack())
+ log_world("Saved [saved_messages.len] engraved messages on map [SSmapping.config.map_name]")
+
chisel_messages_sav[SSmapping.config.map_name] << json_encode(saved_messages)
/datum/controller/subsystem/persistence/proc/SaveChiselMessage(obj/structure/chisel_message/M)
- saved_messages += list(M.pack()) // dm eats one list.
+ saved_messages += list(M.pack()) // dm eats one list
+
+
+/datum/controller/subsystem/persistence/proc/CollectTrophies()
+ trophy_sav << json_encode(saved_trophies)
+
+/datum/controller/subsystem/persistence/proc/SaveTrophy(obj/structure/displaycase/trophy/T)
+ if(!T.added_roundstart && T.showpiece)
+ var/list/data = list()
+ data["path"] = T.showpiece.type
+ data["message"] = T.trophy_message
+ data["placer_key"] = T.placer_key
+ saved_trophies += list(data)
diff --git a/code/controllers/subsystem/ping.dm b/code/controllers/subsystem/ping.dm
index 0829766174..a6b444c4e7 100644
--- a/code/controllers/subsystem/ping.dm
+++ b/code/controllers/subsystem/ping.dm
@@ -3,14 +3,20 @@
SUBSYSTEM_DEF(ping)
name = "Ping"
wait = 6
- flags = SS_NO_INIT|SS_POST_FIRE_TIMING|SS_FIRE_IN_LOBBY
+ flags = SS_POST_FIRE_TIMING|SS_FIRE_IN_LOBBY
priority = 10
var/list/currentrun
+/datum/controller/subsystem/ping/Initialize()
+ if (config.hub)
+ world.visibility = 1
+ ..()
+
/datum/controller/subsystem/ping/fire(resumed = FALSE)
if (!resumed)
src.currentrun = GLOB.clients.Copy()
+ var/round_started = Master.round_started
var/list/currentrun = src.currentrun
while (length(currentrun))
var/client/C = currentrun[currentrun.len]
@@ -19,7 +25,15 @@ SUBSYSTEM_DEF(ping)
if (MC_TICK_CHECK)
return
continue
+
+ if(round_started && C.is_afk(INACTIVITY_KICK))
+ if(!istype(C.mob, /mob/dead))
+ log_access("AFK: [key_name(C)]")
+ to_chat(C, "You have been inactive for more than 10 minutes and have been disconnected.")
+ qdel(C)
+
winset(C, null, "command=.update_ping+[world.time+world.tick_lag*world.tick_usage/100]")
+
if (MC_TICK_CHECK) //one day, when ss13 has 1000 people per server, you guys are gonna be glad I added this tick check
return
diff --git a/code/controllers/subsystem/processing/flightpacks.dm b/code/controllers/subsystem/processing/flightpacks.dm
index 1d85811878..0639f64810 100644
--- a/code/controllers/subsystem/processing/flightpacks.dm
+++ b/code/controllers/subsystem/processing/flightpacks.dm
@@ -5,7 +5,7 @@ PROCESSING_SUBSYSTEM_DEF(flightpacks)
stat_tag = "FM"
flags = SS_NO_INIT|SS_TICKER|SS_KEEP_TIMING
- var/flightsuit_processing = FLIGHTSUIT_PROCESSING_FULL
+ var/flightsuit_processing = FLIGHTSUIT_PROCESSING_NONE
/datum/controller/subsystem/processing/flightpacks/Initialize()
sync_flightsuit_processing()
diff --git a/code/controllers/subsystem/server_maint.dm b/code/controllers/subsystem/server_maint.dm
index 3ef088b328..08f60d23e6 100644
--- a/code/controllers/subsystem/server_maint.dm
+++ b/code/controllers/subsystem/server_maint.dm
@@ -1,17 +1,28 @@
+#define PING_BUFFER_TIME 25
+
SUBSYSTEM_DEF(server_maint)
name = "Server Tasks"
- wait = 6000
- flags = SS_NO_TICK_CHECK
+ wait = 6
+ flags = SS_POST_FIRE_TIMING|SS_FIRE_IN_LOBBY
+ priority = 10
+ var/list/currentrun
/datum/controller/subsystem/server_maint/Initialize(timeofday)
if (config.hub)
world.visibility = 1
..()
-/datum/controller/subsystem/server_maint/fire()
- //handle kicking inactive players
- if(config.kick_inactive)
- for(var/client/C in GLOB.clients)
+/datum/controller/subsystem/server_maint/fire(resumed = FALSE)
+ if(!resumed)
+ src.currentrun = GLOB.clients.Copy()
+
+ var/list/currentrun = src.currentrun
+ var/round_started = SSticker.HasRoundStarted()
+
+ for(var/I in currentrun)
+ var/client/C = I
+ //handle kicking inactive players
+ if(round_started && config.kick_inactive)
if(C.is_afk(config.afk_period))
var/cmob = C.mob
if(!(istype(cmob, /mob/dead/observer) || (istype(cmob, /mob/dead) && C.holder)))
@@ -19,5 +30,10 @@ SUBSYSTEM_DEF(server_maint)
to_chat(C, "You have been inactive for more than [config.afk_period / 600] minutes and have been disconnected.")
qdel(C)
- if(config.sql_enabled)
- sql_poll_population()
+ if (!(!C || world.time - C.connection_time < PING_BUFFER_TIME || C.inactivity >= (wait-1)))
+ winset(C, null, "command=.update_ping+[world.time+world.tick_lag*world.tick_usage/100]")
+
+ if (MC_TICK_CHECK) //one day, when ss13 has 1000 people per server, you guys are gonna be glad I added this tick check
+ return
+
+#undef PING_BUFFER_TIME
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index f701c051da..abf05aad72 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -243,7 +243,7 @@ SUBSYSTEM_DEF(ticker)
send2irc("Server", "Round of [hide_mode ? "secret":"[mode.name]"] has started[allmins.len ? ".":" with no active admins online!"]")
/datum/controller/subsystem/ticker/proc/OnRoundstart(datum/callback/cb)
- if(current_state < GAME_STATE_PLAYING)
+ if(!HasRoundStarted())
LAZYADD(round_start_events, cb)
else
cb.InvokeAsync()
@@ -625,8 +625,8 @@ SUBSYSTEM_DEF(ticker)
if(selected_tip)
m = selected_tip
else
- var/list/randomtips = world.file2list("config/tips.txt")
- var/list/memetips = world.file2list("config/sillytips.txt")
+ var/list/randomtips = world.file2list("strings/tips.txt")
+ var/list/memetips = world.file2list("strings/sillytips.txt")
if(randomtips.len && prob(95))
m = pick(randomtips)
else if(memetips.len)
@@ -672,15 +672,16 @@ SUBSYSTEM_DEF(ticker)
return
INVOKE_ASYNC(SSmapping, /datum/controller/subsystem/mapping/.proc/maprotate)
+/datum/controller/subsystem/ticker/proc/HasRoundStarted()
+ return current_state >= GAME_STATE_PLAYING
+
+/datum/controller/subsystem/ticker/proc/IsRoundInProgress()
+ return current_state == GAME_STATE_PLAYING
+
/proc/send_gamemode_vote()
SSticker.modevoted = TRUE
SSvote.initiate_vote("roundtype","server")
-/world/proc/has_round_started()
- if (SSticker && SSticker.current_state >= GAME_STATE_PLAYING)
- return TRUE
- return FALSE
-
/datum/controller/subsystem/ticker/Recover()
current_state = SSticker.current_state
force_ending = SSticker.force_ending
diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm
index b3b6dce98a..5deb0adf85 100644
--- a/code/controllers/subsystem/vote.dm
+++ b/code/controllers/subsystem/vote.dm
@@ -124,7 +124,7 @@ SUBSYSTEM_DEF(vote)
if("gamemode")
if(GLOB.master_mode != .)
world.save_mode(.)
- if(SSticker && SSticker.mode)
+ if(SSticker.HasRoundStarted())
restart = 1
else
GLOB.master_mode = .
diff --git a/code/datums/action.dm b/code/datums/action.dm
index 08239492f3..5a397fe3bd 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -482,6 +482,7 @@
/datum/action/language_menu/Trigger()
if(!..())
return FALSE
- if(isliving(owner))
- var/mob/living/L = owner
- L.open_language_menu(usr)
+ if(ismob(owner))
+ var/mob/M = owner
+ var/datum/language_holder/H = M.get_language_holder()
+ H.open_language_menu(usr)
diff --git a/code/datums/antagonists/antag_datum.dm b/code/datums/antagonists/antag_datum.dm
index cdad092087..396c185961 100644
--- a/code/datums/antagonists/antag_datum.dm
+++ b/code/datums/antagonists/antag_datum.dm
@@ -1,87 +1,62 @@
-//The Datum, Antagonist. Handles various antag things via a datum.
/datum/antagonist
- var/mob/living/owner //who's our owner and accordingly an antagonist
- var/some_flufftext = "yer an antag larry"
- var/prevented_antag_datum_type //the type of antag datum that this datum can't coexist with; should probably be a list
- var/silent_update = FALSE //if we suppress messages during on_gain, apply_innate_effects, remove_innate_effects, and on_remove
+ var/name = "Antagonist"
-/datum/antagonist/New()
- if(!prevented_antag_datum_type)
- prevented_antag_datum_type = type
+ 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
+ var/list/typecache_datum_blacklist = list() //List of datums this type can't coexist with
+
+/datum/antagonist/New(datum/mind/new_owner)
+ typecache_datum_blacklist = typecacheof(typecache_datum_blacklist)
+ if(new_owner)
+ owner = new_owner
/datum/antagonist/Destroy()
+ if(owner)
+ LAZYREMOVE(owner.antag_datums, src)
owner = null
return ..()
-/datum/antagonist/proc/can_be_owned(mob/living/new_body)
- return new_body && !new_body.has_antag_datum(prevented_antag_datum_type, TRUE)
+/datum/antagonist/proc/can_be_owned(datum/mind/new_owner)
+ . = TRUE
+ if(owner.has_antag_datum(type))
+ return FALSE
+ for(var/i in owner.antag_datums)
+ var/datum/antagonist/A = i
+ if(is_type_in_typecache(src, A.typecache_datum_blacklist))
+ return FALSE
-/datum/antagonist/proc/give_to_body(mob/living/new_body) //tries to give an antag datum to a mob. cancels out if it can't be owned by the new body
- if(new_body && can_be_owned(new_body))
- new_body.antag_datums += src
- owner = new_body
- on_gain()
- . = src //return the datum if successful
- else
- qdel(src)
- . = FALSE
+/datum/antagonist/proc/on_body_transfer(mob/living/old_body, mob/living/new_body)
+ remove_innate_effects(old_body)
+ apply_innate_effects(new_body)
-/datum/antagonist/proc/on_gain() //on initial gain of antag datum, do this. should only be called once per datum
- apply_innate_effects()
- if(!silent_update && some_flufftext)
- to_chat(owner, some_flufftext)
+//This handles the application of antag huds/special abilities
+/datum/antagonist/proc/apply_innate_effects(mob/living/mob_override)
+ return
-/datum/antagonist/proc/apply_innate_effects() //applies innate effects to the owner, may be called multiple times due to mind transferral, but should only be called once per mob
- //antag huds would go here if antag huds were less completely unworkable as-is
+//This handles the removal of antag huds/special abilities
+/datum/antagonist/proc/remove_innate_effects(mob/living/mob_override)
+ return
-/datum/antagonist/proc/remove_innate_effects() //removes innate effects from the owner, may be called multiple times due to mind transferral, but should only be called once per mob
- //also antag huds but see above antag huds a shit
+//Proc called when the datum is given to a mind.
+/datum/antagonist/proc/on_gain()
+ if(owner && owner.current)
+ if(!silent)
+ greet()
+ apply_innate_effects()
-/datum/antagonist/proc/on_remove() //totally removes the antag datum from the owner; can only be called once per owner
+/datum/antagonist/proc/on_removal()
remove_innate_effects()
- owner.antag_datums -= src
+ if(owner)
+ LAZYREMOVE(owner.antag_datums, src)
+ if(!silent && owner.current)
+ farewell()
qdel(src)
-/datum/antagonist/proc/transfer_to_new_body(mob/living/new_body)
- remove_innate_effects()
- if(!islist(new_body.antag_datums))
- new_body.antag_datums = list()
- new_body.antag_datums += src
- owner.antag_datums -= src
- owner = new_body
- apply_innate_effects()
+/datum/antagonist/proc/greet()
+ return
-//mob var and helper procs/Destroy override
-/mob/living
- var/list/antag_datums
-
-/mob/living/Destroy() //TODO: merge this with the living/Destroy() in code\modules\mob\living\living.dm (currently line 29)
- if(islist(antag_datums))
- for(var/i in antag_datums)
- qdel(i)
- antag_datums = null
- return ..()
-
-/mob/living/proc/can_have_antag_datum(datum_type) //if we can have this specific antagonist datum; neccessary, but requires creating a new antag datum each time.
- var/datum/antagonist/D = new datum_type()
- . = D.can_be_owned(src) //we can't exactly cache the results, either, because conditions might change. avoid use? TODO: better proc
- qdel(D)
-
-/mob/living/proc/gain_antag_datum(datum_type) //tries to give a mob a specific antagonist datum; returns the datum if successful.
- if(!islist(antag_datums))
- antag_datums = list()
- var/datum/antagonist/D = new datum_type()
- . = D.give_to_body(src)
-
-/mob/living/proc/has_antag_datum(type, check_subtypes) //checks this mob for if it has the antagonist datum. can either check specific type or subtypes
- if(!islist(antag_datums))
- return FALSE
- for(var/i in antag_datums)
- var/datum/antagonist/D = i
- if(check_subtypes)
- if(istype(D, type))
- return D //if it finds the datum, will return it so you can mess with it
- else
- if(D.type == type)
- return D
- return FALSE
+/datum/antagonist/proc/farewell()
+ return
diff --git a/code/datums/antagonists/datum_clockcult.dm b/code/datums/antagonists/datum_clockcult.dm
index 0c39b5a989..b8dfa00f23 100644
--- a/code/datums/antagonists/datum_clockcult.dm
+++ b/code/datums/antagonists/datum_clockcult.dm
@@ -1,79 +1,75 @@
//CLOCKCULT PROOF OF CONCEPT
-
-/datum/antagonist/clockcultist
- prevented_antag_datum_type = /datum/antagonist/clockcultist
- some_flufftext = null
+/datum/antagonist/clockcult
var/datum/action/innate/hierophant/hierophant_network = new()
-/datum/antagonist/clockcultist/silent
- silent_update = TRUE
+/datum/antagonist/clockcult/silent
+ silent = TRUE
-/datum/antagonist/clockcultist/Destroy()
+/datum/antagonist/clockcult/Destroy()
qdel(hierophant_network)
- ..()
+ return ..()
-/datum/antagonist/clockcultist/can_be_owned(mob/living/new_body)
+/datum/antagonist/clockcult/can_be_owned(datum/mind/new_owner)
. = ..()
if(.)
- . = is_eligible_servant(new_body)
+ if(iscyborg(new_owner.current))
+ var/mob/living/silicon/robot/R = new_owner.current
+ if(R.deployed)
+ var/mob/living/silicon/ai/AI = R.mainframe
+ R.undeploy()
+ to_chat(AI, "Anomaly Detected. Returned to core!") //The AI needs to be in its core to properly be converted
+ . = is_eligible_servant(new_owner.current)
+ if(!silent && new_owner.current)
+ if(issilicon(new_owner.current))
+ to_chat(new_owner.current, "You are unable to compute this truth. Your vision glows a brilliant yellow, and all at once it comes to you. Ratvar, the \
+ Clockwork Justiciar, lies in exile, derelict and forgotten in an unseen realm.")
+ else
+ to_chat(new_owner.current, "[iscarbon(new_owner.current) ? "Your mind is racing! Your body feels incredibly light! ":""]Your world glows a brilliant \
+ yellow! All at once it comes to you. Ratvar, the Clockwork Justiciar, lies in exile, derelict and forgotten in an unseen realm.")
+ if(!.)
+ new_owner.current.visible_message("[new_owner.current] seems to resist an unseen force!")
+ to_chat(new_owner.current, "And yet, you somehow push it all away.")
-/datum/antagonist/clockcultist/give_to_body(mob/living/new_body)
- if(iscyborg(new_body))
- var/mob/living/silicon/robot/R = new_body
- if(R.deployed)
- var/mob/living/silicon/ai/AI = R.mainframe
- R.undeploy()
- var/converted = add_servant_of_ratvar(AI, silent_update)
- to_chat(AI, "Anomaly Detected. Returned to core!") //The AI needs to be in its core to properly be converted
- return converted
- if(!silent_update)
- if(issilicon(new_body))
- to_chat(new_body, "You are unable to compute this truth. Your vision glows a brilliant yellow, and all at once it comes to you. Ratvar, the Clockwork Justiciar, \
- lies in exile, derelict and forgotten in an unseen realm.")
- else
- to_chat(new_body, "[iscarbon(new_body) ? "Your mind is racing! Your body feels incredibly light! ":""]Your world glows a brilliant yellow! All at once it comes to you. \
- Ratvar, the Clockwork Justiciar, lies in exile, derelict and forgotten in an unseen realm.")
- . = ..()
- if(!silent_update && new_body)
- if(.)
- new_body.visible_message("[new_body]'s eyes glow a blazing yellow!")
- to_chat(new_body, "Assist your new companions in their righteous efforts. Your goal is theirs, and theirs yours. You serve the Clockwork Justiciar above all else. \
- Perform his every whim without hesitation.")
- else
- new_body.visible_message("[new_body] seems to resist an unseen force!")
- to_chat(new_body, "And yet, you somehow push it all away.")
+/datum/antagonist/clockcult/greet()
+ if(!owner.current || silent)
+ return
+ owner.current.visible_message("[owner.current]'s eyes glow a blazing yellow!")
+ to_chat(owner.current, "Assist your new companions in their righteous efforts. Your goal is theirs, and theirs yours. You serve the Clockwork \
+ Justiciar above all else. Perform his every whim without hesitation.")
-/datum/antagonist/clockcultist/on_gain()
- if(SSticker && SSticker.mode && owner.mind)
- SSticker.mode.servants_of_ratvar += owner.mind
- SSticker.mode.update_servant_icons_added(owner.mind)
- if(jobban_isbanned(owner, ROLE_SERVANT_OF_RATVAR))
- INVOKE_ASYNC(SSticker.mode, /datum/game_mode.proc/replace_jobbaned_player, owner, ROLE_SERVANT_OF_RATVAR, ROLE_SERVANT_OF_RATVAR)
- if(owner.mind)
- owner.mind.special_role = "Servant of Ratvar"
- owner.log_message("Has been converted to the cult of Ratvar!", INDIVIDUAL_ATTACK_LOG)
- if(issilicon(owner))
- var/mob/living/silicon/S = owner
- if(iscyborg(S) && !silent_update)
- to_chat(S, "You have been desynced from your master AI.\n\
- In addition, your onboard camera is no longer active and you have gained additional equipment, including a limited clockwork slab.")
- if(isAI(S))
- to_chat(S, "You are able to use your cameras to listen in on conversations.")
- to_chat(S, "You can communicate with other servants by using the Hierophant Network action button in the upper left.")
- else if(isbrain(owner) || isclockmob(owner))
- to_chat(owner, "You can communicate with other servants by using the Hierophant Network action button in the upper left.")
+/datum/antagonist/clockcult/on_gain()
+ var/mob/living/current = owner.current
+ SSticker.mode.servants_of_ratvar += owner
+ SSticker.mode.update_servant_icons_added(owner)
+ if(jobban_isbanned(current, ROLE_SERVANT_OF_RATVAR))
+ addtimer(CALLBACK(SSticker.mode, /datum/game_mode.proc/replace_jobbaned_player, current, ROLE_SERVANT_OF_RATVAR, ROLE_SERVANT_OF_RATVAR), 0)
+ owner.special_role = "Servant of Ratvar"
+ owner.current.log_message("Has been converted to the cult of Ratvar!", INDIVIDUAL_ATTACK_LOG)
+ if(issilicon(current))
+ if(iscyborg(current) && !silent)
+ to_chat(current, "You have been desynced from your master AI.")
+ to_chat(current, "In addition, your onboard camera is no longer active and you have gained additional equipment, including a limited clockwork slab.")
+ if(isAI(current))
+ to_chat(current, "You are able to use your cameras to listen in on conversations.")
+ to_chat(current, "You can communicate with other servants by using the Hierophant Network action button in the upper left.")
+ else if(isbrain(current) || isclockmob(current))
+ to_chat(current, "You can communicate with other servants by using the Hierophant Network action button in the upper left.")
..()
if(istype(SSticker.mode, /datum/game_mode/clockwork_cult))
var/datum/game_mode/clockwork_cult/C = SSticker.mode
C.present_tasks(owner) //Memorize the objectives
-/datum/antagonist/clockcultist/apply_innate_effects()
- GLOB.all_clockwork_mobs += owner
- owner.faction |= "ratvar"
- owner.grant_language(/datum/language/ratvar)
- owner.update_action_buttons_icon() //because a few clockcult things are action buttons and we may be wearing/holding them for whatever reason, we need to update buttons
- if(issilicon(owner))
- var/mob/living/silicon/S = owner
+/datum/antagonist/clockcult/apply_innate_effects(mob/living/mob_override)
+ . = ..()
+ var/mob/living/current = owner.current
+ if(istype(mob_override))
+ current = mob_override
+ GLOB.all_clockwork_mobs += current
+ current.faction |= "ratvar"
+ current.grant_language(/datum/language/ratvar)
+ current.update_action_buttons_icon() //because a few clockcult things are action buttons and we may be wearing/holding them for whatever reason, we need to update buttons
+ if(issilicon(current))
+ var/mob/living/silicon/S = current
if(iscyborg(S))
var/mob/living/silicon/robot/R = S
if(!R.shell)
@@ -106,31 +102,33 @@
hierophant_network.title = "Silicon"
hierophant_network.span_for_name = "nezbere"
hierophant_network.span_for_message = "brass"
- else if(isbrain(owner))
- hierophant_network.Grant(owner)
+ else if(isbrain(current))
+ hierophant_network.Grant(current)
hierophant_network.title = "Vessel"
hierophant_network.span_for_name = "nezbere"
hierophant_network.span_for_message = "alloy"
- else if(isclockmob(owner))
- hierophant_network.Grant(owner)
+ else if(isclockmob(current))
+ hierophant_network.Grant(current)
hierophant_network.title = "Construct"
hierophant_network.span_for_name = "nezbere"
hierophant_network.span_for_message = "brass"
- owner.throw_alert("clockinfo", /obj/screen/alert/clockwork/infodump)
+ current.throw_alert("clockinfo", /obj/screen/alert/clockwork/infodump)
if(!GLOB.clockwork_gateway_activated)
- owner.throw_alert("scripturereq", /obj/screen/alert/clockwork/scripture_reqs)
- ..()
+ current.throw_alert("scripturereq", /obj/screen/alert/clockwork/scripture_reqs)
-/datum/antagonist/clockcultist/remove_innate_effects()
- GLOB.all_clockwork_mobs -= owner
- owner.faction -= "ratvar"
- owner.remove_language(/datum/language/ratvar)
- owner.clear_alert("clockinfo")
- owner.clear_alert("scripturereq")
- for(var/datum/action/innate/function_call/F in owner.actions) //Removes any bound Ratvarian spears
+/datum/antagonist/clockcult/remove_innate_effects(mob/living/mob_override)
+ var/mob/living/current = owner.current
+ if(istype(mob_override))
+ current = mob_override
+ GLOB.all_clockwork_mobs -= current
+ current.faction -= "ratvar"
+ current.remove_language(/datum/language/ratvar)
+ current.clear_alert("clockinfo")
+ current.clear_alert("scripturereq")
+ for(var/datum/action/innate/function_call/F in owner.current.actions) //Removes any bound Ratvarian spears
qdel(F)
- if(issilicon(owner))
- var/mob/living/silicon/S = owner
+ if(issilicon(current))
+ var/mob/living/silicon/S = current
if(isAI(S))
var/mob/living/silicon/ai/A = S
A.can_be_carded = initial(A.can_be_carded)
@@ -139,7 +137,7 @@
S.make_laws()
S.update_icons()
S.show_laws()
- var/mob/living/temp_owner = owner
+ var/mob/living/temp_owner = current
..()
if(iscyborg(temp_owner))
var/mob/living/silicon/robot/R = temp_owner
@@ -147,17 +145,15 @@
if(temp_owner)
temp_owner.update_action_buttons_icon() //because a few clockcult things are action buttons and we may be wearing/holding them, we need to update buttons
-/datum/antagonist/clockcultist/on_remove()
- if(!silent_update)
- owner.visible_message("[owner] seems to have remembered their true allegiance!", \
+/datum/antagonist/clockcult/on_removal()
+ SSticker.mode.servants_of_ratvar -= owner
+ SSticker.mode.update_servant_icons_removed(owner)
+ if(!silent)
+ owner.current.visible_message("[owner] seems to have remembered their true allegiance!", \
"A cold, cold darkness flows through your mind, extinguishing the Justiciar's light and all of your memories as his servant.")
- if(SSticker && SSticker.mode && owner.mind)
- SSticker.mode.servants_of_ratvar -= owner.mind
- SSticker.mode.update_servant_icons_removed(owner.mind)
- if(owner.mind)
- owner.mind.wipe_memory()
- owner.mind.special_role = null
- owner.log_message("Has renounced the cult of Ratvar!", INDIVIDUAL_ATTACK_LOG)
- if(iscyborg(owner))
- to_chat(owner, "Despite your freedom from Ratvar's influence, you are still irreparably damaged and no longer possess certain functions such as AI linking.")
- ..()
+ owner.current.log_message("Has renounced the cult of Ratvar!", INDIVIDUAL_ATTACK_LOG)
+ owner.wipe_memory()
+ owner.special_role = null
+ 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.")
+ . = ..()
diff --git a/code/datums/antagonists/datum_cult.dm b/code/datums/antagonists/datum_cult.dm
index 7d6806bab6..9016331440 100644
--- a/code/datums/antagonists/datum_cult.dm
+++ b/code/datums/antagonists/datum_cult.dm
@@ -1,50 +1,148 @@
-/datum/antagonist/cultist
- prevented_antag_datum_type = /datum/antagonist/cultist
- some_flufftext = null
- var/datum/action/innate/cultcomm/communion = new()
+/datum/antagonist/cult
+ var/datum/action/innate/cultcomm/communion = new
-/datum/antagonist/cultist/Destroy()
- qdel(communion)
+/datum/antagonist/cult/Destroy()
+ QDEL_NULL(communion)
return ..()
-/datum/antagonist/cultist/can_be_owned(mob/living/new_body)
+/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"
+ 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."
+ if("eldergod")
+ explanation = "Summon Nar-Sie by invoking the rune 'Summon Nar-Sie' with nine acolytes on it. You must do this after sacrificing your target."
+ 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(.)
- . = is_convertable_to_cult(new_body)
+ . = is_convertable_to_cult(new_owner.current)
-/datum/antagonist/cultist/on_gain()
- if(SSticker && SSticker.mode && owner.mind)
- SSticker.mode.cult += owner.mind
- SSticker.mode.update_cult_icons_added(owner.mind)
- if(istype(SSticker.mode, /datum/game_mode/cult))
- var/datum/game_mode/cult/C = SSticker.mode
- C.memorize_cult_objectives(owner.mind)
- if(jobban_isbanned(owner, ROLE_CULTIST))
- INVOKE_ASYNC(SSticker.mode, /datum/game_mode.proc/replace_jobbaned_player, owner, ROLE_CULTIST, ROLE_CULTIST)
- if(owner.mind)
- owner.mind.special_role = "Cultist"
- owner.log_message("Has been converted to the cult of Nar'Sie!", INDIVIDUAL_ATTACK_LOG)
- ..()
+/datum/antagonist/cult/on_gain()
+ . = ..()
+ var/mob/living/current = owner.current
+ if(!LAZYLEN(SSticker.mode.cult_objectives))
+ add_objectives()
+ SSticker.mode.cult += owner // Only add after they've been given objectives
+ cult_memorization(owner)
+ if(jobban_isbanned(current, ROLE_CULTIST))
+ addtimer(CALLBACK(SSticker.mode, /datum/game_mode.proc/replace_jobbaned_player, current, ROLE_CULTIST, ROLE_CULTIST), 0)
+ 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
-/datum/antagonist/cultist/apply_innate_effects()
- owner.faction |= "cult"
- owner.verbs += /mob/living/proc/cult_help
- communion.Grant(owner)
- ..()
+/datum/antagonist/cult/apply_innate_effects(mob/living/mob_override)
+ . = ..()
+ var/mob/living/current = owner.current
+ if(mob_override)
+ current = mob_override
+ current.faction |= "cult"
+ current.verbs += /mob/living/proc/cult_help
+ if(!GLOB.cult_mastered)
+ current.verbs += /mob/living/proc/cult_master
+ communion.Grant(current)
+ current.throw_alert("bloodsense", /obj/screen/alert/bloodsense)
-/datum/antagonist/cultist/remove_innate_effects()
- owner.faction -= "cult"
- owner.verbs -= /mob/living/proc/cult_help
- ..()
+/datum/antagonist/cult/remove_innate_effects(mob/living/mob_override)
+ . = ..()
+ var/mob/living/current = owner.current
+ if(mob_override)
+ current = mob_override
+ current.faction -= "cult"
+ current.verbs -= /mob/living/proc/cult_help
+ communion.Remove(current)
+ owner.current.verbs -= /mob/living/proc/cult_master
+ for(var/datum/action/innate/cultmast/H in owner.current.actions)
+ qdel(H)
+ current.clear_alert("bloodsense")
-/datum/antagonist/cultist/on_remove()
- if(owner.mind)
- owner.mind.wipe_memory()
- if(SSticker && SSticker.mode)
- SSticker.mode.cult -= owner.mind
- SSticker.mode.update_cult_icons_removed(owner.mind)
- to_chat(owner, "An unfamiliar white light flashes through your mind, cleansing the taint of the Dark One and all your memories as its servant.")
- owner.log_message("Has renounced the cult of Nar'Sie!", INDIVIDUAL_ATTACK_LOG)
- if(!silent_update)
- owner.visible_message("[owner] looks like [owner.p_they()] just reverted to their old faith!")
- ..()
+/datum/antagonist/cult/on_removal()
+ owner.wipe_memory()
+ SSticker.mode.cult -= owner
+ SSticker.mode.update_cult_icons_removed(owner)
+ if(!silent)
+ 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)
+ owner.current.visible_message("[owner.current] looks like [owner.current.p_they()] just reverted to their old faith!")
+ if(GLOB.blood_target && GLOB.blood_target_image && owner.current.client)
+ owner.current.client.images -= GLOB.blood_target_image
+ . = ..()
+
+/datum/antagonist/cult/master
+ var/datum/action/innate/cultmast/finalreck/reckoning = new
+ var/datum/action/innate/cultmast/cultmark/bloodmark = new
+
+/datum/antagonist/cult/master/Destroy()
+ QDEL_NULL(reckoning)
+ QDEL_NULL(bloodmark)
+ return ..()
+
+/datum/antagonist/cult/master/on_gain()
+ . = ..()
+ var/mob/living/current = owner.current
+ SSticker.mode.set_antag_hud(current, "cultmaster")
+
+/datum/antagonist/cult/master/greet()
+ to_chat(owner.current, "You are the cult's Master. As the cult's Master, you have a unique title and loud voice when communicating, are capable of marking \
+ targets, such as a location or a noncultist, to direct the cult to them, and, finally, you are capable of summoning the entire living cult to your location once.")
+ to_chat(owner.current, "Use these abilities to direct the cult to victory at any cost.")
+
+/datum/antagonist/cult/master/apply_innate_effects(mob/living/mob_override)
+ . = ..()
+ var/mob/living/current = owner.current
+ if(mob_override)
+ current = mob_override
+ if(!GLOB.reckoning_complete)
+ reckoning.Grant(current)
+ bloodmark.Grant(current)
+ current.update_action_buttons_icon()
+ current.apply_status_effect(/datum/status_effect/cult_master)
+
+/datum/antagonist/cult/master/remove_innate_effects(mob/living/mob_override)
+ . = ..()
+ var/mob/living/current = owner.current
+ if(mob_override)
+ current = mob_override
+ reckoning.Remove(current)
+ bloodmark.Remove(current)
+ current.update_action_buttons_icon()
+ current.remove_status_effect(/datum/status_effect/cult_master)
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index 7de57e4bb9..fbbf97a6ca 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -27,7 +27,7 @@
. += "---"
.["Call Proc"] = "?_src_=vars;proc_call=\ref[src]"
.["Mark Object"] = "?_src_=vars;mark_object=\ref[src]"
- .["Delete"] = "?_src_=vars;delete=\ref[src]"
+ .["Delete"] = "?_src_=vars;delete=\ref[src]"
/datum/proc/on_reagent_change()
@@ -94,7 +94,6 @@
CLONE:[M.getCloneLoss()]
BRAIN:[M.getBrainLoss()]
STAMINA:[M.getStaminaLoss()]
- AROUSAL:[M.getArousalLoss()]
"}
else
@@ -447,7 +446,7 @@
var/list/L = value
var/list/items = list()
- if (L.len > 0 && !(name == "underlays" || name == "overlays" || L.len > (IS_NORMAL_LIST(L) ? 50 : 150)))
+ if (L.len > 0 && !(name == "underlays" || name == "overlays" || L.len > (IS_NORMAL_LIST(L) ? 50 : 150)))
for (var/i in 1 to L.len)
var/key = L[i]
var/val
@@ -527,16 +526,16 @@
if(T)
callproc_datum(T)
- else if(href_list["delete"])
- if(!check_rights(R_DEBUG, 0))
- return
-
- var/datum/D = locate(href_list["delete"])
- if(!D)
- to_chat(usr, "Unable to locate item!")
- admin_delete(D)
- href_list["datumrefresh"] = href_list["delete"]
-
+ else if(href_list["delete"])
+ if(!check_rights(R_DEBUG, 0))
+ return
+
+ var/datum/D = locate(href_list["delete"])
+ if(!D)
+ to_chat(usr, "Unable to locate item!")
+ admin_delete(D)
+ href_list["datumrefresh"] = href_list["delete"]
+
else if(href_list["regenerateicons"])
if(!check_rights(0))
return
@@ -1166,8 +1165,6 @@
L.adjustCloneLoss(amount)
if("stamina")
L.adjustStaminaLoss(amount)
- if("arousal")
- L.adjustArousalLoss(amount)
else
to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]")
return
@@ -1178,3 +1175,4 @@
message_admins(msg)
admin_ticket_log(L, msg)
href_list["datumrefresh"] = href_list["mobToDamage"]
+
diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm
index 0d6488c633..9f2753dfee 100644
--- a/code/datums/emotes.dm
+++ b/code/datums/emotes.dm
@@ -35,10 +35,6 @@
var/msg = select_message_type(user)
if(params && message_param)
msg = select_param(user, params)
- if(findtext(msg, "their"))
- msg = replacetext(msg, "their", user.p_their())
- if(findtext(msg, "them"))
- msg = replacetext(msg, "them", user.p_them())
if(findtext(msg, "%s"))
msg = replacetext(msg, "%s", user.p_s())
diff --git a/code/datums/helper_datums/getrev.dm b/code/datums/helper_datums/getrev.dm
index 8c9d7d7704..b90e2ec652 100644
--- a/code/datums/helper_datums/getrev.dm
+++ b/code/datums/helper_datums/getrev.dm
@@ -25,7 +25,7 @@
for(var/line in testmerge)
if(line)
log_world("Test merge active of PR #[line]")
- feedback_add_details("testmerged_prs","[line]")
+ SSblackbox.add_details("testmerged_prs","[line]")
log_world("Based off master commit [parentcommit]")
else
log_world(parentcommit)
@@ -75,10 +75,12 @@
if(GLOB.revdata.parentcommit)
to_chat(src, "Server revision compiled on: [GLOB.revdata.date]")
+ var/prefix = ""
if(GLOB.revdata.testmerge.len)
to_chat(src, GLOB.revdata.GetTestMergeInfo())
- to_chat(src, "Based off master commit:")
- to_chat(src, "[GLOB.revdata.parentcommit]")
+ prefix = "Based off master commit: "
+ var/pc = GLOB.revdata.parentcommit
+ to_chat(src, "[prefix][copytext(pc, 1, min(length(pc), 7))]")
else
to_chat(src, "Revision unknown")
to_chat(src, "Current Infomational Settings:")
@@ -89,7 +91,7 @@
to_chat(src, "Enforce Continuous Rounds: [config.continuous.len] of [config.modes.len] roundtypes")
to_chat(src, "Allow Midround Antagonists: [config.midround_antag.len] of [config.modes.len] roundtypes")
if(config.show_game_type_odds)
- if(SSticker.current_state == GAME_STATE_PLAYING)
+ if(SSticker.IsRoundInProgress())
var/prob_sum = 0
var/current_odds_differ = FALSE
var/list/probs = list()
diff --git a/code/datums/holocall.dm b/code/datums/holocall.dm
new file mode 100644
index 0000000000..66085de7d1
--- /dev/null
+++ b/code/datums/holocall.dm
@@ -0,0 +1,160 @@
+
+#define HOLOPAD_MAX_DIAL_TIME 200
+
+/mob/camera/aiEye/remote/holo/setLoc()
+ . = ..()
+ var/obj/machinery/holopad/H = origin
+ H.move_hologram(eye_user, loc)
+
+//this datum manages it's own references
+
+/datum/holocall
+ var/mob/living/user //the one that called
+ var/obj/machinery/holopad/calling_holopad //the one that sent the call
+ var/obj/machinery/holopad/connected_holopad //the one that answered the call (may be null)
+ var/list/dialed_holopads //all things called, will be cleared out to just connected_holopad once answered
+
+ var/mob/camera/aiEye/remote/holo/eye //user's eye, once connected
+ var/obj/effect/overlay/holo_pad_hologram/hologram //user's hologram, once connected
+
+ var/call_start_time
+
+//creates a holocall made by `caller` from `calling_pad` to `callees`
+/datum/holocall/New(mob/living/caller, obj/machinery/holopad/calling_pad, list/callees)
+ call_start_time = world.time
+ user = caller
+ calling_pad.outgoing_call = src
+ calling_holopad = calling_pad
+ dialed_holopads = list()
+
+ for(var/I in callees)
+ var/obj/machinery/holopad/H = I
+ if(!QDELETED(H) && H.is_operational())
+ dialed_holopads += H
+ LAZYADD(H.holo_calls, src)
+
+ if(!dialed_holopads.len)
+ calling_pad.say("Connection failure.")
+ qdel(src)
+ return
+
+ testing("Holocall started")
+
+//cleans up ALL references :)
+/datum/holocall/Destroy()
+ QDEL_NULL(eye)
+
+ user.reset_perspective()
+
+ user = null
+ hologram.HC = null
+ hologram = null
+ calling_holopad.outgoing_call = null
+
+ for(var/I in dialed_holopads)
+ var/obj/machinery/holopad/H = I
+ LAZYREMOVE(H.holo_calls, src)
+ dialed_holopads.Cut()
+
+ if(calling_holopad)
+ calling_holopad.SetLightsAndPower()
+ calling_holopad = null
+ if(connected_holopad)
+ connected_holopad.SetLightsAndPower()
+ connected_holopad = null
+
+ testing("Holocall destroyed")
+
+ return ..()
+
+//Gracefully disconnects a holopad `H` from a call. Pads not in the call are ignored. Notifies participants of the disconnection
+/datum/holocall/proc/Disconnect(obj/machinery/holopad/H)
+ testing("Holocall disconnect")
+ if(H == connected_holopad)
+ calling_holopad.say("[usr] disconnected.")
+ else if(H == calling_holopad && connected_holopad)
+ connected_holopad.say("[usr] disconnected.")
+
+ ConnectionFailure(H, TRUE)
+
+//Forcefully disconnects a holopad `H` from a call. Pads not in the call are ignored.
+/datum/holocall/proc/ConnectionFailure(obj/machinery/holopad/H, graceful = FALSE)
+ testing("Holocall connection failure: graceful [graceful]")
+ if(H == connected_holopad || H == calling_holopad)
+ if(!graceful)
+ calling_holopad.say("Connection failure.")
+ qdel(src)
+ return
+
+ LAZYREMOVE(H.holo_calls, src)
+ dialed_holopads -= H
+ if(!dialed_holopads.len)
+ if(graceful)
+ calling_holopad.say("Call rejected.")
+ testing("No recipients, terminating")
+ qdel(src)
+
+//Answers a call made to a holopad `H` which cannot be the calling holopad. Pads not in the call are ignored
+/datum/holocall/proc/Answer(obj/machinery/holopad/H)
+ testing("Holocall answer")
+ if(H == calling_holopad)
+ CRASH("How cute, a holopad tried to answer itself.")
+
+ if(!(H in dialed_holopads))
+ return
+
+ if(connected_holopad)
+ CRASH("Multi-connection holocall")
+
+ for(var/I in dialed_holopads)
+ if(I == H)
+ continue
+ Disconnect(I)
+
+ for(var/I in H.holo_calls)
+ var/datum/holocall/HC = I
+ if(HC != src)
+ HC.Disconnect(H)
+
+ connected_holopad = H
+
+ if(!Check())
+ return
+
+ hologram = H.activate_holo(user)
+ hologram.HC = src
+
+ //eyeobj code is horrid, this is the best copypasta I could make
+ eye = new
+ eye.origin = H
+ eye.eye_initialized = TRUE
+ eye.eye_user = user
+ eye.name = "Camera Eye ([user.name])"
+ user.remote_control = eye
+ user.reset_perspective(eye)
+ eye.setLoc(H.loc)
+
+//Checks the validity of a holocall and qdels itself if it's not. Returns TRUE if valid, FALSE otherwise
+/datum/holocall/proc/Check()
+ for(var/I in dialed_holopads)
+ var/obj/machinery/holopad/H = I
+ if(!H.is_operational())
+ ConnectionFailure(H)
+
+ if(QDELETED(src))
+ return FALSE
+
+ . = !QDELETED(user) && !user.incapacitated() && !QDELETED(calling_holopad) && calling_holopad.is_operational() && user.loc == calling_holopad.loc
+
+ if(.)
+ if(connected_holopad)
+ . = !QDELETED(connected_holopad) && connected_holopad.is_operational()
+ else
+ . = world.time < (call_start_time + HOLOPAD_MAX_DIAL_TIME)
+ if(!.)
+ calling_holopad.say("No answer recieved.")
+ calling_holopad.temp = ""
+
+ if(!.)
+ testing("Holocall Check fail")
+ qdel(src)
diff --git a/code/datums/material_container.dm b/code/datums/material_container.dm
index af227212a7..76de0329cd 100644
--- a/code/datums/material_container.dm
+++ b/code/datums/material_container.dm
@@ -136,7 +136,7 @@
return FALSE
//For spawning mineral sheets; internal use only
-/datum/material_container/proc/retrieve(sheet_amt, datum/material/M)
+/datum/material_container/proc/retrieve(sheet_amt, datum/material/M, target = null)
if(!M.sheet_type)
return 0
if(sheet_amt > 0)
@@ -149,26 +149,28 @@
use_amount_type(sheet_amt * MINERAL_MATERIAL_AMOUNT, M.id)
sheet_amt -= MAX_STACK_SIZE
if(round((sheet_amt * MINERAL_MATERIAL_AMOUNT) / MINERAL_MATERIAL_AMOUNT))
- new M.sheet_type(get_turf(owner), sheet_amt)
+ var/obj/item/stack/sheet/s = new M.sheet_type(get_turf(owner), sheet_amt)
+ if(target)
+ s.forceMove(target)
count += sheet_amt
use_amount_type(sheet_amt * MINERAL_MATERIAL_AMOUNT, M.id)
return count
return 0
-/datum/material_container/proc/retrieve_sheets(sheet_amt, id)
+/datum/material_container/proc/retrieve_sheets(sheet_amt, id, target = null)
if(materials[id])
- return retrieve(sheet_amt, materials[id])
+ return retrieve(sheet_amt, materials[id], target)
return 0
-/datum/material_container/proc/retrieve_amount(amt, id)
- return retrieve_sheets(amount2sheet(amt), id)
+/datum/material_container/proc/retrieve_amount(amt, id, target)
+ return retrieve_sheets(amount2sheet(amt), id, target)
-/datum/material_container/proc/retrieve_all()
+/datum/material_container/proc/retrieve_all(target = null)
var/result = 0
var/datum/material/M
for(var/MAT in materials)
M = materials[MAT]
- result += retrieve_sheets(amount2sheet(M.amount), MAT)
+ result += retrieve_sheets(amount2sheet(M.amount), MAT, target)
return result
/datum/material_container/proc/has_space(amt = 0)
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 5a6c34bcc4..4dba11e02b 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -44,9 +44,7 @@
var/datum/job/assigned_job
var/list/datum/objective/objectives = list()
- var/list/datum/objective/special_verbs = list()
- var/list/cult_words = list()
var/list/spell_list = list() // Wizard mode & "Give Spell" badmin button.
var/datum/faction/faction //associated faction
@@ -54,6 +52,7 @@
var/linglink
var/miming = 0 // Mime's vow of silence
+ var/list/antag_datums
var/antag_hud_icon_state = null //this mind's ANTAG_HUD should have this icon_state
var/datum/atom_hud/antag/antag_hud = null //this mind's antag HUD
var/datum/gang/gang_datum //Which gang this mind belongs to, if any
@@ -64,19 +63,36 @@
var/mob/living/enslaved_to //If this mind's master is another mob (i.e. adamantine golems)
+ var/datum/language_holder/language_holder
+
/datum/mind/New(var/key)
src.key = key
soulOwner = src
/datum/mind/Destroy()
SSticker.minds -= src
+ if(islist(antag_datums))
+ for(var/i in antag_datums)
+ qdel(i)
+ antag_datums = null
return ..()
+/datum/mind/proc/get_language_holder()
+ if(!language_holder)
+ var/datum/language_holder/L = current.get_language_holder(shadow=FALSE)
+ language_holder = L.copy(src)
+
+ return language_holder
+
/datum/mind/proc/transfer_to(mob/new_character, var/force_key_move = 0)
if(current) // remove ourself from our old body's mind variable
current.mind = null
SStgui.on_transfer(current, new_character)
+ if(!language_holder)
+ var/datum/language_holder/mob_holder = new_character.get_language_holder(shadow = FALSE)
+ language_holder = mob_holder.copy(src)
+
if(key)
if(new_character.key != key) //if we're transfering into a body with a key associated which is not ours
new_character.ghostize(1) //we'll need to ghostize so that key isn't mobless.
@@ -86,13 +102,13 @@
if(new_character.mind) //disassociate any mind currently in our new body's mind variable
new_character.mind.current = null
- if(istype(current) && islist(current.antag_datums)) //wow apparently current isn't always living good fucking job SOMEONE
- for(var/i in current.antag_datums)
- var/datum/antagonist/D = i
- D.transfer_to_new_body(new_character)
var/datum/atom_hud/antag/hud_to_transfer = antag_hud//we need this because leave_hud() will clear this list
+ var/mob/living/old_current = current
current = new_character //associate ourself with our new body
new_character.mind = src //and associate our new body with ourself
+ for(var/a in antag_datums) //Makes sure all antag datums effects are applied in the new body
+ var/datum/antagonist/A = a
+ A.on_body_transfer(old_current, current)
if(iscarbon(new_character))
var/mob/living/carbon/C = new_character
C.last_mind = src
@@ -108,6 +124,41 @@
/datum/mind/proc/wipe_memory()
memory = null
+// Datum antag mind procs
+/datum/mind/proc/add_antag_datum(datum_type)
+ if(!datum_type)
+ return
+ var/datum/antagonist/A = new datum_type(src)
+ if(!A.can_be_owned(src))
+ qdel(A)
+ return
+ LAZYADD(antag_datums, A)
+ A.on_gain()
+ return A
+
+/datum/mind/proc/remove_antag_datum(datum_type)
+ if(!datum_type)
+ return
+ var/datum/antagonist/A = has_antag_datum(datum_type)
+ if(A)
+ A.on_removal()
+ return TRUE
+
+/datum/mind/proc/remove_all_antag_datums() //For the Lazy amongst us.
+ for(var/a in antag_datums)
+ var/datum/antagonist/A = a
+ A.on_removal()
+
+/datum/mind/proc/has_antag_datum(datum_type, check_subtypes = TRUE)
+ if(!datum_type)
+ return
+ . = FALSE
+ for(var/a in antag_datums)
+ var/datum/antagonist/A = a
+ if(check_subtypes && istype(A, datum_type))
+ return A
+ else if(A.type == datum_type)
+ return A
/*
Removes antag type's references from a mind.
@@ -234,7 +285,7 @@
creator.faction |= current.faction
if(creator.mind.special_role)
- message_admins("[key_name_admin(current)](?) has been created by [key_name_admin(creator)](?), an antagonist.")
+ message_admins("[ADMIN_LOOKUPFLW(current)] has been created by [ADMIN_LOOKUPFLW(creator)], an antagonist.")
to_chat(current, "Despite your creators current allegiances, your true master remains [creator.real_name]. If their loyalities change, so do yours. This will never change unless your creator's body is destroyed.")
/datum/mind/proc/show_memory(mob/recipient, window=1)
@@ -255,7 +306,7 @@
to_chat(recipient, "[output]")
/datum/mind/proc/edit_memory()
- if(!SSticker || !SSticker.mode)
+ if(!SSticker.HasRoundStarted())
alert("Not before round-start!", "Alert")
return
@@ -1143,7 +1194,7 @@
log_admin("[key_name_admin(usr)] has made [current] unable to ascend as a devil.")
return
if(!ishuman(current) && !iscyborg(current))
- to_chat(usr, "This only works on humans and cyborgs!")
+ to_chat(usr, "This only works on humans and cyborgs!")
return
SSticker.mode.devils += src
special_role = "devil"
@@ -1192,17 +1243,17 @@
log_admin("[key_name(usr)] turned [current] into abductor.")
SSticker.mode.update_abductor_icons_added(src)
if("equip")
- if(!ishuman(current))
- to_chat(usr, "This only works on humans!")
- return
-
- var/mob/living/carbon/human/H = current
+ if(!ishuman(current))
+ to_chat(usr, "This only works on humans!")
+ return
+
+ var/mob/living/carbon/human/H = current
var/gear = alert("Agent or Scientist Gear","Gear","Agent","Scientist")
if(gear)
if(gear=="Agent")
- H.equipOutfit(/datum/outfit/abductor/agent)
+ H.equipOutfit(/datum/outfit/abductor/agent)
else
- H.equipOutfit(/datum/outfit/abductor/scientist)
+ H.equipOutfit(/datum/outfit/abductor/scientist)
else if (href_list["monkey"])
var/mob/living/L = current
@@ -1399,15 +1450,8 @@
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 the world is, you see that it should be open to the knowledge of Nar-Sie.")
to_chat(current, "Assist your new compatriots 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/game_mode/cult/cult = SSticker.mode
-
- if (istype(cult))
- cult.memorize_cult_objectives(src)
- else
- var/explanation = "Summon Nar-Sie via the use of the appropriate rune (Hell join self). It will only work if nine cultists stand on and around it."
- to_chat(current, "Objective #1: [explanation]")
- memory += "Objective #1: [explanation]
"
-
+ 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.")
@@ -1471,8 +1515,8 @@
H.set_species(/datum/species/abductor)
var/datum/species/abductor/S = H.dna.species
- if(role == "Scientist")
- S.scientist = TRUE
+ if(role == "Scientist")
+ S.scientist = TRUE
S.team = team
var/list/obj/effect/landmark/abductor/agent_landmarks = new
@@ -1491,8 +1535,8 @@
if("Agent")
L = agent_landmarks[team]
if("Scientist")
- L = scientist_landmarks[team]
- H.forceMove(L.loc)
+ L = scientist_landmarks[team]
+ H.forceMove(L.loc)
/datum/mind/proc/AddSpell(obj/effect/proc_holder/spell/S)
spell_list += S
diff --git a/code/datums/ruins/lavaland.dm b/code/datums/ruins/lavaland.dm
index 3a54db4e0a..011363f001 100644
--- a/code/datums/ruins/lavaland.dm
+++ b/code/datums/ruins/lavaland.dm
@@ -42,6 +42,7 @@
However, all the inhabitants seem to do is grow drugs and guns."
suffix = "lavaland_surface_seed_vault.dmm"
cost = 10
+ allow_duplicates = FALSE
/datum/map_template/ruin/lavaland/ash_walker
name = "Ash Walker Nest"
diff --git a/code/datums/ruins/space.dm b/code/datums/ruins/space.dm
index ab55919a03..36883c59fe 100644
--- a/code/datums/ruins/space.dm
+++ b/code/datums/ruins/space.dm
@@ -43,6 +43,13 @@
name = "Asteroid 5"
description = "Oh my god, another giant rock!"
+/datum/map_template/ruin/space/deep_storage
+ id = "deep-storage"
+ suffix = "deepstorage.dmm"
+ name = "Survivalist Bunker"
+ description = "Assume the best, prepare for the worst. Generally, you should do so by digging a three man heavily fortified bunker into a giant unused asteroid. \
+ Then make it self sufficient, mask any evidence of construction, hook it covertly into the telecommunications network and hope for the best."
+
/datum/map_template/ruin/space/bigderelict1
id = "bigderelict1"
suffix = "bigderelict1.dmm"
diff --git a/code/datums/shuttles.dm b/code/datums/shuttles.dm
index 774e0794ff..d3427ac983 100644
--- a/code/datums/shuttles.dm
+++ b/code/datums/shuttles.dm
@@ -72,7 +72,7 @@
/datum/map_template/shuttle/emergency/meteor
suffix = "meteor"
- name = "An Asteroid With Engines Strapped To It"
+ name = "Asteroid With Engines Strapped To It"
description = "A hollowed out asteroid with engines strapped to it. Due to its size and difficulty in steering it, this shuttle may damage the docking area."
admin_notes = "This shuttle will likely crush escape, killing anyone there."
credit_cost = -5000
diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm
index d07f66609b..a0338f72b0 100644
--- a/code/datums/status_effects/buffs.dm
+++ b/code/datums/status_effects/buffs.dm
@@ -13,6 +13,7 @@
/datum/status_effect/shadow_mend/on_apply()
owner.visible_message("Violet light wraps around [owner]'s body!", "Violet light wraps around your body!")
playsound(owner, 'sound/magic/Teleport_app.ogg', 50, 1)
+ return ..()
/datum/status_effect/shadow_mend/tick()
owner.adjustBruteLoss(-15)
@@ -72,9 +73,12 @@
add_logs(owner, null, "gained Vanguard stun immunity")
owner.add_stun_absorption("vanguard", 200, 1, "'s yellow aura momentarily intensifies!", "Your ward absorbs the stun!", " radiating with a soft yellow light!")
owner.visible_message("[owner] begins to faintly glow!", "You will absorb all stuns for the next twenty seconds.")
+ owner.SetStunned(0, FALSE)
+ owner.SetWeakened(0)
progbar = new(owner, duration, owner)
progbar.bar.color = list("#FAE48C", "#FAE48C", "#FAE48C", rgb(0,0,0))
progbar.update(duration - world.time)
+ return ..()
/datum/status_effect/vanguard_shield/tick()
progbar.update(duration - world.time)
@@ -127,6 +131,7 @@
animate(owner, color = oldcolor, time = 150, easing = EASE_IN)
addtimer(CALLBACK(owner, /atom/proc/update_atom_colour), 150)
playsound(owner, 'sound/magic/Ethereal_Enter.ogg', 50, 1)
+ return ..()
/datum/status_effect/inathneqs_endowment/on_remove()
add_logs(owner, null, "lost Inath-neq's invulnerability")
@@ -178,6 +183,7 @@
/datum/status_effect/his_grace/on_apply()
add_logs(owner, null, "gained His Grace's stun immunity")
owner.add_stun_absorption("hisgrace", INFINITY, 3, null, "His Grace protects you from the stun!")
+ return ..()
/datum/status_effect/his_grace/tick()
bloodlust = 0
@@ -211,6 +217,7 @@
/datum/status_effect/wish_granters_gift/on_apply()
to_chat(owner, "Death is not your end! The Wish Granter's energy suffuses you, and you begin to rise...")
+ return ..()
/datum/status_effect/wish_granters_gift/on_remove()
owner.revive(full_heal = 1, admin_revive = 1)
@@ -221,3 +228,32 @@
name = "Wish Granter's Immortality"
desc = "You are being resurrected!"
icon_state = "wish_granter"
+
+/datum/status_effect/cult_master
+ id = "The Cult Master"
+ duration = -1
+ tick_interval = 100
+ alert_type = null
+ var/alive = TRUE
+
+/datum/status_effect/cult_master/proc/deathrattle()
+ var/area/A = get_area(owner)
+ for(var/datum/mind/B in SSticker.mode.cult)
+ if(isliving(B.current))
+ var/mob/living/M = B.current
+ M << 'sound/hallucinations/veryfar_noise.ogg'
+ to_chat(M, "The Cult's Master, [owner], has fallen in the [A]!")
+
+
+/datum/status_effect/cult_master/tick()
+ if(owner.stat != DEAD && !alive)
+ alive = TRUE
+ return
+ if(owner.stat == DEAD && alive)
+ alive = FALSE
+ deathrattle()
+
+/datum/status_effect/cult_master/on_remove()
+ deathrattle()
+ . = ..()
+
diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm
index f78a3ae522..9f6e481edb 100644
--- a/code/datums/status_effects/debuffs.dm
+++ b/code/datums/status_effects/debuffs.dm
@@ -29,3 +29,136 @@
owner.adjustBruteLoss(0.1)
owner.adjustFireLoss(0.1)
owner.adjustToxLoss(0.2, TRUE, TRUE)
+
+/datum/status_effect/belligerent
+ id = "belligerent"
+ duration = 70
+ tick_interval = 0 //tick as fast as possible
+ status_type = STATUS_EFFECT_REPLACE
+ alert_type = /obj/screen/alert/status_effect/belligerent
+ var/leg_damage_on_toggle = 2 //damage on initial application and when the owner tries to toggle to run
+ var/cultist_damage_on_toggle = 10 //damage on initial application and when the owner tries to toggle to run, but to cultists
+
+/obj/screen/alert/status_effect/belligerent
+ name = "Belligerent"
+ desc = "Kneel, her-eti'c."
+ icon_state = "belligerent"
+ alerttooltipstyle = "clockcult"
+
+/datum/status_effect/belligerent/on_apply()
+ return do_movement_toggle(TRUE)
+
+/datum/status_effect/belligerent/tick()
+ if(!do_movement_toggle())
+ qdel(src)
+
+/datum/status_effect/belligerent/proc/do_movement_toggle(force_damage)
+ var/number_legs = owner.get_num_legs()
+ if(iscarbon(owner) && !is_servant_of_ratvar(owner) && !owner.null_rod_check() && number_legs)
+ if(force_damage || owner.m_intent != MOVE_INTENT_WALK)
+ if(GLOB.ratvar_awakens)
+ owner.Weaken(1)
+ if(iscultist(owner))
+ owner.apply_damage(cultist_damage_on_toggle * 0.5, BURN, "l_leg")
+ owner.apply_damage(cultist_damage_on_toggle * 0.5, BURN, "r_leg")
+ else
+ owner.apply_damage(leg_damage_on_toggle * 0.5, BURN, "l_leg")
+ owner.apply_damage(leg_damage_on_toggle * 0.5, BURN, "r_leg")
+ if(owner.m_intent != MOVE_INTENT_WALK)
+ if(!iscultist(owner))
+ to_chat(owner, "Your leg[number_legs > 1 ? "s shiver":" shivers"] with pain!")
+ else //Cultists take extra burn damage
+ to_chat(owner, "Your leg[number_legs > 1 ? "s burn":" burns"] with pain!")
+ owner.toggle_move_intent()
+ return TRUE
+ return FALSE
+
+/datum/status_effect/belligerent/on_remove()
+ if(owner.m_intent == MOVE_INTENT_WALK)
+ owner.toggle_move_intent()
+
+
+/datum/status_effect/maniamotor
+ id = "maniamotor"
+ duration = -1
+ tick_interval = 10
+ status_type = STATUS_EFFECT_MULTIPLE
+ alert_type = null
+ var/obj/structure/destructible/clockwork/powered/mania_motor/motor
+ var/severity = 0 //goes up to a maximum of MAX_MANIA_SEVERITY
+ var/warned_turnoff = FALSE //if we've warned that the motor is off
+ var/warned_outofsight = FALSE //if we've warned that the target is out of sight of the motor
+ var/static/list/mania_messages = list("Go nuts.", "Take a crack at crazy.", "Make a bid for insanity.", "Get kooky.", "Move towards mania.", "Become bewildered.", "Wax wild.", \
+ "Go round the bend.", "Land in lunacy.", "Try dementia.", "Strive to get a screw loose.", "Advance forward.", "Approach the transmitter.", "Touch the antennae.", \
+ "Move towards the mania motor.", "Come closer.", "Get over here already!", "Keep your eyes on the motor.")
+ var/static/list/flee_messages = list("Oh, NOW you flee.", "Get back here!", "If you were smarter, you'd come back.", "Only fools run.", "You'll be back.")
+ var/static/list/turnoff_messages = list("Why would they turn it-", "What are these idi-", "Fools, fools, all of-", "Are they trying to c-", "All this effort just f-")
+ var/static/list/powerloss_messages = list("\"Oh, the id**ts di***t s***e en**** pow**...\"", "\"D*dn't **ey mak* an **te***c*i*n le**?\"", "\"The** f**ls for**t t* make a ***** *f-\"", \
+ "\"No, *O, you **re so cl***-\"", "You hear a yell of frustration, cut off by static.")
+
+/datum/status_effect/maniamotor/Destroy()
+ motor = null
+ return ..()
+
+/datum/status_effect/maniamotor/tick()
+ var/is_servant = is_servant_of_ratvar(owner)
+ var/span_part = severity > 50 ? "" : "_small" //let's save like one check
+ if(QDELETED(motor))
+ if(!is_servant)
+ to_chat(owner, "You feel a frustrated voice quietly fade from your mind...")
+ qdel(src)
+ return
+ if(!motor.active) //it being off makes it fall off much faster
+ if(!is_servant && !warned_turnoff)
+ if(motor.total_accessable_power() > motor.mania_cost)
+ to_chat(owner, "\"[text2ratvar(pick(turnoff_messages))]\"")
+ else
+ to_chat(owner, "[text2ratvar(pick(powerloss_messages))]")
+ warned_turnoff = TRUE
+ severity = max(severity - 2, 0)
+ if(!severity)
+ qdel(src)
+ return
+ else
+ if(prob(severity * 2))
+ warned_turnoff = FALSE
+ if(!(owner in viewers(7, motor))) //not being in range makes it fall off slightly faster
+ if(!is_servant && !warned_outofsight)
+ to_chat(owner, "\"[text2ratvar(pick(flee_messages))]\"")
+ warned_outofsight = TRUE
+ severity = max(severity - 1, 0)
+ if(!severity)
+ qdel(src)
+ return
+ else if(prob(severity * 2))
+ warned_outofsight = FALSE
+ if(is_servant) //heals servants of braindamage, hallucination, druggy, dizziness, and confusion
+ if(owner.hallucination)
+ owner.hallucination = 0
+ if(owner.druggy)
+ owner.adjust_drugginess(-owner.druggy)
+ if(owner.dizziness)
+ owner.dizziness = 0
+ if(owner.confused)
+ owner.confused = 0
+ severity = 0
+ else if(!owner.null_rod_check() && owner.stat != DEAD && severity)
+ var/static/hum = get_sfx('sound/effects/screech.ogg') //same sound for every proc call
+ if(owner.getToxLoss() > MANIA_DAMAGE_TO_CONVERT)
+ if(is_eligible_servant(owner))
+ to_chat(owner, "\"[text2ratvar("You are mine and his, now.")]\"")
+ add_servant_of_ratvar(owner)
+ owner.Paralyse(5)
+ else
+ if(prob(severity * 0.15))
+ to_chat(owner, "\"[text2ratvar(pick(mania_messages))]\"")
+ owner.playsound_local(get_turf(motor), hum, severity, 1)
+ owner.adjust_drugginess(Clamp(max(severity * 0.075, 1), 0, max(0, 50 - owner.druggy))) //7.5% of severity per second, minimum 1
+ if(owner.hallucination < 50)
+ owner.hallucination = min(owner.hallucination + max(severity * 0.075, 1), 50) //7.5% of severity per second, minimum 1
+ if(owner.dizziness < 50)
+ owner.dizziness = min(owner.dizziness + round(severity * 0.05, 1), 50) //5% of severity per second above 10 severity
+ if(owner.confused < 25)
+ owner.confused = min(owner.confused + round(severity * 0.025, 1), 25) //2.5% of severity per second above 20 severity
+ owner.adjustToxLoss(severity * 0.02, TRUE, TRUE) //2% of severity per second
+ severity--
diff --git a/code/datums/status_effects/gas.dm b/code/datums/status_effects/gas.dm
index c2c070dbec..ff92b67978 100644
--- a/code/datums/status_effects/gas.dm
+++ b/code/datums/status_effects/gas.dm
@@ -16,6 +16,7 @@
cube = icon('icons/effects/freeze.dmi', "ice_cube")
owner.add_overlay(cube)
owner.update_canmove()
+ return ..()
/datum/status_effect/freon/tick()
owner.update_canmove()
diff --git a/code/datums/status_effects/status_effect.dm b/code/datums/status_effects/status_effect.dm
index 8b6d255f1a..61c8607975 100644
--- a/code/datums/status_effects/status_effect.dm
+++ b/code/datums/status_effects/status_effect.dm
@@ -8,7 +8,9 @@
var/tick_interval = 10 //How many deciseconds between ticks, approximately. Leave at 10 for every second.
var/mob/living/owner //The mob affected by the status effect.
var/status_type = STATUS_EFFECT_UNIQUE //How many of the effect can be on one mob, and what happens when you try to add another
+ var/on_remove_on_mob_delete = FALSE //if we call on_remove() when the mob is deleted
var/alert_type = /obj/screen/alert/status_effect //the alert thrown by the status effect, contains name and description
+ var/obj/screen/alert/status_effect/linked_alert = null //the alert itself, if it exists
/datum/status_effect/New(mob/living/new_owner)
if(new_owner)
@@ -21,23 +23,24 @@
STOP_PROCESSING(SSfastprocess, src)
if(owner)
owner.clear_alert(id)
- on_remove()
LAZYREMOVE(owner.status_effects, src)
+ on_remove()
+ owner = null
return ..()
/datum/status_effect/proc/start_ticking()
if(!src)
return
- if(!owner)
+ if(!owner || !on_apply())
qdel(src)
return
- on_apply()
if(duration != -1)
duration = world.time + initial(duration)
tick_interval = world.time + initial(tick_interval)
if(alert_type)
var/obj/screen/alert/status_effect/A = owner.throw_alert(id, alert_type)
A.attached_effect = src //so the alert can reference us, if it needs to
+ linked_alert = A //so we can reference the alert, if we need to
START_PROCESSING(SSfastprocess, src)
/datum/status_effect/process()
@@ -51,9 +54,11 @@
qdel(src)
/datum/status_effect/proc/on_apply() //Called whenever the buff is applied.
+ return TRUE
+
/datum/status_effect/proc/tick() //Called every tick.
-/datum/status_effect/proc/on_remove() //Called whenever the buff expires or is removed.
-/datum/status_effect/proc/be_replaced() //Called instead of on_remove when a status effect is replaced by itself
+/datum/status_effect/proc/on_remove() //Called whenever the buff expires or is removed; do note that at the point this is called, it is out of the owner's status_effects but owner is not yet null
+/datum/status_effect/proc/be_replaced() //Called instead of on_remove when a status effect is replaced by itself or when a status effect with on_remove_on_mob_delete = FALSE has its mob deleted
owner.clear_alert(id)
LAZYREMOVE(owner.status_effects, src)
owner = null
@@ -101,3 +106,11 @@
for(var/datum/status_effect/S in status_effects)
if(initial(S1.id) == S.id)
return S
+
+/mob/living/proc/has_status_effect_list(effect) //returns a list of effects with matching IDs that the mod owns; use for effects there can be multiple of
+ . = list()
+ if(status_effects)
+ var/datum/status_effect/S1 = effect
+ for(var/datum/status_effect/S in status_effects)
+ if(initial(S1.id) == S.id)
+ . += S
diff --git a/code/game/area/areas/holodeck.dm b/code/game/area/areas/holodeck.dm
index c4b4e02f7b..7bf213be52 100644
--- a/code/game/area/areas/holodeck.dm
+++ b/code/game/area/areas/holodeck.dm
@@ -96,16 +96,15 @@
/area/holodeck/rec_center/kobayashi
name = "Holodeck - Kobayashi Maru"
+/area/holodeck/rec_center/winterwonderland
+ name = "Holodeck - Winter Wonderland"
+
// Bad programs
/area/holodeck/rec_center/burn
name = "Holodeck - Atmospheric Burn Test"
restricted = 1
-/area/holodeck/rec_center/winterwonderland
- name = "Holodeck - Winter Wonderland"
- restricted = 1
-
/area/holodeck/rec_center/wildlife
name = "Holodeck - Wildlife Simulation"
restricted = 1
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index ab0038f7be..4a0f05602b 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -43,7 +43,7 @@
if(SSatoms.InitAtom(src, args))
//we were deleted
return
-
+
var/list/created = SSatoms.created_atoms
if(created)
created += src
@@ -274,6 +274,7 @@
return
/atom/proc/ex_act(severity, target)
+ set waitfor = FALSE
contents_explosion(severity, target)
/atom/proc/blob_act(obj/structure/blob/B)
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 5d81e1f80c..02d0d38cca 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -13,9 +13,9 @@
var/throw_speed = 2 //How many tiles to move per ds when being thrown. Float values are fully supported
var/throw_range = 7
var/mob/pulledby = null
- var/list/languages
- var/list/initial_languages = list(/datum/language/common)
- var/only_speaks_language = null
+ var/initial_language_holder = /datum/language_holder
+ var/datum/language_holder/language_holder
+ var/datum/language_menu/language_menu = null
var/verb_say = "says"
var/verb_ask = "asks"
var/verb_exclaim = "exclaims"
@@ -44,10 +44,6 @@
return FALSE
return ..()
-/atom/movable/Initialize(mapload)
- . = ..()
- for(var/L in initial_languages)
- grant_language(L)
/atom/movable/Move(atom/newloc, direct = 0)
if(!loc || !newloc) return 0
@@ -120,7 +116,7 @@
if(flags & CLEAN_ON_MOVE)
clean_on_move()
-
+
var/datum/proximity_monitor/proximity_monitor = src.proximity_monitor
if(proximity_monitor)
proximity_monitor.HandleMove()
@@ -177,6 +173,7 @@
STOP_PROCESSING(SSinbounds, src)
QDEL_NULL(proximity_monitor)
+ QDEL_NULL(language_holder)
. = ..()
if(loc)
@@ -576,44 +573,70 @@
/* Language procs */
+/atom/movable/proc/get_language_holder(shadow=TRUE)
+ if(language_holder)
+ return language_holder
+ else
+ language_holder = new initial_language_holder(src)
+ return language_holder
+
+
/atom/movable/proc/grant_language(datum/language/dt)
- LAZYINITLIST(languages)
- languages[dt] = TRUE
+ var/datum/language_holder/H = get_language_holder()
+ H.grant_language(dt)
/atom/movable/proc/grant_all_languages(omnitongue=FALSE)
- for(var/la in subtypesof(/datum/language))
- grant_language(la)
-
- if(omnitongue)
- SET_SECONDARY_FLAG(src, OMNITONGUE)
+ var/datum/language_holder/H = get_language_holder()
+ H.grant_all_languages(omnitongue)
/atom/movable/proc/get_random_understood_language()
- var/list/possible = list()
- for(var/dt in languages)
- possible += dt
- . = safepick(possible)
+ var/datum/language_holder/H = get_language_holder()
+ . = H.get_random_understood_language()
/atom/movable/proc/remove_language(datum/language/dt)
- LAZYREMOVE(languages, dt)
+ var/datum/language_holder/H = get_language_holder()
+ H.remove_language(dt)
/atom/movable/proc/remove_all_languages()
- LAZYCLEARLIST(languages)
+ var/datum/language_holder/H = get_language_holder()
+ H.remove_all_languages()
/atom/movable/proc/has_language(datum/language/dt)
- . = is_type_in_typecache(dt, languages)
+ var/datum/language_holder/H = get_language_holder()
+ . = H.has_language(dt)
+
+// Whether an AM can speak in a language or not, independent of whether
+// it KNOWS the language
+/atom/movable/proc/could_speak_in_language(datum/language/dt)
+ . = TRUE
/atom/movable/proc/can_speak_in_language(datum/language/dt)
- . = has_language(dt)
- if(only_speaks_language && !HAS_SECONDARY_FLAG(src, OMNITONGUE))
- . = . && ispath(only_speaks_language, dt)
+ var/datum/language_holder/H = get_language_holder()
+
+ if(!H.has_language(dt))
+ return FALSE
+ else if(H.omnitongue || could_speak_in_language(dt))
+ return TRUE
+ else
+ return FALSE
/atom/movable/proc/get_default_language()
// if no language is specified, and we want to say() something, which
// language do we use?
+ var/datum/language_holder/H = get_language_holder()
+
+ if(H.selected_default_language)
+ if(H.has_language(H.selected_default_language))
+ return H.selected_default_language
+ else
+ H.selected_default_language = null
+
+
+
var/datum/language/chosen_langtype
var/highest_priority
- for(var/lt in languages)
+ for(var/lt in H.languages)
var/datum/language/langtype = lt
if(!can_speak_in_language(langtype))
continue
@@ -622,8 +645,10 @@
if(!highest_priority || (pri > highest_priority))
chosen_langtype = langtype
highest_priority = pri
-
+ H.selected_default_language = .
. = chosen_langtype
+
+/* End language procs */
/atom/movable/proc/ConveyorMove(movedir)
set waitfor = FALSE
if(!anchored && has_gravity())
diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm
index de830c856b..2e5854509a 100644
--- a/code/game/data_huds.dm
+++ b/code/game/data_huds.dm
@@ -24,7 +24,7 @@
if(!istype(H)) return 0
var/obj/item/clothing/under/U = H.w_uniform
if(!istype(U)) return 0
- if(U.sensor_mode <= 2) return 0
+ if(U.sensor_mode <= SENSOR_VITALS) return 0
return 1
/datum/atom_hud/data/human/medical/basic/add_to_single_hud(mob/M, mob/living/carbon/H)
diff --git a/code/game/gamemodes/antag_spawner.dm b/code/game/gamemodes/antag_spawner.dm
index 74c16cf132..5b568970d9 100644
--- a/code/game/gamemodes/antag_spawner.dm
+++ b/code/game/gamemodes/antag_spawner.dm
@@ -155,7 +155,8 @@
if(!(check_usability(user)))
return
- var/list/nuke_candidates = pollCandidatesForMob("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", ROLE_OPERATIVE, null, ROLE_OPERATIVE, 150, src)
+ to_chat(user, "You activate [src] and wait for confirmation.")
+ var/list/nuke_candidates = pollCandidatesForMob("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", ROLE_OPERATIVE, null, ROLE_OPERATIVE, 150, POLL_IGNORE_SYNDICATE, src)
if(nuke_candidates.len)
if(!(check_usability(user)))
return
diff --git a/code/game/gamemodes/blob/blob_finish.dm b/code/game/gamemodes/blob/blob_finish.dm
index a5fc5e339a..47e2ce6a14 100644
--- a/code/game/gamemodes/blob/blob_finish.dm
+++ b/code/game/gamemodes/blob/blob_finish.dm
@@ -20,7 +20,7 @@
if(round_converted) //So badmin blobs later don't step on the dead natural blobs metaphorical toes
..()
if(blobwincount <= GLOB.blobs_legit.len)
- feedback_set_details("round_end_result","win - blob took over")
+ SSblackbox.set_details("round_end_result","win - blob took over")
to_chat(world, "The blob has taken over the station!")
to_chat(world, "The entire station was eaten by the Blob!")
log_game("Blob mode completed with a blob victory.")
@@ -28,7 +28,7 @@
SSticker.news_report = BLOB_WIN
else if(station_was_nuked)
- feedback_set_details("round_end_result","halfwin - nuke")
+ SSblackbox.set_details("round_end_result","halfwin - nuke")
to_chat(world, "Partial Win: The station has been destroyed!")
to_chat(world, "Directive 7-12 has been successfully carried out, preventing the Blob from spreading.")
log_game("Blob mode completed with a tie (station destroyed).")
@@ -36,7 +36,7 @@
SSticker.news_report = BLOB_NUKE
else if(!GLOB.blob_cores.len)
- feedback_set_details("round_end_result","loss - blob eliminated")
+ SSblackbox.set_details("round_end_result","loss - blob eliminated")
to_chat(world, "The staff has won!")
to_chat(world, "The alien organism has been eradicated from the station!")
log_game("Blob mode completed with a crew victory.")
diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm
index fc39f2222e..9d281b5f2f 100644
--- a/code/game/gamemodes/changeling/changeling.dm
+++ b/code/game/gamemodes/changeling/changeling.dm
@@ -254,19 +254,19 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w
for(var/datum/objective/objective in changeling.objectives)
if(objective.check_completion())
text += "
Objective #[count]: [objective.explanation_text] Success!"
- feedback_add_details("changeling_objective","[objective.type]|SUCCESS")
+ SSblackbox.add_details("changeling_objective","[objective.type]|SUCCESS")
else
text += "
Objective #[count]: [objective.explanation_text] Fail."
- feedback_add_details("changeling_objective","[objective.type]|FAIL")
+ SSblackbox.add_details("changeling_objective","[objective.type]|FAIL")
changelingwin = 0
count++
if(changelingwin)
text += "
The changeling was successful!"
- feedback_add_details("changeling_success","SUCCESS")
+ SSblackbox.add_details("changeling_success","SUCCESS")
else
text += "
The changeling has failed."
- feedback_add_details("changeling_success","FAIL")
+ SSblackbox.add_details("changeling_success","FAIL")
text += "
"
to_chat(world, text)
diff --git a/code/game/gamemodes/changeling/changeling_power.dm b/code/game/gamemodes/changeling/changeling_power.dm
index 1673e59f49..489228a30c 100644
--- a/code/game/gamemodes/changeling/changeling_power.dm
+++ b/code/game/gamemodes/changeling/changeling_power.dm
@@ -13,15 +13,13 @@
var/req_dna = 0 //amount of dna needed to use this ability. Changelings always have atleast 1
var/req_human = 0 //if you need to be human to use this ability
var/req_stat = CONSCIOUS // CONSCIOUS, UNCONSCIOUS or DEAD
- var/genetic_damage = 0 // genetic damage caused by using the sting. Nothing to do with cloneloss.
- var/max_genetic_damage = 100 // hard counter for spamming abilities. Not used/balanced much yet.
var/always_keep = 0 // important for abilities like revive that screw you if you lose them.
var/ignores_fakedeath = FALSE // usable with the FAKEDEATH flag
/obj/effect/proc_holder/changeling/proc/on_purchase(mob/user, is_respec)
if(!is_respec)
- feedback_add_details("changeling_power_purchase",name)
+ SSblackbox.add_details("changeling_power_purchase",name)
/obj/effect/proc_holder/changeling/proc/on_refund(mob/user)
return
@@ -37,9 +35,9 @@
return
var/datum/changeling/c = user.mind.changeling
if(sting_action(user, target))
- feedback_add_details("changeling_powers",name)
+ SSblackbox.add_details("changeling_powers",name)
sting_feedback(user, target)
- take_chemical_cost(c)
+ c.chem_charges -= chemical_cost
/obj/effect/proc_holder/changeling/proc/sting_action(mob/user, mob/target)
return 0
@@ -47,10 +45,6 @@
/obj/effect/proc_holder/changeling/proc/sting_feedback(mob/user, mob/target)
return 0
-/obj/effect/proc_holder/changeling/proc/take_chemical_cost(datum/changeling/changeling)
- changeling.chem_charges -= chemical_cost
- changeling.geneticdamage += genetic_damage
-
//Fairly important to remember to return 1 on success >.<
/obj/effect/proc_holder/changeling/proc/can_sting(mob/user, mob/target)
if(!ishuman(user) && !ismonkey(user)) //typecast everything from mob to carbon from this point onwards
@@ -71,9 +65,6 @@
if((user.status_flags & FAKEDEATH) && (!ignores_fakedeath))
to_chat(user, "We are incapacitated.")
return 0
- if(c.geneticdamage > max_genetic_damage)
- to_chat(user, "Our genomes are still reassembling. We need time to recover first.")
- return 0
return 1
//used in /mob/Stat()
diff --git a/code/game/gamemodes/changeling/evolution_menu.dm b/code/game/gamemodes/changeling/evolution_menu.dm
index db1405537f..fdfec9e270 100644
--- a/code/game/gamemodes/changeling/evolution_menu.dm
+++ b/code/game/gamemodes/changeling/evolution_menu.dm
@@ -68,7 +68,7 @@
mind.changeling.purchasedpowers+=S
S.on_purchase(src, is_respec)
if(is_respec)
- feedback_add_details("changeling_power_purchase","Readapt")
+ SSblackbox.add_details("changeling_power_purchase","Readapt")
var/mob/living/carbon/C = src //only carbons have dna now, so we have to typecaste
if(ishuman(C))
diff --git a/code/game/gamemodes/changeling/powers/absorb.dm b/code/game/gamemodes/changeling/powers/absorb.dm
index 0f1fb93071..b6b3372ae3 100644
--- a/code/game/gamemodes/changeling/powers/absorb.dm
+++ b/code/game/gamemodes/changeling/powers/absorb.dm
@@ -4,7 +4,6 @@
chemical_cost = 0
dna_cost = 0
req_human = 1
- max_genetic_damage = 100
/obj/effect/proc_holder/changeling/absorbDNA/can_sting(mob/living/carbon/user)
if(!..())
@@ -42,13 +41,13 @@
to_chat(target, "You feel a sharp stabbing pain!")
target.take_overall_damage(40)
- feedback_add_details("changeling_powers","Absorb DNA|[i]")
+ SSblackbox.add_details("changeling_powers","Absorb DNA|[i]")
if(!do_mob(user, target, 150))
to_chat(user, "Our absorption of [target] has been interrupted!")
changeling.isabsorbing = 0
return
- feedback_add_details("changeling_powers","Absorb DNA|4")
+ SSblackbox.add_details("changeling_powers","Absorb DNA|4")
user.visible_message("[user] sucks the fluids from [target]!", "We have absorbed [target].")
to_chat(target, "You are absorbed by the changeling!")
diff --git a/code/game/gamemodes/changeling/powers/biodegrade.dm b/code/game/gamemodes/changeling/powers/biodegrade.dm
index 453dec9375..6e2d6b0ddd 100644
--- a/code/game/gamemodes/changeling/powers/biodegrade.dm
+++ b/code/game/gamemodes/changeling/powers/biodegrade.dm
@@ -5,9 +5,6 @@
chemical_cost = 30 //High cost to prevent spam
dna_cost = 2
req_human = 1
- genetic_damage = 10
- max_genetic_damage = 0
-
/obj/effect/proc_holder/changeling/biodegrade/sting_action(mob/living/carbon/human/user)
var/used = FALSE // only one form of shackles removed per use
diff --git a/code/game/gamemodes/changeling/powers/chameleon_skin.dm b/code/game/gamemodes/changeling/powers/chameleon_skin.dm
index 1f18f628a8..3be5103105 100644
--- a/code/game/gamemodes/changeling/powers/chameleon_skin.dm
+++ b/code/game/gamemodes/changeling/powers/chameleon_skin.dm
@@ -5,9 +5,6 @@
dna_cost = 2
chemical_cost = 25
req_human = 1
- genetic_damage = 10
- max_genetic_damage = 50
-
/obj/effect/proc_holder/changeling/chameleon_skin/sting_action(mob/user)
var/mob/living/carbon/human/H = user //SHOULD always be human, because req_human = 1
diff --git a/code/game/gamemodes/changeling/powers/fakedeath.dm b/code/game/gamemodes/changeling/powers/fakedeath.dm
index 798f8030e0..e35f04d9e7 100644
--- a/code/game/gamemodes/changeling/powers/fakedeath.dm
+++ b/code/game/gamemodes/changeling/powers/fakedeath.dm
@@ -5,8 +5,6 @@
dna_cost = 0
req_dna = 1
req_stat = DEAD
- max_genetic_damage = 100
-
//Fake our own death and fully heal. You will appear to be dead but regenerate fully after a short delay.
/obj/effect/proc_holder/changeling/fakedeath/sting_action(mob/living/user)
diff --git a/code/game/gamemodes/changeling/powers/humanform.dm b/code/game/gamemodes/changeling/powers/humanform.dm
index 743dbc91e9..d92e622c2d 100644
--- a/code/game/gamemodes/changeling/powers/humanform.dm
+++ b/code/game/gamemodes/changeling/powers/humanform.dm
@@ -2,10 +2,7 @@
name = "Human Form"
desc = "We change into a human."
chemical_cost = 5
- genetic_damage = 3
req_dna = 1
- max_genetic_damage = 3
-
//Transform into a human.
/obj/effect/proc_holder/changeling/humanform/sting_action(mob/living/carbon/user)
diff --git a/code/game/gamemodes/changeling/powers/lesserform.dm b/code/game/gamemodes/changeling/powers/lesserform.dm
index 538803de00..06824f9bed 100644
--- a/code/game/gamemodes/changeling/powers/lesserform.dm
+++ b/code/game/gamemodes/changeling/powers/lesserform.dm
@@ -3,7 +3,6 @@
desc = "We debase ourselves and become lesser. We become a monkey."
chemical_cost = 5
dna_cost = 1
- genetic_damage = 3
req_human = 1
//Transform into a monkey.
diff --git a/code/game/gamemodes/changeling/powers/linglink.dm b/code/game/gamemodes/changeling/powers/linglink.dm
index 5a64d551b5..232774c937 100644
--- a/code/game/gamemodes/changeling/powers/linglink.dm
+++ b/code/game/gamemodes/changeling/powers/linglink.dm
@@ -4,7 +4,6 @@
chemical_cost = 0
dna_cost = 0
req_human = 1
- max_genetic_damage = 100
/obj/effect/proc_holder/changeling/linglink/can_sting(mob/living/carbon/user)
if(!..())
@@ -56,7 +55,7 @@
to_chat(target, "You can now communicate in the changeling hivemind, say \":g message\" to communicate!")
target.reagents.add_reagent("salbutamol", 40) // So they don't choke to death while you interrogate them
sleep(1800)
- feedback_add_details("changeling_powers","Hivemind Link|[i]")
+ SSblackbox.add_details("changeling_powers","Hivemind Link|[i]")
if(!do_mob(user, target, 20))
to_chat(user, "Our link with [target] has ended!")
changeling.islinking = 0
diff --git a/code/game/gamemodes/changeling/powers/mutations.dm b/code/game/gamemodes/changeling/powers/mutations.dm
index 54e8ea1e38..aef9b6f1d2 100644
--- a/code/game/gamemodes/changeling/powers/mutations.dm
+++ b/code/game/gamemodes/changeling/powers/mutations.dm
@@ -16,7 +16,6 @@
helptext = "Yell at Miauw and/or Perakp"
chemical_cost = 1000
dna_cost = -1
- genetic_damage = 1000
var/silent = FALSE
var/weapon_type
@@ -67,7 +66,6 @@
helptext = "Yell at Miauw and/or Perakp"
chemical_cost = 1000
dna_cost = -1
- genetic_damage = 1000
var/helmet_type = /obj/item
var/suit_type = /obj/item
@@ -89,7 +87,7 @@
return 1
var/mob/living/carbon/human/H = user
if(istype(H.wear_suit, suit_type) || istype(H.head, helmet_type))
- H.visible_message("[H] casts off their [suit_name_simple]!", "We cast off our [suit_name_simple][genetic_damage > 0 ? ", temporarily weakening our genomes." : "."]", "You hear the organic matter ripping and tearing!")
+ H.visible_message("[H] casts off their [suit_name_simple]!", "We cast off our [suit_name_simple].", "You hear the organic matter ripping and tearing!")
H.temporarilyRemoveItemFromInventory(H.head, TRUE) //The qdel on dropped() takes care of it
H.temporarilyRemoveItemFromInventory(H.wear_suit, TRUE)
H.update_inv_wear_suit()
@@ -100,7 +98,6 @@
H.add_splatter_floor()
playsound(H.loc, 'sound/effects/splat.ogg', 50, 1) //So real sounds
- changeling.geneticdamage += genetic_damage //Casting off a space suit leaves you weak for a few seconds.
changeling.chem_recharge_slowdown -= recharge_slowdown
return 1
@@ -139,9 +136,7 @@
helptext = "We may retract our armblade in the same manner as we form it. Cannot be used while in lesser form."
chemical_cost = 20
dna_cost = 2
- genetic_damage = 10
req_human = 1
- max_genetic_damage = 20
weapon_type = /obj/item/weapon/melee/arm_blade
weapon_name_simple = "blade"
@@ -217,9 +212,7 @@
and Harm will stun it, and stab it if we're also holding a sharp weapon. Cannot be used while in lesser form."
chemical_cost = 10
dna_cost = 2
- genetic_damage = 5
req_human = 1
- max_genetic_damage = 10
weapon_type = /obj/item/weapon/gun/magic/tentacle
weapon_name_simple = "tentacle"
silent = TRUE
@@ -381,9 +374,7 @@
helptext = "Organic tissue cannot resist damage forever; the shield will break after it is hit too much. The more genomes we absorb, the stronger it is. Cannot be used while in lesser form."
chemical_cost = 20
dna_cost = 1
- genetic_damage = 12
req_human = 1
- max_genetic_damage = 20
weapon_type = /obj/item/weapon/shield/changeling
weapon_name_simple = "shield"
@@ -430,12 +421,10 @@
/obj/effect/proc_holder/changeling/suit/organic_space_suit
name = "Organic Space Suit"
desc = "We grow an organic suit to protect ourselves from space exposure."
- helptext = "We must constantly repair our form to make it space-proof, reducing chemical production while we are protected. Retreating the suit damages our genomes. Cannot be used in lesser form."
+ helptext = "We must constantly repair our form to make it space-proof, reducing chemical production while we are protected. Cannot be used in lesser form."
chemical_cost = 20
dna_cost = 2
- genetic_damage = 8
req_human = 1
- max_genetic_damage = 20
suit_type = /obj/item/clothing/suit/space/changeling
helmet_type = /obj/item/clothing/head/helmet/space/changeling
@@ -477,12 +466,10 @@
/obj/effect/proc_holder/changeling/suit/armor
name = "Chitinous Armor"
desc = "We turn our skin into tough chitin to protect us from damage."
- helptext = "Upkeep of the armor requires a low expenditure of chemicals. The armor is strong against brute force, but does not provide much protection from lasers. Retreating the armor damages our genomes. Cannot be used in lesser form."
+ helptext = "Upkeep of the armor requires a low expenditure of chemicals. The armor is strong against brute force, but does not provide much protection from lasers. Cannot be used in lesser form."
chemical_cost = 20
dna_cost = 1
- genetic_damage = 11
req_human = 1
- max_genetic_damage = 20
recharge_slowdown = 0.25
suit_type = /obj/item/clothing/suit/armor/changeling
diff --git a/code/game/gamemodes/changeling/powers/tiny_prick.dm b/code/game/gamemodes/changeling/powers/tiny_prick.dm
index c44a32c24d..ad6bf81e7c 100644
--- a/code/game/gamemodes/changeling/powers/tiny_prick.dm
+++ b/code/game/gamemodes/changeling/powers/tiny_prick.dm
@@ -40,10 +40,9 @@
return
if(!AStar(user, target.loc, /turf/proc/Distance, user.mind.changeling.sting_range, simulated_only = 0))
return
- if(target.mind && target.mind.changeling)
- sting_feedback(user,target)
- take_chemical_cost(user.mind.changeling)
- return
+ if(target.mind && target.mind.changeling)
+ sting_feedback(user, target)
+ user.mind.changeling.chem_charges -= chemical_cost
return 1
/obj/effect/proc_holder/changeling/sting/sting_feedback(mob/user, mob/target)
@@ -62,7 +61,6 @@
sting_icon = "sting_transform"
chemical_cost = 40
dna_cost = 3
- genetic_damage = 100
var/datum/changelingprofile/selected_dna = null
/obj/effect/proc_holder/changeling/sting/transformation/Click()
@@ -117,8 +115,6 @@
sting_icon = "sting_armblade"
chemical_cost = 20
dna_cost = 1
- genetic_damage = 20
- max_genetic_damage = 10
/obj/item/weapon/melee/arm_blade/false
desc = "A grotesque mass of flesh that used to be your arm. Although it looks dangerous at first, you can tell it's actually quite dull and useless."
diff --git a/code/game/gamemodes/changeling/powers/transform.dm b/code/game/gamemodes/changeling/powers/transform.dm
index f9aca83d04..9d0b095cc2 100644
--- a/code/game/gamemodes/changeling/powers/transform.dm
+++ b/code/game/gamemodes/changeling/powers/transform.dm
@@ -5,7 +5,6 @@
dna_cost = 0
req_dna = 1
req_human = 1
- max_genetic_damage = 3
/obj/item/clothing/glasses/changeling
name = "flesh"
diff --git a/code/game/gamemodes/clock_cult/clock_cult.dm b/code/game/gamemodes/clock_cult/clock_cult.dm
index 670e08e7a2..7fccb37d3c 100644
--- a/code/game/gamemodes/clock_cult/clock_cult.dm
+++ b/code/game/gamemodes/clock_cult/clock_cult.dm
@@ -46,7 +46,7 @@ Credit where due:
///////////
/proc/is_servant_of_ratvar(mob/living/M)
- return istype(M) && M.has_antag_datum(/datum/antagonist/clockcultist, TRUE)
+ return istype(M) && M.mind && M.mind.has_antag_datum(ANTAG_DATUM_CLOCKCULT)
/proc/is_eligible_servant(mob/living/M)
if(!istype(M))
@@ -65,17 +65,21 @@ Credit where due:
return FALSE
/proc/add_servant_of_ratvar(mob/living/L, silent = FALSE)
- var/update_type = /datum/antagonist/clockcultist
+ if(!L || !L.mind)
+ return
+ var/update_type = ANTAG_DATUM_CLOCKCULT
if(silent)
- update_type = /datum/antagonist/clockcultist/silent
- . = L.gain_antag_datum(update_type)
+ update_type = ANTAG_DATUM_CLOCKCULT_SILENT
+ . = L.mind.add_antag_datum(update_type)
/proc/remove_servant_of_ratvar(mob/living/L, silent = FALSE)
- var/datum/antagonist/clockcultist/clock_datum = L.has_antag_datum(/datum/antagonist/clockcultist, TRUE)
+ if(!L || !L.mind)
+ return
+ var/datum/antagonist/clockcult/clock_datum = L.mind.has_antag_datum(/datum/antagonist/clockcult)
if(!clock_datum)
return FALSE
- clock_datum.silent_update = silent
- clock_datum.on_remove()
+ clock_datum.silent = silent
+ clock_datum.on_removal()
return TRUE
///////////////
@@ -194,7 +198,7 @@ Credit where due:
var/datum/game_mode/clockwork_cult/C = SSticker.mode
if(C.check_clockwork_victory())
text += "Ratvar's servants have succeeded in fulfilling His goals!"
- feedback_set_details("round_end_result", "win - servants completed their objective (summon ratvar)")
+ SSblackbox.set_details("round_end_result", "win - servants completed their objective (summon ratvar)")
else
var/half_victory = FALSE
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = locate() in GLOB.all_clockwork_objects
@@ -203,10 +207,10 @@ Credit where due:
if(half_victory)
text += "The crew escaped before Ratvar could rise, but the gateway \
was successfully constructed!"
- feedback_set_details("round_end_result", "halfwin - servants constructed the gateway but their objective was not completed (summon ratvar)")
+ SSblackbox.set_details("round_end_result", "halfwin - servants constructed the gateway but their objective was not completed (summon ratvar)")
else
text += "Ratvar's servants have failed!"
- feedback_set_details("round_end_result", "loss - servants failed their objective (summon ratvar)")
+ SSblackbox.set_details("round_end_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]"
diff --git a/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm b/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm
index 6c56295c05..cf709f09b0 100644
--- a/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm
+++ b/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm
@@ -54,8 +54,11 @@
clockwork_desc = "A sigil that will stun the next non-Servant to cross it."
icon_state = "sigildull"
layer = HIGH_SIGIL_LAYER
- alpha = 60
+ alpha = 75
color = "#FAE48C"
+ light_range = 1.4
+ light_power = 1
+ light_color = "#FAE48C"
sigil_name = "Sigil of Transgression"
/obj/effect/clockwork/sigil/transgression/sigil_effects(mob/living/L)
@@ -93,10 +96,6 @@
sigil_name = "Sigil of Submission"
var/glow_type
-/obj/effect/clockwork/sigil/submission/New()
- ..()
- update_light()
-
/obj/effect/clockwork/sigil/submission/proc/post_channel(mob/living/L)
/obj/effect/clockwork/sigil/submission/sigil_effects(mob/living/L)
@@ -202,6 +201,21 @@
and [structure_number] Clockwork Structure[structure_number == 1 ? "":"s"] [structure_number == 1 ? "is":"are"] in range.")
if(iscyborg(user))
to_chat(user, "You can recharge from the [sigil_name] by crossing it.")
+ else if(!GLOB.ratvar_awakens)
+ to_chat(user, "Hitting the [sigil_name] with brass sheets will convert them to power at a rate of 1 brass sheet to [POWER_FLOOR]W power.")
+ if(!GLOB.ratvar_awakens)
+ to_chat(user, "You can recharge Clockwork Proselytizers from the [sigil_name].")
+
+/obj/effect/clockwork/sigil/transmission/attackby(obj/item/I, mob/living/user, params)
+ if(is_servant_of_ratvar(user) && istype(I, /obj/item/stack/tile/brass) && !GLOB.ratvar_awakens)
+ var/obj/item/stack/tile/brass/B = I
+ user.visible_message("[user] places [B] on [src], causing it to disintegrate into glowing orange energy!", \
+ "You charge the [sigil_name] with [B], providing it with [B.amount * POWER_FLOOR]W of power.")
+ modify_charge(-(B.amount * POWER_FLOOR))
+ playsound(src, 'sound/effects/light_flicker.ogg', (B.amount * POWER_FLOOR) * 0.01, 1)
+ qdel(B)
+ return TRUE
+ return ..()
/obj/effect/clockwork/sigil/transmission/sigil_effects(mob/living/L)
if(is_servant_of_ratvar(L))
@@ -214,9 +228,7 @@
if(!cyborg_checks(cyborg))
return
to_chat(cyborg, "You start to charge from the [sigil_name]...")
- if(!do_after(cyborg, 50, target = src))
- return
- if(!cyborg_checks(cyborg))
+ if(!do_after(cyborg, 50, target = src, extra_checks = CALLBACK(src, .proc/cyborg_checks, cyborg, TRUE)))
return
var/giving_power = min(Floor(cyborg.cell.maxcharge - cyborg.cell.charge, MIN_CLOCKCULT_POWER), power_charge) //give the borg either all our power or their missing power floored to MIN_CLOCKCULT_POWER
if(modify_charge(giving_power))
@@ -228,23 +240,27 @@
animate(cyborg, color = previous_color, time = 100)
addtimer(CALLBACK(cyborg, /atom/proc/update_atom_colour), 100)
-/obj/effect/clockwork/sigil/transmission/proc/cyborg_checks(mob/living/silicon/robot/cyborg)
+/obj/effect/clockwork/sigil/transmission/proc/cyborg_checks(mob/living/silicon/robot/cyborg, silent)
if(!cyborg.cell)
- to_chat(cyborg, "You have no cell!")
+ if(!silent)
+ to_chat(cyborg, "You have no cell!")
return FALSE
if(!power_charge)
- to_chat(cyborg, "The [sigil_name] has no stored power!")
+ if(!silent)
+ to_chat(cyborg, "The [sigil_name] has no stored power!")
return FALSE
if(cyborg.cell.charge > cyborg.cell.maxcharge - MIN_CLOCKCULT_POWER)
- to_chat(cyborg, "You are already at maximum charge!")
+ if(!silent)
+ to_chat(cyborg, "You are already at maximum charge!")
return FALSE
if(cyborg.has_status_effect(STATUS_EFFECT_POWERREGEN))
- to_chat(cyborg, "You are already regenerating power!")
+ if(!silent)
+ to_chat(cyborg, "You are already regenerating power!")
return FALSE
return TRUE
-/obj/effect/clockwork/sigil/transmission/New()
- ..()
+/obj/effect/clockwork/sigil/transmission/Initialize()
+ . = ..()
update_glow()
/obj/effect/clockwork/sigil/transmission/proc/modify_charge(amount)
@@ -265,7 +281,7 @@
if(!power_charge)
set_light(0)
else
- set_light(round(alpha*0.02, 1), round(alpha*0.005, 1))
+ set_light(max(alpha*0.02, 1.4), max(alpha*0.01, 0.1))
//Vitality Matrix: Drains health from non-servants to heal or even revive servants.
/obj/effect/clockwork/sigil/vitality
@@ -299,8 +315,7 @@
if((is_servant_of_ratvar(L) && L.suiciding) || sigil_active)
return
visible_message("[src] begins to glow bright blue!")
- animate(src, alpha = 255, time = 10)
- addtimer(CALLBACK(src, .proc/update_alpha), 10)
+ animate(src, alpha = 255, time = 10, flags = ANIMATION_END_NOW) //we may have a previous animation going. finish it first, then do this one without delay.
sleep(10)
//as long as they're still on the sigil and are either not a servant or they're a servant AND it has remaining vitality
while(L && (!is_servant_of_ratvar(L) || (is_servant_of_ratvar(L) && (GLOB.ratvar_awakens || vitality))) && get_turf(L) == get_turf(src))
@@ -369,12 +384,4 @@
animation_number = initial(animation_number)
sigil_active = FALSE
visible_message("[src] slowly stops glowing!")
- if(sigil_active || alpha == 255)
- animate(src, alpha = initial(alpha), time = 10)
- addtimer(CALLBACK(src, .proc/update_alpha), 10)
-
-/obj/effect/clockwork/sigil/vitality/proc/update_alpha()
- if(sigil_active)
- alpha = 255
- else
- alpha = initial(alpha)
+ animate(src, alpha = initial(alpha), time = 10, flags = ANIMATION_END_NOW)
diff --git a/code/game/gamemodes/clock_cult/clock_effects/spatial_gateway.dm b/code/game/gamemodes/clock_cult/clock_effects/spatial_gateway.dm
index f376410124..5c1d3fb5fb 100644
--- a/code/game/gamemodes/clock_cult/clock_effects/spatial_gateway.dm
+++ b/code/game/gamemodes/clock_cult/clock_effects/spatial_gateway.dm
@@ -15,9 +15,8 @@
var/obj/effect/clockwork/spatial_gateway/linked_gateway //The gateway linked to this one
var/timerid
-/obj/effect/clockwork/spatial_gateway/New()
- ..()
- update_light()
+/obj/effect/clockwork/spatial_gateway/Initialize()
+ . = ..()
addtimer(CALLBACK(src, .proc/check_setup), 1)
/obj/effect/clockwork/spatial_gateway/Destroy()
@@ -99,12 +98,12 @@
if(severity == 1 && uses)
uses = 0
visible_message("[src] is disrupted!")
- animate(src, alpha = 0, transform = matrix()*2, time = 10)
+ animate(src, alpha = 0, transform = matrix()*2, time = 10, flags = ANIMATION_END_NOW)
deltimer(timerid)
timerid = QDEL_IN(src, 10)
linked_gateway.uses = 0
linked_gateway.visible_message("[linked_gateway] is disrupted!")
- animate(linked_gateway, alpha = 0, transform = matrix()*2, time = 10)
+ animate(linked_gateway, alpha = 0, transform = matrix()*2, time = 10, flags = ANIMATION_END_NOW)
deltimer(linked_gateway.timerid)
linked_gateway.timerid = QDEL_IN(linked_gateway, 10)
return TRUE
@@ -131,9 +130,9 @@
playsound(src, 'sound/effects/EMPulse.ogg', 50, 1)
playsound(linked_gateway, 'sound/effects/EMPulse.ogg', 50, 1)
transform = matrix() * 1.5
- animate(src, transform = matrix() / 1.5, time = 10)
+ animate(src, transform = matrix() / 1.5, time = 10, flags = ANIMATION_END_NOW)
linked_gateway.transform = matrix() * 1.5
- animate(linked_gateway, transform = matrix() / 1.5, time = 10)
+ animate(linked_gateway, transform = matrix() / 1.5, time = 10, flags = ANIMATION_END_NOW)
A.forceMove(get_turf(linked_gateway))
if(!no_cost)
uses = max(0, uses - 1)
@@ -181,17 +180,26 @@
return procure_gateway(invoker, time_duration, gateway_uses, two_way)
var/istargetobelisk = istype(target, /obj/structure/destructible/clockwork/powered/clockwork_obelisk)
var/issrcobelisk = istype(src, /obj/structure/destructible/clockwork/powered/clockwork_obelisk)
- if(issrcobelisk && !anchored)
- to_chat(invoker, "[src] is no longer secured!")
- return FALSE
+ if(issrcobelisk)
+ if(!anchored)
+ to_chat(invoker, "[src] is no longer secured!")
+ return FALSE
+ var/obj/structure/destructible/clockwork/powered/clockwork_obelisk/CO = src //foolish as I am, how I set this proc up makes substypes unfeasible
+ if(CO.active)
+ to_chat(invoker, "[src] is now sustaining a gateway!")
+ return FALSE
if(istargetobelisk)
if(!target.anchored)
to_chat(invoker, "That [target.name] is no longer secured!")
return procure_gateway(invoker, time_duration, gateway_uses, two_way)
var/obj/structure/destructible/clockwork/powered/clockwork_obelisk/CO = target
+ if(CO.active)
+ to_chat(invoker, "That [target.name] is sustaining a gateway, and cannot recieve another!")
+ return procure_gateway(invoker, time_duration, gateway_uses, two_way)
var/efficiency = CO.get_efficiency_mod()
gateway_uses = round(gateway_uses * (2 * efficiency), 1)
time_duration = round(time_duration * (2 * efficiency), 1)
+ CO.active = TRUE //you'd be active in a second but you should update immediately
invoker.visible_message("The air in front of [invoker] ripples before suddenly tearing open!", \
"With a word, you rip open a [two_way ? "two-way":"one-way"] rift to [input_target_key]. It will last for [time_duration / 10] seconds and has [gateway_uses] use[gateway_uses > 1 ? "s" : ""].")
var/obj/effect/clockwork/spatial_gateway/S1 = new(issrcobelisk ? get_turf(src) : get_step(get_turf(invoker), invoker.dir))
diff --git a/code/game/gamemodes/clock_cult/clock_helpers/component_helpers.dm b/code/game/gamemodes/clock_cult/clock_helpers/component_helpers.dm
index e3c456460c..120ed0ff60 100644
--- a/code/game/gamemodes/clock_cult/clock_helpers/component_helpers.dm
+++ b/code/game/gamemodes/clock_cult/clock_helpers/component_helpers.dm
@@ -22,6 +22,12 @@
else
for(var/i in GLOB.clockwork_component_cache)
.[i] = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*GLOB.clockwork_component_cache[i], 1)
+ for(var/obj/structure/destructible/clockwork/massive/celestial_gateway/G in GLOB.all_clockwork_objects)
+ if(G.still_needs_components())
+ for(var/i in G.required_components)
+ if(!G.required_components[i])
+ . -= i
+ break
. = pickweight(.)
//returns a component name from a component id
@@ -37,6 +43,8 @@
return "Replicant Alloy"
if(HIEROPHANT_ANSIBLE)
return "Hierophant Ansible"
+ else
+ return null
//returns a component acronym from a component id
/proc/get_component_acronym(id)
@@ -51,6 +59,8 @@
return "RA"
if(HIEROPHANT_ANSIBLE)
return "HA"
+ else
+ return null
//returns a component id from a component name
/proc/get_component_id(name)
@@ -65,6 +75,8 @@
return REPLICANT_ALLOY
if("Hierophant Ansible")
return HIEROPHANT_ANSIBLE
+ else
+ return null
//returns a component spanclass from a component id
/proc/get_component_span(id)
@@ -121,6 +133,8 @@
return /obj/effect/overlay/temp/ratvar/component/alloy
if(HIEROPHANT_ANSIBLE)
return /obj/effect/overlay/temp/ratvar/component/ansible
+ else
+ return null
//returns a type for a component from a component id
/proc/get_component_type(id)
@@ -134,4 +148,6 @@
if(REPLICANT_ALLOY)
return /obj/item/clockwork/component/replicant_alloy
if(HIEROPHANT_ANSIBLE)
- return /obj/item/clockwork/component/hierophant_ansible
\ No newline at end of file
+ return /obj/item/clockwork/component/hierophant_ansible
+ else
+ return null
\ No newline at end of file
diff --git a/code/game/gamemodes/clock_cult/clock_helpers/proselytizer_helpers.dm b/code/game/gamemodes/clock_cult/clock_helpers/proselytizer_helpers.dm
index c6954bb67e..529f38d832 100644
--- a/code/game/gamemodes/clock_cult/clock_helpers/proselytizer_helpers.dm
+++ b/code/game/gamemodes/clock_cult/clock_helpers/proselytizer_helpers.dm
@@ -286,6 +286,28 @@
user.visible_message("[user]'s [proselytizer.name] stops covering [src] with glowing orange energy.", \
"You finish repairing [src]. It is now at [obj_integrity]/[max_integrity] integrity.")
+//Hitting a sigil of transmission will try to charge from it.
+/obj/effect/clockwork/sigil/transmission/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
+ . = TRUE
+ var/list/charge_values = list()
+ if(!proselytizer.sigil_charge_checks(charge_values, src, user))
+ return
+ user.visible_message("[user]'s [proselytizer.name] starts draining glowing orange energy from [src]...", \
+ "You start recharging your [proselytizer.name]...")
+ proselytizer.recharging = src
+ while(proselytizer && user && src)
+ if(!do_after(user, 10, target = src, extra_checks = CALLBACK(proselytizer, /obj/item/clockwork/clockwork_proselytizer.proc/sigil_charge_checks, charge_values, src, user, TRUE)))
+ break
+ modify_charge(charge_values["power_gain"])
+ proselytizer.modify_stored_power(charge_values["power_gain"])
+ playsound(src, 'sound/effects/light_flicker.ogg', charge_values["power_gain"] * 0.1, 1)
+
+ if(proselytizer)
+ proselytizer.recharging = null
+ if(user)
+ user.visible_message("[user]'s [proselytizer.name] stops draining glowing orange energy from [src].", \
+ "You finish recharging your [proselytizer.name]. It now contains [proselytizer.get_power()]W/[proselytizer.get_max_power()]W power.")
+
//Proselytizer mob heal proc, to avoid as much copypaste as possible.
/mob/living/proc/proselytizer_heal(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
var/list/repair_values = list()
diff --git a/code/game/gamemodes/clock_cult/clock_helpers/ratvarian_language.dm b/code/game/gamemodes/clock_cult/clock_helpers/ratvarian_language.dm
index fcb30a096d..a9727017d3 100644
--- a/code/game/gamemodes/clock_cult/clock_helpers/ratvarian_language.dm
+++ b/code/game/gamemodes/clock_cult/clock_helpers/ratvarian_language.dm
@@ -106,6 +106,6 @@ List of nuances:
if(!whisper)
L.say(message, "clock", spans, language=/datum/language/common)
else
- L.whisper(message)
+ L.whisper(message, "clock", spans, language=/datum/language/common)
else
AM.say(message, language=/datum/language/common)
diff --git a/code/game/gamemodes/clock_cult/clock_items/clockwork_proselytizer.dm b/code/game/gamemodes/clock_cult/clock_items/clockwork_proselytizer.dm
index 25e2138b81..60df56b6ef 100644
--- a/code/game/gamemodes/clock_cult/clock_items/clockwork_proselytizer.dm
+++ b/code/game/gamemodes/clock_cult/clock_items/clockwork_proselytizer.dm
@@ -12,6 +12,7 @@
var/uses_power = TRUE
var/metal_to_power = FALSE
var/repairing = null //what we're currently repairing, if anything
+ var/obj/effect/clockwork/sigil/transmission/recharging = null //the sigil we're charging from, if any
var/speed_multiplier = 1 //how fast this proselytizer works
var/charge_rate = MIN_CLOCKCULT_POWER //how much power we gain every two seconds
var/charge_delay = 2 //how many proccess ticks remain before we can start to charge
@@ -140,10 +141,11 @@
/obj/item/clockwork/clockwork_proselytizer/attack_self(mob/living/user)
if(is_servant_of_ratvar(user))
- if(!can_use_power(POWER_WALL_TOTAL))
- to_chat(user, "[src] requires [POWER_WALL_TOTAL]W of power to produce brass sheets!")
- return
- modify_stored_power(-POWER_WALL_TOTAL)
+ if(uses_power)
+ if(!can_use_power(POWER_WALL_TOTAL))
+ to_chat(user, "[src] requires [POWER_WALL_TOTAL]W of power to produce brass sheets!")
+ return
+ modify_stored_power(-POWER_WALL_TOTAL)
playsound(src, 'sound/items/Deconstruct.ogg', 50, 1)
new/obj/item/stack/tile/brass(user.loc, 5)
to_chat(user, "You user [stored_power ? "some":"all"] of [src]'s power to produce some brass sheets. It now stores [get_power()]W/[get_max_power()]W of power.")
@@ -182,6 +184,9 @@
if(repairing)
to_chat(user, "You are currently repairing [repairing] with [src]!")
return FALSE
+ if(recharging)
+ to_chat(user, "You are currently recharging [src] from the [recharging.sigil_name]!")
+ return FALSE
var/list/proselytize_values = target.proselytize_vals(user, src) //relevant values for proselytizing stuff, given as an associated list
if(!islist(proselytize_values))
if(proselytize_values != TRUE) //if we get true, fail, but don't send a message for whatever reason
@@ -237,7 +242,7 @@
/obj/item/clockwork/clockwork_proselytizer/proc/proselytize_checks(list/proselytize_values, atom/target, expected_type, mob/user, silent) //checked constantly while proselytizing
if(!islist(proselytize_values) || !target || QDELETED(target) || !user)
return FALSE
- if(repairing)
+ if(repairing || recharging)
return FALSE
if(target.type != expected_type)
return FALSE
@@ -296,3 +301,20 @@
[round(repair_values["amount_to_heal"]*MIN_CLOCKCULT_POWER, MIN_CLOCKCULT_POWER)]W to fully repair [target == user ? "yourself" : "[target.p_them()]"]!")
return FALSE
return TRUE
+
+//checked constantly while charging from a sigil
+/obj/item/clockwork/clockwork_proselytizer/proc/sigil_charge_checks(list/charge_values, obj/effect/clockwork/sigil/transmission/sigil, mob/user, silent)
+ if(!islist(charge_values) || !sigil || QDELETED(sigil) || !user)
+ return FALSE
+ if(can_use_power(RATVAR_POWER_CHECK))
+ return FALSE
+ charge_values["power_gain"] = Clamp(sigil.power_charge, 0, POWER_WALL_MINUS_FLOOR)
+ if(!charge_values["power_gain"])
+ if(!silent)
+ to_chat(user, "The [sigil.sigil_name] contains no power!")
+ return FALSE
+ if(stored_power + charge_values["power_gain"] > max_power)
+ if(!silent)
+ to_chat(user, "Your [name] contains too much power to charge from the [sigil.sigil_name]!")
+ return FALSE
+ return TRUE
diff --git a/code/game/gamemodes/clock_cult/clock_items/clockwork_slab.dm b/code/game/gamemodes/clock_cult/clock_items/clockwork_slab.dm
index 36ff67262b..8b3aa01533 100644
--- a/code/game/gamemodes/clock_cult/clock_items/clockwork_slab.dm
+++ b/code/game/gamemodes/clock_cult/clock_items/clockwork_slab.dm
@@ -61,7 +61,7 @@
/obj/item/clockwork/slab/cyborg/medical //five scriptures, plus a spear
quickbound = list(/datum/clockwork_scripture/ranged_ability/linked_vanguard, /datum/clockwork_scripture/ranged_ability/sentinels_compromise, \
- /datum/clockwork_scripture/create_object/vitality_matrix, /datum/clockwork_scripture/fellowship_armory, /datum/clockwork_scripture/create_object/mending_motor)
+ /datum/clockwork_scripture/create_object/vitality_matrix, /datum/clockwork_scripture/channeled/mending_mantra, /datum/clockwork_scripture/fellowship_armory)
/obj/item/clockwork/slab/cyborg/security //four scriptures, plus a spear
quickbound = list(/datum/clockwork_scripture/channeled/belligerent, /datum/clockwork_scripture/ranged_ability/judicial_marker, /datum/clockwork_scripture/channeled/taunting_tirade, \
@@ -326,13 +326,19 @@
if(production_time != SLAB_PRODUCTION_TIME+SLAB_SLOWDOWN_MAXIMUM)
production_text_addon = ", which increases for each human or silicon servant above [SCRIPT_SERVANT_REQ]"
production_time = production_time/600
- var/production_text = "[round(production_time)] minute\s"
+ var/list/production_text
+ if(round(production_time))
+ production_text = list("[round(production_time)] minute\s")
if(production_time != round(production_time))
production_time -= round(production_time)
production_time *= 60
- production_text += " and [round(production_time, 1)] second\s"
+ if(!LAZYLEN(production_text))
+ production_text = list("[round(production_time, 1)] second\s")
+ else
+ production_text += " and [round(production_time, 1)] second\s"
production_text += ""
production_text += production_text_addon
+ production_text = production_text.Join()
textlist = list("Chetr nyy hagehguf-naq-ubabe Ratvar.
\
\
diff --git a/code/game/gamemodes/clock_cult/clock_items/wraith_spectacles.dm b/code/game/gamemodes/clock_cult/clock_items/wraith_spectacles.dm
index 1b6c80af73..6d17fe4e74 100644
--- a/code/game/gamemodes/clock_cult/clock_items/wraith_spectacles.dm
+++ b/code/game/gamemodes/clock_cult/clock_items/wraith_spectacles.dm
@@ -129,6 +129,7 @@
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
apply_eye_damage(H)
+ return ..()
/datum/status_effect/wraith_spectacles/tick()
if(!ishuman(owner))
diff --git a/code/game/gamemodes/clock_cult/clock_mobs.dm b/code/game/gamemodes/clock_cult/clock_mobs.dm
index a5e7dee4cb..d3a33c0c40 100644
--- a/code/game/gamemodes/clock_cult/clock_mobs.dm
+++ b/code/game/gamemodes/clock_cult/clock_mobs.dm
@@ -14,8 +14,7 @@
verb_ask = "requests"
verb_exclaim = "proclaims"
verb_yell = "harangues"
- initial_languages = list(/datum/language/common, /datum/language/ratvar)
- only_speaks_language = /datum/language/ratvar
+ initial_language_holder = /datum/language_holder/clockmob
bubble_icon = "clock"
light_color = "#E42742"
death_sound = 'sound/magic/clockwork/anima_fragment_death.ogg'
diff --git a/code/game/gamemodes/clock_cult/clock_scripture.dm b/code/game/gamemodes/clock_cult/clock_scripture.dm
index 8e688f30aa..e8f1e8dbbc 100644
--- a/code/game/gamemodes/clock_cult/clock_scripture.dm
+++ b/code/game/gamemodes/clock_cult/clock_scripture.dm
@@ -86,7 +86,7 @@ Judgement: 12 servants, 5 caches, 300 CV, and any existing AIs are converted or
else
successful = TRUE
if(slab && !slab.no_cost && !GLOB.ratvar_awakens) //if the slab exists and isn't debug and ratvar isn't up, log the scripture as being used
- feedback_add_details("clockcult_scripture_recited", name)
+ SSblackbox.add_details("clockcult_scripture_recited", name)
if(slab)
slab.busy = null
qdel(src)
diff --git a/code/game/gamemodes/clock_cult/clock_scriptures/scripture_applications.dm b/code/game/gamemodes/clock_cult/clock_scriptures/scripture_applications.dm
index efc479f522..21da8fab00 100644
--- a/code/game/gamemodes/clock_cult/clock_scriptures/scripture_applications.dm
+++ b/code/game/gamemodes/clock_cult/clock_scriptures/scripture_applications.dm
@@ -10,7 +10,7 @@
It will penetrate mindshield implants once before disappearing."
invocations = list("Divinity, enslave...", "...all who trespass here!")
channel_time = 70
- consumed_components = list(BELLIGERENT_EYE = 2, GEIS_CAPACITOR = 1, HIEROPHANT_ANSIBLE = 1)
+ consumed_components = list(BELLIGERENT_EYE = 4, GEIS_CAPACITOR = 2, HIEROPHANT_ANSIBLE = 2)
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/submission/accession
prevent_path = /obj/effect/clockwork/sigil/submission
@@ -32,7 +32,7 @@
It grows faster to invoke with more adjacent Servants."
invocations = list("Shield us...", "...with the...", "... fragments of Engine!")
channel_time = 100
- consumed_components = list(VANGUARD_COGWHEEL = 2, REPLICANT_ALLOY = 1, HIEROPHANT_ANSIBLE = 1)
+ consumed_components = list(VANGUARD_COGWHEEL = 4, REPLICANT_ALLOY = 2, HIEROPHANT_ANSIBLE = 2)
usage_tip = "This scripture will replace all weaker armor worn by affected Servants."
tier = SCRIPTURE_APPLICATION
multiple_invokers_used = TRUE
@@ -99,7 +99,7 @@
If it remains close to you, you will gradually regain health up to a low amount, but it will die if it goes too far from you."
invocations = list("Fright's will...", "...call forth...")
channel_time = 100
- consumed_components = list(BELLIGERENT_EYE = 1, VANGUARD_COGWHEEL = 1, GEIS_CAPACITOR = 2)
+ consumed_components = list(BELLIGERENT_EYE = 2, VANGUARD_COGWHEEL = 2, GEIS_CAPACITOR = 4)
usage_tip = "Marauders are useful as personal bodyguards and frontline warriors."
tier = SCRIPTURE_APPLICATION
primary_component = GEIS_CAPACITOR
@@ -135,7 +135,7 @@
if(!check_special_requirements())
return FALSE
to_chat(invoker, "The tendril shivers slightly as it selects a marauder...")
- var/list/marauder_candidates = pollCandidates("Do you want to play as the clockwork marauder of [invoker.real_name]?", ROLE_SERVANT_OF_RATVAR, null, FALSE, 50, POLL_IGNORE_CLOCKWORK_MARAUDER)
+ var/list/marauder_candidates = pollGhostCandidates("Do you want to play as the clockwork marauder of [invoker.real_name]?", ROLE_SERVANT_OF_RATVAR, null, FALSE, 50, POLL_IGNORE_CLOCKWORK_MARAUDER)
if(!check_special_requirements())
return FALSE
if(!marauder_candidates.len)
@@ -160,7 +160,7 @@
and exceptional speed, though taking damage will temporarily slow it down."
invocations = list("Call forth...", "...the soldiers of Armorer.")
channel_time = 80
- consumed_components = list(BELLIGERENT_EYE = 1, VANGUARD_COGWHEEL = 1, REPLICANT_ALLOY = 2)
+ consumed_components = list(BELLIGERENT_EYE = 2, VANGUARD_COGWHEEL = 2, REPLICANT_ALLOY = 4)
object_path = /obj/structure/destructible/clockwork/shell/fragment
creator_message = "You form an anima fragment, a powerful soul vessel receptacle."
observer_message = "The slab disgorges a puddle of black metal that expands and forms into a strange shell!"
@@ -179,7 +179,7 @@
desc = "Places a sigil that stores energy to power clockwork structures."
invocations = list("Divinity...", "...power our creations!")
channel_time = 70
- consumed_components = list(VANGUARD_COGWHEEL = 1, GEIS_CAPACITOR = 1, HIEROPHANT_ANSIBLE = 2)
+ consumed_components = list(VANGUARD_COGWHEEL = 2, GEIS_CAPACITOR = 2, HIEROPHANT_ANSIBLE = 4)
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/transmission
creator_message = "A sigil silently appears below you. It will automatically power clockwork structures near it."
@@ -199,7 +199,7 @@
desc = "Creates a clockwork totem that sabotages nearby machinery and funnels drained power into nearby Sigils of Transmission or the area's APC."
invocations = list("May this totem...", "...shroud the false suns!")
channel_time = 80
- consumed_components = list(BELLIGERENT_EYE = 3, REPLICANT_ALLOY = 1, HIEROPHANT_ANSIBLE = 1)
+ consumed_components = list(BELLIGERENT_EYE = 5, REPLICANT_ALLOY = 2, HIEROPHANT_ANSIBLE = 2)
object_path = /obj/structure/destructible/clockwork/powered/interdiction_lens
creator_message = "You form an interdiction lens, which disrupts cameras and radios and drains power."
observer_message = "A brass totem rises from the ground, a purple gem appearing in its center!"
@@ -214,48 +214,26 @@
quickbind_desc = "Creates an Interdiction Lens, which drains power into nearby Sigils of Transmission."
-//Mending Motor: Creates a prism that will quickly heal mechanical servants/clockwork structures at a power cost
-/datum/clockwork_scripture/create_object/mending_motor
- descname = "Powered Structure, Repairs Other Structures"
- name = "Mending Motor"
- desc = "Creates a mechanized prism that will rapidly repair damaged clockwork constructs, converted cyborgs, and clockwork structures."
- invocations = list("May this prism...", "...mend our dents and scratches!")
- channel_time = 80
- consumed_components = list(VANGUARD_COGWHEEL = 3, GEIS_CAPACITOR = 1, REPLICANT_ALLOY = 1)
- object_path = /obj/structure/destructible/clockwork/powered/mending_motor
- creator_message = "You form a mending motor, which will rapidly repair damaged clockwork constructs, converted cyborgs, and clockwork structures."
- observer_message = "An onyx prism forms in midair and sprouts tendrils to support itself!"
- invokers_required = 2
- multiple_invokers_used = TRUE
- usage_tip = "Powerful healing but power use is somewhat inefficient, though much better than a proselytizer."
- tier = SCRIPTURE_APPLICATION
- one_per_tile = TRUE
- primary_component = VANGUARD_COGWHEEL
- sort_priority = 7
- quickbind = TRUE
- quickbind_desc = "Creates a Mending Motor, which rapidly repairs constructs and structures at a power cost."
-
-
//Mania Motor: Creates a malevolent transmitter that will broadcast the whispers of Sevtug into the minds of nearby nonservants, causing a variety of mental effects at a power cost.
/datum/clockwork_scripture/create_object/mania_motor
descname = "Powered Structure, Area Denial"
name = "Mania Motor"
- desc = "Creates a mania motor which will cause brain damage and hallucinations in nearby non-Servant humans. It will also try to convert humans directly adjecent to the motor."
+ desc = "Creates a mania motor which causes minor damage and a variety of negative mental effects in nearby non-Servant humans, potentially up to and including conversion."
invocations = list("May this transmitter...", "...break the will of all who oppose us!")
channel_time = 80
- consumed_components = list(GEIS_CAPACITOR = 3, REPLICANT_ALLOY = 1, HIEROPHANT_ANSIBLE = 1)
+ consumed_components = list(GEIS_CAPACITOR = 5, REPLICANT_ALLOY = 2, HIEROPHANT_ANSIBLE = 2)
object_path = /obj/structure/destructible/clockwork/powered/mania_motor
- creator_message = "You form a mania motor which will cause brain damage and hallucinations in nearby humans while active."
+ creator_message = "You form a mania motor, which causes minor damage and negative mental effects in non-Servants."
observer_message = "A two-pronged machine rises from the ground!"
invokers_required = 2
multiple_invokers_used = TRUE
- usage_tip = "Eligible non-Servant humans next to the motor will be converted at an additional power cost. It will also cure hallucinations and brain damage in nearby Servants."
+ usage_tip = "It will also cure hallucinations and brain damage in nearby Servants."
tier = SCRIPTURE_APPLICATION
one_per_tile = TRUE
primary_component = GEIS_CAPACITOR
sort_priority = 8
quickbind = TRUE
- quickbind_desc = "Creates a Mania Motor, which can convert adjacent non-Servants with power."
+ quickbind_desc = "Creates a Mania Motor, which causes minor damage and negative mental effects in non-Servants."
//Tinkerer's Daemon: Creates an efficient machine that rapidly produces components at a power cost.
@@ -266,7 +244,7 @@
and there is at least one existing cache."
invocations = list("May this generator...", "...collect Engine parts that yet hold greatness!")
channel_time = 80
- consumed_components = list(BELLIGERENT_EYE = 1, GEIS_CAPACITOR = 1, REPLICANT_ALLOY = 3)
+ consumed_components = list(BELLIGERENT_EYE = 2, GEIS_CAPACITOR = 2, REPLICANT_ALLOY = 5)
object_path = /obj/structure/destructible/clockwork/powered/tinkerers_daemon
creator_message = "You form a tinkerer's daemon which can rapidly collect components at a power cost."
invokers_required = 2
@@ -300,7 +278,7 @@
desc = "Creates a clockwork obelisk that can broadcast messages over the Hierophant Network or open a Spatial Gateway to any living Servant or clockwork obelisk."
invocations = list("May this obelisk...", "...take us to all places!")
channel_time = 80
- consumed_components = list(BELLIGERENT_EYE = 1, VANGUARD_COGWHEEL = 1, HIEROPHANT_ANSIBLE = 3)
+ consumed_components = list(BELLIGERENT_EYE = 2, VANGUARD_COGWHEEL = 2, HIEROPHANT_ANSIBLE = 5)
object_path = /obj/structure/destructible/clockwork/powered/clockwork_obelisk
creator_message = "You form a clockwork obelisk which can broadcast messages or produce Spatial Gateways."
observer_message = "A brass obelisk appears hanging in midair!"
diff --git a/code/game/gamemodes/clock_cult/clock_scriptures/scripture_drivers.dm b/code/game/gamemodes/clock_cult/clock_scriptures/scripture_drivers.dm
index 023cd50dd1..b49b0c5819 100644
--- a/code/game/gamemodes/clock_cult/clock_scriptures/scripture_drivers.dm
+++ b/code/game/gamemodes/clock_cult/clock_scriptures/scripture_drivers.dm
@@ -17,23 +17,10 @@
sort_priority = 1
quickbind = TRUE
quickbind_desc = "Forces nearby non-Servants to walk, doing minor damage with each chant.
Maximum 15 chants."
- var/noncultist_damage = 2 //damage per chant to noncultists
- var/cultist_damage = 8 //damage per chant to non-walking cultists
/datum/clockwork_scripture/channeled/belligerent/chant_effects(chant_number)
for(var/mob/living/carbon/C in hearers(7, invoker))
- var/number_legs = C.get_num_legs()
- if(!is_servant_of_ratvar(C) && !C.null_rod_check() && number_legs) //you have legs right
- C.apply_damage(noncultist_damage * 0.5, BURN, "l_leg")
- C.apply_damage(noncultist_damage * 0.5, BURN, "r_leg")
- if(C.m_intent != MOVE_INTENT_WALK)
- if(!iscultist(C))
- to_chat(C, "Your leg[number_legs > 1 ? "s shiver":" shivers"] with pain!")
- else //Cultists take extra burn damage
- to_chat(C, "Your leg[number_legs > 1 ? "s burn":" burns"] with pain!")
- C.apply_damage(cultist_damage * 0.5, BURN, "l_leg")
- C.apply_damage(cultist_damage * 0.5, BURN, "r_leg")
- C.toggle_move_intent()
+ C.apply_status_effect(STATUS_EFFECT_BELLIGERENT)
return TRUE
@@ -272,7 +259,7 @@
var/static/prev_cost = 0
/datum/clockwork_scripture/create_object/tinkerers_cache/creation_update()
- var/cache_cost_increase = min(round(GLOB.clockwork_caches*0.25), 5)
+ var/cache_cost_increase = min(round(GLOB.clockwork_caches*0.4), 10)
if(cache_cost_increase != prev_cost)
prev_cost = cache_cost_increase
consumed_components = list(BELLIGERENT_EYE = 0, VANGUARD_COGWHEEL = 0, GEIS_CAPACITOR = 0, REPLICANT_ALLOY = 1, HIEROPHANT_ANSIBLE = 0)
diff --git a/code/game/gamemodes/clock_cult/clock_scriptures/scripture_revenant.dm b/code/game/gamemodes/clock_cult/clock_scriptures/scripture_revenant.dm
index faab84befe..25bedd79d3 100644
--- a/code/game/gamemodes/clock_cult/clock_scriptures/scripture_revenant.dm
+++ b/code/game/gamemodes/clock_cult/clock_scriptures/scripture_revenant.dm
@@ -9,7 +9,7 @@
desc = "Taps the limitless power of Inath-neq, one of Ratvar's four generals. The benevolence of Inath-Neq will grant complete invulnerability to all Servants in range for fifteen seconds."
invocations = list("I call upon you, Vanguard!!", "Let the Resonant Cogs turn once more!!", "Grant me and my allies the strength to vanquish our foes!!")
channel_time = 100
- consumed_components = list(VANGUARD_COGWHEEL = 4, GEIS_CAPACITOR = 2, REPLICANT_ALLOY = 2, HIEROPHANT_ANSIBLE = 2)
+ consumed_components = list(VANGUARD_COGWHEEL = 10, GEIS_CAPACITOR = 3, REPLICANT_ALLOY = 3, HIEROPHANT_ANSIBLE = 3)
usage_tip = "Servants affected by this scripture are only weak to things that outright destroy bodies, such as bombs or the singularity."
tier = SCRIPTURE_REVENANT
primary_component = VANGUARD_COGWHEEL
@@ -44,7 +44,7 @@
for all non-servant humans on the same z-level as them. The power of this scripture falls off somewhat with distance, and certain things may reduce its effects."
invocations = list("I call upon you, Fright!!", "Let your power shatter the sanity of the weak-minded!!", "Let your tendrils hold sway over all!!")
channel_time = 150
- consumed_components = list(BELLIGERENT_EYE = 3, VANGUARD_COGWHEEL = 3, GEIS_CAPACITOR = 6, HIEROPHANT_ANSIBLE = 3)
+ consumed_components = list(BELLIGERENT_EYE = 6, VANGUARD_COGWHEEL = 6, GEIS_CAPACITOR = 10, HIEROPHANT_ANSIBLE = 6)
usage_tip = "Causes brain damage, hallucinations, confusion, and dizziness in massive amounts."
tier = SCRIPTURE_REVENANT
sort_priority = 3
@@ -108,9 +108,9 @@
clockwork proselytizers will charge very rapidly."
invocations = list("I call upon you, Armorer!!", "Let your machinations reign on this miserable station!!", "Let your power flow through the tools of your master!!")
channel_time = 150
- consumed_components = list(BELLIGERENT_EYE = 3, VANGUARD_COGWHEEL = 3, GEIS_CAPACITOR = 3, REPLICANT_ALLOY = 6)
+ consumed_components = list(BELLIGERENT_EYE = 6, VANGUARD_COGWHEEL = 6, GEIS_CAPACITOR = 6, REPLICANT_ALLOY = 10)
usage_tip = "Ocular wardens will become empowered, clockwork proselytizers will require no alloy, tinkerer's daemons will produce twice as quickly, \
- and interdiction lenses, mending motors, mania motors, tinkerer's daemons, and clockwork obelisks will all require no power."
+ and interdiction lenses, mania motors, tinkerer's daemons, and clockwork obelisks will all require no power."
tier = SCRIPTURE_REVENANT
primary_component = REPLICANT_ALLOY
sort_priority = 4
@@ -153,7 +153,7 @@
will be struck by devastating lightning bolts."
invocations = list("I call upon you, Amperage!!", "Let your energy flow through me!!", "Let your boundless power shatter stars!!")
channel_time = 100
- consumed_components = list(BELLIGERENT_EYE = 2, GEIS_CAPACITOR = 2, REPLICANT_ALLOY = 2, HIEROPHANT_ANSIBLE = 4)
+ consumed_components = list(BELLIGERENT_EYE = 3, GEIS_CAPACITOR = 3, REPLICANT_ALLOY = 3, HIEROPHANT_ANSIBLE = 10)
usage_tip = "Struck targets will also be knocked down for about sixteen seconds."
tier = SCRIPTURE_REVENANT
primary_component = HIEROPHANT_ANSIBLE
diff --git a/code/game/gamemodes/clock_cult/clock_scriptures/scripture_scripts.dm b/code/game/gamemodes/clock_cult/clock_scriptures/scripture_scripts.dm
index 4fd4e629a1..bc5ec1b9d8 100644
--- a/code/game/gamemodes/clock_cult/clock_scriptures/scripture_scripts.dm
+++ b/code/game/gamemodes/clock_cult/clock_scriptures/scripture_scripts.dm
@@ -9,7 +9,7 @@
desc = "Forms an automatic short-range turret which will automatically attack nearby unrestrained non-Servants that can see it."
invocations = list("Guardians...", "...of the Engine...", "...defend us!")
channel_time = 120
- consumed_components = list(BELLIGERENT_EYE = 1, REPLICANT_ALLOY = 1)
+ consumed_components = list(BELLIGERENT_EYE = 2, REPLICANT_ALLOY = 1)
object_path = /obj/structure/destructible/clockwork/ocular_warden
creator_message = "You form an ocular warden, which will automatically attack nearby unrestrained non-Servants that can see it."
observer_message = "A brass eye takes shape and slowly rises into the air, its red iris glaring!"
@@ -36,7 +36,7 @@
desc = "Creates a small shell fitted for soul vessels. Adding an active soul vessel to it results in a small construct with tools and an inbuilt proselytizer."
invocations = list("Call forth...", "...the workers of Armorer.")
channel_time = 60
- consumed_components = list(BELLIGERENT_EYE = 1, HIEROPHANT_ANSIBLE = 1)
+ consumed_components = list(BELLIGERENT_EYE = 2, HIEROPHANT_ANSIBLE = 1)
object_path = /obj/structure/destructible/clockwork/shell/cogscarab
creator_message = "You form a cogscarab, a constructor soul vessel receptacle."
observer_message = "The slab disgorges a puddle of black metal that contracts and forms into a strange shell!"
@@ -56,7 +56,7 @@
Matrices have drained from non-Servants. Dead Servants can be revived by this sigil if there is vitality equal to the target Servant's non-oxygen damage."
invocations = list("Divinity...", "...steal their life...", "...for these shells!")
channel_time = 60
- consumed_components = list(BELLIGERENT_EYE = 1, VANGUARD_COGWHEEL = 1)
+ consumed_components = list(BELLIGERENT_EYE = 1, VANGUARD_COGWHEEL = 2)
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/vitality
creator_message = "A vitality matrix appears below you. It will drain life from non-Servants and heal Servants that cross it."
@@ -69,6 +69,118 @@
quickbind_desc = "Creates a Vitality Matrix, which drains non-Servants on it to heal Servants that cross it."
+//Mending Mantra: Channeled for up to ten times over twenty seconds to repair structures and heal allies
+/datum/clockwork_scripture/channeled/mending_mantra
+ descname = "Channeled, Area Healing and Repair"
+ name = "Mending Mantra"
+ desc = "Repairs nearby structures and constructs. Servants wearing clockwork armor will also be healed. Channeled every two seconds for a maximum of twenty seconds."
+ chant_invocations = list("Mend our dents!", "Heal our scratches!", "Repair our gears!")
+ chant_amount = 10
+ chant_interval = 20
+ consumed_components = list(VANGUARD_COGWHEEL = 2, REPLICANT_ALLOY = 1)
+ usage_tip = "This is a very effective way to rapidly reinforce a base after an attack."
+ tier = SCRIPTURE_SCRIPT
+ primary_component = VANGUARD_COGWHEEL
+ sort_priority = 4
+ quickbind = TRUE
+ quickbind_desc = "Repairs nearby structures and constructs. Servants wearing clockwork armor will also be healed.
Maximum 10 chants."
+ var/heal_attempts = 4
+ var/heal_amount = 2.5
+ var/static/list/damage_heal_order = list(BRUTE, BURN, OXY)
+ var/static/list/heal_finish_messages = list("There, all mended!", "Try not to get too damaged.", "No more dents and scratches for you!", "Champions never die.", "All patched up.", \
+ "Ah, child, it's okay now.", "Pain is temporary.", "What you do for the Justiciar is eternal.", "Bear this for me.", "Be strong, child.", "Please, be careful!", \
+ "If you die, you will be remembered.")
+ var/static/list/heal_target_typecache = typecacheof(list(
+ /obj/structure/destructible/clockwork,
+ /obj/machinery/door/airlock/clockwork,
+ /obj/machinery/door/window/clockwork,
+ /obj/structure/window/reinforced/clockwork,
+ /obj/structure/table/reinforced/brass))
+ var/static/list/ratvarian_armor_typecache = typecacheof(list(
+ /obj/item/clothing/suit/armor/clockwork,
+ /obj/item/clothing/head/helmet/clockwork,
+ /obj/item/clothing/gloves/clockwork,
+ /obj/item/clothing/shoes/clockwork))
+
+/datum/clockwork_scripture/channeled/mending_mantra/chant_effects(chant_number)
+ var/turf/T
+ for(var/atom/movable/M in range(7, invoker))
+ if(isliving(M))
+ if(isclockmob(M) || istype(M, /mob/living/simple_animal/drone/cogscarab))
+ var/mob/living/simple_animal/S = M
+ if(S.health == S.maxHealth || S.stat == DEAD)
+ continue
+ T = get_turf(M)
+ for(var/i in 1 to heal_attempts)
+ if(S.health < S.maxHealth)
+ S.adjustHealth(-heal_amount)
+ new /obj/effect/overlay/temp/heal(T, "#1E8CE1")
+ if(i == heal_attempts && S.health >= S.maxHealth) //we finished healing on the last tick, give them the message
+ to_chat(S, "\"[text2ratvar(pick(heal_finish_messages))]\"")
+ break
+ else
+ to_chat(S, "\"[text2ratvar(pick(heal_finish_messages))]\"")
+ break
+ else if(issilicon(M))
+ var/mob/living/silicon/S = M
+ if(S.health == S.maxHealth || S.stat == DEAD || !is_servant_of_ratvar(S))
+ continue
+ T = get_turf(M)
+ for(var/i in 1 to heal_attempts)
+ if(S.health < S.maxHealth)
+ S.heal_ordered_damage(heal_amount, damage_heal_order)
+ new /obj/effect/overlay/temp/heal(T, "#1E8CE1")
+ if(i == heal_attempts && S.health >= S.maxHealth)
+ to_chat(S, "\"[text2ratvar(pick(heal_finish_messages))]\"")
+ break
+ else
+ to_chat(S, "\"[text2ratvar(pick(heal_finish_messages))]\"")
+ break
+ else if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ if(H.health == H.maxHealth || H.stat == DEAD || !is_servant_of_ratvar(H))
+ continue
+ T = get_turf(M)
+ var/heal_ticks = 0 //one heal tick for each piece of ratvarian armor worn
+ var/obj/item/I = H.get_item_by_slot(slot_wear_suit)
+ if(is_type_in_typecache(I, ratvarian_armor_typecache))
+ heal_ticks++
+ I = H.get_item_by_slot(slot_head)
+ if(is_type_in_typecache(I, ratvarian_armor_typecache))
+ heal_ticks++
+ I = H.get_item_by_slot(slot_gloves)
+ if(is_type_in_typecache(I, ratvarian_armor_typecache))
+ heal_ticks++
+ I = H.get_item_by_slot(slot_shoes)
+ if(is_type_in_typecache(I, ratvarian_armor_typecache))
+ heal_ticks++
+ if(heal_ticks)
+ for(var/i in 1 to heal_ticks)
+ if(H.health < H.maxHealth)
+ H.heal_ordered_damage(heal_amount, damage_heal_order)
+ new /obj/effect/overlay/temp/heal(T, "#1E8CE1")
+ if(i == heal_ticks && H.health >= H.maxHealth)
+ to_chat(H, "\"[text2ratvar(pick(heal_finish_messages))]\"")
+ break
+ else
+ to_chat(H, "\"[text2ratvar(pick(heal_finish_messages))]\"")
+ break
+ else if(is_type_in_typecache(M, heal_target_typecache))
+ var/obj/structure/destructible/clockwork/C = M
+ if(C.obj_integrity == C.max_integrity || (istype(C) && !C.can_be_repaired))
+ continue
+ T = get_turf(M)
+ for(var/i in 1 to heal_attempts)
+ if(C.obj_integrity < C.max_integrity)
+ C.obj_integrity = min(C.obj_integrity + 5, C.max_integrity)
+ C.update_icon()
+ new /obj/effect/overlay/temp/heal(T, "#1E8CE1")
+ else
+ break
+ new /obj/effect/overlay/temp/ratvar/mending_mantra(get_turf(invoker))
+ return TRUE
+
+
//Sigil of Submission: Creates a sigil of submission, which converts one heretic above it after a delay.
/datum/clockwork_scripture/create_object/sigil_of_submission
descname = "Trap, Conversion"
@@ -76,7 +188,7 @@
desc = "Places a luminous sigil that will enslave any valid beings standing on it after a time."
invocations = list("Divinity, enlighten...", "...those who trespass here!")
channel_time = 60
- consumed_components = list(BELLIGERENT_EYE = 1, GEIS_CAPACITOR = 1)
+ consumed_components = list(BELLIGERENT_EYE = 1, GEIS_CAPACITOR = 2)
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/submission
creator_message = "A luminous sigil appears below you. The next non-servant to cross it will be enslaved after a brief time if they do not move."
@@ -96,7 +208,7 @@
desc = "Forms an ancient positronic brain with an overriding directive to serve Ratvar."
invocations = list("Herd the souls of...", "...the blasphemous damned!")
channel_time = 30
- consumed_components = list(VANGUARD_COGWHEEL = 1, GEIS_CAPACITOR = 1)
+ consumed_components = list(VANGUARD_COGWHEEL = 1, GEIS_CAPACITOR = 2)
whispered = TRUE
object_path = /obj/item/device/mmi/posibrain/soul_vessel
creator_message = "You form a soul vessel, which can be used in-hand to attract spirits, or used on an unconscious or dead human to extract their consciousness."
@@ -116,7 +228,7 @@
desc = "Forms a device that, when used on certain objects, converts them into their Ratvarian equivalents. It requires power to function."
invocations = list("With this device...", "...his presence shall be made known.")
channel_time = 20
- consumed_components = list(GEIS_CAPACITOR = 1, REPLICANT_ALLOY = 1)
+ consumed_components = list(GEIS_CAPACITOR = 1, REPLICANT_ALLOY = 2)
whispered = TRUE
object_path = /obj/item/clockwork/clockwork_proselytizer/preloaded
creator_message = "You form a clockwork proselytizer."
@@ -137,7 +249,7 @@
vanish three minutes after being summoned."
invocations = list("Grant me...", "...the might of brass!")
channel_time = 20
- consumed_components = list(REPLICANT_ALLOY = 1, HIEROPHANT_ANSIBLE = 1)
+ consumed_components = list(REPLICANT_ALLOY = 2, HIEROPHANT_ANSIBLE = 1)
whispered = TRUE
usage_tip = "You can impale human targets with the spear by pulling them, then attacking. Throwing the spear at a mob will do massive damage and stun them, but break the spear."
tier = SCRIPTURE_SCRIPT
@@ -196,7 +308,7 @@
Each servant assisting in the invocation adds one additional use and four additional seconds to the gateway's uses and duration."
invocations = list("Spatial Gateway...", "...activate!")
channel_time = 80
- consumed_components = list(VANGUARD_COGWHEEL = 1, HIEROPHANT_ANSIBLE = 1)
+ consumed_components = list(VANGUARD_COGWHEEL = 1, HIEROPHANT_ANSIBLE = 2)
multiple_invokers_used = TRUE
multiple_invokers_optional = TRUE
usage_tip = "This gateway is strictly one-way and will only allow things through the invoker's portal."
@@ -245,7 +357,7 @@
chant_invocations = list("Use charge to kill!", "Slay with power!", "Hunt with energy!")
chant_amount = 4
chant_interval = 5
- consumed_components = list(GEIS_CAPACITOR = 1, HIEROPHANT_ANSIBLE = 1)
+ consumed_components = list(GEIS_CAPACITOR = 1, HIEROPHANT_ANSIBLE = 2)
usage_tip = "Though it requires you to stand still, this scripture can do massive damage."
tier = SCRIPTURE_SCRIPT
primary_component = HIEROPHANT_ANSIBLE
diff --git a/code/game/gamemodes/clock_cult/clock_structures/clockwork_obelisk.dm b/code/game/gamemodes/clock_cult/clock_structures/clockwork_obelisk.dm
index c32bcb0b4b..580f5fbed5 100644
--- a/code/game/gamemodes/clock_cult/clock_structures/clockwork_obelisk.dm
+++ b/code/game/gamemodes/clock_cult/clock_structures/clockwork_obelisk.dm
@@ -44,50 +44,57 @@
/obj/structure/destructible/clockwork/powered/clockwork_obelisk/attack_hand(mob/living/user)
if(!is_servant_of_ratvar(user) || total_accessable_power() < hierophant_cost || !anchored)
- to_chat(user, "You place your hand on the obelisk, but it doesn't react.")
+ to_chat(user, "You place your hand on [src], but it doesn't react.")
return
- var/choice = alert(user,"You place your hand on the obelisk...",,"Hierophant Broadcast","Spatial Gateway","Cancel")
+ var/choice = alert(user,"You place your hand on [src]...",,"Hierophant Broadcast","Spatial Gateway","Cancel")
switch(choice)
if("Hierophant Broadcast")
if(active)
- to_chat(user, "The obelisk is sustaining a gateway and cannot broadcast!")
+ to_chat(user, "[src] is sustaining a gateway and cannot broadcast!")
return
if(!user.can_speak_vocal())
- to_chat(user, "You cannot speak through the obelisk!")
+ to_chat(user, "You cannot speak through [src]!")
return
var/input = stripped_input(usr, "Please choose a message to send over the Hierophant Network.", "Hierophant Broadcast", "")
if(!is_servant_of_ratvar(user) || !input || !user.canUseTopic(src, !issilicon(user)))
return
+ if(!anchored)
+ to_chat(user, "[src] is no longer secured!")
+ return FALSE
if(active)
- to_chat(user, "The obelisk is sustaining a gateway and cannot broadcast!")
- return
- if(!try_use_power(hierophant_cost))
- to_chat(user, "The obelisk lacks the power to broadcast!")
+ to_chat(user, "[src] is sustaining a gateway and cannot broadcast!")
return
if(!user.can_speak_vocal())
- to_chat(user, "You cannot speak through the obelisk!")
+ to_chat(user, "You cannot speak through [src]!")
+ return
+ if(!try_use_power(hierophant_cost))
+ to_chat(user, "[src] lacks the power to broadcast!")
return
clockwork_say(user, text2ratvar("Hierophant Broadcast, activate! [html_decode(input)]"))
titled_hierophant_message(user, input, "big_brass", "large_brass")
if("Spatial Gateway")
if(active)
- to_chat(user, "The obelisk is already sustaining a gateway!")
- return
- if(!try_use_power(gateway_cost))
- to_chat(user, "The obelisk lacks the power to open a gateway!")
+ to_chat(user, "[src] is already sustaining a gateway!")
return
if(!user.can_speak_vocal())
to_chat(user, "You need to be able to speak to open a gateway!")
return
- if(procure_gateway(user, round(100 * get_efficiency_mod(), 1), round(5 * get_efficiency_mod(), 1), 1) && !active)
- clockwork_say(user, text2ratvar("Spatial Gateway, activate!"))
- else
- return_power(gateway_cost)
+ if(!try_use_power(gateway_cost))
+ to_chat(user, "[src] lacks the power to open a gateway!")
+ return
+ if(procure_gateway(user, round(100 * get_efficiency_mod(), 1), round(5 * get_efficiency_mod(), 1), 1))
+ process()
+ if(!active) //we won't be active if nobody has sent a gateway to us
+ active = TRUE
+ clockwork_say(user, text2ratvar("Spatial Gateway, activate!"))
+ return
+ return_power(gateway_cost) //if we didn't return above, ie, successfully create a gateway, we give the power back
/obj/structure/destructible/clockwork/powered/clockwork_obelisk/process()
if(!anchored)
return
- if(locate(/obj/effect/clockwork/spatial_gateway) in loc)
+ var/obj/effect/clockwork/spatial_gateway/SG = locate(/obj/effect/clockwork/spatial_gateway)
+ if(SG && SG.timerid) //it's a valid gateway, we're active
icon_state = active_icon
density = 0
active = TRUE
diff --git a/code/game/gamemodes/clock_cult/clock_structures/mania_motor.dm b/code/game/gamemodes/clock_cult/clock_structures/mania_motor.dm
index 09372044a9..dd26b5d404 100644
--- a/code/game/gamemodes/clock_cult/clock_structures/mania_motor.dm
+++ b/code/game/gamemodes/clock_cult/clock_structures/mania_motor.dm
@@ -1,8 +1,8 @@
-//Mania Motor: A pair of antenna that, while active, cause braindamage and hallucinations in nearby human mobs.
+//Mania Motor: A pair of antenna that, while active, cause a variety of negative mental effects in nearby human mobs.
/obj/structure/destructible/clockwork/powered/mania_motor
name = "mania motor"
desc = "A pair of antenna with what appear to be sockets around the base. It reminds you of an antlion."
- clockwork_desc = "A transmitter that allows Sevtug to whisper into the minds of nearby non-servants, causing hallucinations and brain damage as long as it remains powered."
+ clockwork_desc = "A transmitter that allows Sevtug to whisper into the minds of nearby non-servants, causing a variety of negative mental effects, up to and including conversion."
icon_state = "mania_motor_inactive"
active_icon = "mania_motor"
inactive_icon = "mania_motor_inactive"
@@ -16,22 +16,11 @@
/obj/item/clockwork/alloy_shards/small = 2, \
/obj/item/clockwork/component/geis_capacitor/antennae = 1)
var/mania_cost = 150
- var/convert_cost = 150
- var/static/list/mania_messages = list("Go nuts.", "Take a crack at crazy.", "Make a bid for insanity.", "Get kooky.", "Move towards mania.", "Become bewildered.", "Wax wild.", \
- "Go round the bend.", "Land in lunacy.", "Try dementia.", "Strive to get a screw loose.")
- var/static/list/compel_messages = list("Come closer.", "Approach the transmitter.", "Touch the antennae.", "I always have to deal with idiots. Move towards the mania motor.", \
- "Advance forward and place your head between the antennae - that's all it's good for.", "If you were smarter, you'd be over here already.", "Move FORWARD, you fool.")
- var/static/list/convert_messages = list("You won't do. Go to sleep while I tell these nitwits how to convert you.", "You are insufficient. I must instruct these idiots in the art of conversion.", \
- "Oh of course, someone we can't convert. These servants are fools.", "How hard is it to use a Sigil, anyway? All it takes is dragging someone onto it.", \
- "How do they fail to use a Sigil of Accession, anyway?", "Why is it that all servants are this inept?", "It's quite likely you'll be stuck here for a while.")
- var/static/list/close_messages = list("Well, you can't reach the motor from THERE, you moron.", "Interesting location. I'd prefer if you went somewhere you could ACTUALLY TOUCH THE ANTENNAE!", \
- "Amazing. You somehow managed to wedge yourself somewhere you can't actually reach the motor from.", "Such a show of idiocy is unparalleled. Perhaps I should put you on display?", \
- "Did you do this on purpose? I can't imagine you doing so accidentally. Oh, wait, I can.", "How is it that such smart creatures can still do something AS STUPID AS THIS!")
/obj/structure/destructible/clockwork/powered/mania_motor/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
- to_chat(user, "It requires [mania_cost]W to run, and at least [convert_cost]W to attempt to convert humans adjacent to it.")
+ to_chat(user, "It requires [mania_cost]W to run.")
/obj/structure/destructible/clockwork/powered/mania_motor/forced_disable(bad_effects)
if(active)
@@ -60,82 +49,17 @@
if(!try_use_power(mania_cost))
forced_disable(FALSE)
return
- var/turf/T = get_turf(src)
- var/hum = get_sfx('sound/effects/screech.ogg') //like playsound, same sound for everyone affected
var/efficiency = get_efficiency_mod()
for(var/mob/living/carbon/human/H in viewers(7, src))
- if(is_servant_of_ratvar(H)) //heals servants of braindamage, hallucination, druggy, dizziness, and confusion
- var/brainloss = H.getBrainLoss()
- if(brainloss)
- H.adjustBrainLoss(-brainloss)
- if(H.hallucination)
- H.hallucination = 0
- if(H.druggy)
- H.adjust_drugginess(-H.druggy)
- if(H.dizziness)
- H.dizziness = 0
- if(H.confused)
- H.confused = 0
- else if(!H.null_rod_check() && H.stat != DEAD)
- var/distance = 0 + get_dist(T, get_turf(H))
- var/falloff_distance = min((110) - distance * 10, 80)
- var/sound_distance = falloff_distance * 0.5
- var/targetbrainloss = H.getBrainLoss()
- if(distance > 3 && prob(falloff_distance * 0.5))
- to_chat(H, "\"[text2ratvar(pick(mania_messages))]\"")
- if(distance <= 1)
- if(!H.Adjacent(src))
- to_chat(H, "\"[text2ratvar(pick(close_messages))]\"")
- H.playsound_local(T, hum, sound_distance, 1)
- else if(!try_use_power(convert_cost))
- visible_message("[src]'s antennae fizzle quietly.")
- playsound(src, 'sound/effects/light_flicker.ogg', 50, 1)
- else
- H.playsound_local(T, hum, 80, 1)
- if(!H.stat)
- if(H.getBrainLoss() < 100)
- H.adjustBrainLoss(20 * efficiency)
- H.visible_message("[H] reaches out and touches [src].", "You touch [src] involuntarily.")
- else
- H.Paralyse(3)
- else if(is_eligible_servant(H))
- to_chat(H, "\"[text2ratvar("You are mine and his, now.")]\"")
- add_servant_of_ratvar(H)
- H.Paralyse(5)
- else
- H.playsound_local(T, hum, sound_distance, 1)
- switch(distance)
- if(0 to 3)
- if(prob(falloff_distance * 0.5))
- if(prob(falloff_distance))
- to_chat(H, "\"[text2ratvar(pick(mania_messages))]\"")
- else
- to_chat(H, "\"[text2ratvar(pick(compel_messages))]\"")
- if(targetbrainloss <= 40)
- H.adjustBrainLoss(3 * efficiency)
- H.adjust_drugginess(Clamp(7 * efficiency, 0, 50 - H.druggy))
- H.hallucination = min(H.hallucination + (7 * efficiency), 50)
- H.dizziness = min(H.dizziness + (3 * efficiency), 20)
- H.confused = min(H.confused + (3 * efficiency), 20)
- if(3 to 5)
- if(targetbrainloss <= 20)
- H.adjustBrainLoss(2 * efficiency)
- H.adjust_drugginess(Clamp(5 * efficiency, 0, 25 - H.druggy))
- H.hallucination = min(H.hallucination + (5 * efficiency), 25)
- H.dizziness = min(H.dizziness + (2 * efficiency), 10)
- H.confused = min(H.confused + (2 * efficiency), 10)
- if(5 to 6)
- if(targetbrainloss <= 10)
- H.adjustBrainLoss(1 * efficiency)
- H.adjust_drugginess(Clamp(2 * efficiency, 0, 20 - H.druggy))
- H.hallucination = min(H.hallucination + (2 * efficiency), 20)
- H.dizziness = min(H.dizziness + (2 * efficiency), 5)
- H.confused = min(H.confused + (2 * efficiency), 5)
- if(6 to 7)
- if(targetbrainloss <= 5)
- H.adjustBrainLoss(1 * efficiency)
- H.adjust_drugginess(Clamp(2 * efficiency, 0, 10 - H.druggy))
- H.hallucination = min(H.hallucination + (2 * efficiency), 10)
- if(7 to INFINITY)
- H.adjust_drugginess(Clamp(2 * efficiency, 0, 5 - H.druggy))
- H.hallucination = min(H.hallucination + (2 * efficiency), 5)
+ if(is_servant_of_ratvar(H))
+ continue
+ var/list/effects = H.has_status_effect_list(STATUS_EFFECT_MANIAMOTOR)
+ var/datum/status_effect/maniamotor/M
+ for(var/datum/status_effect/maniamotor/MM in effects)
+ if(MM.motor == src)
+ M = MM
+ break
+ if(!M)
+ M = H.apply_status_effect(STATUS_EFFECT_MANIAMOTOR)
+ M.motor = src
+ M.severity = Clamp(M.severity + ((11 - get_dist(src, H)) * efficiency * efficiency), 0, MAX_MANIA_SEVERITY)
diff --git a/code/game/gamemodes/clock_cult/clock_structures/mending_motor.dm b/code/game/gamemodes/clock_cult/clock_structures/mending_motor.dm
deleted file mode 100644
index 6bd0f46e9d..0000000000
--- a/code/game/gamemodes/clock_cult/clock_structures/mending_motor.dm
+++ /dev/null
@@ -1,107 +0,0 @@
-//Mending Motor: A prism that consumes replicant alloy or power to repair nearby mechanical servants at a quick rate.
-/obj/structure/destructible/clockwork/powered/mending_motor
- name = "mending motor"
- desc = "A dark onyx prism, held in midair by spiraling tendrils of stone."
- clockwork_desc = "A powerful prism that rapidly repairs nearby mechanical servants and clockwork structures."
- icon_state = "mending_motor_inactive"
- active_icon = "mending_motor"
- inactive_icon = "mending_motor_inactive"
- unanchored_icon = "mending_motor_unwrenched"
- construction_value = 20
- max_integrity = 125
- obj_integrity = 125
- break_message = "The prism falls to the ground with a heavy thud!"
- debris = list(/obj/item/clockwork/alloy_shards/small = 3, \
- /obj/item/clockwork/alloy_shards/medium = 1, \
- /obj/item/clockwork/alloy_shards/large = 1, \
- /obj/item/clockwork/component/vanguard_cogwheel/onyx_prism = 1)
- var/heal_attempts = 4
- var/heal_cost = MIN_CLOCKCULT_POWER*2
- var/static/list/damage_heal_order = list(BRUTE, BURN, OXY)
- var/static/list/heal_finish_messages = list("There, all mended!", "Try not to get too damaged.", "No more dents and scratches for you!", "Champions never die.", "All patched up.", \
- "Ah, child, it's okay now.")
- var/static/list/heal_failure_messages = list("Pain is temporary.", "What you do for the Justiciar is eternal.", "Bear this for me.", "Be strong, child.", "Please, be careful!", \
- "If you die, you will be remembered.")
- var/static/list/mending_motor_typecache = typecacheof(list(
- /obj/structure/destructible/clockwork,
- /obj/machinery/door/airlock/clockwork,
- /obj/machinery/door/window/clockwork,
- /obj/structure/window/reinforced/clockwork,
- /obj/structure/table/reinforced/brass))
-
-/obj/structure/destructible/clockwork/powered/mending_motor/examine(mob/user)
- ..()
- if(is_servant_of_ratvar(user) || isobserver(user))
- to_chat(user, "It requires at least [heal_cost]W to attempt to repair clockwork mobs, structures, or converted silicons.")
-
-/obj/structure/destructible/clockwork/powered/mending_motor/forced_disable(bad_effects)
- if(active)
- if(bad_effects)
- try_use_power(heal_cost)
- visible_message("[src] emits an airy chuckling sound and falls dark!")
- toggle()
- return TRUE
-
-/obj/structure/destructible/clockwork/powered/mending_motor/attack_hand(mob/living/user)
- if(user.canUseTopic(src, !issilicon(user), NO_DEXTERY) && is_servant_of_ratvar(user))
- if(total_accessable_power() < MIN_CLOCKCULT_POWER)
- to_chat(user, "[src] needs more power to function!")
- return 0
- toggle(0, user)
-
-/obj/structure/destructible/clockwork/powered/mending_motor/process()
- var/efficiency = get_efficiency_mod()
- for(var/atom/movable/M in range(7, src))
- var/turf/T
- if(isclockmob(M) || istype(M, /mob/living/simple_animal/drone/cogscarab))
- T = get_turf(M)
- var/mob/living/simple_animal/S = M
- if(S.health == S.maxHealth || S.stat == DEAD)
- continue
- for(var/i in 1 to heal_attempts)
- if(S.health < S.maxHealth)
- if(try_use_power(heal_cost))
- S.adjustHealth(-(8 * efficiency))
- new /obj/effect/overlay/temp/heal(T, "#1E8CE1")
- else
- to_chat(S, "\"[text2ratvar(pick(heal_failure_messages))]\"")
- break
- else
- to_chat(S, "\"[text2ratvar(pick(heal_finish_messages))]\"")
- break
- else if(is_type_in_typecache(M, mending_motor_typecache))
- T = get_turf(M)
- var/obj/structure/destructible/clockwork/C = M
- if(C.obj_integrity == C.max_integrity || (istype(C) && !C.can_be_repaired))
- continue
- for(var/i in 1 to heal_attempts)
- if(C.obj_integrity < C.max_integrity)
- if(try_use_power(heal_cost))
- C.obj_integrity = min(C.obj_integrity + (8 * efficiency), C.max_integrity)
- if(C == src)
- efficiency = get_efficiency_mod()
- C.update_icon()
- new /obj/effect/overlay/temp/heal(T, "#1E8CE1")
- else
- break
- else
- break
- else if(issilicon(M))
- T = get_turf(M)
- var/mob/living/silicon/S = M
- if(S.health == S.maxHealth || S.stat == DEAD || !is_servant_of_ratvar(S))
- continue
- for(var/i in 1 to heal_attempts)
- if(S.health < S.maxHealth)
- if(try_use_power(heal_cost))
- S.heal_ordered_damage(8 * efficiency, damage_heal_order)
- new /obj/effect/overlay/temp/heal(T, "#1E8CE1")
- else
- to_chat(S, "\"[text2ratvar(pick(heal_failure_messages))]\"")
- break
- else
- to_chat(S, "\"[text2ratvar(pick(heal_finish_messages))]\"")
- break
- . = ..()
- if(. < heal_cost)
- forced_disable(FALSE)
diff --git a/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm b/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm
index 9b2d868730..a8dfb2dd4a 100644
--- a/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm
+++ b/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm
@@ -28,7 +28,7 @@
var/mutable_appearance/alert_overlay = mutable_appearance('icons/effects/clockwork_effects.dmi', "ratvar_alert")
var/area/A = get_area(src)
notify_ghosts("The Justiciar's light calls to you! Reach out to Ratvar in [A.name] to be granted a shell to spread his glory!", null, source = src, alert_overlay = alert_overlay)
- INVOKE_ASYNC(SSshuttle.emergency, /obj/docking_port/mobile/emergency..proc/request, null, 0)
+ INVOKE_ASYNC(SSshuttle.emergency, /obj/docking_port/mobile/emergency..proc/request, null, 0, 0)
/obj/structure/destructible/clockwork/massive/ratvar/Destroy()
GLOB.ratvar_awakens--
@@ -40,8 +40,8 @@
/obj/structure/destructible/clockwork/massive/ratvar/attack_ghost(mob/dead/observer/O)
var/alertresult = alert(O, "Embrace the Justiciar's light? You can no longer be cloned!",,"Yes", "No")
- if(alertresult == "No" || !O)
- return 0
+ if(alertresult == "No" || QDELETED(O) || !istype(O) || !O.key)
+ return FALSE
var/mob/living/simple_animal/drone/cogscarab/ratvar/R = new/mob/living/simple_animal/drone/cogscarab/ratvar(get_turf(src))
R.visible_message("[R] forms, and its eyes blink open, glowing bright red!")
R.key = O.key
@@ -77,7 +77,7 @@
if(!prey && LAZYLEN(meals))
prey = pick(meals)
to_chat(prey, "\"You will do, heretic.\"\n\
- ")
+ You feel something massive turn its crushing focus to you...")
prey << 'sound/effects/ratvar_reveal.ogg'
else
if((!istype(prey, /obj/singularity/narsie) && prob(10) && LAZYLEN(meals) > 1) || prey.z != z || !(prey in meals))
diff --git a/code/game/gamemodes/clock_cult/clock_structures/tinkerers_daemon.dm b/code/game/gamemodes/clock_cult/clock_structures/tinkerers_daemon.dm
index 22ea9e2ce5..b0a17338ff 100644
--- a/code/game/gamemodes/clock_cult/clock_structures/tinkerers_daemon.dm
+++ b/code/game/gamemodes/clock_cult/clock_structures/tinkerers_daemon.dm
@@ -18,7 +18,7 @@
var/static/mutable_appearance/component_glow = mutable_appearance('icons/obj/clockwork_objects.dmi', "t_random_component")
var/component_id_to_produce
var/production_time = 0 //last time we produced a component
- var/production_cooldown = 120
+ var/production_cooldown = 60
/obj/structure/destructible/clockwork/powered/tinkerers_daemon/Initialize()
. = ..()
diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm
index 65dcb863eb..3b30d3e44a 100644
--- a/code/game/gamemodes/cult/cult.dm
+++ b/code/game/gamemodes/cult/cult.dm
@@ -1,34 +1,31 @@
-
-
/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.has_antag_datum(/datum/antagonist/cultist, TRUE)
+ return istype(M) && M.mind && M.mind.has_antag_datum(ANTAG_DATUM_CULT)
/proc/is_sacrifice_target(datum/mind/mind)
- if(SSticker.mode.name == "cult")
- var/datum/game_mode/cult/cult_mode = SSticker.mode
- if(mind == cult_mode.sacrifice_target)
- return 1
- return 0
+ if(mind == GLOB.sac_mind)
+ return TRUE
+ return FALSE
/proc/is_convertable_to_cult(mob/living/M)
if(!istype(M))
- return 0
+ return FALSE
if(M.mind)
if(ishuman(M) && (M.mind.assigned_role in list("Captain", "Chaplain")))
- return 0
+ return FALSE
if(is_sacrifice_target(M.mind))
- return 0
+ return FALSE
if(M.mind.enslaved_to && !iscultist(M.mind.enslaved_to))
- return 0
+ return FALSE
else
- return 0
+ return FALSE
if(M.isloyal() || issilicon(M) || isbot(M) || isdrone(M) || is_servant_of_ratvar(M))
- return 0 //can't convert machines, shielded, or ratvar's dogs
- return 1
+ return FALSE //can't convert machines, shielded, or ratvar's dogs
+ return TRUE
/datum/game_mode/cult
name = "cult"
@@ -47,14 +44,13 @@
Crew: Prevent the cult from expanding and drive it out."
var/finished = 0
- var/eldergod = 1 //for the summon god objective
var/acolytes_needed = 10 //for the survive objective
var/acolytes_survived = 0
- var/datum/mind/sacrifice_target = null//The target to be sacrificed
var/list/cultists_to_cult = list() //the cultists we'll convert
+
/datum/game_mode/cult/pre_setup()
cult_objectives += "sacrifice"
cult_objectives += "eldergod"
@@ -82,22 +78,6 @@
return (cultists_to_cult.len>=required_enemies)
-/datum/game_mode/cult/proc/memorize_cult_objectives(datum/mind/cult_mind)
- for(var/obj_count = 1,obj_count <= cult_objectives.len,obj_count++)
- var/explanation
- switch(cult_objectives[obj_count])
- if("survive")
- explanation = "Our knowledge must live on. Make sure at least [acolytes_needed] acolytes escape on the shuttle to spread their work on an another station."
- if("sacrifice")
- if(sacrifice_target)
- explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role] via invoking a Sacrifice rune with them on it and three acolytes around it."
- else
- explanation = "Free objective."
- if("eldergod")
- explanation = "Summon Nar-Sie by invoking the rune 'Summon Nar-Sie' with nine acolytes on it. You must do this after sacrificing your target."
- to_chat(cult_mind.current, "Objective #[obj_count]: [explanation]")
- cult_mind.memory += "Objective #[obj_count]: [explanation]
"
-
/datum/game_mode/cult/post_setup()
modePlayer += cultists_to_cult
if("sacrifice" in cult_objectives)
@@ -108,9 +88,18 @@
if(player.mind && !(player.mind in cultists_to_cult))
possible_targets += player.mind
if(possible_targets.len > 0)
- sacrifice_target = pick(possible_targets)
- if(!sacrifice_target)
+ 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!")
for(var/datum/mind/cult_mind in cultists_to_cult)
@@ -158,18 +147,18 @@
/datum/game_mode/proc/add_cultist(datum/mind/cult_mind, stun) //BASE
if (!istype(cult_mind))
return 0
- if(cult_mind.current.gain_antag_datum(/datum/antagonist/cultist))
+ if(cult_mind.add_antag_datum(ANTAG_DATUM_CULT))
if(stun)
cult_mind.current.Paralyse(5)
return 1
/datum/game_mode/proc/remove_cultist(datum/mind/cult_mind, show_message = 1, stun)
if(cult_mind.current)
- var/datum/antagonist/cultist/cult_datum = cult_mind.current.has_antag_datum(/datum/antagonist/cultist, TRUE)
+ var/datum/antagonist/cult/cult_datum = cult_mind.has_antag_datum(ANTAG_DATUM_CULT)
if(!cult_datum)
return FALSE
- cult_datum.silent_update = show_message
- cult_datum.on_remove()
+ cult_datum.silent = show_message
+ cult_datum.on_removal()
if(stun)
cult_mind.current.Paralyse(5)
return TRUE
@@ -198,7 +187,7 @@
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(sacrifice_target && !GLOB.sacrificed.Find(sacrifice_target)) //if the target has been GLOB.sacrificed, ignore this step. otherwise, add 1 to cult_fail
+ 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
@@ -218,12 +207,12 @@
/datum/game_mode/cult/declare_completion()
if(!check_cult_victory())
- feedback_set_details("round_end_result","win - cult win")
- feedback_set("round_end_result",acolytes_survived)
+ SSblackbox.set_details("round_end_result","win - cult win")
+ SSblackbox.set_val("round_end_result",acolytes_survived)
to_chat(world, "The cult has succeeded! Nar-sie has snuffed out another torch in the void!")
else
- feedback_set_details("round_end_result","loss - staff stopped the cult")
- feedback_set("round_end_result",acolytes_survived)
+ SSblackbox.set_details("round_end_result","loss - staff stopped the cult")
+ SSblackbox.set_val("round_end_result",acolytes_survived)
to_chat(world, "The staff managed to stop the cult! Dark words and heresy are no match for Nanotrasen's finest!")
var/text = ""
@@ -236,31 +225,27 @@
if("survive")
if(!check_survive())
explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. ([acolytes_survived] escaped) Success!"
- feedback_add_details("cult_objective","cult_survive|SUCCESS|[acolytes_needed]")
+ SSblackbox.add_details("cult_objective","cult_survive|SUCCESS|[acolytes_needed]")
SSticker.news_report = CULT_ESCAPE
else
explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. ([acolytes_survived] escaped) Fail."
- feedback_add_details("cult_objective","cult_survive|FAIL|[acolytes_needed]")
+ SSblackbox.add_details("cult_objective","cult_survive|FAIL|[acolytes_needed]")
SSticker.news_report = CULT_FAILURE
if("sacrifice")
- if(sacrifice_target)
- if(sacrifice_target in GLOB.sacrificed)
- explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Success!"
- feedback_add_details("cult_objective","cult_sacrifice|SUCCESS")
- else if(sacrifice_target && sacrifice_target.current)
- explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Fail."
- feedback_add_details("cult_objective","cult_sacrifice|FAIL")
- else
- explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Fail (Gibbed)."
- feedback_add_details("cult_objective","cult_sacrifice|FAIL|GIBBED")
+ if(GLOB.sac_complete)
+ explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role]. Success!"
+ SSblackbox.add_details("cult_objective","cult_sacrifice|SUCCESS")
+ else
+ explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role]. Fail."
+ SSblackbox.add_details("cult_objective","cult_sacrifice|FAIL")
if("eldergod")
if(!eldergod)
explanation = "Summon Nar-Sie. Success!"
- feedback_add_details("cult_objective","cult_narsie|SUCCESS")
+ SSblackbox.add_details("cult_objective","cult_narsie|SUCCESS")
SSticker.news_report = CULT_SUMMON
else
explanation = "Summon Nar-Sie. Fail."
- feedback_add_details("cult_objective","cult_narsie|FAIL")
+ SSblackbox.add_details("cult_objective","cult_narsie|FAIL")
SSticker.news_report = CULT_FAILURE
text += "
Objective #[obj_count]: [explanation]"
@@ -269,12 +254,45 @@
return 1
-/datum/game_mode/proc/auto_declare_completion_cult()
- if( cult.len || (SSticker && istype(SSticker.mode,/datum/game_mode/cult)) )
- var/text = "
The cultists were:"
- for(var/datum/mind/cultist in cult)
- text += printplayer(cultist)
-
- text += "
"
-
- to_chat(world, text)
+/datum/game_mode/proc/datum_cult_completion()
+ var/text = ""
+ var/acolytes_survived = 0
+ for(var/datum/mind/cult_mind in cult)
+ if (cult_mind.current && cult_mind.current.stat != DEAD)
+ if(cult_mind.current.onCentcom() || cult_mind.current.onSyndieBase())
+ acolytes_survived++
+ var/cult_fail = 0
+ cult_fail += eldergod
+ if(!GLOB.sac_complete)
+ cult_fail++
+ if(!cult_fail)
+ SSblackbox.set_details("round_end_result","win - cult win")
+ SSblackbox.set_val("round_end_result",acolytes_survived)
+ to_chat(world, "The cult has succeeded! Nar-sie has snuffed out another torch in the void!")
+ else
+ SSblackbox.set_details("round_end_result","loss - staff stopped the cult")
+ SSblackbox.set_val("round_end_result",acolytes_survived)
+ 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_complete)
+ explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role]. Success!"
+ SSblackbox.add_details("cult_objective","cult_sacrifice|SUCCESS")
+ else
+ explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role]. Fail."
+ SSblackbox.add_details("cult_objective","cult_sacrifice|FAIL")
+ if("eldergod")
+ if(!eldergod)
+ explanation = "Summon Nar-Sie. Success!"
+ SSblackbox.add_details("cult_objective","cult_narsie|SUCCESS")
+ SSticker.news_report = CULT_SUMMON
+ else
+ explanation = "Summon Nar-Sie. Fail."
+ SSblackbox.add_details("cult_objective","cult_narsie|FAIL")
+ SSticker.news_report = CULT_FAILURE
+ text += "
Objective #[obj_count]: [explanation]"
+ to_chat(world, text)
diff --git a/code/game/gamemodes/cult/cult_comms.dm b/code/game/gamemodes/cult/cult_comms.dm
index 40c7bbece8..ffaaf46b80 100644
--- a/code/game/gamemodes/cult/cult_comms.dm
+++ b/code/game/gamemodes/cult/cult_comms.dm
@@ -1,3 +1,5 @@
+// Contains cult communion, guide, and cult master abilities
+#define MARK_COOLDOWN
/datum/action/innate/cultcomm
name = "Communion"
@@ -19,11 +21,22 @@
cultist_commune(usr, input)
/proc/cultist_commune(mob/living/user, message)
+ var/my_message
if(!message)
return
- user.whisper("O bidai nabora se[pick("'","`")]sma!")
+ user.whisper("O bidai nabora se[pick("'","`")]sma!", language = /datum/language/common)
user.whisper(html_decode(message))
- var/my_message = "[(ishuman(user) ? "Acolyte" : "Construct")] [findtextEx(user.name, user.real_name) ? user.name : "[user.real_name] (as [user.name])"]: [message]"
+ var/title = "Acolyte"
+ var/span = "cultitalic"
+ if(user.mind && user.mind.has_antag_datum(ANTAG_DATUM_CULT_MASTER))
+ span = "cultlarge"
+ if(ishuman(user))
+ title = "Master"
+ else
+ title = "Lord"
+ else if(!ishuman(user))
+ title = "Construct"
+ my_message = "[title] [findtextEx(user.name, user.real_name) ? user.name : "[user.real_name] (as [user.name])"]: [message]"
for(var/mob/M in GLOB.mob_list)
if(iscultist(M))
to_chat(M, my_message)
@@ -65,3 +78,159 @@
popup.set_content(text)
popup.open()
return 1
+
+/mob/living/proc/cult_master()
+ set category = "Cultist"
+ set name = "Assert Leadership"
+ pollCultists(src) // This proc handles the distribution of cult master actions
+
+/datum/action/innate/cultmast
+ background_icon_state = "bg_demon"
+ buttontooltipstyle = "cult"
+ check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_CONSCIOUS
+
+/datum/action/innate/cultmast/IsAvailable()
+ if(!owner.mind || !owner.mind.has_antag_datum(ANTAG_DATUM_CULT_MASTER))
+ return 0
+ return ..()
+
+/datum/action/innate/cultmast/finalreck
+ name = "Final Reckoning"
+ desc = "A single-use spell that brings the entire cult to the master's location."
+ button_icon_state = "sintouch"
+
+/datum/action/innate/cultmast/finalreck/Activate()
+ for(var/i in 1 to 4)
+ chant(i)
+ var/list/destinations = list()
+ for(var/turf/T in orange(1, owner))
+ if(!is_blocked_turf(T, TRUE))
+ destinations += T
+ if(!LAZYLEN(destinations))
+ 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)
+ if(B.current && B.current.stat != DEAD)
+ var/turf/mobloc = get_turf(B.current)
+ switch(i)
+ if(1)
+ new /obj/effect/overlay/temp/cult/sparks(mobloc, B.current.dir)
+ playsound(mobloc, "sparks", 50, 1)
+ if(2)
+ new /obj/effect/overlay/temp/dir_setting/cult/phase/out(mobloc, B.current.dir)
+ playsound(mobloc, "sparks", 75, 1)
+ if(3)
+ new /obj/effect/overlay/temp/dir_setting/cult/phase(mobloc, B.current.dir)
+ playsound(mobloc, "sparks", 100, 1)
+ if(4)
+ playsound(mobloc, 'sound/magic/exit_blood.ogg', 100, 1)
+ if(B.current != owner)
+ B.current.setDir(SOUTH)
+ var/turf/final = pick(destinations)
+ new /obj/effect/overlay/temp/cult/blood(final)
+ addtimer(CALLBACK(B.current, /mob/.proc/reckon, final), 10)
+ else
+ return
+ GLOB.reckoning_complete = TRUE
+ Remove(owner)
+
+/mob/proc/reckon(turf/final)
+ new /obj/effect/overlay/temp/cult/blood/out(get_turf(src))
+ forceMove(final)
+
+/datum/action/innate/cultmast/finalreck/proc/chant(chant_number)
+ switch(chant_number)
+ if(1)
+ owner.say("C'arta forbici!", language = /datum/language/common)
+ if(2)
+ owner.say("Pleggh e'ntrath!", language = /datum/language/common)
+ playsound(get_turf(owner),'sound/magic/clockwork/narsie_attack.ogg', 50, 1)
+ if(3)
+ owner.say("Barhah hra zar'garis!", language = /datum/language/common)
+ playsound(get_turf(owner),'sound/magic/clockwork/narsie_attack.ogg', 75, 1)
+ if(4)
+ owner.say("N'ath reth sh'yro eth d'rekkathnor!!!", language = /datum/language/common)
+ playsound(get_turf(owner),'sound/magic/clockwork/narsie_attack.ogg', 100, 1)
+
+/datum/action/innate/cultmast/cultmark
+ name = "Mark Target"
+ desc = "Marks a target for the cult."
+ button_icon_state = "cult_mark"
+ var/obj/effect/proc_holder/cultmark/CM
+ var/cooldown = 0
+ var/base_cooldown = 1200
+
+/datum/action/innate/cultmast/cultmark/New()
+ CM = new()
+ CM.attached_action = src
+ ..()
+
+/datum/action/innate/cultmast/cultmark/IsAvailable()
+ if(!owner.mind || !owner.mind.has_antag_datum(ANTAG_DATUM_CULT_MASTER))
+ return 0
+ if(cooldown > world.time)
+ if(!CM.active)
+ owner << "You need to wait [round((cooldown - world.time) * 0.1)] seconds before you can mark another target!"
+ return 0
+ return ..()
+
+/datum/action/innate/cultmast/cultmark/Destroy()
+ QDEL_NULL(CM)
+ return ..()
+
+/datum/action/innate/cultmast/cultmark/Activate()
+ CM.toggle(owner) //the important bit
+ return TRUE
+
+/obj/effect/proc_holder/cultmark
+ active = FALSE
+ ranged_mousepointer = 'icons/effects/cult_target.dmi'
+ var/datum/action/innate/cultmast/cultmark/attached_action
+
+/obj/effect/proc_holder/cultmark/Destroy()
+ attached_action = null
+ return ..()
+
+/obj/effect/proc_holder/cultmark/proc/toggle(mob/user)
+ if(active)
+ remove_ranged_ability("You cease the marking ritual.")
+ else
+ add_ranged_ability(user, "You prepare to mark a target for your cult...")
+
+/obj/effect/proc_holder/cultmark/InterceptClickOn(mob/living/caller, params, atom/target)
+ if(..())
+ return
+ if(ranged_ability_user.incapacitated())
+ remove_ranged_ability()
+ return
+ var/turf/T = get_turf(ranged_ability_user)
+ if(!isturf(T))
+ return FALSE
+ if(target in view(7, get_turf(ranged_ability_user)))
+ GLOB.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
+ 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!")
+ B.current << pick(sound('sound/hallucinations/over_here2.ogg',0,1,75), sound('sound/hallucinations/over_here3.ogg',0,1,75))
+ B.current.client.images += GLOB.blood_target_image
+ attached_action.owner.update_action_buttons_icon()
+ remove_ranged_ability("The marking rite is complete! It will last for 90 seconds.")
+ addtimer(CALLBACK(GLOBAL_PROC, .proc/reset_blood_target), 900, TIMER_OVERRIDE)
+ return TRUE
+ return FALSE
+
+/proc/reset_blood_target()
+ for(var/datum/mind/B in SSticker.mode.cult)
+ if(B.current && B.current.stat != DEAD && B.current.client)
+ if(GLOB.blood_target)
+ to_chat(B.current,"The blood mark has expired!")
+ B.current.client.images -= GLOB.blood_target_image
+ QDEL_NULL(GLOB.blood_target)
\ No newline at end of file
diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm
index a90aeb78bc..6058e8f605 100644
--- a/code/game/gamemodes/cult/cult_items.dm
+++ b/code/game/gamemodes/cult/cult_items.dm
@@ -213,7 +213,7 @@
flags_inv = HIDEJUMPSUIT
allowed = list(/obj/item/weapon/tome,/obj/item/weapon/melee/cultblade)
body_parts_covered = CHEST|GROIN|LEGS|ARMS
- armor = list(melee = -50, bullet = -50, laser = -100,energy = -50, bomb = -50, bio = -50, rad = -50, fire = 0, acid = 0)
+ armor = list(melee = -50, bullet = -50, laser = -50,energy = -50, bomb = -50, bio = -50, rad = -50, fire = 0, acid = 0)
slowdown = -1
hoodtype = /obj/item/clothing/head/hooded/berserkerhood
diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm
index 2ed6840c91..069a422480 100644
--- a/code/game/gamemodes/cult/ritual.dm
+++ b/code/game/gamemodes/cult/ritual.dm
@@ -133,7 +133,7 @@ This file contains the arcane tome files.
text += "Talisman of Armaments
The Talisman of Arming will equip the user with armored robes, a backpack, an eldritch longsword, an empowered bola, and a pair of boots. Any items that cannot \
be equipped will not be summoned. Attacking a fellow cultist with it will instead equip them.
"
- text += "Talisman of Horrors
The Talisman of Horror must be applied directly to the victim, it will shatter your victim's mind with visions of the endtimes that may incapitate them.
"
+ text += "Talisman of Horrors
The Talisman of Horror, unlike other talismans, can be applied at range, without the victim noticing. It will cause the victim to have severe hallucinations after a short while.
"
text += "Talisman of Shackling
The Talisman of Shackling must be applied directly to the victim, it has 4 uses and cuffs victims with magic shackles that disappear when removed.
"
@@ -197,35 +197,30 @@ This file contains the arcane tome files.
if(!src || QDELETED(src) || !Adjacent(user) || user.incapacitated() || !check_rune_turf(Turf, user))
return
if(ispath(rune_to_scribe, /obj/effect/rune/narsie))
- if(SSticker.mode.name == "cult")
- var/datum/game_mode/cult/cult_mode = SSticker.mode
- if(!("eldergod" in cult_mode.cult_objectives))
- to_chat(user, "Nar-Sie does not wish to be summoned!")
- return
- if(cult_mode.sacrifice_target && !(cult_mode.sacrifice_target in GLOB.sacrificed))
- to_chat(user, "The sacrifice is not complete. The portal would lack the power to open if you tried!")
- return
- if(!cult_mode.eldergod)
- to_chat(user, "\"I am already here. There is no need to try to summon me now.\"")
- return
- if((loc.z && loc.z != ZLEVEL_STATION) || !A.blob_allowed)
- to_chat(user, "The Geometer is not interested in lesser locations; the station is the prize!")
- 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")
- to_chat(user, "You decide to prepare further before scribing the rune.")
- return
- Turf = get_turf(user)
- A = get_area(src)
- if(!check_rune_turf(Turf, user) || (loc.z && loc.z != ZLEVEL_STATION)|| !A.blob_allowed)
- 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 Dimensionsal Affairs", 'sound/AI/spanomalies.ogg')
- for(var/B in spiral_range_turfs(1, user, 1))
- var/obj/structure/emergency_shield/sanguine/N = new(B)
- shields += N
- else
+ if(!("eldergod" in SSticker.mode.cult_objectives))
to_chat(user, "Nar-Sie does not wish to be summoned!")
return
+ if(!GLOB.sac_complete)
+ to_chat(user, "The sacrifice is not complete. The portal would lack the power to open if you tried!")
+ return
+ if(!SSticker.mode.eldergod)
+ to_chat(user, "\"I am already here. There is no need to try to summon me now.\"")
+ return
+ if((loc.z && loc.z != ZLEVEL_STATION) || !A.blob_allowed)
+ to_chat(user, "The Geometer is not interested in lesser locations; the station is the prize!")
+ 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")
+ to_chat(user, "You decide to prepare further before scribing the rune.")
+ return
+ Turf = get_turf(user)
+ A = get_area(src)
+ if(!check_rune_turf(Turf, user) || (loc.z && loc.z != ZLEVEL_STATION)|| !A.blob_allowed)
+ 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))
+ var/obj/structure/emergency_shield/sanguine/N = new(B)
+ shields += N
user.visible_message("[user] [user.blood_volume ? "cuts open their arm and begins writing in their own blood":"begins sketching out a strange design"]!", \
"You [user.blood_volume ? "slice open your arm and ":""]begin drawing a sigil of the Geometer.")
if(user.blood_volume)
@@ -247,7 +242,7 @@ This file contains the arcane tome files.
var/obj/effect/rune/R = new rune_to_scribe(Turf, chosen_keyword)
R.add_mob_blood(user)
to_chat(user, "The [lowertext(R.cultist_name)] rune [R.cultist_desc]")
- feedback_add_details("cult_runes_scribed", R.cultist_name)
+ SSblackbox.add_details("cult_runes_scribed", R.cultist_name)
/obj/item/weapon/tome/proc/check_rune_turf(turf/T, mob/user)
var/area/A = get_area(T)
diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm
index 296b248938..4573b460da 100644
--- a/code/game/gamemodes/cult/runes.dm
+++ b/code/game/gamemodes/cult/runes.dm
@@ -148,8 +148,7 @@ structure_check() searches for nearby cultist structures required for the invoca
/obj/effect/rune/proc/fail_invoke()
//This proc contains the effects of a rune if it is not invoked correctly, through either invalid wording or not enough cultists. By default, it's just a basic fizzle.
- visible_message("The markings pulse with a \
- small flash of red light, then fall dark.")
+ visible_message("The markings pulse with a small flash of red light, then fall dark.")
var/oldcolor = color
color = rgb(255, 0, 0)
animate(src, color = oldcolor, time = 5)
@@ -211,7 +210,7 @@ structure_check() searches for nearby cultist structures required for the invoca
log_game("Talisman Creation rune failed - already in use")
return
- for(var/I in subtypesof(/obj/item/weapon/paper/talisman) - /obj/item/weapon/paper/talisman/malformed - /obj/item/weapon/paper/talisman/supply - /obj/item/weapon/paper/talisman/supply/weak)
+ for(var/I in subtypesof(/obj/item/weapon/paper/talisman) - /obj/item/weapon/paper/talisman/malformed - /obj/item/weapon/paper/talisman/supply - /obj/item/weapon/paper/talisman/supply/weak - /obj/item/weapon/paper/talisman/summon_tome)
var/obj/item/weapon/paper/talisman/J = I
var/talisman_cult_name = initial(J.cultist_name)
if(talisman_cult_name)
@@ -309,10 +308,11 @@ structure_check() searches for nearby cultist structures required for the invoca
A.forceMove(target)
if(movedsomething)
..()
- visible_message("There is a sharp crack of inrushing air, and everything above the rune disappears!")
+ visible_message("There is a sharp crack of inrushing air, and everything above the rune disappears!", null, "You hear a sharp crack.")
to_chat(user, "You[moveuserlater ? "r vision blurs, and you suddenly appear somewhere else":" send everything above the rune away"].")
if(moveuserlater)
user.forceMove(target)
+ target.visible_message("There is a boom of outrushing air as something appears above the rune!", null, "You hear a boom.")
else
fail_invoke()
@@ -398,23 +398,23 @@ 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/big_sac = FALSE
if((((ishuman(sacrificial) || iscyborg(sacrificial)) && sacrificial.stat != DEAD) || 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
- var/sacrifice_fulfilled = FALSE
-
if(sacrificial.mind)
GLOB.sacrificed += sacrificial.mind
if(is_sacrifice_target(sacrificial.mind))
- sacrifice_fulfilled = TRUE
+ GLOB.sac_complete = TRUE
+ big_sac = TRUE
else
GLOB.sacrificed += sacrificial
new /obj/effect/overlay/temp/cult/sac(get_turf(src))
for(var/M in invokers)
- if(sacrifice_fulfilled)
+ if(big_sac)
to_chat(M, "\"Yes! This is the one I desire! You have done well.\"")
else
if(ishuman(sacrificial) || iscyborg(sacrificial))
@@ -451,7 +451,7 @@ structure_check() searches for nearby cultist structures required for the invoca
scribe_delay = 450 //how long the rune takes to create
scribe_damage = 40.1 //how much damage you take doing it
var/used
- var/ignore_gamemode = FALSE
+ var/ignore_gamemode = TRUE
/obj/effect/rune/narsie/Initialize(mapload, set_keyword)
. = ..()
@@ -490,7 +490,7 @@ structure_check() searches for nearby cultist structures required for the invoca
//BEGIN THE SUMMONING
used = 1
..()
- send_to_playing_players('sound/effects/dimensional_rend.ogg') //There used to be a message for this but every time it was changed it got edgier so I removed it
+ send_to_playing_players('sound/effects/dimensional_rend.ogg')
var/turf/T = get_turf(src)
sleep(40)
if(src)
@@ -633,9 +633,9 @@ structure_check() searches for nearby cultist structures required for the invoca
qdel(src) //delete before pulsing because it's a delay reee
empulse(E, 9*invokers.len, 12*invokers.len) // Scales now, from a single room to most of the station depending on # of chanters
-//Rite of Astral Communion: Separates one's spirit from their body. They will take damage while it is active.
-/obj/effect/rune/astral
- cultist_name = "Astral Communion"
+//Rite of Spirit Sight: Separates one's spirit from their body. They will take damage while it is active.
+/obj/effect/rune/spirit
+ cultist_name = "Spirit Sight"
cultist_desc = "severs the link between one's spirit and body. This effect is taxing and one's physical body will take damage while this is active."
invocation = "Fwe'sh mah erl nyag r'ya!"
icon_state = "7"
@@ -644,24 +644,24 @@ structure_check() searches for nearby cultist structures required for the invoca
construct_invoke = 0
var/mob/living/affecting = null
-/obj/effect/rune/astral/examine(mob/user)
+/obj/effect/rune/spirit/examine(mob/user)
..()
if(affecting)
to_chat(user, "A translucent field encases [user] above the rune!")
-/obj/effect/rune/astral/can_invoke(mob/living/user)
+/obj/effect/rune/spirit/can_invoke(mob/living/user)
if(rune_in_use)
to_chat(user, "[src] cannot support more than one body!")
- log_game("Astral Communion rune failed - more than one user")
+ log_game("Spirit Sight rune failed - more than one user")
return list()
var/turf/T = get_turf(src)
if(!(user in T))
to_chat(user, "You must be standing on top of [src]!")
- log_game("Astral Communion rune failed - user not standing on rune")
+ log_game("Spirit Sight rune failed - user not standing on rune")
return list()
return ..()
-/obj/effect/rune/astral/invoke(var/list/invokers)
+/obj/effect/rune/spirit/invoke(var/list/invokers)
var/mob/living/user = invokers[1]
..()
var/turf/T = get_turf(src)
@@ -898,45 +898,6 @@ structure_check() searches for nearby cultist structures required for the invoca
if(is_servant_of_ratvar(L))
L.adjustStaminaLoss(tick_damage*0.5)
-
-//Deals brute damage to all targets on the rune and heals the invoker for each target drained.
-/obj/effect/rune/leeching
- cultist_name = "Drain Life"
- cultist_desc = "drains the life of all targets on the rune, restoring life to the user."
- invocation = "Yu'gular faras desdae. Umathar uf'kal thenar!"
- icon_state = "3"
- color = "#9F1C34"
-
-/obj/effect/rune/leeching/can_invoke(mob/living/user)
- if(world.time <= user.next_move)
- return list()
- var/turf/T = get_turf(src)
- var/list/potential_targets = list()
- for(var/mob/living/carbon/M in T.contents - user)
- if(M.stat != DEAD)
- potential_targets += M
- if(!potential_targets.len)
- to_chat(user, "There must be at least one valid target on the rune!")
- log_game("Leeching rune failed - no valid targets")
- return list()
- return ..()
-
-/obj/effect/rune/leeching/invoke(var/list/invokers)
- var/mob/living/user = invokers[1]
- user.changeNext_move(CLICK_CD_CLICK_ABILITY)
- ..()
- var/turf/T = get_turf(src)
- for(var/mob/living/carbon/M in T.contents - user)
- if(M.stat != DEAD)
- var/drained_amount = rand(10,20)
- M.apply_damage(drained_amount, BRUTE, "chest")
- user.adjustBruteLoss(-drained_amount)
- to_chat(M, "You feel extremely weak.")
- user.Beam(T,icon_state="drainbeam",time=5)
- user.visible_message("Blood flows from the rune into [user]!", \
- "Blood flows into you, healing your wounds and revitalizing your spirit.")
-
-
//Rite of Spectral Manifestation: Summons a ghost on top of the rune as a cultist human with no items. User must stand on the rune at all times, and takes damage for each summoned ghost.
/obj/effect/rune/manifest
cultist_name = "Manifest Spirit"
diff --git a/code/game/gamemodes/cult/supply.dm b/code/game/gamemodes/cult/supply.dm
new file mode 100644
index 0000000000..71b8c941b1
--- /dev/null
+++ b/code/game/gamemodes/cult/supply.dm
@@ -0,0 +1,124 @@
+//Supply Talisman: Has a few unique effects. Granted only to starter cultists.
+/obj/item/weapon/paper/talisman/supply
+ cultist_name = "Supply Talisman"
+ cultist_desc = "A multi-use talisman that can create various objects. Intended to increase the cult's strength early on."
+ invocation = null
+ uses = 3
+ var/list/possible_summons = list(
+ /datum/cult_supply/tome,
+ /datum/cult_supply/metal,
+ /datum/cult_supply/talisman/teleport,
+ /datum/cult_supply/talisman/emp,
+ /datum/cult_supply/talisman/stun,
+ /datum/cult_supply/talisman/veil,
+ /datum/cult_supply/soulstone,
+ /datum/cult_supply/construct_shell
+ )
+
+/obj/item/weapon/paper/talisman/supply/invoke(mob/living/user, successfuluse = 1)
+ var/list/dat = list()
+ dat += "There are [uses] bloody runes on the parchment.
"
+ dat += "Please choose the chant to be imbued into the fabric of reality.
"
+ dat += "
"
+ for(var/s in possible_summons)
+ var/datum/cult_supply/S = s
+ dat += "[initial(S.invocation)] - [initial(S.desc)]
"
+ var/datum/browser/popup = new(user, "talisman", "", 400, 400)
+ popup.set_content(dat.Join(""))
+ popup.open()
+ return 0
+
+/obj/item/weapon/paper/talisman/supply/Topic(href, href_list)
+ world.log << "[usr], [href], [href_list]"
+ if(QDELETED(src) || usr.incapacitated() || !in_range(src, usr))
+ return
+
+ var/id = href_list["id"]
+ var/datum/cult_supply/match
+
+ for(var/s in possible_summons)
+ var/datum/cult_supply/S = s
+ if(initial(S.id) == id)
+ match = S
+ break
+
+ if(!match)
+ to_chat(usr, "The fabric of reality quivers in agony.")
+ return
+
+ var/turf/T = get_turf(src)
+ var/summon_type = initial(match.summon_type)
+
+
+ var/atom/movable/AM = new summon_type(T)
+ if(istype(AM, /obj/item))
+ usr.put_in_hands(AM)
+
+ uses--
+ if(uses <= 0)
+ to_chat(usr, "[src] crumbles to dust.")
+ burn()
+
+/obj/item/weapon/paper/talisman/supply/weak
+ cultist_name = "Lesser Supply Talisman"
+ uses = 2
+
+/obj/item/weapon/paper/talisman/supply/weak/Initialize(mapload)
+ . = ..()
+ // no runed metal from lesser talismans.
+ possible_summons -= /datum/cult_supply/metal
+
+/datum/cult_supply
+ var/id = "used_popcorn"
+ var/invocation = "Pla'ceho'lder."
+ var/desc = "Summons a generic supply item, to aid the cult."
+ var/summon_type = /obj/item/trash/popcorn // wait this isn't useful
+
+/datum/cult_supply/tome
+ id = "arcane_tome"
+ invocation = "N'ath reth sh'yro eth d'raggathnor!"
+ desc = "Summons an arcane tome, used to scribe runes."
+ summon_type = /obj/item/weapon/tome
+
+/datum/cult_supply/metal
+ id = "runed_metal"
+ invocation = "Bar'tea eas!"
+ desc = "Provides 5 runed metal, which can build a variety of cult structures."
+ summon_type = /obj/item/stack/sheet/runed_metal/five
+
+/datum/cult_supply/talisman/teleport
+ id = "teleport_talisman"
+ invocation = "Sas'so c'arta forbici!"
+ desc = "Allows you to move to a selected teleportation rune."
+ summon_type = /obj/item/weapon/paper/talisman/teleport
+
+/datum/cult_supply/talisman/emp
+ id = "emp_talisman"
+ invocation = "Ta'gh fara'qha fel d'amar det!"
+ desc = "Allows you to destroy technology in a short range."
+ summon_type = /obj/item/weapon/paper/talisman/emp
+
+/datum/cult_supply/talisman/stun
+ id = "stun_talisman"
+ invocation = "Fuu ma'jin!"
+ desc = "Allows you to stun a person by attacking them with the talisman. Does not work on people holding a holy weapon!"
+ summon_type = /obj/item/weapon/paper/talisman/stun
+
+/datum/cult_supply/talisman/veil
+ id = "veil_talisman"
+ invocation = "Kla'atu barada nikt'o!"
+ desc = "Two use talisman, first use makes all nearby runes invisible, secnd use reveals nearby hidden runes."
+ summon_type = /obj/item/weapon/paper/talisman/true_sight
+
+/datum/cult_supply/soulstone
+ id = "soulstone"
+ invocation = "Kal'om neth!"
+ desc = "Summons a soul stone, used to capture the spirits of dead or dying humans."
+ summon_type = /obj/item/device/soulstone
+
+/datum/cult_supply/construct_shell
+ id = "construct_shell"
+ invocation = "Daa'ig osk!"
+ desc = "Summons a construct shell for use with soulstone-captured souls. It is too large to carry on your person."
+ summon_type = /obj/structure/constructshell
+
diff --git a/code/game/gamemodes/cult/talisman.dm b/code/game/gamemodes/cult/talisman.dm
index 2876e80373..15b850eb33 100644
--- a/code/game/gamemodes/cult/talisman.dm
+++ b/code/game/gamemodes/cult/talisman.dm
@@ -45,74 +45,6 @@
var/mob/living/carbon/C = user
C.apply_damage(10, BRUTE, "head")
-//Supply Talisman: Has a few unique effects. Granted only to starter cultists.
-/obj/item/weapon/paper/talisman/supply
- cultist_name = "Supply Talisman"
- cultist_desc = "A multi-use talisman that can create various objects. Intended to increase the cult's strength early on."
- invocation = null
- uses = 3
-
-/obj/item/weapon/paper/talisman/supply/invoke(mob/living/user, successfuluse = 1)
- var/dat = "There are [uses] bloody runes on the parchment.
"
- dat += "Please choose the chant to be imbued into the fabric of reality.
"
- dat += "
"
- dat += "N'ath reth sh'yro eth d'raggathnor! - Summons an arcane tome, used to scribe runes and communicate with other cultists.
"
- dat += "Bar'tea eas! - Provides 5 runed metal.
"
- dat += "Sas'so c'arta forbici! - Allows you to move to a selected teleportation rune.
"
- dat += "Ta'gh fara'qha fel d'amar det! - Allows you to destroy technology in a short range.
"
- dat += "Fuu ma'jin! - Allows you to stun a person by attacking them with the talisman.
"
- dat += "Kla'atu barada nikt'o! - Two use talisman, first use makes all nearby runes invisible, second use reveals nearby hidden runes.
"
- dat += "Kal'om neth! - Summons a soul stone, used to capure the spirits of dead or dying humans.
"
- dat += "Daa'ig osk! - Summons a construct shell for use with soulstone-captured souls. It is too large to carry on your person.
"
- var/datum/browser/popup = new(user, "talisman", "", 400, 400)
- popup.set_content(dat)
- popup.open()
- return 0
-
-/obj/item/weapon/paper/talisman/supply/Topic(href, href_list)
- if(src)
- if(usr.stat || usr.restrained() || !in_range(src, usr))
- return
- if(href_list["rune"])
- switch(href_list["rune"])
- if("newtome")
- var/obj/item/weapon/tome/T = new(usr)
- usr.put_in_hands(T)
- if("metal")
- if(istype(src, /obj/item/weapon/paper/talisman/supply/weak))
- usr.visible_message("Lesser supply talismans lack the strength to materialize runed metal!")
- return
- var/obj/item/stack/sheet/runed_metal/R = new(usr,5)
- usr.put_in_hands(R)
- if("teleport")
- var/obj/item/weapon/paper/talisman/teleport/T = new(usr)
- usr.put_in_hands(T)
- if("emp")
- var/obj/item/weapon/paper/talisman/emp/T = new(usr)
- usr.put_in_hands(T)
- if("runestun")
- var/obj/item/weapon/paper/talisman/stun/T = new(usr)
- usr.put_in_hands(T)
- if("soulstone")
- var/obj/item/device/soulstone/T = new(usr)
- usr.put_in_hands(T)
- if("construct")
- new /obj/structure/constructshell(get_turf(usr))
- if("veiling")
- var/obj/item/weapon/paper/talisman/true_sight/T = new(usr)
- usr.put_in_hands(T)
- src.uses--
- if(src.uses <= 0)
- if(iscarbon(usr))
- var/mob/living/carbon/C = usr
- C.drop_item()
- visible_message("[src] crumbles to dust.")
- qdel(src)
-
-/obj/item/weapon/paper/talisman/supply/weak
- cultist_name = "Lesser Supply Talisman"
- uses = 2
-
//Rite of Translocation: Same as rune
/obj/item/weapon/paper/talisman/teleport
cultist_name = "Talisman of Teleportation"
@@ -147,9 +79,10 @@
if(is_blocked_turf(target, TRUE))
to_chat(user, "The target rune is blocked. Attempting to teleport to it would be massively unwise.")
return ..(user, 0)
- user.visible_message("Dust flows from [user]'s hand, and [user.p_they()] disappear in a flash of red light!", \
- "You speak the words of the talisman and find yourself somewhere else!")
+ user.visible_message("Dust flows from [user]'s hand, and [user.p_they()] disappear with a sharp crack!", \
+ "You speak the words of the talisman and find yourself somewhere else!", "You hear a sharp crack.")
user.forceMove(target)
+ target.visible_message("There is a boom of outrushing air as something appears above the rune!", null, "You hear a boom.")
return ..()
@@ -176,7 +109,7 @@
invocation = "Kla'atu barada nikt'o!"
health_cost = 1
creation_time = 30
- uses = 2
+ uses = 6
var/revealing = FALSE //if it reveals or not
/obj/item/weapon/paper/talisman/true_sight/invoke(mob/living/user, successfuluse = 1)
@@ -186,7 +119,7 @@
"You speak the words of the talisman, hiding nearby runes.")
invocation = "Nikt'o barada kla'atu!"
revealing = TRUE
- for(var/obj/effect/rune/R in range(3,user))
+ for(var/obj/effect/rune/R in range(4,user))
R.talismanhide()
else
user.visible_message("A flash of light shines from [user]'s hand!", \
@@ -194,22 +127,6 @@
for(var/obj/effect/rune/R in range(3,user))
R.talismanreveal()
-//Rite of False Truths: Same as rune
-/obj/item/weapon/paper/talisman/make_runes_fake
- cultist_name = "Talisman of Disguising"
- cultist_desc = "A talisman that will make nearby runes appear fake."
- color = "#ff80d5" // honk
- invocation = "By'o nar'nar!"
- creation_time = 20
-
-/obj/item/weapon/paper/talisman/make_runes_fake/invoke(mob/living/user, successfuluse = 1)
- . = ..()
- user.visible_message("Dust flows from [user]s hand.", \
- "You speak the words of the talisman, making nearby runes appear fake.")
- for(var/obj/effect/rune/R in orange(6,user))
- R.desc = "A rune vandalizing the station."
-
-
//Rite of Disruption: Weaker than rune
/obj/item/weapon/paper/talisman/emp
cultist_name = "Talisman of Electromagnetic Pulse"
@@ -309,12 +226,12 @@
invocation = "Lo'Nab Na'Dm!"
creation_time = 80
-/obj/item/weapon/paper/talisman/horror/attack(mob/living/target, mob/living/user)
- if(iscultist(user))
- to_chat(user, "You disturb [target] with visons of the end!")
+/obj/item/weapon/paper/talisman/horror/afterattack(mob/living/target, mob/living/user)
+ if(iscultist(user) && (get_dist(user, target) < 7))
+ to_chat(user, "You disturb [target] with visions of madness!")
if(iscarbon(target))
var/mob/living/carbon/H = target
- H.reagents.add_reagent("mindbreaker", 25)
+ H.reagents.add_reagent("mindbreaker", 12)
if(is_servant_of_ratvar(target))
to_chat(target, "You see a brief but horrible vision of Ratvar, rusted and scrapped, being torn apart.")
target.emote("scream")
@@ -379,7 +296,7 @@
cultist_desc = "Use this talisman on a victim to handcuff them with dark bindings."
invocation = "In'totum Lig'abis!"
color = "#B27300" // burnt-orange
- uses = 4
+ uses = 6
/obj/item/weapon/paper/talisman/shackle/invoke(mob/living/user, successfuluse = 0)
if(successfuluse) //if we're forced to be successful(we normally aren't) then do the normal stuff
diff --git a/code/game/gamemodes/devil/devil_game_mode.dm b/code/game/gamemodes/devil/devil_game_mode.dm
index 9a528a1240..c275ac790a 100644
--- a/code/game/gamemodes/devil/devil_game_mode.dm
+++ b/code/game/gamemodes/devil/devil_game_mode.dm
@@ -2,7 +2,7 @@
name = "devil"
config_tag = "devil"
antag_flag = ROLE_DEVIL
- protected_jobs = list("Lawyer", "Librarian", "Chaplain", "Head of Security", "Captain", "AI")
+ protected_jobs = list("Lawyer", "Curator", "Chaplain", "Head of Security", "Captain", "AI")
required_players = 0
required_enemies = 1
recommended_enemies = 4
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 2564d1b262..700bfe5f3a 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -80,12 +80,11 @@
report = config.intercept
addtimer(CALLBACK(GLOBAL_PROC, .proc/display_roundstart_logout_report), ROUNDSTART_LOGOUT_REPORT_TIME)
- feedback_set_details("round_start","[time2text(world.realtime)]")
+ SSblackbox.set_details("round_start","[time2text(world.realtime)]")
if(SSticker && SSticker.mode)
- feedback_set_details("game_mode","[SSticker.mode]")
+ SSblackbox.set_details("game_mode","[SSticker.mode]")
if(GLOB.revdata.commit)
- feedback_set_details("revision","[GLOB.revdata.commit]")
- feedback_set_details("server_ip","[world.internet_address]:[world.port]")
+ SSblackbox.set_details("revision","[GLOB.revdata.commit]")
if(report)
addtimer(CALLBACK(src, .proc/send_intercept, 0), rand(waittime_l, waittime_h))
generate_station_goals()
@@ -246,18 +245,20 @@
ghosts++
if(clients > 0)
- feedback_set("round_end_clients",clients)
+ SSblackbox.set_val("round_end_clients",clients)
if(ghosts > 0)
- feedback_set("round_end_ghosts",ghosts)
+ SSblackbox.set_val("round_end_ghosts",ghosts)
if(surviving_humans > 0)
- feedback_set("survived_human",surviving_humans)
+ SSblackbox.set_val("survived_human",surviving_humans)
if(surviving_total > 0)
- feedback_set("survived_total",surviving_total)
+ SSblackbox.set_val("survived_total",surviving_total)
if(escaped_humans > 0)
- feedback_set("escaped_human",escaped_humans)
+ SSblackbox.set_val("escaped_human",escaped_humans)
if(escaped_total > 0)
- feedback_set("escaped_total",escaped_total)
+ SSblackbox.set_val("escaped_total",escaped_total)
send2irc("Server", "Round just ended.")
+ if(cult.len && !istype(SSticker.mode,/datum/game_mode/cult))
+ datum_cult_completion()
return 0
diff --git a/code/game/gamemodes/gang/dominator.dm b/code/game/gamemodes/gang/dominator.dm
index 54099070cd..3b9dce8c3b 100644
--- a/code/game/gamemodes/gang/dominator.dm
+++ b/code/game/gamemodes/gang/dominator.dm
@@ -1,3 +1,5 @@
+#define DOM_BLOCKED_SPAM_CAP 6
+
/obj/machinery/dominator
name = "dominator"
desc = "A visibly sinister device. Looks like you can break it if you hit it enough."
@@ -13,9 +15,20 @@
var/datum/gang/gang
var/operating = 0 //0=standby or broken, 1=takeover
var/warned = 0 //if this device has set off the warning at <3 minutes yet
+ var/spam_prevention = DOM_BLOCKED_SPAM_CAP //first message is immediate
var/datum/effect_system/spark_spread/spark_system
var/obj/effect/countdown/dominator/countdown
+/proc/dominator_excessive_walls(atom/A)
+ var/open = 0
+ for(var/turf/T in circleviewturfs(center=A,radius=3))
+ if(!istype(T, /turf/closed))
+ open++
+ if(open < 40)
+ return TRUE
+ else
+ return FALSE
+
/obj/machinery/dominator/tesla_act()
qdel(src)
@@ -48,6 +61,16 @@
if(gang && gang.is_dominating)
var/time_remaining = gang.domination_time_remaining()
if(time_remaining > 0)
+ if(dominator_excessive_walls(src))
+ gang.domination_timer += 2
+ playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0)
+ if(spam_prevention < DOM_BLOCKED_SPAM_CAP)
+ spam_prevention++
+ else
+ gang.message_gangtools("Warning: There are too many walls around your gang's dominator, its signal is being blocked!")
+ say("Error: Takeover signal is currently blocked! There are too many walls within 3 standard units of this device.")
+ spam_prevention = 0
+ return
. = TRUE
playsound(loc, 'sound/items/timer.ogg', 10, 0)
if(!warned && (time_remaining < 180))
diff --git a/code/game/gamemodes/gang/gang.dm b/code/game/gamemodes/gang/gang.dm
index 22ca4b2298..12191ac44a 100644
--- a/code/game/gamemodes/gang/gang.dm
+++ b/code/game/gamemodes/gang/gang.dm
@@ -249,7 +249,7 @@ GLOBAL_LIST_INIT(gang_colors_pool, list("red","orange","yellow","green","blue","
return gang_bosses
/proc/determine_domination_time(var/datum/gang/G)
- return max(180,900 - (round((G.territory.len/GLOB.start_state.num_territories)*100, 1) * 12))
+ return max(180,480 - (round((G.territory.len/GLOB.start_state.num_territories)*100, 1) * 9))
//////////////////////////////////////////////////////////////////////
//Announces the end of the game with all relavent information stated//
@@ -260,12 +260,12 @@ GLOBAL_LIST_INIT(gang_colors_pool, list("red","orange","yellow","green","blue","
return
if(!winner)
to_chat(world, "The station was [station_was_nuked ? "destroyed!" : "evacuated before a gang could claim it! The station wins!"]
")
- feedback_set_details("round_end_result","loss - gangs failed takeover")
+ SSblackbox.set_details("round_end_result","loss - gangs failed takeover")
SSticker.news_report = GANG_LOSS
else
to_chat(world, "The [winner.name] Gang successfully performed a hostile takeover of the station!
")
- feedback_set_details("round_end_result","win - gang domination complete")
+ SSblackbox.set_details("round_end_result","win - gang domination complete")
SSticker.news_report = GANG_TAKEOVER
@@ -312,7 +312,9 @@ GLOBAL_LIST_INIT(gang_colors_pool, list("red","orange","yellow","green","blue","
G.domination(0.5)
priority_announce("Multiple station takeover attempts have made simultaneously. Conflicting takeover attempts appears to have restarted.","Network Alert")
else
+ var/datum/gang/G = winners[1]
+ G.is_dominating = FALSE
SSticker.mode.explosion_in_progress = 1
- SSticker.station_explosion_cinematic(1)
+ SSticker.station_explosion_cinematic(1,"gang war", null)
SSticker.mode.explosion_in_progress = 0
- SSticker.force_ending = pick(winners)
+ SSticker.force_ending = TRUE
diff --git a/code/game/gamemodes/gang/gang_datum.dm b/code/game/gamemodes/gang/gang_datum.dm
index 3c54d5436b..805cac552f 100644
--- a/code/game/gamemodes/gang/gang_datum.dm
+++ b/code/game/gamemodes/gang/gang_datum.dm
@@ -13,6 +13,7 @@
var/list/territory = list()
var/list/territory_new = list()
var/list/territory_lost = list()
+ var/recalls = 1
var/dom_attempts = 2
var/points = 15
var/datum/atom_hud/antag/gang/ganghud
@@ -262,4 +263,4 @@
ganghud = new()
/datum/gang/multiverse/income()
- return
\ No newline at end of file
+ return
diff --git a/code/game/gamemodes/gang/gang_items.dm b/code/game/gamemodes/gang/gang_items.dm
index a8732c1046..724191c642 100644
--- a/code/game/gamemodes/gang/gang_items.dm
+++ b/code/game/gamemodes/gang/gang_items.dm
@@ -108,10 +108,16 @@
/datum/gang_item/weapon/ammo/get_cost_display(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
return " ↳" + ..() //this is pretty hacky but it looks nice on the popup
+/datum/gang_item/weapon/shuriken
+ name = "Shuriken"
+ id = "shuriken"
+ cost = 3
+ item_path = /obj/item/weapon/throwing_star
+
/datum/gang_item/weapon/switchblade
name = "Switchblade"
id = "switchblade"
- cost = 10
+ cost = 5
item_path = /obj/item/weapon/switchblade
/datum/gang_item/weapon/pistol
@@ -125,6 +131,18 @@
id = "pistol_ammo"
cost = 10
item_path = /obj/item/ammo_box/magazine/m10mm
+
+/datum/gang_item/weapon/sniper
+ name = ".50cal Sniper Rifle"
+ id = "sniper"
+ cost = 40
+ item_path = /obj/item/weapon/gun/ballistic/automatic/sniper_rifle
+
+/datum/gang_item/weapon/ammo/sniper_ammo
+ name = "Standard .50cal Sniper Rounds"
+ id = "sniper_ammo"
+ cost = 15
+ item_path = /obj/item/ammo_box/magazine/sniper_rounds
/datum/gang_item/weapon/uzi
name = "Uzi SMG"
@@ -138,30 +156,8 @@
id = "uzi_ammo"
cost = 40
item_path = /obj/item/ammo_box/magazine/uzim9mm
-
-//SLEEPING CARP
-
-/datum/gang_item/weapon/bostaff
- name = "Bo Staff"
- id = "bostaff"
- cost = 10
- item_path = /obj/item/weapon/twohanded/bostaff
-
-/datum/gang_item/weapon/sleeping_carp_scroll
- name = "Sleeping Carp Scroll (one-use)"
- id = "sleeping_carp_scroll"
- cost = 30
- item_path = /obj/item/weapon/sleeping_carp_scroll
- spawn_msg = "Anyone who reads the sleeping carp scroll will learn secrets of the sleeping carp martial arts style."
-
-/datum/gang_item/weapon/wrestlingbelt
- name = "Wrestling Belt"
- id = "wrastling_belt"
- cost = 20
- item_path = /obj/item/weapon/storage/belt/champion/wrestling
- spawn_msg = "Anyone wearing the wresting belt will know how to be effective with wrestling."
-
-
+
+
///////////////////
//EQUIPMENT
///////////////////
@@ -175,6 +171,12 @@
id = "spraycan"
cost = 5
item_path = /obj/item/toy/crayon/spraycan/gang
+
+/datum/gang_item/equipment/sharpener
+ name = "Sharpener"
+ id = "whetstone"
+ cost = 3
+ item_path = /obj/item/weapon/sharpener
/datum/gang_item/equipment/necklace
name = "Gold Necklace"
@@ -182,12 +184,31 @@
cost = 1
item_path = /obj/item/clothing/neck/necklace/dope
+
+/datum/gang_item/equipment/emp
+ name = "EMP Grenade"
+ id = "EMP"
+ cost = 5
+ item_path = /obj/item/weapon/grenade/empgrenade
+
/datum/gang_item/equipment/c4
name = "C4 Explosive"
id = "c4"
- cost = 10
+ cost = 7
item_path = /obj/item/weapon/grenade/plastic/c4
+/datum/gang_item/equipment/frag
+ name = "Fragmentation Grenade"
+ id = "frag nade"
+ cost = 10
+ item_path = /obj/item/weapon/grenade/syndieminibomb/concussion/frag
+
+/datum/gang_item/equipment/stimpack
+ name = "Black Market Stimulants"
+ id = "stimpack"
+ cost = 15
+ item_path = /obj/item/weapon/reagent_containers/syringe/stimulants
+
/datum/gang_item/equipment/implant_breaker
name = "Implant Breaker"
id = "implant_breaker"
@@ -284,7 +305,11 @@
if(obj.density)
to_chat(user, "There's not enough room here!")
return FALSE
-
+
+ if(dominator_excessive_walls(user))
+ to_chat(user, "span class='warning'>The dominator will not function here! The dominator requires an open space within three standard units so that walls do not interfere with the signal.")
+ return FALSE
+
if(!(usrarea.type in gang.territory|gang.territory_new))
to_chat(user, "The dominator can be spawned only on territory controlled by your gang!")
return FALSE
diff --git a/code/game/gamemodes/gang/gang_pen.dm b/code/game/gamemodes/gang/gang_pen.dm
index 0e01532190..621d40cc45 100644
--- a/code/game/gamemodes/gang/gang_pen.dm
+++ b/code/game/gamemodes/gang/gang_pen.dm
@@ -11,7 +11,7 @@
..()
last_used = world.time
-/obj/item/weapon/pen/gang/attack(mob/living/M, mob/user)
+/obj/item/weapon/pen/gang/attack(mob/living/M, mob/user, stealth = TRUE)
if(!istype(M))
return
if(ishuman(M) && ishuman(user) && M.stat != DEAD)
diff --git a/code/game/gamemodes/gang/recaller.dm b/code/game/gamemodes/gang/recaller.dm
index 0885b32612..f67bcad50a 100644
--- a/code/game/gamemodes/gang/recaller.dm
+++ b/code/game/gamemodes/gang/recaller.dm
@@ -168,6 +168,9 @@
if(recalling)
to_chat(usr, "Error: Recall already in progress.")
return 0
+
+ if(!gang.recalls)
+ to_chat(usr, "Error: Unable to access communication arrays. Firewall has logged our signature and is blocking all further attempts.")
gang.message_gangtools("[usr] is attempting to recall the emergency shuttle.")
recalling = 1
@@ -209,6 +212,7 @@
userturf = get_turf(user)
if(userturf.z == 1) //Check one more time that they are on station.
if(SSshuttle.cancelEvac(user))
+ gang.recalls -= 1
return 1
to_chat(loc, "\icon[src]No response recieved. Emergency shuttle cannot be recalled at this time.")
diff --git a/code/game/gamemodes/meteor/meteor.dm b/code/game/gamemodes/meteor/meteor.dm
index e1a2ddf78f..400b38309b 100644
--- a/code/game/gamemodes/meteor/meteor.dm
+++ b/code/game/gamemodes/meteor/meteor.dm
@@ -50,8 +50,8 @@
else
to_chat(world, "Nobody survived the meteor storm!")
- feedback_set_details("round_end_result","end - evacuation")
- feedback_set("round_end_result",survivors)
+ SSblackbox.set_details("round_end_result","end - evacuation")
+ SSblackbox.set_val("round_end_result",survivors)
..()
return 1
diff --git a/code/game/gamemodes/miniantags/abduction/abductee_objectives.dm b/code/game/gamemodes/miniantags/abduction/abductee_objectives.dm
index aa633e1ef5..574e48fc68 100644
--- a/code/game/gamemodes/miniantags/abduction/abductee_objectives.dm
+++ b/code/game/gamemodes/miniantags/abduction/abductee_objectives.dm
@@ -20,7 +20,7 @@
explanation_text = "Your brain is broken... you can only communicate in"
/datum/objective/abductee/speech/New()
- var/style = pick(list("pantomime", "rhyme", "haiku", "extended metaphors", "riddles", "extremely literal terms", "sound effects", "military jargon"))
+ var/style = pick(list("pantomime", "rhyme", "haiku", "extended metaphors", "riddles", "extremely literal terms", "sound effects", "military jargon", "three word sentences"))
explanation_text+= " [style]."
/datum/objective/abductee/capture
@@ -146,3 +146,25 @@
/datum/objective/abductee/sixthsense
explanation_text = "You died back there and went to heaven... or is it hell? No one here seems to know they're dead. Convince them, and maybe you can escape this limbo."
+
+/datum/objective/abductee/toupefallacy
+ explanation_text = "There are alien parasites masquerading as people's hair. Save people from this invasion."
+
+/datum/objective/abductee/everyoneisthesame
+ explanation_text = "There is only one other person in existence, he is just really good at pretending to be multiple people."
+
+/datum/objective/abductee/forbiddennumber
+ explanation_text = "Numbers, how do they work?" //Shouldn't ever see this.
+
+/datum/objective/abductee/forbiddennumber/New()
+ var/number = rand(2,10)
+ explanation_text = "Ignore anything in a set of [number], they don't exist."
+
+/datum/objective/abductee/foreignname
+ explanation_text = "No matter how they say it, other people keep mispronouncing your name. Be sure to correct them whenever possible."
+
+/datum/objective/abductee/pairoff
+ explanation_text = "Being alone and in large groups are both frightening. Try to be alone with only one other person whenever possible."
+
+/datum/objective/abductee/takeblame
+ explanation_text = "Try to get formally executed for a crime you didn't commit, without a false confession."
diff --git a/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm b/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm
index 193aecd9ce..8f3dfb7ac3 100644
--- a/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm
+++ b/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm
@@ -116,12 +116,13 @@
else
dat += "Subject Status :
"
dat += "[occupant.name] => "
- switch(occupant.stat)
- if(0)
+ var/mob/living/mob_occupant = occupant
+ switch(mob_occupant.stat)
+ if(CONSCIOUS)
dat += "Conscious"
- if(1)
+ if(UNCONSCIOUS)
dat += "Unconscious"
- else
+ else // DEAD
dat += "Deceased"
dat += "
"
dat += "[flash]"
@@ -146,9 +147,11 @@
if(href_list["close"])
close_machine()
return
- if(occupant && occupant.stat != DEAD)
- if(href_list["experiment"])
- flash = Experiment(occupant,href_list["experiment"])
+ if(occupant)
+ var/mob/living/mob_occupant = occupant
+ if(mob_occupant.stat != DEAD)
+ if(href_list["experiment"])
+ flash = Experiment(occupant,href_list["experiment"])
updateUsrDialog()
add_fingerprint(usr)
diff --git a/code/game/gamemodes/miniantags/abduction/machinery/pad.dm b/code/game/gamemodes/miniantags/abduction/machinery/pad.dm
index 5f8ad3de9d..083e610587 100644
--- a/code/game/gamemodes/miniantags/abduction/machinery/pad.dm
+++ b/code/game/gamemodes/miniantags/abduction/machinery/pad.dm
@@ -47,8 +47,8 @@
icon_state = "teleport"
duration = 80
-/obj/effect/overlay/temp/teleport_abductor/New()
+/obj/effect/overlay/temp/teleport_abductor/Initialize()
+ . = ..()
var/datum/effect_system/spark_spread/S = new
S.set_up(10,0,loc)
- S.start()
- ..()
\ No newline at end of file
+ S.start()
\ No newline at end of file
diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
index 6ce04bed14..3b315bb668 100644
--- a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
+++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
@@ -62,7 +62,7 @@
icon = 'icons/mob/swarmer.dmi'
desc = "A robot of unknown design, they seek only to consume materials and replicate themselves indefinitely."
speak_emote = list("tones")
- initial_languages = list(/datum/language/swarmer)
+ initial_language_holder = /datum/language_holder/swarmer
bubble_icon = "swarmer"
health = 40
maxHealth = 40
@@ -527,9 +527,9 @@
icon_state = "disintegrate"
duration = 10
-/obj/effect/overlay/temp/swarmer/disintegration/New()
- playsound(src.loc, "sparks", 100, 1)
- ..()
+/obj/effect/overlay/temp/swarmer/disintegration/Initialize()
+ . = ..()
+ playsound(loc, "sparks", 100, 1)
/obj/effect/overlay/temp/swarmer/dismantle
icon_state = "dismantle"
diff --git a/code/game/gamemodes/miniantags/monkey/monkey.dm b/code/game/gamemodes/miniantags/monkey/monkey.dm
index ecb15bc221..f9ea8dcc2c 100644
--- a/code/game/gamemodes/miniantags/monkey/monkey.dm
+++ b/code/game/gamemodes/miniantags/monkey/monkey.dm
@@ -107,10 +107,10 @@
/datum/game_mode/monkey/declare_completion()
if(check_monkey_victory())
- feedback_set_details("round_end_result","win - monkey win")
- feedback_set("round_end_result",escaped_monkeys)
+ SSblackbox.set_details("round_end_result","win - monkey win")
+ SSblackbox.set_val("round_end_result",escaped_monkeys)
to_chat(world, "The monkeys have overthrown their captors! Eeek eeeek!!")
else
- feedback_set_details("round_end_result","loss - staff stopped the monkeys")
- feedback_set("round_end_result",escaped_monkeys)
+ SSblackbox.set_details("round_end_result","loss - staff stopped the monkeys")
+ SSblackbox.set_val("round_end_result",escaped_monkeys)
to_chat(world, "The staff managed to contain the monkey infestation!")
diff --git a/code/game/gamemodes/miniantags/revenant/revenant.dm b/code/game/gamemodes/miniantags/revenant/revenant.dm
index 7260d8221f..293b114b36 100644
--- a/code/game/gamemodes/miniantags/revenant/revenant.dm
+++ b/code/game/gamemodes/miniantags/revenant/revenant.dm
@@ -14,6 +14,7 @@
var/icon_reveal = "revenant_revealed"
var/icon_stun = "revenant_stun"
var/icon_drain = "revenant_draining"
+ var/stasis = FALSE
incorporeal_move = 3
invisibility = INVISIBILITY_REVENANT
health = INFINITY //Revenants don't use health, they use essence instead
@@ -94,6 +95,8 @@
//Life, Stat, Hud Updates, and Say
/mob/living/simple_animal/revenant/Life()
+ if(stasis)
+ return
if(revealed && essence <= 0)
death()
if(unreveal_time && world.time >= unreveal_time)
@@ -200,9 +203,9 @@
death()
/mob/living/simple_animal/revenant/death()
- if(!revealed || stat == DEAD) //Revenants cannot die if they aren't revealed //or are already dead
+ if(!revealed || stasis) //Revenants cannot die if they aren't revealed //or are already dead
return 0
- ..(1)
+ stasis = TRUE
to_chat(src, "NO! No... it's too late, you can feel your essence [pick("breaking apart", "drifting away")]...")
notransform = TRUE
revealed = TRUE
@@ -217,9 +220,11 @@
var/reforming_essence = essence_regen_cap //retain the gained essence capacity
var/obj/item/weapon/ectoplasm/revenant/R = new(get_turf(src))
R.essence = max(reforming_essence - 15 * perfectsouls, 75) //minus any perfect souls
- R.client_to_revive = src.client //If the essence reforms, the old revenant is put back in the body
- ghostize()
- qdel(src)
+ R.client_to_revive = client //If the essence reforms, the old revenant is put back in the body
+ R.revenant = src
+ invisibility = INVISIBILITY_ABSTRACT
+ revealed = FALSE
+ ghostize(0)//Don't re-enter invisible corpse
return
@@ -302,6 +307,18 @@
to_chat(src, "Lost [essence_amt]E[source ? " from [source]":""].")
return 1
+/mob/living/simple_animal/revenant/proc/death_reset()
+ revealed = FALSE
+ unreveal_time = 0
+ notransform = 0
+ unstun_time = 0
+ inhibited = FALSE
+ draining = FALSE
+ incorporeal_move = 3
+ invisibility = INVISIBILITY_REVENANT
+ alpha=255
+ stasis = FALSE
+
//reforming
/obj/item/weapon/ectoplasm/revenant
@@ -314,11 +331,15 @@
var/reforming = TRUE
var/inert = FALSE
var/client/client_to_revive
+ var/mob/living/simple_animal/revenant/revenant
/obj/item/weapon/ectoplasm/revenant/New()
..()
addtimer(CALLBACK(src, .proc/try_reform), 600)
+/obj/item/weapon/ectoplasm/revenant/proc/scatter()
+ qdel(src)
+
/obj/item/weapon/ectoplasm/revenant/proc/try_reform()
if(reforming)
reforming = FALSE
@@ -333,14 +354,14 @@
user.visible_message("[user] scatters [src] in all directions.", \
"You scatter [src] across the area. The particles slowly fade away.")
user.drop_item()
- qdel(src)
+ scatter()
/obj/item/weapon/ectoplasm/revenant/throw_impact(atom/hit_atom)
..()
if(inert)
return
visible_message("[src] breaks into particles upon impact, which fade away to nothingness.")
- qdel(src)
+ scatter()
/obj/item/weapon/ectoplasm/revenant/examine(mob/user)
..()
@@ -350,47 +371,51 @@
to_chat(user, "It is shifting and distorted. It would be wise to destroy this.")
/obj/item/weapon/ectoplasm/revenant/proc/reform()
- if(QDELETED(src) || inert)
+ if(QDELETED(src) || QDELETED(revenant) || inert)
return
var/key_of_revenant
message_admins("Revenant ectoplasm was left undestroyed for 1 minute and is reforming into a new revenant.")
loc = get_turf(src) //In case it's in a backpack or someone's hand
- var/mob/living/simple_animal/revenant/R = new(get_turf(src))
+ revenant.forceMove(loc)
if(client_to_revive)
for(var/mob/M in GLOB.dead_mob_list)
if(M.client == client_to_revive) //Only recreates the mob if the mob the client is in is dead
- R.client = client_to_revive
+ revenant.client = client_to_revive
key_of_revenant = client_to_revive.key
if(!key_of_revenant)
message_admins("The new revenant's old client either could not be found or is in a new, living mob - grabbing a random candidate instead...")
- var/list/candidates = get_candidates(ROLE_REVENANT)
+ var/list/candidates = pollCandidatesForMob("Do you want to be [revenant.name] (reforming)?", "revenant", null, ROLE_REVENANT, 50, revenant)
if(!candidates.len)
- qdel(R)
+ qdel(revenant)
message_admins("No candidates were found for the new revenant. Oh well!")
inert = TRUE
visible_message("[src] settles down and seems lifeless.")
return
var/client/C = pick(candidates)
+ revenant.client = C
key_of_revenant = C.key
if(!key_of_revenant)
- qdel(R)
+ qdel(revenant)
message_admins("No ckey was found for the new revenant. Oh well!")
inert = TRUE
visible_message("[src] settles down and seems lifeless.")
return
- var/datum/mind/player_mind = new /datum/mind(key_of_revenant)
- R.essence_regen_cap = essence
- R.essence = R.essence_regen_cap
- player_mind.active = 1
- player_mind.transfer_to(R)
- player_mind.assigned_role = "revenant"
- player_mind.special_role = "Revenant"
- SSticker.mode.traitors |= player_mind
+
message_admins("[key_of_revenant] has been [client_to_revive ? "re":""]made into a revenant by reforming ectoplasm.")
log_game("[key_of_revenant] was [client_to_revive ? "re":""]made as a revenant by reforming ectoplasm.")
visible_message("[src] suddenly rises into the air before fading away.")
+
+ revenant.essence = essence
+ revenant.essence_regen_cap = essence
+ revenant.death_reset()
+ revenant.key = key_of_revenant
+ revenant = null
qdel(src)
+/obj/item/weapon/ectoplasm/revenant/Destroy()
+ if(!QDELETED(revenant))
+ qdel(revenant)
+ ..()
//objectives
/datum/objective/revenant
diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm
index d4d788e049..b12dcc3bcc 100644
--- a/code/game/gamemodes/nuclear/nuclear.dm
+++ b/code/game/gamemodes/nuclear/nuclear.dm
@@ -199,70 +199,70 @@
if(nuke_off_station == NUKE_SYNDICATE_BASE)
- feedback_set_details("round_end_result","loss - syndicate nuked - disk secured")
+ SSblackbox.set_details("round_end_result","loss - syndicate nuked - disk secured")
to_chat(world, "Humiliating Syndicate Defeat")
to_chat(world, "The crew of [station_name()] gave [syndicate_name()] operatives back their bomb! The syndicate base was destroyed! Next time, don't lose the nuke!")
SSticker.news_report = NUKE_SYNDICATE_BASE
else if(!disk_rescued && station_was_nuked && !syndies_didnt_escape)
- feedback_set_details("round_end_result","win - syndicate nuke")
+ SSblackbox.set_details("round_end_result","win - syndicate nuke")
to_chat(world, "Syndicate Major Victory!")
to_chat(world, "[syndicate_name()] operatives have destroyed [station_name()]!")
SSticker.news_report = STATION_NUKED
else if (!disk_rescued && station_was_nuked && syndies_didnt_escape)
- feedback_set_details("round_end_result","halfwin - syndicate nuke - did not evacuate in time")
+ SSblackbox.set_details("round_end_result","halfwin - syndicate nuke - did not evacuate in time")
to_chat(world, "Total Annihilation")
to_chat(world, "[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!")
SSticker.news_report = STATION_NUKED
else if (!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape)
- feedback_set_details("round_end_result","halfwin - blew wrong station")
+ SSblackbox.set_details("round_end_result","halfwin - blew wrong station")
to_chat(world, "Crew Minor Victory")
to_chat(world, "[syndicate_name()] operatives secured the authentication disk but blew up something that wasn't [station_name()]. Next time, don't do that!")
SSticker.news_report = NUKE_MISS
else if (!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape)
- feedback_set_details("round_end_result","halfwin - blew wrong station - did not evacuate in time")
+ SSblackbox.set_details("round_end_result","halfwin - blew wrong station - did not evacuate in time")
to_chat(world, "[syndicate_name()] operatives have earned Darwin Award!")
to_chat(world, "[syndicate_name()] operatives blew up something that wasn't [station_name()] and got caught in the explosion. Next time, don't do that!")
SSticker.news_report = NUKE_MISS
else if ((disk_rescued || SSshuttle.emergency.mode != SHUTTLE_ENDGAME) && are_operatives_dead())
- feedback_set_details("round_end_result","loss - evacuation - disk secured - syndi team dead")
+ SSblackbox.set_details("round_end_result","loss - evacuation - disk secured - syndi team dead")
to_chat(world, "Crew Major Victory!")
to_chat(world, "The Research Staff has saved the disk and killed the [syndicate_name()] Operatives")
SSticker.news_report = OPERATIVES_KILLED
else if (disk_rescued)
- feedback_set_details("round_end_result","loss - evacuation - disk secured")
+ SSblackbox.set_details("round_end_result","loss - evacuation - disk secured")
to_chat(world, "Crew Major Victory")
to_chat(world, "The Research Staff has saved the disk and stopped the [syndicate_name()] Operatives!")
SSticker.news_report = OPERATIVES_KILLED
else if (!disk_rescued && are_operatives_dead())
- feedback_set_details("round_end_result","halfwin - evacuation - disk not secured")
+ SSblackbox.set_details("round_end_result","halfwin - evacuation - disk not secured")
to_chat(world, "Neutral Victory!")
to_chat(world, "The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name()] Operatives!")
SSticker.news_report = OPERATIVE_SKIRMISH
else if (!disk_rescued && crew_evacuated)
- feedback_set_details("round_end_result","halfwin - detonation averted")
+ SSblackbox.set_details("round_end_result","halfwin - detonation averted")
to_chat(world, "Syndicate Minor Victory!")
to_chat(world, "[syndicate_name()] operatives survived the assault but did not achieve the destruction of [station_name()]. Next time, don't lose the disk!")
SSticker.news_report = OPERATIVE_SKIRMISH
else if (!disk_rescued && !crew_evacuated)
- feedback_set_details("round_end_result","halfwin - interrupted")
+ SSblackbox.set_details("round_end_result","halfwin - interrupted")
to_chat(world, "Neutral Victory")
to_chat(world, "Round was mysteriously interrupted!")
diff --git a/code/game/gamemodes/nuclear/nuclear_challenge.dm b/code/game/gamemodes/nuclear/nuclear_challenge.dm
index 3e47aa517d..cad958ee64 100644
--- a/code/game/gamemodes/nuclear/nuclear_challenge.dm
+++ b/code/game/gamemodes/nuclear/nuclear_challenge.dm
@@ -57,7 +57,7 @@
U.hidden_uplink.telecrystals = CHALLENGE_TELECRYSTALS
U.hidden_uplink.set_gamemode(/datum/game_mode/nuclear)
config.shuttle_refuel_delay = max(config.shuttle_refuel_delay, CHALLENGE_SHUTTLE_DELAY)
- feedback_set("nuclear_challenge_mode",1)
+ SSblackbox.set_val("nuclear_challenge_mode",1)
qdel(src)
/obj/item/device/nuclear_challenge/proc/check_allowed(mob/living/user)
diff --git a/code/game/gamemodes/nuclear/pinpointer.dm b/code/game/gamemodes/nuclear/pinpointer.dm
index c66b48a6c8..f3ddb6d4be 100644
--- a/code/game/gamemodes/nuclear/pinpointer.dm
+++ b/code/game/gamemodes/nuclear/pinpointer.dm
@@ -17,6 +17,7 @@
var/atom/movable/constant_target = null //The thing we're always focused on, if we're in the right mode
var/target_x = 0 //The target coordinates if we're tracking those
var/target_y = 0
+ var/minimum_range = 0 //at what range the pinpointer declares you to be at your destination
var/nuke_warning = FALSE // If we've set off a miniature alarm about an armed nuke
var/mode = TRACK_NUKE_DISK //What are we looking for?
@@ -111,7 +112,7 @@
var/mob/living/closest_operative = get_closest_atom(/mob/living/carbon/human, possible_targets, here)
if(closest_operative)
target = closest_operative
- if(TRACK_ATOM)
+ if(TRACK_ATOM)
if(constant_target)
target = constant_target
if(TRACK_COORDINATES)
@@ -129,7 +130,7 @@
if(here.z != there.z)
icon_state = "pinon[nuke_warning ? "alert" : ""]null"
return
- if(here == there)
+ if(get_dist_euclidian(here,there)<=minimum_range)
icon_state = "pinon[nuke_warning ? "alert" : ""]direct"
else
setDir(get_dir(here, there))
@@ -171,3 +172,6 @@
desc = "An integrated tracking device, jury-rigged to search for living Syndicate operatives."
mode = TRACK_OPERATIVES
flags = NODROP
+
+
+
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index c082a48224..039b4cc3a4 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -67,7 +67,14 @@
/datum/objective/proc/update_explanation_text()
//Default does nothing, override where needed
-/datum/objective/proc/give_special_equipment()
+/datum/objective/proc/give_special_equipment(special_equipment)
+ if(owner && owner.current)
+ if(ishuman(owner.current))
+ var/mob/living/carbon/human/H = owner.current
+ var/list/slots = list ("backpack" = slot_in_backpack)
+ for(var/eq_path in special_equipment)
+ var/obj/O = new eq_path
+ H.equip_in_one_of_slots(O, slots)
/datum/objective/assassinate
var/target_role_type=0
@@ -94,6 +101,14 @@
else
explanation_text = "Free Objective"
+/datum/objective/assassinate/internal
+ var/stolen = 0 //Have we already eliminated this target?
+
+/datum/objective/assassinate/internal/update_explanation_text()
+ ..()
+ if(target && !target.current)
+ explanation_text = "Assassinate [target.name], who was obliterated"
+
/datum/objective/mutiny
var/target_role_type=0
@@ -468,7 +483,7 @@ GLOBAL_LIST_EMPTY(possible_items)
steal_target = targetinfo.targetitem
explanation_text = "Steal [targetinfo.name]."
dangerrating = targetinfo.difficulty
- give_special_equipment()
+ give_special_equipment(targetinfo.special_equipment)
return steal_target
else
explanation_text = "Free objective"
@@ -511,15 +526,6 @@ GLOBAL_LIST_EMPTY(possible_items)
return 1
return 0
-/datum/objective/steal/give_special_equipment()
- if(owner && owner.current && targetinfo)
- if(ishuman(owner.current))
- var/mob/living/carbon/human/H = owner.current
- var/list/slots = list ("backpack" = slot_in_backpack)
- for(var/eq_path in targetinfo.special_equipment)
- var/obj/O = new eq_path
- H.equip_in_one_of_slots(O, slots)
-
GLOBAL_LIST_EMPTY(possible_items_special)
/datum/objective/steal/special //ninjas are so special they get their own subtype good for them
@@ -654,7 +660,7 @@ GLOBAL_LIST_EMPTY(possible_items_special)
for(var/mob/dead/new_player/P in GLOB.player_list)
if(P.client && P.ready && P.mind!=owner)
n_p ++
- else if (SSticker.current_state == GAME_STATE_PLAYING)
+ else if (SSticker.IsRoundInProgress())
for(var/mob/living/carbon/human/P in GLOB.player_list)
if(P.client && !(P.mind in SSticker.mode.changelings) && P.mind!=owner)
n_p ++
@@ -695,6 +701,9 @@ GLOBAL_LIST_EMPTY(possible_items_special)
explanation_text = "Destroy [target.name], the experimental AI."
else
explanation_text = "Free Objective"
+
+/datum/objective/destroy/internal
+ var/stolen = FALSE //Have we already eliminated this target?
/datum/objective/steal_five_of_type
explanation_text = "Steal at least five items!"
diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm
index 634c68e89a..7a31d1f3cd 100644
--- a/code/game/gamemodes/revolution/revolution.dm
+++ b/code/game/gamemodes/revolution/revolution.dm
@@ -345,13 +345,13 @@
//////////////////////////////////////////////////////////////////////
/datum/game_mode/revolution/declare_completion()
if(finished == 1)
- feedback_set_details("round_end_result","win - heads killed")
+ SSblackbox.set_details("round_end_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)
- feedback_set_details("round_end_result","loss - rev heads killed")
+ SSblackbox.set_details("round_end_result","loss - rev heads killed")
to_chat(world, "The heads of staff managed to stop the revolution!")
SSticker.news_report = REVS_LOSE
diff --git a/code/game/gamemodes/traitor/double_agents.dm b/code/game/gamemodes/traitor/double_agents.dm
index 992e10fa22..2ee546c7aa 100644
--- a/code/game/gamemodes/traitor/double_agents.dm
+++ b/code/game/gamemodes/traitor/double_agents.dm
@@ -1,44 +1,224 @@
-/datum/game_mode/traitor/double_agents
- name = "double agents"
- config_tag = "double_agents"
- required_players = 25
- required_enemies = 5
+#define PINPOINTER_MINIMUM_RANGE 15
+#define PINPOINTER_EXTRA_RANDOM_RANGE 10
+#define PINPOINTER_PING_TIME 40
+
+/datum/game_mode/traitor/internal_affairs
+ name = "Internal Affairs"
+ config_tag = "internal_affairs"
+ employer = "Internal Affairs"
+ required_players = 25
+ required_enemies = 5
recommended_enemies = 8
reroll_friendly = 0
+ traitor_name = "Nanotrasen Internal Affairs Agent"
traitors_possible = 10 //hard limit on traitors if scaling is turned off
num_modifier = 4 // Four additional traitors
- announce_text = "There are double agents trying to kill each other!\n\
- Traitors: Eliminate your targets and protect yourself!\n\
- Crew: Stop the double agents before they can cause too much mayhem."
+ announce_text = "There are Nanotrasen Internal Affairs Agents trying to kill each other!\n\
+ IAA: Eliminate your targets and protect yourself!\n\
+ Crew: Stop the IAA agents before they can cause too much mayhem."
var/list/target_list = list()
var/list/late_joining_list = list()
-/datum/game_mode/traitor/double_agents/post_setup()
+
+/datum/game_mode/traitor/internal_affairs/post_setup()
var/i = 0
for(var/datum/mind/traitor in traitors)
i++
if(i + 1 > traitors.len)
i = 0
- target_list[traitor] = traitors[i + 1]
+ target_list[traitor] = traitors[i+1]
..()
-/datum/game_mode/traitor/double_agents/forge_traitor_objectives(datum/mind/traitor)
+
+/datum/status_effect/agent_pinpointer
+ id = "agent_pinpointer"
+ duration = -1
+ tick_interval = PINPOINTER_PING_TIME
+ alert_type = /obj/screen/alert/status_effect/agent_pinpointer
+ var/minimum_range = PINPOINTER_MINIMUM_RANGE
+ var/mob/scan_target = null
+
+/obj/screen/alert/status_effect/agent_pinpointer
+ name = "Internal Affairs Integrated Pinpointer"
+ desc = "Even stealthier than a normal implant."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "pinon"
+
+/datum/status_effect/agent_pinpointer/proc/point_to_target() //If we found what we're looking for, show the distance and direction
+ if(!scan_target)
+ linked_alert.icon_state = "pinonnull"
+ return
+ var/turf/here = get_turf(owner)
+ var/turf/there = get_turf(scan_target)
+ if(here.z != there.z)
+ linked_alert.icon_state = "pinonnull"
+ return
+ if(get_dist_euclidian(here,there)<=minimum_range + rand(0, PINPOINTER_EXTRA_RANDOM_RANGE))
+ linked_alert.icon_state = "pinondirect"
+ else
+ linked_alert.setDir(get_dir(here, there))
+ switch(get_dist(here, there))
+ if(1 to 8)
+ linked_alert.icon_state = "pinonclose"
+ if(9 to 16)
+ linked_alert.icon_state = "pinonmedium"
+ if(16 to INFINITY)
+ linked_alert.icon_state = "pinonfar"
+
+
+/datum/status_effect/agent_pinpointer/proc/scan_for_target()
+ scan_target = null
+ if(owner)
+ if(owner.mind)
+ if(owner.mind.objectives)
+ for(var/datum/objective/objective_ in owner.mind.objectives)
+ if(!is_internal_objective(objective_))
+ continue
+ var/datum/objective/assassinate/internal/objective = objective_
+ var/mob/current = objective.target.current
+ if(current&¤t.stat!=DEAD)
+ scan_target = current
+ break
+
+
+/datum/status_effect/agent_pinpointer/tick()
+ if(!owner)
+ qdel(src)
+ return
+ scan_for_target()
+ point_to_target()
+
+/proc/give_pinpointer(datum/mind/owner)
+ if(owner && owner.current)
+ owner.current.apply_status_effect(/datum/status_effect/agent_pinpointer)
+
+
+/datum/internal_agent_state
+ var/traitored = FALSE
+ var/datum/mind/owner = null
+ var/list/datum/mind/targets_stolen = list()
+
+/proc/is_internal_objective(datum/objective/O)
+ return (istype(O, /datum/objective/assassinate/internal)||istype(O, /datum/objective/destroy/internal))
+
+/proc/replace_escape_objective(datum/mind/owner)
+ if(!owner||!owner.objectives)
+ return
+ for (var/objective_ in owner.objectives)
+ if(!(istype(objective_, /datum/objective/escape)||istype(objective_,/datum/objective/survive)))
+ continue
+ owner.objectives -= objective_
+ var/datum/objective/martyr/martyr_objective = new
+ martyr_objective.owner = owner
+ owner.objectives += martyr_objective
+
+/proc/reinstate_escape_objective(datum/mind/owner)
+ if(!owner||!owner.objectives)
+ return
+ for (var/objective_ in owner.objectives)
+ if(!istype(objective_, /datum/objective/martyr))
+ continue
+ owner.objectives -= objective_
+ if(issilicon(owner))
+ var/datum/objective/survive/survive_objective = new
+ survive_objective.owner = owner
+ owner.objectives += survive_objective
+ else
+ var/datum/objective/escape/escape_objective = new
+ escape_objective.owner = owner
+ owner.objectives += escape_objective
+
+/datum/internal_agent_state/proc/steal_targets(datum/mind/victim)
+ if(!owner.current||owner.current.stat==DEAD) //Should already be guaranteed if this is only called from steal_targets_timer_func, but better to be safe code than sorry code
+ return
+ var/already_traitored = traitored
+ to_chat(owner.current, " Target eliminated: [victim.name]")
+ for(var/objective_ in victim.objectives)
+ if(istype(objective_, /datum/objective/assassinate/internal))
+ var/datum/objective/assassinate/internal/objective = objective_
+ if(objective.target==owner)
+ traitored = TRUE
+ else if(targets_stolen.Find(objective.target) == 0)
+ var/datum/objective/assassinate/internal/new_objective = new
+ new_objective.owner = owner
+ new_objective.target = objective.target
+ new_objective.update_explanation_text()
+ owner.objectives += new_objective
+ targets_stolen += objective.target
+ var/status_text = objective.check_completion() ? "neutralised" : "active"
+ to_chat(owner.current, " New target added to database: [objective.target.name] ([status_text]) ")
+ else if(istype(objective_, /datum/objective/destroy/internal))
+ var/datum/objective/destroy/internal/objective = objective_
+ var/datum/objective/destroy/internal/new_objective = new
+ if(objective.target==owner)
+ traitored = TRUE
+ else if(targets_stolen.Find(objective.target) == 0)
+ new_objective.owner = owner
+ new_objective.target = objective.target
+ new_objective.update_explanation_text()
+ owner.objectives += new_objective
+ targets_stolen += objective.target
+ var/status_text = objective.check_completion() ? "neutralised" : "active"
+ to_chat(owner.current, " New target added to database: [objective.target.name] ([status_text]) ")
+ if(traitored&&!already_traitored)
+ for(var/objective_ in owner.objectives)
+ if(!is_internal_objective(objective_))
+ continue
+ var/datum/objective/assassinate/internal/objective = objective_
+ if(!objective.check_completion())
+ traitored = FALSE
+ return
+ to_chat(owner.current," All the other agents are dead, and you're the last loose end. Stage a Syndicate terrorist attack to cover up for today's events. You no longer have any limits on collateral damage.")
+ replace_escape_objective(owner)
+
+
+
+/datum/internal_agent_state/proc/steal_targets_timer_func()
+ if(owner&&owner.current&&owner.current.stat!=DEAD)
+ for(var/objective_ in owner.objectives)
+ if(!is_internal_objective(objective_))
+ continue
+ var/datum/objective/assassinate/internal/objective = objective_
+ if(!objective.target)
+ continue
+ if(objective.check_completion())
+ if(objective.stolen)
+ continue
+ else
+ steal_targets(objective.target)
+ objective.stolen = TRUE
+ else
+ if(objective.stolen)
+ var/fail_msg = "Your sensors tell you that [objective.target.current.real_name], one of the targets you were meant to have killed, pulled one over on you, and is still alive - do the job properly this time! "
+ if(traitored)
+ fail_msg += " The truth could still slip out! Cease any terrorist actions as soon as possible, unneeded property damage or loss of employee life will lead to your contract being terminated."
+ reinstate_escape_objective(owner)
+ traitored = FALSE
+ to_chat(owner.current, fail_msg)
+ objective.stolen = FALSE
+ add_steal_targets_timer(owner)
+
+/datum/internal_agent_state/proc/add_steal_targets_timer()
+ var/datum/callback/C = new(src, .steal_targets_timer_func)
+ addtimer(C, 30)
+
+/datum/game_mode/traitor/internal_affairs/forge_traitor_objectives(datum/mind/traitor)
if(target_list.len && target_list[traitor]) // Is a double agent
// Assassinate
var/datum/mind/target_mind = target_list[traitor]
if(issilicon(target_mind.current))
- var/datum/objective/destroy/destroy_objective = new
+ var/datum/objective/destroy/internal/destroy_objective = new
destroy_objective.owner = traitor
destroy_objective.target = target_mind
destroy_objective.update_explanation_text()
traitor.objectives += destroy_objective
else
- var/datum/objective/assassinate/kill_objective = new
+ var/datum/objective/assassinate/internal/kill_objective = new
kill_objective.owner = traitor
kill_objective.target = target_mind
kill_objective.update_explanation_text()
@@ -53,12 +233,17 @@
var/datum/objective/escape/escape_objective = new
escape_objective.owner = traitor
traitor.objectives += escape_objective
+ var/datum/internal_agent_state/state = new
+ state.owner=traitor
+ state.add_steal_targets_timer()
+ if(!issilicon(traitor.current))
+ give_pinpointer(traitor)
else
..() // Give them standard objectives.
return
-/datum/game_mode/traitor/double_agents/add_latejoin_traitor(datum/mind/character)
+/datum/game_mode/traitor/internal_affairs/add_latejoin_traitor(datum/mind/character)
check_potential_agents()
@@ -87,7 +272,7 @@
late_joining_list += character
return
-/datum/game_mode/traitor/double_agents/proc/check_potential_agents()
+/datum/game_mode/traitor/internal_affairs/proc/check_potential_agents()
for(var/M in late_joining_list)
if(istype(M, /datum/mind))
@@ -100,3 +285,22 @@
// If any check fails, remove them from our list
late_joining_list -= M
+
+
+/datum/game_mode/traitor/internal_affairs/greet_traitor(datum/mind/traitor)
+ var/crime = pick("distribution of contraband" , "unauthorized erotic action on duty", "embezzlement", "piloting under the influence", "dereliction of duty", "syndicate collaboration", "mutiny", "multiple homicides", "corporate espionage", "recieving bribes", "malpractice", "worship of prohbited life forms", "possession of profane texts", "murder", "arson", "insulting their manager", "grand theft", "conspiracy", "attempting to unionize", "vandalism", "gross incompetence")
+ to_chat(traitor.current, "You are the [traitor_name].")
+ to_chat(traitor.current, "Your target is suspected of [crime], and you have been tasked with eliminating them by any means necessary to avoid a costly and embarrassing public trial.")
+ to_chat(traitor.current, "While you have a license to kill, unneeded property damage or loss of employee life will lead to your contract being terminated.")
+ to_chat(traitor.current, "For the sake of plausible deniability, you have been equipped with an array of captured Syndicate weaponry available via uplink.")
+ to_chat(traitor.current, "Finally, watch your back. Your target has friends in high places, and intel suggests someone may have taken out a contract of their own to protect them.")
+ traitor.announce_objectives()
+
+
+
+/datum/game_mode/traitor/internal_affairs/give_codewords(mob/living/traitor_mob)
+ return
+
+#undef PINPOINTER_EXTRA_RANDOM_RANGE
+#undef PINPOINTER_MINIMUM_RANGE
+#undef PINPOINTER_PING_TIME
diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm
index c26945801b..90a953c446 100644
--- a/code/game/gamemodes/traitor/traitor.dm
+++ b/code/game/gamemodes/traitor/traitor.dm
@@ -1,5 +1,6 @@
/datum/game_mode
var/traitor_name = "traitor"
+ var/employer = "The Syndicate"
var/list/datum/mind/traitors = list()
var/datum/mind/exchange_red
@@ -213,7 +214,7 @@
..()
return//Traitors will be checked as part of check_extra_completion. Leaving this here as a reminder.
-/proc/give_codewords(mob/living/traitor_mob)
+/datum/game_mode/proc/give_codewords(mob/living/traitor_mob)
to_chat(traitor_mob, "The Syndicate provided you with the following information on how to identify their agents:")
to_chat(traitor_mob, "Code Phrase: [GLOB.syndicate_code_phrase]")
to_chat(traitor_mob, "Code Response: [GLOB.syndicate_code_response]")
@@ -260,10 +261,10 @@
for(var/datum/objective/objective in traitor.objectives)
if(objective.check_completion())
objectives += "
Objective #[count]: [objective.explanation_text] Success!"
- feedback_add_details("traitor_objective","[objective.type]|SUCCESS")
+ SSblackbox.add_details("traitor_objective","[objective.type]|SUCCESS")
else
objectives += "
Objective #[count]: [objective.explanation_text] Fail."
- feedback_add_details("traitor_objective","[objective.type]|FAIL")
+ SSblackbox.add_details("traitor_objective","[objective.type]|FAIL")
traitorwin = 0
count++
@@ -283,10 +284,10 @@
if(traitorwin)
text += "
The [special_role_text] was successful!"
- feedback_add_details("traitor_success","SUCCESS")
+ SSblackbox.add_details("traitor_success","SUCCESS")
else
text += "
The [special_role_text] has failed!"
- feedback_add_details("traitor_success","FAIL")
+ SSblackbox.add_details("traitor_success","FAIL")
text += "
"
@@ -335,7 +336,7 @@
uplink_loc = R
if (!uplink_loc)
- to_chat(traitor_mob, "Unfortunately, the Syndicate wasn't able to get you an Uplink.")
+ to_chat(traitor_mob, "Unfortunately, [employer] wasn't able to get you an Uplink.")
. = 0
else
var/obj/item/device/uplink/U = new(uplink_loc)
@@ -345,19 +346,19 @@
if(uplink_loc == R)
R.traitor_frequency = sanitize_frequency(rand(MIN_FREQ, MAX_FREQ))
- to_chat(traitor_mob, "The Syndicate have cunningly disguised a Syndicate Uplink as your [R.name]. Simply dial the frequency [format_frequency(R.traitor_frequency)] to unlock its hidden features.")
+ to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [R.name]. Simply dial the frequency [format_frequency(R.traitor_frequency)] to unlock its hidden features.")
traitor_mob.mind.store_memory("Radio Frequency: [format_frequency(R.traitor_frequency)] ([R.name]).")
else if(uplink_loc == PDA)
PDA.lock_code = "[rand(100,999)] [pick("Alpha","Bravo","Charlie","Delta","Echo","Foxtrot","Golf","Hotel","India","Juliet","Kilo","Lima","Mike","November","Oscar","Papa","Quebec","Romeo","Sierra","Tango","Uniform","Victor","Whiskey","X-ray","Yankee","Zulu")]"
- to_chat(traitor_mob, "The Syndicate have cunningly disguised a Syndicate Uplink as your [PDA.name]. Simply enter the code \"[PDA.lock_code]\" into the ringtone select to unlock its hidden features.")
+ to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [PDA.name]. Simply enter the code \"[PDA.lock_code]\" into the ringtone select to unlock its hidden features.")
traitor_mob.mind.store_memory("Uplink Passcode: [PDA.lock_code] ([PDA.name]).")
else if(uplink_loc == P)
P.traitor_unlock_degrees = rand(1, 360)
- to_chat(traitor_mob, "The Syndicate have cunningly disguised a Syndicate Uplink as your [P.name]. Simply twist the top of the pen [P.traitor_unlock_degrees] from its starting position to unlock its hidden features.")
+ to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [P.name]. Simply twist the top of the pen [P.traitor_unlock_degrees] from its starting position to unlock its hidden features.")
traitor_mob.mind.store_memory("Uplink Degrees: [P.traitor_unlock_degrees] ([P.name]).")
if(!safety) // If they are not a rev. Can be added on to.
diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm
index c925a5cbd5..137514d1e3 100644
--- a/code/game/gamemodes/wizard/artefact.dm
+++ b/code/game/gamemodes/wizard/artefact.dm
@@ -471,7 +471,7 @@
else if(istype(I,/obj/item/weapon/bikehorn))
to_chat(target, "HONK")
target << 'sound/items/AirHorn.ogg'
- target.adjustEarDamage(0,3)
+ target.adjustEarDamage(0,3)
GiveHint(target)
cooldown = world.time +cooldown_time
return
@@ -630,6 +630,6 @@
duration = 40
pixel_x = 500
-/obj/effect/overlay/temp/tornado/New(loc)
- ..()
+/obj/effect/overlay/temp/tornado/Initialize()
+ . = ..()
animate(src, pixel_x = -500, time = 40)
diff --git a/code/game/gamemodes/wizard/raginmages.dm b/code/game/gamemodes/wizard/raginmages.dm
index 01eb8a52c4..1eb51a95b4 100644
--- a/code/game/gamemodes/wizard/raginmages.dm
+++ b/code/game/gamemodes/wizard/raginmages.dm
@@ -132,7 +132,7 @@
/datum/game_mode/wizard/raginmages/declare_completion()
if(finished)
- feedback_set_details("round_end_result","loss - wizard killed")
+ SSblackbox.set_details("round_end_result","loss - wizard killed")
to_chat(world, "The crew has managed to hold off the wizard attack! The Space Wizards Federation has been taught a lesson they will not soon forget!")
..(1)
diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm
index 7ccf7a6b7e..09412a79ac 100644
--- a/code/game/gamemodes/wizard/soulstone.dm
+++ b/code/game/gamemodes/wizard/soulstone.dm
@@ -54,7 +54,7 @@
if(!ishuman(M))//If target is not a human.
return ..()
if(iscultist(M))
- to_chat(user, "\"Come now, do not capture your fellow's soul.\"")
+ to_chat(user, "\"Come now, do not capture your bretheren's soul.\"")
return
add_logs(user, M, "captured [M.name]'s soul", src)
@@ -132,11 +132,11 @@
if("VICTIM")
var/mob/living/carbon/human/T = target
- if(SSticker.mode.name == "cult" && T.mind == SSticker.mode:sacrifice_target)
+ if(is_sacrifice_target(T.mind))
if(iscultist(user))
to_chat(user, "\"This soul is mine. SACRIFICE THEM!\"")
else
- to_chat(user, "The soulstone doesn't work for no apparent reason.")
+ to_chat(user, "The soulstone seems to reject this soul.")
return 0
if(contents.len)
to_chat(user, "Capture failed!: The soulstone is full! Free an existing soul to make room.")
@@ -188,7 +188,10 @@
else
makeNewConstruct(/mob/living/simple_animal/hostile/construct/builder/noncult, A, user, 0, T.loc)
-
+ for(var/datum/mind/B in SSticker.mode.cult)
+ if(B == A.mind)
+ SSticker.mode.cult -= A.mind
+ SSticker.mode.update_cult_icons_removed(A.mind)
qdel(T)
user.drop_item()
qdel(src)
@@ -200,6 +203,9 @@
var/mob/living/simple_animal/hostile/construct/newstruct = new ctype((loc_override) ? (loc_override) : (get_turf(target)))
if(stoner)
newstruct.faction |= "\ref[stoner]"
+ newstruct.master = stoner
+ var/datum/action/innate/seek_master/SM = new()
+ SM.Grant(newstruct)
newstruct.key = target.key
if(newstruct.mind && ((stoner && iscultist(stoner)) || cultoverride) && SSticker && SSticker.mode)
SSticker.mode.add_cultist(newstruct.mind, 0)
@@ -207,6 +213,9 @@
to_chat(newstruct, "You are still bound to serve the cult[stoner ? " and [stoner]":""], follow their orders and help them complete their goals at all costs.")
else if(stoner)
to_chat(newstruct, "You are still bound to serve your creator, [stoner], follow their orders and help them complete their goals at all costs.")
+ newstruct.throw_alert("bloodsense", /obj/screen/alert/bloodsense)
+ var/obj/screen/alert/bloodsense/BS = newstruct.alerts["bloodsense"]
+ BS.Cviewer = newstruct
newstruct.cancel_camera()
@@ -244,7 +253,7 @@
break
if(!chosen_ghost) //Failing that, we grab a ghost
- var/list/consenting_candidates = pollCandidates("Would you like to play as a Shade?", "Cultist", null, ROLE_CULTIST, poll_time = 50)
+ var/list/consenting_candidates = pollGhostCandidates("Would you like to play as a Shade?", "Cultist", null, ROLE_CULTIST, poll_time = 50)
if(consenting_candidates.len)
chosen_ghost = pick(consenting_candidates)
if(!T)
diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm
index e9e1a95e73..06e241896f 100644
--- a/code/game/gamemodes/wizard/spellbook.dm
+++ b/code/game/gamemodes/wizard/spellbook.dm
@@ -57,10 +57,10 @@
aspell.name = "Instant [aspell.name]"
if(aspell.spell_level >= aspell.level_max)
to_chat(user, "This spell cannot be strengthened any further.")
- feedback_add_details("wizard_spell_improved", "[name]|[aspell.level]")
+ SSblackbox.add_details("wizard_spell_improved", "[name]|[aspell.level]")
return 1
//No same spell found - just learn it
- feedback_add_details("wizard_spell_learned", name)
+ SSblackbox.add_details("wizard_spell_learned", name)
user.mind.AddSpell(S)
to_chat(user, "You have learned [S.name].")
return 1
@@ -253,7 +253,7 @@
/datum/spellbook_entry/the_traps
name = "The Traps!"
spell_type = /obj/effect/proc_holder/spell/aoe_turf/conjure/the_traps
- category = "Offensive"
+ category = "Defensive"
cost = 1
@@ -266,7 +266,7 @@
/datum/spellbook_entry/item/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book)
new item_path(get_turf(user))
- feedback_add_details("wizard_spell_learned", name)
+ SSblackbox.add_details("wizard_spell_learned", name)
return 1
/datum/spellbook_entry/item/GetInfo()
@@ -465,7 +465,7 @@
return TRUE
/datum/spellbook_entry/summon/ghosts/Buy(mob/living/carbon/human/user, obj/item/weapon/spellbook/book)
- feedback_add_details("wizard_spell_learned", name)
+ SSblackbox.add_details("wizard_spell_learned", name)
new /datum/round_event/wizard/ghost()
active = TRUE
to_chat(user, "You have cast summon ghosts!")
@@ -482,7 +482,7 @@
return (SSticker.mode.name != "ragin' mages" && !config.no_summon_guns)
/datum/spellbook_entry/summon/guns/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book)
- feedback_add_details("wizard_spell_learned", name)
+ SSblackbox.add_details("wizard_spell_learned", name)
rightandwrong(0, user, 25)
active = 1
playsound(get_turf(user), 'sound/magic/CastSummon.ogg', 50, 1)
@@ -499,7 +499,7 @@
return (SSticker.mode.name != "ragin' mages" && !config.no_summon_magic)
/datum/spellbook_entry/summon/magic/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book)
- feedback_add_details("wizard_spell_learned", name)
+ SSblackbox.add_details("wizard_spell_learned", name)
rightandwrong(1, user, 25)
active = 1
playsound(get_turf(user), 'sound/magic/CastSummon.ogg', 50, 1)
@@ -517,7 +517,7 @@
return (SSticker.mode.name != "ragin' mages" && !config.no_summon_events)
/datum/spellbook_entry/summon/events/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book)
- feedback_add_details("wizard_spell_learned", name)
+ SSblackbox.add_details("wizard_spell_learned", name)
summonevents()
times++
playsound(get_turf(user), 'sound/magic/CastSummon.ogg', 50, 1)
@@ -778,6 +778,9 @@
icon_state ="booksmoke"
desc = "This book is overflowing with the dank arts."
+/obj/item/weapon/spellbook/oneuse/smoke/lesser //Chaplain smoke book
+ spell = /obj/effect/proc_holder/spell/targeted/smoke/lesser
+
/obj/item/weapon/spellbook/oneuse/smoke/recoil(mob/user)
..()
to_chat(user,"Your stomach rumbles...")
@@ -786,6 +789,7 @@
if(user.nutrition <= 0)
user.nutrition = 0
+
/obj/item/weapon/spellbook/oneuse/blind
spell = /obj/effect/proc_holder/spell/targeted/trigger/blind
spellname = "blind"
diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm
index 34d148f03c..35bccb692a 100644
--- a/code/game/gamemodes/wizard/wizard.dm
+++ b/code/game/gamemodes/wizard/wizard.dm
@@ -176,7 +176,7 @@
/datum/game_mode/wizard/declare_completion()
if(finished)
- feedback_set_details("round_end_result","loss - wizard killed")
+ SSblackbox.set_details("round_end_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
@@ -208,19 +208,19 @@
for(var/datum/objective/objective in wizard.objectives)
if(objective.check_completion())
text += "
Objective #[count]: [objective.explanation_text] Success!"
- feedback_add_details("wizard_objective","[objective.type]|SUCCESS")
+ SSblackbox.add_details("wizard_objective","[objective.type]|SUCCESS")
else
text += "
Objective #[count]: [objective.explanation_text] Fail."
- feedback_add_details("wizard_objective","[objective.type]|FAIL")
+ SSblackbox.add_details("wizard_objective","[objective.type]|FAIL")
wizardwin = 0
count++
if(wizard.current && wizard.current.stat!=2 && wizardwin)
text += "
The wizard was successful!"
- feedback_add_details("wizard_success","SUCCESS")
+ SSblackbox.add_details("wizard_success","SUCCESS")
else
text += "
The wizard has failed!"
- feedback_add_details("wizard_success","FAIL")
+ SSblackbox.add_details("wizard_success","FAIL")
if(wizard.spell_list.len>0)
text += "
[wizard.name] used the following spells: "
var/i = 1
diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm
index 5dc8ea3f20..d45859a371 100644
--- a/code/game/machinery/Sleeper.dm
+++ b/code/game/machinery/Sleeper.dm
@@ -79,7 +79,8 @@
/obj/machinery/sleeper/close_machine(mob/user)
if((isnull(user) || istype(user)) && state_open && !panel_open)
..(user)
- if(occupant && occupant.stat != DEAD)
+ var/mob/living/mob_occupant = occupant
+ if(mob_occupant && mob_occupant.stat != DEAD)
to_chat(occupant, "You feel cool air surround you. You go numb as your senses turn inward.")
/obj/machinery/sleeper/emp_act(severity)
@@ -128,27 +129,30 @@
data["chems"] += list(list("name" = R.name, "id" = R.id, "allowed" = chem_allowed(chem)))
data["occupant"] = list()
- if(occupant)
- data["occupant"]["name"] = occupant.name
- data["occupant"]["stat"] = occupant.stat
- data["occupant"]["health"] = occupant.health
- data["occupant"]["maxHealth"] = occupant.maxHealth
+ var/mob/living/mob_occupant = occupant
+ if(mob_occupant)
+ data["occupant"]["name"] = mob_occupant.name
+ data["occupant"]["stat"] = mob_occupant.stat
+ data["occupant"]["health"] = mob_occupant.health
+ data["occupant"]["maxHealth"] = mob_occupant.maxHealth
data["occupant"]["minHealth"] = HEALTH_THRESHOLD_DEAD
- data["occupant"]["bruteLoss"] = occupant.getBruteLoss()
- data["occupant"]["oxyLoss"] = occupant.getOxyLoss()
- data["occupant"]["toxLoss"] = occupant.getToxLoss()
- data["occupant"]["fireLoss"] = occupant.getFireLoss()
- data["occupant"]["cloneLoss"] = occupant.getCloneLoss()
- data["occupant"]["brainLoss"] = occupant.getBrainLoss()
+ data["occupant"]["bruteLoss"] = mob_occupant.getBruteLoss()
+ data["occupant"]["oxyLoss"] = mob_occupant.getOxyLoss()
+ data["occupant"]["toxLoss"] = mob_occupant.getToxLoss()
+ data["occupant"]["fireLoss"] = mob_occupant.getFireLoss()
+ data["occupant"]["cloneLoss"] = mob_occupant.getCloneLoss()
+ data["occupant"]["brainLoss"] = mob_occupant.getBrainLoss()
data["occupant"]["reagents"] = list()
if(occupant.reagents.reagent_list.len)
- for(var/datum/reagent/R in occupant.reagents.reagent_list)
+ for(var/datum/reagent/R in mob_occupant.reagents.reagent_list)
data["occupant"]["reagents"] += list(list("name" = R.name, "volume" = R.volume))
return data
/obj/machinery/sleeper/ui_act(action, params)
if(..())
return
+ var/mob/living/mob_occupant = occupant
+
switch(action)
if("door")
if(state_open)
@@ -158,9 +162,9 @@
. = TRUE
if("inject")
var/chem = params["chem"]
- if(!is_operational() || !occupant)
+ if(!is_operational() || !mob_occupant)
return
- if(occupant.health < min_health && chem != "epinephrine")
+ if(mob_occupant.health < min_health && chem != "epinephrine")
return
if(inject_chem(chem))
. = TRUE
@@ -177,10 +181,11 @@
return TRUE
/obj/machinery/sleeper/proc/chem_allowed(chem)
- if(!occupant)
+ var/mob/living/mob_occupant = occupant
+ if(!mob_occupant)
return
- var/amount = occupant.reagents.get_reagent_amount(chem) + 10 <= 20 * efficiency
- var/occ_health = occupant.health > min_health || chem == "epinephrine"
+ var/amount = mob_occupant.reagents.get_reagent_amount(chem) + 10 <= 20 * efficiency
+ var/occ_health = mob_occupant.health > min_health || chem == "epinephrine"
return amount && occ_health
/obj/machinery/sleeper/proc/reset_chem_buttons()
diff --git a/code/game/machinery/bank_machine.dm b/code/game/machinery/bank_machine.dm
index 35f7babe1e..ace9b543d1 100644
--- a/code/game/machinery/bank_machine.dm
+++ b/code/game/machinery/bank_machine.dm
@@ -4,7 +4,21 @@
icon = 'goon/icons/obj/goon_terminals.dmi'
idle_power_usage = 100
var/siphoning = FALSE
- var/last_warning = 0
+ var/next_warning = 0
+ var/obj/item/device/radio/radio
+ var/radio_channel = "Common"
+ var/minimum_time_between_warnings = 400
+
+/obj/machinery/computer/bank_machine/Initialize(mapload)
+ ..()
+ radio = new(src)
+ radio.subspace_transmission = TRUE
+ radio.canhear_range = 0
+ radio.recalculateChannels()
+
+/obj/machinery/computer/bank_machine/Destroy()
+ QDEL_NULL(radio)
+ . = ..()
/obj/machinery/computer/bank_machine/attackby(obj/item/I, mob/user)
var/value = 0
@@ -32,18 +46,17 @@
say("Station funds depleted. Halting siphon.")
siphoning = FALSE
else
- var/obj/item/stack/spacecash/c200/on_turf = locate() in src.loc
- if(on_turf && on_turf.amount < on_turf.max_amount)
- on_turf.amount++
- else
- new /obj/item/stack/spacecash/c200(get_turf(src))
+ new /obj/item/stack/spacecash/c200(get_turf(src)) // will autostack
playsound(src.loc, 'sound/items/poster_being_created.ogg', 100, 1)
SSshuttle.points -= 200
- if(last_warning < world.time && prob(15))
+ if(next_warning < world.time && prob(15))
var/area/A = get_area(loc)
- minor_announce("Unauthorized credit withdrawal underway in [A.map_name]." , "Network Breach", TRUE)
- last_warning = world.time + 400
+ var/message = "Unauthorized credit withdrawal underway in [A.map_name]!!"
+ radio.talk_into(src, message, radio_channel, get_spans())
+ next_warning = world.time + minimum_time_between_warnings
+/obj/machinery/computer/bank_machine/get_spans()
+ . = ..() | SPAN_ROBOT
/obj/machinery/computer/bank_machine/attack_hand(mob/user)
if(..())
@@ -67,8 +80,8 @@
if(..())
return
if(href_list["siphon"])
- say("Siphon of station credits has begun!")
+ say("Siphon of station credits has begun!")
siphoning = TRUE
if(href_list["halt"])
- say("Station credit withdrawal halted.")
- siphoning = FALSE
\ No newline at end of file
+ say("Station credit withdrawal halted.")
+ siphoning = FALSE
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index eeef80ca5b..0e5b78af7c 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -121,10 +121,12 @@
/obj/machinery/clonepod/examine(mob/user)
..()
+ var/mob/living/mob_occupant = occupant
if(mess)
to_chat(user, "It's filled with blood and viscera. You swear you can see it moving...")
- if (is_operational() && (!isnull(occupant)) && (occupant.stat != DEAD))
- to_chat(user, "Current clone cycle is [round(get_completion())]% complete.")
+ if(is_operational() && mob_occupant)
+ if(mob_occupant.stat != DEAD)
+ to_chat(user, "Current clone cycle is [round(get_completion())]% complete.")
/obj/machinery/clonepod/return_air()
// We want to simulate the clone not being in contact with
@@ -136,7 +138,10 @@
return GM
/obj/machinery/clonepod/proc/get_completion()
- . = (100 * ((occupant.health + 100) / (heal_level + 100)))
+ . = FALSE
+ var/mob/living/mob_occupant = occupant
+ if(mob_occupant)
+ . = (100 * ((mob_occupant.health + 100) / (heal_level + 100)))
/obj/machinery/clonepod/attack_ai(mob/user)
return examine(user)
@@ -172,11 +177,11 @@
var/mob/living/carbon/human/H = new /mob/living/carbon/human(src)
- if(clonemind.changeling)
- var/obj/item/organ/brain/B = H.getorganslot("brain")
- B.vital = FALSE
- B.decoy_override = TRUE
-
+ if(clonemind.changeling)
+ var/obj/item/organ/brain/B = H.getorganslot("brain")
+ B.vital = FALSE
+ B.decoy_override = TRUE
+
H.hardset_dna(ui, se, H.real_name, null, mrace, features)
if(efficiency > 2)
@@ -204,13 +209,13 @@
clonemind.transfer_to(H)
- if(grab_ghost_when == CLONER_FRESH_CLONE)
- H.grab_ghost()
- to_chat(H, "Consciousness slowly creeps over you as your body regenerates.
So this is what cloning feels like?")
+ if(grab_ghost_when == CLONER_FRESH_CLONE)
+ H.grab_ghost()
+ to_chat(H, "Consciousness slowly creeps over you as your body regenerates.
So this is what cloning feels like?")
if(grab_ghost_when == CLONER_MATURE_CLONE)
- H.ghostize(TRUE) //Only does anything if they were still in their old body and not already a ghost
- to_chat(H.get_ghost(TRUE), "Your body is beginning to regenerate in a cloning pod. You will become conscious when it is complete.")
+ H.ghostize(TRUE) //Only does anything if they were still in their old body and not already a ghost
+ to_chat(H.get_ghost(TRUE), "Your body is beginning to regenerate in a cloning pod. You will become conscious when it is complete.")
if(H)
H.faction |= factions
@@ -223,25 +228,26 @@
//Grow clones to maturity then kick them out. FREELOADERS
/obj/machinery/clonepod/process()
+ var/mob/living/mob_occupant = occupant
if(!is_operational()) //Autoeject if power is lost
- if (occupant)
+ if(mob_occupant)
go_out()
connected_message("Clone Ejected: Loss of power.")
- else if((occupant) && (occupant.loc == src))
- if((occupant.stat == DEAD) || (occupant.suiciding) || occupant.hellbound) //Autoeject corpses and suiciding dudes.
+ else if(mob_occupant && (mob_occupant.loc == src))
+ if((mob_occupant.stat == DEAD) || (mob_occupant.suiciding) || mob_occupant.hellbound) //Autoeject corpses and suiciding dudes.
connected_message("Clone Rejected: Deceased.")
- SPEAK("The cloning of [occupant.real_name] has been \
+ SPEAK("The cloning of [mob_occupant.real_name] has been \
aborted due to unrecoverable tissue failure.")
go_out()
- else if(occupant.cloneloss > (100 - heal_level))
- occupant.Paralyse(4)
+ else if(mob_occupant.cloneloss > (100 - heal_level))
+ mob_occupant.Paralyse(4)
//Slowly get that clone healed and finished.
- occupant.adjustCloneLoss(-((speed_coeff/2) * config.damage_multiplier))
- var/progress = CLONE_INITIAL_DAMAGE - occupant.getCloneLoss()
+ mob_occupant.adjustCloneLoss(-((speed_coeff/2) * config.damage_multiplier))
+ var/progress = CLONE_INITIAL_DAMAGE - mob_occupant.getCloneLoss()
// To avoid the default cloner making incomplete clones
progress += (100 - MINIMUM_HEAL_LEVEL)
var/milestone = CLONE_INITIAL_DAMAGE / flesh_number
@@ -252,24 +258,24 @@
var/obj/item/I = pick_n_take(unattached_flesh)
if(isorgan(I))
var/obj/item/organ/O = I
- O.Insert(occupant)
+ O.Insert(mob_occupant)
else if(isbodypart(I))
var/obj/item/bodypart/BP = I
- BP.attach_limb(occupant)
+ BP.attach_limb(mob_occupant)
//Premature clones may have brain damage.
- occupant.adjustBrainLoss(-((speed_coeff/2) * config.damage_multiplier))
+ mob_occupant.adjustBrainLoss(-((speed_coeff/2) * config.damage_multiplier))
check_brine()
use_power(7500) //This might need tweaking.
- else if((occupant.cloneloss <= (100 - heal_level)))
+ else if((mob_occupant.cloneloss <= (100 - heal_level)))
connected_message("Cloning Process Complete.")
- SPEAK("The cloning cycle of [occupant.real_name] is complete.")
+ SPEAK("The cloning cycle of [mob_occupant.real_name] is complete.")
go_out()
- else if ((!occupant) || (occupant.loc != src))
+ else if (!mob_occupant || mob_occupant.loc != src)
occupant = null
if (!mess && !panel_open)
icon_state = "pod_0"
@@ -305,16 +311,19 @@
to_chat(user, "-% Successfully stored \ref[P.buffer] [P.buffer.name] in buffer %-")
return
+ var/mob/living/mob_occupant = occupant
if(W.GetID())
if(!check_access(W))
to_chat(user, "Access Denied.")
return
- if(!(occupant || mess))
+ if(!(mob_occupant || mess))
to_chat(user, "Error: Pod has no occupant.")
return
else
connected_message("Authorized Ejection")
- SPEAK("An authorized ejection of [occupant.real_name] has occurred.")
+
+ SPEAK("An authorized ejection of [clonemind.name] has occurred.")
+
to_chat(user, "You force an emergency ejection. ")
go_out()
else
@@ -339,6 +348,7 @@
/obj/machinery/clonepod/proc/go_out()
countdown.stop()
+ var/mob/living/mob_occupant = occupant
if(mess) //Clean that mess and dump those gibs!
mess = FALSE
@@ -347,44 +357,54 @@
icon_state = "pod_0"
return
- if(!occupant)
+ if(!mob_occupant)
return
+
if(grab_ghost_when == CLONER_MATURE_CLONE)
- occupant.grab_ghost()
+ mob_occupant.grab_ghost()
to_chat(occupant, "There is a bright flash!
You feel like a new being.")
- occupant.flash_act()
+ mob_occupant.flash_act()
var/turf/T = get_turf(src)
occupant.forceMove(T)
icon_state = "pod_0"
- occupant.domutcheck(1) //Waiting until they're out before possible monkeyizing. The 1 argument forces powers to manifest.
+ mob_occupant.domutcheck(1) //Waiting until they're out before possible monkeyizing. The 1 argument forces powers to manifest.
+
occupant = null
/obj/machinery/clonepod/proc/malfunction()
- if(occupant)
+ var/mob/living/mob_occupant = occupant
+ if(mob_occupant)
connected_message("Critical Error!")
SPEAK("Critical error! Please contact a Thinktronic Systems \
technician, as your warranty may be affected.")
mess = TRUE
+
+ for(var/obj/item/O in unattached_flesh)
+ qdel(O)
+
icon_state = "pod_g"
- if(occupant.mind != clonemind)
- clonemind.transfer_to(occupant)
- occupant.grab_ghost() // We really just want to make you suffer.
- flash_color(occupant, flash_color="#960000", flash_time=100)
- to_chat(occupant, "Agony blazes across your consciousness as your body is torn apart.
Is this what dying is like? Yes it is.")
+ if(mob_occupant.mind != clonemind)
+ clonemind.transfer_to(mob_occupant)
+ mob_occupant.grab_ghost() // We really just want to make you suffer.
+ flash_color(mob_occupant, flash_color="#960000", flash_time=100)
+ to_chat(mob_occupant, "Agony blazes across your consciousness as your body is torn apart.
Is this what dying is like? Yes it is.")
playsound(src.loc, 'sound/machines/warning-buzzer.ogg', 50, 0)
- occupant << sound('sound/hallucinations/veryfar_noise.ogg',0,1,50)
- QDEL_IN(occupant, 40)
+ mob_occupant << sound('sound/hallucinations/veryfar_noise.ogg',0,1,50)
+ QDEL_IN(mob_occupant, 40)
/obj/machinery/clonepod/relaymove(mob/user)
if(user.stat == CONSCIOUS)
go_out()
/obj/machinery/clonepod/emp_act(severity)
- if(prob(100/(severity*efficiency)))
+
+ var/mob/living/mob_occupant = occupant
+ if(mob_occupant && prob(100/(severity*efficiency)))
connected_message(Gibberish("EMP-caused Accidental Ejection", 0))
- SPEAK(Gibberish("Exposure to electromagnetic fields has caused the ejection of [occupant.real_name] prematurely." ,0))
+ SPEAK(Gibberish("Exposure to electromagnetic fields has caused the ejection of [mob_occupant.real_name] prematurely." ,0))
+
go_out()
..()
diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm
index 514cf590af..643282ab4a 100644
--- a/code/game/machinery/computer/arcade.dm
+++ b/code/game/machinery/computer/arcade.dm
@@ -234,7 +234,7 @@
playsound(loc, 'sound/arcade/Win.ogg', 50, 1, extrarange = -3, falloff = 10)
if(emagged)
- feedback_inc("arcade_win_emagged")
+ SSblackbox.inc("arcade_win_emagged")
new /obj/effect/spawner/newbomb/timer/syndicate(loc)
new /obj/item/clothing/head/collectable/petehat(loc)
message_admins("[key_name_admin(usr)] has outbombed Cuban Pete and been awarded a bomb.")
@@ -242,7 +242,7 @@
Reset()
emagged = 0
else
- feedback_inc("arcade_win_normal")
+ SSblackbox.inc("arcade_win_normal")
prizevend()
else if (emagged && (turtle >= 4))
@@ -264,10 +264,10 @@
temp = "You have been drained! GAME OVER"
playsound(loc, 'sound/arcade/Lose.ogg', 50, 1, extrarange = -3, falloff = 10)
if(emagged)
- feedback_inc("arcade_loss_mana_emagged")
+ SSblackbox.inc("arcade_loss_mana_emagged")
usr.gib()
else
- feedback_inc("arcade_loss_mana_normal")
+ SSblackbox.inc("arcade_loss_mana_normal")
else if ((enemy_hp <= 10) && (enemy_mp > 4))
temp = "[enemy_name] heals for 4 health!"
@@ -286,10 +286,10 @@
temp = "You have been crushed! GAME OVER"
playsound(loc, 'sound/arcade/Lose.ogg', 50, 1, extrarange = -3, falloff = 10)
if(emagged)
- feedback_inc("arcade_loss_hp_emagged")
+ SSblackbox.inc("arcade_loss_hp_emagged")
usr.gib()
else
- feedback_inc("arcade_loss_hp_normal")
+ SSblackbox.inc("arcade_loss_hp_normal")
blocked = FALSE
return
diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm
index afa054428a..48819f91e2 100644
--- a/code/game/machinery/computer/cloning.dm
+++ b/code/game/machinery/computer/cloning.dm
@@ -186,21 +186,23 @@
// Scanner
if (!isnull(src.scanner))
+ var/mob/living/scanner_occupant = scanner.occupant
+
dat += "Scanner Functions
"
dat += ""
- if (!src.scanner.occupant)
+ if(!scanner_occupant)
dat += "Scanner Unoccupied"
else if(loading)
- dat += "[src.scanner.occupant] => Scanning..."
+ dat += "[scanner_occupant] => Scanning..."
else
- if (src.scanner.occupant.ckey != scantemp_ckey)
+ if(scanner_occupant.ckey != scantemp_ckey)
scantemp = "Ready to Scan"
- scantemp_ckey = src.scanner.occupant.ckey
- dat += "[src.scanner.occupant] => [scantemp]"
+ scantemp_ckey = scanner_occupant.ckey
+ dat += "[scanner_occupant] => [scantemp]"
dat += "
"
- if (src.scanner.occupant)
+ if(scanner_occupant)
dat += "Start Scan"
dat += "
[src.scanner.locked ? "Unlock Scanner" : "Lock Scanner"]"
else
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index 5af4f9c281..c9a448fc7b 100644
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -115,9 +115,9 @@
message_admins("[key_name_admin(usr)] has changed the security level to [get_security_level()].")
switch(GLOB.security_level)
if(SEC_LEVEL_GREEN)
- feedback_inc("alert_comms_green",1)
+ SSblackbox.inc("alert_comms_green",1)
if(SEC_LEVEL_BLUE)
- feedback_inc("alert_comms_blue",1)
+ SSblackbox.inc("alert_comms_blue",1)
tmp_alertlevel = 0
else
to_chat(usr, "You are not authorized to do this!")
@@ -176,7 +176,7 @@
SSshuttle.points -= S.credit_cost
minor_announce("[usr.name] has purchased [S.name] for [S.credit_cost] credits." , "Shuttle Purchase")
message_admins("[key_name_admin(usr)] purchased [S.name].")
- feedback_add_details("shuttle_purchase", S.name)
+ SSblackbox.add_details("shuttle_purchase", S.name)
else
to_chat(usr, "Something went wrong! The shuttle exchange system seems to be down.")
else
@@ -368,9 +368,9 @@
message_admins("[key_name_admin(usr)] has changed the security level to [get_security_level()].")
switch(GLOB.security_level)
if(SEC_LEVEL_GREEN)
- feedback_inc("alert_comms_green",1)
+ SSblackbox.inc("alert_comms_green",1)
if(SEC_LEVEL_BLUE)
- feedback_inc("alert_comms_blue",1)
+ SSblackbox.inc("alert_comms_blue",1)
tmp_alertlevel = 0
src.aistate = STATE_DEFAULT
if("ai-changeseclevel")
diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm
index 1d331051e6..e3b47873e6 100644
--- a/code/game/machinery/computer/crew.dm
+++ b/code/game/machinery/computer/crew.dm
@@ -56,7 +56,7 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
jobs["Bartender"] = 61
jobs["Cook"] = 62
jobs["Botanist"] = 63
- jobs["Librarian"] = 64
+ jobs["Curator"] = 64
jobs["Chaplain"] = 65
jobs["Clown"] = 66
jobs["Mime"] = 67
@@ -153,8 +153,8 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
U = H.w_uniform
// Are the suit sensors on?
- if (U.has_sensor && U.sensor_mode)
- pos = H.z == 0 || U.sensor_mode == 3 ? get_turf(H) : null
+ if ((U.has_sensor > 0) && U.sensor_mode)
+ pos = H.z == 0 || U.sensor_mode == SENSOR_COORDS ? get_turf(H) : null
// Special case: If the mob is inside an object confirm the z-level on turf level.
if (H.z == 0 && (!pos || pos.z != z)) continue
@@ -170,10 +170,10 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
assignment = ""
ijob = 80
- if (U.sensor_mode >= 1) life_status = (!H.stat ? "true" : "false")
+ if (U.sensor_mode >= SENSOR_LIVING) life_status = (!H.stat ? "true" : "false")
else life_status = null
- if (U.sensor_mode >= 2)
+ if (U.sensor_mode >= SENSOR_VITALS)
dam1 = round(H.getOxyLoss(),1)
dam2 = round(H.getToxLoss(),1)
dam3 = round(H.getFireLoss(),1)
@@ -184,7 +184,7 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
dam3 = null
dam4 = null
- if (U.sensor_mode >= 3)
+ if (U.sensor_mode >= SENSOR_COORDS)
if (!pos) pos = get_turf(H)
var/area/player_area = get_area(H)
diff --git a/code/game/machinery/computer/message.dm b/code/game/machinery/computer/message.dm
index c97f487718..7f8eb32648 100644
--- a/code/game/machinery/computer/message.dm
+++ b/code/game/machinery/computer/message.dm
@@ -455,14 +455,16 @@
name = "monitor decryption key"
var/obj/machinery/message_server/server = null
-/obj/item/weapon/paper/monitorkey/New()
+/obj/item/weapon/paper/monitorkey/Initialize()
..()
- spawn(10)
- if(GLOB.message_servers)
- for(var/obj/machinery/message_server/server in GLOB.message_servers)
- if(!isnull(server))
- if(!isnull(server.decryptkey))
- info = "Daily Key Reset
The new message monitor key is '[server.decryptkey]'.
Please keep this a secret and away from the clown.
If necessary, change the password to a more secure one."
- info_links = info
- add_overlay("paper_words")
- break
+ return INITIALIZE_HINT_LATELOAD
+
+/obj/item/weapon/paper/monitorkey/LateInitialize()
+ if(GLOB.message_servers)
+ for(var/obj/machinery/message_server/server in GLOB.message_servers)
+ if(!isnull(server))
+ if(!isnull(server.decryptkey))
+ info = "Daily Key Reset
The new message monitor key is '[server.decryptkey]'.
Please keep this a secret and away from the clown.
If necessary, change the password to a more secure one."
+ info_links = info
+ add_overlay("paper_words")
+ break
diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm
index 3772dacbdb..0c2894f056 100644
--- a/code/game/machinery/computer/robot.dm
+++ b/code/game/machinery/computer/robot.dm
@@ -113,7 +113,8 @@
to_chat(R.connected_ai, "
ALERT - Cyborg detonation detected: [R.name]
")
R.ResetSecurityCodes()
else
- message_admins("[key_name_admin(usr)] (FLW) detonated [key_name(R, R.client)](JMP)!")
+ var/turf/T = get_turf(R)
+ message_admins("[ADMIN_LOOKUPFLW(usr)] detonated [key_name(R, R.client)][ADMIN_JMP(T)]!")
log_game("\[key_name(usr)] detonated [key_name(R)]!")
if(R.connected_ai)
to_chat(R.connected_ai, "
ALERT - Cyborg detonation detected: [R.name]
")
@@ -127,7 +128,7 @@
if(can_control(usr, R))
var/choice = input("Are you certain you wish to [R.canmove ? "lock down" : "release"] [R.name]?") in list("Confirm", "Abort")
if(choice == "Confirm" && can_control(usr, R) && !..())
- message_admins("[key_name_admin(usr)] (FLW) [R.canmove ? "locked down" : "released"] [key_name(R, R.client)](FLW)!")
+ message_admins("[ADMIN_LOOKUPFLW(usr)] [R.canmove ? "locked down" : "released"] [key_name(R, R.client)][ADMIN_LOOKUPFLW(R)]!")
log_game("[key_name(usr)] [R.canmove ? "locked down" : "released"] [key_name(R)]!")
R.SetLockdown(!R.lockcharge)
to_chat(R, "[!R.lockcharge ? "Your lockdown has been lifted!" : "You have been locked down!"]")
diff --git a/code/game/machinery/computer/telecrystalconsoles.dm b/code/game/machinery/computer/telecrystalconsoles.dm
index 4bb872b347..fd026e4686 100644
--- a/code/game/machinery/computer/telecrystalconsoles.dm
+++ b/code/game/machinery/computer/telecrystalconsoles.dm
@@ -58,7 +58,12 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E
/obj/machinery/computer/telecrystals/uplinker/proc/donateTC(amt, addLog = 1)
if(uplinkholder && linkedboss)
- if(amt <= uplinkholder.hidden_uplink.telecrystals)
+ if(amt < 0)
+ linkedboss.storedcrystals += uplinkholder.hidden_uplink.telecrystals
+ if(addLog)
+ linkedboss.logTransfer("[src] donated [uplinkholder.hidden_uplink.telecrystals] telecrystals to [linkedboss].")
+ uplinkholder.hidden_uplink.telecrystals = 0
+ else if(amt <= uplinkholder.hidden_uplink.telecrystals)
uplinkholder.hidden_uplink.telecrystals -= amt
linkedboss.storedcrystals += amt
if(addLog)
@@ -66,7 +71,12 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E
/obj/machinery/computer/telecrystals/uplinker/proc/giveTC(amt, addLog = 1)
if(uplinkholder && linkedboss)
- if(amt <= linkedboss.storedcrystals)
+ if(amt < 0)
+ uplinkholder.hidden_uplink.telecrystals += linkedboss.storedcrystals
+ if(addLog)
+ linkedboss.logTransfer("[src] received [linkedboss.storedcrystals] telecrystals from [linkedboss].")
+ linkedboss.storedcrystals = 0
+ else if(amt <= linkedboss.storedcrystals)
uplinkholder.hidden_uplink.telecrystals += amt
linkedboss.storedcrystals -= amt
if(addLog)
@@ -89,7 +99,7 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E
if(uplinkholder)
dat += "[uplinkholder.hidden_uplink.telecrystals] telecrystals remain in this uplink.
"
if(linkedboss)
- dat += "Donate TC: 1 | 5"
+ dat += "Donate TC: 1 | 5 | All"
dat += "
Eject Uplink"
@@ -103,11 +113,9 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E
if(..())
return
- if(href_list["donate1"])
- donateTC(1)
-
- if(href_list["donate5"])
- donateTC(5)
+ if(href_list["donate"])
+ var/tcamt = text2num(href_list["donate"])
+ donateTC(tcamt)
if(href_list["eject"])
ejectuplink()
@@ -162,7 +170,7 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E
var/dat = ""
dat += "Scan for TC stations.
"
- dat += "This [src] has [storedcrystals] telecrystals available for distribution.
"
+ dat += "[storedcrystals] telecrystals are available for distribution.
"
dat += "
"
@@ -171,10 +179,10 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E
if(A.uplinkholder)
dat += "[A.uplinkholder.hidden_uplink.telecrystals] telecrystals."
if(storedcrystals)
- dat+= "
Add TC: 1 | 5"
+ dat+= "
Add TC: 1 | 5 | 10 | All"
dat += "
"
- if(TCstations.len)
+ if(TCstations.len && storedcrystals)
dat += "
Evenly distribute remaining TC.
"
@@ -195,13 +203,11 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E
if(href_list["scan"])
scanUplinkers()
- if(href_list["give1"])
- var/obj/machinery/computer/telecrystals/uplinker/A = locate(href_list["give1"])
- A.giveTC(1)
-
- if(href_list["give5"])
- var/obj/machinery/computer/telecrystals/uplinker/A = locate(href_list["give5"])
- A.giveTC(5)
+ if(href_list["give"])
+ var/tcamt = text2num(href_list["give"])
+ if(TCstations.len) // sanity
+ var/obj/machinery/computer/telecrystals/uplinker/A = locate(href_list["target"]) in TCstations
+ A.giveTC(tcamt)
if(href_list["distrib"])
var/sanity = 0
diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm
index 8c935419a5..cf18a99875 100644
--- a/code/game/machinery/dance_machine.dm
+++ b/code/game/machinery/dance_machine.dm
@@ -13,8 +13,6 @@
var/list/rangers = list()
var/charge = 35
var/stop = 0
- var/list/available = list()
- var/list/select_name = list()
var/list/spotlights = list()
var/list/sparkles = list()
var/static/list/songs = list(
@@ -30,6 +28,7 @@
var/song_path = null
var/song_length = 0
var/song_beat = 0
+ var/GBP_required = 0
/datum/track/New(name, path, length, beat)
song_name = name
@@ -37,6 +36,19 @@
song_length = length
song_beat = beat
+/obj/machinery/disco/proc/add_track(file, name, length, beat)
+ var/sound/S = file
+ if(!istype(S))
+ return
+ if(!name)
+ name = "[file]"
+ if(!beat)
+ beat = 5
+ if(!length)
+ length = 2400 //Unless there's a way to discern via BYOND.
+ var/datum/track/T = new /datum/track(name, file, length, beat)
+ songs += T
+
/obj/machinery/disco/Initialize()
..()
selection = songs[1]
@@ -126,14 +138,14 @@
if(active)
to_chat(usr, "Error: You cannot change the song until the current one is over.")
return
- check_GBP()
- select_name = input(usr, "Choose your song", "Track:") as null|anything in available
- if (QDELETED(src))
- return
+
+ var/list/available = list()
for(var/datum/track/S in songs)
- if(select_name == S.song_name)
- selection = S
- break
+ available[S.song_name] = S
+ var/selected = input(usr, "Choose your song", "Track:") as null|anything in available
+ if(QDELETED(src) || !selected || !istype(available[selected], /datum/track))
+ return
+ selection = available[selected]
updateUsrDialog()
if("horn")
deejay('sound/items/AirHorn2.ogg')
@@ -159,13 +171,6 @@
charge -= 5
playsound(src, S,300,1)
-/obj/machinery/disco/proc/check_GBP()
- available |= "Engineering's Basic Beat"
- available |= "Engineering's Domination Dance"
- available |= "Engineering's Superiority Shimmy"
- available |= "Engineering's Ultimate High-Energy Hustle"
-
-
/obj/machinery/disco/proc/dance_setup()
stop = world.time + selection.song_length
var/turf/cen = get_turf(src)
diff --git a/code/game/machinery/dna_scanner.dm b/code/game/machinery/dna_scanner.dm
index af59f63888..d572b3e9dc 100644
--- a/code/game/machinery/dna_scanner.dm
+++ b/code/game/machinery/dna_scanner.dm
@@ -105,13 +105,14 @@
..()
// search for ghosts, if the corpse is empty and the scanner is connected to a cloner
- if(occupant)
+ var/mob/living/mob_occupant = occupant
+ if(mob_occupant)
if(locate(/obj/machinery/computer/cloning, get_step(src, NORTH)) \
|| locate(/obj/machinery/computer/cloning, get_step(src, SOUTH)) \
|| locate(/obj/machinery/computer/cloning, get_step(src, EAST)) \
|| locate(/obj/machinery/computer/cloning, get_step(src, WEST)))
- if(!occupant.suiciding && !(occupant.disabilities & NOCLONE) && !occupant.hellbound)
- occupant.notify_ghost_cloning("Your corpse has been placed into a cloning scanner. Re-enter your corpse if you want to be cloned!", source = src)
+ if(!mob_occupant.suiciding && !(mob_occupant.disabilities & NOCLONE) && !mob_occupant.hellbound)
+ mob_occupant.notify_ghost_cloning("Your corpse has been placed into a cloning scanner. Re-enter your corpse if you want to be cloned!", source = src)
var/obj/machinery/computer/scan_consolenew/console
for(dir in list(NORTH,EAST,SOUTH,WEST))
@@ -157,4 +158,4 @@
if(..(user,1,0)) //don't set the machine, since there's no dialog
return
- toggle_open(user)
\ No newline at end of file
+ toggle_open(user)
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index a9331cd142..7ff38f53c9 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -1283,8 +1283,6 @@
if(!density)
return 1
- if(!SSticker || !SSticker.mode)
- return 0
operating = 1
update_icon(AIRLOCK_OPENING, 1)
src.set_opacity(0)
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index cd587a0c44..4e076e3338 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -229,8 +229,6 @@
return 1
if(operating)
return
- if(!SSticker || !SSticker.mode)
- return 0
operating = 1
do_animate("opening")
set_opacity(0)
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 20dd6860ea..99cdc1adc3 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -129,8 +129,6 @@
/obj/machinery/door/window/open(forced=0)
if (src.operating == 1) //doors can still open when emag-disabled
return 0
- if(!SSticker || !SSticker.mode)
- return 0
if(!forced)
if(!hasPower())
return 0
diff --git a/code/game/machinery/gulag_teleporter.dm b/code/game/machinery/gulag_teleporter.dm
index 92a550b17c..5979abdc29 100644
--- a/code/game/machinery/gulag_teleporter.dm
+++ b/code/game/machinery/gulag_teleporter.dm
@@ -136,13 +136,14 @@ The console is located at computer/gulag_teleporter.dm
/obj/machinery/gulag_teleporter/proc/strip_occupant()
if(linked_reclaimer)
linked_reclaimer.stored_items[occupant] = list()
- for(var/obj/item/W in occupant)
- if(!is_type_in_typecache(W, required_items) && occupant.temporarilyRemoveItemFromInventory(W))
+ var/mob/living/mob_occupant = occupant
+ for(var/obj/item/W in mob_occupant)
+ if(!is_type_in_typecache(W, required_items) && mob_occupant.temporarilyRemoveItemFromInventory(W))
if(istype(W, /obj/item/weapon/restraints/handcuffs))
W.forceMove(get_turf(src))
continue
if(linked_reclaimer)
- linked_reclaimer.stored_items[occupant] += W
+ linked_reclaimer.stored_items[mob_occupant] += W
linked_reclaimer.contents += W
W.forceMove(linked_reclaimer)
else
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index ae8c1ac1c4..12fcd5ec91 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -26,14 +26,13 @@ Possible to do for anyone motivated enough:
#define HOLOPAD_PASSIVE_POWER_USAGE 1
#define HOLOGRAM_POWER_USAGE 2
-#define RANGE_BASED 4
-#define AREA_BASED 6
+GLOBAL_LIST_EMPTY(holopads)
#define HOLOPAD_MODE RANGE_BASED
/obj/machinery/holopad
- name = "\improper AI holopad"
- desc = "It's a floor-mounted device for projecting holographic images. It is activated remotely."
+ name = "Holopad"
+ desc = "It's a floor-mounted device for projecting holographic images."
icon_state = "holopad0"
layer = LOW_OBJ_LAYER
flags = HEAR
@@ -44,21 +43,32 @@ Possible to do for anyone motivated enough:
obj_integrity = 300
max_integrity = 300
armor = list(melee = 50, bullet = 20, laser = 20, energy = 20, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 0)
- var/list/masters = list()//List of AIs that use the holopad
+ var/list/masters = list()//List of living mobs that use the holopad
var/last_request = 0 //to prevent request spam. ~Carn
var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating.
var/temp = ""
+ var/list/holo_calls //array of /datum/holocalls
+ var/datum/holocall/outgoing_call //do not modify the datums only check and call the public procs
+ var/static/force_answer_call = FALSE //Calls will be automatically answered after a couple rings, here for debugging
var/static/list/holopads = list()
-/obj/machinery/holopad/New()
+/obj/machinery/holopad/Initialize()
..()
var/obj/item/weapon/circuitboard/machine/B = new /obj/item/weapon/circuitboard/machine/holopad(null)
B.apply_default_parts(src)
holopads += src
/obj/machinery/holopad/Destroy()
- for (var/mob/living/silicon/ai/master in masters)
- clear_holo(master)
+ if(outgoing_call)
+ LAZYADD(holo_calls, outgoing_call)
+
+ for(var/I in holo_calls)
+ var/datum/holocall/HC = I
+ HC.ConnectionFailure(src)
+ LAZYCLEARLIST(holo_calls)
+
+ for (var/I in masters)
+ clear_holo(I)
holopads -= src
return ..()
@@ -91,20 +101,58 @@ Possible to do for anyone motivated enough:
return
return ..()
+/obj/machinery/holopad/proc/CheckCallClose()
+ for(var/I in holo_calls)
+ var/datum/holocall/HC = I
+ if(usr == HC.eye)
+ HC.Disconnect(HC.calling_holopad) //disconnect via clicking the called holopad
+ return TRUE
+ return FALSE
+
+/obj/machinery/holopad/Click(location,control,params)
+ if(!CheckCallClose())
+ return ..()
+
/obj/machinery/holopad/AltClick(mob/living/carbon/human/user)
- interact(user)
+ if(!CheckCallClose())
+ interact(user)
/obj/machinery/holopad/interact(mob/living/carbon/human/user) //Carn: Hologram requests.
if(!istype(user))
return
- if(user.stat || !is_operational())
+
+ if(outgoing_call || user.incapacitated() || !is_operational())
return
+
user.set_machine(src)
var/dat
if(temp)
dat = temp
else
- dat = "request an AI's presence."
+ dat = "Request an AI's presence.
"
+ dat += "Call another holopad.
"
+
+ if(LAZYLEN(holo_calls))
+ dat += "=====================================================
"
+
+ var/one_answered_call = FALSE
+ var/one_unanswered_call = FALSE
+ for(var/I in holo_calls)
+ var/datum/holocall/HC = I
+ if(HC.connected_holopad != src)
+ dat += "Answer call from [get_area(HC.calling_holopad)].
"
+ one_unanswered_call = TRUE
+ else
+ one_answered_call = TRUE
+
+ if(one_answered_call && one_unanswered_call)
+ dat += "=====================================================
"
+ //we loop twice for formatting
+ for(var/I in holo_calls)
+ var/datum/holocall/HC = I
+ if(HC.connected_holopad == src)
+ dat += "Disconnect call from [HC.user].
"
+
var/datum/browser/popup = new(user, "holopad", name, 300, 130)
popup.set_content(dat)
@@ -112,7 +160,10 @@ Possible to do for anyone motivated enough:
popup.open()
/obj/machinery/holopad/Topic(href, href_list)
- if(..() || !is_operational())
+ if(..() || isAI(usr))
+ return
+ add_fingerprint(usr)
+ if(!is_operational())
return
if (href_list["AIrequest"])
if(last_request + 200 < world.time)
@@ -120,7 +171,7 @@ Possible to do for anyone motivated enough:
temp = "You requested an AI's presence.
"
temp += "Main Menu"
var/area/area = get_area(src)
- for(var/mob/living/silicon/ai/AI in GLOB.living_mob_list)
+ for(var/mob/living/silicon/ai/AI in GLOB.silicon_mobs)
if(!AI.client)
continue
to_chat(AI, "Your presence is requested at \the [area].")
@@ -128,12 +179,49 @@ Possible to do for anyone motivated enough:
temp = "A request for AI presence was already sent recently.
"
temp += "Main Menu"
- else if(href_list["mainmenu"])
+ else if(href_list["Holocall"])
+ if(outgoing_call)
+ return
+
+ temp = "You must stand on the holopad to make a call!
"
+ temp += "Main Menu"
+ if(usr.loc == loc)
+ var/list/callnames = list()
+ for(var/I in holopads)
+ var/area/A = get_area(I)
+ if(A)
+ LAZYADD(callnames[A], I)
+ callnames -= get_area(src)
+
+ var/result = input(usr, "Choose an area to call", "Holocall") as null|anything in callnames
+ if(QDELETED(usr) || !result || outgoing_call)
+ return
+
+ if(usr.loc == loc)
+ temp = "Dialing...
"
+ temp += "Main Menu"
+ new /datum/holocall(usr, src, callnames[result])
+
+ else if(href_list["connectcall"])
+ var/datum/holocall/call_to_connect = locate(href_list["connectcall"])
+ if(!QDELETED(call_to_connect))
+ call_to_connect.Answer(src)
temp = ""
- updateDialog()
- add_fingerprint(usr)
+ else if(href_list["disconnectcall"])
+ var/datum/holocall/call_to_disconnect = locate(href_list["disconnectcall"])
+ if(!QDELETED(call_to_disconnect))
+ call_to_disconnect.Disconnect(src)
+ temp = ""
+ else if(href_list["mainmenu"])
+ temp = ""
+ if(outgoing_call)
+ outgoing_call.Disconnect()
+
+ updateDialog()
+
+//do not allow AIs to answer calls or people will use it to meta the AI sattelite
/obj/machinery/holopad/attack_ai(mob/living/silicon/ai/user)
if (!istype(user))
return
@@ -148,39 +236,77 @@ Possible to do for anyone motivated enough:
clear_holo(user)
/obj/machinery/holopad/process()
- if(masters.len)//If there is a hologram.
- for (var/mob/living/silicon/ai/master in masters)
- if(master && !master.stat && master.client && master.eyeobj)//If there is an AI attached, it's not incapacitated, it has a client, and the client eye is centered on the projector.
- if(!(stat & NOPOWER))//If the machine has power.
- if(HOLOPAD_MODE == RANGE_BASED)
- if(get_dist(master.eyeobj, src) <= holo_range)
- return TRUE
- else
- var/obj/machinery/holopad/pad_close = get_closest_atom(/obj/machinery/holopad, holopads, master.eyeobj)
- if(get_dist(pad_close, master.eyeobj) <= holo_range)
- var/obj/effect/overlay/holo_pad_hologram/h = masters[master]
- unset_holo(master)
- pad_close.set_holo(master, h)
- return TRUE
+ for(var/I in masters)
+ var/mob/living/master = I
+ var/mob/living/silicon/ai/AI = master
+ if(!istype(AI))
+ AI = null
- else if (HOLOPAD_MODE == AREA_BASED)
+ if(!QDELETED(master) && !master.incapacitated() && master.client && (!AI || AI.eyeobj))//If there is an AI attached, it's not incapacitated, it has a client, and the client eye is centered on the projector.
+ if(is_operational())//If the machine has power.
+ if(AI) //ais are range based
+ if(get_dist(AI.eyeobj, src) <= holo_range)
+ continue
+ else
+ var/obj/machinery/holopad/pad_close = get_closest_atom(/obj/machinery/holopad, holopads, AI.eyeobj)
+ if(get_dist(pad_close, AI.eyeobj) <= holo_range)
+ var/obj/effect/overlay/holo_pad_hologram/h = masters[master]
+ unset_holo(master)
+ pad_close.set_holo(master, h)
+ continue
+ else
+ continue
+ clear_holo(master)//If not, we want to get rid of the hologram.
- var/area/holo_area = get_area(src)
- var/area/eye_area = get_area(master.eyeobj)
+ if(outgoing_call)
+ outgoing_call.Check()
- if(eye_area in holo_area.related)
- return TRUE
+ for(var/I in holo_calls)
+ var/datum/holocall/HC = I
+ if(HC.connected_holopad != src)
+ if(force_answer_call && world.time > (HC.call_start_time + (HOLOPAD_MAX_DIAL_TIME / 2)))
+ HC.Answer(src)
+ break
+ if(outgoing_call)
+ HC.Disconnect(src)//can't answer calls while calling
+ else
+ playsound(src, 'sound/machines/twobeep.ogg', 100) //bring, bring!
- clear_holo(master)//If not, we want to get rid of the hologram.
- return TRUE
+/obj/machinery/holopad/proc/activate_holo(mob/living/user)
+ var/mob/living/silicon/ai/AI = user
+ if(!istype(AI))
+ AI = null
-/obj/machinery/holopad/proc/activate_holo(mob/living/silicon/ai/user)
- if(!(stat & NOPOWER) && user.eyeobj.loc == src.loc)//If the projector has power and client eye is on it
- if (istype(user.current, /obj/machinery/holopad))
+ if(is_operational() && (!AI || AI.eyeobj.loc == loc))//If the projector has power and client eye is on it
+ if (AI && istype(AI.current, /obj/machinery/holopad))
to_chat(user, "ERROR: \black Image feed in progress.")
return
- create_holo(user)//Create one.
- src.visible_message("A holographic image of [user] flicks to life right before your eyes!")
+
+ var/obj/effect/overlay/holo_pad_hologram/Hologram = new(loc)//Spawn a blank effect at the location.
+ if(AI)
+ Hologram.icon = AI.holo_icon
+ else //make it like real life
+ Hologram.icon = user.icon
+ Hologram.icon_state = user.icon_state
+ Hologram.copy_overlays(user, TRUE)
+ //codersprite some holo effects here
+ Hologram.alpha = 100
+ Hologram.add_atom_colour("#77abff", FIXED_COLOUR_PRIORITY)
+ Hologram.Impersonation = user
+
+
+ Hologram.language_holder = user.get_language_holder()
+
+ Hologram.mouse_opacity = 0//So you can't click on it.
+ Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them.
+ Hologram.anchored = 1//So space wind cannot drag it.
+ Hologram.name = "[user.name] (Hologram)"//If someone decides to right click.
+ Hologram.set_light(2) //hologram lighting
+
+ set_holo(user, Hologram)
+ visible_message("A holographic image of [user] flicks to life right before your eyes!")
+
+ return Hologram
else
to_chat(user, "ERROR: \black Unable to project hologram.")
@@ -192,58 +318,80 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
if(masters[master] && speaker != master)
master.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans, message_mode)
-/obj/machinery/holopad/proc/create_holo(mob/living/silicon/ai/A, turf/T = loc)
- var/obj/effect/overlay/holo_pad_hologram/h = new(T)//Spawn a blank effect at the location.
- h.icon = A.holo_icon
- h.mouse_opacity = 0//So you can't click on it.
- h.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them.
- h.anchored = 1//So space wind cannot drag it.
- h.name = "[A.name] (Hologram)"//If someone decides to right click.
- h.set_light(2) //hologram lighting
- set_holo(A, h)
+ for(var/I in holo_calls)
+ var/datum/holocall/HC = I
+ if(HC.connected_holopad == src && speaker != HC.hologram)
+ HC.user.Hear(message, speaker, message_language, raw_message, radio_freq, spans)
+
+ if(outgoing_call && speaker == outgoing_call.user)
+ outgoing_call.hologram.say(raw_message)
+
+/obj/machinery/holopad/proc/SetLightsAndPower()
+ var/total_users = masters.len + LAZYLEN(holo_calls)
+ use_power = HOLOPAD_PASSIVE_POWER_USAGE + HOLOGRAM_POWER_USAGE * total_users
+ if(total_users)
+ set_light(2)
+ icon_state = "holopad1"
+ else
+ set_light(0)
+ icon_state = "holopad0"
+
+/obj/machinery/holopad/proc/set_holo(mob/living/user, var/obj/effect/overlay/holo_pad_hologram/h)
+ masters[user] = h
+ var/mob/living/silicon/ai/AI = user
+ if(istype(AI))
+ AI.current = src
+ SetLightsAndPower()
return TRUE
-/obj/machinery/holopad/proc/set_holo(mob/living/silicon/ai/A, var/obj/effect/overlay/holo_pad_hologram/h)
- masters[A] = h
- set_light(2) // pad lighting
- icon_state = "holopad1"
- A.current = src
- use_power += HOLOGRAM_POWER_USAGE
- return TRUE
-
-/obj/machinery/holopad/proc/clear_holo(mob/living/silicon/ai/user)
+/obj/machinery/holopad/proc/clear_holo(mob/living/user)
qdel(masters[user]) // Get rid of user's hologram
unset_holo(user)
return TRUE
-/obj/machinery/holopad/proc/unset_holo(mob/living/silicon/ai/user)
- if(user.current == src)
- user.current = null
+/obj/machinery/holopad/proc/unset_holo(mob/living/user)
+ var/mob/living/silicon/ai/AI = user
+ if(istype(AI) && AI.current == src)
+ AI.current = null
masters -= user // Discard AI from the list of those who use holopad
- use_power = max(HOLOPAD_PASSIVE_POWER_USAGE, use_power - HOLOGRAM_POWER_USAGE)//Reduce power usage
- if (!masters.len) // If no users left
- set_light(0) // pad lighting (hologram lighting will be handled automatically since its owner was deleted)
- icon_state = "holopad0"
- use_power = HOLOPAD_PASSIVE_POWER_USAGE
+ SetLightsAndPower()
return TRUE
-/obj/machinery/holopad/proc/move_hologram(mob/living/silicon/ai/user)
+/obj/machinery/holopad/proc/move_hologram(mob/living/user, turf/new_turf)
if(masters[user])
- step_to(masters[user], user.eyeobj)
var/obj/effect/overlay/holo_pad_hologram/H = masters[user]
- H.loc = get_turf(user.eyeobj)
+ step_to(H, new_turf)
+ H.loc = new_turf
+ var/area/holo_area = get_area(src)
+ var/area/eye_area = new_turf.loc
+
+ if(!(eye_area in holo_area.related))
+ clear_holo(user)
return TRUE
+/obj/effect/overlay/holo_pad_hologram
+ var/mob/living/Impersonation
+ var/datum/holocall/HC
+
+/obj/effect/overlay/holo_pad_hologram/Destroy()
+ Impersonation = null
+ if(HC)
+ HC.Disconnect(HC.calling_holopad)
+ return ..()
+
/obj/effect/overlay/holo_pad_hologram/Process_Spacemove(movement_dir = 0)
return 1
+/obj/effect/overlay/holo_pad_hologram/examine(mob/user)
+ if(Impersonation)
+ return Impersonation.examine(user)
+ return ..()
+
/obj/item/weapon/circuitboard/machine/holopad
name = "AI Holopad (Machine Board)"
build_path = /obj/machinery/holopad
origin_tech = "programming=1"
req_components = list(/obj/item/weapon/stock_parts/capacitor = 1)
-#undef RANGE_BASED
-#undef AREA_BASED
#undef HOLOPAD_PASSIVE_POWER_USAGE
-#undef HOLOGRAM_POWER_USAGE
+#undef HOLOGRAM_POWER_USAGE
\ No newline at end of file
diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm
index 6cd8e75798..96b0029134 100644
--- a/code/game/machinery/machinery.dm
+++ b/code/game/machinery/machinery.dm
@@ -1,8 +1,8 @@
/*
Overview:
- Used to create objects that need a per step proc call. Default definition of 'New()'
+ Used to create objects that need a per step proc call. Default definition of 'Initialize()'
stores a reference to src machine in global 'machines list'. Default definition
- of 'Del' removes reference to src machine in global 'machines list'.
+ of 'Destroy' removes reference to src machine in global 'machines list'.
Class Variables:
use_power (num)
@@ -44,7 +44,7 @@ Class Variables:
EMPED:16 -- temporary broken by EMP pulse
Class Procs:
- New() 'game/machinery/machine.dm'
+ Initialize() 'game/machinery/machine.dm'
Destroy() 'game/machinery/machine.dm'
@@ -118,14 +118,15 @@ Class Procs:
var/panel_open = 0
var/state_open = 0
var/critical_machine = FALSE //If this machine is critical to station operation and should have the area be excempted from power failures.
- var/mob/living/occupant = null
+ var/list/occupant_typecache = list(/mob/living) // turned into typecache in Initialize
+ var/atom/movable/occupant = null
var/unsecuring_tool = /obj/item/weapon/wrench
var/interact_open = 0 // Can the machine be interacted with when in maint/when the panel is open.
var/interact_offline = 0 // Can the machine be interacted with while de-powered.
var/speed_process = 0 // Process as fast as possible?
/obj/machinery/Initialize()
- if (!armor)
+ if(!armor)
armor = list(melee = 25, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 70)
. = ..()
GLOB.machines += src
@@ -135,6 +136,8 @@ Class Procs:
START_PROCESSING(SSfastprocess, src)
power_change()
+ occupant_typecache = typecacheof(occupant_typecache)
+
/obj/machinery/Destroy()
GLOB.machines.Remove(src)
if(!speed_process)
@@ -176,16 +179,24 @@ Class Procs:
L.update_canmove()
occupant = null
-/obj/machinery/proc/close_machine(mob/living/target = null)
+/obj/machinery/proc/close_machine(atom/movable/target = null)
state_open = 0
density = 1
if(!target)
- for(var/mob/living/carbon/C in loc)
- if(C.buckled || C.has_buckled_mobs())
+ for(var/am in loc)
+ if(!is_type_in_typecache(am, occupant_typecache))
continue
- else
- target = C
- if(target && !target.buckled && !target.has_buckled_mobs())
+ var/atom/movable/AM = am
+ if(AM.has_buckled_mobs())
+ continue
+ if(isliving(AM))
+ var/mob/living/L = am
+ if(L.buckled)
+ continue
+ target = am
+
+ var/mob/living/mobtarget = target
+ if(target && !target.has_buckled_mobs() && (!isliving(target) || !mobtarget.buckled))
occupant = target
target.forceMove(src)
updateUsrDialog()
diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm
index a50a2973fc..7c3251a1be 100644
--- a/code/game/machinery/newscaster.dm
+++ b/code/game/machinery/newscaster.dm
@@ -535,7 +535,7 @@ GLOBAL_LIST_EMPTY(allCasters)
if(choice=="Confirm")
scan_user(usr)
GLOB.news_network.CreateFeedChannel(channel_name, scanned_user, c_locked)
- feedback_inc("newscaster_channels",1)
+ SSblackbox.inc("newscaster_channels",1)
screen=5
updateUsrDialog()
else if(href_list["set_channel_receiving"])
@@ -558,7 +558,7 @@ GLOBAL_LIST_EMPTY(allCasters)
screen=6
else
GLOB.news_network.SubmitArticle("[parsepencode(msg, usr, SIGNFONT)]", scanned_user, channel_name, photo, 0, allow_comments)
- feedback_inc("newscaster_stories",1)
+ SSblackbox.inc("newscaster_stories",1)
screen=4
msg = ""
updateUsrDialog()
@@ -850,7 +850,7 @@ GLOBAL_LIST_EMPTY(allCasters)
return
/obj/machinery/newscaster/proc/print_paper()
- feedback_inc("newscaster_newspapers_printed",1)
+ SSblackbox.inc("newscaster_newspapers_printed",1)
var/obj/item/weapon/newspaper/NEWSPAPER = new /obj/item/weapon/newspaper
for(var/datum/newscaster/feed_channel/FC in GLOB.news_network.network_channels)
NEWSPAPER.news_content += FC
diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm
index 6735aa7609..2d3765a934 100644
--- a/code/game/machinery/pipe/construction.dm
+++ b/code/game/machinery/pipe/construction.dm
@@ -251,6 +251,8 @@ GLOBAL_LIST_INIT(pipeID2State, list(
var/mob/living/carbon/C = user
for(var/i=1 to 20)
C.vomit(0,1,0,4,0)
+ if(prob(20))
+ C.spew_organ()
sleep(5)
C.blood_volume = 0
return(OXYLOSS|BRUTELOSS)
diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm
index b8ba21fd35..56b13f0e46 100644
--- a/code/game/machinery/porta_turret/portable_turret.dm
+++ b/code/game/machinery/porta_turret/portable_turret.dm
@@ -1,6 +1,9 @@
#define TURRET_STUN 0
#define TURRET_LETHAL 1
+#define POPUP_ANIM_TIME 5
+#define POPDOWN_ANIM_TIME 5 //Be sure to change the icon animation at the same time or it'll look bad
+
/obj/machinery/porta_turret
name = "turret"
icon = 'icons/obj/turrets.dmi'
@@ -364,6 +367,10 @@
var/list/targets = list()
var/turretview = view(scan_range, base)
for(var/A in turretview)
+ var/atom/AA = A
+ if(AA.invisibility>SEE_INVISIBLE_LIVING)
+ continue
+
if(check_anomalies)//if it's set to check for simple animals
if(istype(A, /mob/living/simple_animal))
var/mob/living/simple_animal/SA = A
@@ -418,7 +425,7 @@
raising = 1
if(cover)
flick("popup", cover)
- sleep(10)
+ sleep(POPUP_ANIM_TIME)
raising = 0
if(cover)
cover.icon_state = "openTurretCover"
@@ -434,7 +441,7 @@
raising = 1
if(cover)
flick("popdown", cover)
- sleep(10)
+ sleep(POPDOWN_ANIM_TIME)
raising = 0
if(cover)
cover.icon_state = "turretCover"
diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm
old mode 100644
new mode 100755
index ca398539a0..a081e3eb80
--- a/code/game/machinery/recharger.dm
+++ b/code/game/machinery/recharger.dm
@@ -61,7 +61,7 @@
G.loc = src
charging = G
use_power = 2
- update_icon()
+ update_icon(scan = TRUE)
else
to_chat(user, "[src] isn't connected to anything!")
return 1
@@ -162,10 +162,13 @@
..()
-/obj/machinery/recharger/update_icon(using_power = 0) //we have an update_icon() in addition to the stuff in process to make it feel a tiny bit snappier.
+/obj/machinery/recharger/update_icon(using_power = 0, scan) //we have an update_icon() in addition to the stuff in process to make it feel a tiny bit snappier.
if(stat & (NOPOWER|BROKEN) || !anchored)
icon_state = "rechargeroff"
return
+ if(scan)
+ icon_state = "rechargeroff"
+ return
if(panel_open)
icon_state = "rechargeropen"
return
diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm
index 57e6ee3f09..c65c0029ff 100644
--- a/code/game/machinery/recycler.dm
+++ b/code/game/machinery/recycler.dm
@@ -121,7 +121,7 @@
var/atom/movable/AM = i
var/obj/item/bodypart/head/as_head = AM
var/obj/item/device/mmi/as_mmi = AM
- var/brain_holder = istype(AM, /obj/item/organ/brain) || (istype(as_head) && as_head.brain) || (istype(as_mmi) && as_mmi.brain)
+ var/brain_holder = istype(AM, /obj/item/organ/brain) || (istype(as_head) && as_head.brain) || (istype(as_mmi) && as_mmi.brain) || istype(AM, /mob/living/brain)
if(isliving(AM) || brain_holder)
if(emagged)
if(!brain_holder)
diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm
index 648b44932a..7506008e79 100644
--- a/code/game/machinery/requests_console.dm
+++ b/code/game/machinery/requests_console.dm
@@ -439,12 +439,12 @@ GLOBAL_LIST_EMPTY(allConsoles)
updateUsrDialog()
return
-/obj/machinery/requests_console/say_quote(input, list/spans, message_mode)
+/obj/machinery/requests_console/say_mod(input, message_mode)
var/ending = copytext(input, length(input) - 2)
if (ending == "!!!")
- return "blares, \"[attach_spans(input, spans)]\""
-
- return ..()
+ . = "blares"
+ else
+ . = ..()
/obj/machinery/requests_console/proc/clear_emergency()
emergency = null
diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm
index 7fc0f98b1c..e81774e147 100644
--- a/code/game/machinery/suit_storage_unit.dm
+++ b/code/game/machinery/suit_storage_unit.dm
@@ -211,13 +211,13 @@
uv = TRUE
locked = TRUE
update_icon()
- if(occupant)
+ if(occupant)
+ var/mob/living/mob_occupant = occupant
if(uv_super)
- occupant.adjustFireLoss(rand(20, 36))
+ mob_occupant.adjustFireLoss(rand(20, 36))
else
- occupant.adjustFireLoss(rand(10, 16))
- if(iscarbon(occupant))
- occupant.emote("scream")
+ mob_occupant.adjustFireLoss(rand(10, 16))
+ mob_occupant.emote("scream")
addtimer(CALLBACK(src, .proc/cook), 50)
else
uv_cycles = initial(uv_cycles)
@@ -368,14 +368,15 @@
else if(!helmet && !mask && !suit && !storage && !occupant)
return
else
- if(occupant)
- to_chat(occupant, "[src]'s confines grow warm, then hot, then scorching. You're being burned [!occupant.stat ? "alive" : "away"]!")
+ if(occupant)
+ var/mob/living/mob_occupant = occupant
+ to_chat(mob_occupant, "[src]'s confines grow warm, then hot, then scorching. You're being burned [!mob_occupant.stat ? "alive" : "away"]!")
cook()
. = TRUE
if("dispense")
if(!state_open)
return
-
+
var/static/list/valid_items = list("helmet", "suit", "mask", "storage")
var/item_name = params["item"]
if(item_name in valid_items)
diff --git a/code/game/machinery/telecomms/broadcasting.dm b/code/game/machinery/telecomms/broadcasting.dm
index d6c6cdc151..6c1fd431e5 100644
--- a/code/game/machinery/telecomms/broadcasting.dm
+++ b/code/game/machinery/telecomms/broadcasting.dm
@@ -144,30 +144,7 @@
// --- This following recording is intended for research and feedback in the use of department radio channels ---
var/blackbox_msg = "[AM] [AM.say_quote(message, spans)]"
- if(istype(GLOB.blackbox))
- switch(freq)
- if(1459)
- GLOB.blackbox.msg_common += blackbox_msg
- if(1351)
- GLOB.blackbox.msg_science += blackbox_msg
- if(1353)
- GLOB.blackbox.msg_command += blackbox_msg
- if(1355)
- GLOB.blackbox.msg_medical += blackbox_msg
- if(1357)
- GLOB.blackbox.msg_engineering += blackbox_msg
- if(1359)
- GLOB.blackbox.msg_security += blackbox_msg
- if(1441)
- GLOB.blackbox.msg_deathsquad += blackbox_msg
- if(1213)
- GLOB.blackbox.msg_syndicate += blackbox_msg
- if(1349)
- GLOB.blackbox.msg_service += blackbox_msg
- if(1347)
- GLOB.blackbox.msg_cargo += blackbox_msg
- else
- GLOB.blackbox.messages += blackbox_msg
+ SSblackbox.LogBroadcast(blackbox_msg, freq)
sleep(50)
if(!QDELETED(virt)) //It could happen to YOU
diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm
index cbb4efd6b0..bd51e1e651 100644
--- a/code/game/machinery/vending.dm
+++ b/code/game/machinery/vending.dm
@@ -56,6 +56,7 @@
var/dish_quants = list() //used by the snack machine's custom compartment to count dishes.
var/obj/item/weapon/vending_refill/refill_canister = null //The type of refill canisters used by this machine.
+ var/refill_count = 3 //The number of canisters the vending machine uses
/obj/machinery/vending/Initialize()
..()
@@ -88,7 +89,9 @@
/obj/machinery/vending/cola = "Robust Softdrinks",
/obj/machinery/vending/cigarette = "ShadyCigs Deluxe",
/obj/machinery/vending/autodrobe = "AutoDrobe",
- /obj/machinery/vending/clothing = "ClothesMate")
+ /obj/machinery/vending/clothing = "ClothesMate",
+ /obj/machinery/vending/medical = "NanoMed Plus",
+ /obj/machinery/vending/wallmed = "NanoMed")
/obj/item/weapon/circuitboard/machine/vendor/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/weapon/screwdriver))
@@ -104,7 +107,7 @@
/obj/item/weapon/circuitboard/machine/vendor/proc/set_type(var/obj/machinery/vending/typepath)
build_path = typepath
name = "[names_paths[build_path]] Vendor (Machine Board)"
- req_components = list(initial(typepath.refill_canister) = 3)
+ req_components = list(initial(typepath.refill_canister) = initial(typepath.refill_count))
/obj/item/weapon/circuitboard/machine/vendor/apply_default_parts(obj/machinery/M)
for(var/typepath in names_paths)
@@ -113,7 +116,6 @@
break
..()
-
/obj/machinery/vending/Destroy()
qdel(wires)
wires = null
@@ -297,7 +299,7 @@
to_chat(user, "You [panel_open ? "open" : "close"] the maintenance panel.")
cut_overlays()
if(panel_open)
- add_overlay("[initial(icon_state)]-panel")
+ add_overlay("[initial(icon_state)]-panel")
playsound(src.loc, W.usesound, 50, 1)
updateUsrDialog()
else
@@ -307,7 +309,10 @@
if(panel_open)
attack_hand(user)
return
- else if(istype(W, /obj/item/weapon/coin) && premium.len > 0)
+ else if(istype(W, /obj/item/weapon/coin))
+ if(!premium.len)
+ to_chat(user, "[src] doesn't have a coin slot.")
+ return
if(!user.drop_item())
return
W.loc = src
@@ -528,7 +533,7 @@
if(icon_vend) //Show the vending animation if needed
flick(icon_vend,src)
new R.product_path(get_turf(src))
- feedback_add_details("vending_machine_usage","[src.type]|[R.product_path]")
+ SSblackbox.add_details("vending_machine_usage","[src.type]|[R.product_path]")
vend_ready = 1
return
@@ -732,7 +737,8 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C
icon_state = "sustenance"
products = list(/obj/item/weapon/reagent_containers/food/snacks/tofu = 24,
/obj/item/weapon/reagent_containers/food/drinks/ice = 12,
- /obj/item/weapon/reagent_containers/food/snacks/candy_corn = 6)
+ /obj/item/weapon/reagent_containers/food/snacks/candy_corn = 6,
+ /obj/item/weapon/reagent_containers/glass/beaker/waterbottle = 10)
contraband = list(/obj/item/weapon/kitchen/knife = 6,
/obj/item/weapon/reagent_containers/food/drinks/coffee = 12,
/obj/item/weapon/tank/internals/emergency_oxygen = 6,
@@ -749,7 +755,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C
products = list(/obj/item/weapon/reagent_containers/food/drinks/soda_cans/cola = 10,/obj/item/weapon/reagent_containers/food/drinks/soda_cans/space_mountain_wind = 10,
/obj/item/weapon/reagent_containers/food/drinks/soda_cans/dr_gibb = 10,/obj/item/weapon/reagent_containers/food/drinks/soda_cans/starkist = 10,
/obj/item/weapon/reagent_containers/food/drinks/soda_cans/space_up = 10,/obj/item/weapon/reagent_containers/food/drinks/soda_cans/pwr_game = 10,
- /obj/item/weapon/reagent_containers/food/drinks/soda_cans/lemon_lime = 10)
+ /obj/item/weapon/reagent_containers/food/drinks/soda_cans/lemon_lime = 10,/obj/item/weapon/reagent_containers/glass/beaker/waterbottle = 10)
contraband = list(/obj/item/weapon/reagent_containers/food/drinks/soda_cans/thirteenloko = 6,/obj/item/weapon/reagent_containers/food/drinks/soda_cans/shamblers = 6)
premium = list(/obj/item/weapon/reagent_containers/food/drinks/drinkingglass/filled/nuka_cola = 1,/obj/item/weapon/reagent_containers/food/drinks/soda_cans/air = 1)
refill_canister = /obj/item/weapon/vending_refill/cola
@@ -873,6 +879,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C
premium = list(/obj/item/weapon/storage/box/hug/medical = 1,/obj/item/weapon/reagent_containers/hypospray/medipen = 3, /obj/item/weapon/storage/belt/medical = 3, /obj/item/weapon/wrench/medical = 1)
armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50)
resistance_flags = FIRE_PROOF
+ refill_canister = /obj/item/weapon/vending_refill/medical
//This one's from bay12
/obj/machinery/vending/plasmaresearch
@@ -895,6 +902,8 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C
contraband = list(/obj/item/weapon/reagent_containers/pill/tox = 2,/obj/item/weapon/reagent_containers/pill/morphine = 2)
armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50)
resistance_flags = FIRE_PROOF
+ refill_canister = /obj/item/weapon/vending_refill/medical
+ refill_count = 1
/obj/machinery/vending/security
name = "\improper SecTech"
@@ -938,7 +947,8 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C
/obj/item/seeds/tea = 3,/obj/item/seeds/tobacco = 3,/obj/item/seeds/tomato = 3,
/obj/item/seeds/tower = 3,/obj/item/seeds/watermelon = 3,/obj/item/seeds/wheat = 3,/obj/item/seeds/whitebeet = 3)
contraband = list(/obj/item/seeds/amanita = 2,/obj/item/seeds/glowshroom = 2,/obj/item/seeds/liberty = 2,/obj/item/seeds/nettle = 2,
- /obj/item/seeds/plump = 2,/obj/item/seeds/reishi = 2,/obj/item/seeds/cannabis = 3, /obj/item/seeds/random = 2)
+ /obj/item/seeds/plump = 2,/obj/item/seeds/reishi = 2,/obj/item/seeds/cannabis = 3,/obj/item/seeds/starthistle = 2,
+ /obj/item/seeds/random = 2)
premium = list(/obj/item/weapon/reagent_containers/spray/waterflower = 1)
armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50)
resistance_flags = FIRE_PROOF
@@ -1119,7 +1129,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C
/obj/item/clothing/head/beanie=1, /obj/item/clothing/head/beanie/black=1, /obj/item/clothing/head/beanie/red=1, /obj/item/clothing/head/beanie/green=1, /obj/item/clothing/head/beanie/darkblue=1,
/obj/item/clothing/head/beanie/purple=1, /obj/item/clothing/head/beanie/yellow=1, /obj/item/clothing/head/beanie/orange=1, /obj/item/clothing/head/beanie/cyan=1, /obj/item/clothing/head/beanie/christmas=1,
/obj/item/clothing/head/beanie/striped=1, /obj/item/clothing/head/beanie/stripedred=1, /obj/item/clothing/head/beanie/stripedblue=1, /obj/item/clothing/head/beanie/stripedgreen=1,
- /obj/item/clothing/suit/jacket/letterman_red=1)
+ /obj/item/clothing/suit/jacket/letterman_red=1, /obj/item/clothing/head/hunter=2)
contraband = list(/obj/item/clothing/under/syndicate/tacticool=1,/obj/item/clothing/mask/balaclava=1,/obj/item/clothing/head/ushanka=1,/obj/item/clothing/under/soviet=1,/obj/item/weapon/storage/belt/fannypack/black=2,/obj/item/clothing/suit/jacket/letterman_syndie=1,/obj/item/clothing/under/jabroni=1, /obj/item/clothing/suit/vapeshirt=1, /obj/item/clothing/under/geisha=1)
premium = list(/obj/item/clothing/under/suit_jacket/checkered=1,/obj/item/clothing/head/mailman=1,/obj/item/clothing/under/rank/mailman=1,/obj/item/clothing/suit/jacket/leather=1,/obj/item/clothing/suit/jacket/leather/overcoat=1,/obj/item/clothing/under/pants/mustangjeans=1,/obj/item/clothing/neck/necklace/dope=3,/obj/item/clothing/suit/jacket/letterman_nanotrasen=1)
refill_canister = /obj/item/weapon/vending_refill/clothing
diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm
index 7a9d01bb7f..bf90f0a569 100644
--- a/code/game/mecha/equipment/weapons/weapons.dm
+++ b/code/game/mecha/equipment/weapons/weapons.dm
@@ -107,7 +107,7 @@
icon_state = "mecha_ion"
origin_tech = "materials=4;engineering=4;combat=6;magnets=6"
energy_drain = 500
- projectile = /obj/item/projectile/energy/tesla_cannon
+ projectile = /obj/item/projectile/energy/tesla/cannon
fire_sound = 'sound/magic/lightningbolt.ogg'
@@ -337,8 +337,8 @@
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang/proj_init(var/obj/item/weapon/grenade/flashbang/F)
var/turf/T = get_turf(src)
- message_admins("[key_name(chassis.occupant, chassis.occupant.client)](?) fired a [src] in ([T.x],[T.y],[T.z] - JMP)",0,1)
- log_game("[key_name(chassis.occupant)] fired a [src] ([T.x],[T.y],[T.z])")
+ message_admins("[ADMIN_LOOKUPFLW(chassis.occupant)] fired a [src] in [ADMIN_COORDJMP(T)]",0,1)
+ log_game("[key_name(chassis.occupant)] fired a [src] [COORD(T)]")
addtimer(CALLBACK(F, /obj/item/weapon/grenade/flashbang.proc/prime), det_time)
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang/clusterbang //Because I am a heartless bastard -Sieve //Heartless? for making the poor man's honkblast? - Kaze
diff --git a/code/game/mecha/mecha_construction_paths.dm b/code/game/mecha/mecha_construction_paths.dm
index b065de37ca..be6e85448b 100644
--- a/code/game/mecha/mecha_construction_paths.dm
+++ b/code/game/mecha/mecha_construction_paths.dm
@@ -277,7 +277,7 @@
/datum/construction/reversible/mecha/ripley/spawn_result()
..()
- feedback_inc("mecha_ripley_created",1)
+ SSblackbox.inc("mecha_ripley_created",1)
return
@@ -564,7 +564,7 @@
var/obj/mecha/combat/gygax/M = new result(get_turf(holder))
M.CheckParts(holder.contents)
qdel(holder)
- feedback_inc("mecha_gygax_created",1)
+ SSblackbox.inc("mecha_gygax_created",1)
return
/datum/construction/mecha/firefighter_chassis
@@ -786,7 +786,7 @@
/datum/construction/reversible/mecha/firefighter/spawn_result()
..()
- feedback_inc("mecha_firefighter_created",1)
+ SSblackbox.inc("mecha_firefighter_created",1)
return
@@ -864,7 +864,7 @@
/datum/construction/mecha/honker/spawn_result()
..()
- feedback_inc("mecha_honker_created",1)
+ SSblackbox.inc("mecha_honker_created",1)
return
/datum/construction/mecha/durand_chassis
@@ -1149,7 +1149,7 @@
var/obj/mecha/combat/gygax/M = new result(get_turf(holder))
M.CheckParts(holder.contents)
qdel(holder)
- feedback_inc("mecha_durand_created",1)
+ SSblackbox.inc("mecha_durand_created",1)
return
//PHAZON
@@ -1481,7 +1481,7 @@
var/obj/mecha/combat/gygax/M = new result(get_turf(holder))
M.CheckParts(holder.contents)
qdel(holder)
- feedback_inc("mecha_phazon_created",1)
+ SSblackbox.inc("mecha_phazon_created",1)
return
//ODYSSEUS
@@ -1692,5 +1692,5 @@
/datum/construction/reversible/mecha/odysseus/spawn_result()
..()
- feedback_inc("mecha_odysseus_created",1)
+ SSblackbox.inc("mecha_odysseus_created",1)
return
diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm
index 7727b4d9e9..a39c4fbe93 100644
--- a/code/game/objects/effects/decals/cleanable/humans.dm
+++ b/code/game/objects/effects/decals/cleanable/humans.dm
@@ -129,7 +129,7 @@
name = "footprints"
icon = 'icons/effects/footprints.dmi'
icon_state = "nothingwhatsoever"
- desc = "where might they lead?"
+ desc = "WHOSE FOOTPRINTS ARE THESE?"
random_icon_states = null
var/entered_dirs = 0
var/exited_dirs = 0
diff --git a/code/game/objects/effects/effect_system/effects_smoke.dm b/code/game/objects/effects/effect_system/effects_smoke.dm
index a34e30e689..9324c6edb7 100644
--- a/code/game/objects/effects/effect_system/effects_smoke.dm
+++ b/code/game/objects/effects/effect_system/effects_smoke.dm
@@ -74,7 +74,7 @@
/obj/effect/particle_effect/smoke/proc/spread_smoke()
var/turf/t_loc = get_turf(src)
if(!t_loc)
- return
+ return
var/list/newsmokes = list()
for(var/turf/T in t_loc.GetAtmosAdjacentTurfs())
var/obj/effect/particle_effect/smoke/foundsmoke = locate() in T //Don't spread smoke where there's already smoke!
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index 4194d9b76e..86d62eacea 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -94,7 +94,7 @@
name = "Head of Personnel"
/obj/effect/landmark/start/librarian
- name = "Librarian"
+ name = "Curator"
/obj/effect/landmark/start/lawyer
name = "Lawyer"
diff --git a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm
index bc9251d9fc..12225321d7 100644
--- a/code/game/objects/effects/overlays.dm
+++ b/code/game/objects/effects/overlays.dm
@@ -31,8 +31,8 @@
. = ..()
deltimer(timerid)
-/obj/effect/overlay/temp/New()
- ..()
+/obj/effect/overlay/temp/Initialize()
+ . = ..()
if(randomdir)
setDir(pick(GLOB.cardinal))
@@ -44,10 +44,10 @@
/obj/effect/overlay/temp/dir_setting
randomdir = FALSE
-/obj/effect/overlay/temp/dir_setting/New(loc, set_dir)
+/obj/effect/overlay/temp/dir_setting/Initialize(mapload, set_dir)
if(set_dir)
setDir(set_dir)
- ..()
+ . = ..()
/obj/effect/overlay/temp/dir_setting/bloodsplatter
icon = 'icons/effects/blood.dmi'
@@ -56,12 +56,12 @@
layer = BELOW_MOB_LAYER
var/splatter_type = "splatter"
-/obj/effect/overlay/temp/dir_setting/bloodsplatter/New(loc, set_dir)
+/obj/effect/overlay/temp/dir_setting/bloodsplatter/Initialize(mapload, set_dir)
if(set_dir in GLOB.diagonals)
icon_state = "[splatter_type][pick(1, 2, 6)]"
else
icon_state = "[splatter_type][pick(3, 4, 5)]"
- ..()
+ . = ..()
var/target_pixel_x = 0
var/target_pixel_y = 0
switch(set_dir)
@@ -93,6 +93,13 @@
/obj/effect/overlay/temp/dir_setting/bloodsplatter/xenosplatter
splatter_type = "xsplatter"
+/obj/effect/overlay/temp/dir_setting/speedbike_trail
+ name = "speedbike trails"
+ icon_state = "ion_fade"
+ layer = BELOW_MOB_LAYER
+ duration = 10
+ randomdir = 0
+
/obj/effect/overlay/temp/dir_setting/firing_effect
icon = 'icons/effects/effects.dmi'
icon_state = "firing_effect"
@@ -189,8 +196,8 @@
desc = "It's a decoy!"
duration = 15
-/obj/effect/overlay/temp/decoy/New(loc, atom/mimiced_atom)
- ..()
+/obj/effect/overlay/temp/decoy/Initialize(mapload, atom/mimiced_atom)
+ . = ..()
alpha = initial(alpha)
if(mimiced_atom)
name = mimiced_atom.name
@@ -198,8 +205,8 @@
setDir(mimiced_atom.dir)
mouse_opacity = 0
-/obj/effect/overlay/temp/decoy/fading/New(loc, atom/mimiced_atom)
- ..()
+/obj/effect/overlay/temp/decoy/fading/Initialize(mapload, atom/mimiced_atom)
+ . = ..()
animate(src, alpha = 0, time = duration)
/obj/effect/overlay/temp/decoy/fading/fivesecond
@@ -223,7 +230,15 @@
name = "blood sparks"
icon_state = "bloodsparkles"
-/obj/effect/overlay/temp/dir_setting/cult/phase
+/obj/effect/overlay/temp/cult/blood // The traditional teleport
+ name = "blood jaunt"
+ duration = 12
+ icon_state = "bloodin"
+
+/obj/effect/overlay/temp/cult/blood/out
+ icon_state = "bloodout"
+
+/obj/effect/overlay/temp/dir_setting/cult/phase // The veil shifter teleport
name = "phase glow"
duration = 7
icon_state = "cultin"
@@ -316,23 +331,41 @@
/obj/effect/overlay/temp/ratvar/grille/broken
icon_state = "ratvarbrokengrilleglow"
+/obj/effect/overlay/temp/ratvar/mending_mantra
+ layer = ABOVE_MOB_LAYER
+ duration = 20
+ alpha = 200
+ icon_state = "mending_mantra"
+ light_range = 1.5
+ light_color = "#1E8CE1"
+
+/obj/effect/overlay/temp/ratvar/mending_mantra/Initialize(mapload)
+ . = ..()
+ transform = matrix()*2
+ var/matrix/M = transform
+ M.Turn(90)
+ animate(src, alpha = 20, time = duration, easing = BOUNCE_EASING, flags = ANIMATION_PARALLEL)
+ animate(src, transform = M, time = duration, flags = ANIMATION_PARALLEL)
+
/obj/effect/overlay/temp/ratvar/volt_hit
name = "volt blast"
layer = ABOVE_MOB_LAYER
duration = 5
icon_state = "volt_hit"
+ light_range = 1.5
+ light_power = 2
+ light_color = LIGHT_COLOR_ORANGE
var/mob/user
var/damage = 20
-/obj/effect/overlay/temp/ratvar/volt_hit/New(loc, caster, multiplier)
+/obj/effect/overlay/temp/ratvar/volt_hit/Initialize(mapload, caster, multiplier)
if(multiplier)
damage *= multiplier
duration = max(round(damage * 0.2), 1)
- ..()
- set_light(1.5, 2, LIGHT_COLOR_ORANGE)
+ . = ..()
-/obj/effect/overlay/temp/ratvar/volt_hit/true/New(loc, caster, multiplier)
- ..()
+/obj/effect/overlay/temp/ratvar/volt_hit/true/Initialize(mapload, caster, multiplier)
+ . = ..()
user = caster
if(user)
var/matrix/M = new
@@ -376,8 +409,8 @@
icon_state = "warden_gaze"
duration = 3
-/obj/effect/overlay/temp/ratvar/ocular_warden/New()
- ..()
+/obj/effect/overlay/temp/ratvar/ocular_warden/Initialize()
+ . = ..()
pixel_x = rand(-8, 8)
pixel_y = rand(-10, 10)
animate(src, alpha = 0, time = 3, easing = EASE_OUT)
@@ -401,8 +434,8 @@
layer = ABOVE_MOB_LAYER
duration = 10
-/obj/effect/overlay/temp/ratvar/component/New()
- ..()
+/obj/effect/overlay/temp/ratvar/component/Initialize()
+ . = ..()
transform = matrix()*0.75
pixel_x = rand(-10, 10)
pixel_y = rand(-10, -2)
@@ -432,9 +465,8 @@
light_power = 2
light_color = "#FAE48C"
-/obj/effect/overlay/temp/ratvar/sigil/transgression/New()
- ..()
- update_light()
+/obj/effect/overlay/temp/ratvar/sigil/transgression/Initialize()
+ . = ..()
var/oldtransform = transform
animate(src, transform = matrix()*2, time = 5)
animate(transform = oldtransform, alpha = 0, time = 65)
@@ -447,10 +479,6 @@
light_power = 0.5
light_color = "#1E8CE1"
-/obj/effect/overlay/temp/ratvar/sigil/vitality/New()
- ..()
- update_light()
-
/obj/effect/overlay/temp/ratvar/sigil/accession
color = "#AF0AAF"
layer = ABOVE_MOB_LAYER
@@ -493,9 +521,9 @@
icon = 'icons/mob/mob.dmi'
duration = 15
-/obj/effect/overlay/temp/gib_animation/New(loc, gib_icon)
+/obj/effect/overlay/temp/gib_animation/Initialize(mapload, gib_icon)
icon_state = gib_icon // Needs to be before ..() so icon is correct
- ..()
+ . = ..()
/obj/effect/overlay/temp/gib_animation/ex_act(severity)
return //so the overlay isn't deleted by the explosion that gibbed the mob.
@@ -507,9 +535,9 @@
icon = 'icons/mob/mob.dmi'
duration = 15
-/obj/effect/overlay/temp/dust_animation/New(loc, dust_icon)
+/obj/effect/overlay/temp/dust_animation/Initialize(mapload, dust_icon)
icon_state = dust_icon // Before ..() so the correct icon is flick()'d
- ..()
+ . = ..()
/obj/effect/overlay/temp/mummy_animation
icon = 'icons/mob/mob.dmi'
@@ -521,12 +549,12 @@
icon_state = "heal"
duration = 15
-/obj/effect/overlay/temp/heal/New(loc, colour)
- ..()
- pixel_x = rand(-12, 12)
- pixel_y = rand(-9, 0)
+/obj/effect/overlay/temp/heal/Initialize(mapload, colour)
if(colour)
color = colour
+ . = ..()
+ pixel_x = rand(-12, 12)
+ pixel_y = rand(-9, 0)
/obj/effect/overlay/temp/kinetic_blast
name = "kinetic explosion"
@@ -558,14 +586,14 @@
icon_state = "impact_bullet"
duration = 5
-/obj/effect/overlay/temp/impact_effect/New(loc, atom/target, obj/item/projectile/P)
+/obj/effect/overlay/temp/impact_effect/Initialize(mapload, atom/target, obj/item/projectile/P)
if(target == P.original) //the projectile hit the target originally clicked
pixel_x = P.p_x + target.pixel_x - 16 + rand(-4,4)
pixel_y = P.p_y + target.pixel_y - 16 + rand(-4,4)
else
pixel_x = target.pixel_x + rand(-4,4)
pixel_y = target.pixel_y + rand(-4,4)
- ..()
+ . = ..()
/obj/effect/overlay/temp/impact_effect/red_laser
icon_state = "impact_laser"
@@ -591,6 +619,18 @@
icon_state = "shieldsparkles"
duration = 6
+/obj/effect/overlay/temp/heart
+ name = "heart"
+ icon = 'icons/mob/animal.dmi'
+ icon_state = "heart"
+ duration = 25
+
+/obj/effect/overlay/temp/heart/Initialize(mapload)
+ . = ..()
+ pixel_x = rand(-4,4)
+ pixel_y = rand(-4,4)
+
+ animate(src, pixel_y = pixel_y + 32, alpha = 0, time = 25)
/obj/effect/overlay/palmtree_r
name = "Palm tree"
diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm
index e8c86390ea..2290456d78 100644
--- a/code/game/objects/effects/portals.dm
+++ b/code/game/objects/effects/portals.dm
@@ -63,7 +63,7 @@
return
if (istype(M, /atom/movable))
if(ismegafauna(M))
- message_admins("[M] (FLW) has teleported through [src].")
+ message_admins("[M] [ADMIN_FLW(M)] has teleported through [src].")
do_teleport(M, target, precision) ///You will appear adjacent to the beacon
diff --git a/code/game/objects/effects/proximity.dm b/code/game/objects/effects/proximity.dm
index 8a6893c887..bfa6cd8fa9 100644
--- a/code/game/objects/effects/proximity.dm
+++ b/code/game/objects/effects/proximity.dm
@@ -9,10 +9,12 @@
host = _host
last_host_loc = _host.loc
ignore_if_not_on_turf = _ignore_if_not_on_turf
+ checkers = list()
SetRange(range)
/datum/proximity_monitor/Destroy()
host = null
+ last_host_loc = null
QDEL_LIST(checkers)
return ..()
@@ -34,46 +36,47 @@
current_range = range
- var/list/old_checkers = checkers
- var/old_checkers_len = LAZYLEN(old_checkers)
+ var/list/checkers_local = checkers
+ var/old_checkers_len = checkers_local.len
- var/atom/host_loc = host.loc
+ var/atom/_host = host
- var/atom/loc_to_use = ignore_if_not_on_turf ? host_loc : get_turf(host)
+ var/atom/loc_to_use = ignore_if_not_on_turf ? _host.loc : get_turf(_host)
if(!isturf(loc_to_use)) //only check the host's loc
if(range)
var/obj/effect/abstract/proximity_checker/pc
if(old_checkers_len)
- pc = old_checkers[old_checkers_len]
- --old_checkers.len
+ pc = checkers_local[old_checkers_len]
+ --checkers_local.len
+ QDEL_LIST(checkers_local)
else
- pc = new(host_loc, src)
+ pc = new(loc_to_use, src)
- checkers = list(pc) //only check the host's loc
+ checkers_local += pc //only check the host's loc
return
var/list/turfs = RANGE_TURFS(range, loc_to_use)
- var/old_checkers_used = min(turfs.len, old_checkers_len)
+ var/turfs_len = turfs.len
+ var/old_checkers_used = min(turfs_len, old_checkers_len)
//reuse what we can
for(var/I in 1 to old_checkers_len)
if(I <= old_checkers_used)
- var/obj/effect/abstract/proximity_checker/pc = old_checkers[I]
+ var/obj/effect/abstract/proximity_checker/pc = checkers_local[I]
pc.loc = turfs[I]
else
- qdel(old_checkers[I]) //delete the leftovers
+ qdel(checkers_local[I]) //delete the leftovers
- LAZYCLEARLIST(old_checkers)
-
- //create what we lack
- var/list/checkers_local = list()
- for(var/I in (old_checkers_used + 1) to turfs.len)
- checkers_local += new /obj/effect/abstract/proximity_checker(turfs[I], src)
-
- checkers = checkers_local
+ if(old_checkers_len < turfs_len)
+ //create what we lack
+ for(var/I in (old_checkers_used + 1) to turfs_len)
+ checkers_local += new /obj/effect/abstract/proximity_checker(turfs[I], src)
+ else
+ checkers_local.Cut(old_checkers_used + 1, old_checkers_len)
/obj/effect/abstract/proximity_checker
invisibility = INVISIBILITY_ABSTRACT
+ anchored = TRUE
var/datum/proximity_monitor/monitor
/obj/effect/abstract/proximity_checker/Initialize(mapload, datum/proximity_monitor/_monitor)
@@ -81,8 +84,8 @@
if(_monitor)
monitor = _monitor
else
- stack_trace("proximity_checker created without proximity_monitor")
- qdel(src)
+ stack_trace("proximity_checker created without host")
+ return INITIALIZE_HINT_QDEL
/obj/effect/abstract/proximity_checker/Destroy()
monitor = null
@@ -90,9 +93,6 @@
/obj/effect/abstract/proximity_checker/Crossed(atom/movable/AM)
set waitfor = FALSE
- var/datum/proximity_monitor/M = monitor
- if(!M.current_range)
- return
- var/atom/H = M.host
+ var/atom/H = monitor.host
testing("HasProx: [H] -> [AM]")
H.HasProximity(AM)
diff --git a/code/game/objects/explosion.dm b/code/game/objects/explosion.dm
index 07ffe61689..11079bf847 100644
--- a/code/game/objects/explosion.dm
+++ b/code/game/objects/explosion.dm
@@ -1,10 +1,15 @@
-/proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1, ignorecap = 0, flame_range = 0 ,silent = 0, smoke = 1)
+/proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1, ignorecap = 0, flame_range, silent = 0, smoke = 1)
set waitfor = 0
src = null //so we don't abort once src is deleted
epicenter = get_turf(epicenter)
if(!epicenter)
return
+ if(isnull(flame_range))
+ flame_range = light_impact_range
+ if(isnull(flash_range))
+ flash_range = devastation_range
+
// Archive the uncapped explosion for the doppler array
var/orig_dev_range = devastation_range
var/orig_heavy_range = heavy_impact_range
diff --git a/code/game/objects/items.dm.rej b/code/game/objects/items.dm.rej
deleted file mode 100644
index 7366e22899..0000000000
--- a/code/game/objects/items.dm.rej
+++ /dev/null
@@ -1,10 +0,0 @@
-diff a/code/game/objects/items.dm b/code/game/objects/items.dm (rejected hunks)
-@@ -102,7 +102,7 @@ var/global/image/fire_overlay = image("icon" = 'icons/effects/fire.dmi', "icon_s
- /obj/item/Initialize()
- if (!materials)
- materials = list()
-- ..()
-+ . = ..()
- for(var/path in actions_types)
- new path(src)
- actions_types = null
diff --git a/code/game/objects/items/charter.dm b/code/game/objects/items/charter.dm
index 41a793df30..36efee0092 100644
--- a/code/game/objects/items/charter.dm
+++ b/code/game/objects/items/charter.dm
@@ -7,6 +7,7 @@
desc = "An official document entrusting the governance of the station \
and surrounding space to the Captain. "
var/used = FALSE
+ var/name_type = "station"
var/unlimited_uses = FALSE
var/ignores_timeout = FALSE
@@ -33,10 +34,10 @@
/obj/item/station_charter/attack_self(mob/living/user)
if(used)
- to_chat(user, "This charter has already been used to name the station.")
+ to_chat(user, "The [name_type] has already been named.")
return
if(!ignores_timeout && (world.time-SSticker.round_start_time > STATION_RENAME_TIME_LIMIT)) //5 minutes
- to_chat(user, "The crew has already settled into the shift. It probably wouldn't be good to rename the station right now.")
+ to_chat(user, "The crew has already settled into the shift. It probably wouldn't be good to rename the [name_type] right now.")
return
if(response_timer_id)
to_chat(user, "You're still waiting for approval from your employers about your proposed name change, it'd be best to wait for now.")
@@ -60,7 +61,7 @@
to_chat(user, "Your name has been sent to your employers for approval.")
// Autoapproves after a certain time
response_timer_id = addtimer(CALLBACK(src, .proc/rename_station, new_name, user.name, user.real_name, key_name(user)), approval_time, TIMER_STOPPABLE)
- to_chat(GLOB.admins, "CUSTOM STATION RENAME:[key_name_admin(user)] (?) proposes to rename the station to [new_name] (will autoapprove in [approval_time / 10] seconds). [ADMIN_SMITE(user)] (REJECT) (RPLY)")
+ to_chat(GLOB.admins, "CUSTOM STATION RENAME:[ADMIN_LOOKUPFLW(user)] proposes to rename the [name_type] to [new_name] (will autoapprove in [approval_time / 10] seconds). [ADMIN_SMITE(user)] (REJECT) [ADMIN_CENTCOM_REPLY(user)]")
/obj/item/station_charter/proc/reject_proposed(user)
if(!user)
@@ -86,7 +87,7 @@
name = "station charter for [station_name()]"
desc = "An official document entrusting the governance of \
[station_name()] and surrounding space to Captain [uname]."
- feedback_set_details("station_renames","[station_name()]")
+ SSblackbox.set_details("station_renames","[station_name()]")
if(!unlimited_uses)
used = TRUE
@@ -94,4 +95,27 @@
unlimited_uses = TRUE
ignores_timeout = TRUE
+
+/obj/item/weapon/station_charter/flag
+ name = "nanotrasen banner"
+ icon = 'icons/obj/items.dmi'
+ var/name_type = "planet"
+ icon_state = "banner"
+ item_state = "banner"
+ desc = "A cunning device used to claim ownership of planets."
+ w_class = 5
+ force = 15
+
+/obj/item/station_charter/flag/rename_station(designation, uname, ureal_name, ukey)
+ set_station_name(designation)
+ minor_announce("[ureal_name] has designated the planet as [station_name()]", "Captain's Banner", 0)
+ log_game("[ukey] has renamed the planet as [station_name()].")
+ name = "banner of [station_name()]"
+ desc = "The banner bears the official coat of arms of Nanotrasen, signifying that [station_name()] has been claimed by Captain [uname] in the name of the company."
+ SSblackbox.set_details("station_renames","[station_name()]")
+ if(!unlimited_uses)
+ used = TRUE
+
+
+
#undef STATION_RENAME_TIME_LIMIT
diff --git a/code/game/objects/items/devices/PDA/PDA_types.dm b/code/game/objects/items/devices/PDA/PDA_types.dm
index 4d46635847..6adc4efe22 100644
--- a/code/game/objects/items/devices/PDA/PDA_types.dm
+++ b/code/game/objects/items/devices/PDA/PDA_types.dm
@@ -161,11 +161,11 @@
icon_state = "pda-roboticist"
default_cartridge = /obj/item/weapon/cartridge/roboticist
-/obj/item/device/pda/librarian
- name = "librarian PDA"
+/obj/item/device/pda/curator
+ name = "curator PDA"
icon_state = "pda-library"
icon_alert = "pda-r-library"
- default_cartridge = /obj/item/weapon/cartridge/librarian
+ default_cartridge = /obj/item/weapon/cartridge/curator
desc = "A portable microcomputer by Thinktronic Systems, LTD. This model is a WGW-11 series e-reader."
note = "Congratulations, your station has chosen the Thinktronic 5290 WGW-11 Series E-reader and Personal Data Assistant!"
silent = 1 //Quiet in the library!
diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm
index 1fd52cf5d9..6f3491ac9c 100644
--- a/code/game/objects/items/devices/PDA/cart.dm
+++ b/code/game/objects/items/devices/PDA/cart.dm
@@ -108,7 +108,7 @@
access_mime = 1
var/mime_charges = 5
-/obj/item/weapon/cartridge/librarian
+/obj/item/weapon/cartridge/curator
name = "\improper Lib-Tweet cartridge"
icon_state = "cart-s"
access_newscaster = 1
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index 496702aa4d..9131c02240 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -37,9 +37,9 @@
return 1
-/obj/item/device/flashlight/attack(mob/living/carbon/human/M, mob/living/carbon/human/user)
+/obj/item/device/flashlight/attack(mob/living/carbon/M, mob/living/carbon/human/user)
add_fingerprint(user)
- if(on && user.zone_selected == "eyes")
+ if(istype(M) && on && user.zone_selected in list("eyes", "mouth"))
if((user.disabilities & CLUMSY || user.getBrainLoss() >= 60) && prob(50)) //too dumb to use flashlight properly
return ..() //just hit them in the head
@@ -48,28 +48,101 @@
to_chat(user, "You don't have the dexterity to do this!")
return
- var/mob/living/carbon/human/H = M //mob has protective eyewear
- if(ishuman(M) && ((H.head && H.head.flags_cover & HEADCOVERSEYES) || (H.wear_mask && H.wear_mask.flags_cover & MASKCOVERSEYES) || (H.glasses && H.glasses.flags_cover & GLASSESCOVERSEYES)))
- to_chat(user, "You're going to need to remove that [(H.head && H.head.flags_cover & HEADCOVERSEYES) ? "helmet" : (H.wear_mask && H.wear_mask.flags_cover & MASKCOVERSEYES) ? "mask": "glasses"] first.")
+ if(!M.get_bodypart("head"))
+ to_chat(user, "[M] doesn't have a head!")
return
- if(M == user) //they're using it on themselves
- if(M.flash_act(visual = 1))
- M.visible_message("[M] directs [src] to [M.p_their()] eyes.", "You wave the light in front of your eyes! Trippy!")
- else
- M.visible_message("[M] directs [src] to [M.p_their()] eyes.", "You wave the light in front of your eyes.")
- else
- user.visible_message("[user] directs [src] to [M]'s eyes.", \
- "You direct [src] to [M]'s eyes.")
- var/mob/living/carbon/C = M
- if(istype(C))
- if(C.stat == DEAD || (C.disabilities & BLIND)) //mob is dead or fully blind
- to_chat(user, "[C] pupils don't react to the light!")
- else if(C.dna.check_mutation(XRAY)) //mob has X-RAY vision
- to_chat(user, "[C] pupils give an eerie glow!")
- else //they're okay!
- if(C.flash_act(visual = 1))
- to_chat(user, "[C]'s pupils narrow.")
+ switch(user.zone_selected)
+ if("eyes")
+ if((M.head && M.head.flags_cover & HEADCOVERSEYES) || (M.wear_mask && M.wear_mask.flags_cover & MASKCOVERSEYES) || (M.glasses && M.glasses.flags_cover & GLASSESCOVERSEYES))
+ to_chat(user, "You're going to need to remove that [(M.head && M.head.flags_cover & HEADCOVERSEYES) ? "helmet" : (M.wear_mask && M.wear_mask.flags_cover & MASKCOVERSEYES) ? "mask": "glasses"] first.")
+ return
+
+ var/obj/item/organ/eyes/E = M.getorganslot("eye_sight")
+ if(!E)
+ to_chat(user, "[M] doesn't have any eyes!")
+ return
+
+ if(M == user) //they're using it on themselves
+ if(M.flash_act(visual = 1))
+ M.visible_message("[M] directs [src] to [M.p_their()] eyes.", "You wave the light in front of your eyes! Trippy!")
+ else
+ M.visible_message("[M] directs [src] to [M.p_their()] eyes.", "You wave the light in front of your eyes.")
+ else
+ user.visible_message("[user] directs [src] to [M]'s eyes.", \
+ "You direct [src] to [M]'s eyes.")
+ if(M.stat == DEAD || (M.disabilities & BLIND) || !M.flash_act(visual = 1)) //mob is dead or fully blind
+ to_chat(user, "[M]'s pupils don't react to the light!")
+ else if(M.dna && M.dna.check_mutation(XRAY)) //mob has X-RAY vision
+ to_chat(user, "[M]'s pupils give an eerie glow!")
+ else //they're okay!
+ to_chat(user, "[M]'s pupils narrow.")
+
+ if("mouth")
+
+ if((M.head && M.head.flags_cover & HEADCOVERSMOUTH) || (M.wear_mask && M.wear_mask.flags_cover & MASKCOVERSMOUTH))
+ to_chat(user, "You're going to need to remove that [(M.head && M.head.flags_cover & HEADCOVERSMOUTH) ? "helmet" : "mask"] first.")
+ return
+
+ var/their = M.p_their()
+
+ var/list/mouth_organs = new
+ for(var/obj/item/organ/O in M.internal_organs)
+ if(O.zone == "mouth")
+ mouth_organs.Add(O)
+ var/organ_list = ""
+ var/organ_count = LAZYLEN(mouth_organs)
+ if(organ_count)
+ for(var/I in 1 to organ_count)
+ if(I > 1)
+ if(I == mouth_organs.len)
+ organ_list += ", and "
+ else
+ organ_list += ", "
+ var/obj/item/organ/O = mouth_organs[I]
+ organ_list += (O.gender == "plural" ? O.name : "\an [O.name]")
+
+ var/pill_count = 0
+ for(var/datum/action/item_action/hands_free/activate_pill/AP in M.actions)
+ pill_count++
+
+ if(M == user)
+ var/can_use_mirror = FALSE
+ if(isturf(user.loc))
+ var/obj/structure/mirror/mirror = locate(/obj/structure/mirror, user.loc)
+ if(mirror)
+ switch(user.dir)
+ if(NORTH)
+ can_use_mirror = mirror.pixel_y > 0
+ if(SOUTH)
+ can_use_mirror = mirror.pixel_y < 0
+ if(EAST)
+ can_use_mirror = mirror.pixel_x > 0
+ if(WEST)
+ can_use_mirror = mirror.pixel_x < 0
+
+ M.visible_message("[M] directs [src] to [their] mouth.", \
+ "You point [src] into your mouth.")
+ if(!can_use_mirror)
+ to_chat(user, "You can't see anything without a mirror.")
+ return
+ if(organ_count)
+ to_chat(user, "Inside your mouth [organ_count > 1 ? "are" : "is"] [organ_list].")
+ else
+ to_chat(user, "There's nothing inside your mouth.")
+ if(pill_count)
+ to_chat(user, "You have [pill_count] implanted pill[pill_count > 1 ? "s" : ""].")
+
+ else
+ user.visible_message("[user] directs [src] to [M]'s mouth.",\
+ "You direct [src] to [M]'s mouth.")
+ if(organ_count)
+ to_chat(user, "Inside [their] mouth [organ_count > 1 ? "are" : "is"] [organ_list].")
+ else
+ to_chat(user, "[M] doesn't have any organs in [their] mouth.")
+ if(pill_count)
+ to_chat(user, "[M] has [pill_count] pill[pill_count > 1 ? "s" : ""] implanted in [their] teeth.")
+
else
return ..()
@@ -100,8 +173,8 @@
icon_state = "medi_holo"
duration = 30
-/obj/effect/overlay/temp/medical_holosign/New(loc, creator)
- ..()
+/obj/effect/overlay/temp/medical_holosign/Initialize(mapload, creator)
+ . = ..()
playsound(loc, 'sound/machines/ping.ogg', 50, 0) //make some noise!
if(creator)
visible_message("[creator] created a medical hologram!")
@@ -280,7 +353,7 @@
return TRUE
/obj/item/device/flashlight/emp/attack(mob/living/M, mob/living/user)
- if(on && user.zone_selected == "eyes") // call original attack proc only if aiming at the eyes
+ if(on && user.zone_selected in list("eyes", "mouth")) // call original attack when examining organs
..()
return
diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm
index 00e221918c..e1cf6bb8c8 100644
--- a/code/game/objects/items/devices/gps.dm
+++ b/code/game/objects/items/devices/gps.dm
@@ -7,10 +7,14 @@ GLOBAL_LIST_EMPTY(GPS_list)
w_class = WEIGHT_CLASS_SMALL
slot_flags = SLOT_BELT
origin_tech = "materials=2;magnets=1;bluespace=2"
+ unique_rename = TRUE
var/gpstag = "COM0"
var/emped = FALSE
var/turf/locked_location
var/tracking = TRUE
+ var/updating = TRUE //Automatic updating of GPS list. Can be set to manual by user.
+ var/global_mode = TRUE //If disabled, only GPS signals of the same Z level are shown
+
/obj/item/device/gps/Initialize()
..()
@@ -27,6 +31,7 @@ GLOBAL_LIST_EMPTY(GPS_list)
cut_overlay("working")
add_overlay("emp")
addtimer(CALLBACK(src, .proc/reboot), 300, TIMER_OVERRIDE) //if a new EMP happens, remove the old timer so it doesn't reactivate early
+ SStgui.close_uis(src) //Close the UI control if it is open.
/obj/item/device/gps/proc/reboot()
emped = FALSE
@@ -34,6 +39,9 @@ GLOBAL_LIST_EMPTY(GPS_list)
add_overlay("working")
/obj/item/device/gps/AltClick(mob/user)
+ toggletracking(user)
+
+/obj/item/device/gps/proc/toggletracking(mob/user)
if(!user.canUseTopic(src, be_close=TRUE))
return //user not valid to use gps
if(emped)
@@ -48,45 +56,89 @@ GLOBAL_LIST_EMPTY(GPS_list)
to_chat(user, "[src] is now tracking, and visible to other GPS devices.")
tracking = TRUE
-/obj/item/device/gps/attack_self(mob/user)
- if(!tracking)
- to_chat(user, "[src] is turned off. Use alt+click to toggle it back on.")
- return
- var/obj/item/device/gps/t = ""
- var/gps_window_height = 110 + GLOB.GPS_list.len * 20 // Variable window height, depending on how many GPS units there are to show
+/obj/item/device/gps/ui_interact(mob/user, ui_key = "gps", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) // Remember to use the appropriate state.
if(emped)
- t += "ERROR"
- else
- t += "
Set Tag "
- t += "
Tag: [gpstag]"
- if(locked_location && locked_location.loc)
- t += "
Bluespace coordinates saved: [locked_location.loc]"
- gps_window_height += 20
-
- for(var/obj/item/device/gps/G in GLOB.GPS_list)
- var/turf/pos = get_turf(G)
- var/area/gps_area = get_area(G)
- var/tracked_gpstag = G.gpstag
- if(G.emped == 1)
- t += "
[tracked_gpstag]: ERROR"
- else if(G.tracking)
- t += "
[tracked_gpstag]: [format_text(gps_area.name)] ([pos.x], [pos.y], [pos.z])"
- else
- continue
- var/datum/browser/popup = new(user, "GPS", name, 360, min(gps_window_height, 800))
- popup.set_content(t)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
- popup.open()
+ to_chat(user, "[src] fizzles weakly.")
+ return
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ var/gps_window_height = 300 + GLOB.GPS_list.len * 20 // Variable window height, depending on how many GPS units there are to show
+ ui = new(user, src, ui_key, "gps", "Global Positioning System", 600, gps_window_height, master_ui, state) //width, height
+ ui.open()
+
+ ui.set_autoupdate(state = updating)
+
+
+/obj/item/device/gps/ui_data(mob/user)
+ var/list/data = list()
+ data["power"] = tracking
+ data["tag"] = gpstag
+ data["updating"] = updating
+ data["globalmode"] = global_mode
+ if(!tracking || emped) //Do not bother scanning if the GPS is off or EMPed
+ return data
+
+ var/turf/curr = get_turf(src)
+ data["current"] = "[get_area_name(curr)] ([curr.x], [curr.y], [curr.z])"
+
+ var/list/signals = list()
+ data["signals"] = list()
+
+ for(var/gps in GLOB.GPS_list)
+ var/obj/item/device/gps/G = gps
+ if(G.emped || !G.tracking || G == src)
+ continue
+ var/turf/pos = get_turf(G)
+ if(!global_mode && pos.z != curr.z)
+ continue
+ var/area/gps_area = get_area_name(G)
+ var/list/signal = list()
+ signal["entrytag"] = G.gpstag //Name or 'tag' of the GPS
+ signal["area"] = format_text(gps_area)
+ signal["coord"] = "[pos.x], [pos.y], [pos.z]"
+ if(pos.z == curr.z) //Distance/Direction calculations for same z-level only
+ signal["dist"] = max(get_dist(curr, pos), 0) //Distance between the src and remote GPS turfs
+ signal["degrees"] = round(Get_Angle(curr, pos)) //0-360 degree directional bearing, for more precision.
+ var/direction = uppertext(dir2text(get_dir(curr, pos))) //Direction text (East, etc). Not as precise, but still helpful.
+ if(!direction)
+ direction = "CENTER"
+ signal["degrees"] = "N/A"
+ signal["direction"] = direction
+
+ signals += list(signal) //Add this signal to the list of signals
+ data["signals"] = signals
+ return data
+
+
+
+/obj/item/device/gps/ui_act(action, params)
+ if(..())
+ return
+ switch(action)
+ if("rename")
+ var/a = input("Please enter desired tag.", name, gpstag) as text
+ a = uppertext(copytext(sanitize(a), 1, 5))
+ gpstag = a
+ name = "global positioning system ([gpstag])"
+ . = TRUE
+ if("power")
+ toggletracking(usr)
+ . = TRUE
+ if("updating")
+ updating = !updating
+ . = TRUE
+ if("globalmode")
+ global_mode = !global_mode
+ . = TRUE
/obj/item/device/gps/Topic(href, href_list)
..()
if(href_list["tag"] )
var/a = input("Please enter desired tag.", name, gpstag) as text
- a = uppertext(copytext(sanitize(a), 1, 5))
+ a = copytext(sanitize(a), 1, 20)
if(in_range(src, usr))
gpstag = a
- name = "global positioning system ([gpstag])"
attack_self(usr)
/obj/item/device/gps/science
@@ -157,4 +209,4 @@ GLOBAL_LIST_EMPTY(GPS_list)
clear()
tagged = null
STOP_PROCESSING(SSfastprocess, src)
- . = ..()
+ . = ..()
\ No newline at end of file
diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm
index 18658ceeb3..f7d11d2f47 100644
--- a/code/game/objects/items/devices/powersink.dm
+++ b/code/game/objects/items/devices/powersink.dm
@@ -95,8 +95,8 @@
"[user] activates \the [src]!", \
"You activate \the [src].",
"You hear a click.")
- message_admins("Power sink activated by [key_name_admin(user)](?) (FLW) at ([x],[y],[z] - JMP)")
- log_game("Power sink activated by [key_name(user)] at ([x],[y],[z])")
+ message_admins("Power sink activated by [ADMIN_LOOKUPFLW(user)] at [ADMIN_COORDJMP(src)]")
+ log_game("Power sink activated by [key_name(user)] at [COORD(src)]")
set_mode(OPERATING)
if(OPERATING)
diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm
index 257418eaa7..8d37640b6d 100644
--- a/code/game/objects/items/devices/transfer_valve.dm
+++ b/code/game/objects/items/devices/transfer_valve.dm
@@ -178,21 +178,21 @@
var/log_attacher = ""
if(attacher)
- log_attacher = "(?) (FLW)"
+ log_attacher = "[ADMIN_QUE(attacher)] [ADMIN_FLW(attacher)]"
var/mob/mob = get_mob_by_key(src.fingerprintslast)
var/last_touch_info = ""
if(mob)
- last_touch_info = "(?) (FLW)"
+ last_touch_info = "[ADMIN_QUE(mob)] [ADMIN_FLW(mob)]"
var/log_str3 = " Last touched by: [key_name_admin(mob)]"
- var/bomb_message = "[log_str1] [A.name] [log_str2][log_attacher] [log_str3][last_touch_info]"
+ var/bomb_message = "[log_str1] [A.name][ADMIN_JMP(bombturf)] [log_str2][log_attacher] [log_str3][last_touch_info]"
GLOB.bombers += bomb_message
message_admins(bomb_message, 0, 1)
- log_game("[log_str1] [A.name]([A.x],[A.y],[A.z]) [log_str2] [log_str3]")
+ log_game("[log_str1] [A.name][COORD(bombturf)] [log_str2] [log_str3]")
merge_gases()
spawn(20) // In case one tank bursts
for (var/i=0,i<5,i++)
diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm
index 1028ca1429..1f923ddd5e 100644
--- a/code/game/objects/items/robot/robot_parts.dm
+++ b/code/game/objects/items/robot/robot_parts.dm
@@ -58,7 +58,7 @@
if(src.l_arm && src.r_arm)
if(src.l_leg && src.r_leg)
if(src.chest && src.head)
- feedback_inc("cyborg_frames_built",1)
+ SSblackbox.inc("cyborg_frames_built",1)
return 1
return 0
@@ -235,7 +235,7 @@
O.mmi = W //and give the real mmi to the borg.
O.updatename()
- feedback_inc("cyborg_birth",1)
+ SSblackbox.inc("cyborg_birth",1)
forceMove(O)
O.robot_suit = src
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index 2aee5acdd9..29463b5a5c 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -237,6 +237,7 @@
/obj/item/borg/upgrade/selfrepair/proc/check_dropped()
if(loc != cyborg)
toggle_action.Remove(cyborg)
+ QDEL_NULL(toggle_action)
cyborg = null
deactivate()
@@ -399,4 +400,4 @@
return
R.make_shell(src)
- return TRUE
\ No newline at end of file
+ return TRUE
diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm
index 611ef76eb4..436bc5ff59 100644
--- a/code/game/objects/items/stacks/sheets/mineral.dm
+++ b/code/game/objects/items/stacks/sheets/mineral.dm
@@ -29,6 +29,7 @@ Mineral Sheets
GLOBAL_LIST_INIT(sandstone_recipes, list ( \
new/datum/stack_recipe("pile of dirt", /obj/machinery/hydroponics/soil, 3, time = 10, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("sandstone door", /obj/structure/mineral_door/sandstone, 10, one_per_turf = 1, on_floor = 1), \
+ new/datum/stack_recipe("aesthetic volcanic floor tile", /obj/item/stack/tile/basalt, 2, 2, 4, 20), \
new/datum/stack_recipe("Assistant Statue", /obj/structure/statue/sandstone/assistant, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("Breakdown into sand", /obj/item/weapon/ore/glass, 1, one_per_turf = 0, on_floor = 1), \
/* new/datum/stack_recipe("sandstone wall", ???), \
@@ -154,8 +155,9 @@ GLOBAL_LIST_INIT(plasma_recipes, list ( \
/obj/item/stack/sheet/mineral/plasma/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if(W.is_hot() > 300)//If the temperature of the object is over 300, then ignite
- message_admins("Plasma sheets ignited by [key_name_admin(user)](?) (FLW) in ([x],[y],[z] - JMP)",0,1)
- log_game("Plasma sheets ignited by [key_name(user)] in ([x],[y],[z])")
+ var/turf/T = get_turf(src)
+ message_admins("Plasma sheets ignited by [ADMIN_LOOKUPFLW(user)] in [ADMIN_COORDJMP(T)]",0,1)
+ log_game("Plasma sheets ignited by [key_name(user)] in [COORD(T)]")
fire_act()
else
return ..()
@@ -334,12 +336,20 @@ GLOBAL_LIST_INIT(snow_recipes, list ( \
/*
* Adamantine
*/
+GLOBAL_LIST_INIT(adamantine_recipes, list(
+ new /datum/stack_recipe("artificial golem shell", /obj/item/golem_shell/artificial, req_amount=1, res_amount=1),
+ ))
+
/obj/item/stack/sheet/mineral/adamantine
name = "adamantine"
icon_state = "sheet-adamantine"
singular_name = "adamantine sheet"
origin_tech = "materials=4"
+/obj/item/stack/sheet/mineral/adamantine/Initialize(mapload, new_amount, merge = TRUE)
+ recipes = GLOB.adamantine_recipes
+ ..()
+
/*
* Mythril
*/
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index a007dd4545..9657c7d888 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -286,13 +286,16 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list ( \
return
..()
-/obj/item/stack/sheet/runed_metal/fifty
- amount = 50
-
/obj/item/stack/sheet/runed_metal/Initialize(mapload, new_amount, merge = TRUE)
recipes = GLOB.runed_metal_recipes
return ..()
+/obj/item/stack/sheet/runed_metal/fifty
+ amount = 50
+
+/obj/item/stack/sheet/runed_metal/five
+ amount = 5
+
/*
* Brass
*/
@@ -328,7 +331,7 @@ GLOBAL_LIST_INIT(brass_recipes, list ( \
/obj/item/stack/tile/brass/Initialize(mapload, new_amount, merge = TRUE)
recipes = GLOB.brass_recipes
- ..()
+ . = ..()
pixel_x = 0
pixel_y = 0
diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm
index f2432cee3d..ef123030f5 100644
--- a/code/game/objects/items/stacks/tiles/tile_types.dm
+++ b/code/game/objects/items/stacks/tiles/tile_types.dm
@@ -13,8 +13,8 @@
var/turf_type = null
var/mineralType = null
-/obj/item/stack/tile/New(loc, amount)
- ..()
+/obj/item/stack/tile/Initialize(mapload, amount)
+ . = ..()
pixel_x = rand(-3, 3)
pixel_y = rand(-3, 3) //randomize a little
@@ -88,6 +88,14 @@
turf_type = /turf/open/floor/wood
resistance_flags = FLAMMABLE
+//Basalt
+/obj/item/stack/tile/basalt
+ name = "basalt tile"
+ singular_name = "basalt floor tile"
+ desc = "Artificially made ashy soil themed on a hostile enviroment."
+ icon_state = "tile_basalt"
+ origin_tech = "materials=1"
+ turf_type = /turf/open/floor/grass/fakebasalt
//Carpets
/obj/item/stack/tile/carpet
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index f332422578..0979a65981 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -1218,9 +1218,9 @@
icon_state = "lawyer"
toysay = "My client is a dirty traitor!"
-/obj/item/toy/figure/librarian
- name = "Librarian action figure"
- icon_state = "librarian"
+/obj/item/toy/figure/curator
+ name = "Curator action figure"
+ icon_state = "curator"
toysay = "One day while..."
/obj/item/toy/figure/md
diff --git a/code/game/objects/items/weapons/clown.dm b/code/game/objects/items/weapons/clown.dm
new file mode 100644
index 0000000000..a01a0f18b6
--- /dev/null
+++ b/code/game/objects/items/weapons/clown.dm
@@ -0,0 +1,46 @@
+
+/obj/item/weapon/pie_cannon
+ name = "pie cannon"
+ desc = "Load cream pie for optimal results"
+ force = 10
+ icon_state = "piecannon"
+ item_state = "powerfist"
+ var/obj/item/weapon/reagent_containers/food/snacks/pie/loaded = null
+
+/obj/item/weapon/pie_cannon/attackby(obj/item/I, mob/living/L)
+ if(istype(I, /obj/item/weapon/reagent_containers/food/snacks/pie))
+ if(!loaded)
+ L.transferItemToLoc(I, src)
+ loaded = I
+ to_chat(L, "You load the [I] into the [src]!")
+ return
+ return ..()
+
+/obj/item/weapon/pie_cannon/afterattack(atom/target, mob/living/user, flag, params)
+ if(!loaded)
+ return ..()
+ var/obj/item/projectile/pie/launched = new /obj/item/projectile/pie(src)
+ launched.P = loaded
+ loaded.forceMove(launched)
+ launched.appearance = loaded.appearance
+ loaded = null
+ launched.preparePixelProjectile(target, get_turf(target), user, params, 0)
+ launched.forceMove(get_turf(src))
+ launched.fire()
+ user.visible_message("[user] fires the [src] at [target]!")
+
+/obj/item/projectile/pie
+ name = "pie"
+ desc = "Think fast!"
+ var/obj/item/weapon/reagent_containers/food/snacks/pie/P = null
+
+/obj/item/projectile/pie/on_hit(atom/A)
+ . = ..()
+ if(P)
+ A.visible_message("[P] smashes into [A] at high velocity!")
+ P.forceMove(get_turf(A))
+ P.throw_impact(A)
+ if(ismovableatom(A))
+ var/atom/movable/AM = A
+ if(!AM.anchored)
+ AM.throw_at(get_edge_target_turf(get_dir(src, AM), 3, 2))
diff --git a/code/game/objects/items/weapons/explosives.dm b/code/game/objects/items/weapons/explosives.dm
index 756512dff1..2ae93fa774 100644
--- a/code/game/objects/items/weapons/explosives.dm
+++ b/code/game/objects/items/weapons/explosives.dm
@@ -81,7 +81,7 @@
to_chat(user, "You start planting the bomb...")
- if(do_after(user, 50, target = AM))
+ if(do_after(user, 50, target = AM))
if(!user.temporarilyRemoveItemFromInventory(src))
return
src.target = AM
diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm
index ac53eb25ff..aab6c9db48 100644
--- a/code/game/objects/items/weapons/flamethrower.dm
+++ b/code/game/objects/items/weapons/flamethrower.dm
@@ -158,7 +158,7 @@
if(lit)
START_PROCESSING(SSobj, src)
if(!warned_admins)
- message_admins("[key_name_admin(usr)]? (FLW) has lit a flamethrower.")
+ message_admins("[ADMIN_LOOKUPFLW(usr)] has lit a flamethrower.")
warned_admins = 1
if(href_list["amount"])
throw_amount = throw_amount + text2num(href_list["amount"])
diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm
index 9ecd9809af..42362c1e55 100644
--- a/code/game/objects/items/weapons/grenades/chem_grenade.dm
+++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm
@@ -19,10 +19,10 @@
var/threatscale = 1 // Used by advanced grenades to make them slightly more worthy.
var/no_splash = FALSE //If the grenade deletes even if it has no reagents to splash with. Used for slime core reactions.
-/obj/item/weapon/grenade/chem_grenade/New()
+/obj/item/weapon/grenade/chem_grenade/Initialize()
+ . = ..()
create_reagents(1000)
stage_change() // If no argument is set, it will change the stage to the current stage, useful for stock grenades that start READY.
- ..()
/obj/item/weapon/grenade/chem_grenade/examine(mob/user)
display_timer = (stage == READY && !nadeassembly) //show/hide the timer based on assembly state
@@ -36,8 +36,8 @@
else if(clown_check(user))
var/turf/bombturf = get_turf(src)
var/area/A = get_area(bombturf)
- message_admins("[key_name_admin(usr)]? (FLW) has primed a [name] for detonation at [A.name] (JMP).")
- log_game("[key_name(usr)] has primed a [name] for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]).")
+ message_admins("[ADMIN_LOOKUPFLW(usr)] has primed a [name] for detonation at [A.name][ADMIN_JMP(bombturf)].")
+ log_game("[key_name(usr)] has primed a [name] for detonation at [A.name] [COORD(bombturf)].")
to_chat(user, "You prime the [name]! [det_time / 10] second\s!")
playsound(user.loc, 'sound/weapons/armbomb.ogg', 60, 1)
active = 1
@@ -177,12 +177,12 @@
var/mob/last = get_mob_by_ckey(nadeassembly.fingerprintslast)
var/turf/T = get_turf(src)
var/area/A = get_area(T)
- message_admins("grenade primed by an assembly, attached by [key_name_admin(M)](?) (FLW) and last touched by [key_name_admin(last)](?) (FLW) ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] (JMP).")
- log_game("grenade primed by an assembly, attached by [key_name(M)] and last touched by [key_name(last)] ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] ([T.x], [T.y], [T.z])")
+ message_admins("grenade primed by an assembly, attached by [ADMIN_LOOKUPFLW(M)] and last touched by [ADMIN_LOOKUPFLW(last)] ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] [ADMIN_JMP(T)].")
+ log_game("grenade primed by an assembly, attached by [key_name(M)] and last touched by [key_name(last)] ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] [COORD(T)]")
var/turf/DT = get_turf(src)
var/area/DA = get_area(DT)
- log_game("A grenade detonated at [DA.name] ([DT.x], [DT.y], [DT.z])")
+ log_game("A grenade detonated at [DA.name] [COORD(DT)]")
update_mob()
@@ -313,8 +313,8 @@
desc = "Used for emergency sealing of air breaches."
stage = READY
-/obj/item/weapon/grenade/chem_grenade/metalfoam/New()
- ..()
+/obj/item/weapon/grenade/chem_grenade/metalfoam/Initialize()
+ . = ..()
var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src)
var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src)
@@ -331,8 +331,8 @@
desc = "Used for clearing rooms of living things."
stage = READY
-/obj/item/weapon/grenade/chem_grenade/incendiary/New()
- ..()
+/obj/item/weapon/grenade/chem_grenade/incendiary/Initialize()
+ . = ..()
var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src)
var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src)
@@ -349,8 +349,8 @@
desc = "Used for purging large areas of invasive plant species. Contents under pressure. Do not directly inhale contents."
stage = READY
-/obj/item/weapon/grenade/chem_grenade/antiweed/New()
- ..()
+/obj/item/weapon/grenade/chem_grenade/antiweed/Initialize()
+ . = ..()
var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src)
var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src)
@@ -368,8 +368,8 @@
desc = "BLAM!-brand foaming space cleaner. In a special applicator for rapid cleaning of wide areas."
stage = READY
-/obj/item/weapon/grenade/chem_grenade/cleaner/New()
- ..()
+/obj/item/weapon/grenade/chem_grenade/cleaner/Initialize()
+ . = ..()
var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src)
var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src)
@@ -386,8 +386,8 @@
desc = "Waffle Co.-brand foaming space cleaner. In a special applicator for rapid cleaning of wide areas."
stage = READY
-/obj/item/weapon/grenade/chem_grenade/ez_clean/New()
- ..()
+/obj/item/weapon/grenade/chem_grenade/ez_clean/Initialize()
+ . = ..()
var/obj/item/weapon/reagent_containers/glass/beaker/large/B1 = new(src)
var/obj/item/weapon/reagent_containers/glass/beaker/large/B2 = new(src)
@@ -405,8 +405,8 @@
desc = "Used for nonlethal riot control. Contents under pressure. Do not directly inhale contents."
stage = READY
-/obj/item/weapon/grenade/chem_grenade/teargas/New()
- ..()
+/obj/item/weapon/grenade/chem_grenade/teargas/Initialize()
+ . = ..()
var/obj/item/weapon/reagent_containers/glass/beaker/large/B1 = new(src)
var/obj/item/weapon/reagent_containers/glass/beaker/large/B2 = new(src)
@@ -424,8 +424,8 @@
desc = "Used for melting armoured opponents."
stage = READY
-/obj/item/weapon/grenade/chem_grenade/facid/New()
- ..()
+/obj/item/weapon/grenade/chem_grenade/facid/Initialize()
+ . = ..()
var/obj/item/weapon/reagent_containers/glass/beaker/bluespace/B1 = new(src)
var/obj/item/weapon/reagent_containers/glass/beaker/bluespace/B2 = new(src)
@@ -444,8 +444,8 @@
desc = "Used for wide scale painting projects."
stage = READY
-/obj/item/weapon/grenade/chem_grenade/colorful/New()
- ..()
+/obj/item/weapon/grenade/chem_grenade/colorful/Initialize()
+ . = ..()
var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src)
var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src)
@@ -463,8 +463,8 @@
stage = READY
var/glitter_type = "glitter"
-/obj/item/weapon/grenade/chem_grenade/glitter/New()
- ..()
+/obj/item/weapon/grenade/chem_grenade/glitter/Initialize()
+ . = ..()
var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src)
var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src)
@@ -496,8 +496,8 @@
desc = "BURN!-brand foaming clf3. In a special applicator for rapid purging of wide areas."
stage = READY
-/obj/item/weapon/grenade/chem_grenade/clf3/New()
- ..()
+/obj/item/weapon/grenade/chem_grenade/clf3/Initialize()
+ . = ..()
var/obj/item/weapon/reagent_containers/glass/beaker/bluespace/B1 = new(src)
var/obj/item/weapon/reagent_containers/glass/beaker/bluespace/B2 = new(src)
@@ -514,8 +514,8 @@
desc = "Tiger Cooperative chemical foam grenade. Causes temporary irration, blindness, confusion, mutism, and mutations to carbon based life forms. Contains additional spore toxin"
stage = READY
-/obj/item/weapon/grenade/chem_grenade/bioterrorfoam/New()
- ..()
+/obj/item/weapon/grenade/chem_grenade/bioterrorfoam/Initialize()
+ . = ..()
var/obj/item/weapon/reagent_containers/glass/beaker/bluespace/B1 = new(src)
var/obj/item/weapon/reagent_containers/glass/beaker/bluespace/B2 = new(src)
@@ -534,8 +534,8 @@
desc = "WARNING: GRENADE WILL RELEASE DEADLY SPORES CONTAINING ACTIVE AGENTS. SEAL SUIT AND AIRFLOW BEFORE USE."
stage = READY
-/obj/item/weapon/grenade/chem_grenade/tuberculosis/New()
- ..()
+/obj/item/weapon/grenade/chem_grenade/tuberculosis/Initialize()
+ . = ..()
var/obj/item/weapon/reagent_containers/glass/beaker/bluespace/B1 = new(src)
var/obj/item/weapon/reagent_containers/glass/beaker/bluespace/B2 = new(src)
diff --git a/code/game/objects/items/weapons/grenades/plastic.dm b/code/game/objects/items/weapons/grenades/plastic.dm
index aedf0954c2..09b9d6aa33 100644
--- a/code/game/objects/items/weapons/grenades/plastic.dm
+++ b/code/game/objects/items/weapons/grenades/plastic.dm
@@ -96,7 +96,7 @@
to_chat(user, "You start planting the [src]. The timer is set to [det_time]...")
- if(do_after(user, 50, target = AM))
+ if(do_after(user, 30, target = AM))
if(!user.temporarilyRemoveItemFromInventory(src))
return
src.target = AM
@@ -119,8 +119,8 @@
qdel(src) //How?
/obj/item/weapon/grenade/plastic/suicide_act(mob/user)
- message_admins("[key_name_admin(user)](?) (FLW) suicided with [src] at ([user.x],[user.y],[user.z] - JMP)",0,1)
- message_admins("[key_name(user)] suicided with [src] at ([user.x],[user.y],[user.z])")
+ message_admins("[ADMIN_LOOKUPFLW(user)] suicided with [src] at [ADMIN_COORDJMP(user)]",0,1)
+ log_game("[key_name(user)] suicided with [src] at [COORD(user)]")
user.visible_message("[user] activates the [src] and holds it above [user.p_their()] head! It looks like [user.p_theyre()] going out with a bang!")
var/message_say = "FOR NO RAISIN!"
if(user.mind)
@@ -165,4 +165,4 @@
icon_state = "plasticx40"
item_state = "plasticx4"
directional = TRUE
- boom_sizes = list(0, 2, 5)
\ No newline at end of file
+ boom_sizes = list(0, 2, 5)
diff --git a/code/game/objects/items/weapons/grenades/spawnergrenade.dm b/code/game/objects/items/weapons/grenades/spawnergrenade.dm
index 2d5c4707a6..86c01d745e 100644
--- a/code/game/objects/items/weapons/grenades/spawnergrenade.dm
+++ b/code/game/objects/items/weapons/grenades/spawnergrenade.dm
@@ -1,5 +1,5 @@
/obj/item/weapon/grenade/spawnergrenade
- desc = "It will unleash unleash an unspecified anomaly into the vicinity."
+ desc = "It will unleash an unspecified anomaly into the vicinity."
name = "delivery grenade"
icon = 'icons/obj/grenade.dmi'
icon_state = "delivery"
diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm
index 49cc975546..0ab74a019d 100644
--- a/code/game/objects/items/weapons/handcuffs.dm
+++ b/code/game/objects/items/weapons/handcuffs.dm
@@ -45,7 +45,7 @@
if(do_mob(user, C, 30) && (C.get_num_arms() >= 2 || C.get_arm_ignore()))
apply_cuffs(C,user)
to_chat(user, "You handcuff [C].")
- feedback_add_details("handcuffs","[type]")
+ SSblackbox.add_details("handcuffs","[type]")
add_logs(user, C, "handcuffed")
else
@@ -275,7 +275,7 @@
C.legcuffed = src
src.loc = C
C.update_inv_legcuffed()
- feedback_add_details("handcuffs","[type]")
+ SSblackbox.add_details("handcuffs","[type]")
else if(isanimal(L))
var/mob/living/simple_animal/SA = L
if(SA.mob_size > MOB_SIZE_TINY)
@@ -336,7 +336,7 @@
C.legcuffed = src
src.loc = C
C.update_inv_legcuffed()
- feedback_add_details("handcuffs","[type]")
+ SSblackbox.add_details("handcuffs","[type]")
to_chat(C, "\The [src] ensnares you!")
C.Weaken(weaken)
diff --git a/code/game/objects/items/weapons/his_grace.dm b/code/game/objects/items/weapons/his_grace.dm
index be269d583d..d8e7dabdf9 100644
--- a/code/game/objects/items/weapons/his_grace.dm
+++ b/code/game/objects/items/weapons/his_grace.dm
@@ -41,7 +41,7 @@
..()
/obj/item/weapon/his_grace/CtrlClick(mob/user) //you can't pull his grace
- attack_hand(user)
+ return
/obj/item/weapon/his_grace/examine(mob/user)
..()
diff --git a/code/game/objects/items/weapons/holy_weapons.dm b/code/game/objects/items/weapons/holy_weapons.dm
index 571aece4c3..4b5acd949d 100644
--- a/code/game/objects/items/weapons/holy_weapons.dm
+++ b/code/game/objects/items/weapons/holy_weapons.dm
@@ -39,7 +39,7 @@
SSreligion.holy_weapon_type = holy_weapon.type
- feedback_set_details("chaplain_weapon","[choice]")
+ SSblackbox.set_details("chaplain_weapon","[choice]")
if(holy_weapon)
holy_weapon.reskinned = TRUE
@@ -214,7 +214,7 @@
possessed = TRUE
- var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_POSSESSED_BLADE)
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_POSSESSED_BLADE)
var/mob/dead/observer/theghost = null
if(LAZYLEN(candidates))
diff --git a/code/game/objects/items/weapons/implants/implant_explosive.dm b/code/game/objects/items/weapons/implants/implant_explosive.dm
index 369effbe7c..009aae3b54 100644
--- a/code/game/objects/items/weapons/implants/implant_explosive.dm
+++ b/code/game/objects/items/weapons/implants/implant_explosive.dm
@@ -44,7 +44,7 @@
active = TRUE
var/turf/boomturf = get_turf(imp_in)
var/area/A = get_area(boomturf)
- message_admins("[key_name_admin(imp_in)]? (FLW) has activated their [name] at [A.name] (JMP).")
+ message_admins("[ADMIN_LOOKUPFLW(imp_in)] has activated their [name] at [A.name] [ADMIN_JMP(boomturf)].")
//If the delay is short, just blow up already jeez
if(delay <= 7)
explosion(src,heavy,medium,weak,weak, flame_range = weak)
diff --git a/code/game/objects/items/weapons/implants/implantchair.dm b/code/game/objects/items/weapons/implants/implantchair.dm
index 6f1a43a93a..a3b72d55bd 100644
--- a/code/game/objects/items/weapons/implants/implantchair.dm
+++ b/code/game/objects/items/weapons/implants/implantchair.dm
@@ -41,9 +41,10 @@
data["open"] = state_open
data["occupant"] = list()
- if(occupant)
- data["occupant"]["name"] = occupant.name
- data["occupant"]["stat"] = occupant.stat
+ if(occupant)
+ var/mob/living/mob_occupant = occupant
+ data["occupant"]["name"] = mob_occupant.name
+ data["occupant"]["stat"] = mob_occupant.stat
data["special_name"] = special ? special_name : null
data["ready_implants"] = ready_implants
diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm
index 56c53e9cd8..fd8c5483a2 100644
--- a/code/game/objects/items/weapons/melee/energy.dm
+++ b/code/game/objects/items/weapons/melee/energy.dm
@@ -16,7 +16,7 @@
var/brightness_on = 3
/obj/item/weapon/melee/energy/Initialize()
- ..()
+ . = ..()
if(LAZYLEN(possible_colors))
item_color = pick(possible_colors)
switch(item_color)//Only run this check if the color was picked randomly, so that colors can be manually set for non-random colored energy weapons.
@@ -183,8 +183,8 @@
light_color = "#40ceff"
possible_colors = null
-/obj/item/weapon/melee/energy/sword/cyborg/saw/New()
- ..()
+/obj/item/weapon/melee/energy/sword/cyborg/saw/Initialize()
+ . = ..()
icon_state = "esaw_0"
item_color = null
@@ -207,19 +207,9 @@
/obj/item/weapon/melee/energy/sword/saber/attackby(obj/item/weapon/W, mob/living/user, params)
- if(istype(W, /obj/item/weapon/melee/energy/sword/saber))
- to_chat(user, "You attach the ends of the two energy swords, making a single double-bladed weapon! You're cool.")
- var/obj/item/weapon/melee/energy/sword/saber/other_esword = W
- var/obj/item/weapon/twohanded/dualsaber/newSaber = new(user.loc)
- if(hacked || other_esword.hacked)
- newSaber.hacked = TRUE
- newSaber.item_color = "rainbow"
- qdel(W)
- qdel(src)
- user.put_in_hands(newSaber)
- else if(istype(W, /obj/item/device/multitool))
- if(hacked == 0)
- hacked = 1
+ if(istype(W, /obj/item/device/multitool))
+ if(!hacked)
+ hacked = TRUE
item_color = "rainbow"
to_chat(user, "RNBW_ENGAGE")
@@ -253,14 +243,12 @@
sharpness = IS_SHARP
//Most of the other special functions are handled in their own files. aka special snowflake code so kewl
-/obj/item/weapon/melee/energy/blade/New()
+/obj/item/weapon/melee/energy/blade/Initialize()
+ . = ..()
spark_system = new /datum/effect_system/spark_spread()
spark_system.set_up(5, 0, src)
spark_system.attach(src)
-/obj/item/weapon/melee/energy/blade/dropped()
- ..()
-
/obj/item/weapon/melee/energy/blade/attack_self(mob/user)
return
diff --git a/code/game/objects/items/weapons/pneumaticCannon.dm b/code/game/objects/items/weapons/pneumaticCannon.dm
index 61e68934b9..86c517aa99 100644
--- a/code/game/objects/items/weapons/pneumaticCannon.dm
+++ b/code/game/objects/items/weapons/pneumaticCannon.dm
@@ -1,3 +1,7 @@
+
+#define PCANNON_FIREALL 1
+#define PCANNON_FILO 2
+#define PCANNON_FIFO 3
/obj/item/weapon/pneumatic_cannon
name = "pneumatic cannon"
desc = "A gas-powered cannon that can fire any object loaded into it."
@@ -16,7 +20,15 @@
var/gasPerThrow = 3 //How much gas is drawn from a tank's pressure to fire
var/list/loadedItems = list() //The items loaded into the cannon that will be fired out
var/pressureSetting = 1 //How powerful the cannon is - higher pressure = more gas but more powerful throws
+ var/checktank = TRUE
+ var/range_multiplier = 1
+ var/throw_amount = 20 //How many items to throw per fire
+ var/fire_mode = PCANNON_FIREALL
+ var/automatic = FALSE
+ var/clumsyCheck = TRUE
+/obj/item/weapon/pneumatic_cannon/CanItemAutoclick()
+ return automatic
/obj/item/weapon/pneumatic_cannon/examine(mob/user)
..()
@@ -30,6 +42,8 @@
/obj/item/weapon/pneumatic_cannon/attackby(obj/item/weapon/W, mob/user, params)
+ if(user.a_intent == INTENT_HARM)
+ return ..()
if(istype(W, /obj/item/weapon/tank/internals))
if(!tank)
var/obj/item/weapon/tank/internals/IT = W
@@ -55,19 +69,31 @@
to_chat(user, "\The [src] can't hold any more items!")
else if(istype(W, /obj/item))
var/obj/item/IW = W
- if((loadedWeightClass + IW.w_class) > maxWeightClass)
- to_chat(user, "\The [IW] won't fit into \the [src]!")
- return
- if(IW.w_class > src.w_class)
- to_chat(user, "\The [IW] is too large to fit into \the [src]!")
- return
- if(!user.transferItemToLoc(W, src))
- return
- to_chat(user, "You load \the [IW] into \the [src].")
- loadedItems.Add(IW)
- loadedWeightClass += IW.w_class
+ load_item(IW, user)
+/obj/item/weapon/pneumatic_cannon/proc/can_load_item(obj/item/I, mob/user)
+ if((loadedWeightClass + I.w_class) > maxWeightClass) //Only make messages if there's a user
+ if(user)
+ to_chat(user, "\The [I] won't fit into \the [src]!")
+ return FALSE
+ if(I.w_class > w_class)
+ if(user)
+ to_chat(user, "\The [I] is too large to fit into \the [src]!")
+ return FALSE
+ return TRUE
+/obj/item/weapon/pneumatic_cannon/proc/load_item(obj/item/I, mob/user)
+ if(!can_load_item(I, user))
+ return FALSE
+ if(user) //Only use transfer proc if there's a user, otherwise just set loc.
+ if(!user.transferItemToLoc(I, src))
+ return FALSE
+ to_chat(user, "You load \the [I] into \the [src].")
+ else
+ I.forceMove(src)
+ loadedItems += I
+ loadedWeightClass += I.w_class
+ return TRUE
/obj/item/weapon/pneumatic_cannon/afterattack(atom/target, mob/living/carbon/human/user, flag, params)
if(flag && user.a_intent == INTENT_HARM) //melee attack
@@ -76,7 +102,6 @@
return
Fire(user, target)
-
/obj/item/weapon/pneumatic_cannon/proc/Fire(mob/living/carbon/human/user, var/atom/target)
if(!istype(user) && !target)
return
@@ -90,13 +115,13 @@
if(!loadedItems || !loadedWeightClass)
to_chat(user, "\The [src] has nothing loaded.")
return
- if(!tank)
+ if(!tank && checktank)
to_chat(user, "\The [src] can't fire without a source of gas.")
return
if(tank && !tank.air_contents.remove(gasPerThrow * pressureSetting))
to_chat(user, "\The [src] lets out a weak hiss and doesn't react!")
return
- if(user.disabilities & CLUMSY && prob(75))
+ if(user.disabilities & CLUMSY && prob(75) && clumsyCheck)
user.visible_message("[user] loses their grip on [src], causing it to go off!", "[src] slips out of your hands and goes off!")
user.drop_item()
if(prob(10))
@@ -109,17 +134,48 @@
user.visible_message("[user] fires \the [src]!", \
"You fire \the [src]!")
add_logs(user, target, "fired at", src)
+ var/turf/T = get_target(target, get_turf(src))
playsound(src.loc, 'sound/weapons/sonic_jackhammer.ogg', 50, 1)
- for(var/obj/item/ITD in loadedItems) //Item To Discharge
- loadedItems.Remove(ITD)
- loadedWeightClass -= ITD.w_class
- ITD.throw_speed = pressureSetting * 2
- ITD.loc = get_turf(src)
- ITD.throw_at(target, pressureSetting * 5, pressureSetting * 2,user)
+ fire_items(T, user)
if(pressureSetting >= 3 && user)
user.visible_message("[user] is thrown down by the force of the cannon!", "[src] slams into your shoulder, knocking you down!")
user.Weaken(3)
+/obj/item/weapon/pneumatic_cannon/proc/fire_items(turf/target, mob/user)
+ if(fire_mode == PCANNON_FIREALL)
+ for(var/obj/item/ITD in loadedItems) //Item To Discharge
+ if(!throw_item(target, ITD, user))
+ break
+ else
+ for(var/i in 1 to throw_amount)
+ if(!loadedItems.len)
+ break
+ var/obj/item/I
+ if(fire_mode == PCANNON_FILO)
+ I = loadedItems[loadedItems.len]
+ else
+ I = loadedItems[1]
+ if(!throw_item(target, I, user))
+ break
+
+/obj/item/weapon/pneumatic_cannon/proc/throw_item(turf/target, obj/item/I, mob/user)
+ if(!istype(I))
+ return FALSE
+ loadedItems -= I
+ loadedWeightClass -= I.w_class
+ I.forceMove(get_turf(src))
+ I.throw_at(target, pressureSetting * 10 * range_multiplier, pressureSetting * 2, user)
+ return TRUE
+
+/obj/item/weapon/pneumatic_cannon/proc/get_target(turf/target, turf/starting)
+ if(range_multiplier == 1)
+ return target
+ var/x_o = (target.x - starting.x)
+ var/y_o = (target.y - starting.y)
+ var/new_x = Clamp((starting.x + (x_o * range_multiplier)), 0, world.maxx)
+ var/new_y = Clamp((starting.y + (y_o * range_multiplier)), 0, world.maxy)
+ var/turf/newtarget = locate(new_x, new_y, starting.z)
+ return newtarget
/obj/item/weapon/pneumatic_cannon/ghetto //Obtainable by improvised methods; more gas per use, less capacity, but smaller
name = "improvised pneumatic cannon"
@@ -164,3 +220,35 @@
return
add_overlay(tank.icon_state)
src.update_icon()
+
+/obj/item/weapon/pneumatic_cannon/proc/fill_with_type(type, amount)
+ if(!ispath(type, /obj/item))
+ return FALSE
+ var/loaded = 0
+ for(var/i in 1 to amount)
+ var/obj/item/I = new type
+ if(!load_item(I, null))
+ qdel(I)
+ return loaded
+ loaded++
+ CHECK_TICK
+ return loaded
+
+/obj/item/weapon/pneumatic_cannon/pie
+ name = "pie cannon"
+ desc = "Load cream pie for optimal results"
+ force = 10
+ icon_state = "piecannon"
+ gasPerThrow = 0
+ checktank = FALSE
+ range_multiplier = 3
+ fire_mode = PCANNON_FIFO
+ throw_amount = 1
+ maxWeightClass = 100 //50 pies. :^)
+ clumsyCheck = FALSE
+
+/obj/item/weapon/pneumatic_cannon/pie/can_load_item(obj/item/I, mob/user)
+ if(istype(I, /obj/item/weapon/reagent_containers/food/snacks/pie))
+ return ..()
+ to_chat(user, "[src] only accepts pies!")
+ return FALSE
diff --git a/code/game/objects/items/weapons/storage/book.dm b/code/game/objects/items/weapons/storage/book.dm
index 83805d969a..7775b3692a 100644
--- a/code/game/objects/items/weapons/storage/book.dm
+++ b/code/game/objects/items/weapons/storage/book.dm
@@ -62,7 +62,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list("bible", "koran", "scrapbook", "bible",
SSreligion.bible_icon_state = B.icon_state
SSreligion.bible_item_state = B.item_state
- feedback_set_details("religion_book","[biblename]")
+ SSblackbox.set_details("religion_book","[biblename]")
usr << browse(null, "window=editicon")
/obj/item/weapon/storage/book/bible/proc/bless(mob/living/carbon/human/H, mob/living/user)
diff --git a/code/game/objects/items/weapons/storage/dakis.dm b/code/game/objects/items/weapons/storage/dakis.dm
new file mode 100644
index 0000000000..a238304cac
--- /dev/null
+++ b/code/game/objects/items/weapons/storage/dakis.dm
@@ -0,0 +1,48 @@
+//////////////////////////////////
+//dakimakuras
+//////////////////////////////////
+
+/obj/item/weapon/storage/daki
+ name = "dakimakura"
+ desc = "A large pillow depicting a girl in a compromising position. Featuring as many dimensions as you."
+ icon = 'icons/obj/daki.dmi'
+ icon_state = "daki_base"
+ slot_flags = SLOT_BACK
+ storage_slots = 3
+ w_class = 4
+ max_w_class = 3
+ max_combined_w_class = 21
+ var/cooldowntime = 20
+ var/static/list/dakimakura_options = list("Callie","Casca","Chaika","Elisabeth","Foxy Grandpa","Haruko","Holo","Ian","Jolyne","Kurisu","Marie","Mugi","Nar'Sie","Patchouli","Plutia","Rei","Reisen","Naga","Squid","Squigly","Tomoko","Toriel","Umaru","Yaranaika","Yoko") //Kurisu is the ideal girl." - Me, Logos.
+
+/obj/item/weapon/storage/daki/attack_self(mob/living/user)
+ var/body_choice
+ var/custom_name
+
+ if(icon_state == "daki_base")
+ body_choice = input("Pick a body.") in dakimakura_options
+ icon_state = "daki_[body_choice]"
+ custom_name = stripped_input(user, "What's her name?")
+ if(length(custom_name) > MAX_NAME_LEN)
+ to_chat(user,"Name is too long!")
+ return FALSE
+ if(custom_name)
+ name = custom_name
+ desc = "A large pillow depicting [custom_name] in a compromising position. Featuring as many dimensions as you."
+ else
+ switch(user.a_intent)
+ if(INTENT_HELP)
+ user.visible_message("[user] hugs the [name].")
+ playsound(src, "rustle", 50, 1, -5)
+ if(INTENT_DISARM)
+ user.visible_message("[user] kisses the [name].")
+ playsound(src, "rustle", 50, 1, -5)
+ if(INTENT_GRAB)
+ user.visible_message("[user] holds the [name]!")
+ playsound(src, 'sound/items/bikehorn.ogg', 50, 1)
+ if(INTENT_HARM)
+ user.visible_message("[user] punches the [name]!")
+ playsound(src, 'sound/effects/shieldbash.ogg', 50, 1)
+ user.changeNext_move(CLICK_CD_MELEE)
+
+////////////////////////////
diff --git a/code/game/objects/items/weapons/storage/internal.dm b/code/game/objects/items/weapons/storage/internal.dm
index 8c7b7df04c..cfdc6cc354 100644
--- a/code/game/objects/items/weapons/storage/internal.dm
+++ b/code/game/objects/items/weapons/storage/internal.dm
@@ -5,10 +5,6 @@
w_class = WEIGHT_CLASS_BULKY
rustle_jimmies = FALSE
-/obj/item/weapon/storage/internal/ClickAccessible(mob/user, depth=1)
- if(loc)
- return loc.ClickAccessible(user, depth)
-
/obj/item/weapon/storage/internal/Adjacent(A)
if(loc)
return loc.Adjacent(A)
@@ -53,7 +49,7 @@
/obj/item/weapon/implanter, /obj/item/weapon/screwdriver, /obj/item/weapon/weldingtool/mini,
/obj/item/device/firing_pin
)
- //can hold both regular pens and energy daggers. made for your every-day tactical librarians/murderers.
+ //can hold both regular pens and energy daggers. made for your every-day tactical curators/murderers.
priority = FALSE
quickdraw = TRUE
silent = TRUE
diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm
index 08e947c45d..b1378617b8 100644
--- a/code/game/objects/items/weapons/storage/storage.dm
+++ b/code/game/objects/items/weapons/storage/storage.dm
@@ -38,7 +38,7 @@
return
// this must come before the screen objects only block, dunno why it wasn't before
- if(over_object == M && (src.ClickAccessible(M, depth=STORAGE_VIEW_DEPTH) || Adjacent(M)))
+ if(over_object == M && M.CanReach(src,view_only = TRUE))
orient2hud(M)
if(M.s_active)
M.s_active.close(M)
diff --git a/code/game/objects/items/weapons/storage/uplink_kits.dm b/code/game/objects/items/weapons/storage/uplink_kits.dm
index 335545e8c9..1962fbd818 100644
--- a/code/game/objects/items/weapons/storage/uplink_kits.dm
+++ b/code/game/objects/items/weapons/storage/uplink_kits.dm
@@ -92,8 +92,7 @@
new /obj/item/pizzabox/bomb
if("darklord") //20 tc + tk + summon item close enough for now
- new /obj/item/weapon/melee/energy/sword/saber(src)
- new /obj/item/weapon/melee/energy/sword/saber(src)
+ new /obj/item/weapon/twohanded/dualsaber(src)
new /obj/item/weapon/dnainjector/telemut/darkbundle(src)
new /obj/item/clothing/suit/hooded/chaplain_hoodie(src)
new /obj/item/weapon/card/id/syndicate(src)
@@ -220,6 +219,7 @@
new /obj/item/weapon/reagent_containers/glass/bottle/venom(src)
new /obj/item/weapon/reagent_containers/glass/bottle/neurotoxin2(src)
new /obj/item/weapon/reagent_containers/glass/bottle/formaldehyde(src)
+ new /obj/item/weapon/reagent_containers/glass/bottle/spewium(src)
new /obj/item/weapon/reagent_containers/glass/bottle/cyanide(src)
new /obj/item/weapon/reagent_containers/glass/bottle/histamine(src)
new /obj/item/weapon/reagent_containers/glass/bottle/initropidril(src)
@@ -296,3 +296,10 @@
/obj/item/weapon/storage/box/syndie_kit/mimery/PopulateContents()
new /obj/item/weapon/spellbook/oneuse/mimery_blockade(src)
new /obj/item/weapon/spellbook/oneuse/mimery_guns(src)
+
+/obj/item/weapon/storage/box/syndie_kit/holoparasite
+ name = "box"
+
+/obj/item/weapon/storage/box/syndie_kit/holoparasite/PopulateContents()
+ new /obj/item/weapon/guardiancreator/tech/choose/traitor(src)
+ new /obj/item/weapon/paper/guardian(src)
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm
index 0595076901..e5ffbade4c 100644
--- a/code/game/objects/items/weapons/tanks/tanks.dm
+++ b/code/game/objects/items/weapons/tanks/tanks.dm
@@ -116,7 +116,7 @@
var/mob/living/carbon/human/H = user
user.visible_message("[user] is putting [src]'s valve to [user.p_their()] lips! It looks like [user.p_theyre()] trying to commit suicide!")
playsound(loc, 'sound/effects/spray.ogg', 10, 1, -3)
- if (H && !QDELETED(H))
+ if (!QDELETED(H) && air_contents && air_contents.return_pressure() >= 1000)
for(var/obj/item/W in H)
H.dropItemToGround(W)
if(prob(50))
diff --git a/code/game/objects/items/weapons/twohanded.dm b/code/game/objects/items/weapons/twohanded.dm
index 002c7773b4..daa86eb833 100644
--- a/code/game/objects/items/weapons/twohanded.dm
+++ b/code/game/objects/items/weapons/twohanded.dm
@@ -262,11 +262,11 @@
armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 70)
resistance_flags = FIRE_PROOF
var/hacked = 0
- var/brightness_on = 6//TWICE AS BRIGHT AS A REGULAR ESWORD
+ var/brightness_on = 6 //TWICE AS BRIGHT AS A REGULAR ESWORD
var/list/possible_colors = list("red", "blue", "green", "purple")
/obj/item/weapon/twohanded/dualsaber/Initialize()
- ..()
+ . = ..()
if(LAZYLEN(possible_colors))
item_color = pick(possible_colors)
switch(item_color)
diff --git a/code/game/objects/items/weapons/vending_items.dm b/code/game/objects/items/weapons/vending_items.dm
index 74ab7be4b6..c93dcaca3b 100644
--- a/code/game/objects/items/weapons/vending_items.dm
+++ b/code/game/objects/items/weapons/vending_items.dm
@@ -71,3 +71,9 @@
icon_state = "refill_clothes"
charges = list(31, 4, 4)// of 101 standard, 12 contraband, 10 premium(?)
init_charges = list(31, 4, 4)
+
+/obj/item/weapon/vending_refill/medical
+ machine_name = "NanoMed"
+ icon_state = "refill_medical"
+ charges = list(26, 5, 3)// of 76 standard, 13 contraband, 8 premium
+ init_charges = list(26, 5, 3)
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm
index a0de9b5f76..e28c916ae4 100644
--- a/code/game/objects/items/weapons/weaponry.dm
+++ b/code/game/objects/items/weapons/weaponry.dm
@@ -390,7 +390,7 @@
desc = "A chainsaw that has replaced your arm."
icon_state = "chainsaw_on"
item_state = "mounted_chainsaw"
- flags = NODROP | ABSTRACT
+ flags = NODROP | ABSTRACT | DROPDEL
w_class = WEIGHT_CLASS_HUGE
force = 21
throwforce = 0
@@ -400,10 +400,17 @@
attack_verb = list("sawed", "torn", "cut", "chopped", "diced")
hitsound = 'sound/weapons/chainsawhit.ogg'
-/obj/item/weapon/mounted_chainsaw/dropped()
- ..()
+/obj/item/weapon/mounted_chainsaw/Destroy()
+ var/obj/item/bodypart/part
new /obj/item/weapon/twohanded/required/chainsaw(get_turf(src))
- qdel(src)
+ if(iscarbon(loc))
+ var/mob/living/carbon/holder = loc
+ var/index = holder.get_held_index_of_item(src)
+ if(index)
+ part = holder.hand_bodyparts[index]
+ . = ..()
+ if(part)
+ part.drop_limb()
/obj/item/weapon/statuebust
name = "bust"
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index e29d372a6c..9766dbc26d 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -23,7 +23,7 @@
var/persistence_replacement //have something WAY too amazing to live to the next round? Set a new path here. Overuse of this var will make me upset.
var/unique_rename = FALSE // can you customize the description/name of the thing?
-
+
var/dangerous_possession = FALSE //Admin possession yes/no
/obj/vv_edit_var(vname, vval)
diff --git a/code/game/objects/structures/ai_core.dm b/code/game/objects/structures/ai_core.dm
index 3cf35daa03..7820031700 100644
--- a/code/game/objects/structures/ai_core.dm
+++ b/code/game/objects/structures/ai_core.dm
@@ -180,7 +180,7 @@
var/mob/living/silicon/ai/A = new /mob/living/silicon/ai(loc, laws, brain.brainmob)
if(brain.force_replace_ai_name)
A.fully_replace_character_name(A.name, brain.replacement_ai_name())
- feedback_inc("cyborg_ais_created",1)
+ SSblackbox.inc("cyborg_ais_created",1)
qdel(src)
else
state = AI_READY_CORE
diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm
index d46136a9ae..6212133b12 100644
--- a/code/game/objects/structures/aliens.dm
+++ b/code/game/objects/structures/aliens.dm
@@ -298,7 +298,7 @@
else
for(var/mob/M in range(1,src))
if(CanHug(M))
- child.Attach(M)
+ child.Leap(M)
break
/obj/structure/alien/egg/obj_break(damage_flag)
diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm
index 6813706ed6..89c815692d 100644
--- a/code/game/objects/structures/bedsheet_bin.dm
+++ b/code/game/objects/structures/bedsheet_bin.dm
@@ -106,6 +106,11 @@ LINEN BINS
icon_state = "sheetrd"
item_color = "director"
+// for Free Golems.
+/obj/item/weapon/bedsheet/rd/royal_cape
+ name = "Royal Cape of the Liberator"
+ desc = "Majestic."
+
/obj/item/weapon/bedsheet/medical
name = "medical blanket"
desc = "It's a sterilized* blanket commonly used in the Medbay. *Sterilization is voided if a virologist is present onboard the station."
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
index 67989257f2..5e1ea02509 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
@@ -11,6 +11,7 @@
else
new /obj/item/weapon/storage/backpack/satchel/cap(src)
new /obj/item/clothing/neck/cloak/cap(src)
+ new /obj/item/weapon/storage/daki(src)
new /obj/item/weapon/storage/backpack/dufflebag/captain(src)
new /obj/item/clothing/head/crown/fancy(src)
new /obj/item/clothing/suit/captunic(src)
diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm
index be1b49074d..43fbdde5ee 100644
--- a/code/game/objects/structures/displaycase.dm
+++ b/code/game/objects/structures/displaycase.dm
@@ -11,32 +11,31 @@
max_integrity = 200
integrity_failure = 50
var/obj/item/showpiece = null
- var/alert = 0
- var/open = 0
+ var/alert = TRUE
+ var/open = FALSE
+ var/openable = TRUE
var/obj/item/weapon/electronics/airlock/electronics
var/start_showpiece_type = null //add type for items on display
-/obj/structure/displaycase/New()
- ..()
+/obj/structure/displaycase/Initialize()
+ . = ..()
if(start_showpiece_type)
showpiece = new start_showpiece_type (src)
update_icon()
/obj/structure/displaycase/Destroy()
if(electronics)
- qdel(electronics)
- electronics = null
+ QDEL_NULL(electronics)
if(showpiece)
- qdel(showpiece)
- showpiece = null
+ QDEL_NULL(showpiece)
return ..()
/obj/structure/displaycase/examine(mob/user)
..()
- if(showpiece)
- to_chat(user, "There's [showpiece] inside.")
if(alert)
to_chat(user, "Hooked up with an anti-theft system.")
+ if(showpiece)
+ to_chat(user, "There's [showpiece] inside.")
/obj/structure/displaycase/proc/dump()
@@ -83,8 +82,8 @@
try
getFlatIcon(A,defdir=4)
catch
- return 0
- return 1
+ return FALSE
+ return TRUE
/obj/structure/displaycase/proc/get_flat_icon_directional(atom/A)
//Get flatIcon even if dir is mismatched for directionless icons
@@ -115,7 +114,7 @@
return
/obj/structure/displaycase/attackby(obj/item/weapon/W, mob/user, params)
- if(W.GetID() && !broken)
+ if(W.GetID() && !broken && openable)
if(allowed(user))
to_chat(user, "You [open ? "close":"open"] the [src]")
toggle_lock(user)
@@ -134,7 +133,7 @@
else
to_chat(user, "[src] is already in good condition!")
return
- else if(!alert && istype(W,/obj/item/weapon/crowbar)) //Only applies to the lab cage and player made display cases
+ else if(!alert && istype(W,/obj/item/weapon/crowbar) && openable) //Only applies to the lab cage and player made display cases
if(broken)
if(showpiece)
to_chat(user, "Remove the displayed object first.")
@@ -147,8 +146,7 @@
to_chat(user, "You [open ? "close":"open"] the [src]")
toggle_lock(user)
else if(open && !showpiece)
- if(user.drop_item())
- W.loc = src
+ if(user.transferItemToLoc(W, src))
showpiece = W
to_chat(user, "You put [W] on display")
update_icon()
@@ -176,8 +174,8 @@
/obj/structure/displaycase/attack_hand(mob/user)
user.changeNext_move(CLICK_CD_MELEE)
if (showpiece && (broken || open))
- dump()
to_chat(user, "You deactivate the hover field built into the case.")
+ dump()
src.add_fingerprint(user)
update_icon()
return
@@ -249,3 +247,115 @@
desc = "A glass lab container for storing interesting creatures."
start_showpiece_type = /obj/item/clothing/mask/facehugger/lamarr
req_access = list(GLOB.access_rd)
+
+/obj/structure/displaycase/trophy
+ name = "trophy display case"
+ desc = "Store your trophies of accomplishment in here, and they will stay forever."
+ var/trophy_message = ""
+ var/placer_key = ""
+ var/added_roundstart = TRUE
+ var/is_locked = TRUE
+
+ alert = TRUE
+ integrity_failure = 0
+ openable = FALSE
+
+/obj/structure/displaycase/trophy/Initialize()
+ . = ..()
+ GLOB.trophy_cases += src
+
+/obj/structure/displaycase/trophy/Destroy()
+ GLOB.trophy_cases -= src
+ return ..()
+
+/obj/structure/displaycase/trophy/examine(mob/user)
+ ..()
+ if(trophy_message)
+ to_chat(user, "The plaque reads:")
+ to_chat(user, trophy_message)
+
+/obj/structure/displaycase/trophy/attackby(obj/item/weapon/W, mob/user, params)
+
+ if(!user.Adjacent(src)) //no TK museology
+ return
+ if(user.a_intent == INTENT_HARM)
+ return ..()
+
+ if(user.is_holding_item_of_type(/obj/item/key/displaycase))
+ if(added_roundstart)
+ is_locked = !is_locked
+ to_chat(user, "You [!is_locked ? "un" : ""]lock the case.")
+ else
+ to_chat(user, "The lock is stuck shut!")
+ return
+
+ if(is_locked)
+ to_chat(user, "The case is shut tight with an old fashioned physical lock. Maybe you should ask the curator for the key?")
+ return
+
+ if(!added_roundstart)
+ to_chat(user, "You've already put something new in this case.")
+ return
+
+ if(is_type_in_typecache(W, GLOB.blacklisted_cargo_types))
+ to_chat(user, "The case rejects the [W].")
+ return
+
+ for(var/a in W.GetAllContents())
+ if(is_type_in_typecache(a, GLOB.blacklisted_cargo_types))
+ to_chat(user, "The case rejects the [W].")
+ return
+
+ if(user.transferItemToLoc(W, src))
+
+ if(showpiece)
+ to_chat(user, "You press a button, and [showpiece] descends into the floor of the case.")
+ QDEL_NULL(showpiece)
+
+ to_chat(user, "You insert [W] into the case.")
+ showpiece = W
+ added_roundstart = FALSE
+ update_icon()
+
+ placer_key = user.ckey
+
+ trophy_message = W.desc //default value
+
+ var/chosen_plaque = stripped_input(user, "What would you like the plaque to say? Default value is item's description.", "Trophy Plaque")
+ if(chosen_plaque)
+ if(user.Adjacent(src))
+ trophy_message = chosen_plaque
+ to_chat(user, "You set the plaque's text.")
+ else
+ to_chat(user, "You are too far to set the plaque's text.")
+
+ SSpersistence.SaveTrophy(src)
+ return TRUE
+
+ else
+ to_chat(user, "\The [W] is stuck to your hand, you can't put it in the [src.name]!")
+
+ return
+
+/obj/structure/displaycase/trophy/dump()
+ if (showpiece)
+ if(added_roundstart)
+ visible_message("The [showpiece] crumbles to dust!")
+ new /obj/effect/decal/cleanable/ash(loc)
+ QDEL_NULL(showpiece)
+ else
+ ..()
+
+/obj/item/key/displaycase
+ name = "display case key"
+ desc = "The key to the curator's display cases."
+
+/obj/item/showpiece_dummy
+ name = "Cheap replica"
+
+/obj/item/showpiece_dummy/Initialize(mapload, path)
+ . = ..()
+ var/obj/item/I = path
+ name = initial(I.name)
+ icon = initial(I.icon)
+ icon_state = initial(I.icon_state)
diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm
index 466be07c50..389ae40e88 100644
--- a/code/game/objects/structures/false_walls.dm
+++ b/code/game/objects/structures/false_walls.dm
@@ -234,8 +234,9 @@
/obj/structure/falsewall/plasma/attackby(obj/item/weapon/W, mob/user, params)
if(W.is_hot() > 300)
- message_admins("Plasma falsewall ignited by [key_name_admin(user)](?) (FLW) in ([x],[y],[z] - JMP)",0,1)
- log_game("Plasma falsewall ignited by [key_name(user)] in ([x],[y],[z])")
+ var/turf/T = get_turf(src)
+ message_admins("Plasma falsewall ignited by [ADMIN_LOOKUPFLW(user)] in [ADMIN_COORDJMP(T)]",0,1)
+ log_game("Plasma falsewall ignited by [key_name(user)] in [COORD(T)]")
burnbabyburn()
else
return ..()
diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm
index cdd706e5df..c00348a2c6 100644
--- a/code/game/objects/structures/flora.dm
+++ b/code/game/objects/structures/flora.dm
@@ -88,7 +88,7 @@
pixel_y = -20
/obj/structure/flora/tree/jungle/Initialize()
- icon_state = "[icon_state][rand(1, 3)]"
+ icon_state = "[icon_state][rand(1, 6)]"
..()
//grass
diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm
index f1a808938e..bb55084dd9 100644
--- a/code/game/objects/structures/ghost_role_spawners.dm
+++ b/code/game/objects/structures/ghost_role_spawners.dm
@@ -101,6 +101,7 @@
death = FALSE
anchored = 0
density = 0
+ var/can_transfer = TRUE //if golems can switch bodies to this new shell
var/mob/living/owner = null //golem's owner if it has one
flavour_text = "You are a Free Golem. Your family worships The Liberator. In his infinite and divine wisdom, he set your clan free to \
travel the stars with a single declaration: \"Yeah go do whatever.\" Though you are bound to the one who created you, it is customary in your society to repeat those same words to newborn \
@@ -119,7 +120,7 @@
Serve [creator], and assist [creator.p_them()] in completing [creator.p_their()] goals at any cost."
owner = creator
-/obj/effect/mob_spawn/human/golem/special(mob/living/new_spawn)
+/obj/effect/mob_spawn/human/golem/special(mob/living/new_spawn, name)
var/datum/species/golem/X = mob_species
to_chat(new_spawn, "[initial(X.info_text)]")
if(!owner)
@@ -132,14 +133,29 @@
if(ishuman(new_spawn))
var/mob/living/carbon/human/H = new_spawn
H.set_cloned_appearance()
- H.real_name = H.dna.species.random_name()
+ if(!name)
+ H.real_name = H.dna.species.random_name()
+ else
+ H.real_name = name
+
+/obj/effect/mob_spawn/human/golem/attack_hand(mob/user)
+ if(isgolem(user) && can_transfer)
+ var/transfer = alert("Transfer your soul to [src]? (Warning, your old body will die!)",,"Yes","No")
+ if(!transfer)
+ return
+ log_game("[user.ckey] golem-swapped into [src]")
+ user.visible_message("A faint light leaves [user], moving to [src] and animating it!","You leave your old body behind, and transfer into [src]!")
+ create(ckey = user.ckey, flavour = FALSE, name = user.real_name)
+ user.death()
+ return
+ ..()
+
/obj/effect/mob_spawn/human/golem/adamantine
name = "dust-caked golem shell"
desc = "A humanoid shape, empty, lifeless, and full of potential."
mob_name = "a free golem"
- anchored = 1
- density = 1
+ can_transfer = FALSE
mob_species = /datum/species/golem/adamantine
//Malfunctioning cryostasis sleepers: Spawns in makeshift shelters in lavaland. Ghosts become hermits with knowledge of how they got to where they are now.
diff --git a/code/game/objects/structures/mineral_doors.dm b/code/game/objects/structures/mineral_doors.dm
index 129862dd62..039f3da5d7 100644
--- a/code/game/objects/structures/mineral_doors.dm
+++ b/code/game/objects/structures/mineral_doors.dm
@@ -188,8 +188,9 @@
/obj/structure/mineral_door/transparent/plasma/attackby(obj/item/weapon/W, mob/user, params)
if(W.is_hot())
- message_admins("Plasma mineral door ignited by [key_name_admin(user)](?) (FLW) in ([x],[y],[z] - JMP)",0,1)
- log_game("Plasma mineral door ignited by [key_name(user)] in ([x],[y],[z])")
+ var/turf/T = get_turf(src)
+ message_admins("Plasma mineral door ignited by [ADMIN_LOOKUPFLW(user)] in [ADMIN_COORDJMP(T)]",0,1)
+ log_game("Plasma mineral door ignited by [key_name(user)] in [COORD(T)]")
TemperatureAct()
else
return ..()
diff --git a/code/game/objects/structures/statues.dm b/code/game/objects/structures/statues.dm
index f5defdab50..6909965614 100644
--- a/code/game/objects/structures/statues.dm
+++ b/code/game/objects/structures/statues.dm
@@ -163,18 +163,20 @@
PlasmaBurn(500)
burn = TRUE
if(burn)
+ var/turf/T = get_turf(src)
if(Proj.firer)
- message_admins("Plasma statue ignited by [key_name_admin(Proj.firer)](?) (FLW) in ([x],[y],[z] - JMP)",0,1)
- log_game("Plasma statue ignited by [key_name(Proj.firer)] in ([x],[y],[z])")
+ message_admins("Plasma statue ignited by [ADMIN_LOOKUPFLW(Proj.firer)] in [ADMIN_COORDJMP(T)]",0,1)
+ log_game("Plasma statue ignited by [key_name(Proj.firer)] in [COORD(T)]")
else
- message_admins("Plasma statue ignited by [Proj]. No known firer.(?) (FLW) in ([x],[y],[z] - JMP)",0,1)
- log_game("Plasma statue ignited by [Proj] in ([x],[y],[z]). No known firer.")
+ message_admins("Plasma statue ignited by [Proj]. No known firer, in [ADMIN_COORDJMP(T)]",0,1)
+ log_game("Plasma statue ignited by [Proj] in [COORD(T)]. No known firer.")
..()
/obj/structure/statue/plasma/attackby(obj/item/weapon/W, mob/user, params)
if(W.is_hot() > 300)//If the temperature of the object is over 300, then ignite
- message_admins("Plasma statue ignited by [key_name_admin(user)](?) (FLW) in ([x],[y],[z] - JMP)",0,1)
- log_game("Plasma statue ignited by [key_name(user)] in ([x],[y],[z])")
+ var/turf/T = get_turf(src)
+ message_admins("Plasma statue ignited by [ADMIN_LOOKUPFLW(user)] in [ADMIN_COORDJMP(T)]",0,1)
+ log_game("Plasma statue ignited by [key_name(user)] in [COORD(T)]")
ignite(W.is_hot())
else
return ..()
diff --git a/code/game/objects/structures/traps.dm b/code/game/objects/structures/traps.dm
index 90500f14db..dad4e9b53e 100644
--- a/code/game/objects/structures/traps.dm
+++ b/code/game/objects/structures/traps.dm
@@ -8,8 +8,10 @@
alpha = 30 //initially quite hidden when not "recharging"
var/last_trigger = 0
var/time_between_triggers = 600 //takes a minute to recharge
+ var/charges = INFINITY
var/list/static/ignore_typecache
+ var/list/mob/immune_minds = list()
var/datum/effect_system/spark_spread/spark_system
@@ -30,9 +32,11 @@
. = ..()
/obj/structure/trap/examine(mob/user)
- ..()
+ . = ..()
if(!isliving(user))
return
+ if(user.mind && user.mind in immune_minds)
+ return
if(get_dist(user, src) <= 1)
to_chat(user, "You reveal [src]!")
flare()
@@ -41,10 +45,15 @@
// Makes the trap visible, and starts the cooldown until it's
// able to be triggered again.
visible_message("[src] flares brightly!")
- alpha = 200
- animate(src, alpha = initial(alpha), time = time_between_triggers)
- last_trigger = world.time
spark_system.start()
+ alpha = 200
+ last_trigger = world.time
+ charges--
+ if(charges <= 0)
+ animate(src, alpha = 0, time = 10)
+ QDEL_IN(src, 10)
+ else
+ animate(src, alpha = initial(alpha), time = time_between_triggers)
/obj/structure/trap/Crossed(atom/movable/AM)
if(last_trigger + time_between_triggers > world.time)
@@ -52,6 +61,12 @@
// Don't want the traps triggered by sparks, ghosts or projectiles.
if(is_type_in_typecache(AM, ignore_typecache))
return
+ if(ismob(AM))
+ var/mob/M = AM
+ if(M.mind in immune_minds)
+ return
+ if(charges <= 0)
+ return
flare()
if(isliving(AM))
trap_effect(AM)
diff --git a/code/game/say.dm b/code/game/say.dm
index efad2f105f..fe76000c71 100644
--- a/code/game/say.dm
+++ b/code/game/say.dm
@@ -56,10 +56,16 @@ GLOBAL_LIST_INIT(freqtospan, list(
var/namepart = "[speaker.GetVoice()][speaker.get_alt_name()]"
//End name span.
var/endspanpart = ""
- //Message
- var/messagepart = " [lang_treat(speaker, message_language, raw_message, spans, message_mode)]"
- return "[spanpart1][spanpart2][freqpart][compose_track_href(speaker, namepart)][namepart][compose_job(speaker, message_language, raw_message, radio_freq)][endspanpart][messagepart]"
+ //Message
+ var/messagepart = " [lang_treat(speaker, message_language, raw_message, spans)]"
+
+ var/languageicon = ""
+ var/datum/language/D = GLOB.language_datum_instances[message_language]
+ if(D.display_icon(src))
+ languageicon = "[D.get_icon()] "
+
+ return "[spanpart1][spanpart2][freqpart][languageicon][compose_track_href(speaker, namepart)][namepart][compose_job(speaker, message_language, raw_message, radio_freq)][endspanpart][messagepart]"
/atom/movable/proc/compose_track_href(atom/movable/speaker, message_langs, raw_message, radio_freq)
return ""
@@ -67,33 +73,37 @@ GLOBAL_LIST_INIT(freqtospan, list(
/atom/movable/proc/compose_job(atom/movable/speaker, message_langs, raw_message, radio_freq)
return ""
-/atom/movable/proc/say_quote(input, list/spans=list(), message_mode)
- if(!input)
- return "says, \"...\"" //not the best solution, but it will stop a large number of runtimes. The cause is somewhere in the Tcomms code
+/atom/movable/proc/say_mod(input, message_mode)
var/ending = copytext(input, length(input))
if(copytext(input, length(input) - 1) == "!!")
- spans |= SPAN_YELL
- return "[verb_yell], \"[attach_spans(input, spans)]\""
- input = attach_spans(input, spans)
- if(ending == "?")
- return "[verb_ask], \"[input]\""
- if(ending == "!")
- return "[verb_exclaim], \"[input]\""
+ return verb_yell
+ else if(ending == "?")
+ return verb_ask
+ else if(ending == "!")
+ return verb_exclaim
+ else
+ return verb_say
- return "[verb_say], \"[input]\""
+/atom/movable/proc/say_quote(input, list/spans=list(), message_mode)
+ if(!input)
+ input = "..."
+
+ if(copytext(input, length(input) - 1) == "!!")
+ spans |= SPAN_YELL
+
+ var/spanned = attach_spans(input, spans)
+ return "[say_mod(input, message_mode)], \"[spanned]\""
/atom/movable/proc/lang_treat(atom/movable/speaker, datum/language/language, raw_message, list/spans, message_mode)
if(has_language(language))
var/atom/movable/AM = speaker.GetSource()
if(AM) //Basically means "if the speaker is virtual"
- if(AM.verb_say != speaker.verb_say || AM.verb_ask != speaker.verb_ask || AM.verb_exclaim != speaker.verb_exclaim || AM.verb_yell != speaker.verb_yell) //If the saymod was changed
- return speaker.say_quote(raw_message, spans, message_mode)
return AM.say_quote(raw_message, spans, message_mode)
else
return speaker.say_quote(raw_message, spans, message_mode)
else if(language)
var/atom/movable/AM = speaker.GetSource()
- var/datum/language/D = new language
+ var/datum/language/D = GLOB.language_datum_instances[language]
raw_message = D.scramble(raw_message)
if(AM)
return AM.say_quote(raw_message, spans, message_mode)
@@ -133,7 +143,7 @@ GLOBAL_LIST_INIT(freqtospan, list(
return "0"
/atom/movable/proc/GetVoice()
- return name
+ return "[src]" //Returns the atom's name, prepended with 'The' if it's not a proper noun
/atom/movable/proc/IsVocal()
return 1
diff --git a/code/game/turfs/simulated/chasm.dm b/code/game/turfs/simulated/chasm.dm
index 8890c50129..8b24a4c816 100644
--- a/code/game/turfs/simulated/chasm.dm
+++ b/code/game/turfs/simulated/chasm.dm
@@ -143,14 +143,14 @@
qdel(S.mmi)
qdel(AM)
-
+
if(AM && !QDELETED(AM)) //It's indestructible
visible_message("[src] spits out the [AM]!")
AM.alpha = oldalpha
AM.color = oldcolor
AM.transform = oldtransform
AM.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1, 10),rand(1, 10))
-
+
/turf/open/chasm/straight_down/lava_land_surface/normal_air
initial_gas_mix = "o2=22;n2=82;TEMP=293.15"
@@ -158,3 +158,17 @@
/turf/open/chasm/CanPass(atom/movable/mover, turf/target, height=0)
return 1
+
+
+
+//Jungle
+
+/turf/open/chasm/jungle
+ icon = 'icons/turf/floors/junglechasm.dmi'
+ planetary_atmos = TRUE
+ initial_gas_mix = "o2=14;n2=23;TEMP=300"
+
+/turf/open/chasm/straight_down/jungle
+ icon = 'icons/turf/floors/junglechasm.dmi'
+ planetary_atmos = TRUE
+ initial_gas_mix = "o2=14;n2=23;TEMP=300"
\ No newline at end of file
diff --git a/code/game/turfs/simulated/floor/fancy_floor.dm b/code/game/turfs/simulated/floor/fancy_floor.dm
index eaba3d50eb..cce7756892 100644
--- a/code/game/turfs/simulated/floor/fancy_floor.dm
+++ b/code/game/turfs/simulated/floor/fancy_floor.dm
@@ -1,6 +1,7 @@
/* In this file:
* Wood floor
* Grass floor
+ * Fake Basalt
* Carpet floor
* Fake pits
* Fake space
@@ -67,6 +68,7 @@
broken_states = list("sand")
flags = NONE
var/ore_type = /obj/item/weapon/ore/glass
+ var/turfverb = "uproot"
/turf/open/floor/grass/Initialize()
..()
@@ -76,7 +78,7 @@
if(istype(C, /obj/item/weapon/shovel) && params)
new ore_type(src)
new ore_type(src) //Make some sand if you shovel grass
- user.visible_message("[user] digs up [src].", "You uproot [src].")
+ user.visible_message("[user] digs up [src].", "You [src.turfverb] [src].")
playsound(src, 'sound/effects/shovel_dig.ogg', 50, 1)
make_plating()
if(..())
@@ -93,7 +95,6 @@
initial_gas_mix = "o2=22;n2=82;TEMP=180"
slowdown = 2
-
/turf/open/floor/grass/snow/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/weapon/crowbar))//You need to dig this turf out instead of crowbarring it
return
@@ -113,6 +114,24 @@
icon_state = "basalt[rand(0, 12)]"
set_basalt_light(src)
+
+/turf/open/floor/grass/fakebasalt //Heart is not a real planeteer power
+ name = "aesthetic volcanic flooring"
+ desc = "Safely recreated turf for your hellplanet-scaping"
+ icon = 'icons/turf/floors.dmi'
+ icon_state = "basalt"
+ floor_tile = /obj/item/stack/tile/basalt
+ ore_type = /obj/item/weapon/ore/glass/basalt
+ turfverb = "dig up"
+ slowdown = 0
+
+/turf/open/floor/grass/fakebasalt/Initialize()
+ ..()
+ if(prob(15))
+ icon_state = "basalt[rand(0, 12)]"
+ set_basalt_light(src)
+
+
/turf/open/floor/carpet
name = "carpet"
desc = "Soft velvet carpeting. Feels good between your toes."
diff --git a/code/game/turfs/simulated/floor/mineral_floor.dm b/code/game/turfs/simulated/floor/mineral_floor.dm
index 3e58e62854..5024dfea90 100644
--- a/code/game/turfs/simulated/floor/mineral_floor.dm
+++ b/code/game/turfs/simulated/floor/mineral_floor.dm
@@ -44,8 +44,8 @@
/turf/open/floor/mineral/plasma/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if(W.is_hot() > 300)//If the temperature of the object is over 300, then ignite
- message_admins("Plasma flooring was ignited by [key_name_admin(user)](?) (FLW) in ([x],[y],[z] - JMP)",0,1)
- log_game("Plasma flooring was ignited by [key_name(user)] in ([x],[y],[z])")
+ message_admins("Plasma flooring was ignited by [ADMIN_LOOKUPFLW(user)] in [ADMIN_COORDJMP(src)]",0,1)
+ log_game("Plasma flooring was ignited by [key_name(user)] in [COORD(src)]")
ignite(W.is_hot())
return
..()
diff --git a/code/game/turfs/simulated/floor/plating/asteroid.dm b/code/game/turfs/simulated/floor/plating/asteroid.dm
index a17fcf2ca8..f4a3403ee5 100644
--- a/code/game/turfs/simulated/floor/plating/asteroid.dm
+++ b/code/game/turfs/simulated/floor/plating/asteroid.dm
@@ -63,7 +63,7 @@
if(istype(src, /turf/open/floor/plating/asteroid))
to_chat(user, "You dig a hole.")
gets_dug()
- feedback_add_details("pick_used_mining","[W.type]")
+ SSblackbox.add_details("pick_used_mining","[W.type]")
if(istype(W,/obj/item/weapon/storage/bag/ore))
var/obj/item/weapon/storage/bag/ore/S = W
diff --git a/code/game/turfs/simulated/floor/plating/dirt.dm b/code/game/turfs/simulated/floor/plating/dirt.dm
index 580aadc85f..0d13b93432 100644
--- a/code/game/turfs/simulated/floor/plating/dirt.dm
+++ b/code/game/turfs/simulated/floor/plating/dirt.dm
@@ -3,19 +3,9 @@
desc = "Upon closer examination, it's still dirt."
icon = 'icons/turf/floors.dmi'
icon_state = "dirt"
- var/smooth_icon = 'icons/turf/floors/dirt.dmi'
- canSmoothWith = list(/turf/closed, /turf/open/floor/plating/dirt)
- smooth = SMOOTH_MORE|SMOOTH_BORDER
- baseturf = /turf/open/chasm/straight_down/lava_land_surface
+ baseturf = /turf/open/chasm/straight_down/jungle
initial_gas_mix = "o2=14;n2=23;TEMP=300"
planetary_atmos = TRUE
-/turf/open/floor/plating/dirt/Initialize()
- pixel_y = -2
- pixel_x = -2
- icon = smooth_icon
- ..()
-
/turf/open/floor/plating/dirt/dark
- icon_state = "darkdirt"
- smooth_icon = 'icons/turf/floors/darkdirt.dmi'
+ icon_state = "greenerdirt"
diff --git a/code/game/turfs/simulated/minerals.dm b/code/game/turfs/simulated/minerals.dm
index 5805d75e56..54f3ad5ad5 100644
--- a/code/game/turfs/simulated/minerals.dm
+++ b/code/game/turfs/simulated/minerals.dm
@@ -63,7 +63,7 @@
if(ismineralturf(src))
to_chat(user, "You finish cutting into the rock.")
gets_drilled(user)
- feedback_add_details("pick_used_mining","[P.type]")
+ SSblackbox.add_details("pick_used_mining","[P.type]")
else
return attack_hand(user)
@@ -72,7 +72,7 @@
var/i
for(i in 1 to mineralAmt)
new mineralType(src)
- feedback_add_details("ore_mined",mineralType)
+ SSblackbox.add_details("ore_mined",mineralType)
ChangeTurf(turf_type, defer_change)
addtimer(CALLBACK(src, .proc/AfterChange), 1, TIMER_UNIQUE)
playsound(src, 'sound/effects/break_stone.ogg', 50, 1) //beautiful destruction
@@ -407,14 +407,14 @@
if(z != 5)
notify_admins = 1
if(!triggered_by_explosion)
- message_admins("[key_name_admin(user)]? (FLW) has triggered a gibtonite deposit reaction at [A.name] (JMP).")
+ message_admins("[ADMIN_LOOKUPFLW(user)] has triggered a gibtonite deposit reaction at [A.name] [ADMIN_JMP(bombturf)].")
else
- message_admins("An explosion has triggered a gibtonite deposit reaction at [A.name] (JMP).")
+ message_admins("An explosion has triggered a gibtonite deposit reaction at [A.name] [ADMIN_JMP(bombturf)].")
if(!triggered_by_explosion)
- log_game("[key_name(user)] has triggered a gibtonite deposit reaction at [A.name] ([A.x], [A.y], [A.z]).")
+ log_game("[key_name(user)] has triggered a gibtonite deposit reaction at [A.name] [ADMIN_JMP(bombturf)].")
else
- log_game("An explosion has triggered a gibtonite deposit reaction at [A.name]([bombturf.x],[bombturf.y],[bombturf.z])")
+ log_game("An explosion has triggered a gibtonite deposit reaction at [A.name] [COORD(bombturf)]")
countdown(notify_admins)
diff --git a/code/game/turfs/simulated/wall/mineral_walls.dm b/code/game/turfs/simulated/wall/mineral_walls.dm
index 355ba66a62..61a26954d3 100644
--- a/code/game/turfs/simulated/wall/mineral_walls.dm
+++ b/code/game/turfs/simulated/wall/mineral_walls.dm
@@ -101,8 +101,8 @@
/turf/closed/wall/mineral/plasma/attackby(obj/item/weapon/W, mob/user, params)
if(W.is_hot() > 300)//If the temperature of the object is over 300, then ignite
- message_admins("Plasma wall ignited by [key_name_admin(user)](?) (FLW) in ([x],[y],[z] - JMP)",0,1)
- log_game("Plasma wall ignited by [key_name(user)] in ([x],[y],[z])")
+ message_admins("Plasma wall ignited by [ADMIN_LOOKUPFLW(user)] in [ADMIN_COORDJMP(src)]",0,1)
+ log_game("Plasma wall ignited by [key_name(user)] in [COORD(src)]")
ignite(W.is_hot())
return
..()
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 1c2d65355e..618894f207 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -398,7 +398,7 @@
/turf/proc/add_blueprints_preround(atom/movable/AM)
- if(!SSticker || SSticker.current_state != GAME_STATE_PLAYING)
+ if(!SSticker.HasRoundStarted())
add_blueprints(AM)
/turf/proc/empty(turf_type=/turf/open/space)
@@ -455,6 +455,8 @@
O = new()
O.underlays.Add(T)
T.ChangeTurf(type)
+ for(var/group in decals)
+ T.add_decal(decals[group],group)
if(underlays.len)
T.underlays = O.underlays
if(T.icon_state != icon_state)
diff --git a/code/modules/VR/vr_sleeper.dm b/code/modules/VR/vr_sleeper.dm
index 41f3bbc001..bba253a9da 100644
--- a/code/modules/VR/vr_sleeper.dm
+++ b/code/modules/VR/vr_sleeper.dm
@@ -9,6 +9,7 @@
icon_state = "sleeper"
state_open = TRUE
anchored = TRUE
+ occupant_typecache = list(/mob/living/carbon/human) // turned into typecache in Initialize
var/you_die_in_the_game_you_die_for_real = FALSE
var/datum/effect_system/spark_spread/sparks
var/mob/living/carbon/human/virtual_reality/vr_human
@@ -17,8 +18,8 @@
var/allow_creating_vr_humans = TRUE //So you can have vr_sleepers that always spawn you as a specific person or 1 life/chance vr games
var/outfit = /datum/outfit/vr_basic
-/obj/machinery/vr_sleeper/New()
- ..()
+/obj/machinery/vr_sleeper/Initialize()
+ . = ..()
sparks = new /datum/effect_system/spark_spread()
sparks.set_up(2,0)
sparks.attach(src)
@@ -92,11 +93,12 @@
return
switch(action)
if("vr_connect")
- if(ishuman(occupant) && occupant.mind)
+ var/mob/living/carbon/human/human_occupant = occupant
+ if(human_occupant && human_occupant.mind)
to_chat(occupant, "Transfering to virtual reality...")
if(vr_human)
vr_human.revert_to_reality(FALSE, FALSE)
- occupant.mind.transfer_to(vr_human)
+ human_occupant.mind.transfer_to(vr_human)
vr_human.real_me = occupant
to_chat(vr_human, "Transfer successful! you are now playing as [vr_human] in VR!")
SStgui.close_user_uis(vr_human, src)
diff --git a/code/modules/admin/DB_ban/functions.dm b/code/modules/admin/DB_ban/functions.dm
index 12517eb385..67d3b394c6 100644
--- a/code/modules/admin/DB_ban/functions.dm
+++ b/code/modules/admin/DB_ban/functions.dm
@@ -71,16 +71,13 @@
computerid = bancid
ip = banip
- var/datum/DBQuery/query_add_ban_get_id = SSdbcore.NewQuery("SELECT id FROM [format_table_name("player")] WHERE ckey = '[ckey]'")
- if(!query_add_ban_get_id.warn_execute())
+ var/datum/DBQuery/query_add_ban_get_ckey = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ckey = '[ckey]'")
+ if(!query_add_ban_get_ckey.warn_execute())
return
- var/validckey = 0
- if(query_add_ban_get_id.NextRow())
- validckey = 1
- if(!validckey)
+ if(!query_add_ban_get_ckey.NextRow())
if(!banned_mob || (banned_mob && !IsGuestKey(banned_mob.key)))
- message_admins("[key_name_admin(usr)] attempted to ban [ckey], but [ckey] has not been seen yet. Please only ban actual players.",1)
- return
+ if(alert(usr, "[ckey] has not been seen before, are you sure you want to create a ban for them?", "Unknown ckey", "Yes", "No", "Cancel") != "Yes")
+ return
var/a_ckey
var/a_computerid
diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm
index 09e3629533..d4fa2e6177 100644
--- a/code/modules/admin/IsBanned.dm
+++ b/code/modules/admin/IsBanned.dm
@@ -63,7 +63,7 @@
if(!SSdbcore.Connect())
log_world("Ban database connection failure. Key [ckeytext] not checked")
- GLOB.diary << "Ban database connection failure. Key [ckeytext] not checked"
+ GLOB.world_game_log << "Ban database connection failure. Key [ckeytext] not checked"
return
var/ipquery = ""
diff --git a/code/modules/admin/NewBan.dm b/code/modules/admin/NewBan.dm
index aa8c8db2db..fabda7fc4d 100644
--- a/code/modules/admin/NewBan.dm
+++ b/code/modules/admin/NewBan.dm
@@ -141,7 +141,7 @@ GLOBAL_PROTECT(Banlist)
ban_unban_log_save("[key_name(usr)] unbanned [key]")
log_admin_private("[key_name(usr)] unbanned [key]")
message_admins("[key_name_admin(usr)] unbanned: [key]")
- feedback_inc("ban_unban",1)
+ SSblackbox.inc("ban_unban",1)
usr.client.holder.DB_ban_unban( ckey(key), BANTYPE_ANY_FULLBAN)
for (var/A in GLOB.Banlist.dir)
GLOB.Banlist.cd = "/base/[A]"
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 25b7deba9b..7196319568 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -37,13 +37,18 @@
else
body += " \[Heal\] "
+ if(M.client)
+ body += "
\[First Seen: [M.client.player_join_date]\]\[Byond account registered on: [M.client.account_join_date]\]"
+
+
+
body += "
\[ "
body += "VV - "
body += "TP - "
body += "PM - "
body += "SM - "
- body += "FLW - "
- body += "LOGS\]
"
+ body += "FLW - "
+ body += "LOGS\]
"
body += "Mob type = [M.type]
"
@@ -166,7 +171,7 @@
body += "