diff --git a/SQL/tgstation_schema.sql b/SQL/tgstation_schema.sql
index 46729fc9bb..4c76e1f275 100644
--- a/SQL/tgstation_schema.sql
+++ b/SQL/tgstation_schema.sql
@@ -119,6 +119,7 @@ CREATE TABLE `connection_log` (
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
+
--
-- Table structure for table `death`
--
@@ -129,10 +130,13 @@ DROP TABLE IF EXISTS `death`;
CREATE TABLE `death` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`pod` varchar(50) NOT NULL,
- `coord` varchar(32) NOT NULL,
+ `x_coord` smallint(5) unsigned NOT NULL,
+ `y_coord` smallint(5) unsigned NOT NULL,
+ `z_coord` smallint(5) unsigned NOT NULL,
`mapname` varchar(32) NOT NULL,
`server_ip` int(10) unsigned NOT NULL,
`server_port` smallint(5) unsigned NOT NULL,
+ `round_id` int(11) NOT NULL
`tod` datetime NOT NULL COMMENT 'Time of death',
`job` varchar(32) NOT NULL,
`special` varchar(32) DEFAULT NULL,
@@ -140,7 +144,6 @@ CREATE TABLE `death` (
`byondkey` varchar(32) NOT NULL,
`laname` varchar(96) DEFAULT NULL,
`lakey` varchar(32) DEFAULT NULL,
- `gender` enum('neuter','male','female','plural') NOT NULL,
`bruteloss` smallint(5) unsigned NOT NULL,
`brainloss` smallint(5) unsigned NOT NULL,
`fireloss` smallint(5) unsigned NOT NULL,
@@ -152,6 +155,7 @@ CREATE TABLE `death` (
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
+
--
-- Table structure for table `feedback`
--
diff --git a/_maps/basemap.dm b/_maps/basemap.dm
index 20396b128a..fd98837cd1 100644
--- a/_maps/basemap.dm
+++ b/_maps/basemap.dm
@@ -1,13 +1,10 @@
-#ifndef ALL_MAPS
-
#include "map_files\generic\Centcomm.dmm"
#include "map_files\generic\SpaceStation.dmm"
#include "map_files\generic\Space.dmm"
#include "map_files\generic\SpaceDock.dmm"
#include "map_files\Mining\Lavaland.dmm"
-#else
-
+#ifdef ALL_MAPS
#include "map_files\debug\runtimestation.dmm"
#include "map_files\Deltastation\DeltaStation2.dmm"
#include "map_files\MetaStation\MetaStation.dmm"
diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index b29d933866..26c163aef4 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -5,4 +5,19 @@
#define ANTAG_DATUM_DEVIL /datum/antagonist/devil
#define ANTAG_DATUM_NINJA /datum/antagonist/ninja
#define ANTAG_DATUM_NINJA_FRIENDLY /datum/antagonist/ninja/friendly
-#define ANTAG_DATUM_NINJA_RANDOM /datum/antagonist/ninja/randomAllegiance/
\ No newline at end of file
+#define ANTAG_DATUM_NINJA_RANDOM /datum/antagonist/ninja/randomAllegiance/
+#define ANTAG_DATUM_TRAITOR /datum/antagonist/traitor
+#define ANTAG_DATUM_TRAITOR_CUSTOM /datum/antagonist/traitor/custom
+#define ANTAG_DATUM_IAA /datum/antagonist/traitor/internal_affairs
+#define ANTAG_DATUM_TRAITOR /datum/antagonist/traitor
+#define ANTAG_DATUM_TRAITOR_CUSTOM /datum/antagonist/traitor/custom
+#define ANTAG_DATUM_TRAITOR_HUMAN /datum/antagonist/traitor/human
+#define ANTAG_DATUM_TRAITOR_HUMAN_CUSTOM /datum/antagonist/traitor/human/custom
+#define ANTAG_DATUM_TRAITOR_AI /datum/antagonist/traitor/AI
+#define ANTAG_DATUM_TRAITOR_AI_CUSTOM /datum/antagonist/traitor/AI/custom
+#define ANTAG_DATUM_IAA /datum/antagonist/traitor/internal_affairs
+#define ANTAG_DATUM_IAA_CUSTOM /datum/antagonist/traitor/internal_affairs/custom
+#define ANTAG_DATUM_IAA_HUMAN /datum/antagonist/traitor/human/internal_affairs
+#define ANTAG_DATUM_IAA_HUMAN_CUSTOM /datum/antagonist/traitor/human/internal_affairs/custom
+#define ANTAG_DATUM_IAA_AI_CUSTOM /datum/antagonist/traitor/AI/internal_affairs/custom
+#define ANTAG_DATUM_IAA_AI /datum/antagonist/traitor/AI/internal_affairs
\ No newline at end of file
diff --git a/code/__DEFINES/clockcult.dm b/code/__DEFINES/clockcult.dm
index 7459ded715..a12cedcd45 100644
--- a/code/__DEFINES/clockcult.dm
+++ b/code/__DEFINES/clockcult.dm
@@ -21,10 +21,10 @@ GLOBAL_LIST_EMPTY(all_scripture) //a list containing scripture instances; not us
#define SCRIPTURE_PERIPHERAL "Peripheral"
#define SCRIPTURE_DRIVER "Driver"
#define SCRIPTURE_SCRIPT "Script"
-#define SCRIPT_SERVANT_REQ 5
+#define SCRIPT_SERVANT_REQ 6
#define SCRIPT_CACHE_REQ 1
#define SCRIPTURE_APPLICATION "Application"
-#define APPLICATION_SERVANT_REQ 8
+#define APPLICATION_SERVANT_REQ 9
#define APPLICATION_CACHE_REQ 3
#define APPLICATION_CV_REQ 100
#define SCRIPTURE_REVENANT "Revenant"
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index e5d1f4c19e..96e8bd6c05 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -2,6 +2,12 @@
//Misc mob defines
+//Ready states at roundstart for mob/dead/new_player
+#define PLAYER_NOT_READY 0
+#define PLAYER_READY_TO_PLAY 1
+#define PLAYER_READY_TO_OBSERVE 2
+
+
//movement intent defines for the m_intent var
#define MOVE_INTENT_WALK "walk"
#define MOVE_INTENT_RUN "run"
diff --git a/code/__DEFINES/server_tools.dm b/code/__DEFINES/server_tools.dm
index 744559f6c7..1e57d8cb80 100644
--- a/code/__DEFINES/server_tools.dm
+++ b/code/__DEFINES/server_tools.dm
@@ -26,3 +26,4 @@
#define SERVICE_REQUEST_KILL_PROCESS "killme"
#define SERVICE_REQUEST_IRC_BROADCAST "irc"
#define SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE "send2irc"
+#define SERVICE_REQUEST_WORLD_REBOOT "worldreboot"
diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index 0858d6acdb..ee42967a52 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -14,7 +14,9 @@
//prevents distinguishing identical timers with the wait variable
#define TIMER_NO_HASH_WAIT 0x10
-#define TIMER_NO_INVOKE_WARNING 200 //number of byond ticks that are allowed to pass before the timer subsystem thinks it hung on something
+#define TIMER_NO_INVOKE_WARNING 600 //number of byond ticks that are allowed to pass before the timer subsystem thinks it hung on something
+
+#define TIMER_ID_NULL -1
//For servers that can't do with any additional lag, set this to none in flightpacks.dm in subsystem/processing.
#define FLIGHTSUIT_PROCESSING_NONE 0
@@ -42,7 +44,8 @@
// Subsystems shutdown in the reverse of the order they initialize in
// The numbers just define the ordering, they are meaningless otherwise.
-#define INIT_ORDER_DBCORE 17
+#define INIT_ORDER_DBCORE 18
+#define INIT_ORDER_BLACKBOX 17
#define INIT_ORDER_SERVER_MAINT 16
#define INIT_ORDER_JOBS 15
#define INIT_ORDER_EVENTS 14
diff --git a/code/__HELPERS/names.dm b/code/__HELPERS/names.dm
index 5074ae4e38..67179e8c8f 100644
--- a/code/__HELPERS/names.dm
+++ b/code/__HELPERS/names.dm
@@ -55,19 +55,19 @@ GLOBAL_VAR(command_name)
return capitalize(name)
/proc/station_name()
- if(!GLOB.station_name)
- var/newname
- if(config && config.station_name)
- newname = config.station_name
- else
- newname = new_station_name()
+ if(!GLOB.station_name)
+ var/newname
+ if(config && config.station_name)
+ newname = config.station_name
+ else
+ newname = new_station_name()
- set_station_name(newname)
-
- return GLOB.station_name
-
-/proc/set_station_name(newname)
- GLOB.station_name = newname
+ set_station_name(newname)
+
+ return GLOB.station_name
+
+/proc/set_station_name(newname)
+ GLOB.station_name = newname
if(config && config.server_name)
world.name = "[config.server_name][config.server_name==GLOB.station_name ? "" : ": [GLOB.station_name]"]"
@@ -167,9 +167,13 @@ GLOBAL_VAR(syndicate_code_response) //Code response for traitors.
/N
*/
-/proc/generate_code_phrase()//Proc is used for phrase and response in master_controller.dm
+/proc/generate_code_phrase(return_list=FALSE)//Proc is used for phrase and response in master_controller.dm
+
+ if(!return_list)
+ . = ""
+ else
+ . = list()
- var/code_phrase = ""//What is returned when the proc finishes.
var/words = pick(//How many words there will be. Minimum of two. 2, 4 and 5 have a lesser chance of being selected. 3 is the most likely.
50; 2,
200; 3,
@@ -204,39 +208,39 @@ GLOBAL_VAR(syndicate_code_response) //Code response for traitors.
switch(rand(1,2))//Mainly to add more options later.
if(1)
if(names.len&&prob(70))
- code_phrase += pick(names)
+ . += pick(names)
else
if(prob(10))
- code_phrase += pick(lizard_name(MALE),lizard_name(FEMALE))
+ . += pick(lizard_name(MALE),lizard_name(FEMALE))
else
- code_phrase += pick(pick(GLOB.first_names_male,GLOB.first_names_female))
- code_phrase += " "
- code_phrase += pick(GLOB.last_names)
+ var/new_name = pick(pick(GLOB.first_names_male,GLOB.first_names_female))
+ new_name += " "
+ new_name += pick(GLOB.last_names)
+ . += new_name
if(2)
- code_phrase += pick(get_all_jobs())//Returns a job.
+ . += pick(get_all_jobs())//Returns a job.
safety -= 1
if(2)
switch(rand(1,3))//Food, drinks, or things. Only selectable once.
if(1)
- code_phrase += lowertext(pick(drinks))
+ . += lowertext(pick(drinks))
if(2)
- code_phrase += lowertext(pick(foods))
+ . += lowertext(pick(foods))
if(3)
- code_phrase += lowertext(pick(locations))
+ . += lowertext(pick(locations))
safety -= 2
if(3)
switch(rand(1,4))//Abstract nouns, objects, adjectives, threats. Can be selected more than once.
if(1)
- code_phrase += lowertext(pick(nouns))
+ . += lowertext(pick(nouns))
if(2)
- code_phrase += lowertext(pick(objects))
+ . += lowertext(pick(objects))
if(3)
- code_phrase += lowertext(pick(adjectives))
+ . += lowertext(pick(adjectives))
if(4)
- code_phrase += lowertext(pick(threats))
- if(words==1)
- code_phrase += "."
- else
- code_phrase += ", "
-
- return code_phrase
+ . += lowertext(pick(threats))
+ if(!return_list)
+ if(words==1)
+ . += "."
+ else
+ . += ", "
diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm
index 48bdbad112..aeadfcf94e 100644
--- a/code/controllers/subsystem/atoms.dm
+++ b/code/controllers/subsystem/atoms.dm
@@ -98,7 +98,7 @@ SUBSYSTEM_DEF(atoms)
else if(!A.initialized)
BadInitializeCalls[the_type] |= BAD_INIT_DIDNT_INIT
- return qdeleted || QDELETED(A)
+ return qdeleted || QDELING(A)
/datum/controller/subsystem/atoms/proc/map_loader_begin()
old_initialized = initialized
diff --git a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm
index 1496479fb7..a4e3aa2fef 100644
--- a/code/controllers/subsystem/blackbox.dm
+++ b/code/controllers/subsystem/blackbox.dm
@@ -3,6 +3,7 @@ SUBSYSTEM_DEF(blackbox)
wait = 6000
flags = SS_NO_TICK_CHECK | SS_NO_INIT
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
+ init_order = INIT_ORDER_BLACKBOX
var/list/msg_common = list()
var/list/msg_science = list()
@@ -18,6 +19,8 @@ SUBSYSTEM_DEF(blackbox)
var/list/feedback = list() //list of datum/feedback_variable
+ var/sealed = FALSE //time to stop tracking stats?
+
//poll population
/datum/controller/subsystem/blackbox/fire()
if(!SSdbcore.Connect())
@@ -45,6 +48,8 @@ SUBSYSTEM_DEF(blackbox)
feedback = SSblackbox.feedback
+ sealed = SSblackbox.sealed
+
//no touchie
/datum/controller/subsystem/blackbox/can_vv_get(var_name)
if(var_name == "feedback")
@@ -55,6 +60,7 @@ SUBSYSTEM_DEF(blackbox)
return FALSE
/datum/controller/subsystem/blackbox/Shutdown()
+ sealed = FALSE
set_val("ahelp_unresolved", GLOB.ahelp_tickets.active_tickets.len)
var/pda_msg_amt = 0
@@ -95,6 +101,8 @@ SUBSYSTEM_DEF(blackbox)
SSdbcore.MassInsert(format_table_name("feedback"), sqlrowlist, ignore_errors = TRUE, delayed = TRUE)
/datum/controller/subsystem/blackbox/proc/LogBroadcast(blackbox_msg, freq)
+ if(sealed)
+ return
switch(freq)
if(1459)
msg_common += blackbox_msg
@@ -129,26 +137,38 @@ SUBSYSTEM_DEF(blackbox)
return FV
/datum/controller/subsystem/blackbox/proc/set_val(variable, value)
+ if(sealed)
+ return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.set_value(value)
/datum/controller/subsystem/blackbox/proc/inc(variable, value)
+ if(sealed)
+ return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.inc(value)
/datum/controller/subsystem/blackbox/proc/dec(variable,value)
+ if(sealed)
+ return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.dec(value)
/datum/controller/subsystem/blackbox/proc/set_details(variable,details)
+ if(sealed)
+ return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.set_details(details)
/datum/controller/subsystem/blackbox/proc/add_details(variable,details)
+ if(sealed)
+ return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.add_details(details)
/datum/controller/subsystem/blackbox/proc/ReportDeath(mob/living/L)
+ if(sealed)
+ return
if(!SSdbcore.Connect())
return
if(!L || !L.key || !L.mind)
@@ -166,19 +186,29 @@ SUBSYSTEM_DEF(blackbox)
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.getToxLoss())
- var/sqlclone = sanitizeSQL(L.getCloneLoss())
+ var/sqltox = sanitizeSQL(L.getToxLoss())
+ var/sqlclone = sanitizeSQL(L.getCloneLoss())
var/sqlstamina = sanitizeSQL(L.getStaminaLoss())
- var/coord = sanitizeSQL("[L.x], [L.y], [L.z]")
+ var/x_coord = sanitizeSQL(L.x)
+ var/y_coord = sanitizeSQL(L.y)
+ var/z_coord = sanitizeSQL(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(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]')")
+ var/datum/DBQuery/query_report_death = SSdbcore.NewQuery("INSERT INTO [format_table_name("death")] (pod, x_coord, y_coord, z_coord, mapname, server_ip, server_port, round_id, tod, job, special, name, byondkey, laname, lakey, bruteloss, fireloss, brainloss, oxyloss, toxloss, cloneloss, staminaloss) VALUES ('[sqlpod]', '[x_coord]', '[y_coord]', '[z_coord]', '[map]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', [GLOB.round_id], '[SQLtime()]', '[sqljob]', '[sqlspecial]', '[sqlname]', '[sqlkey]', '[laname]', '[lakey]', [sqlbrute], [sqlfire], [sqlbrain], [sqloxy], [sqltox], [sqlclone], [sqlstamina])")
query_report_death.Execute()
+/datum/controller/subsystem/blackbox/proc/Seal()
+ if(sealed)
+ return
+ if(IsAdminAdvancedProcCall())
+ var/msg = "[key_name_admin(usr)] sealed the blackbox!"
+ message_admins(msg)
+ log_game("Blackbox sealed[IsAdminAdvancedProcCall() ? " by [key_name(usr)]" : ""].")
+ sealed = TRUE
+
//feedback variable datum, for storing all kinds of data
/datum/feedback_variable
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index aefd62ed8e..3af0bcc399 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -224,7 +224,7 @@ SUBSYSTEM_DEF(job)
//Get the players who are ready
for(var/mob/dead/new_player/player in GLOB.player_list)
- if(player.ready && player.mind && !player.mind.assigned_role)
+ if(player.ready == PLAYER_READY_TO_PLAY && player.mind && !player.mind.assigned_role)
unassigned += player
initial_players_to_assign = unassigned.len
@@ -455,7 +455,7 @@ SUBSYSTEM_DEF(job)
var/level5 = 0 //banned
var/level6 = 0 //account too young
for(var/mob/dead/new_player/player in GLOB.player_list)
- if(!(player.ready && player.mind && !player.mind.assigned_role))
+ if(!(player.ready == PLAYER_READY_TO_PLAY && player.mind && !player.mind.assigned_role))
continue //This player is not ready
if(jobban_isbanned(player, job.title))
level5++
@@ -488,7 +488,7 @@ SUBSYSTEM_DEF(job)
Debug("Popcap overflow Check observer located, Player: [player]")
to_chat(player, "You have failed to qualify for any job you desired.")
unassigned -= player
- player.ready = 0
+ player.ready = PLAYER_NOT_READY
/datum/controller/subsystem/job/Recover()
@@ -533,7 +533,7 @@ SUBSYSTEM_DEF(job)
if(avail.len)
SendToAtom(M, pick(avail), FALSE)
return
-
+
//pick an open spot on arrivals and dump em
var/list/arrivals_turfs = shuffle(get_area_turfs(/area/shuttle/arrival))
if(arrivals_turfs.len)
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index afff0cdf59..8933f0ff09 100755
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -86,6 +86,8 @@ SUBSYSTEM_DEF(ticker)
window_flash(C, ignorepref = TRUE) //let them know lobby has opened up.
to_chat(world, "Welcome to [station_name()]!")
current_state = GAME_STATE_PREGAME
+ //Everyone who wants to be an observer is now spawned
+ create_observers()
if(!modevoted)
send_gamemode_vote()
fire()
@@ -97,7 +99,7 @@ SUBSYSTEM_DEF(ticker)
totalPlayersReady = 0
for(var/mob/dead/new_player/player in GLOB.player_list)
++totalPlayers
- if(player.ready)
+ if(player.ready == PLAYER_READY_TO_PLAY)
++totalPlayersReady
if(start_immediately)
@@ -131,7 +133,7 @@ SUBSYSTEM_DEF(ticker)
scripture_states = scripture_unlock_alert(scripture_states)
SSshuttle.autoEnd()
- if(!mode.explosion_in_progress && mode.check_finished() || force_ending)
+ if(!mode.explosion_in_progress && mode.check_finished(force_ending) || force_ending)
current_state = GAME_STATE_FINISHED
toggle_ooc(1) // Turn it on
declare_completion(force_ending)
@@ -408,7 +410,7 @@ SUBSYSTEM_DEF(ticker)
/datum/controller/subsystem/ticker/proc/create_characters()
for(var/mob/dead/new_player/player in GLOB.player_list)
- if(player.ready && player.mind)
+ if(player.ready == PLAYER_READY_TO_PLAY && player.mind)
GLOB.joined_player_list += player.ckey
player.create_character(FALSE)
else
@@ -584,7 +586,7 @@ SUBSYSTEM_DEF(ticker)
mode.declare_station_goal_completion()
//medals, placed far down so that people can actually see the commendations.
- if(GLOB.commendations)
+ if(GLOB.commendations.len)
to_chat(world, "Medal Commendations:")
for (var/com in GLOB.commendations)
to_chat(world, com)
@@ -612,6 +614,9 @@ SUBSYSTEM_DEF(ticker)
//Collects persistence features
SSpersistence.CollectData()
+ //stop collecting feedback during grifftime
+ SSblackbox.Seal()
+
sleep(50)
if(mode.station_was_nuked)
Reboot("Station destroyed by Nuclear Device.", "nuke")
@@ -793,6 +798,13 @@ SUBSYSTEM_DEF(ticker)
else
timeLeft = newtime
+//Everyone who wanted to be an observer gets made one now
+/datum/controller/subsystem/ticker/proc/create_observers()
+ for(var/mob/dead/new_player/player in GLOB.player_list)
+ if(player.ready == PLAYER_READY_TO_OBSERVE && player.mind)
+ //Break chain since this has a sleep input in it
+ addtimer(CALLBACK(player, /mob/dead/new_player.proc/make_me_an_observer), 1)
+
/datum/controller/subsystem/ticker/proc/load_mode()
var/mode = trim(file2text("data/mode.txt"))
if(mode)
diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm
index 2203e0f1f5..3ebf8f0952 100644
--- a/code/controllers/subsystem/timer.dm
+++ b/code/controllers/subsystem/timer.dm
@@ -43,7 +43,7 @@ SUBSYSTEM_DEF(timer)
WARNING(msg)
if(bucket_auto_reset)
bucket_resolution = 0
-
+
log_world("Active timers at tick [world.time]:")
for(var/I in processing)
var/datum/timedevent/TE = I
@@ -365,10 +365,13 @@ SUBSYSTEM_DEF(timer)
var/datum/timedevent/timer = new(callback, timeToRun, flags, hash)
if (flags & TIMER_STOPPABLE)
return timer.id
+ return TIMER_ID_NULL
/proc/deltimer(id)
if (!id)
return FALSE
+ if (id == TIMER_ID_NULL)
+ CRASH("Tried to delete a null timerid. Use TIMER_STOPPABLE flag")
if (!istext(id))
if (istype(id, /datum/timedevent))
qdel(id)
diff --git a/code/datums/antagonists/datum_clockcult.dm b/code/datums/antagonists/datum_clockcult.dm
index 6c8b83daa4..f62bb7f9ae 100644
--- a/code/datums/antagonists/datum_clockcult.dm
+++ b/code/datums/antagonists/datum_clockcult.dm
@@ -20,22 +20,28 @@
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.")
+ if(.)
+ to_chat(new_owner.current, "The world before you suddenly glows a brilliant yellow. [issilicon(new_owner.current) ? "You cannot compute this truth!" : \
+ "Your mind is racing!"] You hear the whooshing steam and cl[pick("ank", "ink", "unk", "ang")]ing cogs of a billion billion machines, and all at once it comes to you.
\
+ Ratvar, the Clockwork Justiciar, [GLOB.ratvar_awakens ? "has been freed from his eternal prison" : "lies in exile, derelict and forgotten in an unseen realm"].")
+ flash_color(new_owner.current, flash_color = list("#BE8700", "#BE8700", "#BE8700", rgb(0,0,0)), flash_time = 50)
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.")
+ new_owner.current.visible_message("[new_owner.current] seems to resist an unseen force!", null, null, 7, new_owner.current)
+ to_chat(new_owner.current, "The world before you suddenly glows a brilliant yellow. [issilicon(new_owner.current) ? "You cannot compute this truth!" : \
+ "Your mind is racing!"] You hear the whooshing steam and cl[pick("ank", "ink", "unk", "ang")]ing cogs of a billion billion machines, and the sound \
+ is a meaningless cacophony.
\
+ You see an abomination of rusting parts[GLOB.ratvar_awakens ? ", and it is here.
It is too late" : \
+ " in an endless grey void.
It cannot be allowed to escape"].")
+ owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/clockcultalr.ogg', 40, TRUE, frequency = 100000, pressure_affected = FALSE)
+ flash_color(new_owner.current, flash_color = list("#BE8700", "#BE8700", "#BE8700", rgb(0,0,0)), flash_time = 5)
/datum/antagonist/clockcult/greet()
if(!owner.current || silent)
return
- owner.current.visible_message("[owner.current]'s eyes glow a blazing yellow!")
+ owner.current.visible_message("[owner.current]'s eyes glow a blazing yellow!", null, null, 7, owner.current) //don't show the owner this message
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.")
+ owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/clockcultalr.ogg', 70, FALSE, pressure_affected = FALSE)
/datum/antagonist/clockcult/on_gain()
var/mob/living/current = owner.current
@@ -47,10 +53,14 @@
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.")
+ var/mob/living/silicon/robot/R = current
+ if(R.connected_ai && !is_servant_of_ratvar(R.connected_ai))
+ to_chat(R, "You have been desynced from your master AI.
\
+ In addition, your onboard camera is no longer active and you have gained additional equipment, including a limited clockwork slab.")
+ else
+ to_chat(R, "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 are now able to use your cameras to listen in on conversations, but can no longer speak in anything but Ratvarian.")
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.")
@@ -88,11 +98,7 @@
A.eyeobj.relay_speech = TRUE
for(var/mob/living/silicon/robot/R in A.connected_robots)
if(R.connected_ai == A)
- R.visible_message("[R]'s eyes glow a blazing yellow!", \
- "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.")
- to_chat(R, "Your onboard camera is no longer active and you have gained additional equipment, including a limited clockwork slab.")
- add_servant_of_ratvar(R, TRUE)
+ add_servant_of_ratvar(R)
S.laws = new/datum/ai_laws/ratvar
S.laws.associate(S)
S.update_icons()
diff --git a/code/datums/antagonists/datum_iaa.dm b/code/datums/antagonists/datum_iaa.dm
new file mode 100644
index 0000000000..fd2c4e89a0
--- /dev/null
+++ b/code/datums/antagonists/datum_iaa.dm
@@ -0,0 +1,11 @@
+/datum/antagonist/iaa
+
+/datum/antagonist/iaa/apply_innate_effects()
+ .=..() //in case the base is used in future
+ if(owner&&owner.current)
+ give_pinpointer(owner.current)
+
+/datum/antagonist/iaa/remove_innate_effects()
+ .=..()
+ if(owner&&owner.current)
+ owner.current.remove_status_effect(/datum/status_effect/agent_pinpointer)
\ No newline at end of file
diff --git a/code/datums/antagonists/datum_internal_affairs.dm b/code/datums/antagonists/datum_internal_affairs.dm
new file mode 100644
index 0000000000..7cc3bed0af
--- /dev/null
+++ b/code/datums/antagonists/datum_internal_affairs.dm
@@ -0,0 +1,327 @@
+#define PINPOINTER_MINIMUM_RANGE 15
+#define PINPOINTER_EXTRA_RANDOM_RANGE 10
+#define PINPOINTER_PING_TIME 40
+#define PROB_ACTUAL_TRAITOR 20
+#define TRAITOR_AGENT_ROLE "Syndicate External Affairs Agent"
+
+/datum/antagonist/traitor/internal_affairs
+ base_datum_custom = ANTAG_DATUM_IAA_CUSTOM
+ human_datum = ANTAG_DATUM_IAA_HUMAN
+ ai_datum = ANTAG_DATUM_IAA_AI
+
+
+
+/datum/antagonist/traitor/AI/internal_affairs
+ name = "Internal Affairs Agent"
+ employer = "Nanotrasen"
+ special_role = "internal affairs agent"
+ base_datum_custom = ANTAG_DATUM_IAA_CUSTOM
+ var/syndicate = FALSE
+ var/last_man_standing = FALSE
+ var/list/datum/mind/targets_stolen
+
+/datum/antagonist/traitor/AI/internal_affairs/custom
+ silent = TRUE
+ should_give_codewords = FALSE
+ give_objectives = FALSE
+
+/datum/antagonist/traitor/human/internal_affairs
+ name = "Internal Affairs Agent"
+ employer = "Nanotrasen"
+ special_role = "internal affairs agent"
+ base_datum_custom = ANTAG_DATUM_IAA_CUSTOM
+ var/syndicate = FALSE
+ var/last_man_standing = FALSE
+ var/list/datum/mind/targets_stolen
+
+/datum/antagonist/traitor/human/internal_affairs/custom
+ silent = TRUE
+ should_give_codewords = FALSE
+ give_objectives = FALSE
+ should_equip = FALSE //Duplicating TCs is dangerous
+
+/datum/antagonist/traitor/human/internal_affairs/transfer_important_variables(datum/antagonist/traitor/human/internal_affairs/other)
+ ..(other)
+ other.syndicate = syndicate
+ other.last_man_standing = last_man_standing
+ other.targets_stolen = targets_stolen
+
+/datum/antagonist/traitor/AI/internal_affairs/transfer_important_variables(datum/antagonist/traitor/human/internal_affairs/other)
+ ..(other)
+ other.syndicate = syndicate
+ other.last_man_standing = last_man_standing
+ other.targets_stolen = targets_stolen
+
+/datum/antagonist/traitor/human/internal_affairs/proc/give_pinpointer()
+ if(owner && owner.current)
+ owner.current.apply_status_effect(/datum/status_effect/agent_pinpointer)
+
+/datum/antagonist/traitor/human/internal_affairs/apply_innate_effects()
+ .=..() //in case the base is used in future
+ if(owner&&owner.current)
+ give_pinpointer(owner.current)
+
+/datum/antagonist/traitor/human/internal_affairs/remove_innate_effects()
+ .=..()
+ if(owner&&owner.current)
+ owner.current.remove_status_effect(/datum/status_effect/agent_pinpointer)
+
+/datum/antagonist/traitor/internal_affairs/custom
+ ai_datum = ANTAG_DATUM_IAA_AI_CUSTOM
+ human_datum = ANTAG_DATUM_IAA_HUMAN_CUSTOM
+
+/datum/antagonist/traitor/human/internal_affairs/on_gain()
+ START_PROCESSING(SSprocessing, src)
+ .=..()
+/datum/antagonist/traitor/human/internal_affairs/on_removal()
+ STOP_PROCESSING(SSprocessing,src)
+ .=..()
+/datum/antagonist/traitor/human/internal_affairs/process()
+ iaa_process()
+
+/datum/antagonist/traitor/AI/internal_affairs/on_gain()
+ START_PROCESSING(SSprocessing, src)
+ .=..()
+/datum/antagonist/traitor/AI/internal_affairs/on_removal()
+ STOP_PROCESSING(SSprocessing,src)
+ .=..()
+/datum/antagonist/traitor/AI/internal_affairs/process()
+ iaa_process()
+
+/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/is_internal_objective(datum/objective/O)
+ return (istype(O, /datum/objective/assassinate/internal)||istype(O, /datum/objective/destroy/internal))
+
+/datum/antagonist/traitor/proc/replace_escape_objective()
+ if(!owner||!owner.objectives)
+ return
+ for (var/objective_ in owner.objectives)
+ if(!(istype(objective_, /datum/objective/escape)||istype(objective_,/datum/objective/survive)))
+ continue
+ remove_objective(objective_)
+
+ var/datum/objective/martyr/martyr_objective = new
+ martyr_objective.owner = owner
+ add_objective(martyr_objective)
+
+/datum/antagonist/traitor/proc/reinstate_escape_objective()
+ if(!owner||!owner.objectives)
+ return
+ for (var/objective_ in owner.objectives)
+ if(!istype(objective_, /datum/objective/martyr))
+ continue
+ remove_objective(objective_)
+
+/datum/antagonist/traitor/human/internal_affairs/reinstate_escape_objective()
+ ..()
+ var/datum/objective/escape/escape_objective = new
+ escape_objective.owner = owner
+ add_objective(escape_objective)
+
+/datum/antagonist/traitor/AI/internal_affairs/reinstate_escape_objective()
+ ..()
+ var/datum/objective/survive/survive_objective = new
+ survive_objective.owner = owner
+ add_objective(survive_objective)
+
+/datum/antagonist/traitor/proc/steal_targets(datum/mind/victim)
+ var/datum/antagonist/traitor/human/internal_affairs/this = src //Should only use this if IAA
+
+ if(!owner.current||owner.current.stat==DEAD)
+ return
+ 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)
+ continue
+ else if(this.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()
+ add_objective(new_objective)
+ this.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)
+ continue
+ else if(this.targets_stolen.Find(objective.target) == 0)
+ new_objective.owner = owner
+ new_objective.target = objective.target
+ new_objective.update_explanation_text()
+ add_objective(new_objective)
+ this.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]) ")
+ this.last_man_standing = TRUE
+ for(var/objective_ in owner.objectives)
+ if(!is_internal_objective(objective_))
+ continue
+ var/datum/objective/assassinate/internal/objective = objective_
+ if(!objective.check_completion())
+ this.last_man_standing = FALSE
+ return
+ if(this.last_man_standing)
+ if(this.syndicate)
+ to_chat(owner.current," All the loyalist agents are dead, and no more is required of you. Die a glorious death, agent. ")
+ else
+ 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/antagonist/traitor/proc/iaa_process()
+ var/datum/antagonist/traitor/human/internal_affairs/this = src //Should only use this if IAA
+ 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(this.last_man_standing)
+ if(this.syndicate)
+ fail_msg += " You no longer have permission to die. "
+ else
+ 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)
+ this.last_man_standing = FALSE
+ to_chat(owner.current, fail_msg)
+ objective.stolen = FALSE
+
+/datum/antagonist/traitor/proc/forge_iaa_objectives()
+ var/datum/antagonist/traitor/human/internal_affairs/this = src //Should only use this if IAA
+ if(SSticker.mode.target_list.len && SSticker.mode.target_list[owner]) // Is a double agent
+
+ // Assassinate
+ var/datum/mind/target_mind = SSticker.mode.target_list[owner]
+ if(issilicon(target_mind.current))
+ var/datum/objective/destroy/internal/destroy_objective = new
+ destroy_objective.owner = owner
+ destroy_objective.target = target_mind
+ destroy_objective.update_explanation_text()
+ else
+ var/datum/objective/assassinate/internal/kill_objective = new
+ kill_objective.owner = owner
+ kill_objective.target = target_mind
+ kill_objective.update_explanation_text()
+ add_objective(kill_objective)
+
+ //Optional traitor objective
+ if(prob(PROB_ACTUAL_TRAITOR))
+ employer = "The Syndicate"
+ owner.special_role = TRAITOR_AGENT_ROLE
+ special_role = TRAITOR_AGENT_ROLE
+ this.syndicate = TRUE
+ forge_single_objective()
+
+ else
+ ..() // Give them standard objectives.
+ return
+
+/datum/antagonist/traitor/human/internal_affairs/forge_traitor_objectives()
+ forge_iaa_objectives()
+ var/datum/objective/escape/escape_objective = new
+ escape_objective.owner = owner
+ add_objective(escape_objective)
+
+/datum/antagonist/traitor/AI/internal_affairs/forge_traitor_objectives()
+ forge_iaa_objectives()
+ var/datum/objective/survive/survive_objective = new
+ survive_objective.owner = owner
+ add_objective(survive_objective)
+
+/datum/antagonist/traitor/proc/greet_iaa()
+ var/datum/antagonist/traitor/human/internal_affairs/this = src //Should only use this if IAA
+ 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(owner.current, "You are the [special_role].")
+ if(this.syndicate)
+ to_chat(owner.current, "Your target has been framed for [crime], and you have been tasked with eliminating them to prevent them defending themselves in court.")
+ to_chat(owner.current, "Any damage you cause will be a further embarrassment to Nanotrasen, so you have no limits on collateral damage.")
+ to_chat(owner.current, " You have been provided with a standard uplink to accomplish your task. ")
+ else
+ to_chat(owner.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(owner.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(owner.current, "For the sake of plausible deniability, you have been equipped with an array of captured Syndicate weaponry available via uplink.")
+
+ to_chat(owner.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.")
+ owner.announce_objectives()
+
+/datum/antagonist/traitor/AI/internal_affairs/greet()
+ greet_iaa()
+/datum/antagonist/traitor/human/internal_affairs/greet()
+ greet_iaa()
+
+
+#undef PROB_ACTUAL_TRAITOR
+#undef PINPOINTER_EXTRA_RANDOM_RANGE
+#undef PINPOINTER_MINIMUM_RANGE
+#undef PINPOINTER_PING_TIME
diff --git a/code/datums/antagonists/datum_traitor.dm b/code/datums/antagonists/datum_traitor.dm
new file mode 100644
index 0000000000..e2536ad077
--- /dev/null
+++ b/code/datums/antagonists/datum_traitor.dm
@@ -0,0 +1,326 @@
+/datum/antagonist/traitor
+ name = "Traitor"
+ var/should_specialise = TRUE //do we split into AI and human
+ var/base_datum_custom = ANTAG_DATUM_TRAITOR_CUSTOM //used for body transfer
+ var/ai_datum = ANTAG_DATUM_TRAITOR_AI
+ var/human_datum = ANTAG_DATUM_TRAITOR_HUMAN
+ var/special_role = "traitor"
+ var/employer = "The Syndicate"
+ var/give_objectives = TRUE
+ var/should_give_codewords = TRUE
+ var/list/objectives_given = list()
+
+/datum/antagonist/traitor/proc/transfer_important_variables(datum/antagonist/traitor/other)
+ other.silent = silent
+ other.employer = employer
+ other.special_role = special_role
+ other.objectives_given = objectives_given
+
+/datum/antagonist/traitor/custom
+ ai_datum = ANTAG_DATUM_TRAITOR_AI_CUSTOM
+ human_datum = ANTAG_DATUM_TRAITOR_HUMAN_CUSTOM
+
+/datum/antagonist/traitor/human
+ should_specialise = FALSE
+ var/should_equip = TRUE
+/datum/antagonist/traitor/human/custom
+ silent = TRUE
+ should_give_codewords = FALSE
+ give_objectives = FALSE
+ should_equip = FALSE //Duplicating TCs is dangerous
+
+/datum/antagonist/traitor/AI
+ should_specialise = FALSE
+/datum/antagonist/traitor/AI/custom
+ silent = TRUE
+ should_give_codewords = FALSE
+ give_objectives = FALSE
+
+
+/datum/antagonist/traitor/on_body_transfer(mob/living/old_body, mob/living/new_body)
+ if(istype(new_body,/mob/living/silicon/ai)==istype(old_body,/mob/living/silicon/ai))
+ ..()
+ else
+ silent = TRUE
+ owner.add_antag_datum(base_datum_custom)
+ for(var/datum/antagonist/traitor/new_datum in owner.antag_datums)
+ if(new_datum == src)
+ continue
+ transfer_important_variables(new_datum)
+ break
+ on_removal()
+
+
+
+/datum/antagonist/traitor/human/custom //used to give custom objectives
+ silent = TRUE
+ give_objectives = FALSE
+ should_give_codewords = FALSE
+/datum/antagonist/traitor/AI/custom //used to give custom objectives
+ silent = TRUE
+ give_objectives = FALSE
+ should_give_codewords = FALSE
+
+/datum/antagonist/traitor/proc/specialise()
+ silent = TRUE
+ if(owner.current&&istype(owner.current,/mob/living/silicon/ai))
+ owner.add_antag_datum(ai_datum)
+ else owner.add_antag_datum(human_datum)
+ on_removal()
+
+/datum/antagonist/traitor/on_gain()
+ if(should_specialise)
+ specialise()
+ return
+ SSticker.mode.traitors+=owner
+ owner.special_role = special_role
+ if(give_objectives)
+ forge_traitor_objectives()
+ finalize_traitor()
+ ..()
+
+/datum/antagonist/traitor/apply_innate_effects()
+ if(owner.assigned_role == "Clown")
+ var/mob/living/carbon/human/traitor_mob = owner.current
+ if(traitor_mob&&istype(traitor_mob))
+ if(!silent) to_chat(traitor_mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
+ traitor_mob.dna.remove_mutation(CLOWNMUT)
+
+/datum/antagonist/traitor/remove_innate_effects()
+ if(owner.assigned_role == "Clown")
+ var/mob/living/carbon/human/traitor_mob = owner.current
+ if(traitor_mob&&istype(traitor_mob))
+ traitor_mob.dna.add_mutation(CLOWNMUT)
+
+/datum/antagonist/traitor/on_removal()
+ if(should_specialise)
+ return ..()//we never did any of this anyway
+ SSticker.mode.traitors -= owner
+ for(var/O in objectives_given)
+ owner.objectives -= O
+ objectives_given = list()
+ if(!silent && owner.current)
+ to_chat(owner.current," You are no longer the [special_role]! ")
+ owner.special_role = null
+ ..()
+
+/datum/antagonist/traitor/AI/on_removal()
+ if(owner.current && isAI(owner.current))
+ var/mob/living/silicon/ai/A = owner.current
+ A.set_zeroth_law("")
+ A.verbs -= /mob/living/silicon/ai/proc/choose_modules
+ A.malf_picker.remove_verbs(A)
+ qdel(A.malf_picker)
+ ..()
+
+/datum/antagonist/traitor/proc/add_objective(var/datum/objective/O)
+ owner.objectives += O
+ objectives_given += O
+
+/datum/antagonist/traitor/proc/remove_objective(var/datum/objective/O)
+ owner.objectives -= O
+ objectives_given -= O
+
+/datum/antagonist/traitor/proc/forge_traitor_objectives()
+ return
+/datum/antagonist/traitor/human/forge_traitor_objectives()
+ var/is_hijacker = prob(10)
+ var/martyr_chance = prob(20)
+ var/objective_count = is_hijacker //Hijacking counts towards number of objectives
+ if(!SSticker.mode.exchange_blue && SSticker.mode.traitors.len >= 8) //Set up an exchange if there are enough traitors
+ if(!SSticker.mode.exchange_red)
+ SSticker.mode.exchange_red = owner
+ else
+ SSticker.mode.exchange_blue = owner
+ assign_exchange_role(SSticker.mode.exchange_red)
+ assign_exchange_role(SSticker.mode.exchange_blue)
+ objective_count += 1 //Exchange counts towards number of objectives
+ for(var/i = objective_count, i < config.traitor_objectives_amount, i++)
+ forge_single_objective()
+
+ if(is_hijacker && objective_count <= config.traitor_objectives_amount) //Don't assign hijack if it would exceed the number of objectives set in config.traitor_objectives_amount
+ if (!(locate(/datum/objective/hijack) in owner.objectives))
+ var/datum/objective/hijack/hijack_objective = new
+ hijack_objective.owner = owner
+ add_objective(hijack_objective)
+ return
+
+
+ var/martyr_compatibility = 1 //You can't succeed in stealing if you're dead.
+ for(var/datum/objective/O in owner.objectives)
+ if(!O.martyr_compatible)
+ martyr_compatibility = 0
+ break
+
+ if(martyr_compatibility && martyr_chance)
+ var/datum/objective/martyr/martyr_objective = new
+ martyr_objective.owner = owner
+ add_objective(martyr_objective)
+ return
+
+ else
+ if(!(locate(/datum/objective/escape) in owner.objectives))
+ var/datum/objective/escape/escape_objective = new
+ escape_objective.owner = owner
+ add_objective(escape_objective)
+ return
+
+/datum/antagonist/traitor/AI/forge_traitor_objectives()
+ var/objective_count = 0
+
+ if(prob(30))
+ objective_count += forge_single_objective()
+
+ for(var/i = objective_count, i < config.traitor_objectives_amount, i++)
+ var/datum/objective/assassinate/kill_objective = new
+ kill_objective.owner = owner
+ kill_objective.find_target()
+ add_objective(kill_objective)
+
+ var/datum/objective/survive/survive_objective = new
+ survive_objective.owner = owner
+ add_objective(survive_objective)
+/datum/antagonist/traitor/proc/forge_single_objective()
+ return 0
+/datum/antagonist/traitor/human/forge_single_objective() //Returns how many objectives are added
+ .=1
+ if(prob(50))
+ var/list/active_ais = active_ais()
+ if(active_ais.len && prob(100/GLOB.joined_player_list.len))
+ var/datum/objective/destroy/destroy_objective = new
+ destroy_objective.owner = owner
+ destroy_objective.find_target()
+ add_objective(destroy_objective)
+ else if(prob(30))
+ var/datum/objective/maroon/maroon_objective = new
+ maroon_objective.owner = owner
+ maroon_objective.find_target()
+ add_objective(maroon_objective)
+ else
+ var/datum/objective/assassinate/kill_objective = new
+ kill_objective.owner = owner
+ kill_objective.find_target()
+ add_objective(kill_objective)
+ else
+ var/datum/objective/steal/steal_objective = new
+ steal_objective.owner = owner
+ steal_objective.find_target()
+ add_objective(steal_objective)
+
+/datum/antagonist/traitor/AI/forge_single_objective()
+ .=1
+ var/special_pick = rand(1,4)
+ switch(special_pick)
+ if(1)
+ var/datum/objective/block/block_objective = new
+ block_objective.owner = owner
+ add_objective(block_objective)
+ if(2)
+ var/datum/objective/purge/purge_objective = new
+ purge_objective.owner = owner
+ add_objective(purge_objective)
+ if(3)
+ var/datum/objective/robot_army/robot_objective = new
+ robot_objective.owner = owner
+ add_objective(robot_objective)
+ if(4) //Protect and strand a target
+ var/datum/objective/protect/yandere_one = new
+ yandere_one.owner = owner
+ add_objective(yandere_one)
+ yandere_one.find_target()
+ var/datum/objective/maroon/yandere_two = new
+ yandere_two.owner = owner
+ yandere_two.target = yandere_one.target
+ yandere_two.update_explanation_text() // normally called in find_target()
+ add_objective(yandere_two)
+ .=2
+/datum/antagonist/traitor/greet()
+ to_chat(owner.current, "You are the [owner.special_role].")
+ owner.announce_objectives()
+ if(should_give_codewords)
+ give_codewords()
+
+/datum/antagonist/traitor/proc/finalize_traitor()
+ SSticker.mode.update_traitor_icons_added(owner)
+ return
+
+/datum/antagonist/traitor/AI/finalize_traitor()
+ ..()
+ add_law_zero()
+ owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/malf.ogg', 100, FALSE, pressure_affected = FALSE)
+ owner.current.grant_language(/datum/language/codespeak)
+
+/datum/antagonist/traitor/human/finalize_traitor()
+ ..()
+ if(should_equip)
+ equip(silent)
+ owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/tatoralert.ogg', 100, FALSE, pressure_affected = FALSE)
+
+/datum/antagonist/traitor/proc/give_codewords()
+ if(!owner.current)
+ return
+ var/mob/traitor_mob=owner.current
+
+ 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]")
+
+ traitor_mob.mind.store_memory("Code Phrase: [GLOB.syndicate_code_phrase]")
+ traitor_mob.mind.store_memory("Code Response: [GLOB.syndicate_code_response]")
+
+ to_chat(traitor_mob, "Use the code words in the order provided, during regular conversation, to identify other agents. Proceed with caution, however, as everyone is a potential foe.")
+
+/datum/antagonist/traitor/AI/proc/add_law_zero()
+ var/mob/living/silicon/ai/killer = owner.current
+ if(!killer || !istype(killer))
+ return
+ var/law = "Accomplish your objectives at all costs."
+ var/law_borg = "Accomplish your AI's objectives at all costs."
+ killer.set_zeroth_law(law, law_borg)
+ killer.set_syndie_radio()
+ to_chat(killer, "Your radio has been upgraded! Use :t to speak on an encrypted channel with Syndicate Agents!")
+ killer.add_malf_picker()
+
+/datum/antagonist/traitor/proc/equip(var/silent = FALSE)
+/datum/antagonist/traitor/human/equip(var/silent = FALSE)
+ owner.equip_traitor(employer, silent)
+
+/datum/antagonist/traitor/human/proc/assign_exchange_role()
+ //set faction
+ var/faction = "red"
+ if(owner == SSticker.mode.exchange_blue)
+ faction = "blue"
+
+ //Assign objectives
+ var/datum/objective/steal/exchange/exchange_objective = new
+ exchange_objective.set_faction(faction,((faction == "red") ? SSticker.mode.exchange_blue : SSticker.mode.exchange_red))
+ exchange_objective.owner = owner
+ add_objective(exchange_objective)
+
+ if(prob(20))
+ var/datum/objective/steal/exchange/backstab/backstab_objective = new
+ backstab_objective.set_faction(faction)
+ backstab_objective.owner = owner
+ add_objective(backstab_objective)
+
+ //Spawn and equip documents
+ var/mob/living/carbon/human/mob = owner.current
+
+ var/obj/item/weapon/folder/syndicate/folder
+ if(owner == SSticker.mode.exchange_red)
+ folder = new/obj/item/weapon/folder/syndicate/red(mob.loc)
+ else
+ folder = new/obj/item/weapon/folder/syndicate/blue(mob.loc)
+
+ var/list/slots = list (
+ "backpack" = slot_in_backpack,
+ "left pocket" = slot_l_store,
+ "right pocket" = slot_r_store
+ )
+
+ var/where = "At your feet"
+ var/equipped_slot = mob.equip_in_one_of_slots(folder, slots)
+ if (equipped_slot)
+ where = "In your [equipped_slot]"
+ to_chat(mob, "
[where] is a folder containing secret documents that another Syndicate group wants. We have set up a meeting with one of their agents on station to make an exchange. Exercise extreme caution as they cannot be trusted and may be hostile.
")
+
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index cf7368a879..4e5fa6a612 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -187,15 +187,7 @@
/datum/mind/proc/remove_traitor()
if(src in SSticker.mode.traitors)
- SSticker.mode.traitors -= src
- if(isAI(current))
- var/mob/living/silicon/ai/A = current
- A.set_zeroth_law("")
- A.verbs -= /mob/living/silicon/ai/proc/choose_modules
- A.malf_picker.remove_verbs(A)
- qdel(A.malf_picker)
- special_role = null
- remove_antag_equip()
+ src.remove_antag_datum(ANTAG_DATUM_TRAITOR)
SSticker.mode.update_traitor_icons_removed(src)
/datum/mind/proc/remove_nukeop()
@@ -263,6 +255,68 @@
if(gang_datum)
gang_datum.remove_gang_hud(src)
+/datum/mind/proc/equip_traitor(var/employer = "The Syndicate", var/silent = FALSE)
+ if(!current)
+ return
+ var/mob/living/carbon/human/traitor_mob = current
+ if (!istype(traitor_mob))
+ return
+ . = 1
+
+ var/list/all_contents = traitor_mob.GetAllContents()
+ var/obj/item/device/pda/PDA = locate() in all_contents
+ var/obj/item/device/radio/R = locate() in all_contents
+ var/obj/item/weapon/pen/P = locate() in all_contents //including your PDA-pen!
+
+ var/obj/item/uplink_loc
+
+ if(traitor_mob.client && traitor_mob.client.prefs)
+ switch(traitor_mob.client.prefs.uplink_spawn_loc)
+ if(UPLINK_PDA)
+ uplink_loc = PDA
+ if(!uplink_loc)
+ uplink_loc = R
+ if(!uplink_loc)
+ uplink_loc = P
+ if(UPLINK_RADIO)
+ uplink_loc = R
+ if(!uplink_loc)
+ uplink_loc = PDA
+ if(!uplink_loc)
+ uplink_loc = P
+ if(UPLINK_PEN)
+ uplink_loc = P
+ if(!uplink_loc)
+ uplink_loc = PDA
+ if(!uplink_loc)
+ uplink_loc = R
+
+ if (!uplink_loc)
+ if(!silent) 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)
+ U.owner = "[traitor_mob.key]"
+ uplink_loc.hidden_uplink = U
+
+ if(uplink_loc == R)
+ R.traitor_frequency = sanitize_frequency(rand(MIN_FREQ, MAX_FREQ))
+
+ if(!silent) 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")]"
+
+ if(!silent) 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)
+
+ if(!silent) 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]).")
+
//Link a new mobs mind to the creator of said mob. They will join any team they are currently on, and will only switch teams when their creator does.
@@ -1157,27 +1211,29 @@
else if (href_list["traitor"])
switch(href_list["traitor"])
if("clear")
+ to_chat(current, "You have been brainwashed!")
remove_traitor()
- to_chat(current, "You have been brainwashed! You are no longer a traitor!")
message_admins("[key_name_admin(usr)] has de-traitor'ed [current].")
log_admin("[key_name(usr)] has de-traitor'ed [current].")
SSticker.mode.update_traitor_icons_removed(src)
if("traitor")
if(!(src in SSticker.mode.traitors))
- SSticker.mode.traitors += src
- special_role = "traitor"
- to_chat(current, "You are a traitor!")
message_admins("[key_name_admin(usr)] has traitor'ed [current].")
log_admin("[key_name(usr)] has traitor'ed [current].")
- if(isAI(current))
- var/mob/living/silicon/ai/A = current
- SSticker.mode.add_law_zero(A)
- SSticker.mode.update_traitor_icons_added(src)
+ make_Traitor()
if("autoobjectives")
- SSticker.mode.forge_traitor_objectives(src)
- to_chat(usr, "The objectives for traitor [key] have been generated. You can edit them and anounce manually.")
+ var/datum/antagonist/traitor/traitordatum = has_antag_datum(ANTAG_DATUM_TRAITOR)
+ if(!traitordatum)
+ message_admins("[key_name_admin(usr)] has traitor'ed [current] as part of autoobjectives.")
+ log_admin("[key_name(usr)] has traitor'ed [current] as part of autoobjectives.")
+ make_Traitor()
+ else
+ log_admin("[key_name(usr)] has forged objectives for [current] as part of autoobjectives.")
+ traitordatum.forge_traitor_objectives()
+ to_chat(usr, "The objectives for traitor [key] have been generated. You can edit them and anounce manually.")
+
else if(href_list["devil"])
var/datum/antagonist/devil/devilinfo = has_antag_datum(ANTAG_DATUM_DEVIL)
@@ -1353,9 +1409,11 @@
message_admins("[key_name_admin(usr)] changed [current]'s telecrystal count to [crystals].")
log_admin("[key_name(usr)] changed [current]'s telecrystal count to [crystals].")
if("uplink")
- if(!SSticker.mode.equip_traitor(current, !(src in SSticker.mode.traitors)))
+ if(!equip_traitor())
to_chat(usr, "Equipping a syndicate failed!")
- log_admin("[key_name(usr)] attempted to give [current] an uplink.")
+ log_admin("[key_name(usr)] tried and failed to give [current] an uplink.")
+ else
+ log_admin("[key_name(usr)] gave [current] an uplink.")
else if (href_list["obj_announce"])
announce_objectives()
@@ -1383,12 +1441,10 @@
qdel(H)
/datum/mind/proc/make_Traitor()
- if(!(src in SSticker.mode.traitors))
- SSticker.mode.traitors += src
- special_role = "traitor"
- SSticker.mode.forge_traitor_objectives(src)
- SSticker.mode.finalize_traitor(src)
- SSticker.mode.greet_traitor(src)
+ if(!(has_antag_datum(ANTAG_DATUM_TRAITOR)))
+ var/datum/antagonist/traitor/traitordatum = add_antag_datum(ANTAG_DATUM_TRAITOR)
+ return traitordatum
+
/datum/mind/proc/make_Nuke(turf/spawnloc, nuke_code, leader=0, telecrystals = TRUE)
if(!(src in SSticker.mode.syndicates))
@@ -1494,7 +1550,6 @@
qdel(flash)
take_uplink()
var/fail = 0
-// fail |= !SSticker.mode.equip_traitor(current, 1)
fail |= !SSticker.mode.equip_revolutionary(current)
diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm
index 1f9b25361c..7a249c9076 100644
--- a/code/game/gamemodes/changeling/changeling.dm
+++ b/code/game/gamemodes/changeling/changeling.dm
@@ -200,7 +200,7 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w
to_chat(changeling.current, "You are [changeling.changeling.changelingID], a changeling! You have absorbed and taken the form of a human.")
to_chat(changeling.current, "Use say \":g message\" to communicate with your fellow changelings.")
to_chat(changeling.current, "You must complete the following tasks:")
- changeling.current.playsound_local('sound/ambience/antag/ling_aler.ogg',100,0)
+ changeling.current.playsound_local(get_turf(changeling.current), 'sound/ambience/antag/ling_aler.ogg', 100, FALSE, pressure_affected = FALSE)
if (changeling.current.mind)
var/mob/living/carbon/human/H = changeling.current
@@ -255,19 +255,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!"
- SSblackbox.add_details("changeling_objective","[objective.type]|SUCCESS")
+ SSblackbox.add_details("changeling_objective","[objective.type]|SUCCESS")
else
text += "
Objective #[count]: [objective.explanation_text] Fail."
- SSblackbox.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!"
- SSblackbox.add_details("changeling_success","SUCCESS")
+ SSblackbox.add_details("changeling_success","SUCCESS")
else
text += "
The changeling has failed."
- SSblackbox.add_details("changeling_success","FAIL")
+ SSblackbox.add_details("changeling_success","FAIL")
text += "
"
to_chat(world, text)
diff --git a/code/game/gamemodes/clock_cult/clock_cult.dm b/code/game/gamemodes/clock_cult/clock_cult.dm
index 9bb0d89e5c..ec387a71e5 100644
--- a/code/game/gamemodes/clock_cult/clock_cult.dm
+++ b/code/game/gamemodes/clock_cult/clock_cult.dm
@@ -146,7 +146,7 @@ Credit where due:
Rusting eternally in the Celestial Derelict, Ratvar has formed a covenant of mortals, with you as one of its members. As one of the Justiciar's servants, you are to work to the best of your \
ability to assist in completion of His agenda. You may not know the specifics of how to do so, but luckily you have a vessel to help you learn."
to_chat(M, greeting_text)
- M.playsound_local('sound/ambience/antag/ClockCultAlr.ogg',100,0)
+ M.playsound_local(get_turf(M), 'sound/ambience/antag/clockcultalr.ogg', 100, FALSE, pressure_affected = FALSE)
return 1
/datum/game_mode/proc/equip_servant(mob/living/L) //Grants a clockwork slab to the mob, with one of each component
diff --git a/code/game/gamemodes/clock_cult/clock_helpers/slab_abilities.dm b/code/game/gamemodes/clock_cult/clock_helpers/slab_abilities.dm
index ccf7aead44..f909215711 100644
--- a/code/game/gamemodes/clock_cult/clock_helpers/slab_abilities.dm
+++ b/code/game/gamemodes/clock_cult/clock_helpers/slab_abilities.dm
@@ -116,26 +116,29 @@
successful = TRUE
+ to_chat(ranged_ability_user, "You bathe [L == ranged_ability_user ? "yourself":"[L]"] in Inath-neq's power!")
var/targetturf = get_turf(L)
+ var/has_holy_water = (L.reagents && L.reagents.has_reagent("holywater"))
+ var/healseverity = max(round(totaldamage*0.05, 1), 1) //shows the general severity of the damage you just healed, 1 glow per 20
+ for(var/i in 1 to healseverity)
+ new /obj/effect/temp_visual/heal(targetturf, "#1E8CE1")
if(totaldamage)
L.adjustBruteLoss(-brutedamage)
L.adjustFireLoss(-burndamage)
L.adjustOxyLoss(-oxydamage)
L.adjustToxLoss(totaldamage * 0.5, TRUE, TRUE)
- var/healseverity = max(round(totaldamage*0.05, 1), 1) //shows the general severity of the damage you just healed, 1 glow per 20
- for(var/i in 1 to healseverity)
- new /obj/effect/temp_visual/heal(targetturf, "#1E8CE1")
- clockwork_say(ranged_ability_user, text2ratvar("Mend wounded flesh!"))
+ clockwork_say(ranged_ability_user, text2ratvar("[has_holy_water ? "Heal tainted" : "Mend wounded"] flesh!"))
add_logs(ranged_ability_user, L, "healed with Sentinel's Compromise")
+ L.visible_message("A blue light washes over [L], [has_holy_water ? "causing [L.p_them()] to briefly glow as it mends" : " mending"] [L.p_their()] bruises and burns!", \
+ "You feel Inath-neq's power healing your wounds[has_holy_water ? " and purging the darkness within you" : ""], but a deep nausea overcomes you!")
else
clockwork_say(ranged_ability_user, text2ratvar("Purge foul darkness!"))
add_logs(ranged_ability_user, L, "purged of holy water with Sentinel's Compromise")
- to_chat(ranged_ability_user, "You bathe [L == ranged_ability_user ? "yourself":"[L]"] in Inath-neq's power!")
- L.visible_message("A blue light washes over [L], mending [L.p_their()] bruises and burns!", \
- "You feel Inath-neq's power healing your wounds, but a deep nausea overcomes you!")
+ L.visible_message("A blue light washes over [L], causing [L.p_them()] to briefly glow!", \
+ "You feel Inath-neq's power purging the darkness within you!")
playsound(targetturf, 'sound/magic/Staff_Healing.ogg', 50, 1)
- if(L.reagents && L.reagents.has_reagent("holywater"))
+ if(has_holy_water)
L.reagents.remove_reagent("holywater", 1000)
to_chat(L, "Ratvar's light flares, banishing the darkness. Your devotion remains intact!")
diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm
index 76ad53e51c..730c7a5d82 100644
--- a/code/game/gamemodes/cult/cult.dm
+++ b/code/game/gamemodes/cult/cult.dm
@@ -112,7 +112,7 @@
equip_cultist(cult_mind.current)
update_cult_icons_added(cult_mind)
to_chat(cult_mind.current, "You are a member of the cult!")
- cult_mind.current.playsound_local('sound/ambience/antag/bloodcult.ogg',100,0)//subject to change
+ cult_mind.current.playsound_local(get_turf(cult_mind.current), 'sound/ambience/antag/bloodcult.ogg', 100, FALSE, pressure_affected = FALSE)//subject to change
add_cultist(cult_mind, 0)
..()
diff --git a/code/game/gamemodes/devil/devil agent/devil_agent.dm b/code/game/gamemodes/devil/devil agent/devil_agent.dm
index dfdb957153..5c48406f23 100644
--- a/code/game/gamemodes/devil/devil agent/devil_agent.dm
+++ b/code/game/gamemodes/devil/devil agent/devil_agent.dm
@@ -10,8 +10,8 @@
num_modifier = 4
objective_count = 2
- var/list/target_list = list()
- var/list/late_joining_list = list()
+ var/list/devil_target_list = list() //will update to be a child of internal affairs when bothered
+ var/list/devil_late_joining_list = list()
minimum_devils = 3
announce_text = "There are devil agents onboard the station, trying to outbid each other!\n\
@@ -24,7 +24,7 @@
i++
if(i + 1 > devils.len)
i = 0
- target_list[devil] = devils[i + 1]
+ devil_target_list[devil] = devils[i + 1]
..()
/datum/game_mode/devil/devil_agents/add_devil_objectives(datum/mind/devil_mind, quantity)
@@ -32,8 +32,8 @@
/datum/game_mode/devil/devil_agents/proc/give_outsell_objective(datum/mind/devil)
//If you override this method, have it return the number of objectives added.
- if(target_list.len && target_list[devil]) // Is a double agent
- var/datum/mind/target_mind = target_list[devil]
+ if(devil_target_list.len && devil_target_list[devil]) // Is a double agent
+ var/datum/mind/target_mind = devil_target_list[devil]
var/datum/objective/devil/outsell/outsellobjective = new
outsellobjective.owner = devil
outsellobjective.target = target_mind
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 85639f2180..193c7a14b4 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -162,6 +162,10 @@
. = 1
sleep(rand(600,1800))
+ if(!SSticker.IsRoundInProgress())
+ message_admins("Roundtype conversion cancelled, the game appears to have finished!")
+ round_converted = 0
+ return
//somewhere between 1 and 3 minutes from now
if(!config.midround_antag[SSticker.mode.config_tag])
round_converted = 0
@@ -177,7 +181,7 @@
return 0
-/datum/game_mode/proc/check_finished() //to be called by SSticker
+/datum/game_mode/proc/check_finished(force_ending) //to be called by SSticker
if(replacementmode && round_converted == 2)
return replacementmode.check_finished()
if(SSshuttle.emergency && (SSshuttle.emergency.mode == SHUTTLE_ENDGAME))
@@ -208,7 +212,7 @@
living_antag_player = Player
return 0
- if(!config.continuous[config_tag])
+ if(!config.continuous[config_tag] || force_ending)
return 1
else
@@ -345,7 +349,7 @@
// Ultimate randomizing code right here
for(var/mob/dead/new_player/player in GLOB.player_list)
- if(player.client && player.ready)
+ if(player.client && player.ready == PLAYER_READY_TO_PLAY)
players += player
// Shuffling, the players list is now ping-independent!!!
@@ -353,7 +357,7 @@
players = shuffle(players)
for(var/mob/dead/new_player/player in players)
- if(player.client && player.ready)
+ if(player.client && player.ready == PLAYER_READY_TO_PLAY)
if(role in player.client.prefs.be_special)
if(!jobban_isbanned(player, "Syndicate") && !jobban_isbanned(player, role)) //Nodrak/Carn: Antag Job-bans
if(age_check(player.client)) //Must be older than the minimum age
@@ -367,7 +371,7 @@
if(candidates.len < recommended_enemies)
for(var/mob/dead/new_player/player in players)
- if(player.client && player.ready)
+ if(player.client && player.ready == PLAYER_READY_TO_PLAY)
if(!(role in player.client.prefs.be_special)) // We don't have enough people who want to be antagonist, make a seperate list of people who don't want to be one
if(!jobban_isbanned(player, "Syndicate") && !jobban_isbanned(player, role)) //Nodrak/Carn: Antag Job-bans
drafted += player.mind
@@ -389,13 +393,7 @@
else // Not enough scrubs, ABORT ABORT ABORT
break
-/*
- if(candidates.len < recommended_enemies && override_jobbans) //If we still don't have enough people, we're going to start drafting banned people.
- for(var/mob/dead/new_player/player in players)
- if (player.client && player.ready)
- if(jobban_isbanned(player, "Syndicate") || jobban_isbanned(player, roletext)) //Nodrak/Carn: Antag Job-bans
- drafted += player.mind
-*/
+
if(restricted_jobs)
for(var/datum/mind/player in drafted) // Remove people who can't be an antagonist
for(var/job in restricted_jobs)
@@ -418,17 +416,11 @@
// recommended_enemies if the number of people with that role set to yes is less than recomended_enemies,
// Less if there are not enough valid players in the game entirely to make recommended_enemies.
-/*
-/datum/game_mode/proc/check_player_role_pref(var/role, var/mob/dead/new_player/player)
- if(player.preferences.be_special & role)
- return 1
- return 0
-*/
/datum/game_mode/proc/num_players()
. = 0
for(var/mob/dead/new_player/P in GLOB.player_list)
- if(P.client && P.ready)
+ if(P.client && P.ready == PLAYER_READY_TO_PLAY)
. ++
///////////////////////////////////
diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm
index 14a5ffcd5e..e4e032ee01 100644
--- a/code/game/gamemodes/nuclear/nuclear.dm
+++ b/code/game/gamemodes/nuclear/nuclear.dm
@@ -93,7 +93,7 @@
agent_number++
spawnpos++
update_synd_icons_added(synd_mind)
- synd_mind.current.playsound_local('sound/ambience/antag/ops.ogg',100,0)
+ synd_mind.current.playsound_local(get_turf(synd_mind.current), 'sound/ambience/antag/ops.ogg',100,0)
var/obj/machinery/nuclearbomb/nuke = locate("syndienuke") in GLOB.nuke_list
if(nuke)
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index 039b4cc3a4..f2e3480504 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -658,7 +658,7 @@ GLOBAL_LIST_EMPTY(possible_items_special)
var/n_p = 1 //autowin
if (SSticker.current_state == GAME_STATE_SETTING_UP)
for(var/mob/dead/new_player/P in GLOB.player_list)
- if(P.client && P.ready && P.mind!=owner)
+ if(P.client && P.ready == PLAYER_READY_TO_PLAY && P.mind!=owner)
n_p ++
else if (SSticker.IsRoundInProgress())
for(var/mob/living/carbon/human/P in GLOB.player_list)
@@ -701,7 +701,7 @@ 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?
diff --git a/code/game/gamemodes/traitor/double_agents.dm b/code/game/gamemodes/traitor/double_agents.dm
index dfe198eeb9..e83354dcf3 100644
--- a/code/game/gamemodes/traitor/double_agents.dm
+++ b/code/game/gamemodes/traitor/double_agents.dm
@@ -1,327 +1,74 @@
-#define PINPOINTER_MINIMUM_RANGE 15
-#define PINPOINTER_EXTRA_RANDOM_RANGE 10
-#define PINPOINTER_PING_TIME 40
-#define PROB_ACTUAL_TRAITOR 20
-#define TRAITOR_AGENT_ROLE "Syndicate External Affairs Agent"
+/datum/game_mode
+ var/list/target_list = list()
+ var/list/late_joining_list = list()
/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
+ 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
-
+
+ traitors_possible = 10 //hard limit on traitors if scaling is turned off
+ num_modifier = 4 // Four additional traitors
+ antag_datum = ANTAG_DATUM_IAA
+
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/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]
- ..()
-
+ var/i = 0
+ for(var/datum/mind/traitor in pre_traitors)
+ i++
+ if(i + 1 > pre_traitors.len)
+ i = 0
+ target_list[traitor] = pre_traitors[i+1]
+ ..()
-/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
- if(owner.special_role == TRAITOR_AGENT_ROLE)
- to_chat(owner.current," All the loyalist agents are dead, and no more is required of you. Die a glorious death, agent. ")
- else
- 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)
- if(owner.special_role == TRAITOR_AGENT_ROLE)
- fail_msg += " You no longer have permission to die. "
- else
- 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/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/internal/kill_objective = new
- kill_objective.owner = traitor
- kill_objective.target = target_mind
- kill_objective.update_explanation_text()
- traitor.objectives += kill_objective
-
- // Escape
- if(issilicon(traitor.current))
- var/datum/objective/survive/survive_objective = new
- survive_objective.owner = traitor
- traitor.objectives += survive_objective
- else
- 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)
- //Optional traitor objective
- if(prob(PROB_ACTUAL_TRAITOR))
- traitor.special_role = TRAITOR_AGENT_ROLE
- forge_single_objective(traitor)
-
- else
- ..() // Give them standard objectives.
- return
-
/datum/game_mode/traitor/internal_affairs/add_latejoin_traitor(datum/mind/character)
-
- check_potential_agents()
-
- // As soon as we get 3 or 4 extra latejoin traitors, make them traitors and kill each other.
- if(late_joining_list.len >= rand(3, 4))
- // True randomness
- shuffle_inplace(late_joining_list)
- // Reset the target_list, it'll be used again in force_traitor_objectives
- target_list = list()
-
- // Basically setting the target_list for who is killing who
- var/i = 0
- for(var/datum/mind/traitor in late_joining_list)
- i++
- if(i + 1 > late_joining_list.len)
- i = 0
- target_list[traitor] = late_joining_list[i + 1]
- traitor.special_role = traitor_name
-
- // Now, give them their targets
- for(var/datum/mind/traitor in target_list)
- ..(traitor)
-
- late_joining_list = list()
- else
- late_joining_list += character
- return
-
-/datum/game_mode/traitor/internal_affairs/proc/check_potential_agents()
-
- for(var/M in late_joining_list)
- if(istype(M, /datum/mind))
- var/datum/mind/agent_mind = M
- if(ishuman(agent_mind.current))
- var/mob/living/carbon/human/H = agent_mind.current
- if(H.stat != DEAD)
- if(H.client)
- continue // It all checks out.
-
- // If any check fails, remove them from our list
- late_joining_list -= M
+ check_potential_agents()
-/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")
- if(traitor.special_role == TRAITOR_AGENT_ROLE)
- to_chat(traitor.current, "You are the [TRAITOR_AGENT_ROLE].")
- to_chat(traitor.current, "Your target has been framed for [crime], and you have been tasked with eliminating them to prevent them defending themselves in court.")
- to_chat(traitor.current, "Any damage you cause will be a further embarrassment to Nanotrasen, so you have no limits on collateral damage.")
- to_chat(traitor.current, " You have been provided with a standard uplink to accomplish your task. ")
- 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.")
+ // As soon as we get 3 or 4 extra latejoin traitors, make them traitors and kill each other.
+ if(late_joining_list.len >= rand(3, 4))
+ // True randomness
+ shuffle_inplace(late_joining_list)
+ // Reset the target_list, it'll be used again in force_traitor_objectives
+ target_list = list()
+
+ // Basically setting the target_list for who is killing who
+ var/i = 0
+ for(var/datum/mind/traitor in late_joining_list)
+ i++
+ if(i + 1 > late_joining_list.len)
+ i = 0
+ target_list[traitor] = late_joining_list[i + 1]
+ traitor.special_role = traitor_name
+
+ // Now, give them their targets
+ for(var/datum/mind/traitor in target_list)
+ ..(traitor)
+
+ late_joining_list = list()
else
- 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)
+ late_joining_list += character
return
-#undef PROB_ACTUAL_TRAITOR
-#undef PINPOINTER_EXTRA_RANDOM_RANGE
-#undef PINPOINTER_MINIMUM_RANGE
-#undef PINPOINTER_PING_TIME
+/datum/game_mode/traitor/internal_affairs/proc/check_potential_agents()
+ for(var/M in late_joining_list)
+ if(istype(M, /datum/mind))
+ var/datum/mind/agent_mind = M
+ if(ishuman(agent_mind.current))
+ var/mob/living/carbon/human/H = agent_mind.current
+ if(H.stat != DEAD)
+ if(H.client)
+ continue // It all checks out.
+
+ // If any check fails, remove them from our list
+ late_joining_list -= M
\ No newline at end of file
diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm
index 7ff5c03739..2e19ca4fc0 100644
--- a/code/game/gamemodes/traitor/traitor.dm
+++ b/code/game/gamemodes/traitor/traitor.dm
@@ -1,6 +1,5 @@
/datum/game_mode
var/traitor_name = "traitor"
- var/employer = "The Syndicate"
var/list/datum/mind/traitors = list()
var/datum/mind/exchange_red
@@ -23,8 +22,10 @@
Traitors: Accomplish your objectives.\n\
Crew: Do not let the traitors succeed!"
+ var/list/datum/mind/pre_traitors = list()
var/traitors_possible = 4 //hard limit on traitors if scaling is turned off
var/num_modifier = 0 // Used for gamemodes, that are a child of traitor, that need more than the usual.
+ var/antag_datum = ANTAG_DATUM_TRAITOR //what type of antag to create
/datum/game_mode/traitor/pre_setup()
@@ -46,24 +47,22 @@
if (!antag_candidates.len)
break
var/datum/mind/traitor = pick(antag_candidates)
- traitors += traitor
+ pre_traitors += traitor
traitor.special_role = traitor_name
traitor.restricted_roles = restricted_jobs
log_game("[traitor.key] (ckey) has been selected as a [traitor_name]")
antag_candidates.Remove(traitor)
- if(traitors.len < required_enemies)
+ if(pre_traitors.len < required_enemies)
return 0
return 1
/datum/game_mode/traitor/post_setup()
- for(var/datum/mind/traitor in traitors)
- forge_traitor_objectives(traitor)
+ for(var/datum/mind/traitor in pre_traitors)
spawn(rand(10,100))
- finalize_traitor(traitor)
- greet_traitor(traitor)
+ traitor.add_antag_datum(antag_datum)
if(!exchange_blue)
exchange_blue = -1 //Block latejoiners from getting exchange objectives
modePlayer += traitors
@@ -82,164 +81,14 @@
add_latejoin_traitor(character.mind)
/datum/game_mode/traitor/proc/add_latejoin_traitor(datum/mind/character)
- character.make_Traitor()
+ character.add_antag_datum(antag_datum)
-/datum/game_mode/proc/forge_single_objective(datum/mind/traitor) //Returns how many objectives are added
- .=1
- if(issilicon(traitor.current))
- var/special_pick = rand(1,4)
- switch(special_pick)
- if(1)
- var/datum/objective/block/block_objective = new
- block_objective.owner = traitor
- traitor.objectives += block_objective
- if(2)
- var/datum/objective/purge/purge_objective = new
- purge_objective.owner = traitor
- traitor.objectives += purge_objective
- if(3)
- var/datum/objective/robot_army/robot_objective = new
- robot_objective.owner = traitor
- traitor.objectives += robot_objective
- if(4) //Protect and strand a target
- var/datum/objective/protect/yandere_one = new
- yandere_one.owner = traitor
- traitor.objectives += yandere_one
- yandere_one.find_target()
- var/datum/objective/maroon/yandere_two = new
- yandere_two.owner = traitor
- yandere_two.target = yandere_one.target
- yandere_two.update_explanation_text() // normally called in find_target()
- traitor.objectives += yandere_two
- .=2
- else
- if(prob(50))
- var/list/active_ais = active_ais()
- if(active_ais.len && prob(100/GLOB.joined_player_list.len))
- var/datum/objective/destroy/destroy_objective = new
- destroy_objective.owner = traitor
- destroy_objective.find_target()
- traitor.objectives += destroy_objective
- else if(prob(30))
- var/datum/objective/maroon/maroon_objective = new
- maroon_objective.owner = traitor
- maroon_objective.find_target()
- traitor.objectives += maroon_objective
- else
- var/datum/objective/assassinate/kill_objective = new
- kill_objective.owner = traitor
- kill_objective.find_target()
- traitor.objectives += kill_objective
- else
- var/datum/objective/steal/steal_objective = new
- steal_objective.owner = traitor
- steal_objective.find_target()
- traitor.objectives += steal_objective
-
-
-/datum/game_mode/proc/forge_traitor_objectives(datum/mind/traitor)
- if(issilicon(traitor.current))
- var/objective_count = 0
-
- if(prob(30))
- objective_count+=forge_single_objective(traitor)
-
- for(var/i = objective_count, i < config.traitor_objectives_amount, i++)
- var/datum/objective/assassinate/kill_objective = new
- kill_objective.owner = traitor
- kill_objective.find_target()
- traitor.objectives += kill_objective
-
- var/datum/objective/survive/survive_objective = new
- survive_objective.owner = traitor
- traitor.objectives += survive_objective
-
- else
- var/is_hijacker = prob(10)
- var/martyr_chance = prob(20)
- var/objective_count = is_hijacker //Hijacking counts towards number of objectives
- if(!exchange_blue && traitors.len >= 8) //Set up an exchange if there are enough traitors
- if(!exchange_red)
- exchange_red = traitor
- else
- exchange_blue = traitor
- assign_exchange_role(exchange_red)
- assign_exchange_role(exchange_blue)
- objective_count += 1 //Exchange counts towards number of objectives
- for(var/i = objective_count, i < config.traitor_objectives_amount, i++)
- forge_single_objective(traitor)
-
- if(is_hijacker && objective_count <= config.traitor_objectives_amount) //Don't assign hijack if it would exceed the number of objectives set in config.traitor_objectives_amount
- if (!(locate(/datum/objective/hijack) in traitor.objectives))
- var/datum/objective/hijack/hijack_objective = new
- hijack_objective.owner = traitor
- traitor.objectives += hijack_objective
- return
-
-
- var/martyr_compatibility = 1 //You can't succeed in stealing if you're dead.
- for(var/datum/objective/O in traitor.objectives)
- if(!O.martyr_compatible)
- martyr_compatibility = 0
- break
-
- if(martyr_compatibility && martyr_chance)
- var/datum/objective/martyr/martyr_objective = new
- martyr_objective.owner = traitor
- traitor.objectives += martyr_objective
- return
-
- else
- if(!(locate(/datum/objective/escape) in traitor.objectives))
- var/datum/objective/escape/escape_objective = new
- escape_objective.owner = traitor
- traitor.objectives += escape_objective
- return
-
-
-
-/datum/game_mode/proc/greet_traitor(datum/mind/traitor)
- to_chat(traitor.current, "You are the [traitor_name].")
- traitor.announce_objectives()
- return
-
-
-
-/datum/game_mode/proc/finalize_traitor(var/datum/mind/traitor)
- if(issilicon(traitor.current))
- add_law_zero(traitor.current)
- traitor.current.playsound_local('sound/ambience/antag/Malf.ogg',100,0)
- else
- equip_traitor(traitor.current)
- traitor.current.playsound_local('sound/ambience/antag/TatorAlert.ogg',100,0)
- SSticker.mode.update_traitor_icons_added(traitor)
- return
-
/datum/game_mode/traitor/declare_completion()
..()
return//Traitors will be checked as part of check_extra_completion. Leaving this here as a reminder.
-/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]")
-
- traitor_mob.mind.store_memory("Code Phrase: [GLOB.syndicate_code_phrase]")
- traitor_mob.mind.store_memory("Code Response: [GLOB.syndicate_code_response]")
-
- to_chat(traitor_mob, "Use the code words in the order provided, during regular conversation, to identify other agents. Proceed with caution, however, as everyone is a potential foe.")
-
-
-/datum/game_mode/proc/add_law_zero(mob/living/silicon/ai/killer)
- var/law = "Accomplish your objectives at all costs."
- var/law_borg = "Accomplish your AI's objectives at all costs."
- killer.set_zeroth_law(law, law_borg)
- give_codewords(killer)
- killer.set_syndie_radio()
- to_chat(killer, "Your radio has been upgraded! Use :t to speak on an encrypted channel with Syndicate Agents!")
- killer.add_malf_picker()
/datum/game_mode/proc/auto_declare_completion_traitor()
if(traitors.len)
@@ -264,18 +113,18 @@
for(var/datum/objective/objective in traitor.objectives)
if(objective.check_completion())
objectives += "
Objective #[count]: [objective.explanation_text] Success!"
- SSblackbox.add_details("traitor_objective","[objective.type]|SUCCESS")
+ SSblackbox.add_details("traitor_objective","[objective.type]|SUCCESS")
else
objectives += "
Objective #[count]: [objective.explanation_text] Fail."
- SSblackbox.add_details("traitor_objective","[objective.type]|FAIL")
+ SSblackbox.add_details("traitor_objective","[objective.type]|FAIL")
traitorwin = 0
count++
if(uplink_true)
text += " (used [TC_uses] TC) [purchases]"
if(TC_uses==0 && traitorwin)
- var/static/icon/badass = icon('icons/BadAss.dmi', "badass")
- text += "[bicon(badass)]"
+ var/static/icon/badass = icon('icons/BadAss.dmi', "badass")
+ text += "[bicon(badass)]"
text += objectives
@@ -288,10 +137,10 @@
if(traitorwin)
text += "
The [special_role_text] was successful!"
- SSblackbox.add_details("traitor_success","SUCCESS")
+ SSblackbox.add_details("traitor_success","SUCCESS")
else
text += "
The [special_role_text] has failed!"
- SSblackbox.add_details("traitor_success","FAIL")
+ SSblackbox.add_details("traitor_success","FAIL")
text += "
"
@@ -302,110 +151,7 @@
return 1
-/datum/game_mode/proc/equip_traitor(mob/living/carbon/human/traitor_mob, safety = 0)
- if (!istype(traitor_mob))
- return
- . = 1
- if (traitor_mob.mind)
- if (traitor_mob.mind.assigned_role == "Clown")
- to_chat(traitor_mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
- traitor_mob.dna.remove_mutation(CLOWNMUT)
- var/list/all_contents = traitor_mob.GetAllContents()
- var/obj/item/device/pda/PDA = locate() in all_contents
- var/obj/item/device/radio/R = locate() in all_contents
- var/obj/item/weapon/pen/P = locate() in all_contents //including your PDA-pen!
-
- var/obj/item/uplink_loc
-
- if(traitor_mob.client && traitor_mob.client.prefs)
- switch(traitor_mob.client.prefs.uplink_spawn_loc)
- if(UPLINK_PDA)
- uplink_loc = PDA
- if(!uplink_loc)
- uplink_loc = R
- if(!uplink_loc)
- uplink_loc = P
- if(UPLINK_RADIO)
- uplink_loc = R
- if(!uplink_loc)
- uplink_loc = PDA
- if(!uplink_loc)
- uplink_loc = P
- if(UPLINK_PEN)
- uplink_loc = P
- if(!uplink_loc)
- uplink_loc = PDA
- if(!uplink_loc)
- uplink_loc = R
-
- if (!uplink_loc)
- 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)
- U.owner = "[traitor_mob.key]"
- uplink_loc.hidden_uplink = U
-
- if(uplink_loc == R)
- R.traitor_frequency = sanitize_frequency(rand(MIN_FREQ, MAX_FREQ))
-
- 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, "[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, "[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.
- give_codewords(traitor_mob)
-
-/datum/game_mode/proc/assign_exchange_role(datum/mind/owner)
- //set faction
- var/faction = "red"
- if(owner == exchange_blue)
- faction = "blue"
-
- //Assign objectives
- var/datum/objective/steal/exchange/exchange_objective = new
- exchange_objective.set_faction(faction,((faction == "red") ? exchange_blue : exchange_red))
- exchange_objective.owner = owner
- owner.objectives += exchange_objective
-
- if(prob(20))
- var/datum/objective/steal/exchange/backstab/backstab_objective = new
- backstab_objective.set_faction(faction)
- backstab_objective.owner = owner
- owner.objectives += backstab_objective
-
- //Spawn and equip documents
- var/mob/living/carbon/human/mob = owner.current
-
- var/obj/item/weapon/folder/syndicate/folder
- if(owner == exchange_red)
- folder = new/obj/item/weapon/folder/syndicate/red(mob.loc)
- else
- folder = new/obj/item/weapon/folder/syndicate/blue(mob.loc)
-
- var/list/slots = list (
- "backpack" = slot_in_backpack,
- "left pocket" = slot_l_store,
- "right pocket" = slot_r_store
- )
-
- var/where = "At your feet"
- var/equipped_slot = mob.equip_in_one_of_slots(folder, slots)
- if (equipped_slot)
- where = "In your [equipped_slot]"
- to_chat(mob, "
[where] is a folder containing secret documents that another Syndicate group wants. We have set up a meeting with one of their agents on station to make an exchange. Exercise extreme caution as they cannot be trusted and may be hostile.
")
/datum/game_mode/proc/update_traitor_icons_added(datum/mind/traitor_mind)
var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR]
diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm
index a39c4fbe93..d7d510974c 100644
--- a/code/game/objects/effects/decals/cleanable/humans.dm
+++ b/code/game/objects/effects/decals/cleanable/humans.dm
@@ -180,7 +180,7 @@
. += "You recognise the footprints as belonging to:\n"
for(var/shoe in shoe_types)
var/obj/item/clothing/shoes/S = shoe
- . += "some [initial(S.name)] \icon[S]\n"
+ . += "some [initial(S.name)] [bicon(initial(S.icon))]\n"
to_chat(user, .)
diff --git a/code/game/objects/effects/decals/decal.dm b/code/game/objects/effects/decals/decal.dm
index 79606ae09c..3ce79ca6f9 100644
--- a/code/game/objects/effects/decals/decal.dm
+++ b/code/game/objects/effects/decals/decal.dm
@@ -25,13 +25,12 @@
/obj/effect/turf_decal/proc/get_decal()
return image(icon='icons/turf/decals.dmi',icon_state=icon_state,dir=dir,layer=TURF_LAYER)
-/obj/effect/turf_decal/Initialize(mapload)
+/obj/effect/turf_decal/Initialize()
+ ..()
var/turf/T = loc
if(!istype(T)) //you know this will happen somehow
CRASH("Turf decal initialized in an object/nullspace")
T.add_decal(get_decal(),group)
- qdel(src)
-
/obj/effect/turf_decal/stripes/line
icon_state = "warningline"
diff --git a/code/game/objects/items/weapons/pneumaticCannon.dm b/code/game/objects/items/weapons/pneumaticCannon.dm
index 3de096680b..aca83c1b9e 100644
--- a/code/game/objects/items/weapons/pneumaticCannon.dm
+++ b/code/game/objects/items/weapons/pneumaticCannon.dm
@@ -32,13 +32,16 @@
/obj/item/weapon/pneumatic_cannon/examine(mob/user)
..()
+ var/list/out = list()
if(!in_range(user, src))
- to_chat(user, "You'll need to get closer to see any more.")
+ out += "You'll need to get closer to see any more."
return
for(var/obj/item/I in loadedItems)
- to_chat(user, "[bicon(I)] It has \the [I] loaded.")
+ out += "[bicon(I)] It has \the [I] loaded."
+ CHECK_TICK
if(tank)
- to_chat(user, "[bicon(tank)] It has \the [tank] mounted onto it.")
+ out += "[bicon(tank)] It has \the [tank] mounted onto it."
+ to_chat(user, out.Join("
"))
/obj/item/weapon/pneumatic_cannon/attackby(obj/item/weapon/W, mob/user, params)
diff --git a/code/game/objects/items/weapons/storage/lockbox.dm b/code/game/objects/items/weapons/storage/lockbox.dm
index 94f34e0e3b..423943fbbc 100644
--- a/code/game/objects/items/weapons/storage/lockbox.dm
+++ b/code/game/objects/items/weapons/storage/lockbox.dm
@@ -8,8 +8,9 @@
max_combined_w_class = 14 //The sum of the w_classes of all the items in this storage item.
storage_slots = 4
req_access = list(GLOB.access_armory)
- var/locked = 1
- var/broken = 0
+ var/locked = TRUE
+ var/broken = FALSE
+ var/open = FALSE
var/icon_locked = "lockbox+l"
var/icon_closed = "lockbox"
var/icon_broken = "lockbox+b"
@@ -48,8 +49,8 @@
/obj/item/weapon/storage/lockbox/emag_act(mob/user)
if(!broken)
- broken = 1
- locked = 0
+ broken = TRUE
+ locked = FALSE
desc += "It appears to be broken."
icon_state = src.icon_broken
if(user)
@@ -67,12 +68,22 @@
if(locked)
to_chat(user, "It's locked!")
return 0
+ open = TRUE
return ..()
/obj/item/weapon/storage/lockbox/can_be_inserted(obj/item/W, stop_messages = 0)
if(locked)
return 0
return ..()
+/obj/item/weapon/storage/lockbox/handle_item_insertion(obj/item/W, prevent_warning = 0, mob/user)
+ open = TRUE
+ update_icon()
+ return ..()
+/obj/item/weapon/storage/lockbox/remove_from_storage(obj/item/W, atom/new_location, burn = 0)
+ open = TRUE
+ update_icon()
+ return ..()
+
/obj/item/weapon/storage/lockbox/loyalty
name = "lockbox of mindshield implants"
@@ -107,51 +118,63 @@
icon_broken = "medalbox+b"
can_hold = list(/obj/item/clothing/accessory/medal)
+/obj/item/weapon/storage/lockbox/medal/AltClick()
+ if(!locked)
+ open = (open ? FALSE : TRUE)
+ update_icon()
+ ..()
+
+
/obj/item/weapon/storage/lockbox/medal/PopulateContents()
+ new /obj/item/clothing/accessory/medal/gold/captain(src)
new /obj/item/clothing/accessory/medal/silver/valor(src)
+ new /obj/item/clothing/accessory/medal/silver/valor(src)
+ new /obj/item/clothing/accessory/medal/silver/security(src)
new /obj/item/clothing/accessory/medal/bronze_heart(src)
+ new /obj/item/clothing/accessory/medal/plasma/nobel_science(src)
+ new /obj/item/clothing/accessory/medal/plasma/nobel_science(src)
for(var/i in 1 to 3)
new /obj/item/clothing/accessory/medal/conduct(src)
- new /obj/item/clothing/accessory/medal/gold/captain(src)
- new /obj/item/clothing/accessory/medal/silver/security(src)
- new /obj/item/clothing/accessory/medal/plasma(src)
- new /obj/item/clothing/accessory/medal/plasma/nobel_science(src)
- new /obj/item/clothing/accessory/medal/gold/heroism(src)
-/obj/item/weapon/storage/lockbox/secmedal
+/obj/item/weapon/storage/lockbox/medal/update_icon()
+ cut_overlays()
+ if(locked)
+ icon_state = "medalbox+l"
+ open = FALSE
+ else
+ icon_state = "medalbox"
+ if(open)
+ icon_state += "open"
+ if(broken)
+ icon_state += "+b"
+ if(contents && open)
+ for (var/i in 1 to contents.len)
+ var/obj/item/clothing/accessory/medal/M = contents[i]
+ var/mutable_appearance/medalicon = mutable_appearance(initial(icon), M.medaltype)
+ if(i > 1 && i <= 5)
+ medalicon.pixel_x += ((i-1)*3)
+ else if(i > 5)
+ medalicon.pixel_y -= 7
+ medalicon.pixel_x -= 2
+ medalicon.pixel_x += ((i-6)*3)
+ add_overlay(medalicon)
+
+/obj/item/weapon/storage/lockbox/medal/sec
name = "security medal box"
desc = "A locked box used to store medals to be given to members of the security department."
- icon_state = "medalbox+l"
- item_state = "syringe_kit"
- w_class = WEIGHT_CLASS_NORMAL
- max_w_class = WEIGHT_CLASS_SMALL
- storage_slots = 10
- max_combined_w_class = 20
req_access = list(GLOB.access_hos)
- icon_locked = "medalbox+l"
- icon_closed = "medalbox"
- icon_broken = "medalbox+b"
can_hold = list(/obj/item/clothing/accessory/medal)
-/obj/item/weapon/storage/lockbox/secmedal/PopulateContents()
+obj/item/weapon/storage/lockbox/medal/sec/PopulateContents()
for(var/i in 1 to 3)
new /obj/item/clothing/accessory/medal/silver/security(src)
-/obj/item/weapon/storage/lockbox/scimedal
+/obj/item/weapon/storage/lockbox/medal/sci
name = "science medal box"
desc = "A locked box used to store medals to be given to members of the science department."
- icon_state = "medalbox+l"
- item_state = "syringe_kit"
- w_class = WEIGHT_CLASS_NORMAL
- max_w_class = WEIGHT_CLASS_SMALL
- storage_slots = 10
- max_combined_w_class = 20
req_access = list(GLOB.access_rd)
- icon_locked = "medalbox+l"
- icon_closed = "medalbox"
- icon_broken = "medalbox+b"
can_hold = list(/obj/item/clothing/accessory/medal)
-/obj/item/weapon/storage/lockbox/scimedal/PopulateContents()
+/obj/item/weapon/storage/lockbox/medal/sci/PopulateContents()
for(var/i in 1 to 3)
new /obj/item/clothing/accessory/medal/plasma/nobel_science(src)
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm
index feee04d56e..1ac2ccd77b 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm
@@ -19,9 +19,9 @@
new /obj/item/weapon/tank/internals/air(src)
new /obj/item/clothing/mask/gas(src)
new /obj/item/device/megaphone/command(src)
+ new /obj/item/weapon/storage/lockbox/medal/sci(src)
new /obj/item/clothing/suit/armor/reactive/teleport(src)
new /obj/item/device/assembly/flash/handheld(src)
new /obj/item/device/laser_pointer(src)
new /obj/item/weapon/door_remote/research_director(src)
new /obj/item/weapon/storage/box/firingpins(src)
- new /obj/item/weapon/storage/lockbox/scimedal(src)
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 c763b4a5ad..ce31c76378 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
@@ -73,6 +73,8 @@
new /obj/item/clothing/head/HoS(src)
new /obj/item/clothing/glasses/hud/security/sunglasses/eyepatch(src)
new /obj/item/clothing/glasses/hud/security/sunglasses/gars/supergars(src)
+ new /obj/item/clothing/under/rank/head_of_security/grey(src)
+ new /obj/item/weapon/storage/lockbox/medal/sec(src)
new /obj/item/device/megaphone/sec(src)
new /obj/item/weapon/holosign_creator/security(src)
new /obj/item/weapon/storage/lockbox/loyalty(src)
@@ -83,9 +85,6 @@
new /obj/item/weapon/gun/energy/e_gun/hos(src)
new /obj/item/device/flashlight/seclite(src)
new /obj/item/weapon/pinpointer(src)
- new /obj/item/clothing/under/rank/head_of_security/grey(src)
- new /obj/item/clothing/under/rank/head_of_security/grey(src)
- new /obj/item/weapon/storage/lockbox/secmedal(src)
/obj/structure/closet/secure_closet/warden
name = "\proper warden's locker"
diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm
index e569eacd2b..428ae95984 100644
--- a/code/modules/admin/secrets.dm
+++ b/code/modules/admin/secrets.dm
@@ -334,31 +334,18 @@
if(!objective)
return
SSblackbox.add_details("admin_secrets_fun_used","Traitor All ([objective])")
- for(var/mob/living/carbon/human/H in GLOB.player_list)
- if(H.stat == 2 || !H.client || !H.mind) continue
+ for(var/mob/living/H in GLOB.player_list)
+ if(!(istype(H, /mob/living/carbon/human)||istype(H, /mob/living/silicon/))) continue
+ if(H.stat == 2 || !H.client || !H.mind || ispAI(H)) continue
if(is_special_character(H)) continue
- //traitorize(H, objective, 0)
- SSticker.mode.traitors += H.mind
- H.mind.special_role = "traitor"
+ H.mind.add_antag_datum(ANTAG_DATUM_TRAITOR_CUSTOM)
+ var/datum/antagonist/traitor/traitordatum = H.mind.has_antag_datum(ANTAG_DATUM_TRAITOR) //original datum self deletes
var/datum/objective/new_objective = new
new_objective.owner = H
new_objective.explanation_text = objective
- H.mind.objectives += new_objective
- SSticker.mode.greet_traitor(H.mind)
- //SSticker.mode.forge_traitor_objectives(H.mind)
- SSticker.mode.finalize_traitor(H.mind)
- for(var/mob/living/silicon/A in GLOB.player_list)
- if(A.stat == 2 || !A.client || !A.mind) continue
- if(ispAI(A)) continue
- else if(is_special_character(A)) continue
- SSticker.mode.traitors += A.mind
- A.mind.special_role = "traitor"
- var/datum/objective/new_objective = new
- new_objective.owner = A
- new_objective.explanation_text = objective
- A.mind.objectives += new_objective
- SSticker.mode.greet_traitor(A.mind)
- SSticker.mode.finalize_traitor(A.mind)
+ traitordatum.add_objective(new_objective)
+ traitordatum.equip(FALSE)
+ traitordatum.greet()
message_admins("[key_name_admin(usr)] used everyone is a traitor secret. Objective is [objective]")
log_admin("[key_name(usr)] used everyone is a traitor secret. Objective is [objective]")
diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm
index 87a9e5a040..2bf7842c1c 100644
--- a/code/modules/admin/verbs/one_click_antag.dm
+++ b/code/modules/admin/verbs/one_click_antag.dm
@@ -202,6 +202,7 @@
to_chat(H, "The world before you suddenly glows a brilliant yellow. You hear the whooshing steam and clanking cogs of a billion billion machines, and all at once \
you see the truth. Ratvar, the Clockwork Justiciar, lies derelict and forgotten in an unseen realm, and he has selected you as one of his harbringers. You are now a servant of \
Ratvar, and you will bring him back.")
+ H.playsound_local(get_turf(H), 'sound/ambience/antag/clockcultalr.ogg', 100, FALSE, pressure_affected = FALSE)
add_servant_of_ratvar(H, TRUE)
SSticker.mode.equip_servant(H)
candidates.Remove(H)
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index c31ec9b537..00796de907 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -112,7 +112,6 @@
message_admins(" LocalNarrate: [key_name_admin(usr)] at [get_area(A)][ADMIN_JMP(A)]: [msg]
")
SSblackbox.add_details("admin_verb","Local Narrate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
/client/proc/cmd_admin_godmode(mob/M in GLOB.mob_list)
set category = "Special Verbs"
set name = "Godmode"
@@ -227,7 +226,7 @@
return 0
var/alien_caste = input(usr, "Please choose which caste to spawn.","Pick a caste",null) as null|anything in list("Queen","Praetorian","Hunter","Sentinel","Drone","Larva")
- var/obj/effect/landmark/spawn_here = GLOB.xeno_spawn.len ? pick(GLOB.xeno_spawn) : null
+ var/obj/effect/landmark/spawn_here = GLOB.xeno_spawn.len ? pick(GLOB.xeno_spawn) : null
var/mob/living/carbon/alien/new_xeno
switch(alien_caste)
if("Queen")
@@ -244,8 +243,8 @@
new_xeno = new /mob/living/carbon/alien/larva(spawn_here)
else
return 0
- if(!spawn_here)
- SSjob.SendToLateJoin(new_xeno, FALSE)
+ if(!spawn_here)
+ SSjob.SendToLateJoin(new_xeno, FALSE)
new_xeno.ckey = ckey
var/msg = "[key_name_admin(usr)] has spawned [ckey] as a filthy xeno [alien_caste]."
@@ -302,9 +301,9 @@ Traitors and the like can also be revived with the previous role mostly intact.
else//If we don't know what special role they have, for whatever reason, or they're a larva.
create_xeno(G_found.ckey)
return
-
- if(!T)
- SSjob.SendToLateJoin(new_xeno, FALSE)
+
+ if(!T)
+ SSjob.SendToLateJoin(new_xeno, FALSE)
//Now to give them their mind back.
G_found.mind.transfer_to(new_xeno) //be careful when doing stuff like this! I've already checked the mind isn't in use
@@ -318,8 +317,8 @@ Traitors and the like can also be revived with the previous role mostly intact.
//check if they were a monkey
else if(findtext(G_found.real_name,"monkey"))
if(alert("This character appears to have been a monkey. Would you like to respawn them as such?",,"Yes","No")=="Yes")
- var/mob/living/carbon/monkey/new_monkey = new
- SSjob.SendToLateJoin(new_monkey)
+ var/mob/living/carbon/monkey/new_monkey = new
+ SSjob.SendToLateJoin(new_monkey)
G_found.mind.transfer_to(new_monkey) //be careful when doing stuff like this! I've already checked the mind isn't in use
new_monkey.key = G_found.key
to_chat(new_monkey, "You have been fully respawned. Enjoy the game.")
@@ -330,8 +329,8 @@ Traitors and the like can also be revived with the previous role mostly intact.
//Ok, it's not a xeno or a monkey. So, spawn a human.
- var/mob/living/carbon/human/new_character = new//The mob being spawned.
- SSjob.SendToLateJoin(new_character)
+ var/mob/living/carbon/human/new_character = new//The mob being spawned.
+ SSjob.SendToLateJoin(new_character)
var/datum/data/record/record_found //Referenced to later to either randomize or not randomize the character.
if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something
@@ -374,10 +373,13 @@ Traitors and the like can also be revived with the previous role mostly intact.
var/player_key = G_found.key
//Now for special roles and equipment.
+ var/datum/antagonist/traitor/traitordatum = new_character.mind.has_antag_datum(ANTAG_DATUM_TRAITOR)
+ if(traitordatum)
+ SSjob.EquipRank(new_character, new_character.mind.assigned_role, 1)
+ traitordatum.equip()
+
+
switch(new_character.mind.special_role)
- if("traitor")
- SSjob.EquipRank(new_character, new_character.mind.assigned_role, 1)
- SSticker.mode.equip_traitor(new_character)
if("Wizard")
new_character.loc = pick(GLOB.wizardstart)
//SSticker.mode.learn_basic_spells(new_character)
@@ -392,8 +394,8 @@ Traitors and the like can also be revived with the previous role mostly intact.
for(var/obj/effect/landmark/L in GLOB.landmarks_list)
if(L.name=="carpspawn")
ninja_spawn += L
- var/datum/antagonist/ninja/ninjadatum = new_character.mind.has_antag_datum(ANTAG_DATUM_NINJA)
- ninjadatum.equip_space_ninja()
+ var/datum/antagonist/ninja/ninjadatum = new_character.mind.has_antag_datum(ANTAG_DATUM_NINJA)
+ ninjadatum.equip_space_ninja()
if(ninja_spawn.len)
var/obj/effect/landmark/ninja_spawn_here = pick(ninja_spawn)
new_character.loc = ninja_spawn_here.loc
@@ -402,12 +404,8 @@ Traitors and the like can also be revived with the previous role mostly intact.
switch(new_character.mind.assigned_role)
if("Cyborg")//More rigging to make em' work and check if they're traitor.
new_character = new_character.Robotize()
- if(new_character.mind.special_role=="traitor")
- SSticker.mode.add_law_zero(new_character)
if("AI")
new_character = new_character.AIize()
- if(new_character.mind.special_role=="traitor")
- SSticker.mode.add_law_zero(new_character)
else
SSjob.EquipRank(new_character, new_character.mind.assigned_role, 1)//Or we simply equip them.
@@ -591,7 +589,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
empulse(O, heavy, light)
log_admin("[key_name(usr)] created an EM Pulse ([heavy],[light]) at ([O.x],[O.y],[O.z])")
- message_admins("[key_name_admin(usr)] created an EM Pulse ([heavy],[light]) at ([O.x],[O.y],[O.z])")
+ message_admins("[key_name_admin(usr)] created an EM Pulse ([heavy],[light]) at ([O.x],[O.y],[O.z])")
SSblackbox.add_details("admin_verb","EM Pulse") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
@@ -1199,17 +1197,17 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
message_admins(msg)
admin_ticket_log(target, msg)
log_admin("[key_name(usr)] punished [key_name(target)] with [punishment].")
-
-
-/client/proc/trigger_centcom_recall()
- if(!holder)
- return
- var/message = pick(GLOB.admiral_messages)
- message = input("Enter message from the on-call admiral to be put in the recall report.", "Admiral Message", message) as text|null
-
- if(!message)
- return
-
- message_admins("[key_name_admin(usr)] triggered a Centcom recall, with the admiral message of: [message]")
- log_game("[key_name(usr)] triggered a Centcom recall, with the message of: [message]")
- SSshuttle.centcom_recall(SSshuttle.emergency.timer, message)
\ No newline at end of file
+
+
+/client/proc/trigger_centcom_recall()
+ if(!holder)
+ return
+ var/message = pick(GLOB.admiral_messages)
+ message = input("Enter message from the on-call admiral to be put in the recall report.", "Admiral Message", message) as text|null
+
+ if(!message)
+ return
+
+ message_admins("[key_name_admin(usr)] triggered a Centcom recall, with the admiral message of: [message]")
+ log_game("[key_name(usr)] triggered a Centcom recall, with the message of: [message]")
+ SSshuttle.centcom_recall(SSshuttle.emergency.timer, message)
diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm
index c125cd1757..94441ba607 100644
--- a/code/modules/client/asset_cache.dm
+++ b/code/modules/client/asset_cache.dm
@@ -259,41 +259,41 @@ GLOBAL_LIST_EMPTY(asset_datums)
"jquery-1.10.2.min.js" = 'html/IRV/jquery-1.10.2.min.js'
)
-/datum/asset/simple/changelog
- assets = list(
- "88x31.png" = 'html/88x31.png',
- "bug-minus.png" = 'html/bug-minus.png',
- "cross-circle.png" = 'html/cross-circle.png',
- "hard-hat-exclamation.png" = 'html/hard-hat-exclamation.png',
- "image-minus.png" = 'html/image-minus.png',
- "image-plus.png" = 'html/image-plus.png',
- "music-minus.png" = 'html/music-minus.png',
- "music-plus.png" = 'html/music-plus.png',
- "tick-circle.png" = 'html/tick-circle.png',
- "wrench-screwdriver.png" = 'html/wrench-screwdriver.png',
- "spell-check.png" = 'html/spell-check.png',
- "burn-exclamation.png" = 'html/burn-exclamation.png',
- "chevron.png" = 'html/chevron.png',
- "chevron-expand.png" = 'html/chevron-expand.png',
- "scales.png" = 'html/scales.png',
- "changelog.css" = 'html/changelog.css'
- )
-
-/datum/asset/simple/goonchat
- verify = TRUE
- assets = list(
- "jquery.min.js" = 'code/modules/html_interface/js/jquery.min.js',
- "json2.min.js" = 'code/modules/goonchat/browserassets/js/json2.min.js',
- "errorHandler.js" = 'code/modules/goonchat/browserassets/js/errorHandler.js',
- "browserOutput.js" = 'code/modules/goonchat/browserassets/js/browserOutput.js',
- "fontawesome-webfont.eot" = 'tgui/assets/fonts/fontawesome-webfont.eot',
- "fontawesome-webfont.svg" = 'tgui/assets/fonts/fontawesome-webfont.svg',
- "fontawesome-webfont.ttf" = 'tgui/assets/fonts/fontawesome-webfont.ttf',
- "fontawesome-webfont.woff" = 'tgui/assets/fonts/fontawesome-webfont.woff',
- "font-awesome.css" = 'code/modules/goonchat/browserassets/css/font-awesome.css',
- "browserOutput.css" = 'code/modules/goonchat/browserassets/css/browserOutput.css',
- )
-
+/datum/asset/simple/changelog
+ assets = list(
+ "88x31.png" = 'html/88x31.png',
+ "bug-minus.png" = 'html/bug-minus.png',
+ "cross-circle.png" = 'html/cross-circle.png',
+ "hard-hat-exclamation.png" = 'html/hard-hat-exclamation.png',
+ "image-minus.png" = 'html/image-minus.png',
+ "image-plus.png" = 'html/image-plus.png',
+ "music-minus.png" = 'html/music-minus.png',
+ "music-plus.png" = 'html/music-plus.png',
+ "tick-circle.png" = 'html/tick-circle.png',
+ "wrench-screwdriver.png" = 'html/wrench-screwdriver.png',
+ "spell-check.png" = 'html/spell-check.png',
+ "burn-exclamation.png" = 'html/burn-exclamation.png',
+ "chevron.png" = 'html/chevron.png',
+ "chevron-expand.png" = 'html/chevron-expand.png',
+ "scales.png" = 'html/scales.png',
+ "changelog.css" = 'html/changelog.css'
+ )
+
+/datum/asset/simple/goonchat
+ verify = FALSE
+ assets = list(
+ "jquery.min.js" = 'code/modules/html_interface/js/jquery.min.js',
+ "json2.min.js" = 'code/modules/goonchat/browserassets/js/json2.min.js',
+ "errorHandler.js" = 'code/modules/goonchat/browserassets/js/errorHandler.js',
+ "browserOutput.js" = 'code/modules/goonchat/browserassets/js/browserOutput.js',
+ "fontawesome-webfont.eot" = 'tgui/assets/fonts/fontawesome-webfont.eot',
+ "fontawesome-webfont.svg" = 'tgui/assets/fonts/fontawesome-webfont.svg',
+ "fontawesome-webfont.ttf" = 'tgui/assets/fonts/fontawesome-webfont.ttf',
+ "fontawesome-webfont.woff" = 'tgui/assets/fonts/fontawesome-webfont.woff',
+ "font-awesome.css" = 'code/modules/goonchat/browserassets/css/font-awesome.css',
+ "browserOutput.css" = 'code/modules/goonchat/browserassets/css/browserOutput.css',
+ )
+
//Registers HTML Interface assets.
/datum/asset/HTML_interface/register()
for(var/path in typesof(/datum/html_interface))
diff --git a/code/modules/clothing/under/accessories.dm b/code/modules/clothing/under/accessories.dm
index 93321a053a..0bde06b6b9 100644
--- a/code/modules/clothing/under/accessories.dm
+++ b/code/modules/clothing/under/accessories.dm
@@ -85,6 +85,7 @@
item_color = "bronze"
materials = list(MAT_METAL=1000)
resistance_flags = FIRE_PROOF
+ var/medaltype = "medal" //Sprite used for medalbox
var/commended = FALSE
//Pinning medals on people
@@ -138,6 +139,7 @@
desc = "A silver medal."
icon_state = "silver"
item_color = "silver"
+ medaltype = "medal-silver"
materials = list(MAT_SILVER=1000)
/obj/item/clothing/accessory/medal/silver/valor
@@ -153,6 +155,7 @@
desc = "A prestigious golden medal."
icon_state = "gold"
item_color = "gold"
+ medaltype = "medal-gold"
materials = list(MAT_GOLD=1000)
/obj/item/clothing/accessory/medal/gold/captain
@@ -169,6 +172,7 @@
desc = "An eccentric medal made of plasma."
icon_state = "plasma"
item_color = "plasma"
+ medaltype = "medal-plasma"
armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = -10, acid = 0) //It's made of plasma. Of course it's flammable.
materials = list(MAT_PLASMA=1000)
diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm
index 430e91a356..c6462f9d66 100644
--- a/code/modules/flufftext/Hallucination.dm
+++ b/code/modules/flufftext/Hallucination.dm
@@ -262,51 +262,57 @@ Gunshots/explosions/opening doors/less rare audio (done)
px = -32
/obj/effect/hallucination/oh_yeah
- var/turf/closed/wall/wall
- var/obj/effect/hallucination/simple/bubblegum/bubblegum = null
+ var/obj/effect/hallucination/simple/bubblegum/bubblegum
var/image/fakebroken
var/image/fakerune
/obj/effect/hallucination/oh_yeah/Initialize(mapload, var/mob/living/carbon/T)
- ..()
+ . = ..()
target = T
+ var/turf/closed/wall/wall
for(var/turf/closed/wall/W in range(7,target))
wall = W
break
- if(wall)
- fakebroken = image('icons/turf/floors.dmi', wall, "plating", layer = TURF_LAYER)
- var/turf/landing = get_turf(target)
- var/turf/landing_image_turf = get_step(landing, SOUTHWEST) //the icon is 3x3
- fakerune = image('icons/effects/96x96.dmi', landing_image_turf, "landing", layer = ABOVE_OPEN_TURF_LAYER)
- fakebroken.override = TRUE
- if(target.client)
- target.client.images |= fakebroken
- target.client.images |= fakerune
- target.playsound_local(wall,'sound/effects/meteorimpact.ogg', 150, 1)
- bubblegum = new(wall, target)
- sleep(10) //ominous wait
- var/charged = FALSE //only get hit once
- while(get_turf(bubblegum) != landing && target && target.stat != DEAD)
- bubblegum.forceMove(get_step_towards(bubblegum, landing))
- bubblegum.setDir(get_dir(bubblegum, landing))
- target.playsound_local(get_turf(bubblegum), 'sound/effects/meteorimpact.ogg', 150, 1)
- shake_camera(target, 2, 1)
- if(bubblegum.Adjacent(target) && !charged)
- charged = TRUE
- target.Weaken(4)
- target.staminaloss += 40
- step_away(target, bubblegum)
- shake_camera(target, 4, 3)
- target.visible_message("[target] jumps backwards, falling on the ground!","[bubblegum] slams into you!")
- sleep(2)
- sleep(30)
- qdel(bubblegum)
+ if(!wall)
+ return INITIALIZE_HINT_QDEL
+
+ fakebroken = image('icons/turf/floors.dmi', wall, "plating", layer = TURF_LAYER)
+ var/turf/landing = get_turf(target)
+ var/turf/landing_image_turf = get_step(landing, SOUTHWEST) //the icon is 3x3
+ fakerune = image('icons/effects/96x96.dmi', landing_image_turf, "landing", layer = ABOVE_OPEN_TURF_LAYER)
+ fakebroken.override = TRUE
+ if(target.client)
+ target.client.images |= fakebroken
+ target.client.images |= fakerune
+ target.playsound_local(wall,'sound/effects/meteorimpact.ogg', 150, 1)
+ bubblegum = new(wall, target)
+ addtimer(CALLBACK(src, .proc/bubble_attack, landing), 10)
+
+/obj/effect/hallucination/oh_yeah/proc/bubble_attack(turf/landing)
+ var/charged = FALSE //only get hit once
+ while(get_turf(bubblegum) != landing && target && target.stat != DEAD)
+ bubblegum.forceMove(get_step_towards(bubblegum, landing))
+ bubblegum.setDir(get_dir(bubblegum, landing))
+ target.playsound_local(get_turf(bubblegum), 'sound/effects/meteorimpact.ogg', 150, 1)
+ shake_camera(target, 2, 1)
+ if(bubblegum.Adjacent(target) && !charged)
+ charged = TRUE
+ target.Weaken(4)
+ target.adjustStaminaLoss(40)
+ step_away(target, bubblegum)
+ shake_camera(target, 4, 3)
+ target.visible_message("[target] jumps backwards, falling on the ground!","[bubblegum] slams into you!")
+ sleep(2)
+ sleep(30)
qdel(src)
/obj/effect/hallucination/oh_yeah/Destroy()
if(target.client)
target.client.images.Remove(fakebroken)
target.client.images.Remove(fakerune)
+ QDEL_NULL(fakebroken)
+ QDEL_NULL(fakerune)
+ QDEL_NULL(bubblegum)
return ..()
/obj/effect/hallucination/singularity_scare
@@ -544,7 +550,7 @@ Gunshots/explosions/opening doors/less rare audio (done)
/obj/item/clothing/suit/space/nasavoid, /obj/item/weapon/tank)
var/obj/effect/fake_attacker/F = new/obj/effect/fake_attacker(get_turf(target),target)
-
+
for(var/obj/item/I in clone.held_items)
if(!(locate(I) in non_fakeattack_weapons))
clone_weapon = I.name
@@ -625,7 +631,7 @@ Gunshots/explosions/opening doors/less rare audio (done)
else if(src.dir == WEST)
del src.currentimage
src.currentimage = new /image(left,src)
- SEND_IMAGE(my_target, currentimage)
+ to_chat(my_target, currentimage)
/obj/effect/fake_attacker/proc/attack_loop()
@@ -669,7 +675,7 @@ Gunshots/explosions/opening doors/less rare audio (done)
var/obj/effect/overlay/O = new/obj/effect/overlay(target.loc)
O.name = "blood"
var/image/I = image('icons/effects/blood.dmi',O,"floor[rand(1,7)]",O.dir,1)
- SEND_IMAGE(target, I)
+ to_chat(target, I)
QDEL_IN(O, 300)
@@ -786,6 +792,7 @@ Gunshots/explosions/opening doors/less rare audio (done)
new /obj/effect/hallucination/items_other(src.loc,src)
if("sounds")
//Strange audio
+ //to_chat(src, "Strange Audio")
switch(rand(1,20))
if(1) playsound_local(null,'sound/machines/airlock.ogg', 15, 1)
if(2)
diff --git a/code/modules/language/codespeak.dm b/code/modules/language/codespeak.dm
new file mode 100644
index 0000000000..35fc6e0aa7
--- /dev/null
+++ b/code/modules/language/codespeak.dm
@@ -0,0 +1,83 @@
+/datum/language/codespeak
+ name = "Codespeak"
+ desc = "Syndicate operatives can use a series of codewords to convey complex information, while sounding like random concepts and drinks to anyone listening in."
+ key = "t"
+ default_priority = 0
+ flags = TONGUELESS_SPEECH | LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD
+ icon_state = "codespeak"
+
+/datum/language/codespeak/scramble(input)
+ var/lookup = check_cache(input)
+ if(lookup)
+ return lookup
+
+ . = ""
+ var/list/words = list()
+ while(length(.) < length(input))
+ words += generate_code_phrase(return_list=TRUE)
+ . = jointext(words, ", ")
+
+ . = capitalize(.)
+
+ var/input_ending = copytext(input, length(input))
+
+ var/static/list/endings
+ if(!endings)
+ endings = list("!", "?", ".")
+
+ if(input_ending in endings)
+ . += input_ending
+
+ add_to_cache(input, .)
+
+/obj/item/weapon/codespeak_manual
+ name = "codespeak manual"
+ desc = "The book's cover reads: \"Codespeak(tm) - Secure your communication with metaphors so elaborate, they seem randomly generated!\""
+ icon = 'icons/obj/library.dmi'
+ icon_state = "book2"
+ var/charges = 1
+
+/obj/item/weapon/codespeak_manual/attack_self(mob/living/user)
+ if(!isliving(user))
+ return
+
+ if(user.has_language(/datum/language/codespeak))
+ to_chat(user, "You start skimming through [src], but you already know Codespeak.")
+ return
+
+ to_chat(user, "You start skimming through [src], and suddenly your mind is filled with codewords and responses.")
+ user.grant_language(/datum/language/codespeak)
+
+ use_charge(user)
+
+/obj/item/weapon/codespeak_manual/attack(mob/living/M, mob/living/user)
+ if(!istype(M) || !istype(user))
+ return
+ if(M == user)
+ attack_self(user)
+ return
+
+ playsound(loc, "punch", 25, 1, -1)
+
+ if(M.stat == DEAD)
+ M.visible_message("[user] smacks [M]'s lifeless corpse with [src].", "[user] smacks your lifeless corpse with [src].", "You hear smacking.")
+ else if(M.has_language(/datum/language/codespeak))
+ M.visible_message("[user] beats [M] over the head with [src]!", "[user] beats you over the head with [src]!", "You hear smacking.")
+ else
+ M.visible_message("[user] teaches [M] by beating them over the head with [src]!", "As [user] hits you with [src], codewords and responses flow through your mind.", "You hear smacking.")
+ M.grant_language(/datum/language/codespeak)
+ use_charge(user)
+
+/obj/item/weapon/codespeak_manual/proc/use_charge(mob/user)
+ charges--
+ if(!charges)
+ var/turf/T = get_turf(src)
+ T.visible_message("The cover and contents of [src] start shifting and changing!")
+
+ qdel(src)
+ var/obj/item/weapon/book/manual/random/book = new(T)
+ user.put_in_active_hand(book)
+
+/obj/item/weapon/codespeak_manual/unlimited
+ name = "deluxe codespeak manual"
+ charges = INFINITY
diff --git a/code/modules/language/language.dm b/code/modules/language/language.dm
index 5354532cfa..6d9bdcf256 100644
--- a/code/modules/language/language.dm
+++ b/code/modules/language/language.dm
@@ -35,7 +35,7 @@
return TRUE
/datum/language/proc/get_icon()
- return ""
+ return "[bicon(icon(icon, icon_state))]"
/datum/language/proc/get_random_name(gender, name_count=2, syllable_count=4, syllable_divisor=2)
if(!syllables || !syllables.len)
@@ -56,6 +56,18 @@
return "[trim(full_name)]"
+/datum/language/proc/check_cache(input)
+ var/lookup = check_cache(input)
+ if(lookup)
+ return lookup
+
+/datum/language/proc/add_to_cache(input, scrambled_text)
+ add_to_cache(input, scrambled_text)
+
+
+ return scrambled_text
+
+
/datum/language/proc/scramble(input)
if(!syllables || !syllables.len)
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 59ba5d1aac..f6524d3502 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -1,4 +1,4 @@
-
+#define LINKIFY_READY(string, value) "[string]"
/mob/dead/new_player
var/ready = 0
@@ -24,26 +24,28 @@
loc = pick(GLOB.newplayer_start)
else
loc = locate(1,1,1)
- . = ..()
-
-/mob/dead/new_player/prepare_huds()
- return
+ . = ..()
+
+/mob/dead/new_player/prepare_huds()
+ return
/mob/dead/new_player/proc/new_player_panel()
- var/output = "
\[ Ready | Not Ready \]
" - else - output += "\[ Ready | Not Ready \]
" + if(SSticker && SSticker.current_state <= GAME_STATE_PREGAME) + switch(ready) + if(PLAYER_NOT_READY) + output += "\[ [LINKIFY_READY("Ready", PLAYER_READY_TO_PLAY)] | Not Ready | [LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)] \]
" + if(PLAYER_READY_TO_PLAY) + output += "\[ Ready | [LINKIFY_READY("Not Ready", PLAYER_NOT_READY)] | [LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)] \]
" + if(PLAYER_READY_TO_OBSERVE) + output += "\[ [LINKIFY_READY("Ready", PLAYER_READY_TO_PLAY)] | [LINKIFY_READY("Not Ready", PLAYER_NOT_READY)] | Observe \]
" else - output += "" - output += "" - - output += "" + output += "" + output += "" + output += "[LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)]
" if(!IsGuestKey(src.key)) if (SSdbcore.Connect()) @@ -65,7 +67,7 @@ output += "