Merge branch 'master' into upstream-merge-31044

This commit is contained in:
LetterJay
2017-09-30 14:53:08 -04:00
committed by GitHub
346 changed files with 9273 additions and 7009 deletions

View File

@@ -19,3 +19,6 @@
#define ANTAG_DATUM_IAA_AI_CUSTOM /datum/antagonist/traitor/AI/internal_affairs/custom
#define ANTAG_DATUM_IAA_AI /datum/antagonist/traitor/AI/internal_affairs
#define ANTAG_DATUM_BROTHER /datum/antagonist/brother
#define ANTAG_DATUM_ABDUCTOR /datum/antagonist/abductor
#define ANTAG_DATUM_ABDUCTOR_SCIENTIST /datum/antagonist/abductor/scientist
#define ANTAG_DATUM_ABDUCTOR_AGENT /datum/antagonist/abductor/agent

View File

@@ -0,0 +1,11 @@
#define CINEMATIC_DEFAULT 1
#define CINEMATIC_SELFDESTRUCT 2
#define CINEMATIC_SELFDESTRUCT_MISS 3
#define CINEMATIC_NUKE_WIN 4
#define CINEMATIC_NUKE_MISS 5
#define CINEMATIC_ANNIHILATION 6
#define CINEMATIC_MALF 7
#define CINEMATIC_CULT 8
#define CINEMATIC_NUKE_FAKE 9
#define CINEMATIC_NUKE_NO_CORE 10
#define CINEMATIC_NUKE_FAR 11

View File

@@ -8,43 +8,31 @@
GLOBAL_VAR_INIT(clockwork_construction_value, 0) //The total value of all structures built by the clockwork cult
GLOBAL_VAR_INIT(clockwork_caches, 0) //How many clockwork caches exist in the world (not each individual)
GLOBAL_VAR_INIT(clockwork_vitality, 0) //How much Vitality is stored, total
GLOBAL_LIST_EMPTY(active_daemons) //A list of all active tinkerer's daemons
GLOBAL_VAR_INIT(clockwork_power, 0) //How many watts of power are globally available to the clockwork cult
GLOBAL_LIST_EMPTY(all_clockwork_objects) //All clockwork items, structures, and effects in existence
GLOBAL_LIST_EMPTY(all_clockwork_mobs) //All clockwork SERVANTS (not creatures) in existence
GLOBAL_LIST_INIT(clockwork_component_cache, list(BELLIGERENT_EYE = 0, VANGUARD_COGWHEEL = 0, GEIS_CAPACITOR = 0, REPLICANT_ALLOY = 0, HIEROPHANT_ANSIBLE = 0)) //The pool of components that caches draw from
GLOBAL_VAR_INIT(ratvar_approaches, 0) //The servants can choose to "herald" Ratvar, permanently buffing them but announcing their presence to the crew.
GLOBAL_VAR_INIT(ratvar_awakens, 0) //If Ratvar has been summoned; not a boolean, for proper handling of multiple Ratvars
GLOBAL_VAR_INIT(ark_of_the_clockwork_justiciar, FALSE) //The Ark on the Reebe z-level
GLOBAL_VAR_INIT(clockwork_gateway_activated, FALSE) //if a gateway to the celestial derelict has ever been successfully activated
GLOBAL_VAR_INIT(script_scripture_unlocked, FALSE) //If script scripture is available, through converting at least one crewmember
GLOBAL_VAR_INIT(application_scripture_unlocked, FALSE) //If script scripture is available
GLOBAL_LIST_EMPTY(all_scripture) //a list containing scripture instances; not used to track existing scripture
//Scripture tiers and requirements; peripherals should never be used
#define SCRIPTURE_PERIPHERAL "Peripheral"
#define SCRIPTURE_DRIVER "Driver"
#define SCRIPTURE_SCRIPT "Script"
#define SCRIPT_SERVANT_REQ 6
#define SCRIPT_CACHE_REQ 1
#define SCRIPTURE_APPLICATION "Application"
#define APPLICATION_SERVANT_REQ 9
#define APPLICATION_CACHE_REQ 3
#define APPLICATION_CV_REQ 100
#define SCRIPTURE_JUDGEMENT "Judgement"
#define JUDGEMENT_SERVANT_REQ 12
#define JUDGEMENT_CACHE_REQ 5
#define JUDGEMENT_CV_REQ 300
//general component/cooldown things
#define SLAB_PRODUCTION_TIME 450 //how long(deciseconds) slabs require to produce a single component; defaults to 45 seconds
//Various costs related to power.
#define SCRIPT_UNLOCK_THRESHOLD 50000 //Scripts will unlock if the total power reaches this amount
#define APPLICATION_UNLOCK_THRESHOLD 100000 //Applications will unlock if the total powre reaches this amount
#define SLAB_SERVANT_SLOWDOWN 150 //how much each servant above 5 slows down slab-based generation; defaults to 15 seconds per sevant
#define SLAB_SLOWDOWN_MAXIMUM 1350 //maximum slowdown from additional servants; defaults to 2 minutes 15 seconds
#define CACHE_PRODUCTION_TIME 300 //how long(deciseconds) caches require to produce a component; defaults to 30 seconds
#define ACTIVE_CACHE_SLOWDOWN 50 //how many additional deciseconds caches take to produce a component for each linked cache; defaults to 5 seconds
#define LOWER_PROB_PER_COMPONENT 10 //how much each component in the cache reduces the weight of getting another of that component type
#define MAX_COMPONENTS_BEFORE_RAND (10*LOWER_PROB_PER_COMPONENT) //the number of each component, times LOWER_PROB_PER_COMPONENT, you need to have before component generation will become random
#define ABSCOND_ABDUCTION_COST 95
//clockcult power defines
#define MIN_CLOCKCULT_POWER 25 //the minimum amount of power clockcult machines will handle gracefully
@@ -72,11 +60,11 @@ GLOBAL_LIST_EMPTY(all_scripture) //a list containing scripture instances; not us
//Ark defines
#define GATEWAY_SUMMON_RATE 1 //the time amount the Gateway to the Celestial Derelict gets each process tick; defaults to 1 per tick
#define GATEWAY_REEBE_FOUND 119 //when progress is at or above this, the gateway finds reebe and begins drawing power
#define GATEWAY_REEBE_FOUND 240 //when progress is at or above this, the gateway finds reebe and begins drawing power
#define GATEWAY_RATVAR_COMING 239 //when progress is at or above this, ratvar has entered and is coming through the gateway
#define GATEWAY_RATVAR_COMING 480 //when progress is at or above this, ratvar has entered and is coming through the gateway
#define GATEWAY_RATVAR_ARRIVAL 300 //when progress is at or above this, game over ratvar's here everybody go home
#define GATEWAY_RATVAR_ARRIVAL 600 //when progress is at or above this, game over ratvar's here everybody go home
#define ARK_SUMMON_COST 5 //how many of each component an Ark costs to summon
@@ -94,6 +82,6 @@ GLOBAL_LIST_EMPTY(all_scripture) //a list containing scripture instances; not us
#define OCULAR_WARDEN_EXCLUSION_RANGE 3 //the range at which ocular wardens cannot be placed near other ocular wardens
#define RATVARIAN_SPEAR_DURATION 1800 //how long ratvarian spears last; defaults to 3 minutes
#define CLOCKWORK_ARMOR_COOLDOWN 1800 //The cooldown period between summoning suits of clockwork armor
#define PRISM_DELAY_DURATION 1200 //how long prolonging prisms delay the shuttle for; defaults to 2 minutes
#define RATVARIAN_SPEAR_COOLDOWN 300 //The cooldown period between summoning another Ratvarian spear

View File

@@ -12,6 +12,7 @@
#define HIGH_TURF_LAYER 2.03
#define ABOVE_OPEN_TURF_LAYER 2.04
#define CLOSED_TURF_LAYER 2.05
#define BULLET_HOLE_LAYER 2.06
#define ABOVE_NORMAL_TURF_LAYER 2.08
#define LATTICE_LAYER 2.2
#define DISPOSAL_PIPE_LAYER 2.3

View File

@@ -3,10 +3,11 @@ The /tg/ codebase currently requires you to have 11 z-levels of the same size di
z-level order is important, the order you put them in inside the map config.dm will determine what z level number they are assigned ingame.
Names of z-level do not matter, but order does greatly, for instances such as checking alive status of revheads on z1
current as of 2016/6/2
current as of september 17, 2017
z1 = station
z2 = centcom
z5 = mining
z6 = city of cogs
Everything else = randomized space
Last space-z level = empty
*/
@@ -17,6 +18,7 @@ Last space-z level = empty
#define MAIN_STATION "Main Station"
#define CENTCOM "CentCom"
#define CITY_OF_COGS "City of Cogs"
#define EMPTY_AREA_1 "Empty Area 1"
#define EMPTY_AREA_2 "Empty Area 2"
#define MINING "Mining Asteroid"
@@ -38,6 +40,7 @@ Last space-z level = empty
#define ZLEVEL_STATION_PRIMARY 2
#define ZLEVEL_MINING 5
#define ZLEVEL_LAVALAND 5
#define ZLEVEL_CITYOFCOGS 6
#define ZLEVEL_EMPTY_SPACE 12
#define ZLEVEL_TRANSIT 11

View File

@@ -62,6 +62,8 @@
#define CURSE_WASTING 4 //causes gradual damage
#define CURSE_GRASPING 8 //hands reach out from the sides of the screen, doing damage and stunning if they hit the target
#define STATUS_EFFECT_KINDLE /datum/status_effect/kindle //A knockdown reduced by 1 second for every 3 points of damage the target takes.
/////////////
// NEUTRAL //
/////////////

View File

@@ -474,7 +474,7 @@
#error Remie said that lummox was adding a way to get a lists
#error contents via list.values, if that is true remove this
#error otherwise, update the version and bug lummox
#elseif
#endif
//Flattens a keyed list into a list of it's contents
/proc/flatten_list(list/key_list)
if(!islist(key_list))

View File

@@ -43,7 +43,7 @@
/client/proc/file_spam_check()
var/time_to_wait = GLOB.fileaccess_timer - world.time
if(time_to_wait > 0)
to_chat(src, "<font color='red'>Error: file_spam_check(): Spam. Please wait [round(time_to_wait/10)] seconds.</font>")
to_chat(src, "<font color='red'>Error: file_spam_check(): Spam. Please wait [DisplayTimeText(time_to_wait)].</font>")
return 1
GLOB.fileaccess_timer = world.time + FTPDELAY
return 0

View File

@@ -421,7 +421,7 @@
SEND_SOUND(M, 'sound/misc/notice2.ogg') //Alerting them to their consideration
if(flashwindow)
window_flash(M.client)
switch(ignore_category ? askuser(M,Question,"Please answer in [poll_time/10] seconds!","Yes","No","Never for this round", StealFocus=0, Timeout=poll_time) : askuser(M,Question,"Please answer in [poll_time/10] seconds!","Yes","No", StealFocus=0, Timeout=poll_time))
switch(ignore_category ? askuser(M,Question,"Please answer in [DisplayTimeText(poll_time)]!","Yes","No","Never for this round", StealFocus=0, Timeout=poll_time) : askuser(M,Question,"Please answer in [DisplayTimeText(poll_time)]!","Yes","No", StealFocus=0, Timeout=poll_time))
if(1)
to_chat(M, "<span class='notice'>Choice registered: Yes.</span>")
if(time_passed + poll_time <= world.time)

View File

@@ -219,3 +219,8 @@ GLOBAL_LIST_INIT(sqrtTable, list(1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4,
new_x = Clamp(new_x, 0, world.maxx)
new_y = Clamp(new_y, 0, world.maxy)
return locate(new_x, new_y, starting.z)
/proc/round_down(num)
if(round(num) != num)
return round(num--)
else return num

View File

@@ -465,13 +465,17 @@ Proc for attack log creation, because really why not
if(H.dna && istype(H.dna.species, species_datum))
. = TRUE
/proc/spawn_atom_to_turf(spawn_type, target, amount, admin_spawn=FALSE)
/proc/spawn_atom_to_turf(spawn_type, target, amount, admin_spawn=FALSE, list/extra_args)
var/turf/T = get_turf(target)
if(!T)
CRASH("attempt to spawn atom type: [spawn_type] in nullspace")
for(var/j in 1 to amount)
var/atom/X = new spawn_type(T)
var/list/new_args = list(T)
if(extra_args)
new_args += extra_args
for(var/j in 1 to amount)
var/atom/X = new spawn_type(arglist(new_args))
X.admin_spawned = admin_spawn
/proc/spawn_and_random_walk(spawn_type, target, amount, walk_chance=100, max_walk=3, always_max_walk=FALSE, admin_spawn=FALSE)

View File

@@ -414,34 +414,159 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
end = temp
return end
/proc/parsepencode(t, mob/user=null, signfont=SIGNFONT)
if(length(t) < 1) //No input means nothing needs to be parsed
/proc/parsemarkdown_basic_step1(t, limited=FALSE)
if(length(t) <= 0)
return
t = replacetext(t, "\[center\]", "<center>")
t = replacetext(t, "\[/center\]", "</center>")
t = replacetext(t, "\[br\]", "<BR>")
t = replacetext(t, "\[b\]", "<B>")
t = replacetext(t, "\[/b\]", "</B>")
t = replacetext(t, "\[i\]", "<I>")
t = replacetext(t, "\[/i\]", "</I>")
t = replacetext(t, "\[u\]", "<U>")
t = replacetext(t, "\[/u\]", "</U>")
t = replacetext(t, "\[large\]", "<font size=\"4\">")
t = replacetext(t, "\[/large\]", "</font>")
if(user)
t = replacetext(t, "\[sign\]", "<font face=\"[signfont]\"><i>[user.real_name]</i></font>")
else
t = replacetext(t, "\[sign\]", "")
t = replacetext(t, "\[field\]", "<span class=\"paper_field\"></span>")
// This parses markdown with no custom rules
t = replacetext(t, "\[*\]", "<li>")
t = replacetext(t, "\[hr\]", "<HR>")
t = replacetext(t, "\[small\]", "<font size = \"1\">")
t = replacetext(t, "\[/small\]", "</font>")
t = replacetext(t, "\[list\]", "<ul>")
t = replacetext(t, "\[/list\]", "</ul>")
// Escape backslashed
t = replacetext(t, "$", "$-")
t = replacetext(t, "\\\\", "$1")
t = replacetext(t, "\\**", "$2")
t = replacetext(t, "\\*", "$3")
t = replacetext(t, "\\__", "$4")
t = replacetext(t, "\\_", "$5")
t = replacetext(t, "\\^", "$6")
t = replacetext(t, "\\((", "$7")
t = replacetext(t, "\\))", "$8")
t = replacetext(t, "\\|", "$9")
t = replacetext(t, "\\%", "$0")
// Escape single characters that will be used
t = replacetext(t, "!", "$a")
// Parse hr and small
if(!limited)
t = replacetext(t, "((", "<font size=\"1\">")
t = replacetext(t, "))", "</font>")
t = replacetext(t, regex("^-{3,}$", "gm"), "<hr>")
t = replacetext(t, regex("^\\(-{3,})$", "gm"), "$1")
// Parse lists
var/list/tlist = splittext(t, "\n")
var/tlistlen = tlist.len
var/listlevel = -1
var/singlespace = -1 // if 0, double spaces are used before asterisks, if 1, single are
for(var/i = 1, i <= tlistlen, i++)
var/line = tlist[i]
var/count_asterisk = length(replacetext(line, regex("\[^\\*\]+", "g"), ""))
if(count_asterisk % 2 == 1 && findtext(line, regex("^\\s*\\*", "g"))) // there is an extra asterisk in the beggining
var/count_w = length(replacetext(line, regex("^( *)\\*.*$", "g"), "$1")) // whitespace before asterisk
line = replacetext(line, regex("^ *(\\*.*)$", "g"), "$1")
if(singlespace == -1 && count_w == 2)
if(listlevel == 0)
singlespace = 0
else
singlespace = 1
if(singlespace == 0)
count_w = count_w % 2 ? round(count_w / 2 + 0.25) : count_w / 2
line = replacetext(line, regex("\\*", ""), "<li>")
while(listlevel < count_w)
line = "<ul>" + line
listlevel++
while(listlevel > count_w)
line = "</ul>" + line
listlevel--
else while(listlevel >= 0)
line = "</ul>" + line
listlevel--
tlist[i] = line
// end for
t = tlist[1]
for(var/i = 2, i <= tlistlen, i++)
t += "\n" + tlist[i]
while(listlevel >= 0)
t += "</ul>"
listlevel--
else
t = replacetext(t, "((", "")
t = replacetext(t, "))", "")
// Parse headers
t = replacetext(t, regex("^#(?!#) ?(.+)$", "gm"), "<h2>$1</h2>")
t = replacetext(t, regex("^##(?!#) ?(.+)$", "gm"), "<h3>$1</h3>")
t = replacetext(t, regex("^###(?!#) ?(.+)$", "gm"), "<h4>$1</h4>")
t = replacetext(t, regex("^#### ?(.+)$", "gm"), "<h5>$1</h5>")
// Parse most rules
t = replacetext(t, regex("\\*(\[^\\*\]*)\\*", "g"), "<i>$1</i>")
t = replacetext(t, regex("_(\[^_\]*)_", "g"), "<i>$1</i>")
t = replacetext(t, "<i></i>", "!")
t = replacetext(t, "</i><i>", "!")
t = replacetext(t, regex("\\!(\[^\\!\]+)\\!", "g"), "<b>$1</b>")
t = replacetext(t, regex("\\^(\[^\\^\]+)\\^", "g"), "<font size=\"4\">$1</font>")
t = replacetext(t, regex("\\|(\[^\\|\]+)\\|", "g"), "<center>$1</center>")
t = replacetext(t, "!", "</i><i>")
return t
/proc/parsemarkdown_basic_step2(t)
if(length(t) <= 0)
return
// Restore the single characters used
t = replacetext(t, "$a", "!")
// Redo the escaping
t = replacetext(t, "$1", "\\")
t = replacetext(t, "$2", "**")
t = replacetext(t, "$3", "*")
t = replacetext(t, "$4", "__")
t = replacetext(t, "$5", "_")
t = replacetext(t, "$6", "^")
t = replacetext(t, "$7", "((")
t = replacetext(t, "$8", "))")
t = replacetext(t, "$9", "|")
t = replacetext(t, "$0", "%")
t = replacetext(t, "$-", "$")
return t
/proc/parsemarkdown_basic(t, limited=FALSE)
t = parsemarkdown_basic_step1(t, limited)
t = parsemarkdown_basic_step2(t)
return t
/proc/parsemarkdown(t, mob/user=null, limited=FALSE)
if(length(t) <= 0)
return
// Premanage whitespace
t = replacetext(t, regex("\[^\\S\\r\\n \]", "g"), " ")
t = parsemarkdown_basic_step1(t)
t = replacetext(t, regex("%s(?:ign)?(?=\\s|$)", "igm"), user ? "<font face=\"[SIGNFONT]\"><i>[user.real_name]</i></font>" : "<span class=\"paper_field\"></span>")
t = replacetext(t, regex("%f(?:ield)?(?=\\s|$)", "igm"), "<span class=\"paper_field\"></span>")
t = parsemarkdown_basic_step2(t)
// Manage whitespace
t = replacetext(t, regex("(?:\\r\\n?|\\n)", "g"), "<br>")
t = replacetext(t, " ", "&nbsp;&nbsp;")
// Done
return t

View File

@@ -1,52 +1,148 @@
//Returns the world time in english
/proc/worldtime2text()
return gameTimestamp("hh:mm:ss", world.time)
/proc/time_stamp(format = "hh:mm:ss", show_ds)
var/time_string = time2text(world.timeofday, format)
return show_ds ? "[time_string]:[world.timeofday % 10]" : time_string
/proc/gameTimestamp(format = "hh:mm:ss", wtime=null)
if(!wtime)
wtime = world.time
return time2text(wtime - GLOB.timezoneOffset + SSticker.gametime_offset - SSticker.round_start_time, format)
/* Returns 1 if it is the selected month and day */
/proc/isDay(month, day)
if(isnum(month) && isnum(day))
var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month
var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day
if(month == MM && day == DD)
return 1
// Uncomment this out when debugging!
//else
//return 1
//returns timestamp in a sql and ISO 8601 friendly format
/proc/SQLtime(timevar)
if(!timevar)
timevar = world.realtime
return time2text(timevar, "YYYY-MM-DD hh:mm:ss")
GLOBAL_VAR_INIT(midnight_rollovers, 0)
GLOBAL_VAR_INIT(rollovercheck_last_timeofday, 0)
/proc/update_midnight_rollover()
if (world.timeofday < GLOB.rollovercheck_last_timeofday) //TIME IS GOING BACKWARDS!
return GLOB.midnight_rollovers++
return GLOB.midnight_rollovers
/proc/weekdayofthemonth()
var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day
switch(DD)
if(8 to 13)
return 2
if(14 to 20)
return 3
if(21 to 27)
return 4
if(28 to INFINITY)
return 5
else
return 1
//Returns the world time in english
/proc/worldtime2text()
return gameTimestamp("hh:mm:ss", world.time)
/proc/time_stamp(format = "hh:mm:ss", show_ds)
var/time_string = time2text(world.timeofday, format)
return show_ds ? "[time_string]:[world.timeofday % 10]" : time_string
/proc/gameTimestamp(format = "hh:mm:ss", wtime=null)
if(!wtime)
wtime = world.time
return time2text(wtime - GLOB.timezoneOffset + SSticker.gametime_offset - SSticker.round_start_time, format)
/* Returns 1 if it is the selected month and day */
/proc/isDay(month, day)
if(isnum(month) && isnum(day))
var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month
var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day
if(month == MM && day == DD)
return 1
// Uncomment this out when debugging!
//else
//return 1
//returns timestamp in a sql and ISO 8601 friendly format
/proc/SQLtime(timevar)
if(!timevar)
timevar = world.realtime
return time2text(timevar, "YYYY-MM-DD hh:mm:ss")
GLOBAL_VAR_INIT(midnight_rollovers, 0)
GLOBAL_VAR_INIT(rollovercheck_last_timeofday, 0)
/proc/update_midnight_rollover()
if (world.timeofday < GLOB.rollovercheck_last_timeofday) //TIME IS GOING BACKWARDS!
return GLOB.midnight_rollovers++
return GLOB.midnight_rollovers
/proc/weekdayofthemonth()
var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day
switch(DD)
if(8 to 13)
return 2
if(14 to 20)
return 3
if(21 to 27)
return 4
if(28 to INFINITY)
return 5
else
return 1
//Takes a value of time in deciseconds.
//Returns a text value of that number in hours, minutes, or seconds.
/proc/DisplayTimeText(time_value)
var/second = time_value*0.1
var/second_adjusted = null
var/second_rounded = FALSE
var/minute = null
var/hour = null
var/day = null
if(!second)
return "0 seconds"
if(second >= 60)
minute = round_down(second/60)
second = round(second - (minute*60), 0.1)
second_rounded = TRUE
if(second) //check if we still have seconds remaining to format, or if everything went into minute.
second_adjusted = round(second) //used to prevent '1 seconds' being shown
if(day || hour || minute)
if(second_adjusted == 1 && second >= 1)
second = " and 1 second"
else if(second > 1)
second = " and [second_adjusted] seconds"
else //shows a fraction if seconds is < 1
if(second_rounded) //no sense rounding again if it's already done
second = " and [second] seconds"
else
second = " and [round(second, 0.1)] seconds"
else
if(second_adjusted == 1 && second >= 1)
second = "1 second"
else if(second > 1)
second = "[second_adjusted] seconds"
else
if(second_rounded)
second = "[second] seconds"
else
second = "[round(second, 0.1)] seconds"
else
second = null
if(!minute)
return "[second]"
if(minute >= 60)
hour = round_down(minute/60,1)
minute = (minute - (hour*60))
if(minute) //alot simpler from here since you don't have to worry about fractions
if(minute != 1)
if((day || hour) && second)
minute = ", [minute] minutes"
else if((day || hour) && !second)
minute = " and [minute] minutes"
else
minute = "[minute] minutes"
else
if((day || hour) && second)
minute = ", 1 minute"
else if((day || hour) && !second)
minute = " and 1 minute"
else
minute = "1 minute"
else
minute = null
if(!hour)
return "[minute][second]"
if(hour >= 24)
day = round_down(hour/24,1)
hour = (hour - (day*24))
if(hour)
if(hour != 1)
if(day && (minute || second))
hour = ", [hour] hours"
else if(day && (!minute || !second))
hour = " and [hour] hours"
else
hour = "[hour] hours"
else
if(day && (minute || second))
hour = ", 1 hour"
else if(day && (!minute || !second))
hour = " and 1 hour"
else
hour = "1 hour"
else
hour = null
if(!day)
return "[hour][minute][second]"
if(day > 1)
day = "[day] days"
else
day = "1 day"
return "[day][hour][minute][second]"

View File

@@ -502,28 +502,12 @@ Turf and target are separate in case you want to teleport some distance from a t
var/y=arcsin(x/sqrt(1+x*x))
return y
/atom/proc/GetAllContents(list/ignore_typecache)
var/list/processing_list = list(src)
var/list/assembled = list()
if(ignore_typecache) //If there's a typecache, use it.
while(processing_list.len)
var/atom/A = processing_list[1]
processing_list -= A
if(ignore_typecache[A.type])
continue
processing_list |= (A.contents - assembled)
assembled |= A
else //If there's none, only make this check once for performance.
while(processing_list.len)
var/atom/A = processing_list[1]
processing_list -= A
processing_list |= (A.contents - assembled)
assembled |= A
return assembled
/atom/proc/GetAllContents(list/output=list())
. = output
output += src
for(var/i in 1 to contents.len)
var/atom/thing = contents[i]
thing.GetAllContents(output)
//Step-towards method of determining whether one atom can see another. Similar to viewers()
/proc/can_see(atom/source, atom/target, length=5) // I couldnt be arsed to do actual raycasting :I This is horribly inaccurate.

View File

@@ -33,7 +33,7 @@
#define MAX_CHARTER_LEN 80
//MINOR TWEAKS/MISC
#define AGE_MIN 17 //youngest a character can be
#define AGE_MIN 18 //youngest a character can be
#define AGE_MAX 85 //oldest a character can be
#define WIZARD_AGE_MIN 30 //youngest a wizard can be
#define SHOES_SLOWDOWN 0 //How much shoes slow you down by default. Negative values speed you up

View File

@@ -33,6 +33,8 @@ GLOBAL_LIST_EMPTY(department_security_spawns) //list of all department security
GLOBAL_LIST_EMPTY(generic_event_spawns) //list of all spawns for events
GLOBAL_LIST_EMPTY(wizardstart)
GLOBAL_LIST_EMPTY(nukeop_start)
GLOBAL_LIST_EMPTY(nukeop_leader_start)
GLOBAL_LIST_EMPTY(newplayer_start)
GLOBAL_LIST_EMPTY(prisonwarp) //prisoners go to these
GLOBAL_LIST_EMPTY(holdingfacility) //captured people go here
@@ -46,6 +48,8 @@ GLOBAL_LIST_EMPTY(blobstart)
GLOBAL_LIST_EMPTY(secequipment)
GLOBAL_LIST_EMPTY(deathsquadspawn)
GLOBAL_LIST_EMPTY(emergencyresponseteamspawn)
GLOBAL_LIST_EMPTY(servant_spawns) //Servants of Ratvar spawn here
GLOBAL_LIST_EMPTY(city_of_cogs_spawns) //Anyone entering the City of Cogs spawns here
GLOBAL_LIST_EMPTY(ruin_landmarks)
//away missions

View File

@@ -378,90 +378,6 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
/obj/screen/alert/clockwork
alerttooltipstyle = "clockcult"
/obj/screen/alert/clockwork/scripture_reqs
name = "Next Tier Requirements"
desc = "You shouldn't be seeing this description unless you're very fast. If you're very fast, good job!"
icon_state = "no-servants-caches"
/obj/screen/alert/clockwork/scripture_reqs/Initialize()
. = ..()
START_PROCESSING(SSprocessing, src)
process()
/obj/screen/alert/clockwork/scripture_reqs/Destroy()
STOP_PROCESSING(SSprocessing, src)
return ..()
/obj/screen/alert/clockwork/scripture_reqs/process()
if(GLOB.clockwork_gateway_activated)
mob_viewer.clear_alert("scripturereq")
return
var/current_state
for(var/i in SSticker.scripture_states)
if(!SSticker.scripture_states[i])
current_state = i
break
icon_state = "no"
if(!current_state)
name = "Current Objective"
for(var/obj/structure/destructible/clockwork/massive/celestial_gateway/G in GLOB.all_clockwork_objects)
var/area/gate_area = get_area(G)
desc = "<b>Protect the Ark at [gate_area.map_name]!</b>"
return
desc = "<b>All tiers of Scripture are unlocked.<br>\
Acquire components and summon the Ark.</b>"
else
name = "Next Tier Requirements"
var/validservants = 0
var/unconverted_ais_exist = get_unconverted_ais()
for(var/mob/living/L in GLOB.living_mob_list)
if(is_servant_of_ratvar(L) && (ishuman(L) || issilicon(L)))
validservants++
var/req_servants = 0
var/req_caches = 0
var/req_cv = 0
var/req_ai = FALSE
var/list/textlist = list("Requirements for <b>[current_state] Scripture:</b>")
switch(current_state) //get our requirements based on the tier
if(SCRIPTURE_SCRIPT)
req_servants = SCRIPT_SERVANT_REQ
req_caches = SCRIPT_CACHE_REQ
if(SCRIPTURE_APPLICATION)
req_servants = APPLICATION_SERVANT_REQ
req_caches = APPLICATION_CACHE_REQ
req_cv = APPLICATION_CV_REQ
if(SCRIPTURE_JUDGEMENT)
req_servants = JUDGEMENT_SERVANT_REQ
req_caches = JUDGEMENT_CACHE_REQ
req_cv = JUDGEMENT_CV_REQ
req_ai = TRUE
textlist += "<br><b>[validservants]/[req_servants]</b> Servants"
if(validservants < req_servants)
icon_state += "-servants" //in this manner, generate an icon key based on what we're missing
else
textlist += ": <b><font color=#5A6068>\[CHECK\]</font></b>"
textlist += "<br><b>[GLOB.clockwork_caches]/[req_caches]</b> Tinkerer's Caches"
if(GLOB.clockwork_caches < req_caches)
icon_state += "-caches"
else
textlist += ": <b><font color=#5A6068>\[CHECK\]</font></b>"
if(req_cv) //cv only shows up if the tier requires it
textlist += "<br><b>[GLOB.clockwork_construction_value]/[req_cv]</b> Construction Value"
if(GLOB.clockwork_construction_value < req_cv)
icon_state += "-cv"
else
textlist += ": <b><font color=#5A6068>\[CHECK\]</font></b>"
if(req_ai) //same for ai
if(unconverted_ais_exist)
if(unconverted_ais_exist > 1)
textlist += "<br><b>[unconverted_ais_exist] unconverted AIs exist!</b><br>"
else
textlist += "<br><b>An unconverted AI exists!</b>"
icon_state += "-ai"
else
textlist += "<br>No unconverted AIs exist: <b><font color=#5A6068>\[CHECK\]</font></b>"
desc = textlist.Join()
/obj/screen/alert/clockwork/infodump
name = "Global Records"
desc = "You shouldn't be seeing this description, because it should be dynamically generated."
@@ -473,7 +389,6 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
else
var/servants = 0
var/validservants = 0
var/unconverted_ais_exist = get_unconverted_ais()
var/list/textlist
for(var/mob/living/L in GLOB.living_mob_list)
if(is_servant_of_ratvar(L))
@@ -487,29 +402,25 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
textlist = list("<b>[servants]</b> Servants, [validservants ? "<b>[validservants]</b> of which counts":"none of which count"] towards scripture.<br>")
else
textlist = list("<b>[servants]</b> Servant, who [validservants ? "counts":"does not count"] towards scripture.<br>")
textlist += "<b>[GLOB.clockwork_caches ? "[GLOB.clockwork_caches]</b> Tinkerer's Caches.":"No Tinkerer's Caches, construct one!</b>"]<br>\
<b>[GLOB.clockwork_construction_value]</b> Construction Value.<br>"
textlist += "<b>[Floor(servants * 0.2)]</b> Tinkerer's Daemons can be active at once. <b>[LAZYLEN(GLOB.active_daemons)]</b> are active.<br>"
for(var/obj/structure/destructible/clockwork/massive/celestial_gateway/G in GLOB.all_clockwork_objects)
var/area/gate_area = get_area(G)
textlist += "Ark Location: <b>[uppertext(gate_area.map_name)]</b><br>"
if(G.still_needs_components())
textlist += "Ark Components required:<br>"
for(var/i in G.required_components)
if(G.required_components[i])
textlist += "[get_component_icon(i)] <b><font color=[get_component_color_bright(i)]>[G.required_components[i]]</font></b> "
textlist += "<br>"
else
textlist += "Seconds until Ratvar's arrival: <b>[G.get_arrival_text(TRUE)]</b><br>"
break
if(unconverted_ais_exist)
if(unconverted_ais_exist > 1)
textlist += "<b>[unconverted_ais_exist] unconverted AIs exist!</b><br>"
else
textlist += "<b>An unconverted AI exists!</b><br>"
for(var/i in SSticker.scripture_states)
if(i != SCRIPTURE_DRIVER) //ignore the always-unlocked stuff
textlist += "[i] Scripture: <b>[SSticker.scripture_states[i] ? "UNLOCKED":"LOCKED"]</b><br>"
for(var/i in SSticker.scripture_states)
if(i != SCRIPTURE_DRIVER) //ignore the always-unlocked stuff
textlist += "[i] Scripture: <b>[SSticker.scripture_states[i] ? "UNLOCKED":"LOCKED"]</b><br>"
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
if(G)
var/time_info
var/time_name
if(G.seconds_until_activation)
time_info = G.seconds_until_activation
time_name = "until the Ark activates"
else if(G.grace_period)
time_info = G.grace_period
time_name = "of grace period remaining"
else if(G.progress_in_seconds)
time_info = GATEWAY_RATVAR_ARRIVAL - G.progress_in_seconds
time_name = "until the Ark finishes summoning"
if(time_info)
textlist += "<b>[time_info / 60] minutes</b> [time_name].<br>"
textlist += "<b>[DisplayPower(get_clockwork_power())]</b> power available for use."
desc = textlist.Join()
..()

View File

@@ -1,45 +0,0 @@
/datum/hud/marauder
var/obj/screen/hosthealth
var/obj/screen/blockchance
var/obj/screen/counterchance
/datum/hud/marauder/New(mob/living/simple_animal/hostile/guardian/owner)
..()
var/obj/screen/using
healths = new /obj/screen/healths/clock()
infodisplay += healths
hosthealth = new /obj/screen/healths/clock()
hosthealth.screen_loc = ui_internal
infodisplay += hosthealth
using = new /obj/screen/marauder/emerge()
using.screen_loc = ui_zonesel
static_inventory += using
/datum/hud/marauder/Destroy()
blockchance = null
counterchance = null
hosthealth = null
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/create_mob_hud()
if(client && !hud_used)
hud_used = new /datum/hud/marauder(src, ui_style2icon(client.prefs.UI_style))
/obj/screen/marauder
icon = 'icons/mob/clockwork_mobs.dmi'
/obj/screen/marauder/emerge
icon_state = "marauder_emerge"
name = "Emerge/Return"
desc = "Emerge or Return."
/obj/screen/marauder/emerge/Click()
if(istype(usr, /mob/living/simple_animal/hostile/clockwork/marauder))
var/mob/living/simple_animal/hostile/clockwork/marauder/M = usr
if(M.is_in_host())
M.try_emerge()
else
M.return_to_host()

View File

@@ -172,6 +172,27 @@
spread = 30 //should be 40 for XCOM memes, but since its adminspawn only, might as well make it useable
recoil = 1
///toy memes///
/obj/item/ammo_box/magazine/toy/x9
name = "foam force X9 magazine"
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "toy9magazine"
max_ammo = 30
multiple_sprites = 2
/obj/item/gun/ballistic/automatic/x9/toy
name = "donksoft X9"
desc = "An old but reliable assault rifle made for combat against unknown enemies. Appears to be hastily converted. Ages 8 and up."
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "toy9"
can_suppress = 0
needs_permit = 0
mag_type = /obj/item/ammo_box/magazine/toy/x9
casing_ejector = 0
spread = 45 //MAXIMUM XCOM MEMES (actually that'd be 90 spread)
////////XCOM2 Magpistol/////////
//////projectiles//////
@@ -213,7 +234,7 @@
//////magazines/////
/obj/item/ammo_box/magazine/mmags
/obj/item/ammo_box/magazine/mmag/small
name = "magpistol magazine (non-lethal disabler)"
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "nlmagmag"
@@ -223,7 +244,7 @@
max_ammo = 7
multiple_sprites = 2
/obj/item/ammo_box/magazine/mmags/lethal
/obj/item/ammo_box/magazine/mmag/small/lethal
name = "magpistol magazine (lethal)"
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "smallmagmag"
@@ -239,7 +260,7 @@
icon_state = "magpistol"
force = 10
fire_sound = 'sound/weapons/magpistol.ogg'
mag_type = /obj/item/ammo_box/magazine/mmags
mag_type = /obj/item/ammo_box/magazine/mmag/small
can_suppress = 0
casing_ejector = 0
fire_delay = 5
@@ -276,7 +297,7 @@
req_tech = list("combat" = 5, "magnets" = 6, "materials" = 5, "syndicate" = 3)
build_type = PROTOLATHE
materials = list(MAT_METAL = 4000, MAT_SILVER = 500)
build_path = /obj/item/ammo_box/magazine/mmags/lethal
build_path = /obj/item/ammo_box/magazine/mmag/small/lethal
category = list("Ammo")
/datum/design/mag_magpistol/nl
@@ -285,7 +306,7 @@
id = "mag_magpistol_nl"
req_tech = list("combat" = 5, "magnets" = 6, "materials" = 5)
materials = list(MAT_METAL = 3000, MAT_SILVER = 250, MAT_TITANIUM = 250)
build_path = /obj/item/ammo_box/magazine/mmags
build_path = /obj/item/ammo_box/magazine/mmag/small
//////toy memes/////
@@ -339,3 +360,412 @@
materials = list(MAT_METAL = 7500, MAT_GLASS = 1000)
build_path = /obj/item/gun/ballistic/shotgun/toy/mag
category = list("hacked", "Misc")
//////Magrifle//////
///projectiles///
/obj/item/projectile/bullet/magrifle
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "magjectile-large"
damage = 30
armour_penetration = 25
light_range = 3
light_color = LIGHT_COLOR_RED
/obj/item/projectile/bullet/nlmagrifle //non-lethal boolets
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "magjectile-large-nl"
damage = 5
knockdown = 30
stamina = 75
armour_penetration = 0
light_range = 3
light_color = LIGHT_COLOR_BLUE
///ammo casings///
/obj/item/ammo_casing/caseless/amagm
desc = "A large ferromagnetic slug intended to be launched out of a compatible weapon."
caliber = "magm"
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "mag-casing-live"
projectile_type = /obj/item/projectile/bullet/magrifle
/obj/item/ammo_casing/caseless/anlmagm
desc = "A large, specialized ferromagnetic slug designed with a less-than-lethal payload."
caliber = "magm"
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "mag-casing-live"
projectile_type = /obj/item/projectile/bullet/nlmagrifle
///magazines///
/obj/item/ammo_box/magazine/mmag/
name = "magrifle magazine (non-lethal disabler)"
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "mediummagmag"
origin_tech = "magnets=6"
ammo_type = /obj/item/ammo_casing/caseless/anlmagm
caliber = "magm"
max_ammo = 15
multiple_sprites = 2
/obj/item/ammo_box/magazine/mmag/lethal
name = "magrifle magazine (lethal)"
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "mediummagmag"
origin_tech = "combat=6"
ammo_type = /obj/item/ammo_casing/caseless/amagm
///the gun itself///
/obj/item/gun/ballistic/automatic/magrifle
name = "\improper Magnetic Rifle"
desc = "A simple upscalling of the technologies used in the magpistol, the magrifle is capable of firing slightly larger slugs in bursts. Compatible with the magpistol's slugs."
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "magrifle"
item_state = "arg"
slot_flags = 0
origin_tech = "combat=6;engineering=6;magnets=6"
mag_type = /obj/item/ammo_box/magazine/mmag
fire_sound = 'sound/weapons/magrifle.ogg'
can_suppress = 0
burst_size = 3
fire_delay = 2
spread = 15
recoil = 1
casing_ejector = 0
///research///
/obj/item/gun/ballistic/automatic/magrifle/nopin
pin = null
/datum/design/magrifle
name = "Magrifle"
desc = "An upscaled Magpistol in rifle form."
id = "magrifle"
req_tech = list("combat" = 7, "magnets" = 7, "powerstorage" = 7)
build_type = PROTOLATHE
materials = list(MAT_METAL = 10000, MAT_GLASS = 2000, MAT_URANIUM = 2000, MAT_TITANIUM = 10000, MAT_SILVER = 4000, MAT_GOLD = 2000)
build_path = /obj/item/gun/ballistic/automatic/magrifle/nopin
category = list("Weapons")
/datum/design/mag_magrifle
name = "Magrifle Magazine (Lethal)"
desc = "A 15 round magazine for the Magrifle."
id = "mag_magrifle"
req_tech = list("combat" = 7, "magnets" = 7, "materials" = 5, "syndicate" = 4)
build_type = PROTOLATHE
materials = list(MAT_METAL = 8000, MAT_SILVER = 1000)
build_path = /obj/item/ammo_box/magazine/mmag/lethal
category = list("Ammo")
/datum/design/mag_magrifle/nl
name = "Magrifle Magazine (Non-Lethal)"
desc = "A 15 round non-lethal magazine for the Magrifle."
id = "mag_magrifle_nl"
req_tech = list("combat" = 7, "magnets" = 7, "materials" = 5)
materials = list(MAT_METAL = 6000, MAT_SILVER = 500, MAT_TITANIUM = 500)
build_path = /obj/item/ammo_box/magazine/mmag
//////Hyper-Burst Rifle//////
///projectiles///
/obj/item/projectile/bullet/mags/hyper
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "magjectile"
damage = 10
armour_penetration = 10
stamina = 10
forcedodge = TRUE
range = 6
light_range = 1
light_color = LIGHT_COLOR_RED
/obj/item/projectile/bullet/mags/hyper/inferno
icon_state = "magjectile-large"
stamina = 0
forcedodge = FALSE
range = 25
light_range = 4
/obj/item/projectile/bullet/mags/hyper/inferno/on_hit(atom/target, blocked = FALSE)
..()
explosion(target, -1, 1, 2, 4, 5)
return 1
///ammo casings///
/obj/item/ammo_casing/caseless/ahyper
desc = "A large block of speciallized ferromagnetic material designed to be fired out of the experimental Hyper-Burst Rifle."
caliber = "hypermag"
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "hyper-casing-live"
projectile_type = /obj/item/projectile/bullet/mags/hyper
pellets = 12
variance = 40
/obj/item/ammo_casing/caseless/ahyper/inferno
projectile_type = /obj/item/projectile/bullet/mags/hyper/inferno
pellets = 1
variance = 0
///magazines///
/obj/item/ammo_box/magazine/mhyper
name = "hyper-burst rifle magazine"
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "hypermag-4"
ammo_type = /obj/item/ammo_casing/caseless/ahyper
caliber = "hypermag"
desc = "A magazine for the Hyper-Burst Rifle. Loaded with a special slug that fragments into 12 smaller shards which can absolutely puncture anything, but has rather short effective range."
max_ammo = 4
/obj/item/ammo_box/magazine/mhyper/update_icon()
..()
icon_state = "hypermag-[ammo_count() ? "4" : "0"]"
/obj/item/ammo_box/magazine/mhyper/inferno
name = "hyper-burst rifle magazine (inferno)"
ammo_type = /obj/item/ammo_casing/caseless/ahyper/inferno
desc = "A magazine for the Hyper-Burst Rifle. Loaded with a special slug that violently reacts with whatever surface it strikes, generating a massive amount of heat and light."
///gun itself///
/obj/item/gun/ballistic/automatic/hyperburst
name = "\improper Hyper-Burst Rifle"
desc = "An extremely beefed up version of a stolen Nanotrasen weapon prototype, this 'rifle' is more like a cannon, with an extremely large bore barrel capable of generating several smaller magnetic 'barrels' to simultaneously launch multiple projectiles at once."
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "hyperburst"
item_state = "arg"
slot_flags = 0
origin_tech = "combat=6;engineering=6;magnets=6;syndicate=6"
mag_type = /obj/item/ammo_box/magazine/mhyper
fire_sound = 'sound/weapons/magburst.ogg'
can_suppress = 0
burst_size = 1
fire_delay = 40
recoil = 2
casing_ejector = 0
weapon_weight = WEAPON_HEAVY
/obj/item/gun/ballistic/automatic/hyperburst/update_icon()
..()
icon_state = "hyperburst[magazine ? "-[get_ammo()]" : ""][chambered ? "" : "-e"]"
/* made redundant by reskinnable stetchkins
//////Stealth Pistol//////
/obj/item/gun/ballistic/automatic/pistol/stealth
name = "stealth pistol"
desc = "A unique bullpup pistol with a compact frame. Has an integrated surpressor."
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "stealthpistol"
w_class = WEIGHT_CLASS_SMALL
origin_tech = "combat=3;materials=3;syndicate=4"
mag_type = /obj/item/ammo_box/magazine/m10mm
can_suppress = 0
fire_sound = 'sound/weapons/gunshot_silenced.ogg'
suppressed = 1
burst_size = 1
/obj/item/gun/ballistic/automatic/pistol/stealth/update_icon()
..()
if(magazine)
cut_overlays()
add_overlay("stealthpistol-magazine")
else
cut_overlays()
icon_state = "[initial(icon_state)][chambered ? "" : "-e"]"
*/
//////10mm soporific bullets//////
obj/item/projectile/bullet/c10mm/soporific
name ="10mm soporific bullet"
armour_penetration = 0
nodamage = TRUE
dismemberment = 0
knockdown = 0
/obj/item/projectile/bullet/c10mm/soporific/on_hit(atom/target, blocked = FALSE)
if((blocked != 100) && isliving(target))
var/mob/living/L = target
L.blur_eyes(6)
if(L.staminaloss >= 40)
L.Sleeping(250)
else
L.adjustStaminaLoss(58)
return 1
/obj/item/ammo_casing/c10mm/soporific
name = ".10mm soporific bullet casing"
desc = "A 10mm soporific bullet casing."
projectile_type = /obj/item/projectile/bullet/c10mm/soporific
/obj/item/ammo_box/magazine/m10mm/soporific
name = "pistol magazine (10mm soporific)"
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "9x19pS"
desc = "A gun magazine. Loaded with rounds which inject the target with a variety of illegal substances to induce sleep in the target."
ammo_type = /obj/item/ammo_casing/c10mm/soporific
/obj/item/ammo_box/c10mm/soporific
name = "ammo box (10mm soporific)"
ammo_type = /obj/item/ammo_casing/c10mm/soporific
max_ammo = 24
//////Flechette Launcher//////
///projectiles///
/obj/item/projectile/bullet/cflechetteap //shreds armor
name = "flechette (armor piercing)"
damage = 8
armour_penetration = 80
/obj/item/projectile/bullet/cflechettes //shreds flesh and forces bleeding
name = "flechette (serrated)"
damage = 8
dismemberment = 10
armour_penetration = -80
/obj/item/projectile/bullet/cflechettes/on_hit(atom/target, blocked = FALSE)
if((blocked != 100) && iscarbon(target))
var/mob/living/carbon/C = target
C.bleed(10)
return ..()
///ammo casings (CASELESS AMMO CASINGS WOOOOOOOO)///
/obj/item/ammo_casing/caseless/flechetteap
name = "flechette (armor piercing)"
desc = "A flechette made with a tungsten alloy."
projectile_type = /obj/item/projectile/bullet/cflechetteap
caliber = "flechette"
throwforce = 1
throw_speed = 3
/obj/item/ammo_casing/caseless/flechettes
name = "flechette (serrated)"
desc = "A serrated flechette made of a special alloy intended to deform drastically upon penetration of human flesh."
projectile_type = /obj/item/projectile/bullet/cflechettes
caliber = "flechette"
throwforce = 2
throw_speed = 3
embed_chance = 75
///magazine///
/obj/item/ammo_box/magazine/flechette
name = "flechette magazine (armor piercing)"
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "flechettemag"
origin_tech = "combat=5;syndicate=1"
ammo_type = /obj/item/ammo_casing/caseless/flechetteap
caliber = "flechette"
max_ammo = 40
multiple_sprites = 2
/obj/item/ammo_box/magazine/flechette/s
name = "flechette magazine (serrated)"
ammo_type = /obj/item/ammo_casing/caseless/flechettes
///the gun itself///
/obj/item/gun/ballistic/automatic/flechette
name = "\improper CX Flechette Launcher"
desc = "A flechette launching machine pistol with an unconventional bullpup frame."
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "flechettegun"
item_state = "gun"
w_class = WEIGHT_CLASS_NORMAL
slot_flags = 0
/obj/item/device/firing_pin/implant/pindicate
origin_tech = "combat=6;materials=2;syndicate=5"
mag_type = /obj/item/ammo_box/magazine/flechette/
fire_sound = 'sound/weapons/gunshot_smg.ogg'
can_suppress = 0
burst_size = 5
fire_delay = 1
casing_ejector = 0
spread = 20
/obj/item/gun/ballistic/automatic/flechette/update_icon()
..()
if(magazine)
cut_overlays()
add_overlay("flechettegun-magazine")
else
cut_overlays()
icon_state = "[initial(icon_state)][chambered ? "" : "-e"]"
///unique variant///
/obj/item/projectile/bullet/cflechetteshredder
name = "flechette (shredder)"
damage = 5
dismemberment = 40
/obj/item/ammo_casing/caseless/flechetteshredder
name = "flechette (shredder)"
desc = "A serrated flechette made of a special alloy that forms a monofilament edge."
projectile_type = /obj/item/projectile/bullet/cflechettes
/obj/item/ammo_box/magazine/flechette/shredder
name = "flechette magazine (shredder)"
icon_state = "shreddermag"
ammo_type = /obj/item/ammo_casing/caseless/flechetteshredder
/obj/item/gun/ballistic/automatic/flechette/shredder
name = "\improper CX Shredder"
desc = "A flechette launching machine pistol made of ultra-light CFRP optimized for firing serrated monofillament flechettes."
w_class = WEIGHT_CLASS_SMALL
mag_type = /obj/item/ammo_box/magazine/flechette/shredder
spread = 30
/obj/item/gun/ballistic/automatic/flechette/shredder/update_icon()
..()
if(magazine)
cut_overlays()
add_overlay("shreddergun-magazine")
else
cut_overlays()
icon_state = "[initial(icon_state)][chambered ? "" : "-e"]"
//////modular pistol////// (reskinnable stetchkins)
/obj/item/gun/ballistic/automatic/pistol/modular
name = "modular pistol"
desc = "A small, easily concealable 10mm handgun. Has a threaded barrel for suppressors."
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "cde"
can_unsuppress = TRUE
unique_rename = TRUE
unique_reskin = list("Default" = "cde",
"NT-99" = "n99",
"Stealth" = "stealthpistol",
"HKVP-78" = "vp78",
"Luger" = "p08b",
"Mk.58" = "secguncomp",
"PX4 Storm" = "px4"
)
/obj/item/gun/ballistic/automatic/pistol/modular/update_icon()
..()
if(current_skin)
icon_state = "[unique_reskin[current_skin]][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]"
else
icon_state = "[initial(icon_state)][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]"
if(magazine && suppressed)
cut_overlays()
add_overlay("[unique_reskin[current_skin]]-magazine-sup") //Yes, this means the default iconstate can't have a magazine overlay
else if (magazine)
cut_overlays()
add_overlay("[unique_reskin[current_skin]]-magazine")
else
cut_overlays()

View File

@@ -0,0 +1,198 @@
#undef CURRENT_RESIDENT_FILE
#define LIST_MODE_NUM 0
#define LIST_MODE_TEXT 1
#define LIST_MODE_FLAG 2
/datum/config_entry
var/name //read-only, this is determined by the last portion of the derived entry type
var/value
var/default //read-only, just set value directly
var/resident_file //the file which this belongs to, must be set
var/modified = FALSE //set to TRUE if the default has been overridden by a config entry
var/protection = NONE
var/abstract_type = /datum/config_entry //do not instantiate if type matches this
var/dupes_allowed = FALSE
/datum/config_entry/New()
if(!resident_file)
CRASH("Config entry [type] has no resident_file set")
if(type == abstract_type)
CRASH("Abstract config entry [type] instatiated!")
name = lowertext(type2top(type))
if(islist(value))
var/list/L = value
default = L.Copy()
else
default = value
/datum/config_entry/Destroy()
config.RemoveEntry(src)
return ..()
/datum/config_entry/can_vv_get(var_name)
. = ..()
if(var_name == "value" || var_name == "default")
. &= !(protection & CONFIG_ENTRY_HIDDEN)
/datum/config_entry/vv_edit_var(var_name, var_value)
var/static/list/banned_edits = list("name", "default", "resident_file", "protection", "abstract_type", "modified", "dupes_allowed")
if(var_name == "value")
if(protection & CONFIG_ENTRY_LOCKED)
return FALSE
. = ValidateAndSet("[var_value]")
if(.)
var_edited = TRUE
return
if(var_name in banned_edits)
return FALSE
return ..()
/datum/config_entry/proc/VASProcCallGuard(str_val)
. = !(IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "ValidateAndSet" && GLOB.LastAdminCalledTargetRef == "\ref[src]")
if(!.)
log_admin_private("Config set of [type] to [str_val] attempted by [key_name(usr)]")
/datum/config_entry/proc/ValidateAndSet(str_val)
VASProcCallGuard(str_val)
CRASH("Invalid config entry type!")
/datum/config_entry/proc/ValidateKeyedList(str_val, list_mode, splitter)
str_val = trim(str_val)
var/key_pos = findtext(str_val, splitter)
var/key_name = null
var/key_value = null
if(key_pos || list_mode == LIST_MODE_FLAG)
key_name = lowertext(copytext(str_val, 1, key_pos))
key_value = copytext(str_val, key_pos + 1)
var/temp
var/continue_check
switch(list_mode)
if(LIST_MODE_FLAG)
temp = TRUE
continue_check = TRUE
if(LIST_MODE_NUM)
temp = text2num(key_value)
continue_check = !isnull(temp)
if(LIST_MODE_TEXT)
temp = key_value
continue_check = temp
if(continue_check && ValidateKeyName(key_name))
value[key_name] = temp
return TRUE
return FALSE
/datum/config_entry/proc/ValidateKeyName(key_name)
return TRUE
/datum/config_entry/string
value = ""
abstract_type = /datum/config_entry/string
var/auto_trim = TRUE
/datum/config_entry/string/vv_edit_var(var_name, var_value)
return var_name != "auto_trim" && ..()
/datum/config_entry/string/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
value = auto_trim ? trim(str_val) : str_val
return TRUE
/datum/config_entry/number
value = 0
abstract_type = /datum/config_entry/number
var/integer = TRUE
var/max_val = INFINITY
var/min_val = -INFINITY
/datum/config_entry/number/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
var/temp = text2num(trim(str_val))
if(!isnull(temp))
value = Clamp(integer ? round(temp) : temp, min_val, max_val)
if(value != temp && !var_edited)
log_config("Changing [name] from [temp] to [value]!")
return TRUE
return FALSE
/datum/config_entry/number/vv_edit_var(var_name, var_value)
var/static/list/banned_edits = list("max_val", "min_val", "integer")
return !(var_name in banned_edits) && ..()
/datum/config_entry/flag
value = FALSE
abstract_type = /datum/config_entry/flag
/datum/config_entry/flag/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
value = text2num(trim(str_val)) != 0
return TRUE
/datum/config_entry/number_list
abstract_type = /datum/config_entry/number_list
value = list()
/datum/config_entry/number_list/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
str_val = trim(str_val)
var/list/new_list = list()
var/list/values = splittext(str_val," ")
for(var/I in values)
var/temp = text2num(I)
if(isnull(temp))
return FALSE
new_list += temp
if(!new_list.len)
return FALSE
value = new_list
return TRUE
/datum/config_entry/keyed_flag_list
abstract_type = /datum/config_entry/keyed_flag_list
value = list()
dupes_allowed = TRUE
/datum/config_entry/keyed_flag_list/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
return ValidateKeyedList(str_val, LIST_MODE_FLAG, " ")
/datum/config_entry/keyed_number_list
abstract_type = /datum/config_entry/keyed_number_list
value = list()
dupes_allowed = TRUE
var/splitter = " "
/datum/config_entry/keyed_number_list/vv_edit_var(var_name, var_value)
return var_name != "splitter" && ..()
/datum/config_entry/keyed_number_list/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
return ValidateKeyedList(str_val, LIST_MODE_NUM, splitter)
/datum/config_entry/keyed_string_list
abstract_type = /datum/config_entry/keyed_string_list
value = list()
dupes_allowed = TRUE
var/splitter = " "
/datum/config_entry/keyed_string_list/vv_edit_var(var_name, var_value)
return var_name != "splitter" && ..()
/datum/config_entry/keyed_string_list/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
return ValidateKeyedList(str_val, LIST_MODE_TEXT, splitter)
#undef LIST_MODE_NUM
#undef LIST_MODE_TEXT
#undef LIST_MODE_FLAG

View File

@@ -0,0 +1,287 @@
GLOBAL_VAR_INIT(config_dir, "config/")
GLOBAL_PROTECT(config_dir)
/datum/controller/configuration
name = "Configuration"
var/hiding_entries_by_type = TRUE //Set for readability, admins can set this to FALSE if they want to debug it
var/list/entries
var/list/entries_by_type
var/list/maplist
var/datum/map_config/defaultmap
var/list/modes // allowed modes
var/list/gamemode_cache
var/list/votable_modes // votable modes
var/list/mode_names
var/list/mode_reports
var/list/mode_false_report_weight
/datum/controller/configuration/New()
config = src
var/list/config_files = InitEntries()
LoadModes()
for(var/I in config_files)
LoadEntries(I)
if(Get(/datum/config_entry/flag/maprotation))
loadmaplist(CONFIG_MAPS_FILE)
/datum/controller/configuration/Destroy()
entries_by_type.Cut()
QDEL_LIST_ASSOC_VAL(entries)
QDEL_LIST_ASSOC_VAL(maplist)
QDEL_NULL(defaultmap)
config = null
return ..()
/datum/controller/configuration/proc/InitEntries()
var/list/_entries = list()
entries = _entries
var/list/_entries_by_type = list()
entries_by_type = _entries_by_type
. = list()
for(var/I in typesof(/datum/config_entry)) //typesof is faster in this case
var/datum/config_entry/E = I
if(initial(E.abstract_type) == I)
continue
E = new I
_entries_by_type[I] = E
var/esname = E.name
var/datum/config_entry/test = _entries[esname]
if(test)
log_config("Error: [test.type] has the same name as [E.type]: [esname]! Not initializing [E.type]!")
qdel(E)
continue
_entries[esname] = E
.[E.resident_file] = TRUE
/datum/controller/configuration/proc/RemoveEntry(datum/config_entry/CE)
entries -= CE.name
entries_by_type -= CE.type
/datum/controller/configuration/proc/LoadEntries(filename)
log_config("Loading config file [filename]...")
var/list/lines = world.file2list("[GLOB.config_dir][filename]")
var/list/_entries = entries
for(var/L in lines)
if(!L)
continue
if(copytext(L, 1, 2) == "#")
continue
var/pos = findtext(L, " ")
var/entry = null
var/value = null
if(pos)
entry = lowertext(copytext(L, 1, pos))
value = copytext(L, pos + 1)
else
entry = lowertext(L)
if(!entry)
continue
var/datum/config_entry/E = _entries[entry]
if(!E)
log_config("Unknown setting in configuration: '[entry]'")
continue
if(filename != E.resident_file)
log_config("Found [entry] in [filename] when it should have been in [E.resident_file]! Ignoring.")
continue
var/validated = E.ValidateAndSet(value)
if(!validated)
log_config("Failed to validate setting \"[value]\" for [entry]")
else if(E.modified && !E.dupes_allowed)
log_config("Duplicate setting for [entry] ([value]) detected! Using latest.")
if(validated)
E.modified = TRUE
/datum/controller/configuration/can_vv_get(var_name)
return (var_name != "entries_by_type" || !hiding_entries_by_type) && ..()
/datum/controller/configuration/vv_edit_var(var_name, var_value)
return !(var_name in list("entries_by_type", "entries")) && ..()
/datum/controller/configuration/stat_entry()
if(!statclick)
statclick = new/obj/effect/statclick/debug(null, "Edit", src)
stat("[name]:", statclick)
/datum/controller/configuration/proc/Get(entry_type)
if(IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "Get" && GLOB.LastAdminCalledTargetRef == "\ref[src]")
log_admin_private("Config access of [entry_type] attempted by [key_name(usr)]")
return
var/datum/config_entry/E = entry_type
var/entry_is_abstract = initial(E.abstract_type) == entry_type
if(entry_is_abstract)
CRASH("Tried to retrieve an abstract config_entry: [entry_type]")
E = entries_by_type[entry_type]
if(!E)
CRASH("Missing config entry for [entry_type]!")
return E.value
/datum/controller/configuration/proc/Set(entry_type, new_val)
if(IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "Set" && GLOB.LastAdminCalledTargetRef == "\ref[src]")
log_admin_private("Config rewrite of [entry_type] to [new_val] attempted by [key_name(usr)]")
return
var/datum/config_entry/E = entry_type
var/entry_is_abstract = initial(E.abstract_type) == entry_type
if(entry_is_abstract)
CRASH("Tried to retrieve an abstract config_entry: [entry_type]")
E = entries_by_type[entry_type]
if(!E)
CRASH("Missing config entry for [entry_type]!")
return E.ValidateAndSet(new_val)
/datum/controller/configuration/proc/LoadModes()
gamemode_cache = typecacheof(/datum/game_mode, TRUE)
modes = list()
mode_names = list()
mode_reports = list()
mode_false_report_weight = list()
votable_modes = list()
var/list/probabilities = Get(/datum/config_entry/keyed_number_list/probability)
for(var/T in gamemode_cache)
// I wish I didn't have to instance the game modes in order to look up
// their information, but it is the only way (at least that I know of).
var/datum/game_mode/M = new T()
if(M.config_tag)
if(!(M.config_tag in modes)) // ensure each mode is added only once
modes += M.config_tag
mode_names[M.config_tag] = M.name
probabilities[M.config_tag] = M.probability
mode_reports[M.config_tag] = M.generate_report()
mode_false_report_weight[M.config_tag] = M.false_report_weight
if(M.votable)
votable_modes += M.config_tag
qdel(M)
votable_modes += "secret"
/datum/controller/configuration/proc/loadmaplist(filename)
filename = "[GLOB.config_dir][filename]"
var/list/Lines = world.file2list(filename)
var/datum/map_config/currentmap = null
for(var/t in Lines)
if(!t)
continue
t = trim(t)
if(length(t) == 0)
continue
else if(copytext(t, 1, 2) == "#")
continue
var/pos = findtext(t, " ")
var/command = null
var/data = null
if(pos)
command = lowertext(copytext(t, 1, pos))
data = copytext(t, pos + 1)
else
command = lowertext(t)
if(!command)
continue
if (!currentmap && command != "map")
continue
switch (command)
if ("map")
currentmap = new ("_maps/[data].json")
if(currentmap.defaulted)
log_config("Failed to load map config for [data]!")
if ("minplayers","minplayer")
currentmap.config_min_users = text2num(data)
if ("maxplayers","maxplayer")
currentmap.config_max_users = text2num(data)
if ("weight","voteweight")
currentmap.voteweight = text2num(data)
if ("default","defaultmap")
defaultmap = currentmap
if ("endmap")
LAZYINITLIST(maplist)
maplist[currentmap.map_name] = currentmap
currentmap = null
if ("disabled")
currentmap = null
else
WRITE_FILE(GLOB.config_error_log, "Unknown command in map vote config: '[command]'")
/datum/controller/configuration/proc/pick_mode(mode_name)
// I wish I didn't have to instance the game modes in order to look up
// their information, but it is the only way (at least that I know of).
// ^ This guy didn't try hard enough
for(var/T in gamemode_cache)
var/datum/game_mode/M = T
var/ct = initial(M.config_tag)
if(ct && ct == mode_name)
return new T
return new /datum/game_mode/extended()
/datum/controller/configuration/proc/get_runnable_modes()
var/list/datum/game_mode/runnable_modes = new
var/list/probabilities = Get(/datum/config_entry/keyed_number_list/probability)
var/list/min_pop = Get(/datum/config_entry/keyed_number_list/min_pop)
var/list/max_pop = Get(/datum/config_entry/keyed_number_list/max_pop)
var/list/repeated_mode_adjust = Get(/datum/config_entry/number_list/repeated_mode_adjust)
for(var/T in gamemode_cache)
var/datum/game_mode/M = new T()
if(!(M.config_tag in modes))
qdel(M)
continue
if(probabilities[M.config_tag]<=0)
qdel(M)
continue
if(min_pop[M.config_tag])
M.required_players = min_pop[M.config_tag]
if(max_pop[M.config_tag])
M.maximum_players = max_pop[M.config_tag]
if(M.can_start())
var/final_weight = probabilities[M.config_tag]
if(SSpersistence.saved_modes.len == 3 && repeated_mode_adjust.len == 3)
var/recent_round = min(SSpersistence.saved_modes.Find(M.config_tag),3)
var/adjustment = 0
while(recent_round)
adjustment += repeated_mode_adjust[recent_round]
recent_round = SSpersistence.saved_modes.Find(M.config_tag,recent_round+1,0)
final_weight *= ((100-adjustment)/100)
runnable_modes[M] = final_weight
return runnable_modes
/datum/controller/configuration/proc/get_runnable_midround_modes(crew)
var/list/datum/game_mode/runnable_modes = new
var/list/probabilities = Get(/datum/config_entry/keyed_number_list/probability)
var/list/min_pop = Get(/datum/config_entry/keyed_number_list/min_pop)
var/list/max_pop = Get(/datum/config_entry/keyed_number_list/max_pop)
for(var/T in (gamemode_cache - SSticker.mode.type))
var/datum/game_mode/M = new T()
if(!(M.config_tag in modes))
qdel(M)
continue
if(probabilities[M.config_tag]<=0)
qdel(M)
continue
if(min_pop[M.config_tag])
M.required_players = min_pop[M.config_tag]
if(max_pop[M.config_tag])
M.maximum_players = max_pop[M.config_tag]
if(M.required_players <= crew)
if(M.maximum_players >= 0 && M.maximum_players < crew)
continue
runnable_modes[M] = probabilities[M.config_tag]
return runnable_modes

View File

@@ -69,7 +69,7 @@
can_fire = 0
flags |= SS_NO_FIRE
Master.subsystems -= src
return ..()
//Queue it to run.
// (we loop thru a linked list until we get to the end or find the right point)

View File

@@ -322,7 +322,7 @@ SUBSYSTEM_DEF(air)
EG.dismantle()
CHECK_TICK
var/msg = "HEY! LISTEN! [(world.timeofday - timer)/10] Seconds were wasted processing [starting_ats] turf(s) (connected to [ending_ats] other turfs) with atmos differences at round start."
var/msg = "HEY! LISTEN! [DisplayTimeText(world.timeofday - timer)] were wasted processing [starting_ats] turf(s) (connected to [ending_ats] other turfs) with atmos differences at round start."
to_chat(world, "<span class='boldannounce'>[msg]</span>")
warning(msg)

View File

@@ -50,7 +50,7 @@ SUBSYSTEM_DEF(mapping)
var/space_zlevels = list()
for(var/i in ZLEVEL_SPACEMIN to ZLEVEL_SPACEMAX)
switch(i)
if(ZLEVEL_MINING, ZLEVEL_LAVALAND, ZLEVEL_EMPTY_SPACE, ZLEVEL_TRANSIT)
if(ZLEVEL_MINING, ZLEVEL_LAVALAND, ZLEVEL_EMPTY_SPACE, ZLEVEL_TRANSIT, ZLEVEL_CITYOFCOGS)
continue
else
space_zlevels += i

View File

@@ -29,7 +29,7 @@ SUBSYSTEM_DEF(server_maint)
var/cmob = C.mob
if(!(isobserver(cmob) || (isdead(cmob) && C.holder)))
log_access("AFK: [key_name(C)]")
to_chat(C, "<span class='danger'>You have been inactive for more than [config.afk_period / 600] minutes and have been disconnected.</span>")
to_chat(C, "<span class='danger'>You have been inactive for more than [DisplayTimeText(config.afk_period)] and have been disconnected.</span>")
qdel(C)
if (!(!C || world.time - C.connection_time < PING_BUFFER_TIME || C.inactivity >= (wait-1)))

View File

@@ -178,7 +178,7 @@ SUBSYSTEM_DEF(shuttle)
emergency = backup_shuttle
if(world.time - SSticker.round_start_time < config.shuttle_refuel_delay)
to_chat(user, "The emergency shuttle is refueling. Please wait another [abs(round(((world.time - SSticker.round_start_time) - config.shuttle_refuel_delay)/600))] minutes before trying again.")
to_chat(user, "The emergency shuttle is refueling. Please wait [DisplayTimeText((world.time - SSticker.round_start_time) - config.shuttle_refuel_delay)] before trying again.")
return
switch(emergency.mode)

View File

@@ -11,6 +11,7 @@ SUBSYSTEM_DEF(squeak)
/datum/controller/subsystem/squeak/Initialize(timeofday)
trigger_migration(config.mice_roundstart)
return ..()
/datum/controller/subsystem/squeak/proc/trigger_migration(num_mice=10)
if(!num_mice)

View File

@@ -57,6 +57,9 @@ SUBSYSTEM_DEF(throwing)
var/pure_diagonal
var/diagonal_error
var/datum/callback/callback
var/paused = FALSE
var/delayed_time = 0
var/last_move = 0
/datum/thrownthing/proc/tick()
var/atom/movable/AM = thrownthing
@@ -64,14 +67,20 @@ SUBSYSTEM_DEF(throwing)
finalize()
return
if(paused)
delayed_time += world.time - last_move
return
if (dist_travelled && hitcheck()) //to catch sneaky things moving on our tile while we slept
finalize()
return
var/atom/step
last_move = world.time
//calculate how many tiles to move, making up for any missed ticks.
var/tilestomove = Ceiling(min(((((world.time+world.tick_lag) - start_time) * speed) - (dist_travelled ? dist_travelled : -1)), speed*MAX_TICKS_TO_MAKE_UP) * (world.tick_lag * SSthrowing.wait))
var/tilestomove = Ceiling(min(((((world.time+world.tick_lag) - start_time + delayed_time) * speed) - (dist_travelled ? dist_travelled : -1)), speed*MAX_TICKS_TO_MAKE_UP) * (world.tick_lag * SSthrowing.wait))
while (tilestomove-- > 0)
if ((dist_travelled >= maxrange || AM.loc == target_turf) && AM.has_gravity(AM.loc))
finalize()

View File

@@ -51,14 +51,14 @@ SUBSYSTEM_DEF(ticker)
var/queue_delay = 0
var/list/queued_players = list() //used for join queues when the server exceeds the hard population cap
var/obj/screen/cinematic = null //used for station explosion cinematic
var/maprotatechecked = 0
var/news_report
var/late_join_disabled
var/roundend_check_paused = FALSE
var/round_start_time = 0
var/list/round_start_events
var/mode_result = "undefined"
@@ -137,7 +137,7 @@ SUBSYSTEM_DEF(ticker)
scripture_states = scripture_unlock_alert(scripture_states)
SSshuttle.autoEnd()
if(!mode.explosion_in_progress && mode.check_finished(force_ending) || force_ending)
if(!roundend_check_paused && mode.check_finished(force_ending) || force_ending)
current_state = GAME_STATE_FINISHED
toggle_ooc(TRUE) // Turn it on
toggle_dooc(TRUE)
@@ -273,144 +273,7 @@ SUBSYSTEM_DEF(ticker)
qdel(bomb)
if(epi)
explosion(epi, 0, 256, 512, 0, TRUE, TRUE, 0, TRUE)
//Plus it provides an easy way to make cinematics for other events. Just use this as a template
/datum/controller/subsystem/ticker/proc/station_explosion_cinematic(station_missed=0, override = null, atom/bomb = null)
if( cinematic )
return //already a cinematic in progress!
for (var/datum/html_interface/hi in GLOB.html_interfaces)
hi.closeAll()
SStgui.close_all_uis()
//Turn off the shuttles, there's no escape now
if(!station_missed && bomb)
SSshuttle.registerHostileEnvironment(src)
SSshuttle.lockdown = TRUE
//initialise our cinematic screen object
cinematic = new /obj/screen{icon='icons/effects/station_explosion.dmi';icon_state="station_intact";layer=21;mouse_opacity = MOUSE_OPACITY_TRANSPARENT;screen_loc="1,0";}(src)
for(var/mob/M in GLOB.mob_list)
M.notransform = TRUE //stop everything moving
if(M.client)
M.client.screen += cinematic //show every client the cinematic
var/actually_blew_up = TRUE
//Now animate the cinematic
switch(station_missed)
if(NUKE_NEAR_MISS) //nuke was nearby but (mostly) missed
if(mode && !override )
override = mode.name
switch( override )
if("nuclear emergency") //Nuke wasn't on station when it blew up
flick("intro_nuke",cinematic)
sleep(35)
SEND_SOUND(world, sound('sound/effects/explosion_distant.ogg'))
station_explosion_detonation(bomb)
flick("station_intact_fade_red",cinematic)
cinematic.icon_state = "summary_nukefail"
if("cult")
cinematic.icon_state = null
flick("intro_cult",cinematic)
sleep(25)
SEND_SOUND(world, sound('sound/magic/enter_blood.ogg'))
sleep(28)
SEND_SOUND(world, sound('sound/machines/terminal_off.ogg'))
sleep(20)
flick("station_corrupted",cinematic)
SEND_SOUND(world, sound('sound/effects/ghost.ogg'))
actually_blew_up = FALSE
if("fake") //The round isn't over, we're just freaking people out for fun
flick("intro_nuke",cinematic)
sleep(35)
SEND_SOUND(world, sound('sound/items/bikehorn.ogg'))
flick("summary_selfdes",cinematic)
actually_blew_up = FALSE
else
flick("intro_nuke",cinematic)
sleep(35)
SEND_SOUND(world, sound('sound/effects/explosion_distant.ogg'))
station_explosion_detonation(bomb)
if(NUKE_MISS_STATION || NUKE_SYNDICATE_BASE) //nuke was nowhere nearby //TODO: a really distant explosion animation
sleep(50)
SEND_SOUND(world, sound('sound/effects/explosion_distant.ogg'))
station_explosion_detonation(bomb)
actually_blew_up = station_missed == NUKE_SYNDICATE_BASE //don't kill everyone on station if it detonated off station
else //station was destroyed
if( mode && !override )
override = mode.name
switch( override )
if("nuclear emergency") //Nuke Ops successfully bombed the station
flick("intro_nuke",cinematic)
sleep(35)
flick("station_explode_fade_red",cinematic)
SEND_SOUND(world, sound('sound/effects/explosion_distant.ogg'))
station_explosion_detonation(bomb)
cinematic.icon_state = "summary_nukewin"
if("AI malfunction") //Malf (screen,explosion,summary)
flick("intro_malf",cinematic)
sleep(76)
flick("station_explode_fade_red",cinematic)
SEND_SOUND(world, sound('sound/effects/explosion_distant.ogg'))
station_explosion_detonation(bomb) //TODO: If we ever decide to actually detonate the vault bomb
cinematic.icon_state = "summary_malf"
if("blob") //Station nuked (nuke,explosion,summary)
flick("intro_nuke",cinematic)
sleep(35)
flick("station_explode_fade_red",cinematic)
SEND_SOUND(world, sound('sound/effects/explosion_distant.ogg'))
station_explosion_detonation(bomb) //TODO: no idea what this case could be
cinematic.icon_state = "summary_selfdes"
if("cult") //Station nuked (nuke,explosion,summary)
flick("intro_nuke",cinematic)
sleep(35)
flick("station_explode_fade_red",cinematic)
SEND_SOUND(world, sound('sound/effects/explosion_distant.ogg'))
station_explosion_detonation(bomb) //TODO: no idea what this case could be
cinematic.icon_state = "summary_cult"
if("no_core") //Nuke failed to detonate as it had no core
flick("intro_nuke",cinematic)
sleep(35)
flick("station_intact",cinematic)
SEND_SOUND(world, sound('sound/ambience/signal.ogg'))
addtimer(CALLBACK(src, .proc/finish_cinematic, null, FALSE), 100)
return //Faster exit, since nothing happened
else //Station nuked (nuke,explosion,summary)
flick("intro_nuke",cinematic)
sleep(35)
flick("station_explode_fade_red", cinematic)
SEND_SOUND(world, sound('sound/effects/explosion_distant.ogg'))
station_explosion_detonation(bomb)
cinematic.icon_state = "summary_selfdes"
//If its actually the end of the round, wait for it to end.
//Otherwise if its a verb it will continue on afterwards.
var/bombloc = null
if(actually_blew_up)
if(bomb && bomb.loc)
bombloc = bomb.z
else if(!station_missed)
bombloc = ZLEVEL_STATION_PRIMARY
if(mode)
mode.explosion_in_progress = 0
to_chat(world, "<B>The station was destoyed by the nuclear blast!</B>")
mode.station_was_nuked = (station_missed<2) //station_missed==1 is a draw. the station becomes irradiated and needs to be evacuated.
addtimer(CALLBACK(src, .proc/finish_cinematic, bombloc, actually_blew_up), 300)
/datum/controller/subsystem/ticker/proc/finish_cinematic(killz, actually_blew_up)
if(cinematic)
qdel(cinematic) //end the cinematic
cinematic = null
for(var/mob/M in GLOB.mob_list)
M.notransform = FALSE
if(actually_blew_up && !isnull(killz) && M.stat != DEAD && M.z == killz)
M.gib()
/datum/controller/subsystem/ticker/proc/create_characters()
for(var/mob/dead/new_player/player in GLOB.player_list)
if(player.ready == PLAYER_READY_TO_PLAY && player.mind)
@@ -505,7 +368,7 @@ SUBSYSTEM_DEF(ticker)
end_state.count()
var/station_integrity = min(PERCENT(GLOB.start_state.score(end_state)), 100)
to_chat(world, "<BR>[GLOB.TAB]Shift Duration: <B>[round(world.time / 36000)]:[add_zero("[world.time / 600 % 60]", 2)]:[world.time / 100 % 6][world.time / 100 % 10]</B>")
to_chat(world, "<BR>[GLOB.TAB]Shift Duration: <B>[DisplayTimeText(world.time - SSticker.round_start_time)]</B>")
to_chat(world, "<BR>[GLOB.TAB]Station Integrity: <B>[mode.station_was_nuked ? "<font color='red'>Destroyed</font>" : "[station_integrity]%"]</B>")
if(mode.station_was_nuked)
SSticker.news_report = STATION_DESTROYED_NUKE
@@ -711,13 +574,11 @@ SUBSYSTEM_DEF(ticker)
queue_delay = SSticker.queue_delay
queued_players = SSticker.queued_players
cinematic = SSticker.cinematic
maprotatechecked = SSticker.maprotatechecked
round_start_time = SSticker.round_start_time
queue_delay = SSticker.queue_delay
queued_players = SSticker.queued_players
cinematic = SSticker.cinematic
maprotatechecked = SSticker.maprotatechecked
modevoted = SSticker.modevoted

View File

@@ -169,7 +169,7 @@ SUBSYSTEM_DEF(vote)
admin = TRUE
if(next_allowed_time > world.time && !admin)
to_chat(usr, "<span class='warning'>A vote was initiated recently, you must wait roughly [(next_allowed_time-world.time)/10] seconds before a new vote can be started!</span>")
to_chat(usr, "<span class='warning'>A vote was initiated recently, you must wait [DisplayTimeText(next_allowed_time-world.time)] before a new vote can be started!</span>")
return 0
reset()
@@ -198,7 +198,7 @@ SUBSYSTEM_DEF(vote)
if(mode == "custom")
text += "\n[question]"
log_vote(text)
to_chat(world, "\n<font color='purple'><b>[text]</b>\nType <b>vote</b> or click <a href='?src=\ref[src]'>here</a> to place your votes.\nYou have [config.vote_period/10] seconds to vote.</font>")
to_chat(world, "\n<font color='purple'><b>[text]</b>\nType <b>vote</b> or click <a href='?src=\ref[src]'>here</a> to place your votes.\nYou have [DisplayTimeText(config.vote_period)] to vote.</font>")
time_remaining = round(config.vote_period/10)
for(var/c in GLOB.clients)
var/client/C = c

View File

@@ -485,7 +485,7 @@
/datum/action/spell_action/New(Target)
..()
var/obj/effect/proc_holder/spell/S = target
var/obj/effect/proc_holder/S = target
S.action = src
name = S.name
desc = S.desc
@@ -495,36 +495,40 @@
button.name = name
/datum/action/spell_action/Destroy()
var/obj/effect/proc_holder/spell/S = target
var/obj/effect/proc_holder/S = target
S.action = null
return ..()
/datum/action/spell_action/Trigger()
if(!..())
return 0
return FALSE
if(target)
var/obj/effect/proc_holder/spell = target
spell.Click()
return 1
var/obj/effect/proc_holder/S = target
S.Click()
return TRUE
/datum/action/spell_action/IsAvailable()
if(!target)
return 0
var/obj/effect/proc_holder/spell/spell = target
if(owner)
return spell.can_cast(owner)
return 0
return FALSE
return TRUE
/datum/action/spell_action/spell/IsAvailable()
if(!target)
return FALSE
var/obj/effect/proc_holder/spell/S = target
if(owner)
return S.can_cast(owner)
return FALSE
/datum/action/spell_action/alien
/datum/action/spell_action/alien/IsAvailable()
if(!target)
return 0
return FALSE
var/obj/effect/proc_holder/alien/ab = target
if(owner)
return ab.cost_check(ab.check_turf,owner,1)
return 0
return FALSE

View File

@@ -0,0 +1,60 @@
/datum/antagonist/abductor
name = "Abductor"
var/datum/objective_team/abductor_team/team
var/sub_role
var/outfit
var/landmark_type
var/greet_text
/datum/antagonist/abductor/agent
sub_role = "Agent"
outfit = /datum/outfit/abductor/agent
landmark_type = /obj/effect/landmark/abductor/agent
greet_text = "Use your stealth technology and equipment to incapacitate humans for your scientist to retrieve."
/datum/antagonist/abductor/scientist
sub_role = "Scientist"
outfit = /datum/outfit/abductor/scientist
landmark_type = /obj/effect/landmark/abductor/scientist
greet_text = "Use your stealth technology and equipment to incapacitate humans for your scientist to retrieve."
/datum/antagonist/abductor/New(datum/mind/new_owner, datum/objective_team/abductor_team/T)
team = T
return ..()
/datum/antagonist/abductor/on_gain()
SSticker.mode.abductors += owner
owner.special_role = "[name] [sub_role]"
owner.objectives += team.objectives
finalize_abductor()
return ..()
/datum/antagonist/abductor/on_removal()
SSticker.mode.abductors -= owner
team.members -= owner
owner.objectives -= team.objectives
if(owner.current)
to_chat(owner.current,"<span class='userdanger'>You are no longer the [owner.special_role]!</span>")
owner.special_role = null
return ..()
/datum/antagonist/abductor/greet()
to_chat(owner.current, "<span class='notice'>You are the [owner.special_role]!</span>")
to_chat(owner.current, "<span class='notice'>With the help of your teammate, kidnap and experiment on station crew members!</span>")
to_chat(owner.current, "<span class='notice'>[greet_text]</span>")
owner.announce_objectives()
/datum/antagonist/abductor/proc/finalize_abductor()
//Equip
var/mob/living/carbon/human/H = owner.current
H.set_species(/datum/species/abductor)
H.real_name = "[team.name] [sub_role]"
H.equipOutfit(outfit)
//Teleport to ship
for(var/obj/effect/landmark/abductor/LM in GLOB.landmarks_list)
if(istype(LM, landmark_type) && LM.team_number == team.team_number)
H.forceMove(LM.loc)
break
SSticker.mode.update_abductor_icons_added(owner)

View File

@@ -103,23 +103,22 @@
S.laws.associate(S)
S.update_icons()
S.show_laws()
hierophant_network.Grant(S)
hierophant_network.title = "Silicon"
hierophant_network.span_for_name = "nezbere"
hierophant_network.span_for_message = "brass"
else if(isbrain(current))
hierophant_network.Grant(current)
hierophant_network.title = "Vessel"
hierophant_network.span_for_name = "nezbere"
hierophant_network.span_for_message = "alloy"
else if(isclockmob(current))
hierophant_network.Grant(current)
hierophant_network.title = "Construct"
hierophant_network.span_for_name = "nezbere"
hierophant_network.span_for_message = "brass"
hierophant_network.Grant(current)
current.throw_alert("clockinfo", /obj/screen/alert/clockwork/infodump)
if(!GLOB.clockwork_gateway_activated)
current.throw_alert("scripturereq", /obj/screen/alert/clockwork/scripture_reqs)
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
if(G.active && ishuman(current))
current.add_overlay(mutable_appearance('icons/effects/genetics.dmi', "servitude", -MUTATIONS_LAYER))
/datum/antagonist/clockcult/remove_innate_effects(mob/living/mob_override)
var/mob/living/current = owner.current
@@ -129,9 +128,10 @@
current.faction -= "ratvar"
current.remove_language(/datum/language/ratvar)
current.clear_alert("clockinfo")
current.clear_alert("scripturereq")
for(var/datum/action/innate/function_call/F in owner.current.actions) //Removes any bound Ratvarian spears
qdel(F)
for(var/datum/action/innate/clockwork_arnaments/C in owner.current.actions) //Removes any bound clockwork armor
qdel(C)
for(var/datum/action/innate/call_weapon/W in owner.current.actions) //and weapons too
qdel(W)
if(issilicon(current))
var/mob/living/silicon/S = current
if(isAI(S))
@@ -149,6 +149,8 @@
R.module.rebuild_modules()
if(temp_owner)
temp_owner.update_action_buttons_icon() //because a few clockcult things are action buttons and we may be wearing/holding them, we need to update buttons
temp_owner.cut_overlays()
temp_owner.regenerate_icons()
/datum/antagonist/clockcult/on_removal()
SSticker.mode.servants_of_ratvar -= owner

View File

@@ -1,6 +1,5 @@
/datum/antagonist/ninja
name = "Ninja"
var/team
var/helping_station = 0
var/give_objectives = TRUE
@@ -19,36 +18,8 @@
..(new_owner)
helping_station = rand(0,1)
/datum/antagonist/ninja/proc/equip_space_ninja(mob/living/carbon/human/H = owner.current, safety=0)//Safety in case you need to unequip stuff for existing characters.
if(safety)
qdel(H.w_uniform)
qdel(H.wear_suit)
qdel(H.wear_mask)
qdel(H.head)
qdel(H.shoes)
qdel(H.gloves)
var/obj/item/clothing/suit/space/space_ninja/theSuit = new(H)
var/obj/item/dash/energy_katana/EK = new(H)
theSuit.energyKatana = EK
H.equip_to_slot_or_del(new /obj/item/device/radio/headset(H), slot_ears)
H.equip_to_slot_or_del(new /obj/item/clothing/under/color/black(H), slot_w_uniform)
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/space_ninja(H), slot_shoes)
H.equip_to_slot_or_del(theSuit, slot_wear_suit)
H.equip_to_slot_or_del(new /obj/item/clothing/gloves/space_ninja(H), slot_gloves)
H.equip_to_slot_or_del(new /obj/item/clothing/head/helmet/space/space_ninja(H), slot_head)
H.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/space_ninja(H), slot_wear_mask)
H.equip_to_slot_or_del(new /obj/item/clothing/glasses/night(H), slot_glasses)
H.equip_to_slot_or_del(EK, slot_belt)
H.equip_to_slot_or_del(new /obj/item/grenade/plastic/x4(H), slot_l_store)
H.equip_to_slot_or_del(new /obj/item/tank/internals/emergency_oxygen(H), slot_s_store)
H.equip_to_slot_or_del(new /obj/item/tank/jetpack/carbondioxide(H), slot_back)
theSuit.randomize_param()
var/obj/item/implant/explosive/E = new/obj/item/implant/explosive(H)
E.implant(H)
return 1
/datum/antagonist/ninja/proc/equip_space_ninja(mob/living/carbon/human/H = owner.current)
return H.equipOutfit(/datum/outfit/ninja)
/datum/antagonist/ninja/proc/addMemories()
owner.store_memory("I am an elite mercenary assassin of the mighty Spider Clan. A <font color='red'><B>SPACE NINJA</B></font>!")

223
code/datums/cinematic.dm Normal file
View File

@@ -0,0 +1,223 @@
GLOBAL_LIST_EMPTY(cinematics)
// Use to play cinematics.
// Watcher can be world,mob, or a list of mobs
// Blocks until sequence is done.
/proc/Cinematic(id,watcher,datum/callback/special_callback)
var/datum/cinematic/playing
for(var/V in subtypesof(/datum/cinematic))
var/datum/cinematic/C = V
if(initial(C.id) == id)
playing = new V()
break
if(!playing)
CRASH("Cinematic type not found")
if(special_callback)
playing.special_callback = special_callback
if(watcher == world)
playing.is_global = TRUE
watcher = GLOB.mob_list
playing.play(watcher)
/obj/screen/cinematic
icon = 'icons/effects/station_explosion.dmi'
icon_state = "station_intact"
layer = 21
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
screen_loc = "1,1"
/datum/cinematic
var/id = CINEMATIC_DEFAULT
var/list/watching = list() //List of clients watching this
var/list/locked = list() //Who had notransform set during the cinematic
var/is_global = FALSE //Global cinematics will override mob-specific ones
var/obj/screen/cinematic/screen
var/datum/callback/special_callback //For special effects synced with animation (explosions after the countdown etc)
var/cleanup_time = 300 //How long for the final screen to remain
/datum/cinematic/New()
GLOB.cinematics += src
screen = new(src)
/datum/cinematic/Destroy()
GLOB.cinematics -= src
QDEL_NULL(screen)
for(var/mob/M in locked)
M.notransform = FALSE
return ..()
/datum/cinematic/proc/play(watchers)
//Check if you can actually play it (stop mob cinematics for global ones) and create screen objects
for(var/A in GLOB.cinematics)
var/datum/cinematic/C = A
if(C == src)
continue
if(C.is_global || !is_global)
return //Can't play two global or local cinematics at the same time
//Close all open windows if global
if(is_global)
for (var/datum/html_interface/hi in GLOB.html_interfaces)
hi.closeAll()
SStgui.close_all_uis()
for(var/mob/M in GLOB.mob_list)
if(M in watchers)
M.notransform = TRUE //Should this be done for non-global cinematics or even at all ?
locked += M
//Close watcher ui's
SStgui.close_user_uis(M)
if(M.client)
watching += M.client
M.client.screen += screen
else
if(is_global)
M.notransform = TRUE
locked += M
//Actually play it
content()
//Cleanup
sleep(cleanup_time)
qdel(src)
//Sound helper
/datum/cinematic/proc/cinematic_sound(s)
if(is_global)
SEND_SOUND(world,s)
else
for(var/C in watching)
SEND_SOUND(C,s)
//Fire up special callback for actual effects synchronized with animation (eg real nuke explosion happens midway)
/datum/cinematic/proc/special()
if(special_callback)
special_callback.Invoke()
//Actual cinematic goes in here
/datum/cinematic/proc/content()
sleep(50)
/datum/cinematic/nuke_win
id = CINEMATIC_NUKE_WIN
/datum/cinematic/nuke_win/content()
flick("intro_nuke",screen)
sleep(35)
flick("station_explode_fade_red",screen)
cinematic_sound(sound('sound/effects/explosion_distant.ogg'))
special()
screen.icon_state = "summary_nukewin"
/datum/cinematic/nuke_miss
id = CINEMATIC_NUKE_MISS
/datum/cinematic/nuke_miss/content()
flick("intro_nuke",screen)
sleep(35)
cinematic_sound(sound('sound/effects/explosion_distant.ogg'))
special()
flick("station_intact_fade_red",screen)
screen.icon_state = "summary_nukefail"
//Also used for blob
/datum/cinematic/nuke_selfdestruct
id = CINEMATIC_SELFDESTRUCT
/datum/cinematic/nuke_selfdestruct/content()
flick("intro_nuke",screen)
sleep(35)
flick("station_explode_fade_red", screen)
cinematic_sound(sound('sound/effects/explosion_distant.ogg'))
special()
screen.icon_state = "summary_selfdes"
/datum/cinematic/nuke_selfdestruct_miss
id = CINEMATIC_SELFDESTRUCT_MISS
/datum/cinematic/nuke_selfdestruct_miss/content()
flick("intro_nuke",screen)
sleep(35)
cinematic_sound(sound('sound/effects/explosion_distant.ogg'))
special()
screen.icon_state = "station_intact"
/datum/cinematic/malf
id = CINEMATIC_MALF
/datum/cinematic/malf/content()
flick("intro_malf",screen)
sleep(76)
flick("station_explode_fade_red",screen)
cinematic_sound(sound('sound/effects/explosion_distant.ogg'))
special()
screen.icon_state = "summary_malf"
/datum/cinematic/cult
id = CINEMATIC_CULT
/datum/cinematic/cult/content()
screen.icon_state = null
flick("intro_cult",screen)
sleep(25)
cinematic_sound(sound('sound/magic/enter_blood.ogg'))
sleep(28)
cinematic_sound(sound('sound/machines/terminal_off.ogg'))
sleep(20)
flick("station_corrupted",screen)
cinematic_sound(sound('sound/effects/ghost.ogg'))
sleep(70)
special()
/datum/cinematic/nuke_annihilation
id = CINEMATIC_ANNIHILATION
/datum/cinematic/nuke_annihilation/content()
flick("intro_nuke",screen)
sleep(35)
flick("station_explode_fade_red",screen)
cinematic_sound(sound('sound/effects/explosion_distant.ogg'))
special()
screen.icon_state = "summary_totala"
/datum/cinematic/fake
id = CINEMATIC_NUKE_FAKE
cleanup_time = 100
/datum/cinematic/fake/content()
flick("intro_nuke",screen)
sleep(35)
cinematic_sound(sound('sound/items/bikehorn.ogg'))
flick("summary_selfdes",screen) //???
special()
/datum/cinematic/no_core
id = CINEMATIC_NUKE_NO_CORE
cleanup_time = 100
/datum/cinematic/no_core/content()
flick("intro_nuke",screen)
sleep(35)
flick("station_intact",screen)
cinematic_sound(sound('sound/ambience/signal.ogg'))
sleep(100)
/datum/cinematic/nuke_far
id = CINEMATIC_NUKE_FAR
cleanup_time = 0
/datum/cinematic/nuke_far/content()
cinematic_sound(sound('sound/effects/explosion_distant.ogg'))
special()
/* Intended usage.
Nuke.Explosion()
-> Cinematic(NUKE_BOOM,world)
-> ActualExplosion()
-> Mode.OnExplosion()
Narsie()
-> Cinematic(CULT,world)
*/

View File

@@ -94,7 +94,8 @@ Stands have a lot of procs which mimic mob procs. Rather than inserting hooks fo
* Allows the component to react to ownership transfers
1. `/datum/component/proc/_RemoveNoSignal()` (private, final)
* Internal, clears the parent var and removes the component from the parents component list
1. `/datum/component/proc/RegisterSignal(signal(string), proc_ref(type), override(boolean))` (protected, final) (Consider removing for performance gainz)
1. `/datum/component/proc/RegisterSignal(signal(string/list of strings), proc_ref(type), override(boolean))` (protected, final) (Consider removing for performance gainz)
* If signal is a list it will be as if RegisterSignal was called for each of the entries with the same following arguments
* Makes a component listen for the specified `signal` on it's `parent` datum.
* When that signal is recieved `proc_ref` will be called on the component, along with associated arguments
* Example proc ref: `.proc/OnEvent`

View File

@@ -95,7 +95,7 @@
P.datum_components = null
parent = null
/datum/component/proc/RegisterSignal(sig_type, proc_on_self, override = FALSE)
/datum/component/proc/RegisterSignal(sig_type_or_types, proc_on_self, override = FALSE)
if(QDELETED(src))
return
var/list/procs = signal_procs
@@ -103,12 +103,14 @@
procs = list()
signal_procs = procs
if(!override)
. = procs[sig_type]
if(.)
stack_trace("[sig_type] overridden. Use override = TRUE to suppress this warning")
procs[sig_type] = CALLBACK(src, proc_on_self)
var/list/sig_types = islist(sig_type_or_types) ? sig_type_or_types : list(sig_type_or_types)
for(var/sig_type in sig_types)
if(!override)
. = procs[sig_type]
if(.)
stack_trace("[sig_type] overridden. Use override = TRUE to suppress this warning")
procs[sig_type] = CALLBACK(src, proc_on_self)
/datum/component/proc/InheritComponent(datum/component/C, i_am_original)
return

View File

@@ -6,10 +6,7 @@
/datum/component/slippery/Initialize(_intensity, _lube_flags = NONE)
intensity = max(_intensity, 0)
lube_flags = _lube_flags
if(ismovableatom(parent))
RegisterSignal(COMSIG_MOVABLE_CROSSED, .proc/Slip)
else
RegisterSignal(COMSIG_ATOM_ENTERED, .proc/Slip)
RegisterSignal(list(COMSIG_MOVABLE_CROSSED, COMSIG_ATOM_ENTERED), .proc/Slip)
/datum/component/slippery/proc/Slip(atom/movable/AM)
var/mob/victim = AM

View File

@@ -24,22 +24,9 @@
if(use_delay_override)
use_delay = use_delay_override
if(istype(parent, /atom))
RegisterSignal(COMSIG_ATOM_BLOB_ACT, .proc/play_squeak)
RegisterSignal(COMSIG_ATOM_HULK_ATTACK, .proc/play_squeak)
RegisterSignal(COMSIG_PARENT_ATTACKBY, .proc/play_squeak)
if(istype(parent, /atom/movable))
RegisterSignal(COMSIG_MOVABLE_CROSSED, .proc/play_squeak)
RegisterSignal(COMSIG_MOVABLE_COLLIDE, .proc/play_squeak)
RegisterSignal(COMSIG_MOVABLE_IMPACT, .proc/play_squeak)
if(istype(parent, /obj/item))
RegisterSignal(COMSIG_ITEM_ATTACK, .proc/play_squeak)
RegisterSignal(COMSIG_ITEM_ATTACK_SELF, .proc/use_squeak)
RegisterSignal(COMSIG_ITEM_ATTACK_OBJ, .proc/play_squeak)
if(istype(parent, /obj/item/clothing/shoes))
RegisterSignal(COMSIG_SHOES_STEP_ACTION, .proc/step_squeak)
else
RegisterSignal(COMSIG_ATOM_ENTERED, .proc/play_squeak)
RegisterSignal(list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_BLOB_ACT, COMSIG_ATOM_HULK_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_MOVABLE_CROSSED, COMSIG_MOVABLE_COLLIDE, COMSIG_MOVABLE_IMPACT, COMSIG_ITEM_ATTACK, COMSIG_ITEM_ATTACK_OBJ, COMSIG_ITEM_ATTACK_OBJ), .proc/play_squeak)
RegisterSignal(COMSIG_ITEM_ATTACK_SELF, .proc/use_squeak)
RegisterSignal(COMSIG_SHOES_STEP_ACTION, .proc/step_squeak)
/datum/component/squeak/proc/play_squeak()
if(prob(squeak_chance))

View File

@@ -1,103 +1,111 @@
#define FORWARD -1
#define BACKWARD 1
/datum/construction
var/list/steps
var/atom/holder
var/result
var/list/steps_desc
/datum/construction/New(atom)
..()
holder = atom
if(!holder) //don't want this without a holder
qdel(src)
set_desc(steps.len)
return
/datum/construction/proc/next_step()
steps.len--
if(!steps.len)
spawn_result()
else
set_desc(steps.len)
return
/datum/construction/proc/action(atom/used_atom,mob/user)
return
/datum/construction/proc/check_step(atom/used_atom,mob/user) //check last step only
var/valid_step = is_right_key(used_atom)
if(valid_step)
if(custom_action(valid_step, used_atom, user))
next_step()
return 1
return 0
/datum/construction/proc/is_right_key(atom/used_atom) // returns current step num if used_atom is of the right type.
var/list/L = steps[steps.len]
if(istype(used_atom, L["key"]))
return steps.len
return 0
/datum/construction/proc/custom_action(step, used_atom, user)
return 1
/datum/construction/proc/check_all_steps(atom/used_atom,mob/user) //check all steps, remove matching one.
for(var/i=1;i<=steps.len;i++)
var/list/L = steps[i];
if(istype(used_atom, L["key"]))
if(custom_action(i, used_atom, user))
steps[i]=null;//stupid byond list from list removal...
listclearnulls(steps);
if(!steps.len)
spawn_result()
return 1
return 0
/datum/construction/proc/spawn_result()
if(result)
new result(get_turf(holder))
qdel(holder)
return
/datum/construction/proc/set_desc(index as num)
var/list/step = steps[index]
holder.desc = step["desc"]
return
/datum/construction/reversible
var/index
/datum/construction/reversible/New(atom)
..()
index = steps.len
return
/datum/construction/reversible/proc/update_index(diff as num)
index+=diff
if(index==0)
spawn_result()
else
set_desc(index)
return
/datum/construction/reversible/is_right_key(atom/used_atom) // returns index step
var/list/L = steps[index]
if(istype(used_atom, L["key"]))
return FORWARD //to the first step -> forward
else if(L["backkey"] && istype(used_atom, L["backkey"]))
return BACKWARD //to the last step -> backwards
return 0
/datum/construction/reversible/check_step(atom/used_atom,mob/user)
var/diff = is_right_key(used_atom)
if(diff)
if(custom_action(index, diff, used_atom, user))
update_index(diff)
return 1
return 0
/datum/construction/reversible/custom_action(index, diff, used_atom, user)
return 1
#define FORWARD -1
#define BACKWARD 1
/datum/construction
var/list/steps
var/atom/holder
var/result
var/list/steps_desc
/datum/construction/New(atom)
..()
holder = atom
if(!holder) //don't want this without a holder
qdel(src)
set_desc(steps.len)
return
/datum/construction/proc/next_step()
steps.len--
if(!steps.len)
spawn_result()
else
set_desc(steps.len)
return
/datum/construction/proc/action(atom/used_atom,mob/user)
return
/datum/construction/proc/check_step(atom/used_atom,mob/user) //check last step only
var/valid_step = is_right_key(used_atom)
if(valid_step)
if(custom_action(valid_step, used_atom, user))
next_step()
return 1
return 0
/datum/construction/proc/is_right_key(atom/used_atom) // returns current step num if used_atom is of the right type.
var/list/L = steps[steps.len]
if(istype(used_atom, L["key"]))
return steps.len
return 0
/datum/construction/proc/custom_action(step, used_atom, user)
return 1
/datum/construction/proc/check_all_steps(atom/used_atom,mob/user) //check all steps, remove matching one.
for(var/i=1;i<=steps.len;i++)
var/list/L = steps[i];
if(istype(used_atom, L["key"]))
if(custom_action(i, used_atom, user))
steps[i]=null;//stupid byond list from list removal...
listclearnulls(steps);
if(!steps.len)
spawn_result()
return 1
return 0
/datum/construction/proc/spawn_result()
if(result)
new result(get_turf(holder))
qdel(holder)
return
/datum/construction/proc/spawn_mecha_result()
if(result)
var/obj/mecha/m = new result(get_turf(holder))
var/obj/item/oldcell = locate (/obj/item/stock_parts/cell) in m
QDEL_NULL(oldcell)
m.CheckParts(holder.contents)
QDEL_NULL(holder)
/datum/construction/proc/set_desc(index as num)
var/list/step = steps[index]
holder.desc = step["desc"]
return
/datum/construction/reversible
var/index
/datum/construction/reversible/New(atom)
..()
index = steps.len
return
/datum/construction/reversible/proc/update_index(diff as num)
index+=diff
if(index==0)
spawn_result()
else
set_desc(index)
return
/datum/construction/reversible/is_right_key(atom/used_atom) // returns index step
var/list/L = steps[index]
if(istype(used_atom, L["key"]))
return FORWARD //to the first step -> forward
else if(L["backkey"] && istype(used_atom, L["backkey"]))
return BACKWARD //to the last step -> backwards
return 0
/datum/construction/reversible/check_step(atom/used_atom,mob/user)
var/diff = is_right_key(used_atom)
if(diff)
if(custom_action(index, diff, used_atom, user))
update_index(diff)
return 1
return 0
/datum/construction/reversible/custom_action(index, diff, used_atom, user)
return 1

View File

@@ -12,6 +12,7 @@
var/minetype = "lavaland"
var/list/transition_config = list(CENTCOM = SELFLOOPING,
CITY_OF_COGS = SELFLOOPING,
MAIN_STATION = CROSSLINKED,
EMPTY_AREA_1 = CROSSLINKED,
EMPTY_AREA_2 = CROSSLINKED,
@@ -119,6 +120,8 @@
return MAIN_STATION
if("CENTCOM")
return CENTCOM
if("CITY_OF_COGS")
return CITY_OF_COGS
if("MINING")
return MINING
if("EMPTY_AREA_1")

View File

@@ -11,7 +11,7 @@
/datum/action/neck_chop/Trigger()
if(owner.incapacitated())
to_chat(owner, "<span class='warning'>You can't use Krav Maga while you're incapacitated.</span>")
to_chat(owner, "<span class='warning'>You can't use [name] while you're incapacitated.</span>")
return
var/mob/living/carbon/human/H = owner
if (H.mind.martial_art.streak == "neck_chop")
@@ -28,7 +28,7 @@
/datum/action/leg_sweep/Trigger()
if(owner.incapacitated())
to_chat(owner, "<span class='warning'>You can't use Krav Maga while you're incapacitated.</span>")
to_chat(owner, "<span class='warning'>You can't use [name] while you're incapacitated.</span>")
return
var/mob/living/carbon/human/H = owner
if (H.mind.martial_art.streak == "leg_sweep")
@@ -45,7 +45,7 @@
/datum/action/lung_punch/Trigger()
if(owner.incapacitated())
to_chat(owner, "<span class='warning'>You can't use Krav Maga while you're incapacitated.</span>")
to_chat(owner, "<span class='warning'>You can't use [name] while you're incapacitated.</span>")
return
var/mob/living/carbon/human/H = owner
if (H.mind.martial_art.streak == "quick_choke")
@@ -57,14 +57,14 @@
/datum/martial_art/krav_maga/teach(mob/living/carbon/human/H,make_temporary=0)
if(..())
to_chat(H, "<span class = 'userdanger'>You know the arts of Krav Maga!</span>")
to_chat(H, "<span class = 'userdanger'>You know the arts of [name]!</span>")
to_chat(H, "<span class = 'danger'>Place your cursor over a move at the top of the screen to see what it does.</span>")
neckchop.Grant(H)
legsweep.Grant(H)
lungpunch.Grant(H)
/datum/martial_art/krav_maga/on_remove(mob/living/carbon/human/H)
to_chat(H, "<span class = 'userdanger'>You suddenly forget the arts of Krav Maga...</span>")
to_chat(H, "<span class = 'userdanger'>You suddenly forget the arts of [name]...</span>")
neckchop.Remove(H)
legsweep.Remove(H)
lungpunch.Remove(H)
@@ -140,7 +140,7 @@
playsound(get_turf(D), 'sound/effects/hit_punch.ogg', 50, 1, -1)
D.visible_message("<span class='danger'>[A] [picked_hit_type] [D]!</span>", \
"<span class='userdanger'>[A] [picked_hit_type] you!</span>")
add_logs(A, D, "[picked_hit_type] with Krav Maga")
add_logs(A, D, "[picked_hit_type] with [name]")
return 1
/datum/martial_art/krav_maga/disarm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)

View File

@@ -590,8 +590,6 @@
if(src in SSticker.mode.abductors)
text += "<b>Abductor</b> | <a href='?src=\ref[src];abductor=clear'>human</a>"
text += " | <a href='?src=\ref[src];common=undress'>undress</a> | <a href='?src=\ref[src];abductor=equip'>equip</a>"
else
text += "<a href='?src=\ref[src];abductor=abductor'>abductor</a> | <b>human</b>"
if(current && current.client && (ROLE_ABDUCTOR in current.client.prefs.be_special))
text += " | Enabled in Prefs"
@@ -674,7 +672,7 @@
text = "<i><b>[text]</b></i>: "
if(is_servant_of_ratvar(current))
text += "not mindshielded | <a href='?src=\ref[src];clockcult=clear'>employee</a> | <b>SERVANT</b>"
text += "<br><a href='?src=\ref[src];clockcult=slab'>Give slab</a>"
text += "<br><a href='?src=\ref[src];clockcult=slab'>Equip</a>"
else if(is_eligible_servant(current))
text += "not mindshielded | <b>EMPLOYEE</b> | <a href='?src=\ref[src];clockcult=servant'>servant</a>"
else
@@ -1037,9 +1035,9 @@
log_admin("[key_name(usr)] has made [current] into a servant of Ratvar.")
if("slab")
if(!SSticker.mode.equip_servant(current))
to_chat(usr, "<span class='warning'>Failed to outfit [current] with a slab!</span>")
to_chat(usr, "<span class='warning'>Failed to outfit [current]!</span>")
else
to_chat(usr, "<span class='notice'>Successfully gave [current] a clockwork slab!</span>")
to_chat(usr, "<span class='notice'>Successfully gave [current] servant equipment!</span>")
else if (href_list["wizard"])
switch(href_list["wizard"])
@@ -1244,13 +1242,6 @@
if("clear")
to_chat(usr, "Not implemented yet. Sorry!")
//SSticker.mode.update_abductor_icons_removed(src)
if("abductor")
if(!ishuman(current))
to_chat(usr, "<span class='warning'>This only works on humans!</span>")
return
make_Abductor()
log_admin("[key_name(usr)] turned [current] into abductor.")
SSticker.mode.update_abductor_icons_added(src)
if("equip")
if(!ishuman(current))
to_chat(usr, "<span class='warning'>This only works on humans!</span>")
@@ -1496,52 +1487,7 @@
take_uplink()
var/fail = 0
fail |= !SSticker.mode.equip_revolutionary(current)
/datum/mind/proc/make_Abductor()
var/role = alert("Abductor Role ?","Role","Agent","Scientist")
var/team = input("Abductor Team ?","Team ?") in list(1,2,3,4)
var/teleport = alert("Teleport to ship ?","Teleport","Yes","No")
if(!role || !team || !teleport)
return
if(!ishuman(current))
return
SSticker.mode.abductors |= src
var/datum/objective/experiment/O = new
O.owner = src
objectives += O
var/mob/living/carbon/human/H = current
H.set_species(/datum/species/abductor)
var/datum/species/abductor/S = H.dna.species
if(role == "Scientist")
S.scientist = TRUE
S.team = team
var/list/obj/effect/landmark/abductor/agent_landmarks = new
var/list/obj/effect/landmark/abductor/scientist_landmarks = new
agent_landmarks.len = 4
scientist_landmarks.len = 4
for(var/obj/effect/landmark/abductor/A in GLOB.landmarks_list)
if(istype(A, /obj/effect/landmark/abductor/agent))
agent_landmarks[text2num(A.team)] = A
else if(istype(A, /obj/effect/landmark/abductor/scientist))
scientist_landmarks[text2num(A.team)] = A
var/obj/effect/landmark/L
if(teleport=="Yes")
switch(role)
if("Agent")
L = agent_landmarks[team]
if("Scientist")
L = scientist_landmarks[team]
H.forceMove(L.loc)
/datum/mind/proc/AddSpell(obj/effect/proc_holder/spell/S)
spell_list += S
S.action.Grant(current)

View File

@@ -102,10 +102,10 @@
if(implants)
for(var/implant_type in implants)
var/obj/item/implant/I = new implant_type(H)
I.implant(H, null, silent=TRUE)
I.implant(H, null, TRUE)
H.update_body()
return 1
return TRUE
/datum/outfit/proc/apply_fingerprints(mob/living/carbon/human/H)
if(!istype(H))

View File

@@ -148,21 +148,6 @@
if(owner.m_intent == MOVE_INTENT_WALK)
owner.toggle_move_intent()
/datum/status_effect/geis_tracker
id = "geis_tracker"
duration = -1
alert_type = null
var/obj/structure/destructible/clockwork/geis_binding/binding
/datum/status_effect/geis_tracker/on_creation(mob/living/new_owner, obj/structure/destructible/clockwork/geis_binding/new_binding)
. = ..()
if(.)
binding = new_binding
/datum/status_effect/geis_tracker/tick()
if(QDELETED(binding))
qdel(src)
/datum/status_effect/maniamotor
id = "maniamotor"
duration = -1
@@ -200,7 +185,7 @@
return
if(!motor.active) //it being off makes it fall off much faster
if(!is_servant && !warned_turnoff)
if(motor.total_accessable_power() > motor.mania_cost)
if(can_access_clockwork_power(motor, motor.mania_cost))
to_chat(owner, "<span class='sevtug[span_part]'>\"[text2ratvar(pick(turnoff_messages))]\"</span>")
else
var/pickedmessage = pick(powerloss_messages)
@@ -461,3 +446,40 @@
/obj/effect/temp_visual/curse/Initialize()
. = ..()
deltimer(timerid)
//Kindle: Used by servants of Ratvar. 10-second knockdown, reduced by 1 second per 5 damage taken while the effect is active.
/datum/status_effect/kindle
id = "kindle"
status_type = STATUS_EFFECT_UNIQUE
tick_interval = 5
duration = 100
alert_type = /obj/screen/alert/status_effect/kindle
var/old_health
/datum/status_effect/kindle/tick()
owner.Knockdown(15)
if(iscarbon(owner))
var/mob/living/carbon/C = owner
C.silent = max(2, C.silent)
C.stuttering = max(5, C.stuttering)
if(!old_health)
old_health = owner.health
var/health_difference = old_health - owner.health
if(!health_difference)
return
owner.visible_message("<span class='warning'>The light in [owner]'s eyes dims as they're harmed!</span>", \
"<span class='boldannounce'>The dazzling lights dim as you're harmed!</span>")
health_difference *= 2 //so 10 health difference translates to 20 deciseconds of stun reduction
duration -= health_difference
old_health = owner.health
/datum/status_effect/kindle/on_remove()
owner.visible_message("<span class='warning'>The light in [owner]'s eyes fades!</span>", \
"<span class='boldannounce'>You snap out of your daze!</span>")
/obj/screen/alert/status_effect/kindle
name = "Dazzling Lights"
desc = "Blinding light dances in your vision, stunning and silencing you. <i>Any damage taken will shorten the light's effects!</i>"
icon_state = "kindle"
alerttooltipstyle = "clockcult"

View File

@@ -143,3 +143,18 @@
/area/ctf/flag_room2
name = "Flag Room B"
// REEBE
/area/reebe
name = "Reebe"
icon_state = "yellow"
requires_power = FALSE
has_gravity = TRUE
noteleport = TRUE
hidden = TRUE
/area/reebe/city_of_cogs
name = "City of Cogs"
icon_state = "purple"
hidden = FALSE

View File

@@ -225,6 +225,17 @@
A.CollidedWith(src)
/atom/movable/proc/forceMove(atom/destination)
. = FALSE
if(destination)
. = doMove(destination)
else
CRASH("No valid destination passed into forceMove")
/atom/movable/proc/moveToNullspace()
return doMove(null)
/atom/movable/proc/doMove(atom/destination)
. = FALSE
if(destination)
if(pulledby)
pulledby.stop_pulling()
@@ -251,8 +262,17 @@
AM.Crossed(src, oldloc)
Moved(oldloc, 0)
return 1
return 0
. = TRUE
//If no destination, move the atom into nullspace (don't do this unless you know what you're doing)
else
. = TRUE
var/atom/oldloc = loc
var/area/old_area = get_area(oldloc)
oldloc.Exited(src, null)
if(old_area)
old_area.Exited(src, null)
loc = null
/mob/living/forceMove(atom/destination)
stop_pulling()
@@ -261,9 +281,10 @@
if(has_buckled_mobs())
unbuckle_all_mobs(force=1)
. = ..()
if(client)
reset_perspective(destination)
update_canmove() //if the mob was asleep inside a container and then got forceMoved out we need to make them fall.
if(.)
if(client)
reset_perspective(destination)
update_canmove() //if the mob was asleep inside a container and then got forceMoved out we need to make them fall.
/mob/living/brain/forceMove(atom/destination)
if(container)

View File

@@ -203,9 +203,9 @@
var/mob/living/silicon/robot/R
switch(borg_to_spawn)
if("Medical")
R = new /mob/living/silicon/robot/syndicate/medical(T)
R = new /mob/living/silicon/robot/modules/syndicate/medical(T)
else
R = new /mob/living/silicon/robot/syndicate(T) //Assault borg by default
R = new /mob/living/silicon/robot/modules/syndicate(T) //Assault borg by default
var/brainfirstname = pick(GLOB.first_names_male)
if(prob(50))

View File

@@ -57,7 +57,7 @@
if(!placed)
if(manualplace_min_time && world.time >= manualplace_min_time)
to_chat(src, "<b><span class='big'><font color=\"#EE4000\">You may now place your blob core.</font></span></b>")
to_chat(src, "<span class='big'><font color=\"#EE4000\">You will automatically place your blob core in [round((autoplace_max_time - world.time)/600, 0.5)] minutes.</font></span>")
to_chat(src, "<span class='big'><font color=\"#EE4000\">You will automatically place your blob core in [DisplayTimeText(autoplace_max_time - world.time)].</font></span>")
manualplace_min_time = 0
if(autoplace_max_time && world.time >= autoplace_max_time)
place_blob_core(base_point_rate, 1)

View File

@@ -365,5 +365,5 @@
to_chat(src, "<b>Shortcuts:</b> Click = Expand Blob <b>|</b> Middle Mouse Click = Rally Spores <b>|</b> Ctrl Click = Create Shield Blob <b>|</b> Alt Click = Remove Blob")
to_chat(src, "Attempting to talk will send a message to all other overminds, allowing you to coordinate with them.")
if(!placed && autoplace_max_time <= world.time)
to_chat(src, "<span class='big'><font color=\"#EE4000\">You will automatically place your blob core in [round((autoplace_max_time - world.time)/600, 0.5)] minutes.</font></span>")
to_chat(src, "<span class='big'><font color=\"#EE4000\">You will automatically place your blob core in [DisplayTimeText(autoplace_max_time - world.time)].</font></span>")
to_chat(src, "<span class='big'><font color=\"#EE4000\">You [manualplace_min_time ? "will be able to":"can"] manually place your blob core by pressing the Place Blob Core button in the bottom right corner of the screen.</font></span>")

View File

@@ -8,12 +8,9 @@ Imprisoned within a massive construct known as the Celestial Derelict - or Reebe
Ratvar, unable to act in the mortal plane, seeks to return and forms covenants with mortals in order to bolster his influence.
Due to his mechanical nature, Ratvar is also capable of influencing silicon-based lifeforms, unlike Nar-Sie, who can only influence natural life.
This is a team-based gamemode, and the team's objective is shared by all cultists. It can include summoning Ratvar, escaping on the shuttle, or converting the AI and its cyborgs.
This is a team-based gamemode, and the team's objective is shared by all cultists. Their goal is to defend an object called the Ark on a separate z-level.
The clockwork version of an arcane tome is the clockwork slab.
While it can perform certain actions, it consumes a resource called components.
Components, which are fallen fragments of Ratvar's body as he rusts in Reebe, are powerful and have various effects.
Game-wise, clockwork slabs will generate components over time, with more powerful components being slower.
This file's folder contains:
clock_cult.dm: Core gamemode files.
@@ -36,8 +33,8 @@ Credit where due:
2. SkowronX from /vg/ for MANY of the assets
3. FuryMcFlurry from /vg/ for many of the assets
4. PJB3005 from /vg/ for the failed continuation PR
5. Xhuis from /tg/ for coding the basic gamemode as it is today
6. ChangelingRain from /tg/ for maintaining the gamemode for months after its release
5. Xhuis from /tg/ for coding the first iteration of the mode, and the new, reworked version
6. ChangelingRain from /tg/ for maintaining the gamemode for months after its release prior to its rework
*/
@@ -88,7 +85,7 @@ Credit where due:
/datum/game_mode
var/list/servants_of_ratvar = list() //The Enlightened servants of Ratvar
var/clockwork_explanation = "Construct a Gateway to the Celestial Derelict and free Ratvar." //The description of the current objective
var/clockwork_explanation = "Defend the Ark of the Clockwork Justiciar and free Ratvar." //The description of the current objective
/datum/game_mode/clockwork_cult
name = "clockwork cult"
@@ -103,30 +100,34 @@ Credit where due:
restricted_jobs = list("Chaplain", "Captain")
announce_span = "brass"
announce_text = "Servants of Ratvar are trying to summon the Justiciar!\n\
<span class='brass'>Servants</span>: Take over the station and summon Ratvar.\n\
<span class='brass'>Servants</span>: Construct defenses to protect the Ark. Sabotage the station!\n\
<span class='notice'>Crew</span>: Stop the servants before they can summon the Clockwork Justiciar."
var/servants_to_serve = list()
var/roundstart_player_count
var/ark_time //In minutes, how long the Ark waits before activation; this is equal to 30 + (number of players / 5) (max 40 mins.)
/datum/game_mode/clockwork_cult/pre_setup()
if(config.protect_roles_from_antagonist)
restricted_jobs += protected_jobs
if(config.protect_assistant_from_antagonist)
restricted_jobs += "Assistant"
var/starter_servants = 3 //Guaranteed three servants
var/starter_servants = 4 //Guaranteed four servants
var/number_players = num_players()
roundstart_player_count = number_players
if(number_players > 30) //plus one servant for every additional 15 players
if(number_players > 30) //plus one servant for every additional 10 players above 30
number_players -= 30
starter_servants += round(number_players/15)
starter_servants += round(number_players / 10)
starter_servants = min(starter_servants, 8) //max 8 servants (that sould only happen with a ton of players)
while(starter_servants)
var/datum/mind/servant = pick(antag_candidates)
servants_to_serve += servant
antag_candidates -= servant
modePlayer += servant
servant.assigned_role = "Servant of Ratvar"
servant.special_role = "Servant of Ratvar"
servant.restricted_roles = restricted_jobs
starter_servants--
ark_time = 30 + round((roundstart_player_count / 5)) //In minutes, how long the Ark will wait before activation
ark_time = min(ark_time, 35) //35 minute maximum for the activation timer
return 1
/datum/game_mode/clockwork_cult/post_setup()
@@ -134,26 +135,35 @@ Credit where due:
var/datum/mind/servant = S
log_game("[servant.key] was made an initial servant of Ratvar")
var/mob/living/L = servant.current
var/turf/T = pick(GLOB.servant_spawns)
L.forceMove(T)
GLOB.servant_spawns -= T
greet_servant(L)
equip_servant(L)
add_servant_of_ratvar(L, TRUE)
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar //that's a mouthful
G.initial_activation_delay = ark_time * 60
G.seconds_until_activation = ark_time * 60 //60 seconds in a minute * number of minutes
..()
return 1
/datum/game_mode/clockwork_cult/proc/greet_servant(mob/M) //Description of their role
if(!M)
return 0
var/greeting_text = "<br><b><span class='large_brass'>You are a servant of Ratvar, the Clockwork Justiciar.</span>\n\
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.</b>"
to_chat(M, greeting_text)
to_chat(M, "<span class='bold large_brass'>You are a servant of Ratvar, the Clockwork Justiciar!</span>")
to_chat(M, "<span class='brass'>You have approximately <b>[ark_time]</b> minutes until the Ark activates.</span>")
to_chat(M, "<span class='brass'>Unlock <b>Script</b> scripture by converting a new servant.</span>")
to_chat(M, "<span class='brass'><b>Application</b> scripture will be unlocked halfway until the Ark's activation.</span>")
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
if(!L || !istype(L))
/datum/game_mode/proc/equip_servant(mob/living/M) //Grants a clockwork slab to the mob, with one of each component
if(!M || !ishuman(M))
return FALSE
var/obj/item/clockwork/slab/starter/S = new/obj/item/clockwork/slab/starter(null) //start it off in null
var/mob/living/carbon/human/L = M
L.set_species(/datum/species/human)
L.equipOutfit(/datum/outfit/servant_of_ratvar)
var/obj/item/clockwork/slab/S = new
var/slot = "At your feet"
var/list/slots = list("In your left pocket" = slot_l_store, "In your right pocket" = slot_r_store, "In your backpack" = slot_in_backpack, "On your belt" = slot_belt)
if(ishuman(L))
@@ -165,26 +175,24 @@ Credit where due:
if(!S.forceMove(get_turf(L)))
qdel(S)
if(S && !QDELETED(S))
to_chat(L, "<b>[slot] is a link to the halls of Reebe and your master. You may use it to perform many tasks, but also become oriented with the workings of Ratvar and how to best complete your \
tasks. This clockwork slab will be instrumental in your triumph. Remember: you can speak discreetly with your fellow servants by using the <span class='brass'>Hierophant Network</span> action button, \
and you can find a concise tutorial by using the slab in-hand and selecting Recollection.</b>")
to_chat(L, "<i>Alternatively, check out the wiki page at </i><b>https://tgstation13.org/wiki/Clockwork_Cult</b><i>, which contains additional information.</i>")
to_chat(L, "<span class='bold large_brass'>There is a paper in your backpack! Read it!</span>")
to_chat(L, "<span class='alloy'>[slot] is a <b>clockwork slab</b>, a multipurpose tool used to construct machines and invoke ancient words of power. If this is your first time \
as a servant, you can find a concise tutorial in the Recollection category of its interface.</span>")
to_chat(L, "<span class='alloy italics'>If you want more information, you can find a wiki link here!</span> https://tgstation13.org/wiki/Clockwork_Cult")
return TRUE
return FALSE
/datum/game_mode/clockwork_cult/proc/check_clockwork_victory()
if(GLOB.clockwork_gateway_activated)
SSticker.news_report = CLOCK_PROSELYTIZATION //failure, technically, but we have the station
if(GLOB.ratvar_awakens)
SSticker.news_report = CLOCK_SUMMON
return TRUE
if(GLOB.clockwork_gateway_activated || SSshuttle.emergency.mode == SHUTTLE_ESCAPE)
SSticker.news_report = CLOCK_SUMMON
return TRUE
else
SSticker.news_report = CULT_FAILURE
return FALSE
/datum/game_mode/clockwork_cult/declare_completion()
..()
return 0 //Doesn't end until the round does
return //Doesn't end until the round does
/datum/game_mode/clockwork_cult/generate_report()
return "We have lost contact with multiple stations in your sector. They have gone dark and do not respond to all transmissions, although they appear intact and the crew's life \
@@ -198,21 +206,12 @@ Credit where due:
if(istype(SSticker.mode, /datum/game_mode/clockwork_cult)) //Possibly hacky?
var/datum/game_mode/clockwork_cult/C = SSticker.mode
if(C.check_clockwork_victory())
text += "<span class='large_brass'><b>Ratvar's servants have succeeded in fulfilling His goals!</b></span>"
text += "<span class='large_brass'><b>Ratvar's servants defended the Ark until its activation!</b></span>"
SSticker.mode_result = "win - servants completed their objective (summon ratvar)"
else
var/half_victory = FALSE
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = locate() in GLOB.all_clockwork_objects
if(G)
half_victory = TRUE
if(half_victory)
text += "<span class='large_brass'><b>The crew escaped before Ratvar could rise, but the gateway \
was successfully constructed!</b></span>"
SSticker.mode_result = "halfwin - servants constructed the gateway but their objective was not completed (summon ratvar)"
else
text += "<span class='userdanger'>Ratvar's servants have failed!</span>"
SSticker.mode_result = "loss - servants failed their objective (summon ratvar)"
text += "<br><b>The servants' objective was:</b> <br>[CLOCKCULT_OBJECTIVE]"
text += "<span class='userdanger'>The Ark was destroyed! Ratvar will rust away for all eternity!</span>"
SSticker.mode_result = "loss - servants failed their objective (summon ratvar)"
text += "<br><b>The servants' objective was:</b> [CLOCKCULT_OBJECTIVE]."
text += "<br>Ratvar's servants had <b>[GLOB.clockwork_caches]</b> Tinkerer's Caches."
text += "<br><b>Construction Value(CV)</b> was: <b>[GLOB.clockwork_construction_value]</b>"
for(var/i in SSticker.scripture_states)
@@ -233,3 +232,66 @@ Credit where due:
var/datum/atom_hud/antag/A = GLOB.huds[ANTAG_HUD_CLOCKWORK]
A.leave_hud(M.current)
set_antag_hud(M.current, null)
//Servant of Ratvar outfit
/datum/outfit/servant_of_ratvar
uniform = /obj/item/clothing/under/chameleon/ratvar
shoes = /obj/item/clothing/shoes/workboots
back = /obj/item/storage/backpack
ears = /obj/item/device/radio/headset
gloves = /obj/item/clothing/gloves/color/yellow
belt = /obj/item/storage/belt/utility/servant
backpack_contents = list(/obj/item/storage/box/engineer = 1, \
/obj/item/clockwork/replica_fabricator = 1, /obj/item/stack/tile/brass/fifty = 1, /obj/item/paper/servant_primer = 1)
id = /obj/item/card/id
/datum/outfit/servant_of_ratvar/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
var/obj/item/card/id/W = H.wear_id
W.assignment = "Assistant"
W.access += ACCESS_MAINT_TUNNELS
W.registered_name = H.real_name
W.update_label()
/obj/item/paper/servant_primer
name = "The Ark And You: A Primer On Servitude"
color = "#DAAA18"
info = "<b>DON'T PANIC.</b><br><br>\
Here's a quick primer on what you should know here.\
<ol>\
<li>You're in a place called Reebe right now. The crew can't get here normally.</li>\
<li>In the north is your base camp, with supplies, consoles, and the Ark. In the south is an inaccessible area that the crew can walk between \
once they arrive (more on that later.) Everything between that space is an open area.</li>\
<li>Your job as a servant is to build fortifications and defenses to protect the Ark and your base once the Ark activates. You can do this \
however you like, but work with your allies and coordinate your efforts.</li>\
<li>Once the Ark activates, the station will be alerted. Portals to Reebe will open up in nearly every room. When they take these portals, \
the crewmembers will arrive in the area that you can't access, but can get through it freely - whereas you can't. Treat this as the \"spawn\" of the \
crew and defend it accordingly.</li>\
</ol>\
<hr>\
Here is the layout of Reebe, from left to right:\
<ul>\
<li><b>Dressing Room:</b> Contains clothing, a dresser, and a mirror. There are spare slabs and absconders here.</li>\
<li><b>Listening Station:</b> Contains intercoms, a telecomms relay, and a list of frequencies.</li>\
<li><b>Ark Chamber:</b> Houses the Ark.</li>\
<li><b>Observation Room:</b> Contains five camera observers. These can be used to watch the station through its cameras, as well as to teleport down \
to most areas. To do this, use the Warp action while hovering over the tile you want to warp to.</li>\
<li><b>Infirmary:</b> Contains sleepers and basic medical supplies for superficial wounds. The sleepers can consume Vitality to heal any occupants. \
This room is generally more useful during the preparation phase; when defending the Ark, scripture is more useful.</li>\
</ul>\
<hr>\
<h2>Things that have changed:</h2>\
<ul>\
<li><b><i>Scripture no longer requires components, and instead uses power.</i></b></li>\
<li>Added a <b>5-minute grace period</b> for the crew to prepare for the assault when the Ark activates.</li>\
<li>Script and Application scriptures can now be unlocked with enough power.</li>\
<li><b>Added the Hateful Manacles scripture</b>, which handcuffs targets!</li>\
</ul>\
<hr>\
<b>Good luck!</b>"
/obj/item/paper/servant_primer/examine(mob/user)
if(!is_servant_of_ratvar(user) && !isobserver(user))
to_chat(user, "<span class='danger'>You can't understand any of the words on [src].</span>")
..()

View File

@@ -0,0 +1,50 @@
//These spawn across the station when the Ark activates. Anyone can walk through one to teleport to Reebe.
/obj/effect/clockwork/city_of_cogs_rift
name = "celestial rift"
desc = "A stable bluespace rip. You're not sure it where leads."
clockwork_desc = "A one-way rift to the City of Cogs. Because it's linked to the Ark, it can't be closed."
icon_state = "city_of_cogs_rift"
resistance_flags = INDESTRUCTIBLE
density = TRUE
light_range = 2
light_power = 3
light_color = "#6A4D2F"
/obj/effect/clockwork/city_of_cogs_rift/Initialize()
. = ..()
visible_message("<span class='warning'>The air above [loc] shimmers and pops as a [name] forms there!</span>")
for(var/mob/M in GLOB.player_list)
if(M.z == z)
if(get_dist(src, M) >= 7)
M.playsound_local(src, 'sound/magic/blink.ogg', 10, FALSE, falloff = 10)
else
M.playsound_local(src, 'sound/magic/blink.ogg', 50, FALSE)
/obj/effect/clockwork/city_of_cogs_rift/Destroy()
visible_message("<span class='warning'>[src] cracks as it destabilizes and breaks apart!</span>")
return ..()
/obj/effect/clockwork/city_of_cogs_rift/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/nullrod))
to_chat(user, "<span class='warning'>Your [I.name] seems to have no effect on [src]!</span>")
return
. = ..()
/obj/effect/clockwork/city_of_cogs_rift/attack_hand(atom/movable/AM)
beckon(AM)
/obj/effect/clockwork/city_of_cogs_rift/CollidedWith(atom/movable/AM)
if(!QDELETED(AM))
beckon(AM)
/obj/effect/clockwork/city_of_cogs_rift/proc/beckon(atom/movable/AM)
AM.visible_message("<span class='danger'>[AM] passes through [src]!</span>", ignore_mob = AM)
AM.forceMove(pick(!is_servant_of_ratvar(AM) ? GLOB.city_of_cogs_spawns : GLOB.servant_spawns))
AM.visible_message("<span class='danger'>[AM] materializes from the air!</span>", \
"<span class='boldannounce'>You pass through [src] and appear [is_servant_of_ratvar(AM) ? "back at the City of Cogs" : "somewhere unfamiliar. Looks like it was a one-way trip.."].</span>")
do_sparks(5, TRUE, src)
do_sparks(5, TRUE, AM)
if(isliving(AM))
var/mob/living/L = AM
L.overlay_fullscreen("flash", /obj/screen/fullscreen/flash/static)
L.clear_fullscreen("flash", 5)

View File

@@ -11,21 +11,11 @@
var/stat_affected = CONSCIOUS
var/sigil_name = "Sigil"
var/resist_string = "glows blinding white" //string for when a null rod blocks its effects, "glows [resist_string]"
var/list/component_refund = list()
/obj/effect/clockwork/sigil/attackby(obj/item/I, mob/living/user, params)
if(is_servant_of_ratvar(user))
if(istype(I, /obj/item/clockwork/slab))
user.visible_message("<span class='warning'>[user] starts to dispel [src]...</span>", "<span class='danger'>You start to dispel [src]...</span>")
if(do_after(user, 20, target = src))
user.visible_message("<span class='warning'>[user] dispels [src]!</span>", "<span class='danger'>You dispel [src]!</span>")
for(var/i in component_refund)
if(component_refund[i])
for(var/r in 1 to component_refund[i])
generate_cache_component(i, src)
qdel(src)
return 1
else if(I.force)
if(I.force)
if(is_servant_of_ratvar(user) && user.a_intent != INTENT_HARM)
return ..()
user.visible_message("<span class='warning'>[user] scatters [src] with [I]!</span>", "<span class='danger'>You scatter [src] with [I]!</span>")
qdel(src)
return 1
@@ -35,7 +25,9 @@
return //you can't tk stomp sigils, but you can hit them with something
/obj/effect/clockwork/sigil/attack_hand(mob/user)
if(iscarbon(user) && !user.stat && !is_servant_of_ratvar(user))
if(iscarbon(user) && !user.stat)
if(is_servant_of_ratvar(user) && user.a_intent != INTENT_HARM)
return ..()
user.visible_message("<span class='warning'>[user] stamps out [src]!</span>", "<span class='danger'>You stomp on [src], scattering it into thousands of particles.</span>")
qdel(src)
return 1
@@ -75,7 +67,6 @@
light_power = 1
light_color = "#FAE48C"
sigil_name = "Sigil of Transgression"
component_refund = list(HIEROPHANT_ANSIBLE = 1)
/obj/effect/clockwork/sigil/transgression/sigil_effects(mob/living/L)
var/target_flashed = L.flash_act()
@@ -85,10 +76,11 @@
if(iscultist(L))
to_chat(L, "<span class='heavy_brass'>\"Watch your step, wretch.\"</span>")
L.adjustBruteLoss(10)
L.Knockdown(140, FALSE)
L.Knockdown(80, FALSE)
L.visible_message("<span class='warning'>[src] appears around [L] in a burst of light!</span>", \
"<span class='userdanger'>[target_flashed ? "An unseen force":"The glowing sigil around you"] holds you in place!</span>")
L.Stun(100)
L.Stun(40)
L.apply_status_effect(STATUS_EFFECT_BELLIGERENT)
new /obj/effect/temp_visual/ratvar/sigil/transgression(get_turf(src))
qdel(src)
@@ -107,18 +99,23 @@
light_color = "#FAE48C"
stat_affected = UNCONSCIOUS
resist_string = "glows faintly yellow"
sigil_name = "Sigil of Submission"
component_refund = list(GEIS_CAPACITOR = 1)
var/convert_time = 80
var/delete_on_finish = TRUE
sigil_name = "Sigil of Submission"
var/glow_type = /obj/effect/temp_visual/ratvar/sigil/submission
/obj/effect/clockwork/sigil/submission/sigil_effects(mob/living/L)
if(istype(L.buckled, /obj/structure/destructible/clockwork/geis_binding))
if(is_servant_of_ratvar(L.pulledby))
L.pulledby.stop_pulling()
if(is_servant_of_ratvar(L.buckled.pulledby))
L.buckled.pulledby.stop_pulling()
var/turf/T = get_turf(src)
var/has_sigil = FALSE
var/has_servant = FALSE
if(locate(/obj/effect/clockwork/sigil/transgression) in T)
has_sigil = TRUE
for(var/mob/living/M in range(3, src))
if(is_servant_of_ratvar(M) && !M.stat)
has_servant = TRUE
if(!has_sigil && !has_servant)
visible_message("<span class='danger'>[src] strains into a gentle violet color, but quietly fades...</span>")
return
L.visible_message("<span class='warning'>[src] begins to glow a piercing magenta!</span>", "<span class='sevtug'>You feel something start to invade your mind...</span>")
var/oldcolor = color
animate(src, color = "#AF0AAF", time = convert_time, flags = ANIMATION_END_NOW)
@@ -127,10 +124,10 @@
glow = new glow_type(get_turf(src))
animate(glow, alpha = 255, time = convert_time)
var/I = 0
while(I < convert_time && !QDELETED(L) && get_turf(L) == get_turf(src))
while(I < convert_time && get_turf(L) == get_turf(src))
I++
sleep(1)
if(QDELETED(L) || get_turf(L) != get_turf(src))
if(get_turf(L) != get_turf(src))
if(glow)
qdel(glow)
animate(src, color = oldcolor, time = 20, flags = ANIMATION_END_NOW)
@@ -139,6 +136,9 @@
return
if(is_eligible_servant(L))
to_chat(L, "<span class='heavy_brass'>\"You belong to me now.\"</span>")
if(!GLOB.application_scripture_unlocked)
GLOB.application_scripture_unlocked = TRUE
hierophant_message("<span class='large_brass bold'>With the conversion of a new servant the Ark's power grows. Application scriptures are now available.</span>")
if(add_servant_of_ratvar(L))
L.log_message("<font color=#BE8700>Conversion was done with a [sigil_name].</font>", INDIVIDUAL_ATTACK_LOG)
L.Knockdown(50) //Completely defenseless for five seconds - mainly to give them time to read over the information they've just been presented with
@@ -160,29 +160,26 @@
visible_message("<span class='warning'>[src] slowly stops glowing!</span>")
//Sigil of Transmission: Stores power for clockwork machinery, serving as a battery.
//Sigil of Transmission: Serves as an access point for powered structures.
/obj/effect/clockwork/sigil/transmission
name = "suspicious sigil"
desc = "A glowing orange sigil. The air around it feels staticky."
clockwork_desc = "A sigil that serves as power generation and a battery for clockwork structures."
clockwork_desc = "A sigil that serves as power generation and a battery for clockwork structures, linked to all other sigils of its type."
icon_state = "sigiltransmission"
alpha = 50
color = "#EC8A2D"
light_color = "#EC8A2D"
resist_string = "glows faintly"
sigil_name = "Sigil of Transmission"
component_refund = list(HIEROPHANT_ANSIBLE = 1)
affects_servants = TRUE
var/power_charge = 0 //starts with no power
var/drain_range = 14
/obj/effect/clockwork/sigil/transmission/Initialize()
. = ..()
update_glow()
update_icon()
/obj/effect/clockwork/sigil/transmission/ex_act(severity)
if(severity == 3)
modify_charge(-500)
adjust_clockwork_power(500) //Light explosions charge the network!
visible_message("<span class='warning'>[src] flares a brilliant orange!</span>")
else
..()
@@ -193,70 +190,26 @@
var/structure_number = 0
for(var/obj/structure/destructible/clockwork/powered/P in range(SIGIL_ACCESS_RANGE, src))
structure_number++
to_chat(user, "<span class='[power_charge ? "brass":"alloy"]'>It is storing <b>[GLOB.ratvar_awakens ? "INFINITE</b>":"[DisplayPower(power_charge)]</b> of"] power, \
and <b>[structure_number]</b> Clockwork Structure[structure_number == 1 ? " is":"s are"] in range.</span>")
to_chat(user, "<span class='brass'>While active, it will gradually drain power from nearby electronics. It is currently [isprocessing ? "active":"disabled"].</span>")
to_chat(user, "<span class='[get_clockwork_power() ? "brass":"alloy"]'>It is storing <b>[DisplayPower(get_clockwork_power())]</b> of shared power, \
and <b>[structure_number]</b> clockwork structure[structure_number == 1 ? " is":"s are"] in range.</span>")
if(iscyborg(user))
to_chat(user, "<span class='brass'>You can recharge from the [sigil_name] by crossing it.</span>")
else if(!GLOB.ratvar_awakens)
to_chat(user, "<span class='brass'>Hitting the [sigil_name] with brass sheets will convert them to power at a rate of <b>1</b> brass sheet to <b>[DisplayPower(POWER_FLOOR)]</b> power.</span>")
if(!GLOB.ratvar_awakens)
to_chat(user, "<span class='brass'>You can recharge Replica Fabricators from the [sigil_name].</span>")
/obj/effect/clockwork/sigil/transmission/attackby(obj/item/I, mob/living/user, params)
if(is_servant_of_ratvar(user) && istype(I, /obj/item/stack/tile/brass) && !GLOB.ratvar_awakens)
var/obj/item/stack/tile/brass/B = I
user.visible_message("<span class='warning'>[user] places [B] on [src], causing it to disintegrate into glowing orange energy!</span>", \
"<span class='brass'>You charge the [sigil_name] with [B], providing it with <b>[DisplayPower(B.amount * POWER_FLOOR)]</b> of power.</span>")
modify_charge(-(B.amount * POWER_FLOOR))
playsound(src, 'sound/effects/light_flicker.ogg', (B.amount * POWER_FLOOR) * 0.01, 1)
qdel(B)
return TRUE
return ..()
/obj/effect/clockwork/sigil/transmission/attack_ai(mob/user)
if(is_servant_of_ratvar(user))
attack_hand(user)
/obj/effect/clockwork/sigil/transmission/attack_hand(mob/user)
if(is_servant_of_ratvar(user))
visible_message("<span class='notice'>[user] [isprocessing ? "de":""]activates [src].</span>", "<span class='brass'>You [isprocessing ? "de":""]activate the [sigil_name].</span>")
if(isprocessing)
STOP_PROCESSING(SSobj, src)
else
START_PROCESSING(SSobj, src)
else
return ..()
/obj/effect/clockwork/sigil/transmission/sigil_effects(mob/living/L)
if(is_servant_of_ratvar(L))
if(iscyborg(L))
charge_cyborg(L)
else if(power_charge)
else if(get_clockwork_power())
to_chat(L, "<span class='brass'>You feel a slight, static shock.</span>")
/obj/effect/clockwork/sigil/transmission/process()
var/power_drained = 0
for(var/t in spiral_range_turfs(drain_range, src))
var/turf/T = t
for(var/M in T)
var/atom/movable/A = M
power_drained += A.power_drain(TRUE)
CHECK_TICK
modify_charge(-Floor(power_drained, MIN_CLOCKCULT_POWER))
new /obj/effect/temp_visual/ratvar/sigil/transmission(loc, 1 + (power_drained * 0.0035))
/obj/effect/clockwork/sigil/transmission/proc/charge_cyborg(mob/living/silicon/robot/cyborg)
if(!cyborg_checks(cyborg))
return
to_chat(cyborg, "<span class='brass'>You start to charge from the [sigil_name]...</span>")
if(!do_after(cyborg, 50, target = src, extra_checks = CALLBACK(src, .proc/cyborg_checks, cyborg, TRUE)))
return
var/giving_power = min(Floor(cyborg.cell.maxcharge - cyborg.cell.charge, MIN_CLOCKCULT_POWER), power_charge) //give the borg either all our power or their missing power floored to MIN_CLOCKCULT_POWER
if(modify_charge(giving_power))
var/giving_power = min(Floor(cyborg.cell.maxcharge - cyborg.cell.charge, MIN_CLOCKCULT_POWER), get_clockwork_power()) //give the borg either all our power or their missing power floored to MIN_CLOCKCULT_POWER
if(adjust_clockwork_power(-giving_power))
cyborg.visible_message("<span class='warning'>[cyborg] glows a brilliant orange!</span>")
var/previous_color = cyborg.color
cyborg.color = list("#EC8A2D", "#EC8A2D", "#EC8A2D", rgb(0,0,0))
@@ -269,9 +222,9 @@
if(!silent)
to_chat(cyborg, "<span class='warning'>You have no cell!</span>")
return FALSE
if(!power_charge)
if(!get_clockwork_power())
if(!silent)
to_chat(cyborg, "<span class='warning'>The [sigil_name] has no stored power!</span>")
to_chat(cyborg, "<span class='warning'>There is no power available across sigils!</span>")
return FALSE
if(cyborg.cell.charge > cyborg.cell.maxcharge - MIN_CLOCKCULT_POWER)
if(!silent)
@@ -283,25 +236,15 @@
return FALSE
return TRUE
/obj/effect/clockwork/sigil/transmission/proc/modify_charge(amount)
if(GLOB.ratvar_awakens)
update_glow()
return TRUE
if(power_charge - amount < 0)
return FALSE
power_charge -= amount
update_glow()
return TRUE
/obj/effect/clockwork/sigil/transmission/proc/update_glow()
/obj/effect/clockwork/sigil/transmission/update_icon()
if(GLOB.ratvar_awakens)
alpha = 255
else
alpha = min(initial(alpha) + power_charge*0.02, 255)
var/power_charge = get_clockwork_power()
alpha = min(initial(alpha) + power_charge * 0.02, 255)
if(!power_charge)
set_light(0)
else
set_light(max(alpha*0.02, 1.4), max(alpha*0.01, 0.1))
set_light(max(alpha * 0.02, 1.4), max(alpha * 0.01, 0.1))
//Vitality Matrix: Drains health from non-servants to heal or even revive servants.
/obj/effect/clockwork/sigil/vitality
@@ -316,7 +259,6 @@
stat_affected = DEAD
resist_string = "glows shimmering yellow"
sigil_name = "Vitality Matrix"
component_refund = list(VANGUARD_COGWHEEL = 1)
var/revive_cost = 150
var/sigil_active = FALSE
var/animation_number = 3 //each cycle increments this by 1, at 4 it produces an animation and resets

View File

@@ -0,0 +1,20 @@
//Marks the point at which "no man's land" begins on Reebe. Servants can't pass beyond this point in any way.
/obj/effect/clockwork/servant_blocker
name = "glowing arrow"
desc = "A faintly glowing image of an arrow on the ground. Convenient!"
icon_state = "servant_blocker"
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
density = TRUE
/obj/effect/clockwork/servant_blocker/Destroy(force)
if(!force)
return
. = ..()
/obj/effect/clockwork/servant_blocker/CanPass(atom/movable/M, turf/target)
var/list/target_contents = M.GetAllContents() + M
for(var/mob/living/L in target_contents)
if(is_servant_of_ratvar(L) && get_dir(M, src) != dir) //Unless we're on the side the arrow is pointing directly away from, no-go
to_chat(L, "<span class='danger'>The space beyond here can't be accessed by you or other servants.</span>")
return
return TRUE

View File

@@ -207,7 +207,7 @@
time_duration = round(time_duration * (2 * efficiency), 1)
CO.active = TRUE //you'd be active in a second but you should update immediately
invoker.visible_message("<span class='warning'>The air in front of [invoker] ripples before suddenly tearing open!</span>", \
"<span class='brass'>With a word, you rip open a [two_way ? "two-way":"one-way"] rift to [input_target_key]. It will last for [time_duration / 10] seconds and has [gateway_uses] use[gateway_uses > 1 ? "s" : ""].</span>")
"<span class='brass'>With a word, you rip open a [two_way ? "two-way":"one-way"] rift to [input_target_key]. It will last for [DisplayTimeText(time_duration)] and has [gateway_uses] use[gateway_uses > 1 ? "s" : ""].</span>")
var/obj/effect/clockwork/spatial_gateway/S1 = new(issrcobelisk ? get_turf(src) : get_step(get_turf(invoker), invoker.dir))
var/obj/effect/clockwork/spatial_gateway/S2 = new(istargetobelisk ? get_turf(target) : get_step(get_turf(target), target.dir))

View File

@@ -1,93 +1,3 @@
//generates a component in the global component cache, either random based on lowest or a specific component
/proc/generate_cache_component(specific_component_id, atom/A)
if(!specific_component_id)
specific_component_id = get_weighted_component_id()
GLOB.clockwork_component_cache[specific_component_id]++
if(A)
var/component_animation_type = get_component_animation_type(specific_component_id)
new component_animation_type(get_turf(A))
update_slab_info()
return specific_component_id
//returns a chosen component id based on the lowest amount of that component in the global cache, the global cache plus the slab if there are caches, or the slab if there are no caches.
/proc/get_weighted_component_id(obj/item/clockwork/slab/storage_slab)
. = list()
if(storage_slab)
if(GLOB.clockwork_caches)
for(var/i in GLOB.clockwork_component_cache)
.[i] = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*(GLOB.clockwork_component_cache[i] + storage_slab.stored_components[i]), 1)
else
for(var/i in GLOB.clockwork_component_cache)
.[i] = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*storage_slab.stored_components[i], 1)
else
for(var/i in GLOB.clockwork_component_cache)
.[i] = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*GLOB.clockwork_component_cache[i], 1)
for(var/obj/structure/destructible/clockwork/massive/celestial_gateway/G in GLOB.all_clockwork_objects)
if(G.still_needs_components())
for(var/i in G.required_components)
if(!G.required_components[i])
. -= i
break
. = pickweight(.)
//returns a component name from a component id
/proc/get_component_name(id)
switch(id)
if(BELLIGERENT_EYE)
return "Belligerent Eye"
if(VANGUARD_COGWHEEL)
return "Vanguard Cogwheel"
if(GEIS_CAPACITOR)
return "Geis Capacitor"
if(REPLICANT_ALLOY)
return "Replicant Alloy"
if(HIEROPHANT_ANSIBLE)
return "Hierophant Ansible"
else
return null
//returns a component acronym from a component id
/proc/get_component_acronym(id)
switch(id)
if(BELLIGERENT_EYE)
return "BE"
if(VANGUARD_COGWHEEL)
return "VC"
if(GEIS_CAPACITOR)
return "GC"
if(REPLICANT_ALLOY)
return "RA"
if(HIEROPHANT_ANSIBLE)
return "HA"
else
return null
//returns a component icon from a component id
/proc/get_component_icon(id)
var/static/list/tgui_component_icons = list(
BELLIGERENT_EYE = icon2base64(icon('icons/obj/tgui_components.dmi', BELLIGERENT_EYE)),
VANGUARD_COGWHEEL = icon2base64(icon('icons/obj/tgui_components.dmi', VANGUARD_COGWHEEL)),
GEIS_CAPACITOR = icon2base64(icon('icons/obj/tgui_components.dmi', GEIS_CAPACITOR)),
REPLICANT_ALLOY = icon2base64(icon('icons/obj/tgui_components.dmi', REPLICANT_ALLOY)),
HIEROPHANT_ANSIBLE = icon2base64(icon('icons/obj/tgui_components.dmi', HIEROPHANT_ANSIBLE)))
return "<img style='width:14px; height:14px' src='data:image/png;base64,[tgui_component_icons[id]]'/>"
//returns a component id from a component name
/proc/get_component_id(name)
switch(name)
if("Belligerent Eye")
return BELLIGERENT_EYE
if("Vanguard Cogwheel")
return VANGUARD_COGWHEEL
if("Geis Capacitor")
return GEIS_CAPACITOR
if("Replicant Alloy")
return REPLICANT_ALLOY
if("Hierophant Ansible")
return HIEROPHANT_ANSIBLE
else
return null
//returns a component spanclass from a component id
/proc/get_component_span(id)
switch(id)
@@ -129,35 +39,3 @@
return "#DAAA18"
else
return "#BE8700"
//returns a type for a floating component animation from a component id
/proc/get_component_animation_type(id)
switch(id)
if(BELLIGERENT_EYE)
return /obj/effect/temp_visual/ratvar/component
if(VANGUARD_COGWHEEL)
return /obj/effect/temp_visual/ratvar/component/cogwheel
if(GEIS_CAPACITOR)
return /obj/effect/temp_visual/ratvar/component/capacitor
if(REPLICANT_ALLOY)
return /obj/effect/temp_visual/ratvar/component/alloy
if(HIEROPHANT_ANSIBLE)
return /obj/effect/temp_visual/ratvar/component/ansible
else
return null
//returns a type for a component from a component id
/proc/get_component_type(id)
switch(id)
if(BELLIGERENT_EYE)
return /obj/item/clockwork/component/belligerent_eye
if(VANGUARD_COGWHEEL)
return /obj/item/clockwork/component/vanguard_cogwheel
if(GEIS_CAPACITOR)
return /obj/item/clockwork/component/geis_capacitor
if(REPLICANT_ALLOY)
return /obj/item/clockwork/component/replicant_alloy
if(HIEROPHANT_ANSIBLE)
return /obj/item/clockwork/component/hierophant_ansible
else
return null

View File

@@ -9,7 +9,7 @@
return FALSE
/atom/proc/consume_visual(obj/item/clockwork/replica_fabricator/fabricator, power_amount)
if(fabricator.can_use_power(power_amount))
if(get_clockwork_power(power_amount))
var/obj/effect/temp_visual/ratvar/beam/itemconsume/B = new /obj/effect/temp_visual/ratvar/beam/itemconsume(get_turf(src))
B.pixel_x = pixel_x
B.pixel_y = pixel_y
@@ -240,7 +240,7 @@
extra_checks = CALLBACK(fabricator, /obj/item/clockwork/replica_fabricator.proc/fabricator_repair_checks, repair_values, src, user, TRUE)))
break
obj_integrity = Clamp(obj_integrity + repair_values["healing_for_cycle"], 0, max_integrity)
fabricator.modify_stored_power(-repair_values["power_required"])
adjust_clockwork_power(-repair_values["power_required"])
playsound(src, 'sound/machines/click.ogg', 50, 1)
if(fabricator)
@@ -249,28 +249,6 @@
user.visible_message("<span class='notice'>[user]'s [fabricator.name] stops covering [src] with glowing orange energy.</span>", \
"<span class='alloy'>You finish repairing [src]. It is now at <b>[obj_integrity]/[max_integrity]</b> integrity.</span>")
//Hitting a sigil of transmission will try to charge from it.
/obj/effect/clockwork/sigil/transmission/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
. = TRUE
var/list/charge_values = list()
if(!fabricator.sigil_charge_checks(charge_values, src, user))
return
user.visible_message("<span class='notice'>[user]'s [fabricator.name] starts draining glowing orange energy from [src]...</span>", \
"<span class='alloy'>You start recharging your [fabricator.name]...</span>")
fabricator.recharging = src
while(fabricator && user && src)
if(!do_after(user, 10, target = src, extra_checks = CALLBACK(fabricator, /obj/item/clockwork/replica_fabricator.proc/sigil_charge_checks, charge_values, src, user, TRUE)))
break
modify_charge(charge_values["power_gain"])
fabricator.modify_stored_power(charge_values["power_gain"])
playsound(src, 'sound/effects/light_flicker.ogg', charge_values["power_gain"] * 0.1, 1)
if(fabricator)
fabricator.recharging = null
if(user)
user.visible_message("<span class='notice'>[user]'s [fabricator.name] stops draining glowing orange energy from [src].</span>", \
"<span class='alloy'>You finish recharging your [fabricator.name]. It now contains <b>[DisplayPower(fabricator.get_power())]/[DisplayPower(fabricator.get_max_power())]</b> power.</span>")
//Fabricator mob heal proc, to avoid as much copypaste as possible.
/mob/living/proc/fabricator_heal(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator)
var/list/repair_values = list()
@@ -284,7 +262,7 @@
extra_checks = CALLBACK(fabricator, /obj/item/clockwork/replica_fabricator.proc/fabricator_repair_checks, repair_values, src, user, TRUE)))
break
fabricator_heal_tick(repair_values["healing_for_cycle"])
fabricator.modify_stored_power(-repair_values["power_required"])
adjust_clockwork_power(-repair_values["power_required"])
playsound(src, 'sound/machines/click.ogg', 50, 1)
if(fabricator)

View File

@@ -26,6 +26,7 @@
/datum/action/innate/hierophant
name = "Hierophant Network"
desc = "Allows you to communicate with other Servants."
icon_icon = 'icons/mob/actions/actions_clockcult.dmi'
button_icon_state = "hierophant"
background_icon_state = "bg_clock"
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUN|AB_CHECK_CONSCIOUS
@@ -43,6 +44,7 @@
var/input = stripped_input(usr, "Please enter a message to send to other servants.", "Hierophant Network", "")
if(!input || !IsAvailable())
return
if(ishuman(owner))
clockwork_say(owner, "[text2ratvar("Servants, hear my words: [input]")]", TRUE)
log_talk(owner,"CLOCK:[key_name(owner)] : [input]",LOGSAY)
titled_hierophant_message(owner, input, span_for_name, span_for_message, title)

View File

@@ -0,0 +1,25 @@
//Helper procs for clockwork power, used by structures and items and that kind of jazz.
/proc/get_clockwork_power(amount) //If no amount is provided, returns the clockwork power; otherwise, returns if there's enough power for that amount.
return amount ? GLOB.clockwork_power >= amount : GLOB.clockwork_power
/proc/adjust_clockwork_power(amount) //Adjusts the global clockwork power by this amount (min 0.)
if(GLOB.ratvar_approaches)
amount *= 0.75 //The herald's beacon reduces power costs by 25% across the board!
GLOB.clockwork_power = GLOB.ratvar_awakens ? INFINITY : max(0, GLOB.clockwork_power + amount)
for(var/obj/effect/clockwork/sigil/transmission/T in GLOB.all_clockwork_objects)
T.update_icon()
var/power_overwhelming = GLOB.clockwork_power
if(power_overwhelming >= SCRIPT_UNLOCK_THRESHOLD && !GLOB.script_scripture_unlocked)
GLOB.script_scripture_unlocked = TRUE
hierophant_message("<span class='large_brass bold'>The Ark swells as a key power threshold is reached. Script scriptures are now available.</span>")
if(power_overwhelming >= APPLICATION_UNLOCK_THRESHOLD && !GLOB.application_scripture_unlocked)
GLOB.application_scripture_unlocked = TRUE
hierophant_message("<span class='large_brass bold'>The Ark surges as a key power threshold is reached. Application scriptures are now available.</span>")
return TRUE
/proc/can_access_clockwork_power(atom/movable/access_point, amount) //Returns true if the access point has access to clockwork power (and optionally, a number of watts for it)
if(amount && !get_clockwork_power(amount)) //No point in trying if we don't have the power anyway
return
var/list/possible_conduits = view(5, access_point)
return locate(/obj/effect/clockwork/sigil/transmission) in possible_conduits || GLOB.ratvar_awakens

View File

@@ -1,39 +1,23 @@
//returns a list of scriptures and if they're unlocked or not
/proc/scripture_unlock_check()
var/servants = 0
var/unconverted_ai_exists = get_unconverted_ais()
for(var/mob/living/M in GLOB.living_mob_list)
if(is_servant_of_ratvar(M) && (ishuman(M) || issilicon(M)))
servants++
. = list(SCRIPTURE_DRIVER = TRUE, SCRIPTURE_SCRIPT = FALSE, SCRIPTURE_APPLICATION = FALSE, SCRIPTURE_JUDGEMENT = FALSE)
. = list(SCRIPTURE_DRIVER = TRUE, SCRIPTURE_SCRIPT = FALSE, SCRIPTURE_APPLICATION = FALSE)
//Drivers: always unlocked
.[SCRIPTURE_SCRIPT] = (SSticker.scripture_states[SCRIPTURE_SCRIPT] || \
(servants >= SCRIPT_SERVANT_REQ && GLOB.clockwork_caches >= SCRIPT_CACHE_REQ))
//Script: SCRIPT_SERVANT_REQ or more non-brain servants and SCRIPT_CACHE_REQ or more clockwork caches
.[SCRIPTURE_APPLICATION] = (SSticker.scripture_states[SCRIPTURE_APPLICATION] || \
(servants >= APPLICATION_SERVANT_REQ && GLOB.clockwork_caches >= APPLICATION_CACHE_REQ && GLOB.clockwork_construction_value >= APPLICATION_CV_REQ))
.[SCRIPTURE_SCRIPT] = GLOB.script_scripture_unlocked
//Script: Convert a new servant using a sigil of submission.
.[SCRIPTURE_APPLICATION] = GLOB.application_scripture_unlocked
//Application: APPLICATION_SERVANT_REQ or more non-brain servants, APPLICATION_CACHE_REQ or more clockwork caches, and at least APPLICATION_CV_REQ CV
.[SCRIPTURE_JUDGEMENT] = (SSticker.scripture_states[SCRIPTURE_JUDGEMENT] || \
(servants >= JUDGEMENT_SERVANT_REQ && GLOB.clockwork_caches >= JUDGEMENT_CACHE_REQ && GLOB.clockwork_construction_value >= JUDGEMENT_CV_REQ && !unconverted_ai_exists))
//Judgement: JUDGEMENT_SERVANT_REQ or more non-brain servants, JUDGEMENT_CACHE_REQ or more clockwork caches, at least JUDGEMENT_CV_REQ CV, and there are no living, non-servant ais
//reports to servants when scripture is locked or unlocked
/proc/scripture_unlock_alert(list/previous_states)
. = scripture_unlock_check()
for(var/i in .)
if(.[i] != previous_states[i])
hierophant_message("<span class='large_brass'><i>Hierophant Network:</i> <b>[i] Scripture has been [.[i] ? "un":""]locked.</b></span>") //maybe admins fucked with scripture states?
update_slab_info()
/proc/get_unconverted_ais()
. = 0
for(var/ai in GLOB.ai_list)
var/mob/living/silicon/ai/AI = ai
if(AI.deployed_shell && is_servant_of_ratvar(AI.deployed_shell))
continue
if(is_servant_of_ratvar(AI) || !isturf(AI.loc) || !(AI.z in GLOB.station_z_levels) || AI.stat == DEAD)
continue
.++
for(var/mob/M in GLOB.player_list)
if(is_servant_of_ratvar(M) || isobserver(M))
M.playsound_local(M, 'sound/magic/clockwork/scripture_tier_up.ogg', 50, FALSE, pressure_affected = FALSE)
/proc/update_slab_info(obj/item/clockwork/slab/set_slab)
generate_all_scripture()

View File

@@ -21,70 +21,58 @@
remove_ranged_ability()
return TRUE
//For the Geis scripture; binds a target to convert.
/obj/effect/proc_holder/slab/geis
ranged_mousepointer = 'icons/effects/geis_target.dmi'
//For the Hateful Manacles scripture; applies replicant handcuffs to the target.
/obj/effect/proc_holder/slab/hateful_manacles
/obj/effect/proc_holder/slab/geis/InterceptClickOn(mob/living/caller, params, atom/target)
/obj/effect/proc_holder/slab/hateful_manacles/InterceptClickOn(mob/living/caller, params, atom/target)
if(..())
return TRUE
var/turf/T = ranged_ability_user.loc
if(!isturf(T))
return TRUE
var/target_is_binding = istype(target, /obj/structure/destructible/clockwork/geis_binding)
if(iscarbon(target) && target.Adjacent(ranged_ability_user))
var/mob/living/carbon/L = target
if(is_servant_of_ratvar(L))
to_chat(ranged_ability_user, "<span class='neovgre'>\"They're a servant.\"</span>")
return TRUE
else if(L.stat)
to_chat(ranged_ability_user, "<span class='neovgre'>\"There is use in shackling the dead, but for examples.\"</span>")
return TRUE
else if(L.handcuffed)
to_chat(ranged_ability_user, "<span class='neovgre'>\"They are already helpless, no?\"</span>")
return TRUE
if((target_is_binding || isliving(target)) && ranged_ability_user.Adjacent(target))
if(target_is_binding)
var/obj/structure/destructible/clockwork/geis_binding/GB = target
GB.repair_and_interrupt()
for(var/m in GB.buckled_mobs)
if(m)
add_logs(ranged_ability_user, m, "rebound with Geis")
successful = TRUE
playsound(loc, 'sound/weapons/handcuffs.ogg', 30, TRUE)
ranged_ability_user.visible_message("<span class='danger'>[ranged_ability_user] begins forming manacles around [L]'s wrists!</span>", \
"<span class='neovgre_small'>You begin shaping replicant alloy into manacles around [L]'s wrists...</span>")
to_chat(L, "<span class='userdanger'>[ranged_ability_user] begins forming manacles around your wrists!</span>")
if(do_mob(ranged_ability_user, L, 30))
if(!L.handcuffed)
L.handcuffed = new/obj/item/restraints/handcuffs/clockwork(L)
L.update_handcuffed()
to_chat(ranged_ability_user, "<span class='neovgre_small'>You shackle [L].</span>")
add_logs(ranged_ability_user, L, "handcuffed")
else
var/mob/living/L = target
if(L.null_rod_check())
to_chat(ranged_ability_user, "<span class='sevtug'>\"A void weapon? Really, you expect me to be able to do anything?\"</span>")
return TRUE
if(is_servant_of_ratvar(L))
if(L != ranged_ability_user)
to_chat(ranged_ability_user, "<span class='sevtug'>\"[L.p_they(TRUE)] already serve[L.p_s()] Ratvar. [text2ratvar("Perhaps [ranged_ability_user.p_theyre()] into bondage?")]\"</span>")
return TRUE
if(L.stat == DEAD)
to_chat(ranged_ability_user, "<span class='sevtug'>\"[L.p_theyre(TRUE)] dead, idiot.\"</span>")
return TRUE
to_chat(ranged_ability_user, "<span class='warning'>You fail to shackle [L].</span>")
if(istype(L.buckled, /obj/structure/destructible/clockwork/geis_binding)) //if they're already bound, just stun them
var/obj/structure/destructible/clockwork/geis_binding/GB = L.buckled
GB.repair_and_interrupt()
add_logs(ranged_ability_user, L, "rebound with Geis")
successful = TRUE
else
clockwork_say(ranged_ability_user, text2ratvar("Be bound, heathen!"))
add_logs(ranged_ability_user, L, "bound with Geis")
playsound(target, 'sound/magic/blink.ogg', 50, TRUE, -4, frequency = 0.5)
if(slab.speed_multiplier >= 0.5) //excuse my debug...
ranged_ability_user.notransform = TRUE
addtimer(CALLBACK(src, .proc/reset_user_notransform, ranged_ability_user), 4) //stop us moving for a little bit so we don't break the binding immediately
if(L.buckled)
L.buckled.unbuckle_mob(target, TRUE)
var/obj/structure/destructible/clockwork/geis_binding/binding = new(get_turf(target))
binding.setDir(target.dir)
binding.buckle_mob(target, TRUE)
ranged_ability_user.start_pulling(binding)
ranged_ability_user.apply_status_effect(STATUS_EFFECT_GEISTRACKER, binding)
successful = TRUE
successful = TRUE
remove_ranged_ability()
else
..()
return TRUE
/obj/effect/proc_holder/slab/geis/proc/reset_user_notransform(mob/living/user)
if(user)
user.notransform = FALSE
/obj/item/restraints/handcuffs/clockwork
name = "replicant manacles"
desc = "Cold, heavy manacles made out of some strange black metal."
origin_tech = "materials=2;magnets=5"
flags_1 = DROPDEL_1
/obj/item/restraints/handcuffs/clockwork/dropped(mob/user)
user.visible_message("<span class='danger'>[user]'s [name] come apart at the seams!</span>", \
"<span class='userdanger'>Your [name] break apart as they're removed!</span>")
. = ..()
//For the Sentinel's Compromise scripture; heals a target servant.
/obj/effect/proc_holder/slab/compromise
@@ -140,12 +128,83 @@
playsound(targetturf, 'sound/magic/staff_healing.ogg', 50, 1)
if(has_holy_water)
L.reagents.del_reagent("holywater")
L.reagents.remove_reagent("holywater", 1000)
remove_ranged_ability()
return TRUE
//For the Kindle scripture; stuns and mutes a target non-servant.
/obj/effect/proc_holder/slab/kindle
ranged_mousepointer = 'icons/effects/volt_target.dmi'
/obj/effect/proc_holder/slab/kindle/InterceptClickOn(mob/living/caller, params, atom/target)
if(..())
return TRUE
var/turf/T = ranged_ability_user.loc
if(!isturf(T))
return TRUE
if(target in view(7, get_turf(ranged_ability_user)))
successful = TRUE
var/turf/U = get_turf(target)
to_chat(ranged_ability_user, "<span class='brass'>You release the light of Ratvar!</span>")
clockwork_say(ranged_ability_user, text2ratvar("Purge all untruths and honor Engine!"))
add_logs(ranged_ability_user, U, "fired at with Kindle")
playsound(ranged_ability_user, 'sound/magic/blink.ogg', 50, TRUE, frequency = 0.5)
var/obj/item/projectile/kindle/A = new(T)
A.original = target
A.starting = T
A.current = T
A.yo = U.y - T.y
A.xo = U.x - T.x
A.fire()
remove_ranged_ability()
return TRUE
/obj/item/projectile/kindle
name = "kindled flame"
icon_state = "pulse0"
nodamage = TRUE
damage = 0 //We're just here for the stunning!
damage_type = BURN
flag = "bomb"
range = 3
log_override = TRUE
/obj/item/projectile/kindle/Destroy()
visible_message("<span class='warning'>[src] flickers out!</span>")
. = ..()
/obj/item/projectile/kindle/on_hit(atom/target, blocked = FALSE)
if(isliving(target))
var/mob/living/L = target
if(is_servant_of_ratvar(L) || L.stat || L.has_status_effect(STATUS_EFFECT_KINDLE))
return
var/obj/O = L.null_rod_check()
playsound(L, 'sound/magic/fireball.ogg', 50, TRUE, frequency = 1.25)
if(O)
L.visible_message("<span class='warning'>[L]'s eyes flare with dim light as they stumble!</span>", \
"<span class='userdanger'>Your [O] glows white-hot against you as it absorbs some sort of power!</span>")
L.adjustFireLoss(5)
L.Stun(40)
playsound(L, 'sound/weapons/sear.ogg', 50, TRUE)
else
L.visible_message("<span class='warning'>[L]'s eyes blaze with brilliant light!</span>", \
"<span class='userdanger'>Your vision suddenly screams with white-hot light!</span>")
L.Knockdown(15)
L.apply_status_effect(STATUS_EFFECT_KINDLE)
L.flash_act(1, 1)
if(iscultist(L))
L.adjustFireLoss(15)
..()
//For the cyborg Linked Vanguard scripture, grants you and a nearby ally Vanguard
/obj/effect/proc_holder/slab/vanguard
ranged_mousepointer = 'icons/effects/vanguard_target.dmi'

View File

@@ -0,0 +1,50 @@
//This is the innate action for "binding" and calling weapons to yourself. These weapons can appear and disappear at will.
//You can invoke a cooldown period by calling "weapon_reset(cooldown in deciseconds)." By default, this only applies to dismissing weapons.
/datum/action/innate/call_weapon
name = "Call Weapon"
desc = "This definitely shouldn't exist."
icon_icon = 'icons/mob/actions/actions_clockcult.dmi'
button_icon_state = "ratvarian_spear"
background_icon_state = "bg_clock"
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUN|AB_CHECK_CONSCIOUS
buttontooltipstyle = "clockcult"
var/cooldown = 0
var/obj/item/clockwork/weapon/weapon_type //The type of weapon to create
var/obj/item/clockwork/weapon/weapon
/datum/action/innate/call_weapon/IsAvailable()
if(!is_servant_of_ratvar(owner))
qdel(src)
return
if(cooldown > world.time)
return
return ..()
/datum/action/innate/call_weapon/Activate()
if(!owner.get_empty_held_indexes())
to_chat(usr, "<span class='warning'>You need an empty hand to call forth your [initial(weapon_type.name)]!</span>")
return
if(weapon)
if(weapon.loc == owner)
owner.visible_message("<span class='danger'>[owner]'s [weapon.name] flickers and disappears!</span>")
to_chat(owner, "<span class='brass'>You dismiss [weapon].</span>")
owner.drop_item()
QDEL_NULL(weapon)
weapon_reset(RATVARIAN_SPEAR_COOLDOWN * 0.5)
return
else
weapon.visible_message("<span class='warning'>[weapon] suddenly flickers and disappears!</span>")
owner.visible_message("<span class='danger'>A [weapon.name] suddenly flickers into [owner]'s hands!</span>", "<span class='brass'>You recall [weapon] to you.</span>")
else
weapon = new weapon_type (get_turf(usr), src)
owner.visible_message("<span class='warning'>A [weapon.name] materializes in [owner]'s hands!</span>", "<span class='brass'>You call forth your [weapon.name]!</span>")
weapon.forceMove(get_turf(owner))
owner.put_in_hands(weapon)
owner.update_action_buttons_icon()
return TRUE
/datum/action/innate/call_weapon/proc/weapon_reset(cooldown_time)
cooldown = world.time + cooldown_time
addtimer(CALLBACK(owner, /mob.proc/update_action_buttons_icon), cooldown_time)
owner.update_action_buttons_icon()
QDEL_NULL(weapon)

View File

@@ -1,13 +1,10 @@
//Ratvarian spear: A relatively fragile spear from the Celestial Derelict. Deals extreme damage to silicons and enemy cultists, but doesn't last long when summoned.
/obj/item/clockwork/ratvarian_spear
/obj/item/clockwork/weapon/ratvarian_spear
name = "ratvarian spear"
desc = "A razor-sharp spear made of brass. It thrums with barely-contained energy."
clockwork_desc = "A powerful spear of Ratvarian making. It's more effective against enemy cultists and silicons."
icon = 'icons/obj/clockwork_objects.dmi'
icon_state = "ratvarian_spear"
item_state = "ratvarian_spear"
lefthand_file = 'icons/mob/inhands/antag/clockwork_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/clockwork_righthand.dmi'
force = 15 //Extra damage is dealt to targets in attack()
throwforce = 25
armour_penetration = 10
@@ -16,42 +13,27 @@
hitsound = 'sound/weapons/bladeslice.ogg'
w_class = WEIGHT_CLASS_BULKY
var/bonus_burn = 5
var/timerid
/obj/item/clockwork/ratvarian_spear/Destroy()
deltimer(timerid)
return ..()
/obj/item/clockwork/ratvarian_spear/ratvar_act()
/obj/item/clockwork/weapon/ratvarian_spear/ratvar_act()
if(GLOB.ratvar_awakens) //If Ratvar is alive, the spear is extremely powerful
force = 20
bonus_burn = 10
throwforce = 40
armour_penetration = 50
clockwork_desc = initial(clockwork_desc)
deltimer(timerid)
else
force = initial(force)
bonus_burn = initial(bonus_burn)
throwforce = initial(throwforce)
armour_penetration = initial(armour_penetration)
clockwork_desc = "A powerful spear of Ratvarian making. It's more effective against enemy cultists and silicons, though it won't last for long."
deltimer(timerid)
timerid = addtimer(CALLBACK(src, .proc/break_spear), RATVARIAN_SPEAR_DURATION, TIMER_STOPPABLE)
/obj/item/clockwork/ratvarian_spear/cyborg/ratvar_act() //doesn't break!
..()
clockwork_desc = "A powerful spear of Ratvarian making. It's more effective against enemy cultists and silicons."
deltimer(timerid)
/obj/item/clockwork/ratvarian_spear/examine(mob/user)
/obj/item/clockwork/weapon/ratvarian_spear/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
to_chat(user, "<span class='inathneq_small'>Attacks on living non-Servants will generate <b>[bonus_burn]</b> units of vitality.</span>")
if(!iscyborg(user))
to_chat(user, "<span class='brass'>Throwing the spear will do massive damage, break the spear, and knock down the target.</span>")
/obj/item/clockwork/ratvarian_spear/attack(mob/living/target, mob/living/carbon/human/user)
/obj/item/clockwork/weapon/ratvarian_spear/attack(mob/living/target, mob/living/carbon/human/user)
. = ..()
if(!QDELETED(target) && target.stat != DEAD && !target.null_rod_check() && !is_servant_of_ratvar(target)) //we do bonus damage on attacks unless they're a servant, have a null rod, or are dead
var/bonus_damage = bonus_burn //normally a total of 20 damage, 30 with ratvar
@@ -63,7 +45,7 @@
bonus_damage *= 3 //total 30 damage on cultists, 50 with ratvar
GLOB.clockwork_vitality += target.adjustFireLoss(bonus_damage) //adds the damage done to existing vitality
/obj/item/clockwork/ratvarian_spear/throw_impact(atom/target)
/obj/item/clockwork/weapon/ratvarian_spear/throw_impact(atom/target)
var/turf/T = get_turf(target)
if(isliving(target))
var/mob/living/L = target
@@ -83,11 +65,12 @@
else
..()
/obj/item/clockwork/ratvarian_spear/proc/break_spear(turf/T)
/obj/item/clockwork/weapon/ratvarian_spear/proc/break_spear(turf/T)
if(src)
if(!T)
T = get_turf(src)
if(T) //make sure we're not in null or something
T.visible_message("<span class='warning'>[src] [pick("cracks in two and fades away", "snaps in two and dematerializes")]!</span>")
new /obj/effect/temp_visual/ratvar/spearbreak(T)
qdel(src)
action.weapon_reset(RATVARIAN_SPEAR_COOLDOWN)

View File

@@ -6,7 +6,8 @@
icon_state = "clockwork_helmet"
w_class = WEIGHT_CLASS_NORMAL
resistance_flags = FIRE_PROOF | ACID_PROOF
armor = list(melee = 80, bullet = 70, laser = -25, energy = 0, bomb = 60, bio = 0, rad = 0, fire = 100, acid = 100)
flags_inv = HIDEEARS|HIDEHAIR|HIDEFACE
armor = list(melee = 50, bullet = 70, laser = -25, energy = 0, bomb = 60, bio = 0, rad = 0, fire = 100, acid = 100)
/obj/item/clothing/head/helmet/clockwork/Initialize()
. = ..()
@@ -23,8 +24,13 @@
flags_1 |= STOPSPRESSUREDMAGE_1
max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
else if(GLOB.ratvar_approaches)
armor = list(melee = 70, bullet = 80, laser = -15, energy = 25, bomb = 70, bio = 0, rad = 0, fire = 100, acid = 100)
flags_1 |= STOPSPRESSUREDMAGE_1
max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
else
armor = list(melee = 80, bullet = 70, laser = -25, energy = 0, bomb = 60, bio = 0, rad = 0, fire = 100, acid = 100)
armor = list(melee = 60, bullet = 70, laser = -25, energy = 0, bomb = 60, bio = 0, rad = 0, fire = 100, acid = 100)
flags_1 &= ~STOPSPRESSUREDMAGE_1
max_heat_protection_temperature = initial(max_heat_protection_temperature)
min_cold_protection_temperature = initial(min_cold_protection_temperature)
@@ -61,7 +67,7 @@
cold_protection = CHEST|GROIN|LEGS
heat_protection = CHEST|GROIN|LEGS
resistance_flags = FIRE_PROOF | ACID_PROOF
armor = list(melee = 80, bullet = 70, laser = -25, energy = 0, bomb = 60, bio = 0, rad = 0, fire = 100, acid = 100)
armor = list(melee = 60, bullet = 70, laser = -25, energy = 0, bomb = 60, bio = 0, rad = 0, fire = 100, acid = 100)
allowed = list(/obj/item/clockwork, /obj/item/clothing/glasses/wraith_spectacles, /obj/item/clothing/glasses/judicial_visor, /obj/item/device/mmi/posibrain/soul_vessel)
/obj/item/clothing/suit/armor/clockwork/Initialize()
@@ -79,8 +85,13 @@
flags_1 |= STOPSPRESSUREDMAGE_1
max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
else if(GLOB.ratvar_approaches)
armor = list(melee = 70, bullet = 80, laser = -15, energy = 25, bomb = 70, bio = 0, rad = 0, fire = 100, acid = 100)
flags_1 |= STOPSPRESSUREDMAGE_1
max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
else
armor = list(melee = 80, bullet = 70, laser = -25, energy = 0, bomb = 60, bio = 0, rad = 0, fire = 100, acid = 100)
armor = list(melee = 60, bullet = 70, laser = -25, energy = 0, bomb = 60, bio = 0, rad = 0, fire = 100, acid = 100)
flags_1 &= ~STOPSPRESSUREDMAGE_1
max_heat_protection_temperature = initial(max_heat_protection_temperature)
min_cold_protection_temperature = initial(min_cold_protection_temperature)

View File

@@ -1,9 +1,7 @@
/obj/item/clockwork/slab //Clockwork slab: The most important tool in Ratvar's arsenal. Allows scripture recital, tutorials, and generates components.
name = "clockwork slab"
desc = "A strange metal tablet. A clock in the center turns around and around."
clockwork_desc = "A link between you and the Celestial Derelict. It produces components, contains information, and is your most vital tool as a Servant.\n\
Use the <span class='brass'>Hierophant Network</span> action button to discreetly talk with other Servants.\n\
Clockwork slabs will only make components if held or if inside an item held by a human, and when making a component will prevent all other slabs held from making components.\n\
clockwork_desc = "A link between you and the Celestial Derelict. It contains information, recites scripture, and is your most vital tool as a Servant.\n\
Hitting a slab, a Servant with a slab, or a cache will <b>transfer</b> this slab's components into the target, the target's slab, or the global cache, respectively."
icon_state = "dread_ipad"
lefthand_file = 'icons/mob/inhands/antag/clockwork_lefthand.dmi'
@@ -11,30 +9,21 @@
var/inhand_overlay //If applicable, this overlay will be applied to the slab's inhand
slot_flags = SLOT_BELT
w_class = WEIGHT_CLASS_SMALL
var/list/stored_components = list(BELLIGERENT_EYE = 0, VANGUARD_COGWHEEL = 0, GEIS_CAPACITOR = 0, REPLICANT_ALLOY = 0, HIEROPHANT_ANSIBLE = 0)
var/busy //If the slab is currently being used by something
var/production_time = 0
var/target_component_id //the target component ID to create, if any
var/no_cost = FALSE //If the slab is admin-only and needs no components and has no scripture locks
var/speed_multiplier = 1 //multiples how fast this slab recites scripture
var/produces_components = TRUE //if it produces components at all
var/selected_scripture = SCRIPTURE_DRIVER
var/recollecting = FALSE //if we're looking at fancy recollection
var/obj/effect/proc_holder/slab/slab_ability //the slab's current bound ability, for certain scripture
var/list/quickbound = list(/datum/clockwork_scripture/ranged_ability/geis, /datum/clockwork_scripture/create_object/sigil_of_submission, \
/datum/clockwork_scripture/create_object/replicant, /datum/clockwork_scripture/create_object/tinkerers_cache) //quickbound scripture, accessed by index
var/list/quickbound = list(/datum/clockwork_scripture/abscond, \
/datum/clockwork_scripture/ranged_ability/kindle, /datum/clockwork_scripture/ranged_ability/hateful_manacles) //quickbound scripture, accessed by index
var/maximum_quickbound = 5 //how many quickbound scriptures we can have
var/recollection_category = "Default"
actions_types = list(/datum/action/item_action/clock/hierophant)
/obj/item/clockwork/slab/starter
stored_components = list(BELLIGERENT_EYE = 1, VANGUARD_COGWHEEL = 1, GEIS_CAPACITOR = 1, REPLICANT_ALLOY = 1, HIEROPHANT_ANSIBLE = 1)
/obj/item/clockwork/slab/internal //an internal motor for mobs running scripture
name = "scripture motor"
quickbound = list()
no_cost = TRUE
produces_components = FALSE
/obj/item/clockwork/slab/debug
speed_multiplier = 0
@@ -49,33 +38,33 @@
clockwork_desc = "A divine link to the Celestial Derelict, allowing for limited recital of scripture.\n\
Hitting a slab, a Servant with a slab, or a cache will <b>transfer</b> this slab's components into the target, the target's slab, or the global cache, respectively."
quickbound = list(/datum/clockwork_scripture/ranged_ability/judicial_marker, /datum/clockwork_scripture/ranged_ability/linked_vanguard, \
/datum/clockwork_scripture/create_object/tinkerers_cache)
/datum/clockwork_scripture/create_object/stargazer)
maximum_quickbound = 6 //we usually have one or two unique scriptures, so if ratvar is up let us bind one more
actions_types = list()
/obj/item/clockwork/slab/cyborg/engineer //two scriptures, plus a fabricator
quickbound = list(/datum/clockwork_scripture/create_object/replicant, /datum/clockwork_scripture/create_object/sigil_of_transmission)
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/create_object/replicant, /datum/clockwork_scripture/create_object/sigil_of_transmission)
/obj/item/clockwork/slab/cyborg/medical //five scriptures, plus a spear
quickbound = list(/datum/clockwork_scripture/ranged_ability/linked_vanguard, /datum/clockwork_scripture/ranged_ability/sentinels_compromise, \
/datum/clockwork_scripture/create_object/vitality_matrix, /datum/clockwork_scripture/channeled/mending_mantra, /datum/clockwork_scripture/fellowship_armory)
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/ranged_ability/linked_vanguard, /datum/clockwork_scripture/ranged_ability/sentinels_compromise, \
/datum/clockwork_scripture/create_object/vitality_matrix, /datum/clockwork_scripture/channeled/mending_mantra)
/obj/item/clockwork/slab/cyborg/security //twoscriptures, plus a spear
quickbound = list(/datum/clockwork_scripture/channeled/belligerent, /datum/clockwork_scripture/ranged_ability/judicial_marker)
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/ranged_ability/hateful_manacles, /datum/clockwork_scripture/ranged_ability/judicial_marker)
/obj/item/clockwork/slab/cyborg/peacekeeper //two scriptures, plus a spear
quickbound = list(/datum/clockwork_scripture/channeled/belligerent, /datum/clockwork_scripture/ranged_ability/judicial_marker)
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/ranged_ability/hateful_manacles, /datum/clockwork_scripture/ranged_ability/judicial_marker)
/obj/item/clockwork/slab/cyborg/janitor //five scriptures, plus a fabricator
quickbound = list(/datum/clockwork_scripture/create_object/replicant, /datum/clockwork_scripture/create_object/sigil_of_transgression, \
/datum/clockwork_scripture/create_object/ocular_warden, /datum/clockwork_scripture/create_object/mania_motor, /datum/clockwork_scripture/create_object/tinkerers_daemon)
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/create_object/replicant, /datum/clockwork_scripture/create_object/sigil_of_transgression, \
/datum/clockwork_scripture/create_object/ocular_warden, /datum/clockwork_scripture/create_object/mania_motor, /datum/clockwork_scripture/create_object/stargazer)
/obj/item/clockwork/slab/cyborg/service //five scriptures, plus xray vision
quickbound = list(/datum/clockwork_scripture/create_object/replicant, /datum/clockwork_scripture/create_object/tinkerers_cache, \
/datum/clockwork_scripture/spatial_gateway, /datum/clockwork_scripture/fellowship_armory, /datum/clockwork_scripture/create_object/clockwork_obelisk)
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/create_object/replicant, /datum/clockwork_scripture/create_object/stargazer, \
/datum/clockwork_scripture/spatial_gateway, /datum/clockwork_scripture/create_object/clockwork_obelisk)
/obj/item/clockwork/slab/cyborg/miner //two scriptures, plus a spear and xray vision
quickbound = list(/datum/clockwork_scripture/ranged_ability/linked_vanguard, /datum/clockwork_scripture/spatial_gateway)
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/ranged_ability/linked_vanguard, /datum/clockwork_scripture/spatial_gateway)
/obj/item/clockwork/slab/cyborg/access_display(mob/living/user)
if(!GLOB.ratvar_awakens)
@@ -92,7 +81,9 @@
. = ..()
update_slab_info(src)
START_PROCESSING(SSobj, src)
production_time = world.time + SLAB_PRODUCTION_TIME
if(GLOB.ratvar_approaches)
name = "supercharged [name]"
speed_multiplier = max(0.1, speed_multiplier - 0.25)
/obj/item/clockwork/slab/Destroy()
STOP_PROCESSING(SSobj, src)
@@ -115,35 +106,12 @@
if(user && !(src in user.held_items) && slab_ability && slab_ability.ranged_ability_user) //if we happen to check and we AREN'T in user's hands, remove whatever ability we have
slab_ability.remove_ranged_ability()
//Component Generation
//Power generation
/obj/item/clockwork/slab/process()
if(!produces_components)
STOP_PROCESSING(SSobj, src)
return
if(production_time > world.time)
return
var/servants = 0
var/production_slowdown = 0
for(var/mob/living/M in GLOB.living_mob_list)
if(is_servant_of_ratvar(M) && (ishuman(M) || issilicon(M)))
servants++
if(servants > SCRIPT_SERVANT_REQ)
servants -= SCRIPT_SERVANT_REQ
production_slowdown = min(SLAB_SERVANT_SLOWDOWN * servants, SLAB_SLOWDOWN_MAXIMUM) //SLAB_SERVANT_SLOWDOWN additional seconds for each servant above 5, up to SLAB_SLOWDOWN_MAXIMUM
production_time = world.time + SLAB_PRODUCTION_TIME + production_slowdown
var/mob/living/L
L = get_atom_on_turf(src, /mob/living)
if(istype(L) && (no_cost || can_recite_scripture(L)))
var/component_to_generate = target_component_id
if(!component_to_generate)
component_to_generate = get_weighted_component_id(src) //more likely to generate components that we have less of
stored_components[component_to_generate]++
update_slab_info(src)
for(var/obj/item/clockwork/slab/S in L.GetAllContents()) //prevent slab abuse today
if(S == src)
continue
S.production_time = production_time + 50 //set it to our next production plus five seconds, so that if you hold the same slabs, the same one will always generate
to_chat(L, "<span class='warning'>Your slab cl[pick("ank", "ink", "unk", "ang")]s as it produces a </span><span class='[get_component_span(component_to_generate)]'>component</span><span class='warning'>.</span>")
if(GLOB.ratvar_approaches && speed_multiplier == initial(speed_multiplier))
name = "supercharged [name]"
speed_multiplier = max(0.1, speed_multiplier - 0.25)
adjust_clockwork_power(0.1) //Slabs serve as very weak power generators on their own (no, not enough to justify spamming them)
/obj/item/clockwork/slab/examine(mob/user)
..()
@@ -154,97 +122,14 @@
continue
var/datum/clockwork_scripture/quickbind_slot = quickbound[i]
to_chat(user, "<b>Quickbind</b> button: <span class='[get_component_span(initial(quickbind_slot.primary_component))]'>[initial(quickbind_slot.name)]</span>.")
if(GLOB.clockwork_caches) //show components on examine
to_chat(user, "<b>Stored components (with global cache):</b>")
for(var/i in stored_components)
to_chat(user, "[get_component_icon(i)] <span class='[get_component_span(i)]_small'><i>[get_component_name(i)][i != REPLICANT_ALLOY ? "s":""]:</i> <b>[stored_components[i]]</b> \
(<b>[stored_components[i] + GLOB.clockwork_component_cache[i]]</b>)</span>")
else
to_chat(user, "<b>Stored components:</b>")
for(var/i in stored_components)
to_chat(user, "[get_component_icon(i)] <span class='[get_component_span(i)]_small'><i>[get_component_name(i)][i != REPLICANT_ALLOY ? "s":""]:</i> <b>[stored_components[i]]</b></span>")
//Component Transferal
/obj/item/clockwork/slab/attack(mob/living/target, mob/living/carbon/human/user)
if(is_servant_of_ratvar(user) && is_servant_of_ratvar(target))
var/obj/item/clockwork/slab/targetslab
var/highest_component_amount = 0
for(var/obj/item/clockwork/slab/S in target.GetAllContents())
if(!istype(S, /obj/item/clockwork/slab/internal))
var/totalcomponents = 0
for(var/i in S.stored_components)
totalcomponents += S.stored_components[i]
if(!targetslab || totalcomponents > highest_component_amount)
highest_component_amount = totalcomponents
targetslab = S
if(targetslab)
if(targetslab == src)
to_chat(user, "<span class='heavy_brass'>\"You can't transfer components into your own slab, idiot.\"</span>")
else
for(var/i in stored_components)
targetslab.stored_components[i] += stored_components[i]
stored_components[i] = 0
update_slab_info(targetslab)
update_slab_info(src)
user.visible_message("<span class='notice'>[user] empties [src] into [target]'s [targetslab.name].</span>", \
"<span class='notice'>You transfer your slab's components into [target]'s [targetslab.name].</span>")
else
to_chat(user, "<span class='warning'>[target] has no slabs to transfer components to.</span>")
else
return ..()
/obj/item/clockwork/slab/attackby(obj/item/I, mob/user, params)
var/ratvarian = is_servant_of_ratvar(user)
if(istype(I, /obj/item/clockwork/component) && ratvarian)
var/obj/item/clockwork/component/C = I
if(!C.component_id)
return 0
user.visible_message("<span class='notice'>[user] inserts [C] into [src].</span>", "<span class='notice'>You insert [C] into [src]\
[GLOB.clockwork_caches ? ", where it is added to the global cache":""].</span>")
if(GLOB.clockwork_caches)
GLOB.clockwork_component_cache[C.component_id]++
update_slab_info()
else
stored_components[C.component_id]++
update_slab_info(src)
user.drop_item()
qdel(C)
return 1
else if(istype(I, /obj/item/clockwork/slab) && ratvarian)
var/obj/item/clockwork/slab/S = I
var/needs_update = FALSE
for(var/i in stored_components)
stored_components[i] += S.stored_components[i]
S.stored_components[i] = 0
if(S.stored_components[i])
needs_update = TRUE
if(needs_update)
update_slab_info(src)
update_slab_info(S)
user.visible_message("<span class='notice'>[user] empties [src] into [S].</span>", "<span class='notice'>You transfer your slab's components into [S].</span>")
else
return ..()
to_chat(user, "<b>Available Power:</b> <span class='bold brass'>[DisplayPower(get_clockwork_power())]</span>")
//Slab actions; Hierophant, Quickbind
/obj/item/clockwork/slab/ui_action_click(mob/user, action)
if(istype(action, /datum/action/item_action/clock/hierophant))
show_hierophant(user)
else if(istype(action, /datum/action/item_action/clock/quickbind))
if(istype(action, /datum/action/item_action/clock/quickbind))
var/datum/action/item_action/clock/quickbind/Q = action
recite_scripture(quickbound[Q.scripture_index], user, FALSE)
/obj/item/clockwork/slab/proc/show_hierophant(mob/living/user)
if(!user.can_speak_vocal())
to_chat(user, "<span class='warning'>You cannot speak into the slab!</span>")
return FALSE
var/message = stripped_input(user, "Enter a message to send to your fellow Servants.", "Hierophant")
if(!message || !user || !user.canUseTopic(src) || !user.can_speak_vocal())
return FALSE
clockwork_say(user, text2ratvar("Servants, hear my words: [html_decode(message)]"), TRUE)
log_talk(user,"CLOCK:[key_name(user)] : [message]",LOGSAY)
titled_hierophant_message(user, message)
return TRUE
//Scripture Recital
/obj/item/clockwork/slab/attack_self(mob/living/user)
if(iscultist(user))
@@ -310,34 +195,10 @@
textlist += "HONOR RATVAR "
textlist += "</b></font>"
else
var/servants = 0
var/production_time = SLAB_PRODUCTION_TIME
for(var/mob/living/M in GLOB.living_mob_list)
if(is_servant_of_ratvar(M) && (ishuman(M) || issilicon(M)))
servants++
if(servants > SCRIPT_SERVANT_REQ)
servants -= SCRIPT_SERVANT_REQ
production_time += min(SLAB_SERVANT_SLOWDOWN * servants, SLAB_SLOWDOWN_MAXIMUM)
var/production_text_addon = ""
if(production_time != SLAB_PRODUCTION_TIME+SLAB_SLOWDOWN_MAXIMUM)
production_text_addon = ", which increases for each human or silicon Servant above <b>[SCRIPT_SERVANT_REQ]</b>"
production_time = production_time/600
var/list/production_text
if(round(production_time))
production_text = list("<b>[round(production_time)] minute\s")
if(production_time != round(production_time))
production_time -= round(production_time)
production_time *= 60
if(!LAZYLEN(production_text))
production_text = list("<b>[round(production_time, 1)] second\s")
else
production_text += " and [round(production_time, 1)] second\s"
production_text += "</b>"
production_text += production_text_addon
production_text = production_text.Join()
textlist = list("<font color=#BE8700 size=3><b><center>[text2ratvar("Purge all untruths and honor Engine.")]</center></b></font><br>\
\
<b><i>NOTICE:</b> This information is out of date. Read the Ark & You primer in your backpack or read the wiki page for current info.</i><br>\
<hr><br>\
These pages serve as the archives of Ratvar, the Clockwork Justiciar. This section of your slab has information on being as a Servant, advice for what to do next, and \
pointers for serving the master well. You should recommended that you check this area for help if you get stuck or need guidance on what to do next.<br><br>\
\
@@ -402,11 +263,6 @@
dat += "<font color=#BE8700><b>CV:</b></font> Construction Value. All clockwork structures, floors, and walls increase this number.<br>"
dat += "<font color=#BE8700><b>Vitality:</b></font> Used for healing effects, produced by Ratvarian spear attacks and Vitality Matrices.<br>"
dat += "<font color=#BE8700><b>Geis:</b></font> An important scripture used to make normal crew and robots into Servants of Ratvar.<br>"
dat += "<font color=#6E001A><b>[get_component_icon(BELLIGERENT_EYE)]BE:</b></font> Belligerent Eye, a component type used in offensive scriptures.<br>"
dat += "<font color=#1E8CE1><b>[get_component_icon(VANGUARD_COGWHEEL)]VC:</b></font> Vanguard Cogwheel, a component type used in defensive scriptures.<br>"
dat += "<font color=#AF0AAF><b>[get_component_icon(GEIS_CAPACITOR)]GC:</b></font> Geis Capacitor, a component type used in mind-related scriptures.<br>"
dat += "<font color=#5A6068><b>[get_component_icon(REPLICANT_ALLOY)]RA:</b></font> Replicant Alloy, a component type used in construction scriptures.<br>"
dat += "<font color=#DAAA18><b>[get_component_icon(HIEROPHANT_ANSIBLE)]HA:</b></font> Hierophant Ansible, a component type used in energy-related scriptures.<br>"
dat += "<font color=#BE8700><b>Ark:</b></font> The cult's win condition, a huge structure that needs to be defended.<br><br>"
dat += "<font color=#BE8700 size=3>Items</font><br>"
dat += "<font color=#BE8700><b>Slab:</b></font> A clockwork slab, a Servant's most important tool. You're holding one! Keep it safe and hidden.<br>"
@@ -431,42 +287,12 @@
dat += "<font color=#BE8700><b>Transmission:</b></font> Drains and stores power for clockwork structures. Feeding it brass sheets will create additional power.<br><br>"
dat += "<font color=#BE8700 size=3>-=-=-=-=-=-</font>"
if("Components")
var/servants = 0 //Calculate the current production time for slab components
var/production_time = SLAB_PRODUCTION_TIME
for(var/mob/living/M in GLOB.living_mob_list)
if(is_servant_of_ratvar(M) && (ishuman(M) || issilicon(M)))
servants++
if(servants > SCRIPT_SERVANT_REQ)
servants -= SCRIPT_SERVANT_REQ
production_time += min(SLAB_SERVANT_SLOWDOWN * servants, SLAB_SLOWDOWN_MAXIMUM)
var/production_text_addon = ""
if(production_time != SLAB_PRODUCTION_TIME+SLAB_SLOWDOWN_MAXIMUM)
production_text_addon = ", which increases for each human or silicon Servant above <b>[SCRIPT_SERVANT_REQ]</b>"
production_time = production_time/600
var/list/production_text
if(round(production_time))
production_text = list("<b>[round(production_time)] minute\s")
if(production_time != round(production_time))
production_time -= round(production_time)
production_time *= 60
if(!LAZYLEN(production_text))
production_text = list("<b>[round(production_time, 1)] second\s")
else
production_text += " and [round(production_time, 1)] second\s"
production_text += "</b>"
production_text += production_text_addon
production_text = production_text.Join()
dat += "<font color=#BE8700 size=3>Components & Their Uses</font><br><br>"
dat += "<b>Components</b> are your primary resource as a Servant. There are five types of component, with each one being used in different roles:<br><br>"
dat += "<font color=#6E001A>[get_component_icon(BELLIGERENT_EYE)]BE</font> Belligerent Eyes are aggressive and judgemental, and are used in offensive scripture;<br>"
dat += "<font color=#1E8CE1>[get_component_icon(VANGUARD_COGWHEEL)]VC</font> Vanguard Cogwheels are defensive and repairing, and are used in defensive scripture;<br>"
dat += "<font color=#AF0AAF>[get_component_icon(GEIS_CAPACITOR)]GC</font> Geis Capacitors are for conversion and control, and are used in mind-related scripture;<br>"
dat += "<font color=#5A6068>[get_component_icon(REPLICANT_ALLOY)]RA</font> Replicant Alloy is a strong, malleable metal and is used for construction and creation;<br>"
dat += "<font color=#DAAA18>[get_component_icon(HIEROPHANT_ANSIBLE)]HA</font> Hierophant Ansibles are for transmission and power, and are used in power and teleportation scripture<br><br>"
dat += "Although this is a good rule of thumb, their effects become much more nuanced when used together. For instance, a turret might have both belligerent eyes and \
vanguard cogwheels as construction requirements, because it defends its allies by harming its enemies.<br><br>"
dat += "Components' primary use is fueling <b>scripture</b> (covered in its own section), and they can be created through various ways. This clockwork slab, for instance, \
will make a random component of every type - or a specific one, if you choose a target component from the interface - every <b>[production_text]</b>. This number will increase \
will make a random component of every type - or a specific one, if you choose a target component from the interface - every <b>remove me already</b>. This number will increase \
as the amount of Servants in the covenant increase; additionally, slabs can only produce components when held by a Servant, and holding more than one slab will cause both \
of them to halt progress until one of them is removed from their person.<br><br>"
dat += "Your slab has an internal storage of components, but it isn't meant to be the main one. Instead, there's a <b>global storage</b> of components that can be \
@@ -487,7 +313,7 @@
dat += "It should also be noted that some scripture cannot be recited alone. Especially with more powerful scripture, you may need multiple Servants to recite a piece of \
scripture; both of you will need to stand still until the recital completes. <i>Only human and silicon Servants are valid for scripture recital!</i> Constructs cannot help \
in reciting scripture.<br><br>"
dat += "Finally, scripture is separated into four \"tiers\" based on power: Drivers, Scripts, Applications, and Judgement.[prob(1) ? " (The Revenant tier was removed a long time ago. \
dat += "Finally, scripture is separated into three \"tiers\" based on power: Drivers, Scripts, and Applications.[prob(1) ? " (The Revenant tier was removed a long time ago. \
Get with the times.)" : ""] You can view the requirements to unlock each tier in its scripture list. Once a tier is unlocked, it's unlocked permanently; the cult only needs to fill the \
requirement for unlocking a tier once!<br><br>"
dat += "<font color=#BE8700 size=3>-=-=-=-=-=-</font>"
@@ -551,7 +377,7 @@
One of the cogscarabs must've misplaced this section, because the game wasn't able to find any info regarding it. Report this to the coders!"
return "<br><br>[dat.Join()]<br><br>"
//Gets the quickbound scripture as a text block."
//Gets the quickbound scripture as a text block.
/obj/item/clockwork/slab/proc/get_recollection_quickbinds()
var/list/dat = list()
dat += "<font color=#BE8700 size=3>Quickbound Scripture</font><br>\
@@ -568,24 +394,7 @@
/obj/item/clockwork/slab/ui_data(mob/user) //we display a lot of data via TGUI
var/list/data = list()
data["components"] = stored_components.Copy()
var/list/temp_data = list("<font color=#B18B25>")
for(var/i in data["components"]) //display the slab's components
temp_data += "<font color=[get_component_color_bright(i)]>[get_component_icon(i)] <b>[data["components"][i]]</b></font>"
if(i != HIEROPHANT_ANSIBLE)
temp_data += " "
else
temp_data += " ("
if(GLOB.clockwork_caches) //if we have caches, display what's in the global cache
for(var/i in GLOB.clockwork_component_cache)
temp_data += "<font color=[get_component_color_bright(i)]>[get_component_icon(i)] <b>[data["components"][i] + GLOB.clockwork_component_cache[i]]</b></font>"
if(i != HIEROPHANT_ANSIBLE)
temp_data += " "
else
temp_data += "<b>NONE</b>"
temp_data += ")</font>"
temp_data = temp_data.Join()
data["components"] = temp_data
data["power"] = "<b><font color=#B18B25>[DisplayPower(get_clockwork_power())]</b> power is available for scripture and other consumers.</font>"
switch(selected_scripture) //display info based on selected scripture tier
if(SCRIPTURE_DRIVER)
@@ -594,24 +403,15 @@
if(SSticker.scripture_states[SCRIPTURE_SCRIPT])
data["tier_info"] = "<font color=#B18B25><b>These scriptures are permenantly unlocked.</b></font>"
else
data["tier_info"] = "<font color=#B18B25><i>These scriptures require at least <b>[SCRIPT_SERVANT_REQ]</b> Servants and <b>[SCRIPT_CACHE_REQ]</b> Tinkerer's Cache.</i></font>"
data["tier_info"] = "<font color=#B18B25><i>These scriptures will automatically unlock when the Ark is halfway ready or if [DisplayPower(SCRIPT_UNLOCK_THRESHOLD)] of power is reached.</i></font>"
if(SCRIPTURE_APPLICATION)
if(SSticker.scripture_states[SCRIPTURE_APPLICATION])
data["tier_info"] = "<font color=#B18B25><b>These scriptures are permenantly unlocked.</b></font>"
else
data["tier_info"] = "<font color=#B18B25><i>These scriptures require at least <b>[APPLICATION_SERVANT_REQ]</b> Servants, <b>[APPLICATION_CACHE_REQ]</b> Tinkerer's Caches, and <b>[APPLICATION_CV_REQ]CV</b>.</i></font>"
if(SCRIPTURE_JUDGEMENT)
if(SSticker.scripture_states[SCRIPTURE_JUDGEMENT])
data["tier_info"] = "<font color=#B18B25><b>This scripture is permenantly unlocked.</b></font>"
else
data["tier_info"] = "<font color=#B18B25><i>This scripture requires at least <b>[JUDGEMENT_SERVANT_REQ]</b> Servants, <b>[JUDGEMENT_CACHE_REQ]</b> Tinkerer's Caches, and <b>[JUDGEMENT_CV_REQ]CV</b>.<br>In addition, there may not be any active non-Servant AIs.</i></font>"
data["tier_info"] = "<font color=#B18B25><i>Unlock these optional scriptures by converting another servant or if [DisplayPower(APPLICATION_UNLOCK_THRESHOLD)] of power is reached..</i></font>"
data["selected"] = selected_scripture
data["target_comp"] = "<font color=#B18B25>NONE</font>"
if(target_component_id) //if we have a component to make, display that, too
data["target_comp"] = "<font color=[get_component_color_bright(target_component_id)]>[get_component_icon(target_component_id)]</font>"
generate_all_scripture()
data["scripture"] = list()
@@ -622,7 +422,7 @@
var/list/temp_info = list("name" = "<font color=[scripture_color]><b>[S.name]</b></font>",
"descname" = "<font color=[scripture_color]>([S.descname])</font>",
"tip" = "[S.desc]\n[S.usage_tip]",
"required" = list(BELLIGERENT_EYE = 0, VANGUARD_COGWHEEL = 0, GEIS_CAPACITOR = 0, REPLICANT_ALLOY = 0, HIEROPHANT_ANSIBLE = 0),
"required" = "([DisplayPower(S.power_cost)][S.special_power_text ? "+ [replacetext(S.special_power_text, "POWERCOST", "[DisplayPower(S.special_power_cost)]")]" : ""])",
"type" = "[S.type]",
"quickbind" = S.quickbind)
var/found = quickbound.Find(S.type)
@@ -630,20 +430,6 @@
temp_info["bound"] = "<b>[found]</b>"
if(S.invokers_required > 1)
temp_info["invokers"] = "<font color=#B18B25>Invokers: <b>[S.invokers_required]</b></font>"
var/costs_components = FALSE
for(var/i in S.consumed_components)
if(S.consumed_components[i])
temp_info["required"][i] += S.consumed_components[i]
costs_components = TRUE
if(costs_components) //if we have a component cost, we'll need a : next to the recital button
var/list/really_temp_data = list(": ")
for(var/i in temp_info["required"])
if(temp_info["required"][i])
really_temp_data += "<font color=[get_component_color_bright(i)]>[get_component_icon(i)] <b>[temp_info["required"][i]]</b></font> "
really_temp_data = really_temp_data.Join()
temp_info["required"] = really_temp_data
else //and if we don't, we won't.
temp_info["required"] = ""
data["scripture"] += list(temp_info)
data["recollection"] = recollecting
if(recollecting)
@@ -669,16 +455,6 @@
INVOKE_ASYNC(src, .proc/recite_scripture, text2path(params["category"]), usr, FALSE)
if("select")
selected_scripture = params["category"]
if("component")
var/list/components = list("Random Components")
for(var/i in GLOB.clockwork_component_cache)
var/cache_components = 0
if(GLOB.clockwork_caches)
cache_components = GLOB.clockwork_component_cache[i]
components["[get_component_name(i)] [(cache_components + stored_components[i])]"] = i
var/input_component = input("Choose a component type.", "Target Component") as null|anything in components
if(input_component && !..())
target_component_id = components[input_component]
if("bind")
var/datum/clockwork_scripture/path = text2path(params["category"]) //we need a path and not a string
var/found_index = quickbound.Find(path)
@@ -723,14 +499,7 @@
Q.scripture_index = i
var/datum/clockwork_scripture/quickbind_slot = GLOB.all_scripture[quickbound[i]]
Q.name = "[quickbind_slot.name] ([Q.scripture_index])"
var/list/temp_desc = list()
for(var/c in quickbind_slot.consumed_components) //show how much the bound scripture costs
if(quickbind_slot.consumed_components[c])
temp_desc += "<font color=[get_component_color_bright(c)]>[get_component_icon(c)] <b>[quickbind_slot.consumed_components[c]]</b></font> "
if(LAZYLEN(temp_desc))
temp_desc += "<br>"
temp_desc += "[quickbind_slot.quickbind_desc]"
Q.desc = temp_desc.Join()
Q.desc = quickbind_slot.quickbind_desc
Q.button_icon_state = quickbind_slot.name
Q.UpdateButtonIcon()
if(isliving(loc))

View File

@@ -0,0 +1,15 @@
//This is the base type for clockwork melee weapons.
/obj/item/clockwork/weapon
name = "clockwork weapon"
desc = "Weaponized brass. Whould've thunk it?"
clockwork_desc = "This shouldn't exist. Report it to a coder."
icon = 'icons/obj/clockwork_objects.dmi'
lefthand_file = 'icons/mob/inhands/antag/clockwork_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/clockwork_righthand.dmi'
var/datum/action/innate/call_weapon/action //Some melee weapons use an action that lets you return and dismiss them
/obj/item/clockwork/weapon/Initialize(mapload, new_action)
. = ..()
if(new_action)
action = new_action
action.weapon = src

View File

@@ -0,0 +1,69 @@
//Construct shells that can be activated by ghosts.
/obj/item/clockwork/construct_chassis
name = "construct chassis"
desc = "A shell formed out of brass, presumably for housing machinery."
clockwork_desc = "A construct chassis. It can be activated at any time by a willing ghost."
var/construct_name = "basic construct"
var/construct_desc = "<span class='alloy'>There is no construct for this chassis. Report this to a coder.</span>"
icon_state = "anime_fragment"
resistance_flags = FIRE_PROOF | ACID_PROOF
w_class = WEIGHT_CLASS_HUGE
var/creation_message = "<span class='brass'>The chassis shudders and hums to life!</span>"
var/construct_type //The construct this shell will create
/obj/item/clockwork/construct_chassis/Initialize()
. = ..()
var/area/A = get_area(src)
if(A && construct_type)
notify_ghosts("A [construct_name] chassis has been created in [A.name]!", 'sound/magic/clockwork/fellowship_armory.ogg', source = src, action = NOTIFY_ORBIT, flashwindow = FALSE)
GLOB.poi_list += src
/obj/item/clockwork/construct_chassis/Destroy()
GLOB.poi_list -= src
. = ..()
/obj/item/clockwork/construct_chassis/examine(mob/user)
clockwork_desc = "[clockwork_desc]<br>[construct_desc]"
..()
clockwork_desc = initial(clockwork_desc)
/obj/item/clockwork/construct_chassis/attack_hand(mob/living/user)
if(w_class >= WEIGHT_CLASS_HUGE)
to_chat(user, "<span class='warning'>[src] is too cumbersome to carry! Drag it around instead!</span>")
return
. = ..()
/obj/item/clockwork/construct_chassis/attack_ghost(mob/user)
if(alert(user, "Become a [construct_name]? You can no longer be cloned!", construct_name, "Yes", "Cancel") == "Cancel")
return
if(QDELETED(src))
to_chat(user, "<span class='danger'>You were too late! Better luck next time.</span>")
return
visible_message(creation_message)
var/mob/living/construct = new construct_type(get_turf(src))
construct.key = user.key
qdel(user)
qdel(src)
//Marauder armor, used to create clockwork marauders - sturdy frontline combatants that can deflect projectiles.
/obj/item/clockwork/construct_chassis/clockwork_marauder
name = "marauder armor"
desc = "A pile of sleek and well-polished brass armor. A small red gemstone sits in its faceplate."
icon_state = "marauder_armor"
construct_name = "clockwork marauder"
construct_desc = "<span class='neovgre_small'>It will become a <b>clockwork marauder,</b> a well-rounded frontline combatant.</span>"
creation_message = "<span class='neovgre_small bold'>Crimson fire begins to rage in the armor as it rises into the air with its arnaments!</span>"
construct_type = /mob/living/simple_animal/hostile/clockwork/marauder
//Cogscarab shell, used to create cogcarabs - fragile but zippy little drones that build and maintain the base.
/obj/item/clockwork/construct_chassis/cogscarab
name = "cogscarab shell"
desc = "A small, complex shell that resembles a repair drone, but much larger and made out of brass."
icon_state = "cogscarab_shell"
construct_name = "cogscarab"
construct_desc = "<span class='alloy'>It will become a <b>cogscarab,</b> a small and fragile drone that builds, repairs, and maintains.</span>"
creation_message = "<span class='alloy bold'>The cogscarab clicks and whirrs as it hops up and springs to life!</span>"
construct_type = /mob/living/simple_animal/drone/cogscarab
w_class = WEIGHT_CLASS_SMALL

View File

@@ -0,0 +1,38 @@
#define COG_MAX_SIPHON_THRESHOLD 0.25 //The cog will not siphon power if the APC's cell is at this % of power
//Can be used on an open APC to replace its guts with clockwork variants, and begin passively siphoning power from it
/obj/item/clockwork/integration_cog
name = "integration cog"
desc = "A small cogwheel that fits in the palm of your hand."
clockwork_desc = "A small cogwheel that can be inserted into an open APC to siphon power from it passively.<br>\
<span class='brass'>It can be used on a locked APC to open its cover!</span><br>\
<span class='brass'>Siphons <b>5 W</b> of power per second while in an APC.</span>"
icon_state = "wall_gear"
w_class = WEIGHT_CLASS_TINY
flags_1 = NOBLUDGEON_1
var/obj/machinery/power/apc/apc
/obj/item/clockwork/integration_cog/Initialize()
. = ..()
transform *= 0.5 //little cog!
/obj/item/clockwork/integration_cog/Destroy()
STOP_PROCESSING(SSfastprocess, src)
. = ..()
/obj/item/clockwork/integration_cog/process()
if(!apc)
if(istype(loc, /obj/machinery/power/apc))
apc = loc
else
STOP_PROCESSING(SSfastprocess, src)
else
var/obj/item/stock_parts/cell/cell = apc.cell
if(cell && (cell.charge / cell.maxcharge > COG_MAX_SIPHON_THRESHOLD))
cell.use(1)
adjust_clockwork_power(1) //Power is shared, so only do it once; this runs very quickly so it's about 1W/second
if(prob(1))
playsound(apc, 'sound/machines/clockcult/steam_whoosh.ogg', 10, TRUE)
new/obj/effect/temp_visual/steam(get_turf(apc), pick(GLOB.cardinals))
#undef COG_MAX_SIPHON_THRESHOLD

View File

@@ -170,7 +170,7 @@
if(!QDELETED(B))
B.duration = world.time + 30
C.Knockdown(5) //knocks down for half a second if affected
sleep(16)
sleep(!GLOB.ratvar_approaches ? 16 : 10)
name = "judicial blast"
layer = ABOVE_ALL_MOB_LAYER
flick("judicial_explosion", src)

View File

@@ -9,17 +9,9 @@
w_class = WEIGHT_CLASS_NORMAL
force = 5
flags_1 = NOBLUDGEON_1
var/stored_power = 0 //Requires power to function
var/max_power = CLOCKCULT_POWER_UNIT * 10
var/speed_multiplier = 1 //The speed ratio the fabricator operates at
var/uses_power = TRUE
var/repairing = null //what we're currently repairing, if anything
var/obj/effect/clockwork/sigil/transmission/recharging = null //the sigil we're charging from, if any
var/speed_multiplier = 1 //how fast this fabricator works
var/charge_rate = MIN_CLOCKCULT_POWER //how much power we gain every two seconds
var/charge_delay = 2 //how many proccess ticks remain before we can start to charge
/obj/item/clockwork/replica_fabricator/preloaded
stored_power = POWER_WALL_MINUS_FLOOR+POWER_WALL_TOTAL
/obj/item/clockwork/replica_fabricator/scarab
name = "scarab fabricator"
@@ -27,7 +19,6 @@
item_state = "nothing"
w_class = WEIGHT_CLASS_TINY
speed_multiplier = 0.5
charge_rate = MIN_CLOCKCULT_POWER * 2
var/debug = FALSE
/obj/item/clockwork/replica_fabricator/scarab/fabricate(atom/target, mob/living/user)
@@ -42,75 +33,7 @@
/obj/item/clockwork/replica_fabricator/cyborg
name = "cyborg fabricator"
clockwork_desc = "A cyborg's internal fabricator. It is capable of using the cyborg's power in addition to stored power."
/obj/item/clockwork/replica_fabricator/cyborg/get_power() //returns power and cyborg's power
var/mob/living/silicon/robot/R = get_atom_on_turf(src, /mob/living)
var/borg_power = 0
var/current_charge = 0
if(istype(R) && R.cell)
current_charge = R.cell.charge
while(current_charge > MIN_CLOCKCULT_POWER)
current_charge -= MIN_CLOCKCULT_POWER
borg_power += MIN_CLOCKCULT_POWER
return ..() + borg_power
/obj/item/clockwork/replica_fabricator/cyborg/get_max_power()
var/mob/living/silicon/robot/R = get_atom_on_turf(src, /mob/living)
var/cell_maxcharge = 0
if(istype(R) && R.cell)
cell_maxcharge = R.cell.maxcharge
return ..() + cell_maxcharge
/obj/item/clockwork/replica_fabricator/cyborg/can_use_power(amount)
if(amount != RATVAR_POWER_CHECK)
var/mob/living/silicon/robot/R = get_atom_on_turf(src, /mob/living)
var/current_charge = 0
if(istype(R) && R.cell)
current_charge = R.cell.charge
while(amount > 0 && stored_power - amount < 0) //amount is greater than 0 and stored power minus the amount is still less than 0
current_charge -= MIN_CLOCKCULT_POWER
amount -= MIN_CLOCKCULT_POWER
if(current_charge < 0)
return FALSE
. = ..()
/obj/item/clockwork/replica_fabricator/cyborg/modify_stored_power(amount)
var/mob/living/silicon/robot/R = get_atom_on_turf(src, /mob/living)
if(istype(R) && R.cell && amount)
if(amount < 0)
while(amount < 0 && stored_power + amount < 0) //amount is less than 0 and stored alloy plus the amount is less than 0
R.cell.use(MIN_CLOCKCULT_POWER)
amount += MIN_CLOCKCULT_POWER
else
while(amount > 0 && R.cell.charge + MIN_CLOCKCULT_POWER < R.cell.maxcharge) //amount is greater than 0 and cell charge plus MIN_CLOCKCULT_POWER is less than maximum cell charge
R.cell.give(MIN_CLOCKCULT_POWER)
amount -= MIN_CLOCKCULT_POWER
. = ..()
/obj/item/clockwork/replica_fabricator/Initialize()
. = ..()
START_PROCESSING(SSobj, src)
/obj/item/clockwork/replica_fabricator/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/clockwork/replica_fabricator/process()
if(!charge_rate)
return
var/mob/living/L = get_atom_on_turf(src, /mob/living)
if(istype(L) && is_servant_of_ratvar(L))
if(charge_delay)
charge_delay--
return
modify_stored_power(charge_rate)
for(var/obj/item/clockwork/replica_fabricator/S in L.GetAllContents()) //no multiple fabricators
if(S == src)
continue
S.charge_delay = 2
else
charge_delay = 2
clockwork_desc = "A cyborg's internal fabricator."
/obj/item/clockwork/replica_fabricator/ratvar_act()
if(GLOB.ratvar_awakens)
@@ -129,45 +52,24 @@
to_chat(user, "<span class='alloy'>It can consume floor tiles, rods, metal, and plasteel for power at rates of <b>2:[DisplayPower(POWER_ROD)]</b>, <b>1:[DisplayPower(POWER_ROD)]</b>, <b>1:[DisplayPower(POWER_METAL)]</b>, \
and <b>1:[DisplayPower(POWER_PLASTEEL)]</b>, respectively.</span>")
to_chat(user, "<span class='alloy'>It can also consume brass sheets for power at a rate of <b>1:[DisplayPower(POWER_FLOOR)]</b>.</span>")
to_chat(user, "<span class='alloy'>It is storing <b>[DisplayPower(get_power())]/[DisplayPower(get_max_power())]</b> of power[charge_rate ? ", and is gaining <b>[DisplayPower(charge_rate*0.5)]</b> of power per second":""].</span>")
to_chat(user, "<span class='alloy'>Use it in-hand to produce <b>5</b> brass sheets at a cost of <b>[DisplayPower(POWER_WALL_TOTAL)]</b> power.</span>")
to_chat(user, "<span class='alloy'>It has access to <b>[DisplayPower(get_clockwork_power())]</b> of power.</span>")
/obj/item/clockwork/replica_fabricator/attack_self(mob/living/user)
if(is_servant_of_ratvar(user))
if(uses_power)
if(!can_use_power(POWER_WALL_TOTAL))
if(!get_clockwork_power(POWER_WALL_TOTAL))
to_chat(user, "<span class='warning'>[src] requires <b>[DisplayPower(POWER_WALL_TOTAL)]</b> of power to produce brass sheets!</span>")
return
modify_stored_power(-POWER_WALL_TOTAL)
adjust_clockwork_power(-POWER_WALL_TOTAL)
playsound(src, 'sound/items/deconstruct.ogg', 50, 1)
new/obj/item/stack/tile/brass(user.loc, 5)
to_chat(user, "<span class='brass'>You use [stored_power ? "some":"all"] of [src]'s power to produce <b>5</b> brass sheets. It now stores <b>[DisplayPower(get_power())]/[DisplayPower(get_max_power())]</b> of power.</span>")
to_chat(user, "<span class='brass'>You use [get_clockwork_power() ? "some":"all"] of [src]'s power to produce <b>5</b> brass sheets. It now has access to <b>[DisplayPower(get_clockwork_power())]</b> of power.</span>")
/obj/item/clockwork/replica_fabricator/pre_attackby(atom/target, mob/living/user, params)
if(!target || !user || !is_servant_of_ratvar(user) || istype(target, /obj/item/storage))
return TRUE
return fabricate(target, user)
/obj/item/clockwork/replica_fabricator/proc/get_power()
return stored_power
/obj/item/clockwork/replica_fabricator/proc/get_max_power()
return max_power
/obj/item/clockwork/replica_fabricator/proc/modify_stored_power(amount)
stored_power = Clamp(stored_power + amount, 0, max_power)
return TRUE
/obj/item/clockwork/replica_fabricator/proc/can_use_power(amount)
if(amount == RATVAR_POWER_CHECK)
if(GLOB.ratvar_awakens || !uses_power)
return TRUE
else
return FALSE
if(stored_power - amount < 0)
return FALSE
if(stored_power - amount > max_power)
return FALSE
return TRUE
//A note here; return values are for if we CAN BE PUT ON A TABLE, not IF WE ARE SUCCESSFUL, unless no_table_check is TRUE
@@ -178,10 +80,6 @@
if(!silent)
to_chat(user, "<span class='warning'>You are currently repairing [repairing] with [src]!</span>")
return FALSE
if(recharging)
if(!silent)
to_chat(user, "<span class='warning'>You are currently recharging [src] from the [recharging.sigil_name]!</span>")
return FALSE
var/list/fabrication_values = target.fabrication_vals(user, src, silent) //relevant values for fabricating stuff, given as an associated list
if(!islist(fabrication_values))
if(fabrication_values != TRUE) //if we get true, fail, but don't send a message for whatever reason
@@ -192,7 +90,7 @@
if(!no_table_check)
return TRUE
return FALSE
if(can_use_power(RATVAR_POWER_CHECK))
if(get_clockwork_power(RATVAR_POWER_CHECK))
fabrication_values["power_cost"] = 0
var/turf/Y = get_turf(user)
@@ -252,7 +150,7 @@
A.setDir(fabrication_values["spawn_dir"])
if(!fabrication_values["no_target_deletion"]) //for some cases where fabrication_vals() modifies the object but doesn't want it deleted
qdel(target)
modify_stored_power(-fabrication_values["power_cost"])
adjust_clockwork_power(-fabrication_values["power_cost"])
if(no_table_check)
return TRUE
return FALSE
@@ -265,25 +163,18 @@
/obj/item/clockwork/replica_fabricator/proc/fabricate_checks(list/fabrication_values, atom/target, expected_type, mob/user, silent) //checked constantly while fabricating
if(!islist(fabrication_values) || QDELETED(target) || QDELETED(user))
return FALSE
if(repairing || recharging)
if(repairing)
return FALSE
if(target.type != expected_type)
return FALSE
if(can_use_power(RATVAR_POWER_CHECK))
if(get_clockwork_power(RATVAR_POWER_CHECK))
fabrication_values["power_cost"] = 0
if(!can_use_power(fabrication_values["power_cost"]))
if(stored_power - fabrication_values["power_cost"] < 0)
if(!get_clockwork_power(fabrication_values["power_cost"]))
if(get_clockwork_power() - fabrication_values["power_cost"] < 0)
if(!silent)
var/atom/A = fabrication_values["new_obj_type"]
if(A)
to_chat(user, "<span class='warning'>You need <b>[DisplayPower(fabrication_values["power_cost"])]</b> power to fabricate \a [initial(A.name)] from [target]!</span>")
else if(stored_power - fabrication_values["power_cost"] > max_power)
if(!silent)
var/atom/A = fabrication_values["new_obj_type"]
if(A)
to_chat(user, "<span class='warning'>Your [name] contains too much power to fabricate \a [initial(A.name)] from [target]!</span>")
else
to_chat(user, "<span class='warning'>Your [name] contains too much power to consume [target]!</span>")
return FALSE
return TRUE
@@ -321,26 +212,9 @@
return FALSE
repair_values["healing_for_cycle"] = min(repair_values["amount_to_heal"], FABRICATOR_REPAIR_PER_TICK) //modify the healing for this cycle
repair_values["power_required"] = round(repair_values["healing_for_cycle"]*MIN_CLOCKCULT_POWER, MIN_CLOCKCULT_POWER) //and get the power cost from that
if(!can_use_power(RATVAR_POWER_CHECK) && !can_use_power(repair_values["power_required"]))
if(!get_clockwork_power(RATVAR_POWER_CHECK) && !get_clockwork_power(repair_values["power_required"]))
if(!silent)
to_chat(user, "<span class='warning'>You need at least <b>[DisplayPower(repair_values["power_required"])]</b> power to start repairin[target == user ? "g yourself" : "g [target]"], and at least \
<b>[DisplayPower(repair_values["amount_to_heal"]*MIN_CLOCKCULT_POWER, MIN_CLOCKCULT_POWER)]</b> to fully repair [target == user ? "yourself" : "[target.p_them()]"]!</span>")
return FALSE
return TRUE
//The sigil charge check proc.
/obj/item/clockwork/replica_fabricator/proc/sigil_charge_checks(list/charge_values, obj/effect/clockwork/sigil/transmission/sigil, mob/user, silent)
if(!islist(charge_values) || QDELETED(sigil) || QDELETED(user))
return FALSE
if(can_use_power(RATVAR_POWER_CHECK))
return FALSE
charge_values["power_gain"] = Clamp(sigil.power_charge, 0, POWER_WALL_MINUS_FLOOR)
if(!charge_values["power_gain"])
if(!silent)
to_chat(user, "<span class='warning'>The [sigil.sigil_name] contains no power!</span>")
return FALSE
if(stored_power + charge_values["power_gain"] > max_power)
if(!silent)
to_chat(user, "<span class='warning'>Your [name] contains too much power to charge from the [sigil.sigil_name]!</span>")
return FALSE
return TRUE

View File

@@ -23,6 +23,7 @@
autoping = FALSE
resistance_flags = FIRE_PROOF | ACID_PROOF
force_replace_ai_name = TRUE
overrides_aicore_laws = TRUE
/obj/item/device/mmi/posibrain/soul_vessel/Initialize()
. = ..()

View File

@@ -83,7 +83,7 @@
return
set_vision_vars(TRUE)
if(is_servant_of_ratvar(user))
to_chat(user, "<span class='heavy_brass'>As you put on the spectacles, all is revealed to you.[GLOB.ratvar_awakens ? "" : " Your eyes begin to itch - you cannot do this for long."]</span>")
to_chat(user, "<span class='heavy_brass'>As you put on the spectacles, all is revealed to you.[GLOB.ratvar_awakens || GLOB.ratvar_approaches ? "" : " Your eyes begin to itch - you cannot do this for long."]</span>")
var/datum/status_effect/wraith_spectacles/WS = user.has_status_effect(STATUS_EFFECT_WRAITHSPECS)
if(WS)
WS.apply_eye_damage(user)
@@ -138,7 +138,7 @@
var/mob/living/carbon/human/H = owner
var/glasses_right = istype(H.glasses, /obj/item/clothing/glasses/wraith_spectacles)
var/obj/item/clothing/glasses/wraith_spectacles/WS = H.glasses
if(glasses_right && !WS.up && !GLOB.ratvar_awakens)
if(glasses_right && !WS.up && !GLOB.ratvar_awakens && !GLOB.ratvar_approaches)
apply_eye_damage(H)
else
if(GLOB.ratvar_awakens)
@@ -148,15 +148,15 @@
eye_damage_done = 0
else if(prob(50) && eye_damage_done)
H.adjust_eye_damage(-1)
eye_damage_done--
eye_damage_done = max(0, eye_damage_done - 1)
if(!eye_damage_done)
qdel(src)
/datum/status_effect/wraith_spectacles/proc/apply_eye_damage(mob/living/carbon/human/H)
if(H.disabilities & BLIND)
return
H.adjust_eye_damage(1)
eye_damage_done++
H.adjust_eye_damage(0.5)
eye_damage_done += 0.5
if(eye_damage_done >= 20)
H.adjust_blurriness(2)
if(eye_damage_done >= nearsight_breakpoint)

View File

@@ -20,6 +20,11 @@
light_color = "#E42742"
death_sound = 'sound/magic/clockwork/anima_fragment_death.ogg'
var/playstyle_string = "<span class='heavy_brass'>You are a bug, yell at whoever spawned you!</span>"
var/empower_string = "<span class='heavy_brass'>You have nothing to empower, yell at the coders!</span>" //Shown to the mob when the herald beacon activates
/mob/living/simple_animal/hostile/clockwork/Initialize()
. = ..()
update_values()
/mob/living/simple_animal/hostile/clockwork/get_spans()
return ..() | SPAN_ROBOT
@@ -28,6 +33,8 @@
..()
add_servant_of_ratvar(src, TRUE)
to_chat(src, playstyle_string)
if(GLOB.ratvar_approaches)
to_chat(src, empower_string)
/mob/living/simple_animal/hostile/clockwork/ratvar_act()
fully_heal(TRUE)
@@ -50,3 +57,5 @@
msg += "*---------*</span>"
to_chat(user, msg)
/mob/living/simple_animal/hostile/clockwork/proc/update_values() //This is called by certain things to check GLOB.ratvar_awakens and GLOB.ratvar_approaches

View File

@@ -1,11 +1,12 @@
//Clockwork marauder: Slow but with high damage, resides inside of a servant. Created via the Memory Allocation scripture.
//Clockwork marauder: A well-rounded frontline construct. Only one can exist for every two human servants.
/mob/living/simple_animal/hostile/clockwork/marauder
name = "clockwork marauder"
desc = "A stalwart apparition of a soldier, blazing with crimson flames. It's armed with a gladius and shield."
desc = "The stalwart apparition of a soldier, blazing with crimson flames. It's armed with a gladius and shield."
icon_state = "clockwork_marauder"
health = 300
maxHealth = 300
speed = 1
health = 150
maxHealth = 150
force_threshold = 8
speed = 0
obj_damage = 40
melee_damage_lower = 12
melee_damage_upper = 12
@@ -14,145 +15,40 @@
weather_immunities = list("lava")
movement_type = FLYING
loot = list(/obj/item/clockwork/component/geis_capacitor/fallen_armor)
var/true_name = "Meme Master 69" //Required to call forth the marauder
var/global/list/possible_true_names = list("Servant", "Warden", "Serf", "Page", "Usher", "Knave", "Vassal", "Escort")
var/mob/living/host //The mob that the marauder is living inside of
var/recovering = FALSE //If the marauder is recovering from recalling
var/blockchance = 17 //chance to block attacks entirely
var/counterchance = 30 //chance to counterattack after blocking
var/static/list/damage_heal_order = list(OXY, BURN, BRUTE, TOX) //we heal our host's damage in this order
light_range = 2
light_power = 1.1
playstyle_string = "<span class='sevtug'>You are a clockwork marauder</span><b>, a living extension of Sevtug's will. As a marauder, you are somewhat slow, but may block attacks, \
and have a chance to also counter blocked melee attacks for extra damage, in addition to being immune to extreme temperatures and pressures. \
Your primary goal is to serve the creature that you are now a part of. You can use <span class='sevtug_small'><i>:b</i></span> to communicate silently with your master, \
but can only exit if your master calls your true name or if they are exceptionally damaged. \
\n\n\
Stay near your host to protect and heal them; being too far from your host will rapidly cause you massive damage. Recall to your host if you are too weak and believe you cannot continue \
fighting safely. As a final note, you should probably avoid harming any fellow servants of Ratvar.</span>"
playstyle_string = "<b><span class='neovgre'>You are a clockwork marauder,</span> a well-rounded frontline construct of Ratvar. Although you have no \
unique abilities, you're a fearsome fighter in one-on-one combat, and your shield protects from projectiles!<br><br>Obey the Servants and do as they \
tell you. Your primary goal is to defend the Ark from destruction; they are your allies in this, and should be protected from harm.</b>"
empower_string = "<span class='neovgre'>The Anima Bulwark's power flows through you! Your weapon will strike harder, your armor is sturdier, and your shield is considerably more \
likely to deflect shots.</span>"
var/deflect_chance = 40 //Chance to deflect any given projectile (non-damaging energy projectiles are always deflected)
/mob/living/simple_animal/hostile/clockwork/marauder/Initialize()
. = ..()
true_name = pick(possible_true_names)
/mob/living/simple_animal/hostile/clockwork/marauder/Life()
..()
if(is_in_host())
if(!is_servant_of_ratvar(host))
emerge_from_host(FALSE, TRUE)
unbind_from_host()
return
if(!GLOB.ratvar_awakens && host.stat == DEAD)
death()
return
if(GLOB.ratvar_awakens)
adjustHealth(-50)
else
adjustHealth(-10)
if(!recovering)
heal_host() //also heal our host if inside of them and we aren't recovering
else if(health == maxHealth)
to_chat(src, "<span class='userdanger'>Your strength has returned. You can once again come forward!</span>")
to_chat(host, "<span class='userdanger'>Your marauder is now strong enough to come forward again!</span>")
recovering = FALSE
else
if(GLOB.ratvar_awakens) //If Ratvar is alive, marauders don't need a host and are downright impossible to kill
adjustHealth(-5)
heal_host()
else if(host)
if(!is_servant_of_ratvar(host))
unbind_from_host()
return
if(host.stat == DEAD)
adjustHealth(50)
to_chat(src, "<span class='userdanger'>Your host is dead!</span>")
return
if(z && host.z && z == host.z)
switch(get_dist(get_turf(src), get_turf(host)))
if(2)
adjustHealth(-1)
if(3)
//EQUILIBRIUM
if(4)
adjustHealth(1)
if(5)
adjustHealth(3)
if(6)
adjustHealth(6)
if(7)
adjustHealth(9)
if(8 to INFINITY)
adjustHealth(15)
to_chat(src, "<span class='userdanger'>You're too far from your host and rapidly taking damage!</span>")
else //right next to or on top of host
adjustHealth(-2)
heal_host() //gradually heal host if nearby and host is very weak
else //well then, you're not even in the same zlevel
adjustHealth(15)
to_chat(src, "<span class='userdanger'>You're too far from your host and rapidly taking damage!</span>")
/mob/living/simple_animal/hostile/clockwork/marauder/update_values()
if(GLOB.ratvar_awakens) //Massive attack damage bonuses and health increase, because Ratvar
health = 300
maxHealth = 300
melee_damage_upper = 25
melee_damage_lower = 25
attacktext = "devastates"
speed = -1
obj_damage = 100
else if(GLOB.ratvar_approaches) //Hefty health bonus and slight attack damage increase
health = 200
maxHealth = 200
melee_damage_upper = 15
melee_damage_lower = 15
attacktext = "carves"
obj_damage = 50
/mob/living/simple_animal/hostile/clockwork/marauder/death(gibbed)
emerge_from_host(FALSE, TRUE)
unbind_from_host()
visible_message("<span class='warning'>[src]'s equipment clatters lifelessly to the ground as the red flames within dissipate.</span>", \
"<span class='userdanger'>Your equipment falls away. You feel a moment of confusion before your fragile form is annihilated.</span>")
visible_message("<span class='danger'>[src]'s equipment clatters lifelessly to the ground as the red flames within dissipate.</span>", \
"<span class='userdanger'>Dented and scratched, your armor falls away, and your fragile form breaks apart without its protection.</span>")
. = ..()
/mob/living/simple_animal/hostile/clockwork/marauder/Stat()
..()
if(statpanel("Status"))
stat(null, "Current True Name: [true_name]")
stat(null, "Host: [host ? host : "NONE"]")
if(host)
var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5)
if(iscarbon(host))
resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100)
stat(null, "Host Health: [resulthealth]%")
if(GLOB.ratvar_awakens)
stat(null, "You are [recovering ? "un" : ""]able to deploy!")
else
if(resulthealth > MARAUDER_EMERGE_THRESHOLD)
stat(null, "You are [recovering ? "unable to deploy" : "able to deploy on hearing your True Name"]!")
else
stat(null, "You are [recovering ? "unable to deploy" : "able to deploy to protect your host"]!")
stat(null, "You do [melee_damage_upper] damage on melee attacks.")
/mob/living/simple_animal/hostile/clockwork/marauder/Process_Spacemove(movement_dir = 0)
return 1
/mob/living/simple_animal/hostile/clockwork/marauder/proc/bind_to_host(mob/living/new_host)
if(!new_host)
return FALSE
host = new_host
var/datum/action/innate/summon_marauder/SM = new()
SM.linked_marauder = src
SM.Grant(host)
var/datum/action/innate/linked_minds/LM = new()
LM.linked_marauder = src
LM.Grant(host)
return TRUE
/mob/living/simple_animal/hostile/clockwork/marauder/proc/unbind_from_host()
if(host)
for(var/datum/action/innate/summon_marauder/SM in host.actions)
qdel(SM)
for(var/datum/action/innate/linked_minds/LM in host.actions)
qdel(LM)
host = null
return TRUE
return FALSE
//DAMAGE and FATIGUE
/mob/living/simple_animal/hostile/clockwork/marauder/proc/heal_host()
if(!host)
return
var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5)
if(iscarbon(host))
resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100)
if(GLOB.ratvar_awakens || resulthealth <= MARAUDER_EMERGE_THRESHOLD)
new /obj/effect/temp_visual/heal(host.loc, "#AF0AAF")
host.heal_ordered_damage(4, damage_heal_order)
/mob/living/simple_animal/hostile/clockwork/marauder/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
if(amount > 0)
for(var/mob/living/L in view(2, src))
@@ -161,269 +57,28 @@
amount *= 4 //if a wielded null rod is nearby, it takes four times the health damage
break
. = ..()
if(src && updating_health)
update_health_hud()
update_stats()
/mob/living/simple_animal/hostile/clockwork/marauder/update_health_hud()
if(hud_used && hud_used.healths)
if(istype(hud_used, /datum/hud/marauder))
var/datum/hud/marauder/M = hud_used
var/resulthealth
if(host)
if(iscarbon(host))
resulthealth = "[round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100)]%"
else
resulthealth = "[round((host.health / host.maxHealth) * 100, 0.5)]%"
else
resulthealth = "NONE"
M.hosthealth.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#AF0AAF'>HOST<br>[resulthealth]</font></div>"
hud_used.healths.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#AF0AAF'>[round((health / maxHealth) * 100, 0.5)]%</font>"
/mob/living/simple_animal/hostile/clockwork/marauder/bullet_act(obj/item/projectile/P)
if(deflect_projectile(P))
return
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/proc/update_stats()
/mob/living/simple_animal/hostile/clockwork/marauder/proc/deflect_projectile(obj/item/projectile/P)
var/final_deflection_chance = deflect_chance
var/energy_projectile = istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)
if(P.nodamage || P.damage_type == STAMINA)
final_deflection_chance = 100
else if(!energy_projectile) //Flat 40% chance against energy projectiles; ballistic projectiles are 40% - (damage of projectile)%, min. 10%
final_deflection_chance = max(10, deflect_chance - P.damage)
if(GLOB.ratvar_awakens)
speed = 0
melee_damage_lower = 20
melee_damage_upper = 20
attacktext = "devastates"
else
var/healthpercent = (health/maxHealth) * 100
switch(healthpercent)
if(100 to 70) //Bonuses to speed and damage at high health
speed = 0
melee_damage_lower = 16
melee_damage_upper = 16
attacktext = "viciously slashes"
if(70 to 40)
speed = initial(speed)
melee_damage_lower = initial(melee_damage_lower)
melee_damage_upper = initial(melee_damage_upper)
attacktext = initial(attacktext)
if(40 to 30) //Damage decrease, but not speed
speed = initial(speed)
melee_damage_lower = 10
melee_damage_upper = 10
attacktext = "lightly slashes"
if(30 to 20) //Speed decrease
speed = 2
melee_damage_lower = 8
melee_damage_upper = 8
attacktext = "lightly slashes"
if(20 to 10) //Massive speed decrease and weak melee attacks
speed = 3
melee_damage_lower = 6
melee_damage_upper = 6
attacktext = "weakly slashes"
if(10 to 0) //We are super weak and going to die
speed = 4
melee_damage_lower = 4
melee_damage_upper = 4
attacktext = "taps"
//ATTACKING, BLOCKING, and COUNTERING
/mob/living/simple_animal/hostile/clockwork/marauder/AttackingTarget()
if(is_in_host())
return FALSE
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/bullet_act(obj/item/projectile/Proj)
if(blockOrCounter(null, Proj))
return
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/hitby(atom/movable/AM, skipcatch, hitpush, blocked)
if(blockOrCounter(null, AM))
return
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/attack_animal(mob/living/simple_animal/M)
if(istype(M, /mob/living/simple_animal/hostile/clockwork/marauder) || !blockOrCounter(M, M)) //we don't want infinite blockcounter loops if fighting another marauder
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/attack_paw(mob/living/carbon/monkey/M)
if(!blockOrCounter(M, M))
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/attack_alien(mob/living/carbon/alien/humanoid/M)
if(!blockOrCounter(M, M))
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/attack_slime(mob/living/simple_animal/slime/M)
if(!blockOrCounter(M, M))
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/attack_hand(mob/living/carbon/human/M)
if(!blockOrCounter(M, M))
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/nullrod) || !blockOrCounter(user, I))
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/proc/blockOrCounter(mob/target, atom/textobject)
if(GLOB.ratvar_awakens) //if ratvar has woken, we block nearly everything at a very high chance
blockchance = 90
counterchance = 90
if(prob(blockchance))
final_deflection_chance = 100
else if(GLOB.ratvar_approaches)
final_deflection_chance = min(100, final_deflection_chance + 20) //20% bonus to deflection if the servants heralded Ratvar
if(prob(final_deflection_chance))
visible_message("<span class='danger'>[src] deflects [P] with their shield!</span>", \
"<span class='danger'>You block [P] with your shield!</span>")
if(energy_projectile)
playsound(src, 'sound/weapons/effects/searwall.ogg', 50, TRUE)
else
playsound(src, "ricochet", 50, TRUE)
. = TRUE
if(target)
target.do_attack_animation(src)
target.changeNext_move(CLICK_CD_MELEE)
blockchance = initial(blockchance)
playsound(src, 'sound/magic/clockwork/fellowship_armory.ogg', 30, 1, 0, 1) //clang
visible_message("<span class='boldannounce'>[src] blocks [target && isitem(textobject) ? "[target]'s [textobject.name]":"\the [textobject]"]!</span>", \
"<span class='userdanger'>You block [target && isitem(textobject) ? "[target]'s [textobject.name]":"\the [textobject]"]!</span>")
if(target && Adjacent(target))
if(prob(counterchance))
counterchance = initial(counterchance)
var/previousattacktext = attacktext
attacktext = "counters"
UnarmedAttack(target)
attacktext = previousattacktext
else
counterchance = min(counterchance + initial(counterchance), 100)
else
blockchance = min(blockchance + initial(blockchance), 100)
if(GLOB.ratvar_awakens)
blockchance = 90
counterchance = 90
//COMMUNICATION and EMERGENCE
/mob/living/simple_animal/hostile/clockwork/marauder/handle_inherent_channels(message, message_mode)
if(host && (is_in_host() || message_mode == MODE_BINARY))
marauder_comms(message)
return TRUE
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/proc/marauder_comms(message)
var/name_part = "<span class='sevtug'>[src] ([true_name])</span>"
message = "<span class='sevtug_small'>\"[message]\"</span>" //Processed output
to_chat(src, "[name_part]<span class='sevtug_small'>:</span> [message]")
to_chat(host, "[name_part]<span class='sevtug_small'>:</span> [message]")
for(var/M in GLOB.mob_list)
if(isobserver(M))
var/link = FOLLOW_LINK(M, src)
to_chat(M, "[link] [name_part] <span class='sevtug_small'>(to</span> <span class='sevtug'>[findtextEx(host.name, host.real_name) ? "[host.name]" : "[host.real_name] (as [host.name])"]</span><span class='sevtug_small'>):</span> [message] ")
return TRUE
/mob/living/simple_animal/hostile/clockwork/marauder/proc/return_to_host()
if(is_in_host())
return FALSE
if(!host)
to_chat(src, "<span class='warning'>You don't have a host!</span>")
return FALSE
var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5)
if(iscarbon(host))
resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100)
host.visible_message("<span class='warning'>[host]'s skin flashes crimson!</span>", "<span class='sevtug'>You feel [true_name]'s consciousness settle in your mind.</span>")
visible_message("<span class='warning'>[src] suddenly disappears!</span>", "<span class='sevtug'>You return to [host].</span>")
forceMove(host)
if(resulthealth > MARAUDER_EMERGE_THRESHOLD && health != maxHealth)
recovering = TRUE
to_chat(src, "<span class='userdanger'>You have weakened and will need to recover before manifesting again!</span>")
to_chat(host, "<span class='sevtug'>[true_name] has weakened and will need to recover before manifesting again!</span>")
return TRUE
/mob/living/simple_animal/hostile/clockwork/marauder/proc/try_emerge()
if(!host)
to_chat(src, "<span class='warning'>You don't have a host!</span>")
return FALSE
if(!GLOB.ratvar_awakens)
var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5)
if(iscarbon(host))
resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100)
if(host.stat != DEAD && resulthealth > MARAUDER_EMERGE_THRESHOLD) //if above 20 health, fails
to_chat(src, "<span class='warning'>Your host must be at [MARAUDER_EMERGE_THRESHOLD]% or less health to emerge like this!</span>")
return FALSE
return emerge_from_host(FALSE)
/mob/living/simple_animal/hostile/clockwork/marauder/proc/emerge_from_host(hostchosen, force) //Notice that this is a proc rather than a verb - marauders can NOT exit at will, but they CAN return
if(!is_in_host())
return FALSE
if(!force && recovering)
if(hostchosen)
to_chat(host, "<span class='sevtug'>[true_name] is too weak to come forth!</span>")
else
to_chat(host, "<span class='sevtug'>[true_name] tries to emerge to protect you, but it's too weak!</span>")
to_chat(src, "<span class='userdanger'>You try to come forth, but you're too weak!</span>")
return FALSE
if(!force)
if(hostchosen) //marauder approved
to_chat(host, "<span class='sevtug'>Your words echo with power as [true_name] emerges from your body!</span>")
else
to_chat(host, "<span class='sevtug'>[true_name] emerges from your body to protect you!</span>")
forceMove(host.loc)
visible_message("<span class='warning'>[host]'s skin glows red as [name] emerges from their body!</span>", "<span class='sevtug_small'>You exit the safety of [host]'s body!</span>")
return TRUE
/mob/living/simple_animal/hostile/clockwork/marauder/get_alt_name()
return " ([text2ratvar(true_name)])"
/mob/living/simple_animal/hostile/clockwork/marauder/proc/is_in_host() //Checks if the marauder is inside of their host
return host && loc == host
//HOST ACTIONS
//Summon Marauder action: Calls forth or recalls your marauder
/datum/action/innate/summon_marauder
name = "Force Marauder to Emerge/Recall"
desc = "Allows you to force your clockwork marauder to emerge or recall as required."
button_icon_state = "clockwork_marauder"
background_icon_state = "bg_clock"
check_flags = AB_CHECK_CONSCIOUS
buttontooltipstyle = "clockcult"
var/mob/living/simple_animal/hostile/clockwork/marauder/linked_marauder
var/static/list/defend_phrases = list("Defend me", "Come forth", "Assist me", "Protect me", "Give aid", "Help me")
var/static/list/return_phrases = list("Return", "Return to me", "Your job is done", "You have served", "Come back", "Retreat")
/datum/action/innate/summon_marauder/IsAvailable()
if(!linked_marauder)
return FALSE
if(isliving(owner))
var/mob/living/L = owner
if(!L.can_speak_vocal() || L.stat)
return FALSE
return ..()
/datum/action/innate/summon_marauder/Activate()
if(linked_marauder.is_in_host())
clockwork_say(owner, text2ratvar("[pick(defend_phrases)], [linked_marauder.true_name]!"))
linked_marauder.emerge_from_host(TRUE)
else
clockwork_say(owner, text2ratvar("[pick(return_phrases)], [linked_marauder.true_name]!"))
linked_marauder.return_to_host()
return TRUE
//Linked Minds action: talks to your marauder
/datum/action/innate/linked_minds
name = "Linked Minds"
desc = "Allows you to silently communicate with your marauder."
button_icon_state = "linked_minds"
background_icon_state = "bg_clock"
check_flags = AB_CHECK_CONSCIOUS
buttontooltipstyle = "clockcult"
var/mob/living/simple_animal/hostile/clockwork/marauder/linked_marauder
/datum/action/innate/linked_minds/IsAvailable()
if(!linked_marauder)
return FALSE
return ..()
/datum/action/innate/linked_minds/Activate()
var/message = stripped_input(owner, "Enter a message to tell your marauder.", "Telepathy")
if(!owner || !message)
return FALSE
if(!linked_marauder)
to_chat(owner, "<span class='warning'>Your marauder seems to have been destroyed!</span>")
return FALSE
var/name_part = "<span class='sevtug'>Servant [findtextEx(owner.name, owner.real_name) ? "[owner.name]" : "[owner.real_name] (as [owner.name])"]</span>"
message = "<span class='sevtug_small'>\"[message]\"</span>" //Processed output
to_chat(owner, "[name_part]<span class='sevtug_small'>:</span> [message]")
to_chat(linked_marauder, "[name_part]<span class='sevtug_small'>:</span> [message]")
for(var/M in GLOB.mob_list)
if(isobserver(M))
var/link = FOLLOW_LINK(M, src)
to_chat(M, "[link] [name_part] <span class='sevtug_small'>(to</span> <span class='sevtug'>[linked_marauder] ([linked_marauder.true_name])</span><span class='sevtug_small'>):</span> [message]")
return TRUE

View File

@@ -5,8 +5,6 @@ Pieces of scripture require certain follower counts, contruction value, and acti
Drivers: Unlocked by default
Scripts: 5 servants and a cache
Applications: 8 servants, 3 caches, and 100 CV
Revenant: 10 servants, 4 caches, and 200 CV
Judgement: 12 servants, 5 caches, 300 CV, and any existing AIs are converted or destroyed
*/
/datum/clockwork_scripture
@@ -15,7 +13,9 @@ Judgement: 12 servants, 5 caches, 300 CV, and any existing AIs are converted or
var/desc = "Ancient Ratvarian lore. This piece seems particularly mundane."
var/list/invocations = list() //Spoken over time in the ancient language of Ratvar. See clock_unsorted.dm for more details on the language and how to make it.
var/channel_time = 10 //In deciseconds, how long a ritual takes to chant
var/list/consumed_components = list(BELLIGERENT_EYE = 0, VANGUARD_COGWHEEL = 0, GEIS_CAPACITOR = 0, REPLICANT_ALLOY = 0, HIEROPHANT_ANSIBLE = 0) //Components consumed
var/power_cost = 5 //In watts, how much a scripture takes to invoke
var/special_power_text //If the scripture can use additional power to have a unique function, use this; put POWERCOST here to display the special power cost.
var/special_power_cost //This should be a define in __DEFINES/clockcult.dm, such as ABSCOND_ABDUCTION_COST
var/obj/item/clockwork/slab/slab //The parent clockwork slab
var/mob/living/invoker //The slab's holder
var/whispered = FALSE //If the invocation is whispered rather than spoken aloud
@@ -29,11 +29,6 @@ Judgement: 12 servants, 5 caches, 300 CV, and any existing AIs are converted or
var/primary_component
var/sort_priority = 1 //what position the scripture should have in a list of scripture. Should be based off of component costs/reqs, but you can't initial() lists.
//components the scripture used from a slab
var/list/used_slab_components = list(BELLIGERENT_EYE = 0, VANGUARD_COGWHEEL = 0, GEIS_CAPACITOR = 0, REPLICANT_ALLOY = 0, HIEROPHANT_ANSIBLE = 0)
//components the scripture used from the global cache
var/list/used_cache_components = list(BELLIGERENT_EYE = 0, VANGUARD_COGWHEEL = 0, GEIS_CAPACITOR = 0, REPLICANT_ALLOY = 0, HIEROPHANT_ANSIBLE = 0)
//messages for offstation scripture recital, courtesy ratvar's generals(and neovgre)
var/static/list/neovgre_penalty = list("Go to the station.", "Useless.", "Don't waste time.", "Pathetic.", "Wasteful.")
var/static/list/inathneq_penalty = list("Child, this is too far out!", "The barrier isn't thin enough for for me to help!", "Please, go to the station so I can assist you.", \
@@ -57,34 +52,14 @@ Judgement: 12 servants, 5 caches, 300 CV, and any existing AIs are converted or
if(slab.busy)
to_chat(invoker, "<span class='warning'>[slab] refuses to work, displaying the message: \"[slab.busy]!\"</span>")
return FALSE
if(invoker.has_status_effect(STATUS_EFFECT_GEISTRACKER))
to_chat(invoker, "<span class='warning'>[slab] refuses to work, displaying the message: \"Sustaining Geis!\"</span>")
return FALSE
slab.busy = "Invocation ([name]) in progress"
if(GLOB.ratvar_awakens)
channel_time *= 0.5 //if ratvar has awoken, half channel time and no cost
else if(!slab.no_cost)
for(var/i in consumed_components)
if(consumed_components[i])
for(var/j in 1 to consumed_components[i])
if(slab.stored_components[i])
slab.stored_components[i]--
used_slab_components[i]++
else
GLOB.clockwork_component_cache[i]--
used_cache_components[i]++
update_slab_info()
adjust_clockwork_power(-power_cost)
channel_time *= slab.speed_multiplier
if(!recital() || !check_special_requirements() || !scripture_effects()) //if we fail any of these, refund components used
for(var/i in used_slab_components)
if(used_slab_components[i])
if(slab)
slab.stored_components[i] += consumed_components[i]
else //if we can't find a slab add to the global cache
GLOB.clockwork_component_cache[i] += consumed_components[i]
for(var/i in used_cache_components)
if(used_cache_components[i])
GLOB.clockwork_component_cache[i] += consumed_components[i]
adjust_clockwork_power(power_cost)
update_slab_info()
else
successful = TRUE
@@ -92,6 +67,7 @@ Judgement: 12 servants, 5 caches, 300 CV, and any existing AIs are converted or
SSblackbox.add_details("clockcult_scripture_recited", name)
if(slab)
slab.busy = null
post_recital()
qdel(src)
return successful
@@ -103,23 +79,13 @@ Judgement: 12 servants, 5 caches, 300 CV, and any existing AIs are converted or
return FALSE
return TRUE
/datum/clockwork_scripture/proc/has_requirements() //if we have the components and invokers to do it
/datum/clockwork_scripture/proc/has_requirements() //if we have the power and invokers to do it
var/checked_penalty = FALSE
if(!GLOB.ratvar_awakens && !slab.no_cost)
checked_penalty = check_offstation_penalty()
var/component_printout = "<span class='warning'>You lack the components to recite this piece of scripture!"
var/failed = FALSE
for(var/i in consumed_components)
var/cache_components = GLOB.clockwork_caches ? GLOB.clockwork_component_cache[i] : 0
var/total_components = slab.stored_components[i] + cache_components
if(consumed_components[i] && total_components < consumed_components[i])
component_printout += "\nYou have <span class='[get_component_span(i)]_small'><b>[total_components]/[consumed_components[i]]</b> \
[get_component_name(i)][i != REPLICANT_ALLOY ? "s":""].</span>"
failed = TRUE
if(failed)
component_printout += "</span>"
to_chat(invoker, component_printout)
return FALSE
if(!get_clockwork_power(power_cost))
to_chat(invoker, "<span class='warning'>There isn't enough power to recite this scripture! ([DisplayPower(get_clockwork_power())]/[DisplayPower(power_cost)])</span>")
return
if(multiple_invokers_used && !multiple_invokers_optional && !GLOB.ratvar_awakens && !slab.no_cost)
var/nearby_servants = 0
for(var/mob/living/L in range(1, get_turf(invoker)))
@@ -158,13 +124,10 @@ Judgement: 12 servants, 5 caches, 300 CV, and any existing AIs are converted or
/datum/clockwork_scripture/proc/check_offstation_penalty()
var/turf/T = get_turf(invoker)
if(!T || (!(T.z in GLOB.station_z_levels) && T.z != ZLEVEL_CENTCOM && T.z != ZLEVEL_MINING && T.z != ZLEVEL_LAVALAND))
if(!T || (!(T.z in GLOB.station_z_levels) && T.z != ZLEVEL_CENTCOM && T.z != ZLEVEL_MINING && T.z != ZLEVEL_LAVALAND && T.z != ZLEVEL_CITYOFCOGS))
channel_time *= 2
for(var/i in consumed_components)
if(consumed_components[i])
consumed_components[i] *= 2
power_cost *= 2
return TRUE
return FALSE
/datum/clockwork_scripture/proc/check_special_requirements() //Special requirements for scriptures, checked multiple times during invocation
return TRUE
@@ -185,6 +148,7 @@ Judgement: 12 servants, 5 caches, 300 CV, and any existing AIs are converted or
for(var/invocation in invocations)
if(!do_after(invoker, channel_time / invocations.len, target = invoker, extra_checks = CALLBACK(src, .proc/check_special_requirements)))
slab.busy = null
scripture_fail()
return FALSE
if(multiple_invokers_used)
for(var/mob/living/L in range(1, get_turf(invoker)))
@@ -197,6 +161,11 @@ Judgement: 12 servants, 5 caches, 300 CV, and any existing AIs are converted or
/datum/clockwork_scripture/proc/scripture_effects() //The actual effects of the recital after its conclusion
/datum/clockwork_scripture/proc/scripture_fail() //Called if the scripture fails to invoke.
/datum/clockwork_scripture/proc/post_recital() //Called after the scripture is recited
//Channeled scripture begins instantly but runs constantly
/datum/clockwork_scripture/channeled
var/list/chant_invocations = list("AYY LMAO")
@@ -228,6 +197,7 @@ Judgement: 12 servants, 5 caches, 300 CV, and any existing AIs are converted or
//Creates an object at the invoker's feet
/datum/clockwork_scripture/create_object
var/object_path = /obj/item/clockwork //The path of the object created
var/put_object_in_hands = TRUE
var/creator_message = "<span class='brass'>You create a meme.</span>" //Shown to the invoker
var/observer_message
var/one_per_tile = FALSE
@@ -256,10 +226,51 @@ Judgement: 12 servants, 5 caches, 300 CV, and any existing AIs are converted or
to_chat(invoker, creator_message)
var/obj/O = new object_path (get_turf(invoker))
O.ratvar_act() //update the new object so it gets buffed if ratvar is alive
if(isitem(O))
if(isitem(O) && put_object_in_hands)
invoker.put_in_hands(O)
return TRUE
//Used specifically to create construct shells.
/datum/clockwork_scripture/create_object/construct
put_object_in_hands = FALSE
var/construct_type //The type of construct that the scripture is made to create, even if not directly
var/construct_limit = 1 //How many constructs of this type can exist
var/combat_construct = FALSE //If this construct is meant for fighting and shouldn't be at the base before the assault phase
var/confirmed = FALSE //If we've confirmed that we want to make this construct outside of the station Z
/datum/clockwork_scripture/create_object/construct/check_special_requirements()
update_construct_limit()
var/constructs = get_constructs()
if(constructs >= construct_limit)
to_chat(invoker, "<span class='warning'>There are too many constructs of this type ([constructs])! You may only have [round(construct_limit)] at once.</span>")
return
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
if(G && !G.active && combat_construct && invoker.z == ZLEVEL_CITYOFCOGS && !confirmed) //Putting marauders on the base during the prep phase is a bad idea mmkay
if(alert(invoker, "This is a combat construct, and you cannot easily get it to the station. Are you sure you want to make one here?", "Construct Alert", "Yes", "Cancel") == "Cancel")
return
if(!is_servant_of_ratvar(invoker) || !invoker.canUseTopic(slab))
return
confirmed = TRUE
return TRUE
/datum/clockwork_scripture/create_object/construct/post_recital()
creation_update()
confirmed = FALSE
/datum/clockwork_scripture/create_object/construct/proc/get_constructs()
var/constructs = 0
for(var/V in GLOB.all_clockwork_mobs)
if(istype(V, construct_type))
constructs++
for(var/V in GLOB.all_clockwork_objects)
if(istype(V, object_path)) //nice try
constructs++
return constructs
/datum/clockwork_scripture/create_object/construct/proc/update_construct_limit() //Change this on a per-scripture basis, for dynamic limits
//Uses a ranged slab ability, returning only when the ability no longer exists(ie, when interrupted) or finishes.
/datum/clockwork_scripture/ranged_ability
var/slab_overlay

View File

@@ -2,173 +2,35 @@
// APPLICATIONS //
//////////////////
//Fellowship Armory: Arms the invoker and nearby servants with Ratvarian armor.
/datum/clockwork_scripture/fellowship_armory
descname = "Area Servant Armor"
name = "Fellowship Armory"
desc = "Equips the invoker and all visible Servants with Ratvarian armor. This armor provides high melee resistance but a weakness to lasers. \
It grows faster to invoke with more adjacent Servants."
invocations = list("Shield us...", "...with the...", "... fragments of Engine!")
channel_time = 100
consumed_components = list(VANGUARD_COGWHEEL = 4, REPLICANT_ALLOY = 2, HIEROPHANT_ANSIBLE = 2)
usage_tip = "This scripture will replace all weaker armor worn by affected Servants."
tier = SCRIPTURE_APPLICATION
multiple_invokers_used = TRUE
multiple_invokers_optional = TRUE
primary_component = VANGUARD_COGWHEEL
sort_priority = 2
quickbind = TRUE
quickbind_desc = "Attempts to armor all nearby Servants with powerful Ratvarian armor."
var/static/list/ratvarian_armor_typecache = typecacheof(list(
/obj/item/clothing/suit/armor/clockwork,
/obj/item/clothing/head/helmet/clockwork,
/obj/item/clothing/gloves/clockwork,
/obj/item/clothing/shoes/clockwork)) //don't replace this ever
var/static/list/better_armor_typecache = typecacheof(list(
/obj/item/clothing/suit/space,
/obj/item/clothing/head/helmet/space,
/obj/item/clothing/shoes/magboots)) //replace this only if ratvar is up
/datum/clockwork_scripture/fellowship_armory/run_scripture()
for(var/mob/living/L in orange(1, invoker))
if(can_recite_scripture(L))
channel_time = max(channel_time - 10, 0)
return ..()
/datum/clockwork_scripture/fellowship_armory/scripture_effects()
var/affected = 0
for(var/mob/living/L in view(7, get_turf(invoker)))
if(L.stat == DEAD || !is_servant_of_ratvar(L))
continue
var/do_message = 0
var/obj/item/I = L.get_item_by_slot(slot_wear_suit)
if(remove_item_if_better(I, L))
do_message += L.equip_to_slot_or_del(new/obj/item/clothing/suit/armor/clockwork(null), slot_wear_suit)
I = L.get_item_by_slot(slot_head)
if(remove_item_if_better(I, L))
do_message += L.equip_to_slot_or_del(new/obj/item/clothing/head/helmet/clockwork(null), slot_head)
I = L.get_item_by_slot(slot_gloves)
if(remove_item_if_better(I, L))
do_message += L.equip_to_slot_or_del(new/obj/item/clothing/gloves/clockwork(null), slot_gloves)
I = L.get_item_by_slot(slot_shoes)
if(remove_item_if_better(I, L))
do_message += L.equip_to_slot_or_del(new/obj/item/clothing/shoes/clockwork(null), slot_shoes)
if(do_message)
L.visible_message("<span class='warning'>Strange armor appears on [L]!</span>", "<span class='heavy_brass'>A bright shimmer runs down your body, equipping you with Ratvarian armor.</span>")
playsound(L, 'sound/magic/clockwork/fellowship_armory.ogg', 15*do_message, 1) //get sound loudness based on how much we equipped
affected++
return affected
/datum/clockwork_scripture/fellowship_armory/proc/remove_item_if_better(obj/item/I, mob/user)
if(!I)
return TRUE
if(is_type_in_typecache(I, ratvarian_armor_typecache))
return FALSE
if(!GLOB.ratvar_awakens && is_type_in_typecache(I, better_armor_typecache))
return FALSE
return user.dropItemToGround(I)
//Memory Allocation: Finds a willing ghost and makes them into a clockwork marauders for the invoker.
/datum/clockwork_scripture/memory_allocation
descname = "Guardian"
name = "Memory Allocation"
desc = "Allocates part of your consciousness to a Clockwork Marauder, a vigilant fighter that lives within you, able to be \
called forth by Speaking its True Name or if you become exceptionally low on health.<br>\
If it remains close to you, you will gradually regain health up to a low amount, but it will die if it goes too far from you."
invocations = list("Fright's will...", "...call forth...")
channel_time = 100
consumed_components = list(BELLIGERENT_EYE = 2, VANGUARD_COGWHEEL = 2, GEIS_CAPACITOR = 4)
usage_tip = "Marauders are useful as personal bodyguards and frontline warriors."
tier = SCRIPTURE_APPLICATION
primary_component = GEIS_CAPACITOR
sort_priority = 3
/datum/clockwork_scripture/memory_allocation/check_special_requirements()
for(var/mob/living/simple_animal/hostile/clockwork/marauder/M in GLOB.all_clockwork_mobs)
if(M.host == invoker)
to_chat(invoker, "<span class='warning'>You can only house one marauder at a time!</span>")
return FALSE
return TRUE
/datum/clockwork_scripture/memory_allocation/scripture_effects()
return create_marauder()
/datum/clockwork_scripture/memory_allocation/proc/create_marauder()
invoker.visible_message("<span class='warning'>A purple tendril appears from [invoker]'s [slab.name] and impales itself in [invoker.p_their()] forehead!</span>", \
"<span class='sevtug'>A tendril flies from [slab] into your forehead. You begin waiting while it painfully rearranges your thought pattern...</span>")
invoker.notransform = TRUE //Vulnerable during the process
slab.busy = "Thought Modification in progress"
if(!do_after(invoker, 50, target = invoker))
invoker.visible_message("<span class='warning'>The tendril, covered in blood, retracts from [invoker]'s head and back into the [slab.name]!</span>", \
"<span class='userdanger'>Total agony overcomes you as the tendril is forced out early!</span>")
invoker.notransform = FALSE
invoker.Knockdown(100)
invoker.apply_damage(10, BRUTE, "head")
slab.busy = null
return FALSE
clockwork_say(invoker, text2ratvar("...the mind made..."))
invoker.notransform = FALSE
slab.busy = "Marauder Selection in progress"
if(!check_special_requirements())
return FALSE
to_chat(invoker, "<span class='warning'>The tendril shivers slightly as it selects a marauder...</span>")
var/list/marauder_candidates = pollGhostCandidates("Do you want to play as the clockwork marauder of [invoker.real_name]?", ROLE_SERVANT_OF_RATVAR, null, FALSE, 50, POLL_IGNORE_CLOCKWORK_MARAUDER)
if(!check_special_requirements())
return FALSE
if(!marauder_candidates.len)
invoker.visible_message("<span class='warning'>The tendril retracts from [invoker]'s head, sealing the entry wound as it does so!</span>", \
"<span class='warning'>The tendril was unsuccessful! Perhaps you should try again another time.</span>")
return FALSE
clockwork_say(invoker, text2ratvar("...sword and shield!"))
var/mob/dead/observer/theghost = pick(marauder_candidates)
var/mob/living/simple_animal/hostile/clockwork/marauder/M = new(invoker)
M.key = theghost.key
M.bind_to_host(invoker)
invoker.visible_message("<span class='warning'>The tendril retracts from [invoker]'s head, sealing the entry wound as it does so!</span>", \
"<span class='sevtug'>[M.true_name], a clockwork marauder, has taken up residence in your mind. Communicate with it via the \"Linked Minds\" action button.</span>")
return TRUE
//Sigil of Transmission: Creates a sigil of transmission that can drain and store power for clockwork structures.
/datum/clockwork_scripture/create_object/sigil_of_transmission
descname = "Structure Power Generator & Battery"
name = "Sigil of Transmission"
desc = "Places a sigil that can drain and will store energy to power clockwork structures."
invocations = list("Divinity...", "...power our creations!")
channel_time = 70
consumed_components = list(VANGUARD_COGWHEEL = 2, GEIS_CAPACITOR = 2, HIEROPHANT_ANSIBLE = 4)
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/transmission
creator_message = "<span class='brass'>A sigil silently appears below you. It will automatically power clockwork structures near it and will drain power when activated.</span>"
usage_tip = "Cyborgs can charge from this sigil by remaining over it for 5 seconds."
//Clockwork Marauder: Creates a construct shell for a clockwork marauder, a well-rounded frontline fighter.
/datum/clockwork_scripture/create_object/construct/clockwork_marauder
descname = "Well-Rounded Combat Construct"
name = "Clockwork Marauder"
desc = "Creates a shell for a clockwork marauder, a balanced frontline construct."
invocations = list("Arise, avatar of Arbiter!", "Defend the Ark with vengeful zeal.")
channel_time = 50
power_cost = 1000
creator_message = "<span class='brass'>Your slab disgorges several chunks of replicant alloy that form into a suit of thrumming armor.</span>"
usage_tip = "The marauder's shield can effectively deflect energy-based projectiles."
tier = SCRIPTURE_APPLICATION
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 5
primary_component = BELLIGERENT_EYE
sort_priority = 1
quickbind = TRUE
quickbind_desc = "Creates a Sigil of Transmission, which can drain and will store power for clockwork structures."
quickbind_desc = "Creates a clockwork marauder, used for frontline combat."
object_path = /obj/item/clockwork/construct_chassis/clockwork_marauder
construct_type = /mob/living/simple_animal/hostile/clockwork/marauder
combat_construct = TRUE
//Prolonging Prism: Creates a prism that will delay the shuttle at a power cost
/datum/clockwork_scripture/create_object/prolonging_prism
descname = "Powered Structure, Delay Emergency Shuttles"
name = "Prolonging Prism"
desc = "Creates a mechanized prism which will delay the arrival of an emergency shuttle by 2 minutes at a massive power cost."
invocations = list("May this prism...", "...grant us time to enact his will!")
channel_time = 80
consumed_components = list(VANGUARD_COGWHEEL = 5, GEIS_CAPACITOR = 2, REPLICANT_ALLOY = 2)
object_path = /obj/structure/destructible/clockwork/powered/prolonging_prism
creator_message = "<span class='brass'>You form a prolonging prism, which will delay the arrival of an emergency shuttle at a massive power cost.</span>"
observer_message = "<span class='warning'>An onyx prism forms in midair and sprouts tendrils to support itself!</span>"
invokers_required = 2
multiple_invokers_used = TRUE
usage_tip = "The power cost to delay a shuttle increases based on the number of times activated."
tier = SCRIPTURE_APPLICATION
one_per_tile = TRUE
primary_component = VANGUARD_COGWHEEL
sort_priority = 7
quickbind = TRUE
quickbind_desc = "Creates a Prolonging Prism, which will delay the arrival of an emergency shuttle by 2 minutes at a massive power cost."
/datum/clockwork_scripture/create_object/construct/clockwork_marauder/update_construct_limit()
var/human_servants = 0
for(var/V in SSticker.mode.servants_of_ratvar)
var/datum/mind/M = V
if(ishuman(M.current))
human_servants++
construct_limit = human_servants / 4 //1 per 4 human servants, and a maximum of 3 marauders
construct_limit = Clamp(construct_limit, 1, 3)
/datum/clockwork_scripture/create_object/prolonging_prism/check_special_requirements()
if(SSshuttle.emergency.mode == SHUTTLE_DOCKED || SSshuttle.emergency.mode == SHUTTLE_IGNITING || SSshuttle.emergency.mode == SHUTTLE_STRANDED || SSshuttle.emergency.mode == SHUTTLE_ESCAPE)
@@ -188,7 +50,7 @@
desc = "Creates a mania motor which causes minor damage and a variety of negative mental effects in nearby non-Servant humans, potentially up to and including conversion."
invocations = list("May this transmitter...", "...break the will of all who oppose us!")
channel_time = 80
consumed_components = list(GEIS_CAPACITOR = 5, REPLICANT_ALLOY = 2, HIEROPHANT_ANSIBLE = 2)
power_cost = 750
object_path = /obj/structure/destructible/clockwork/powered/mania_motor
creator_message = "<span class='brass'>You form a mania motor, which causes minor damage and negative mental effects in non-Servants.</span>"
observer_message = "<span class='warning'>A two-pronged machine rises from the ground!</span>"
@@ -198,31 +60,29 @@
tier = SCRIPTURE_APPLICATION
one_per_tile = TRUE
primary_component = GEIS_CAPACITOR
sort_priority = 8
sort_priority = 2
quickbind = TRUE
quickbind_desc = "Creates a Mania Motor, which causes minor damage and negative mental effects in non-Servants."
//Tinkerer's Daemon: Creates an efficient machine that rapidly produces components at a power cost.
/datum/clockwork_scripture/create_object/tinkerers_daemon
descname = "Powered Structure, Component Generator"
name = "Tinkerer's Daemon"
desc = "Creates a tinkerer's daemon which can rapidly collect components. It will only function if it has sufficient power, active daemons are outnumbered by Servants by a ratio of 5:1, \
and there is at least one existing cache."
invocations = list("May this generator...", "...collect Engine parts that yet hold greatness!")
channel_time = 80
consumed_components = list(BELLIGERENT_EYE = 2, GEIS_CAPACITOR = 2, REPLICANT_ALLOY = 5)
object_path = /obj/structure/destructible/clockwork/powered/tinkerers_daemon
creator_message = "<span class='brass'>You form a tinkerer's daemon which can rapidly collect components at a power cost.</span>"
invokers_required = 2
multiple_invokers_used = TRUE
usage_tip = "Vital to your success!"
//Sigil of Transmission: Creates a sigil of transmission that can drain and store power for clockwork structures.
/datum/clockwork_scripture/create_object/sigil_of_transmission
descname = "Structure Power Generator & Battery"
name = "Sigil of Transmission"
desc = "Places a sigil that can drain and will store energy to power clockwork structures."
invocations = list("Divinity...", "...power our creations!")
channel_time = 70
power_cost = 200
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/transmission
creator_message = "<span class='brass'>A sigil silently appears below you. It will automatically power clockwork structures near it and will drain power when activated.</span>"
usage_tip = "Cyborgs can charge from this sigil by remaining over it for 5 seconds."
tier = SCRIPTURE_APPLICATION
one_per_tile = TRUE
primary_component = REPLICANT_ALLOY
sort_priority = 9
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 3
quickbind = TRUE
quickbind_desc = "Creates a Tinkerer's Daemon, which can rapidly collect components for power."
quickbind_desc = "Creates a Sigil of Transmission, which can drain and will store power for clockwork structures."
//Clockwork Obelisk: Creates a powerful obelisk that can be used to broadcast messages or open a gateway to any servant or clockwork obelisk at a power cost.
@@ -232,7 +92,7 @@
desc = "Creates a clockwork obelisk that can broadcast messages over the Hierophant Network or open a Spatial Gateway to any living Servant or clockwork obelisk."
invocations = list("May this obelisk...", "...take us to all places!")
channel_time = 80
consumed_components = list(BELLIGERENT_EYE = 2, VANGUARD_COGWHEEL = 2, HIEROPHANT_ANSIBLE = 5)
power_cost = 300
object_path = /obj/structure/destructible/clockwork/powered/clockwork_obelisk
creator_message = "<span class='brass'>You form a clockwork obelisk which can broadcast messages or produce Spatial Gateways.</span>"
observer_message = "<span class='warning'>A brass obelisk appears hanging in midair!</span>"
@@ -242,6 +102,6 @@
tier = SCRIPTURE_APPLICATION
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 10
sort_priority = 4
quickbind = TRUE
quickbind_desc = "Creates a Clockwork Obelisk, which can send messages or open Spatial Gateways with power."

View File

@@ -2,47 +2,47 @@
// DRIVERS //
/////////////
//Belligerent: Channeled for up to fifteen times over thirty seconds. Forces non-servants that can hear the chant to walk, doing minor damage. Nar-Sian cultists are burned.
/datum/clockwork_scripture/channeled/belligerent
descname = "Channeled, Area Slowdown"
name = "Belligerent"
desc = "Forces all nearby non-servants to walk rather than run, doing minor damage. Chanted every two seconds for up to thirty seconds."
chant_invocations = list("Punish their blindness!", "Take time, make slow!")
chant_amount = 15
chant_interval = 20
channel_time = 20
usage_tip = "Useful for crowd control in a populated area and disrupting mass movement."
//Hateful Manacles: Applies restraints from melee over several seconds. The restraints function like handcuffs and break on removal.
/datum/clockwork_scripture/ranged_ability/hateful_manacles
descname = "Handcuffs"
name = "Hateful Manacles"
desc = "Forms replicant manacles around a target's wrists that function like handcuffs."
invocations = list("Shackle the heretic!", "Break them in body and spirit!")
channel_time = 15
power_cost = 25
whispered = TRUE
usage_tip = "The manacles are about as strong as zipties, and break when removed."
tier = SCRIPTURE_DRIVER
primary_component = BELLIGERENT_EYE
sort_priority = 1
ranged_type = /obj/effect/proc_holder/slab/hateful_manacles
slab_overlay = "hateful_manacles"
ranged_message = "<span class='neovgre_small'><i>You charge the clockwork slab with divine energy.</i>\n\
<b>Left-click a target within melee range to shackle!\n\
Click your slab to cancel.</b></span>"
timeout_time = 200
quickbind = TRUE
quickbind_desc = "Forces nearby non-Servants to walk, doing minor damage with each chant.<br><b>Maximum 15 chants.</b>"
/datum/clockwork_scripture/channeled/belligerent/chant_effects(chant_number)
for(var/mob/living/carbon/C in hearers(7, invoker))
C.apply_status_effect(STATUS_EFFECT_BELLIGERENT)
new /obj/effect/temp_visual/ratvar/belligerent(get_turf(invoker))
return TRUE
quickbind_desc = "Applies handcuffs to a struck target."
//Judicial Visor: Creates a judicial visor, which can smite an area.
/datum/clockwork_scripture/create_object/judicial_visor
descname = "Delayed Area Knockdown Glasses"
name = "Judicial Visor"
desc = "Creates a visor that can smite an area, applying Belligerent and briefly stunning. The smote area will explode after 3 seconds."
invocations = list("Grant me the flames of Engine!")
channel_time = 10
consumed_components = list(BELLIGERENT_EYE = 1)
//Sigil of Transgression: Creates a sigil of transgression, which briefly stuns and applies Belligerent to the first non-servant to cross it.
/datum/clockwork_scripture/create_object/sigil_of_transgression
descname = "Trap, Stunning"
name = "Sigil of Transgression"
desc = "Wards a tile with a sigil, which will briefly stun the next non-Servant to cross it and apply Belligerent to them."
invocations = list("Divinity, smite...", "...those who tresspass here!")
channel_time = 50
power_cost = 50
whispered = TRUE
object_path = /obj/item/clothing/glasses/judicial_visor
creator_message = "<span class='brass'>You form a judicial visor, which is capable of smiting a small area.</span>"
usage_tip = "The visor has a thirty-second cooldown once used."
object_path = /obj/effect/clockwork/sigil/transgression
creator_message = "<span class='brass'>A sigil silently appears below you. The next non-Servant to cross it will be smitten.</span>"
usage_tip = "The sigil does not silence its victim, and is generally used to soften potential converts or would-be invaders."
tier = SCRIPTURE_DRIVER
space_allowed = TRUE
one_per_tile = TRUE
primary_component = BELLIGERENT_EYE
sort_priority = 2
quickbind = TRUE
quickbind_desc = "Creates a Judicial Visor, which can smite an area, applying Belligerent and briefly stunning."
quickbind_desc = "Creates a Sigil of Transgression, which will briefly stun and slow the next non-Servant to cross it."
//Vanguard: Provides twenty seconds of stun immunity. At the end of the twenty seconds, 25% of all stuns absorbed are applied to the invoker.
@@ -53,6 +53,7 @@
Excessive absorption will cause unconsciousness."
invocations = list("Shield me...", "...from darkness!")
channel_time = 30
power_cost = 25
usage_tip = "You cannot reactivate Vanguard while still shielded by it."
tier = SCRIPTURE_DRIVER
primary_component = VANGUARD_COGWHEEL
@@ -84,7 +85,7 @@
desc = "Charges your slab with healing power, allowing you to convert all of a target Servant's brute, burn, and oxygen damage to half as much toxin damage."
invocations = list("Mend the wounds of...", "...my inferior flesh.")
channel_time = 30
consumed_components = list(VANGUARD_COGWHEEL = 1)
power_cost = 100
usage_tip = "The Compromise is very fast to invoke, and will remove holy water from the target Servant."
tier = SCRIPTURE_DRIVER
primary_component = VANGUARD_COGWHEEL
@@ -98,38 +99,77 @@
Click your slab to cancel.</b></span>"
//Geis: Grants a short-range binding attack that allows you to mute and drag around a target in a very obvious manner.
/datum/clockwork_scripture/ranged_ability/geis
descname = "Melee Mute & Stun"
name = "Geis"
desc = "Charges your slab with divine energy, allowing you to bind and pull a struck heretic."
invocations = list("Divinity, grant me strength...", "...to bind the heathen!")
//Abscond: Used to return to Reebe.
/datum/clockwork_scripture/abscond
descname = "Return to Reebe"
name = "Abscond"
desc = "Yanks you through space, returning you to home base."
invocations = list("As we bid farewell, and return to the stars...", "...we shall find our way home.")
whispered = TRUE
channel_time = 20
usage_tip = "You CANNOT TAKE ANY NON-PULL ACTIONS while the target is bound, so Sigils of Submission should be placed before use."
channel_time = 50
power_cost = 5
special_power_text = "POWERCOST to bring pulled creature"
special_power_cost = ABSCOND_ABDUCTION_COST
usage_tip = "This can't be used while on Reebe, for obvious reasons."
tier = SCRIPTURE_DRIVER
primary_component = GEIS_CAPACITOR
sort_priority = 5
quickbind = TRUE
quickbind_desc = "Allows you to bind and mute an adjacent target non-Servant.<br><b>Click your slab to disable.</b>"
slab_overlay = "geis"
ranged_type = /obj/effect/proc_holder/slab/geis
ranged_message = "<span class='sevtug_small'><i>You charge the clockwork slab with divine energy.</i>\n\
<b>Left-click a target within melee range to bind!\n\
Click your slab to cancel.</b></span>"
timeout_time = 100
quickbind_desc = "Returns you to Reebe."
/datum/clockwork_scripture/ranged_ability/geis/run_scripture()
var/servants = 0
if(!GLOB.ratvar_awakens)
for(var/mob/living/M in GLOB.living_mob_list)
if(can_recite_scripture(M, TRUE))
servants++
if(servants > SCRIPT_SERVANT_REQ)
whispered = FALSE
servants -= SCRIPT_SERVANT_REQ
channel_time = min(channel_time + servants*3, 50)
return ..()
/datum/clockwork_scripture/abscond/check_special_requirements()
if(invoker.z == ZLEVEL_CITYOFCOGS)
to_chat(invoker, "<span class='danger'>You're already at Reebe.</span>")
return
return TRUE
/datum/clockwork_scripture/abscond/recital()
animate(invoker.client, color = "#AF0AAF", time = 50)
. = ..()
/datum/clockwork_scripture/abscond/scripture_effects()
var/take_pulling = invoker.pulling && isliving(invoker.pulling) && get_clockwork_power(ABSCOND_ABDUCTION_COST)
var/turf/T = get_turf(pick(GLOB.servant_spawns))
invoker.visible_message("<span class='warning'>[invoker] flickers and phases out of existence!</span>", \
"<span class='bold sevtug_small'>You feel a dizzying sense of vertigo as you're yanked back to Reebe!</span>")
T.visible_message("<span class='warning'>[invoker] flickers and phases into existence!</span>")
playsound(invoker, 'sound/magic/magic_missile.ogg', 50, TRUE)
playsound(T, 'sound/magic/magic_missile.ogg', 50, TRUE)
do_sparks(5, TRUE, invoker)
do_sparks(5, TRUE, T)
if(take_pulling)
adjust_clockwork_power(-special_power_cost)
invoker.pulling.forceMove(T)
invoker.forceMove(T)
if(invoker.client)
animate(invoker.client, color = initial(invoker.client.color), time = 25)
/datum/clockwork_scripture/abscond/scripture_fail()
if(invoker && invoker.client)
animate(invoker.client, color = initial(invoker.client.color), time = 10)
//Kindle: Charges the slab with blazing energy. It can be released to stun and silence a target.
/datum/clockwork_scripture/ranged_ability/kindle
descname = "Short-Range Single-Target Stun"
name = "Kindle"
desc = "Charges your slab with divine energy, allowing you to overwhelm a target with Ratvar's light."
invocations = list("Divinity, show them your light!")
whispered = TRUE
channel_time = 30
power_cost = 125
usage_tip = "The light can be used from up to two tiles away. Damage taken will GREATLY REDUCE the stun's duration."
tier = SCRIPTURE_DRIVER
primary_component = GEIS_CAPACITOR
sort_priority = 6
slab_overlay = "volt"
ranged_type = /obj/effect/proc_holder/slab/kindle
ranged_message = "<span class='brass'><i>You charge the clockwork slab with divine energy.</i>\n\
<b>Left-click a target within melee range to stun!\n\
Click your slab to cancel.</b></span>"
timeout_time = 150
quickbind = TRUE
quickbind_desc = "Stuns and mutes a target from a short range."
//Sigil of Submission: Creates a sigil of submission, which converts one heretic above it after a delay.
@@ -139,7 +179,7 @@
desc = "Places a luminous sigil that will convert any non-Servants that remain on it for 8 seconds."
invocations = list("Divinity, enlighten...", "...those who trespass here!")
channel_time = 60
consumed_components = list(GEIS_CAPACITOR = 1)
power_cost = 125
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/submission
creator_message = "<span class='brass'>A luminous sigil appears below you. Any non-Servants to cross it will be converted after 8 seconds if they do not move.</span>"
@@ -159,6 +199,7 @@
desc = "Creates a new clockwork slab."
invocations = list("Metal, become greater!")
channel_time = 10
power_cost = 25
whispered = TRUE
object_path = /obj/item/clockwork/slab
creator_message = "<span class='brass'>You copy a piece of replicant alloy and command it into a new slab.</span>"
@@ -171,37 +212,44 @@
quickbind_desc = "Creates a new Clockwork Slab."
//Tinkerer's Cache: Creates a tinkerer's cache, allowing global component storage.
/datum/clockwork_scripture/create_object/tinkerers_cache
descname = "Necessary Structure, Shares Components"
name = "Tinkerer's Cache"
desc = "Forms a cache that can store an infinite amount of components. All caches are linked and will provide components to slabs. \
Striking a cache with a slab will transfer that slab's components to the global cache."
invocations = list("Constructing...", "...a cache!")
//Stargazer: Creates a stargazer, a cheap power generator that utilizes starlight.
/datum/clockwork_scripture/create_object/stargazer
descname = "Necessary Structure, Generates Power From Starlight"
name = "Stargazer"
desc = "Forms a weak structure that generates power every second while within three tiles of starlight."
invocations = list("Capture their inferior light for us!")
channel_time = 50
consumed_components = list(BELLIGERENT_EYE = 0, VANGUARD_COGWHEEL = 0, GEIS_CAPACITOR = 0, REPLICANT_ALLOY = 1, HIEROPHANT_ANSIBLE = 0)
object_path = /obj/structure/destructible/clockwork/cache
creator_message = "<span class='brass'>You form a tinkerer's cache, which is capable of storing components, which will automatically be used by slabs.</span>"
observer_message = "<span class='warning'>A hollow brass spire rises and begins to blaze!</span>"
usage_tip = "Slabs will draw components from the global cache after the slab's own repositories, making caches extremely useful."
power_cost = 50
object_path = /obj/structure/destructible/clockwork/stargazer
creator_message = "<span class='brass'>You form a stargazer, which will generate power near starlight.</span>"
observer_message = "<span class='warning'>A large lantern-shaped machine forms!</span>"
usage_tip = "For obvious reasons, make sure to place this near a window or somewhere else that can see space!"
tier = SCRIPTURE_DRIVER
one_per_tile = TRUE
primary_component = REPLICANT_ALLOY
sort_priority = 8
quickbind = TRUE
quickbind_desc = "Creates a Tinkerer's Cache, which stores components globally for slab access."
var/static/prev_cost = 0
quickbind_desc = "Creates a stargazer, which generates power when near starlight."
/datum/clockwork_scripture/create_object/tinkerers_cache/creation_update()
var/cache_cost_increase = min(round(GLOB.clockwork_caches*0.4), 10)
if(cache_cost_increase != prev_cost)
prev_cost = cache_cost_increase
consumed_components = list(BELLIGERENT_EYE = 0, VANGUARD_COGWHEEL = 0, GEIS_CAPACITOR = 0, REPLICANT_ALLOY = 1, HIEROPHANT_ANSIBLE = 0)
for(var/i in consumed_components)
if(i != REPLICANT_ALLOY)
consumed_components[i] += cache_cost_increase
return TRUE
return FALSE
//Integration Cog: Creates an integration cog that can be inserted into APCs to passively siphon power.
/datum/clockwork_scripture/create_object/integration_cog
descname = "APC Power Siphoner"
name = "Integration Cog"
desc = "Fabricates an integration cog, which can be used on an open APC to replace its innards and passively siphon its power."
invocations = list("Take that which sustains them!")
channel_time = 10
power_cost = 10
whispered = TRUE
object_path = /obj/item/clockwork/integration_cog
creator_message = "<span class='brass'>You form an integration cog, which can be inserted into an open APC to passively siphon power.</span>"
usage_tip = "Tampering isn't visible unless the APC is opened."
tier = SCRIPTURE_DRIVER
space_allowed = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 9
quickbind = TRUE
quickbind_desc = "Creates an integration cog, which can be used to siphon power from an open APC."
//Wraith Spectacles: Creates a pair of wraith spectacles, which grant xray vision but damage vision slowly.
@@ -211,6 +259,7 @@
desc = "Fabricates a pair of glasses which grant true sight but cause gradual vision loss."
invocations = list("Show the truth of this world to me!")
channel_time = 10
power_cost = 50
whispered = TRUE
object_path = /obj/item/clothing/glasses/wraith_spectacles
creator_message = "<span class='brass'>You form a pair of wraith spectacles, which grant true sight but cause gradual vision loss.</span>"
@@ -218,26 +267,6 @@
tier = SCRIPTURE_DRIVER
space_allowed = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 9
quickbind = TRUE
quickbind_desc = "Creates a pair of Wraith Spectacles, which grant true sight but cause gradual vision loss."
//Sigil of Transgression: Creates a sigil of transgression, which stuns the first nonservant to cross it.
/datum/clockwork_scripture/create_object/sigil_of_transgression
descname = "Trap, Stunning"
name = "Sigil of Transgression"
desc = "Wards a tile with a sigil, which will stun the next non-Servant to cross it."
invocations = list("Divinity, smite...", "...those who tresspass here!")
channel_time = 50
consumed_components = list(HIEROPHANT_ANSIBLE = 1)
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/transgression
creator_message = "<span class='brass'>A sigil silently appears below you. The next non-Servant to cross it will be stunned.</span>"
usage_tip = "The sigil, while fairly powerful in its stun, does not induce muteness in its victim."
tier = SCRIPTURE_DRIVER
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 10
quickbind = TRUE
quickbind_desc = "Creates a Sigil of Transgression, which will stun the next non-Servant to cross it."
quickbind_desc = "Creates a pair of Wraith Spectacles, which grant true sight but cause gradual vision loss."

View File

@@ -1,42 +0,0 @@
///////////////
// JUDGEMENT //
///////////////
//Ark of the Clockwork Justiciar: Creates a Gateway to the Celestial Derelict, summoning ratvar.
/datum/clockwork_scripture/create_object/ark_of_the_clockwork_justiciar
descname = "Structure, Win Condition"
name = "Ark of the Clockwork Justiciar"
desc = "Tears apart a rift in spacetime to Reebe, the Celestial Derelict, using a massive amount of components.\n\
This gateway will, after some time, call forth Ratvar from his exile and massively empower all scriptures and tools."
invocations = list("ARMORER! FRIGHT! AMPERAGE! VANGUARD! I CALL UPON YOU!!", \
"THE TIME HAS COME FOR OUR MASTER TO BREAK THE CHAINS OF EXILE!!", \
"LEND US YOUR AID! ENGINE COMES!!")
channel_time = 150
consumed_components = list(BELLIGERENT_EYE = ARK_SUMMON_COST, VANGUARD_COGWHEEL = ARK_SUMMON_COST, GEIS_CAPACITOR = ARK_SUMMON_COST, REPLICANT_ALLOY = ARK_SUMMON_COST, HIEROPHANT_ANSIBLE = ARK_SUMMON_COST)
invokers_required = 6
multiple_invokers_used = TRUE
object_path = /obj/structure/destructible/clockwork/massive/celestial_gateway
creator_message = null
usage_tip = "The gateway is completely vulnerable to attack during its five-minute duration. It will periodically give indication of its general position to everyone on the station \
as well as being loud enough to be heard throughout the entire sector. Defend it with your life!"
tier = SCRIPTURE_JUDGEMENT
sort_priority = 1
/datum/clockwork_scripture/create_object/ark_of_the_clockwork_justiciar/check_special_requirements()
if(!slab.no_cost)
if(GLOB.ratvar_awakens)
to_chat(invoker, "<span class='big_brass'>\"I am already here, idiot.\"</span>")
return FALSE
for(var/obj/structure/destructible/clockwork/massive/celestial_gateway/G in GLOB.all_clockwork_objects)
var/area/gate_area = get_area(G)
to_chat(invoker, "<span class='userdanger'>There is already an Ark at [gate_area.map_name]!</span>")
return FALSE
var/area/A = get_area(invoker)
var/turf/T = get_turf(invoker)
if(!T || !(T.z in GLOB.station_z_levels) || istype(A, /area/shuttle) || !A.blob_allowed)
to_chat(invoker, "<span class='warning'>You must be on the station to activate the Ark!</span>")
return FALSE
if(GLOB.clockwork_gateway_activated)
to_chat(invoker, "<span class='warning'>Ratvar's recent banishment renders him too weak to be wrung forth from Reebe!</span>")
return FALSE
return ..()

View File

@@ -7,9 +7,9 @@
descname = "Structure, Turret"
name = "Ocular Warden"
desc = "Forms an automatic short-range turret which will automatically attack nearby unrestrained non-Servants that can see it."
invocations = list("Guardians...", "...of the Engine...", "...defend us!")
channel_time = 120
consumed_components = list(BELLIGERENT_EYE = 2, REPLICANT_ALLOY = 1)
invocations = list("Guardians of Engine...", "...judge those who would harm us!")
channel_time = 100
power_cost = 250
object_path = /obj/structure/destructible/clockwork/ocular_warden
creator_message = "<span class='brass'>You form an ocular warden, which will automatically attack nearby unrestrained non-Servants that can see it.</span>"
observer_message = "<span class='warning'>A brass eye takes shape and slowly rises into the air, its red iris glaring!</span>"
@@ -29,15 +29,35 @@
return ..()
//Judicial Visor: Creates a judicial visor, which can smite an area.
/datum/clockwork_scripture/create_object/judicial_visor
descname = "Delayed Area Knockdown Glasses"
name = "Judicial Visor"
desc = "Creates a visor that can smite an area, applying Belligerent and briefly stunning. The smote area will explode after 3 seconds."
invocations = list("Grant me the flames of Engine!")
channel_time = 10
power_cost = 400
whispered = TRUE
object_path = /obj/item/clothing/glasses/judicial_visor
creator_message = "<span class='brass'>You form a judicial visor, which is capable of smiting a small area.</span>"
usage_tip = "The visor has a thirty-second cooldown once used."
tier = SCRIPTURE_SCRIPT
space_allowed = TRUE
primary_component = BELLIGERENT_EYE
sort_priority = 2
quickbind = TRUE
quickbind_desc = "Creates a Judicial Visor, which can smite an area, applying Belligerent and briefly stunning."
//Vitality Matrix: Creates a sigil which will drain health from nonservants and can use that health to heal or even revive servants.
/datum/clockwork_scripture/create_object/vitality_matrix
descname = "Trap, Damage to Healing"
name = "Vitality Matrix"
desc = "Places a sigil that drains life from any living non-Servants that cross it, producing Vitality. Servants that cross it, however, will be healed using existing Vitality. \
Dead Servants can be revived by this sigil at a cost of 150 Vitality."
invocations = list("Divinity...", "...steal their life...", "...for these shells!")
invocations = list("Divinity, siphon their essence...", "...for this shell to consume.")
channel_time = 60
consumed_components = list(BELLIGERENT_EYE = 1, VANGUARD_COGWHEEL = 2)
power_cost = 1000
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/vitality
creator_message = "<span class='brass'>A vitality matrix appears below you. It will drain life from non-Servants and heal Servants that cross it.</span>"
@@ -58,7 +78,7 @@
chant_invocations = list("Mend our dents!", "Heal our scratches!", "Repair our gears!")
chant_amount = 10
chant_interval = 20
consumed_components = list(VANGUARD_COGWHEEL = 2, REPLICANT_ALLOY = 1)
power_cost = 1000
usage_tip = "This is a very effective way to rapidly reinforce a base after an attack."
tier = SCRIPTURE_SCRIPT
primary_component = VANGUARD_COGWHEEL
@@ -169,76 +189,115 @@
desc = "Forms a device that, when used on certain objects, replaces them with their Ratvarian equivalents. It requires power to function."
invocations = list("With this device...", "...his presence shall be made known.")
channel_time = 20
consumed_components = list(GEIS_CAPACITOR = 1, REPLICANT_ALLOY = 2)
power_cost = 250
whispered = TRUE
object_path = /obj/item/clockwork/replica_fabricator/preloaded
object_path = /obj/item/clockwork/replica_fabricator
creator_message = "<span class='brass'>You form a replica fabricator.</span>"
usage_tip = "Clockwork Walls cause nearby Tinkerer's Caches to generate components passively, making this a vital tool. Clockwork Floors heal toxin damage in Servants standing on them."
tier = SCRIPTURE_SCRIPT
space_allowed = TRUE
primary_component = REPLICANT_ALLOY
sort_priority = 7
sort_priority = 5
quickbind = TRUE
quickbind_desc = "Creates a Replica Fabricator, which can convert various objects to Ratvarian variants."
//Function Call: Grants the invoker the ability to call forth a Ratvarian spear that deals significant damage to silicons.
/datum/clockwork_scripture/function_call
descname = "Permanent Summonable Spear"
name = "Function Call"
desc = "Grants the invoker the ability to call forth a powerful Ratvarian spear every 3 minutes, with it lasting 3 minutes. The spear's attacks will generate Vitality, used for healing."
invocations = list("Grant me...", "...the might of brass!")
//Clockwork Arnaments: Grants the invoker the ability to call forth a Ratvarian spear and clockwork armor.
/datum/clockwork_scripture/clockwork_arnaments
descname = "Summonable Armor and Weapons"
name = "Clockwork Arnaments"
desc = "Allows the invoker to summon clockwork armor and a Ratvarian spear at will. The spear's attacks will generate Vitality, used for healing."
invocations = list("Grant me arnaments...", "...from the forge of Armorer!")
channel_time = 20
consumed_components = list(REPLICANT_ALLOY = 2, HIEROPHANT_ANSIBLE = 1)
power_cost = 250
whispered = TRUE
usage_tip = "Throwing the spear at a mob will do massive damage and knock them down, but break the spear."
usage_tip = "Throwing the spear at a mob will do massive damage and knock them down, but break the spear. You will need to wait for 30 seconds before resummoning it."
tier = SCRIPTURE_SCRIPT
primary_component = REPLICANT_ALLOY
sort_priority = 8
sort_priority = 6
quickbind = TRUE
quickbind_desc = "Permanently binds clockwork armor and a Ratvarian spear to you."
/datum/clockwork_scripture/function_call/check_special_requirements()
for(var/datum/action/innate/function_call/F in invoker.actions)
/datum/clockwork_scripture/clockwork_arnaments/check_special_requirements()
for(var/datum/action/innate/clockwork_arnaments/F in invoker.actions)
to_chat(invoker, "<span class='warning'>You have already bound a Ratvarian spear to yourself!</span>")
return FALSE
return invoker.can_hold_items()
/datum/clockwork_scripture/function_call/scripture_effects()
/datum/clockwork_scripture/clockwork_arnaments/scripture_effects()
invoker.visible_message("<span class='warning'>A shimmer of yellow light infuses [invoker]!</span>", \
"<span class='brass'>You bind a Ratvarian spear to yourself. Use the \"Function Call\" action button to call it forth.</span>")
var/datum/action/innate/function_call/F = new()
F.Grant(invoker)
"<span class='brass'>You bind clockwork equipment to yourself. Use Clockwork Arnaments and Call Spear to summon them.</span>")
var/datum/action/innate/call_weapon/ratvarian_spear/S = new()
S.Grant(invoker)
var/datum/action/innate/clockwork_arnaments/A = new()
A.Grant(invoker)
return TRUE
//Function Call action: Calls forth a Ratvarian spear once every 3 minutes.
/datum/action/innate/function_call
name = "Function Call"
desc = "Allows you to summon a Ratvarian spear to fight enemies."
button_icon_state = "ratvarian_spear"
//Clockwork Arnaments: Equips a set of clockwork armor. Three-minute cooldown.
/datum/action/innate/clockwork_arnaments
name = "Clockwork Arnaments"
desc = "Outfits you in a full set of Ratvarian armor."
icon_icon = 'icons/mob/actions/actions_clockcult.dmi'
button_icon_state = "clockwork_armor"
background_icon_state = "bg_clock"
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUN|AB_CHECK_CONSCIOUS
buttontooltipstyle = "clockcult"
var/cooldown = 0
var/base_cooldown = RATVARIAN_SPEAR_DURATION
var/static/list/ratvarian_armor_typecache = typecacheof(list(
/obj/item/clothing/suit/armor/clockwork,
/obj/item/clothing/head/helmet/clockwork,
/obj/item/clothing/gloves/clockwork,
/obj/item/clothing/shoes/clockwork)) //don't replace this ever
var/static/list/better_armor_typecache = typecacheof(list(
/obj/item/clothing/suit/space,
/obj/item/clothing/head/helmet/space,
/obj/item/clothing/shoes/magboots)) //replace this only if ratvar is up
/datum/action/innate/function_call/IsAvailable()
if(!is_servant_of_ratvar(owner) || cooldown > world.time)
return FALSE
/datum/action/innate/clockwork_arnaments/IsAvailable()
if(!is_servant_of_ratvar(owner))
qdel(src)
return
if(cooldown > world.time)
return
return ..()
/datum/action/innate/function_call/Activate()
if(!owner.get_empty_held_indexes())
to_chat(usr, "<span class='warning'>You need an empty hand to call forth your spear!</span>")
return FALSE
owner.visible_message("<span class='warning'>A strange spear materializes in [owner]'s hands!</span>", "<span class='brass'>You call forth your spear!</span>")
var/obj/item/clockwork/ratvarian_spear/R = new(get_turf(usr))
owner.put_in_hands(R)
if(!GLOB.ratvar_awakens)
to_chat(owner, "<span class='warning'>Your spear begins to break down in this plane of existence. You can't use it for long!</span>")
cooldown = base_cooldown + world.time
/datum/action/innate/clockwork_arnaments/Activate()
var/do_message = 0
var/obj/item/I = owner.get_item_by_slot(slot_wear_suit)
if(remove_item_if_better(I, owner))
do_message += owner.equip_to_slot_or_del(new/obj/item/clothing/suit/armor/clockwork(null), slot_wear_suit)
I = owner.get_item_by_slot(slot_head)
if(remove_item_if_better(I, owner))
do_message += owner.equip_to_slot_or_del(new/obj/item/clothing/head/helmet/clockwork(null), slot_head)
I = owner.get_item_by_slot(slot_gloves)
if(remove_item_if_better(I, owner))
do_message += owner.equip_to_slot_or_del(new/obj/item/clothing/gloves/clockwork(null), slot_gloves)
I = owner.get_item_by_slot(slot_shoes)
if(remove_item_if_better(I, owner))
do_message += owner.equip_to_slot_or_del(new/obj/item/clothing/shoes/clockwork(null), slot_shoes)
if(do_message)
owner.visible_message("<span class='warning'>Strange armor appears on [owner]!</span>", "<span class='heavy_brass'>A bright shimmer runs down your body, equipping you with Ratvarian armor.</span>")
playsound(owner, 'sound/magic/clockwork/fellowship_armory.ogg', 15 * do_message, TRUE) //get sound loudness based on how much we equipped
cooldown = CLOCKWORK_ARMOR_COOLDOWN + world.time
owner.update_action_buttons_icon()
addtimer(CALLBACK(owner, /mob.proc/update_action_buttons_icon), base_cooldown)
addtimer(CALLBACK(owner, /mob.proc/update_action_buttons_icon), CLOCKWORK_ARMOR_COOLDOWN)
return TRUE
/datum/action/innate/clockwork_arnaments/proc/remove_item_if_better(obj/item/I, mob/user)
if(!I)
return TRUE
if(is_type_in_typecache(I, ratvarian_armor_typecache))
return FALSE
if(!GLOB.ratvar_awakens && is_type_in_typecache(I, better_armor_typecache))
return FALSE
return user.dropItemToGround(I)
//Call Spear: Calls forth a powerful Ratvarian spear.
/datum/action/innate/call_weapon/ratvarian_spear
name = "Call Spear"
desc = "Calls a Ratvarian spear into your hands to fight your enemies."
weapon_type = /obj/item/clockwork/weapon/ratvarian_spear
//Spatial Gateway: Allows the invoker to teleport themselves and any nearby allies to a conscious servant or clockwork obelisk.
/datum/clockwork_scripture/spatial_gateway
@@ -248,7 +307,7 @@
Each servant assisting in the invocation adds one additional use and four additional seconds to the gateway's uses and duration."
invocations = list("Spatial Gateway...", "...activate!")
channel_time = 80
consumed_components = list(VANGUARD_COGWHEEL = 1, HIEROPHANT_ANSIBLE = 2)
power_cost = 400
multiple_invokers_used = TRUE
multiple_invokers_optional = TRUE
usage_tip = "This gateway is strictly one-way and will only allow things through the invoker's portal."
@@ -285,3 +344,4 @@
portal_uses = max(portal_uses, 100) //Very powerful if Ratvar has been summoned
duration = max(duration, 100)
return slab.procure_gateway(invoker, duration, portal_uses)

View File

@@ -77,14 +77,10 @@
return FALSE
return ..()
/obj/structure/destructible/clockwork/proc/get_efficiency_mod(increasing)
/obj/structure/destructible/clockwork/proc/get_efficiency_mod()
if(GLOB.ratvar_awakens)
if(increasing)
return 0.5
return 2
. = max(sqrt(obj_integrity/max(max_integrity, 1)), 0.5)
if(increasing)
. *= min(max_integrity/max(obj_integrity, 1), 4)
. = round(., 0.01)
/obj/structure/destructible/clockwork/attack_ai(mob/user)
@@ -116,7 +112,7 @@
else
icon_state = unanchored_icon
if(do_damage)
playsound(src, break_sound, 10 * get_efficiency_mod(TRUE), 1)
playsound(src, break_sound, 10 * (40 * (1 - get_efficiency_mod())), 1)
take_damage(round(max_integrity * 0.25, 1), BRUTE)
to_chat(user, "<span class='warning'>As you unsecure [src] from the floor, you see cracks appear in its surface!</span>")
@@ -158,10 +154,10 @@
/obj/structure/destructible/clockwork/powered/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
var/powered = total_accessable_power()
var/sigil_number = LAZYLEN(check_apc_and_sigils())
to_chat(user, "<span class='[powered ? "brass":"alloy"]'>It has access to <b>[powered == INFINITY ? "INFINITE</b>":"[DisplayPower(powered)]</b> of"] power, \
and <b>[sigil_number]</b> Sigil[sigil_number == 1 ? "":"s"] of Transmission [sigil_number == 1 ? "is":"are"] in range.</span>")
if(!can_access_clockwork_power(src))
to_chat(user, "<span class='alloy'>It has no access to the power network! Create a sigil of transmission nearby.</span>")
else
to_chat(user, "<span class='brass'>It has access to <b>[DisplayPower(get_clockwork_power())]</b> of power.</span>")
/obj/structure/destructible/clockwork/powered/Destroy()
SSfastprocess.processing -= src
@@ -169,7 +165,7 @@
return ..()
/obj/structure/destructible/clockwork/powered/process()
var/powered = total_accessable_power()
var/powered = can_access_clockwork_power(src)
return powered == PROCESS_KILL ? 25 : powered //make sure we don't accidentally return the arbitrary PROCESS_KILL define
/obj/structure/destructible/clockwork/powered/can_be_unfasten_wrench(mob/user, silent)
@@ -210,102 +206,12 @@
if(forced_disable(TRUE))
new /obj/effect/temp_visual/emp(loc)
/obj/structure/destructible/clockwork/powered/proc/total_accessable_power() //how much power we have and can use
if(!needs_power || GLOB.ratvar_awakens)
return INFINITY //oh yeah we've got power why'd you ask
var/power = 0
power += accessable_apc_power()
power += accessable_sigil_power()
return power
/obj/structure/destructible/clockwork/powered/proc/accessable_apc_power()
var/power = 0
var/area/A = get_area(src)
var/area/targetAPCA
for(var/obj/machinery/power/apc/APC in GLOB.apcs_list)
var/area/APCA = get_area(APC)
if(APCA == A)
target_apc = APC
if(target_apc)
targetAPCA = get_area(target_apc)
if(targetAPCA != A)
target_apc = null
else if(target_apc.cell)
var/apccharge = target_apc.cell.charge
if(apccharge >= MIN_CLOCKCULT_POWER)
power += apccharge
return power
/obj/structure/destructible/clockwork/powered/proc/accessable_sigil_power()
var/power = 0
for(var/obj/effect/clockwork/sigil/transmission/T in range(SIGIL_ACCESS_RANGE, src))
power += T.power_charge
return power
/obj/structure/destructible/clockwork/powered/proc/try_use_power(amount) //try to use an amount of power
if(!needs_power || GLOB.ratvar_awakens)
return 1
if(amount <= 0)
return FALSE
var/power = total_accessable_power()
if(!power || power < amount)
return FALSE
if(!needs_power || GLOB.ratvar_awakens || !amount)
return TRUE
if(!can_access_clockwork_power(src, amount))
return
return use_power(amount)
/obj/structure/destructible/clockwork/powered/proc/use_power(amount) //we've made sure we had power, so now we use it
var/sigilpower = accessable_sigil_power()
var/list/sigils_in_range = list()
for(var/obj/effect/clockwork/sigil/transmission/T in range(SIGIL_ACCESS_RANGE, src))
sigils_in_range += T
while(sigilpower && amount >= MIN_CLOCKCULT_POWER)
for(var/S in sigils_in_range)
var/obj/effect/clockwork/sigil/transmission/T = S
if(amount >= MIN_CLOCKCULT_POWER && T.modify_charge(MIN_CLOCKCULT_POWER))
sigilpower -= MIN_CLOCKCULT_POWER
amount -= MIN_CLOCKCULT_POWER
var/apcpower = accessable_apc_power()
while(apcpower >= MIN_CLOCKCULT_POWER && amount >= MIN_CLOCKCULT_POWER)
if(target_apc.cell.use(MIN_CLOCKCULT_POWER))
apcpower -= MIN_CLOCKCULT_POWER
amount -= MIN_CLOCKCULT_POWER
target_apc.charging = 1
target_apc.chargemode = TRUE
target_apc.update()
target_apc.update_icon()
target_apc.updateUsrDialog()
else
apcpower = 0
if(amount)
return FALSE
else
return TRUE
/obj/structure/destructible/clockwork/powered/proc/return_power(amount) //returns a given amount of power to all nearby sigils or if there are no sigils, to the APC
if(amount <= 0)
return FALSE
var/list/sigils_in_range = check_apc_and_sigils()
if(!istype(sigils_in_range))
return FALSE
if(sigils_in_range.len)
while(amount >= MIN_CLOCKCULT_POWER)
for(var/S in sigils_in_range)
var/obj/effect/clockwork/sigil/transmission/T = S
if(amount >= MIN_CLOCKCULT_POWER && T.modify_charge(-MIN_CLOCKCULT_POWER))
amount -= MIN_CLOCKCULT_POWER
if(target_apc && target_apc.cell && target_apc.cell.give(amount))
target_apc.charging = 1
target_apc.chargemode = TRUE
target_apc.update()
target_apc.update_icon()
target_apc.updateUsrDialog()
return TRUE
/obj/structure/destructible/clockwork/powered/proc/check_apc_and_sigils() //checks for sigils and an APC, returning FALSE if it finds neither, and a list of sigils otherwise
. = list()
for(var/obj/effect/clockwork/sigil/transmission/T in range(SIGIL_ACCESS_RANGE, src))
. += T
var/list/L = .
if(!L.len && (!target_apc || !target_apc.cell))
return FALSE
return adjust_clockwork_power(-amount)

View File

@@ -1,18 +1,24 @@
#define ARK_GRACE_PERIOD 300 //In seconds, how long the crew has before the Ark truly "begins"
//The gateway to Reebe, from which Ratvar emerges.
/obj/structure/destructible/clockwork/massive/celestial_gateway
name = "ark of the Clockwork Justicar"
desc = "A massive, thrumming rip in spacetime."
clockwork_desc = "A portal to the Celestial Derelict. Massive and intimidating, it is the only thing that can both transport Ratvar and withstand the massive amount of energy he emits."
name = "\improper Ark of the Clockwork Justicar"
desc = "A massive, hulking amalgamation of parts. It seems to be maintaining a very unstable bluespace anomaly."
clockwork_desc = "Nezbere's magnum opus: a hulking clockwork machine capable of combining bluespace and steam power to summon Ratvar. Once activated, \
its instability will cause one-way bluespace rifts to open across the station to the City of Cogs, so be prepared to defend it at all costs."
max_integrity = 500
mouse_opacity = MOUSE_OPACITY_OPAQUE
icon = 'icons/effects/clockwork_effects.dmi'
icon_state = "nothing"
density = FALSE
invisibility = INVISIBILITY_MAXIMUM
resistance_flags = FIRE_PROOF | ACID_PROOF | INDESTRUCTIBLE | FREEZE_PROOF
density = TRUE
resistance_flags = FIRE_PROOF | ACID_PROOF | FREEZE_PROOF
can_be_repaired = FALSE
immune_to_servant_attacks = TRUE
var/active = FALSE
var/progress_in_seconds = 0 //Once this reaches GATEWAY_RATVAR_ARRIVAL, it's game over
var/grace_period = ARK_GRACE_PERIOD //This exists to allow the crew to gear up and prepare for the invasion
var/initial_activation_delay = -1 //How many seconds the Ark will have initially taken to activate
var/seconds_until_activation = -1 //How many seconds until the Ark activates; if it should never activate, set this to -1
var/purpose_fulfilled = FALSE
var/first_sound_played = FALSE
var/second_sound_played = FALSE
@@ -20,31 +26,41 @@
var/fourth_sound_played = FALSE
var/obj/effect/clockwork/overlay/gateway_glow/glow
var/obj/effect/countdown/clockworkgate/countdown
var/list/required_components = list(BELLIGERENT_EYE = ARK_CONSUME_COST, VANGUARD_COGWHEEL = ARK_CONSUME_COST, GEIS_CAPACITOR = ARK_CONSUME_COST, REPLICANT_ALLOY = ARK_CONSUME_COST, HIEROPHANT_ANSIBLE = ARK_CONSUME_COST)
/obj/structure/destructible/clockwork/massive/celestial_gateway/Initialize()
. = ..()
INVOKE_ASYNC(src, .proc/spawn_animation)
glow = new(get_turf(src))
if(!GLOB.ark_of_the_clockwork_justiciar)
GLOB.ark_of_the_clockwork_justiciar = src
START_PROCESSING(SSprocessing, src)
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/cry_havoc()
visible_message("<span class='boldwarning'>[src] shudders and roars to life, its parts beginning to whirr and screech!</span>")
hierophant_message("<span class='bold large_brass'>The Ark is activating! Get back to the base!</span>")
for(var/mob/M in GLOB.player_list)
if(is_servant_of_ratvar(M) || isobserver(M) || M.z == z)
M.playsound_local(M, 'sound/magic/clockwork/ark_activation_sequence.ogg', 30, FALSE, pressure_affected = FALSE)
addtimer(CALLBACK(src, .proc/let_slip_the_dogs), 300)
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/let_slip_the_dogs()
spawn_animation()
first_sound_played = TRUE
active = TRUE
priority_announce("Massive [Gibberish("bluespace", 100)] anomaly detected on all frequencies. All crew are directed to \
@!$, [text2ratvar("PURGE ALL UNTRUTHS")] <&. the anomalies and destroy their source to prevent further damage to corporate property. This is \
not a drill.[grace_period ? " Estimated time of appearance: [grace_period] seconds. Use this time to prepare." : ""]", \
"Central Command Higher Dimensional Affairs", 'sound/magic/clockwork/ark_activation.ogg')
set_security_level("delta")
for(var/V in SSticker.mode.servants_of_ratvar)
var/datum/mind/M = V
if(ishuman(M.current))
M.current.add_overlay(mutable_appearance('icons/effects/genetics.dmi', "servitude", -MUTATIONS_LAYER))
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/open_portal(turf/T)
new/obj/effect/clockwork/city_of_cogs_rift(T)
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/spawn_animation()
var/turf/T = get_turf(src)
new/obj/effect/clockwork/general_marker/inathneq(T)
hierophant_message("<span class='inathneq'>\"[text2ratvar("Engine, come forth and show your servants your mercy")]!\"</span>")
playsound(T, 'sound/magic/clockwork/invoke_general.ogg', 30, 0)
sleep(10)
new/obj/effect/clockwork/general_marker/sevtug(T)
hierophant_message("<span class='sevtug'>\"[text2ratvar("Engine, come forth and show this station your decorating skills")]!\"</span>")
playsound(T, 'sound/magic/clockwork/invoke_general.ogg', 45, 0)
sleep(10)
new/obj/effect/clockwork/general_marker/nezbere(T)
hierophant_message("<span class='nezbere'>\"[text2ratvar("Engine, come forth and shine your light across this realm")]!!\"</span>")
playsound(T, 'sound/magic/clockwork/invoke_general.ogg', 60, 0)
sleep(10)
new/obj/effect/clockwork/general_marker/nzcrentr(T)
hierophant_message("<span class='nzcrentr'>\"[text2ratvar("Engine, come forth")].\"</span>")
playsound(T, 'sound/magic/clockwork/invoke_general.ogg', 75, 0)
sleep(10)
playsound(T, 'sound/magic/clockwork/invoke_general.ogg', 100, 0)
var/list/open_turfs = list()
for(var/turf/open/OT in orange(1, T))
if(!is_blocked_turf(OT, TRUE))
@@ -52,16 +68,10 @@
if(open_turfs.len)
for(var/mob/living/L in T)
L.forceMove(pick(open_turfs))
resistance_flags &= ~INDESTRUCTIBLE
density = TRUE
invisibility = 0
glow = new(get_turf(src))
countdown = new(src)
countdown.start()
var/area/gate_area = get_area(src)
hierophant_message("<span class='large_brass'><b>An Ark of the Clockwork Justicar has been created in [gate_area.map_name]!</b></span>", FALSE, src)
hierophant_message("<span class='bold large_brass'>The Ark has activated! [grace_period ? "You have [round(grace_period / 60)] minutes until the crew invades! " : ""]Defend it at all costs!</span>", FALSE, src)
sound_to_playing_players(volume = 10, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_charging.ogg', TRUE))
seconds_until_activation = 0
SSshuttle.registerHostileEnvironment(src)
START_PROCESSING(SSprocessing, src)
/obj/structure/destructible/clockwork/massive/celestial_gateway/Destroy()
STOP_PROCESSING(SSprocessing, src)
@@ -76,6 +86,18 @@
if(countdown)
qdel(countdown)
countdown = null
for(var/mob/L in GLOB.player_list)
if(L.z == z)
L.forceMove(get_turf(pick(GLOB.generic_event_spawns)))
L.overlay_fullscreen("flash", /obj/screen/fullscreen/flash/static)
L.clear_fullscreen("flash", 30)
if(isliving(L))
var/mob/living/LI = L
LI.Stun(50)
for(var/obj/effect/clockwork/city_of_cogs_rift/R in GLOB.all_clockwork_objects)
qdel(R)
if(GLOB.ark_of_the_clockwork_justiciar == src)
GLOB.ark_of_the_clockwork_justiciar = null
. = ..()
/obj/structure/destructible/clockwork/massive/celestial_gateway/deconstruct(disassembled = TRUE)
@@ -84,12 +106,13 @@
resistance_flags |= INDESTRUCTIBLE
countdown.stop()
visible_message("<span class='userdanger'>[src] begins to pulse uncontrollably... you might want to run!</span>")
sound_to_playing_players(volume = 50, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_disrupted.ogg', 0))
sound_to_playing_players(volume = 50, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_disrupted.ogg'))
make_glow()
glow.icon_state = "clockwork_gateway_disrupted"
resistance_flags |= INDESTRUCTIBLE
sleep(27)
explosion(src, 1, 3, 8, 8)
sound_to_playing_players('sound/effects/explosion_distant.ogg', volume = 50)
qdel(src)
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/make_glow()
@@ -101,46 +124,11 @@
var/damage = max((obj_integrity * 0.7) / severity, 100) //requires multiple bombs to take down
take_damage(damage, BRUTE, "bomb", 0)
/obj/structure/destructible/clockwork/massive/celestial_gateway/attackby(obj/item/I, mob/living/user, params) //add components directly to the ark
if(!is_servant_of_ratvar(user) || !still_needs_components())
return ..()
if(istype(I, /obj/item/clockwork/component))
var/obj/item/clockwork/component/C = I
if(required_components[C.component_id])
required_components[C.component_id]--
to_chat(user, "<span class='notice'>You add [C] to [src].</span>")
user.drop_item()
qdel(C)
else
to_chat(user, "<span class='notice'>[src] has enough [get_component_name(C.component_id)][C.component_id != REPLICANT_ALLOY ? "s":""].</span>")
return 1
else if(istype(I, /obj/item/clockwork/slab))
var/obj/item/clockwork/slab/S = I
var/used_components = FALSE
var/used_all = TRUE
for(var/i in S.stored_components)
if(required_components[i])
var/to_use = min(S.stored_components[i], required_components[i])
required_components[i] -= to_use
S.stored_components[i] -= to_use
if(to_use)
used_components = TRUE
if(S.stored_components[i])
used_all = FALSE
if(used_components)
update_slab_info(S)
user.visible_message("<span class='notice'>[user][used_all ? "":" partially"] empties [S] into [src].</span>", \
"<span class='notice'>You offload [used_all ? "all":"some"] of your slab's components into [src].</span>")
return 1
else
return ..()
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/still_needs_components()
for(var/i in required_components)
if(required_components[i])
return TRUE
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/get_arrival_text(s_on_time)
if(seconds_until_activation)
return "[seconds_until_activation][s_on_time ? "S" : ""]"
if(grace_period)
return "[grace_period][s_on_time ? "S" : ""]"
. = "IMMINENT"
if(!obj_integrity)
. = "DETONATING"
@@ -152,97 +140,87 @@
..()
icon_state = initial(icon_state)
if(is_servant_of_ratvar(user) || isobserver(user))
if(still_needs_components())
to_chat(user, "<span class='big'><b>Components required until activation:</b></span>")
for(var/i in required_components)
if(required_components[i])
to_chat(user, "[get_component_icon(i)] <span class='[get_component_span(i)]'>[get_component_name(i)][i != REPLICANT_ALLOY ? "s":""]:</span> \
<span class='[get_component_span(i)]_large'>[required_components[i]]</span>")
if(!active)
to_chat(user, "<span class='big'><b>Seconds until the Ark's activation:</b> [get_arrival_text(TRUE)]</span>")
else
if(grace_period)
to_chat(user, "<span class='big'><b>Crew grace period time remaining:</b> [get_arrival_text(TRUE)]</span>")
else
to_chat(user, "<span class='big'><b>Seconds until Ratvar's arrival:</b> [get_arrival_text(TRUE)]</span>")
switch(progress_in_seconds)
if(-INFINITY to GATEWAY_REEBE_FOUND)
to_chat(user, "<span class='heavy_brass'>The Ark is feeding power into the bluespace field.</span>")
if(GATEWAY_REEBE_FOUND to GATEWAY_RATVAR_COMING)
to_chat(user, "<span class='heavy_brass'>The field is ripping open a copy of itself in Ratvar's prison.</span>")
if(GATEWAY_RATVAR_COMING to INFINITY)
to_chat(user, "<span class='heavy_brass'>With the bluespace field established, Ratvar is preparing to come through!</span>")
else
if(!active)
to_chat(user, "<span class='warning'>Whatever it is, it doesn't seem to be active.</span>")
else
to_chat(user, "<span class='big'><b>Seconds until Ratvar's arrival:</b> [get_arrival_text(TRUE)]</span>")
switch(progress_in_seconds)
if(-INFINITY to GATEWAY_REEBE_FOUND)
to_chat(user, "<span class='heavy_brass'>It's still opening.</span>")
to_chat(user, "<span class='warning'>You see a swirling bluespace anomaly steadily growing in intensity.</span>")
if(GATEWAY_REEBE_FOUND to GATEWAY_RATVAR_COMING)
to_chat(user, "<span class='heavy_brass'>It's reached the Celestial Derelict and is drawing power from it.</span>")
to_chat(user, "<span class='warning'>The anomaly is stable, and you can see flashes of something from it.</span>")
if(GATEWAY_RATVAR_COMING to INFINITY)
to_chat(user, "<span class='heavy_brass'>Ratvar is coming through the gateway!</span>")
else
switch(progress_in_seconds)
if(-INFINITY to GATEWAY_REEBE_FOUND)
to_chat(user, "<span class='warning'>It's a swirling mass of blackness.</span>")
if(GATEWAY_REEBE_FOUND to GATEWAY_RATVAR_COMING)
to_chat(user, "<span class='warning'>It seems to be leading somewhere.</span>")
if(GATEWAY_RATVAR_COMING to INFINITY)
to_chat(user, "<span class='boldwarning'>Something is coming through!</span>")
to_chat(user, "<span class='boldwarning'>The anomaly is stable! Something is coming through!</span>")
/obj/structure/destructible/clockwork/massive/celestial_gateway/process()
if(seconds_until_activation == -1) //we never do anything
return
adjust_clockwork_power(2.5) //Provides weak power generation on its own
if(seconds_until_activation)
if(!countdown)
countdown = new(src)
countdown.start()
seconds_until_activation--
if(!GLOB.script_scripture_unlocked && initial_activation_delay * 0.5 > seconds_until_activation)
GLOB.script_scripture_unlocked = TRUE
hierophant_message("<span class='large_brass bold'>The Ark is halfway prepared. Script scripture is now available!</span>")
if(!seconds_until_activation)
cry_havoc()
seconds_until_activation = -1 //we'll set this after cry_havoc()
return
if(!first_sound_played || prob(7))
for(var/M in GLOB.player_list)
for(var/mob/M in GLOB.player_list)
if(M && !isnewplayer(M))
to_chat(M, "<span class='warning'><b>You hear otherworldly sounds from the [dir2text(get_dir(get_turf(M), get_turf(src)))]...</span>")
if(M.z == z)
to_chat(M, "<span class='warning'><b>You hear otherworldly sounds from the [dir2text(get_dir(get_turf(M), get_turf(src)))]...</span>")
else
to_chat(M, "<span class='boldwarning'>You hear otherworldly sounds from all around you...</span>")
if(!obj_integrity)
return 0
var/convert_dist = 1 + (round(Floor(progress_in_seconds, 15) * 0.067))
for(var/t in RANGE_TURFS(convert_dist, loc))
var/turf/T = t
if(!T)
continue
if(get_dist(T, src) < 2)
if(iswallturf(T))
var/turf/closed/wall/W = T
W.dismantle_wall()
else if(t && (isclosedturf(T) || !is_blocked_turf(T)))
T.ChangeTurf(/turf/open/floor/clockwork)
var/dist = cheap_hypotenuse(T.x, T.y, x, y)
if(dist < convert_dist)
T.ratvar_act(FALSE, TRUE, 3)
return
for(var/turf/closed/wall/W in RANGE_TURFS(2, src))
W.dismantle_wall()
for(var/obj/O in orange(1, src))
if(!O.pulledby && !istype(O, /obj/effect) && O.density)
if(!step_away(O, src, 2) || get_dist(O, src) < 2)
O.take_damage(50, BURN, "bomb")
O.update_icon()
if(still_needs_components())
if(!first_sound_played)
priority_announce("Massive energy anomaly detected on short-range scanners. Attempting to triangulate location...", "Anomaly Alert")
sound_to_playing_players(volume = 10, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_charging.ogg', 1))
first_sound_played = TRUE
make_glow()
glow.icon_state = "clockwork_gateway_components"
var/used_components = FALSE
for(var/i in required_components)
if(required_components[i])
var/to_use = min(GLOB.clockwork_component_cache[i], required_components[i])
required_components[i] -= to_use
GLOB.clockwork_component_cache[i] -= to_use
if(to_use)
used_components = TRUE
if(used_components)
update_slab_info()
if(still_needs_components())
return
if(grace_period)
grace_period--
return
progress_in_seconds += GATEWAY_SUMMON_RATE
switch(progress_in_seconds)
if(-INFINITY to GATEWAY_REEBE_FOUND)
if(!second_sound_played)
new /obj/effect/temp_visual/decoy/fading/threesecond(loc, glow)
sound_to_playing_players(volume = 30, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_charging.ogg', 1))
for(var/V in GLOB.generic_event_spawns)
addtimer(CALLBACK(src, .proc/open_portal, get_turf(V)), rand(100, 600))
sound_to_playing_players('sound/magic/clockwork/invoke_general.ogg', 30, FALSE)
sound_to_playing_players(volume = 30, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_charging.ogg', TRUE))
second_sound_played = TRUE
make_glow()
glow.icon_state = "clockwork_gateway_charging"
if(GATEWAY_REEBE_FOUND to GATEWAY_RATVAR_COMING)
if(!third_sound_played)
var/area/gate_area = get_area(src)
priority_announce("Location of massive energy anomaly has been triangulated. Location: [gate_area.map_name].", "Anomaly Alert")
new /obj/effect/temp_visual/decoy/fading/threesecond(loc, glow)
sound_to_playing_players(volume = 35, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_active.ogg', 1))
sound_to_playing_players(volume = 35, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_active.ogg', TRUE))
third_sound_played = TRUE
make_glow()
glow.icon_state = "clockwork_gateway_active"
if(GATEWAY_RATVAR_COMING to GATEWAY_RATVAR_ARRIVAL)
if(!fourth_sound_played)
new /obj/effect/temp_visual/decoy/fading/threesecond(loc, glow)
sound_to_playing_players(volume = 40, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_closing.ogg', 1))
sound_to_playing_players(volume = 40, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_closing.ogg', TRUE))
fourth_sound_played = TRUE
make_glow()
glow.icon_state = "clockwork_gateway_closing"
@@ -252,38 +230,25 @@
resistance_flags |= INDESTRUCTIBLE
purpose_fulfilled = TRUE
make_glow()
animate(glow, transform = matrix() * 3, time = 125)
sound_to_playing_players('sound/effects/ratvar_rises.ogg', channel = CHANNEL_JUSTICAR_ARK) //End the sounds
animate(glow, transform = matrix() * 1.5, alpha = 255, time = 125)
sound_to_playing_players(volume = 100, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/ratvar_rises.ogg')) //End the sounds
sleep(125)
var/turf/startpoint = get_turf(src)
make_glow()
glow.transform = matrix() * 4.5
animate(glow, transform = matrix() * 0.1, time = 10)
QDEL_IN(src, 10)
animate(glow, transform = matrix() * 3, alpha = 0, time = 5)
var/turf/startpoint = get_turf(src)
QDEL_IN(src, 3)
sleep(3)
GLOB.clockwork_gateway_activated = TRUE
new/obj/structure/destructible/clockwork/massive/ratvar(startpoint)
send_to_playing_players("<span class='inathneq_large'>\"[text2ratvar("See Engine's mercy")]!\"</span>\n\
<span class='sevtug_large'>\"[text2ratvar("Observe Engine's design skills")]!\"</span>\n<span class='nezbere_large'>\"[text2ratvar("Behold Engine's light")]!!\"</span>\n\
<span class='nzcrentr_large'>\"[text2ratvar("Gaze upon Engine's power")].\"</span>")
sound_to_playing_players('sound/effects/empulse.ogg')
var/x0 = startpoint.x
var/y0 = startpoint.y
for(var/I in spiral_range_turfs(255, startpoint))
var/turf/T = I
if(!T)
continue
var/dist = cheap_hypotenuse(T.x, T.y, x0, y0)
if(dist < 100)
dist = TRUE
else
dist = FALSE
T.ratvar_act(dist, TRUE)
CHECK_TICK
var/obj/structure/destructible/clockwork/massive/ratvar/R = new(startpoint)
var/turf/T = locate(round(world.maxx * 0.5, 1), round(world.maxy * 0.5, 1), ZLEVEL_STATION_PRIMARY) //approximate center of the station
R.forceMove(T)
//the actual appearance of the Ark of the Clockwork Justicar; an object so the edges of the gate can be clicked through.
/obj/effect/clockwork/overlay/gateway_glow
icon = 'icons/effects/96x96.dmi'
icon_state = "clockwork_gateway_charging"
icon_state = "clockwork_gateway_components"
pixel_x = -32
pixel_y = -32
layer = BELOW_OPEN_DOOR_LAYER

View File

@@ -42,7 +42,7 @@
return affected
/obj/structure/destructible/clockwork/powered/clockwork_obelisk/attack_hand(mob/living/user)
if(!is_servant_of_ratvar(user) || total_accessable_power() < hierophant_cost || !anchored)
if(!is_servant_of_ratvar(user) || !can_access_clockwork_power(src, hierophant_cost) || !anchored)
to_chat(user, "<span class='warning'>You place your hand on [src], but it doesn't react.</span>")
return
var/choice = alert(user,"You place your hand on [src]...",,"Hierophant Broadcast","Spatial Gateway","Cancel")
@@ -87,7 +87,7 @@
active = TRUE
clockwork_say(user, text2ratvar("Spatial Gateway, activate!"))
return
return_power(gateway_cost) //if we didn't return above, ie, successfully create a gateway, we give the power back
adjust_clockwork_power(gateway_cost) //if we didn't return above, ie, successfully create a gateway, we give the power back
/obj/structure/destructible/clockwork/powered/clockwork_obelisk/process()
if(!anchored)

View File

@@ -1,174 +0,0 @@
//Used by the Geis scripture to hold its target in place
/obj/structure/destructible/clockwork/geis_binding
name = "glowing ring"
desc = "A flickering, glowing purple ring around a target."
clockwork_desc = "A binding ring around a target, preventing them from taking action."
max_integrity = 20
light_range = 2
light_power = 0.8
light_color = "#AF0AAF"
anchored = FALSE
density = FALSE
immune_to_servant_attacks = TRUE
icon = 'icons/effects/clockwork_effects.dmi'
icon_state = "geisbinding_full"
break_message = null
break_sound = 'sound/magic/repulse.ogg'
debris = list()
can_buckle = TRUE
buckle_lying = 0
var/mob_layer = MOB_LAYER
var/last_mob_health = 0
var/apply_time = 0
/obj/structure/destructible/clockwork/geis_binding/Initialize(mapload, obj/item/clockwork/slab/the_slab)
. = ..()
START_PROCESSING(SSprocessing, src)
/obj/structure/destructible/clockwork/geis_binding/Destroy()
STOP_PROCESSING(SSprocessing, src)
return ..()
/obj/structure/destructible/clockwork/geis_binding/examine(mob/user)
icon_state = "geisbinding_full"
..()
icon_state = "geisbinding"
/obj/structure/destructible/clockwork/geis_binding/process()
var/tick_damage = 1
if(locate(/obj/effect/clockwork/sigil/submission) in loc)
tick_damage *= 0.5
if(LAZYLEN(buckled_mobs))
for(var/V in buckled_mobs)
var/mob/living/L = V
if(is_servant_of_ratvar(L)) //servants are freed automatically
take_damage(obj_integrity)
return
if(last_mob_health > L.health)
tick_damage += last_mob_health - L.health
last_mob_health = L.health
if(L.layer != mob_layer)
mob_layer = L.layer
layer = mob_layer - 0.01
cut_overlays()
add_overlay(mutable_appearance('icons/effects/clockwork_effects.dmi', "geisbinding_top", mob_layer + 0.01))
break
take_damage(tick_damage, sound_effect = FALSE)
playsound(src, 'sound/effects/empulse.ogg', tick_damage * 40, TRUE, -4)
/obj/structure/destructible/clockwork/geis_binding/attack_hand(mob/living/user)
return
/obj/structure/destructible/clockwork/geis_binding/attackby(obj/item/I, mob/user, params)
if(is_servant_of_ratvar(user) && istype(I, /obj/item/clockwork/slab))
user.visible_message("<span class='warning'>[user] starts to dispel [src]...</span>", "<span class='danger'>You start to dispel [src]...</span>")
if(do_after(user, 30, target = src))
user.visible_message("<span class='warning'>[user] dispels [src]!</span>", "<span class='danger'>You dispel [src]!</span>")
take_damage(obj_integrity)
return 1
return ..()
/obj/structure/destructible/clockwork/geis_binding/emp_act(severity)
new /obj/effect/temp_visual/emp(loc)
qdel(src)
/obj/structure/destructible/clockwork/geis_binding/post_buckle_mob(mob/living/M)
..()
if(M.buckled == src)
desc = "A flickering, glowing purple ring around [M]."
clockwork_desc = "A binding ring around [M], preventing [M.p_them()] from taking action."
icon_state = "geisbinding"
mob_layer = M.layer
layer = mob_layer - 0.01
add_overlay(mutable_appearance('icons/effects/clockwork_effects.dmi', "geisbinding_top", mob_layer + 0.01))
last_mob_health = M.health
apply_time = world.time
for(var/obj/item/I in M.held_items)
M.dropItemToGround(I)
for(var/i in M.get_empty_held_indexes())
var/obj/item/geis_binding/B = new(M)
M.put_in_hands(B, i)
if(iscarbon(M))
var/mob/living/carbon/C = M
if(!C.handcuffed)
C.handcuffed = new /obj/item/restraints/handcuffs/energy/clock(C)
M.regenerate_icons()
M.visible_message("<span class='warning'>A [name] appears around [M]!</span>", "<span class='warning'>A [name] appears around you!</span>")
repair_and_interrupt()
else
var/obj/effect/temp_visual/ratvar/geis_binding/G = new /obj/effect/temp_visual/ratvar/geis_binding(M.loc)
var/obj/effect/temp_visual/ratvar/geis_binding/T = new /obj/effect/temp_visual/ratvar/geis_binding/top(M.loc)
G.layer = mob_layer - 0.01
T.layer = mob_layer + 0.01
G.alpha = alpha
T.alpha = alpha
animate(G, transform = matrix()*2, alpha = 0, time = 8, easing = EASE_OUT)
animate(T, transform = matrix()*2, alpha = 0, time = 8, easing = EASE_OUT)
M.visible_message("<span class='warning'>[src] snaps into glowing pieces and dissipates!</span>")
M.AdjustStun(-130 + (apply_time - world.time), 1, 1) //remove exactly as much stun as was applied
if(iscarbon(M))
var/mob/living/carbon/C = M
C.silent = max(C.silent - 7, 0)
for(var/obj/item/geis_binding/GB in M.held_items)
M.dropItemToGround(GB, TRUE)
if(iscarbon(M))
var/mob/living/carbon/C = M
if(istype(C.handcuffed, /obj/item/restraints/handcuffs/energy/clock))
QDEL_NULL(C.handcuffed)
C.update_handcuffed()
/obj/structure/destructible/clockwork/geis_binding/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
playsound(src, 'sound/effects/empulse.ogg', 50, 1)
/obj/structure/destructible/clockwork/geis_binding/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
. = ..()
if(.)
update_icon()
/obj/structure/destructible/clockwork/geis_binding/update_icon()
alpha = min(255 * ((obj_integrity/max_integrity) + 0.2) , 255)
/obj/structure/destructible/clockwork/geis_binding/proc/repair_and_interrupt()
obj_integrity = max_integrity
update_icon()
for(var/m in buckled_mobs)
var/mob/living/L = m
if(L)
L.Stun(130, 1, 1) //basically here to act as a mute for borgs
if(iscarbon(L))
var/mob/living/carbon/C = L
C.silent += 7
visible_message("<span class='sevtug'>[src] flares brightly!</span>")
var/obj/effect/temp_visual/ratvar/geis_binding/G1 = new /obj/effect/temp_visual/ratvar/geis_binding(loc)
var/obj/effect/temp_visual/ratvar/geis_binding/G2 = new /obj/effect/temp_visual/ratvar/geis_binding(loc)
var/obj/effect/temp_visual/ratvar/geis_binding/T1 = new /obj/effect/temp_visual/ratvar/geis_binding/top(loc)
var/obj/effect/temp_visual/ratvar/geis_binding/T2 = new /obj/effect/temp_visual/ratvar/geis_binding/top(loc)
G1.layer = mob_layer - 0.01
G2.layer = mob_layer - 0.01
T1.layer = mob_layer + 0.01
T2.layer = mob_layer + 0.01
animate(G1, pixel_y = pixel_y + 9, alpha = 0, time = 8, easing = EASE_IN)
animate(G2, pixel_y = pixel_y - 9, alpha = 0, time = 8, easing = EASE_IN)
animate(T1, pixel_y = pixel_y + 9, alpha = 0, time = 8, easing = EASE_IN)
animate(T2, pixel_y = pixel_y - 9, alpha = 0, time = 8, easing = EASE_IN)
/obj/structure/destructible/clockwork/geis_binding/user_unbuckle_mob(mob/living/buckled_mob, mob/user)
if(buckled_mob != user)
return ..()
/obj/item/geis_binding
name = "glowing ring"
desc = "A flickering ring preventing you from holding items."
icon = 'icons/effects/clockwork_effects.dmi'
icon_state = "geisbinding_full"
flags_1 = NODROP_1|ABSTRACT_1|DROPDEL_1
/obj/item/geis_binding/pre_attackby(atom/target, mob/living/user, params)
return FALSE
/obj/item/restraints/handcuffs/energy/clock
name = "glowing rings"
desc = "Flickering rings preventing you from holding items."
icon = 'icons/effects/clockwork_effects.dmi'
icon_state = "geisbinding_full"
flags_1 = NODROP_1|ABSTRACT_1|DROPDEL_1

View File

@@ -0,0 +1,109 @@
//Used to "declare war" against the station. The servants' equipment will be permanently supercharged, and the Ark given extra time to prepare.
//This will send an announcement to the station, meaning that they will be warned very early in advance about the impending attack.
/obj/structure/destructible/clockwork/heralds_beacon
name = "herald's beacon"
desc = "An imposing spire formed of brass, with a thrumming gemstone at its peak."
clockwork_desc = "A massively-powerful beacon. If enough servants decide to activate it, it will send an incredibly large energy pulse to the Ark, \
permanently empowering many clockwork objects and reducing all power costs by 50%, but alerting the crew to your presence. It doesn't have enough \
energy to sustain itself for long, and if not activated within five minutes, it will permanently shut down."
icon_state = "interdiction_lens"
break_message = "<span class='warning'>The beacon crackles with power before collapsing into pieces!</span>"
max_integrity = 250
light_color = "#EF078E"
var/time_remaining = 300 //Amount of seconds left to vote on whether or not to activate the beacon
var/list/voters //People who have voted to activate the beacon
var/votes_needed = 0 //How many votes are needed to activate the beacon
var/available = FALSE //If the beacon can be used
/obj/structure/destructible/clockwork/heralds_beacon/Initialize()
. = ..()
voters = list()
START_PROCESSING(SSprocessing, src)
/obj/structure/destructible/clockwork/heralds_beacon/Destroy()
STOP_PROCESSING(SSprocessing, src)
. = ..()
/obj/structure/destructible/clockwork/heralds_beacon/process()
if(!available)
if(istype(SSticker.mode, /datum/game_mode/clockwork_cult))
available = TRUE
else
return
if(!SSticker.mode.servants_of_ratvar.len)
return
if(!votes_needed)
var/servants = SSticker.mode.servants_of_ratvar.len
if(servants)
votes_needed = round(servants * 0.66)
time_remaining--
if(!time_remaining)
hierophant_message("<span class='bold sevtug_small'>[src] has lost its power, and can no longer be activated.</span>")
for(var/mob/M in GLOB.player_list)
if(isobserver(M) || is_servant_of_ratvar(M))
M.playsound_local(M, 'sound/magic/blind.ogg', 50, FALSE)
available = FALSE
icon_state = "interdiction_lens_unwrenched"
STOP_PROCESSING(SSprocessing, src)
/obj/structure/destructible/clockwork/heralds_beacon/examine(mob/user)
..()
if(isobserver(user) || is_servant_of_ratvar(user))
if(!available)
if(!GLOB.ratvar_approaches)
to_chat(user, "<span class='bold alloy'>It can no longer be activated.</span>")
else
to_chat(user, "<span class='bold neovgre_small'>It has been activated!</span>")
else
to_chat(user, "<span class='brass'>There are <b>[time_remaining]</b> second[time_remaining != 1 ? "s" : ""] remaining to vote.</span>")
to_chat(user, "<span class='big brass'>There are <b>[voters.len]/[votes_needed]</b> votes to activate the beacon!</span>")
/obj/structure/destructible/clockwork/heralds_beacon/attack_hand(mob/living/user)
if(!is_servant_of_ratvar(user))
to_chat(user, "<span class='notice'>You can tell how powerful [src] is; you know better than to touch it.</span>")
return
if(!available)
to_chat(user, "<span class='danger'>You can no longer vote with [src].</span>")
return
var/voting = !(user.key in voters)
if(alert(user, "[voting ? "Cast a" : "Undo your"] vote to activate the beacon?", "Herald's Beacon", "Change Vote", "Cancel") == "Cancel")
return
if(!user.canUseTopic(src) || !is_servant_of_ratvar(user) || !available)
return
if(voting)
if(user.key in voters)
return
voters += user.key
else
if(!user.key in voters)
return
voters -= user.key
var/votes_left = votes_needed - voters.len
message_admins("[ADMIN_LOOKUPFLW(user)] has [voting ? "voted" : "undone their vote"] to activate [src]! [ADMIN_JMP(user)]")
hierophant_message("<span class='brass'><b>[user.real_name]</b> has [voting ? "voted" : "undone their vote"] to activate [src]! The beacon needs [votes_left] more votes to activate.")
for(var/mob/M in GLOB.player_list)
if(isobserver(M) || is_servant_of_ratvar(M))
M.playsound_local(M, 'sound/magic/clockwork/fellowship_armory.ogg', 50, FALSE)
if(!votes_left)
herald_the_justiciar()
/obj/structure/destructible/clockwork/heralds_beacon/proc/herald_the_justiciar()
priority_announce("A powerful group of fanatical zealots following the cause of Ratvar have brazenly sacrificed stealth for power, and dare anyone \
to try and stop them.", title = "The Justiciar Comes", sound = 'sound/magic/clockwork/ark_activation.ogg')
GLOB.ratvar_approaches = TRUE
available = FALSE
STOP_PROCESSING(SSprocessing, src)
icon_state = "interdiction_lens_active"
hierophant_message("<span class='big bold brass'>The beacon's activation has given your team great power! Many of your objects are permanently empowered!</span>")
for(var/mob/living/simple_animal/hostile/clockwork/C in GLOB.all_clockwork_mobs)
if(C.stat == DEAD)
continue
C.update_values()
to_chat(C, C.empower_string)
for(var/mob/living/carbon/human/H in GLOB.living_mob_list)
if(is_servant_of_ratvar(H))
to_chat(H, "<span class='bold alloy'>The beacon's power warps your body into a clockwork form! You are now immune to many hazards, and your body is more robust against damage!</span>")
H.set_species(/datum/species/golem/clockwork/no_scrap)
SSshuttle.registerHostileEnvironment(GLOB.ark_of_the_clockwork_justiciar) //no leaving when we need to purge you, heretics
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
G.grace_period = FALSE //no grace period if we've declared war

View File

@@ -32,7 +32,7 @@
/obj/structure/destructible/clockwork/powered/mania_motor/attack_hand(mob/living/user)
if(user.canUseTopic(src, !issilicon(user), NO_DEXTERY) && is_servant_of_ratvar(user))
if(!total_accessable_power() >= mania_cost)
if(!can_access_clockwork_power(src, mania_cost))
to_chat(user, "<span class='warning'>[src] needs more power to function!</span>")
return 0
toggle(0, user)

View File

@@ -11,7 +11,7 @@
break_message = "<span class='warning'>The warden's eye gives a glare of utter hate before falling dark!</span>"
debris = list(/obj/item/clockwork/component/belligerent_eye/blind_eye = 1)
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
var/damage_per_tick = 2.7
var/damage_per_tick = 2.5
var/sight_range = 3
var/atom/movable/target
var/list/idle_messages = list(" sulkily glares around.", " lazily drifts from side to side.", " looks around for something to burn.", " slowly turns in circles.")
@@ -68,9 +68,9 @@
else
R.reveal(10)
if(prob(50))
L.playsound_local(null,'sound/machines/clockcult/ocularwarden-dot1.ogg',50,1)
L.playsound_local(null,'sound/machines/clockcult/ocularwarden-dot1.ogg',75 * get_efficiency_mod(),1)
else
L.playsound_local(null,'sound/machines/clockcult/ocularwarden-dot2.ogg',50,1)
L.playsound_local(null,'sound/machines/clockcult/ocularwarden-dot2.ogg',75 * get_efficiency_mod(),1)
L.adjustFireLoss((!iscultist(L) ? damage_per_tick : damage_per_tick * 2) * get_efficiency_mod()) //Nar-Sian cultists take additional damage
if(GLOB.ratvar_awakens && L)
L.adjust_fire_stacks(damage_per_tick)
@@ -89,10 +89,10 @@
visible_message("<span class='warning'>[src] swivels to face [target]!</span>")
if(isliving(target))
var/mob/living/L = target
to_chat(L, "<span class='heavy_brass'>\"I SEE YOU!\"</span>\n<span class='userdanger'>[src]'s gaze [GLOB.ratvar_awakens ? "melts you alive" : "burns you"]!</span>")
to_chat(L, "<span class='neovgre'>\"I SEE YOU!\"</span>\n<span class='userdanger'>[src]'s gaze [GLOB.ratvar_awakens ? "melts you alive" : "burns you"]!</span>")
else if(istype(target, /obj/mecha))
var/obj/mecha/M = target
to_chat(M.occupant, "<span class='heavy_brass'>\"I SEE YOU!\"</span>" )
to_chat(M.occupant, "<span class='neovgre'>\"I SEE YOU!\"</span>" )
else if(prob(0.5)) //Extremely low chance because of how fast the subsystem it uses processes
if(prob(50))
visible_message("<span class='notice'>[src][pick(idle_messages)]</span>")
@@ -112,7 +112,7 @@
continue
if(is_servant_of_ratvar(L) || (L.disabilities & BLIND) || L.null_rod_check())
continue
if(L.stat || L.restrained() || L.buckled || L.lying || istype(L.buckled, /obj/structure/destructible/clockwork/geis_binding))
if(L.stat || L.restrained() || L.buckled || L.lying)
continue
if(ishostile(L))
var/mob/living/simple_animal/hostile/H = L
@@ -134,3 +134,15 @@
target = null
visible_message("<span class='warning'>[src] settles and seems almost disappointed.</span>")
return 1
/obj/structure/destructible/clockwork/ocular_warden/get_efficiency_mod()
if(GLOB.ratvar_awakens)
return 2
. = 1
if(target)
for(var/turf/T in getline(src, target))
for(var/obj/structure/O in T)
if(O.density)
. -= 0.15
. -= (get_dist(src, target) * 0.05)
. = max(., 0.1) //The lowest damage a warden can do is 10% of its normal amount (0.25 by default)

View File

@@ -1,135 +0,0 @@
//Prolonging Prism: A prism that consumes power to delay the shuttle
/obj/structure/destructible/clockwork/powered/prolonging_prism
name = "prolonging prism"
desc = "A dark onyx prism, held in midair by spiraling tendrils of stone."
clockwork_desc = "A powerful prism that will delay the arrival of an emergency shuttle."
icon_state = "prolonging_prism_inactive"
active_icon = "prolonging_prism"
inactive_icon = "prolonging_prism_inactive"
unanchored_icon = "prolonging_prism_unwrenched"
construction_value = 20
max_integrity = 125
break_message = "<span class='warning'>The prism falls to the ground with a heavy thud!</span>"
debris = list(/obj/item/clockwork/alloy_shards/small = 3, \
/obj/item/clockwork/alloy_shards/medium = 1, \
/obj/item/clockwork/alloy_shards/large = 1, \
/obj/item/clockwork/component/vanguard_cogwheel/onyx_prism = 1)
var/static/list/component_refund = list(VANGUARD_COGWHEEL = 2, GEIS_CAPACITOR = 1, REPLICANT_ALLOY = 1)
var/static/delay_cost = 3000
var/static/delay_cost_increase = 1250
var/static/delay_remaining = 0
/obj/structure/destructible/clockwork/powered/prolonging_prism/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
if(SSshuttle.emergency.mode == SHUTTLE_DOCKED || SSshuttle.emergency.mode == SHUTTLE_IGNITING || SSshuttle.emergency.mode == SHUTTLE_STRANDED || SSshuttle.emergency.mode == SHUTTLE_ESCAPE)
to_chat(user, "<span class='inathneq'>An emergency shuttle has arrived and this prism is no longer useful; attempt to activate it to gain a partial refund of components used.</span>")
else
var/efficiency = get_efficiency_mod(TRUE)
to_chat(user, "<span class='inathneq_small'>It requires at least <b>[DisplayPower(get_delay_cost())]</b> of power to attempt to delay the arrival of an emergency shuttle by <b>[2 * efficiency]</b> minutes.</span>")
to_chat(user, "<span class='inathneq_small'>This cost increases by <b>[DisplayPower(delay_cost_increase)]</b> for every previous activation.</span>")
/obj/structure/destructible/clockwork/powered/prolonging_prism/forced_disable(bad_effects)
if(active)
if(bad_effects)
try_use_power(MIN_CLOCKCULT_POWER*4)
visible_message("<span class='warning'>[src] emits an airy chuckling sound and falls dark!</span>")
toggle()
return TRUE
/obj/structure/destructible/clockwork/powered/prolonging_prism/attack_hand(mob/living/user)
if(user.canUseTopic(src, !issilicon(user), NO_DEXTERY) && is_servant_of_ratvar(user))
if(SSshuttle.emergency.mode == SHUTTLE_DOCKED || SSshuttle.emergency.mode == SHUTTLE_IGNITING || SSshuttle.emergency.mode == SHUTTLE_STRANDED || SSshuttle.emergency.mode == SHUTTLE_ESCAPE)
to_chat(user, "<span class='brass'>You break [src] apart, refunding some of the components used.</span>")
for(var/i in component_refund)
generate_cache_component(i, src)
take_damage(max_integrity)
return 0
if(active)
return 0
var/turf/T = get_turf(src)
if(!T || !(T.z in GLOB.station_z_levels))
to_chat(user, "<span class='warning'>[src] must be on the station to function!</span>")
return 0
if(SSshuttle.emergency.mode != SHUTTLE_CALL)
to_chat(user, "<span class='warning'>No emergency shuttles are attempting to arrive at the station!</span>")
return 0
if(!try_use_power(get_delay_cost()))
to_chat(user, "<span class='warning'>[src] needs more power to function!</span>")
return 0
delay_cost += delay_cost_increase
delay_remaining += PRISM_DELAY_DURATION
toggle(0, user)
/obj/structure/destructible/clockwork/powered/prolonging_prism/process()
var/turf/own_turf = get_turf(src)
if(SSshuttle.emergency.mode != SHUTTLE_CALL || delay_remaining <= 0 || !own_turf || !(own_turf.z in GLOB.station_z_levels))
forced_disable(FALSE)
return
. = ..()
var/delay_amount = 40
delay_remaining -= delay_amount
var/efficiency = get_efficiency_mod()
SSshuttle.emergency.setTimer(SSshuttle.emergency.timeLeft(1) + (delay_amount * efficiency))
var/highest_y
var/highest_x
var/lowest_y
var/lowest_x
var/list/prism_turfs = list()
for(var/t in SSshuttle.emergency.ripple_area(SSshuttle.getDock("emergency_home")))
prism_turfs[t] = TRUE
var/turf/T = t
if(!highest_y || T.y > highest_y)
highest_y = T.y
if(!highest_x || T.x > highest_x)
highest_x = T.x
if(!lowest_y || T.y < lowest_y)
lowest_y = T.y
if(!lowest_x || T.x < lowest_x)
lowest_x = T.x
var/mean_y = Lerp(lowest_y, highest_y)
var/mean_x = Lerp(lowest_x, highest_x)
if(prob(50))
mean_y = Ceiling(mean_y)
else
mean_y = Floor(mean_y)
if(prob(50))
mean_x = Ceiling(mean_x)
else
mean_x = Floor(mean_x)
var/turf/semi_random_center_turf = locate(mean_x, mean_y, ZLEVEL_STATION_PRIMARY)
for(var/t in getline(src, semi_random_center_turf))
prism_turfs[t] = TRUE
var/placement_style = prob(50)
for(var/t in prism_turfs)
var/turf/T = t
if(placement_style)
if(IsOdd(T.x + T.y))
seven_random_hexes(T, efficiency)
else if(prob(50 * efficiency))
new /obj/effect/temp_visual/ratvar/prolonging_prism(T)
else
if(IsEven(T.x + T.y))
seven_random_hexes(T, efficiency)
else if(prob(50 * efficiency))
new /obj/effect/temp_visual/ratvar/prolonging_prism(T)
CHECK_TICK //we may be going over a hell of a lot of turfs
/obj/structure/destructible/clockwork/powered/prolonging_prism/proc/get_delay_cost()
return Floor(delay_cost, MIN_CLOCKCULT_POWER)
/obj/structure/destructible/clockwork/powered/prolonging_prism/proc/seven_random_hexes(turf/T, efficiency)
var/static/list/hex_states = list("prismhex1", "prismhex2", "prismhex3", "prismhex4", "prismhex5", "prismhex6", "prismhex7")
var/mutable_appearance/hex_combo
for(var/n in hex_states) //BUILD ME A HEXAGON
if(prob(50 * efficiency))
if(!hex_combo)
hex_combo = mutable_appearance('icons/effects/64x64.dmi', n, RIPPLE_LAYER)
else
hex_combo.add_overlay(mutable_appearance('icons/effects/64x64.dmi', n, RIPPLE_LAYER))
if(hex_combo) //YOU BUILT A HEXAGON
hex_combo.pixel_x = -16
hex_combo.pixel_y = -16
hex_combo.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
hex_combo.plane = GAME_PLANE
new /obj/effect/temp_visual/ratvar/prolonging_prism(T, hex_combo)

View File

@@ -1,8 +1,8 @@
//Ratvar himself. Impossible to damage by most standard means, He will dominate the station and all upon it.
//Ratvar himself. Impossible to damage by most standard means, and converts nearby objects and players into clockwork variants and Servants.
/obj/structure/destructible/clockwork/massive/ratvar
name = "Ratvar, the Clockwork Justiciar"
desc = "<span class='userdanger'>What is what is what are what real what is all a lie all a lie it's all a lie why how can what is</span>"
clockwork_desc = "<span class='large_brass'><b><i>Ratvar, the Clockwork Justiciar, your master eternal.</i></b></span>"
desc = "..."
clockwork_desc = "<span class='large_brass bold italics'>Ratvar, free at last!</span>"
icon = 'icons/effects/512x512.dmi'
icon_state = "ratvar"
pixel_x = -235
@@ -13,7 +13,7 @@
light_range = 15
light_color = "#BE8700"
var/atom/prey //Whatever Ratvar is chasing
var/clashing = FALSE //If Ratvar is FUCKING FIGHTING WITH NAR-SIE
var/clashing = FALSE //If Ratvar is fighting with Nar-Sie
var/convert_range = 10
dangerous_possession = TRUE
@@ -22,20 +22,21 @@
GLOB.ratvar_awakens++
for(var/obj/O in GLOB.all_clockwork_objects)
O.ratvar_act()
for(var/mob/living/simple_animal/hostile/clockwork/M in GLOB.all_clockwork_mobs)
M.ratvar_act()
START_PROCESSING(SSobj, src)
INVOKE_ASYNC(SSshuttle.emergency, /obj/docking_port/mobile/emergency.proc/request, null, 0, null, FALSE, 0)
send_to_playing_players("<span class='ratvar'>\"[text2ratvar("ONCE AGAIN MY LIGHT SHALL SHINE ACROSS THIS PATHETIC REALM")]!!\"</span>")
send_to_playing_players("<span class='ratvar'>[text2ratvar("ONCE AGAIN MY LIGHT SHINES AMONG THESE PATHETIC STARS")]</span>")
sound_to_playing_players('sound/effects/ratvar_reveal.ogg')
var/mutable_appearance/alert_overlay = mutable_appearance('icons/effects/clockwork_effects.dmi', "ratvar_alert")
var/area/A = get_area(src)
notify_ghosts("The Justiciar's light calls to you! Reach out to Ratvar in [A.name] to be granted a shell to spread his glory!", null, source = src, alert_overlay = alert_overlay)
INVOKE_ASYNC(SSshuttle.emergency, /obj/docking_port/mobile/emergency.proc/request, null, 10, null, FALSE, 0)
/obj/structure/destructible/clockwork/massive/ratvar/Destroy()
GLOB.ratvar_awakens--
for(var/obj/O in GLOB.all_clockwork_objects)
O.ratvar_act()
STOP_PROCESSING(SSobj, src)
send_to_playing_players("<span class='heavy_brass'><font size=6>\"NO! I will not... be...</font> <font size=5>banished...</font> <font size=4>again...\"</font></span>")
return ..()
/obj/structure/destructible/clockwork/massive/ratvar/attack_ghost(mob/dead/observer/O)
@@ -100,12 +101,12 @@
return
clashing = TRUE
GLOB.cult_narsie.clashing = TRUE
to_chat(world, "<span class='heavy_brass'><font size=5>\"[pick("BLOOD GOD!!!", "NAR-SIE!!!", "AT LAST, YOUR TIME HAS COME!")]\"</font></span>")
to_chat(world, "<span class='cult'><font size=5>\"<b>Ratvar?! How?!</b>\"</font></span>")
clash_of_the_titans(GLOB.cult_narsie) //IT'S TIME FOR THE BATTLE OF THE AGES
to_chat(world, "<span class='bold brass'><font size=5>\"YOU.\"</font></span>")
to_chat(world, "<span class='bold cult'><font size=5>\"Ratvar?!\"</font></span>")
clash_of_the_titans(GLOB.cult_narsie) // >:(
return TRUE
//Put me in Reebe, will you? Ratvar has found and is going to fucking murder Nar-Sie
//Put me in Reebe, will you? Ratvar has found and is going to do a hecking murder on Nar-Sie
/obj/structure/destructible/clockwork/massive/ratvar/proc/clash_of_the_titans(obj/singularity/narsie/narsie)
var/winner = "Undeclared"
var/base_victory_chance = 1
@@ -136,7 +137,7 @@
base_victory_chance *= 2 //The clash has a higher chance of resolving each time both gods attack one another
switch(winner)
if("Ratvar")
send_to_playing_players("<span class='heavy_brass'><font size=5>\"[pick("DIE! DIE! DIE!", "FILTH!!!", "SUFFER!!!", text2ratvar("ROT FOR CENTURIES AS I HAVE!!"))]\"</font></span>\n\
send_to_playing_players("<span class='heavy_brass'><font size=5>\"[pick("DIE.", "ROT.")]\"</font></span>\n\
<span class='cult'><font size=5>\"<b>[pick("Nooooo...", "Not die. To y-", "Die. Ratv-", "Sas tyen re-")]\"</b></font></span>") //nar-sie get out
sound_to_playing_players('sound/magic/clockwork/anima_fragment_attack.ogg')
sound_to_playing_players('sound/magic/demon_dies.ogg', 50)

View File

@@ -0,0 +1,66 @@
#define STARGAZER_RANGE 3 //How many tiles the stargazer can see out to
#define STARGAZER_POWER 20 //How many watts will be produced per second when the stargazer sees starlight
//Stargazer: A very fragile but cheap generator that creates power from starlight.
/obj/structure/destructible/clockwork/stargazer
name = "stargazer"
desc = "A large lantern-shaped machine made of thin brass. It looks fragile."
clockwork_desc = "A lantern-shaped generator that produces power when near starlight."
icon_state = "stargazer"
unanchored_icon = "stargazer_unwrenched"
max_integrity = 40
construction_value = 5
layer = WALL_OBJ_LAYER
break_message = "<span class='warning'>The stargazer's fragile body shatters into pieces!</span>"
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
light_color = "#DAAA18"
var/star_light_star_bright = FALSE //If this stargazer can see starlight
/obj/structure/destructible/clockwork/stargazer/Initialize()
. = ..()
START_PROCESSING(SSprocessing, src)
/obj/structure/destructible/clockwork/stargazer/Destroy()
STOP_PROCESSING(SSprocessing, src)
. = ..()
/obj/structure/destructible/clockwork/stargazer/examine(mob/user)
..()
if(is_servant_of_ratvar(user))
to_chat(user, "<span class='nzcrentr_small'>Generates <b>[DisplayPower(STARGAZER_POWER)]</b> per second while viewing starlight within [STARGAZER_RANGE] tiles.</span>")
if(star_light_star_bright)
to_chat(user, "[is_servant_of_ratvar(user) ? "<span class='nzcrentr_small'>It can see starlight!</span>" : "It's shining brilliantly!"]")
/obj/structure/destructible/clockwork/stargazer/process()
star_light_star_bright = check_starlight()
if(star_light_star_bright)
adjust_clockwork_power(STARGAZER_POWER)
/obj/structure/destructible/clockwork/stargazer/update_anchored(mob/living/user, damage)
. = ..()
star_light_star_bright = check_starlight()
/obj/structure/destructible/clockwork/stargazer/proc/check_starlight()
var/old_status = star_light_star_bright
var/has_starlight
if(!anchored)
has_starlight = FALSE
else
for(var/turf/T in view(3, src))
if(isspaceturf(T))
has_starlight = TRUE
break
if(old_status != has_starlight)
if(has_starlight)
visible_message("<span class='nzcrentr_small'>[src] hums and shines brilliantly!</span>")
playsound(src, 'sound/machines/clockcult/stargazer_activate.ogg', 50, TRUE)
add_overlay("stargazer_light")
set_light(1.5, 5)
else
if(anchored) //We lost visibility somehow
visible_message("<span class='danger'>[src] flickers, and falls dark.</span>")
else
visible_message("<span class='danger'>[src] whooshes quietly as it slides into a less bulky form.</span>")
cut_overlays()
set_light(0)
return has_starlight

View File

@@ -105,7 +105,7 @@
..()
if(is_servant_of_ratvar(user) || isobserver(user))
if(linkedwall)
to_chat(user, "<span class='brass'>It is linked to a Clockwork Wall and will generate a component every <b>[round(get_production_time() * 0.1, 0.1)]</b> seconds!</span>")
to_chat(user, "<span class='brass'>It is linked to a Clockwork Wall and will generate a component every <b>[DisplayTimeText(get_production_time())]</b>!</span>")
else
to_chat(user, "<span class='alloy'>It is unlinked! Construct a Clockwork Wall nearby to generate components!</span>")
to_chat(user, "<b>Stored components:</b>")

View File

@@ -1,171 +0,0 @@
//Tinkerer's Daemon: A machine that rapidly produces components at a power cost.
/obj/structure/destructible/clockwork/powered/tinkerers_daemon
name = "tinkerer's daemon"
desc = "A strange machine with three small brass obelisks attached to it."
clockwork_desc = "An efficient machine that can rapidly produce components at a small power cost. It will only function if active daemons are outnumbered by servants at a rate to 5:1."
icon_state = "tinkerers_daemon"
active_icon = "tinkerers_daemon"
inactive_icon = "tinkerers_daemon"
unanchored_icon = "tinkerers_daemon_unwrenched"
max_integrity = 100
construction_value = 20
break_message = "<span class='warning'>The daemon shatters into millions of pieces, leaving only a disc of metal!</span>"
debris = list(/obj/item/clockwork/alloy_shards/medium = 1, \
/obj/item/clockwork/alloy_shards/small = 6, \
/obj/item/clockwork/component/replicant_alloy/replication_plate = 1)
var/static/mutable_appearance/daemon_glow = mutable_appearance('icons/obj/clockwork_objects.dmi', "tinkerglow")
var/static/mutable_appearance/component_glow = mutable_appearance('icons/obj/clockwork_objects.dmi', "t_random_component")
var/component_id_to_produce
var/production_time = 0 //last time we produced a component
var/production_cooldown = 70
/obj/structure/destructible/clockwork/powered/tinkerers_daemon/Destroy()
GLOB.active_daemons -= src
return ..()
/obj/structure/destructible/clockwork/powered/tinkerers_daemon/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
if(active)
if(component_id_to_produce)
to_chat(user, "<span class='[get_component_span(component_id_to_produce)]_small'>It is currently producing [get_component_name(component_id_to_produce)][component_id_to_produce != REPLICANT_ALLOY ? "s":""].</span>")
else
to_chat(user, "<span class='brass'>It is currently producing random components.</span>")
to_chat(user, "<span class='nezbere_small'>It will produce a component every <b>[round((production_cooldown*0.1) * get_efficiency_mod(TRUE), 0.1)]</b> seconds and requires at least the following power for each component type:</span>")
for(var/i in GLOB.clockwork_component_cache)
to_chat(user, "[get_component_icon(i)] <span class='[get_component_span(i)]_small'><i>[get_component_name(i)]:</i> <b>[DisplayPower(get_component_cost(i))]</b> <i>([GLOB.clockwork_component_cache[i]] exist[GLOB.clockwork_component_cache[i] == 1 ? "s" : ""])</i></span>")
/obj/structure/destructible/clockwork/powered/tinkerers_daemon/forced_disable(bad_effects)
if(active)
if(bad_effects)
try_use_power(MIN_CLOCKCULT_POWER*4)
visible_message("<span class='warning'>[src] shuts down with a horrible grinding noise!</span>")
playsound(src, 'sound/magic/clockwork/anima_fragment_attack.ogg', 50, 1)
else
visible_message("<span class='warning'>[src] shuts down!</span>")
toggle()
return TRUE
/obj/structure/destructible/clockwork/powered/tinkerers_daemon/attack_hand(mob/living/user)
if(!is_servant_of_ratvar(user))
to_chat(user, "<span class='warning'>You place your hand on the daemon, but nothing happens.</span>")
return
if(active)
toggle(0, user)
else
if(!anchored)
to_chat(user, "<span class='warning'>[src] needs to be secured to the floor before it can be activated!</span>")
return FALSE
if(!GLOB.clockwork_caches)
to_chat(user, "<span class='nezbere'>\"You require a cache for this daemon to operate. Get to it.\"</span>")
return
var/min_power_usable = 0
for(var/i in GLOB.clockwork_component_cache)
if(!min_power_usable)
min_power_usable = get_component_cost(i)
else
min_power_usable = min(min_power_usable, get_component_cost(i))
if(total_accessable_power() < min_power_usable)
to_chat(user, "<span class='nezbere'>\"You need more power to activate this daemon, friend.\"</span>")
return
var/servants = 0
for(var/mob/living/L in GLOB.living_mob_list)
if(is_servant_of_ratvar(L))
servants++
if(servants * 0.2 < 1)
to_chat(user, "<span class='nezbere'>\"There are too few servants for daemons to work.\"</span>")
return
var/choice = alert(user,"Activate Daemon...",,"Specific Component","Random Component","Cancel")
switch(choice)
if("Specific Component")
var/list/components = list()
for(var/i in GLOB.clockwork_component_cache)
components["[get_component_name(i)] ([DisplayPower(get_component_cost(i))])"] = i
var/input_component = input(user, "Choose a component type.", name) as null|anything in components
component_id_to_produce = components[input_component]
servants = 0
for(var/mob/living/L in GLOB.living_mob_list)
if(is_servant_of_ratvar(L))
servants++
if(!is_servant_of_ratvar(user) || !user.canUseTopic(src, !issilicon(user), NO_DEXTERY) || active || !GLOB.clockwork_caches || servants * 0.2 < 1)
return
if(!component_id_to_produce)
to_chat(user, "<span class='warning'>You decide not to select a component and activate the daemon.</span>")
return
if(total_accessable_power() < get_component_cost(component_id_to_produce))
to_chat(user, "<span class='warning'>There is too little power to produce this type of component!</span>")
return
toggle(0, user)
if("Random Component")
component_id_to_produce = null
servants = 0
for(var/mob/living/L in GLOB.living_mob_list)
if(is_servant_of_ratvar(L))
servants++
if(!is_servant_of_ratvar(user) || !user.canUseTopic(src, !issilicon(user), NO_DEXTERY) || active || !GLOB.clockwork_caches || servants * 0.2 < 1)
return
toggle(0, user)
/obj/structure/destructible/clockwork/powered/tinkerers_daemon/toggle(fast_process, mob/living/user)
. = ..()
if(active)
GLOB.active_daemons += src
var/component_color = get_component_color(component_id_to_produce)
daemon_glow.color = component_color
add_overlay(daemon_glow)
component_glow.icon_state = "t_[component_id_to_produce ? component_id_to_produce :"random_component"]"
component_glow.color = component_color
add_overlay(component_glow)
production_time = world.time + production_cooldown //don't immediately produce when turned on after being off
set_light(2, 0.9, get_component_color_bright(component_id_to_produce))
else
GLOB.active_daemons -= src
cut_overlays()
set_light(0)
/obj/structure/destructible/clockwork/powered/tinkerers_daemon/proc/get_component_cost(id)
return max(MIN_CLOCKCULT_POWER*2, (MIN_CLOCKCULT_POWER*2) * (1 + round(GLOB.clockwork_component_cache[id] * 0.2)))
/obj/structure/destructible/clockwork/powered/tinkerers_daemon/process()
var/servants = 0
for(var/mob/living/L in GLOB.living_mob_list)
if(is_servant_of_ratvar(L))
servants++
while(servants * 0.2 < LAZYLEN(GLOB.active_daemons))
var/obj/structure/destructible/clockwork/powered/tinkerers_daemon/D = GLOB.active_daemons[1]
if(!istype(D))
break
if(D.active)
D.forced_disable(FALSE)
if(D == src)
return
. = ..()
var/min_power_usable = 0
if(!component_id_to_produce)
for(var/i in GLOB.clockwork_component_cache)
if(!min_power_usable)
min_power_usable = get_component_cost(i)
else
min_power_usable = min(min_power_usable, get_component_cost(i))
else
min_power_usable = get_component_cost(component_id_to_produce)
if(!GLOB.clockwork_caches || . < min_power_usable) //if we don't have enough to produce the lowest or what we chose to produce, cancel out
forced_disable(FALSE)
return
if(production_time <= world.time)
var/component_to_generate = component_id_to_produce
if(!component_to_generate)
component_to_generate = get_weighted_component_id() //more likely to generate components that we have less of
if(!try_use_power(get_component_cost(component_to_generate)))
component_to_generate = null
if(!component_id_to_produce)
for(var/i in GLOB.clockwork_component_cache)
if(try_use_power(get_component_cost(i))) //if we fail but are producing random, try and get a different component to produce
component_to_generate = i
break
if(component_to_generate)
generate_cache_component(component_to_generate, src)
production_time = world.time + (production_cooldown * get_efficiency_mod(TRUE)) //go on cooldown
visible_message("<span class='warning'>[src] hums as it produces a </span><span class='[get_component_span(component_to_generate)]'>component</span><span class='warning'>.</span>")
else
forced_disable(FALSE) //we shouldn't actually ever get here, as we should cancel out way before this

View File

@@ -96,7 +96,7 @@
/proc/pollCultists(var/mob/living/Nominee) //Cult Master Poll
if(world.time < CULT_POLL_WAIT)
to_chat(Nominee, "It would be premature to select a leader while everyone is still settling in, try again in [round((CULT_POLL_WAIT-world.time)/10)] seconds.")
to_chat(Nominee, "It would be premature to select a leader while everyone is still settling in, try again in [DisplayTimeText(CULT_POLL_WAIT-world.time)].")
return
GLOB.cult_vote_called = TRUE //somebody's trying to be a master, make sure we don't let anyone else try
for(var/datum/mind/B in SSticker.mode.cult)
@@ -232,7 +232,7 @@
return FALSE
if(cooldown > world.time)
if(!CM.active)
to_chat(owner, "<span class='cultlarge'><b>You need to wait [round((cooldown - world.time) * 0.1)] seconds before you can mark another target!</b></span>")
to_chat(owner, "<span class='cultlarge'><b>You need to wait [DisplayTimeText(cooldown - world.time)] before you can mark another target!</b></span>")
return FALSE
return ..()
@@ -324,7 +324,7 @@
return FALSE
if(cooldown > world.time)
if(!PM.active)
to_chat(owner, "<span class='cultlarge'><b>You need to wait [round((cooldown - world.time) * 0.1)] seconds before you can pulse again!</b></span>")
to_chat(owner, "<span class='cultlarge'><b>You need to wait [DisplayTimeText(cooldown - world.time)] before you can pulse again!</b></span>")
return FALSE
return ..()

View File

@@ -10,7 +10,7 @@
..()
to_chat(user, "<span class='notice'>\The [src] is [anchored ? "":"not "]secured to the floor.</span>")
if((iscultist(user) || isobserver(user)) && cooldowntime > world.time)
to_chat(user, "<span class='cultitalic'>The magic in [src] is too weak, [p_they()] will be ready to use again in [getETA()].</span>")
to_chat(user, "<span class='cultitalic'>The magic in [src] is too weak, [p_they()] will be ready to use again in [DisplayTimeText(cooldowntime - world.time)].</span>")
/obj/structure/destructible/cult/examine_status(mob/user)
if(iscultist(user) || isobserver(user))
@@ -50,13 +50,6 @@
animate(src, color = previouscolor, time = 8)
addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 8)
/obj/structure/destructible/cult/proc/getETA()
var/time = (cooldowntime - world.time)/600
var/eta = "[round(time, 1)] minutes"
if(time <= 1)
time = (cooldowntime - world.time)*0.1
eta = "[round(time, 1)] seconds"
return eta
/obj/structure/destructible/cult/talisman
name = "altar"
@@ -72,7 +65,7 @@
to_chat(user, "<span class='cultitalic'>You need to anchor [src] to the floor with a tome first.</span>")
return
if(cooldowntime > world.time)
to_chat(user, "<span class='cultitalic'>The magic in [src] is weak, it will be ready to use again in [getETA()].</span>")
to_chat(user, "<span class='cultitalic'>The magic in [src] is weak, it will be ready to use again in [DisplayTimeText(cooldowntime - world.time)].</span>")
return
var/choice = alert(user,"You study the schematics etched into the forge...",,"Eldritch Whetstone","Zealot's Blindfold","Flask of Unholy Water")
var/pickedtype
@@ -105,7 +98,7 @@
to_chat(user, "<span class='cultitalic'>You need to anchor [src] to the floor with a tome first.</span>")
return
if(cooldowntime > world.time)
to_chat(user, "<span class='cultitalic'>The magic in [src] is weak, it will be ready to use again in [getETA()].</span>")
to_chat(user, "<span class='cultitalic'>The magic in [src] is weak, it will be ready to use again in [DisplayTimeText(cooldowntime - world.time)].</span>")
return
var/choice = alert(user,"You study the schematics etched into the forge...",,"Shielded Robe","Flagellant's Robe","Nar-Sien Hardsuit")
var/pickedtype
@@ -212,7 +205,7 @@
to_chat(user, "<span class='cultitalic'>You need to anchor [src] to the floor with a tome first.</span>")
return
if(cooldowntime > world.time)
to_chat(user, "<span class='cultitalic'>The magic in [src] is weak, it will be ready to use again in [getETA()].</span>")
to_chat(user, "<span class='cultitalic'>The magic in [src] is weak, it will be ready to use again in [DisplayTimeText(cooldowntime - world.time)].</span>")
return
var/choice = alert(user,"You flip through the black pages of the archives...",,"Supply Talisman","Shuttle Curse","Veil Walker Set")
var/list/pickedtype = list()

View File

@@ -19,7 +19,6 @@
var/probability = 0
var/false_report_weight = 0 //How often will this show up incorrectly in a centcom report?
var/station_was_nuked = 0 //see nuclearbomb.dm and malfunction.dm
var/explosion_in_progress = 0 //sit back and relax
var/round_ends_with_antag_death = 0 //flags the "one verse the station" antags as such
var/list/datum/mind/modePlayer = new
var/list/datum/mind/antag_candidates = list() // List of possible starting antags goes here
@@ -562,3 +561,8 @@
/datum/game_mode/proc/generate_report() //Generates a small text blurb for the gamemode in centcom report
return "Gamemode report for [name] not set. Contact a coder."
//By default nuke just ends the round
/datum/game_mode/proc/OnNukeExplosion(off_station)
if(off_station < 2)
station_was_nuked = TRUE //Will end the round on next check.

View File

@@ -1,5 +1,17 @@
/datum/objective_team/abductor_team
member_name = "abductor"
var/list/objectives = list()
var/team_number
/datum/objective_team/abductor_team/is_solo()
return FALSE
/datum/objective_team/abductor_team/proc/add_objective(datum/objective/O)
O.team = src
O.update_explanation_text()
objectives += O
/datum/game_mode
var/abductor_teams = 0
var/list/datum/mind/abductors = list()
var/list/datum/mind/abductees = list()
@@ -12,12 +24,8 @@
required_players = 15
maximum_players = 50
var/max_teams = 4
abductor_teams = 1
var/list/datum/mind/scientists = list()
var/list/datum/mind/agents = list()
var/list/datum/objective/team_objectives = list()
var/list/team_names = list()
var/finished = 0
var/list/datum/objective_team/abductor_team/abductor_teams = list()
var/finished = FALSE
/datum/game_mode/abduction/announce()
to_chat(world, "<B>The current game mode is - Abduction!</B>")
@@ -26,163 +34,78 @@
to_chat(world, "<b>Crew</b> - don't get abducted and stop the abductors.")
/datum/game_mode/abduction/pre_setup()
abductor_teams = max(1, min(max_teams,round(num_players()/config.abductor_scaling_coeff)))
var/possible_teams = max(1,round(antag_candidates.len / 2))
abductor_teams = min(abductor_teams,possible_teams)
var/num_teams = max(1, min(max_teams, round(num_players() / config.abductor_scaling_coeff)))
var/possible_teams = max(1, round(antag_candidates.len / 2))
num_teams = min(num_teams, possible_teams)
abductors.len = 2*abductor_teams
scientists.len = abductor_teams
agents.len = abductor_teams
team_objectives.len = abductor_teams
team_names.len = abductor_teams
for(var/i = 1 to num_teams)
if(!make_abductor_team())
return FALSE
return TRUE
for(var/i=1,i<=abductor_teams,i++)
if(!make_abductor_team(i))
return 0
/datum/game_mode/abduction/proc/make_abductor_team(datum/mind/agent, datum/mind/scientist)
var/team_number = abductor_teams.len+1
return 1
var/datum/objective_team/abductor_team/team = new
team.team_number = team_number
team.name = "Mothership [pick(GLOB.possible_changeling_IDs)]" //TODO Ensure unique and actual alieny names
team.add_objective(new/datum/objective/experiment)
/datum/game_mode/abduction/proc/make_abductor_team(team_number,preset_agent=null,preset_scientist=null)
//Team Name
team_names[team_number] = "Mothership [pick(GLOB.possible_changeling_IDs)]" //TODO Ensure unique and actual alieny names
//Team Objective
var/datum/objective/experiment/team_objective = new
team_objective.team_number = team_number
team_objectives[team_number] = team_objective
//Team Members
if(antag_candidates.len < (!agent + !scientist))
return
if(!preset_agent || !preset_scientist)
if(antag_candidates.len <=2)
return 0
var/datum/mind/scientist
var/datum/mind/agent
if(!preset_scientist)
if(!scientist)
scientist = pick(antag_candidates)
antag_candidates -= scientist
else
scientist = preset_scientist
antag_candidates -= scientist
team.members |= scientist
scientist.assigned_role = "Abductor Scientist"
log_game("[scientist.key] (ckey) has been selected as [team.name] abductor scientist.")
if(!preset_agent)
if(!agent)
agent = pick(antag_candidates)
antag_candidates -= agent
else
agent = preset_agent
antag_candidates -= agent
team.members |= agent
agent.assigned_role = "Abductor Agent"
log_game("[agent.key] (ckey) has been selected as [team.name] abductor agent.")
scientist.assigned_role = "abductor scientist"
scientist.special_role = "abductor scientist"
log_game("[scientist.key] (ckey) has been selected as an abductor team [team_number] scientist.")
agent.assigned_role = "abductor agent"
agent.special_role = "abductor agent"
log_game("[agent.key] (ckey) has been selected as an abductor team [team_number] agent.")
abductors |= agent
abductors |= scientist
scientists[team_number] = scientist
agents[team_number] = agent
return 1
abductor_teams += team
return team
/datum/game_mode/abduction/post_setup()
for(var/team_number=1,team_number<=abductor_teams,team_number++)
post_setup_team(team_number)
for(var/datum/objective_team/abductor_team/team in abductor_teams)
post_setup_team(team)
return ..()
//Used for create antag buttons
/datum/game_mode/abduction/proc/post_setup_team(team_number)
var/list/obj/effect/landmark/abductor/agent_landmarks = list()
var/list/obj/effect/landmark/abductor/scientist_landmarks = list()
agent_landmarks.len = max_teams
scientist_landmarks.len = max_teams
for(var/obj/effect/landmark/abductor/A in GLOB.landmarks_list)
if(istype(A, /obj/effect/landmark/abductor/agent))
agent_landmarks[text2num(A.team)] = A
else if(istype(A, /obj/effect/landmark/abductor/scientist))
scientist_landmarks[text2num(A.team)] = A
var/team_name = team_names[team_number]
var/datum/mind/agent
var/obj/effect/landmark/L
var/datum/mind/scientist
var/mob/living/carbon/human/H
var/datum/species/abductor/S
agent = agents[team_number]
H = agent.current
L = agent_landmarks[team_number]
H.forceMove(L.loc)
H.set_species(/datum/species/abductor)
S = H.dna.species
S.team = team_number
H.real_name = team_name + " Agent"
H.equipOutfit(/datum/outfit/abductor/agent)
greet_agent(agent,team_number)
scientist = scientists[team_number]
H = scientist.current
L = scientist_landmarks[team_number]
H.forceMove(L.loc)
H.set_species(/datum/species/abductor)
S = H.dna.species
S.scientist = TRUE
S.team = team_number
H.real_name = team_name + " Scientist"
H.equipOutfit(/datum/outfit/abductor/scientist)
greet_scientist(scientist,team_number)
/datum/game_mode/abduction/proc/greet_agent(datum/mind/abductor,team_number)
abductor.objectives += team_objectives[team_number]
var/team_name = team_names[team_number]
to_chat(abductor.current, "<span class='notice'>You are an agent of [team_name]!</span>")
to_chat(abductor.current, "<span class='notice'>With the help of your teammate, kidnap and experiment on station crew members!</span>")
to_chat(abductor.current, "<span class='notice'>Use your stealth technology and equipment to incapacitate humans for your scientist to retrieve.</span>")
abductor.announce_objectives()
/datum/game_mode/abduction/proc/greet_scientist(datum/mind/abductor,team_number)
abductor.objectives += team_objectives[team_number]
var/team_name = team_names[team_number]
to_chat(abductor.current, "<span class='notice'>You are a scientist of [team_name]!</span>")
to_chat(abductor.current, "<span class='notice'>With the help of your teammate, kidnap and experiment on station crew members!</span>")
to_chat(abductor.current, "<span class='notice'>Use your tool and ship consoles to support the agent and retrieve human specimens.</span>")
abductor.announce_objectives()
/datum/game_mode/abduction/proc/get_team_console(team_number)
for(var/obj/machinery/abductor/console/C in GLOB.machines)
if(C.team == team_number)
return C
/datum/game_mode/abduction/proc/post_setup_team(datum/objective_team/abductor_team/team)
for(var/datum/mind/M in team.members)
if(M.assigned_role == "Abductor Scientist")
M.add_antag_datum(ANTAG_DATUM_ABDUCTOR_SCIENTIST, team)
else
M.add_antag_datum(ANTAG_DATUM_ABDUCTOR_AGENT, team)
/datum/game_mode/abduction/check_finished()
if(!finished)
for(var/team_number=1,team_number<=abductor_teams,team_number++)
var/obj/machinery/abductor/console/con = get_team_console(team_number)
var/datum/objective/objective = team_objectives[team_number]
if (con.experiment.points >= objective.target_amount)
SSshuttle.emergency.request(null, set_coefficient = 0.5)
finished = 1
return ..()
for(var/datum/objective_team/abductor_team/team in abductor_teams)
for(var/datum/objective/O in team.objectives)
if(O.check_completion())
SSshuttle.emergency.request(null, set_coefficient = 0.5)
finished = TRUE
return ..()
return ..()
/datum/game_mode/abduction/declare_completion()
for(var/team_number=1,team_number<=abductor_teams,team_number++)
var/obj/machinery/abductor/console/console = get_team_console(team_number)
var/datum/objective/objective = team_objectives[team_number]
var/team_name = team_names[team_number]
if(console.experiment.points >= objective.target_amount)
to_chat(world, "<span class='greenannounce'>[team_name] team fulfilled its mission!</span>")
for(var/datum/objective_team/abductor_team/team in abductor_teams)
var/won = TRUE
for(var/datum/objective/O in team.objectives)
if(!O.check_completion())
won = FALSE
if(won)
to_chat(world, "<span class='greenannounce'>[team.name] team fulfilled its mission!</span>")
else
to_chat(world, "<span class='boldannounce'>[team_name] team failed its mission.</span>")
to_chat(world, "<span class='boldannounce'>[team.name] team failed its mission.</span>")
..()
return 1
return TRUE
/datum/game_mode/proc/auto_declare_completion_abduction()
var/text = ""
@@ -200,40 +123,28 @@
text += "<br>"
to_chat(world, text)
//Landmarks
// TODO: Split into separate landmarks for prettier ships
// LANDMARKS
/obj/effect/landmark/abductor
var/team = 1
var/team_number = 1
/obj/effect/landmark/abductor/agent
/obj/effect/landmark/abductor/scientist
// OBJECTIVES
/datum/objective/experiment
target_amount = 6
var/team_number
/datum/objective/experiment/New()
explanation_text = "Experiment on [target_amount] humans."
/datum/objective/experiment/check_completion()
var/ab_team = team_number
if(owner)
if(!owner.current || !ishuman(owner.current))
return 0
var/mob/living/carbon/human/H = owner.current
if(H.dna.species.id != "abductor")
return 0
var/datum/species/abductor/S = H.dna.species
ab_team = S.team
for(var/obj/machinery/abductor/experiment/E in GLOB.machines)
if(E.team == ab_team)
if(E.points >= target_amount)
return 1
else
return 0
return 0
if(!istype(team, /datum/objective_team/abductor_team))
return FALSE
var/datum/objective_team/abductor_team/T = team
if(E.team_number == T.team_number)
return E.points >= target_amount
return FALSE
/datum/game_mode/proc/update_abductor_icons_added(datum/mind/alien_mind)
var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_ABDUCTOR]

View File

@@ -328,7 +328,7 @@ Congratulations! You are now trained for invasive xenobiology research!"}
return
/obj/item/paper/guides/antag/abductor/AltClick()
return
return //otherwise it would fold into a paperplane.
#define BATON_STUN 0
#define BATON_SLEEP 1

View File

@@ -5,20 +5,14 @@
back = /obj/item/storage/backpack
ears = /obj/item/device/radio/headset/abductor
/datum/outfit/abductor/proc/get_team_console(team_number)
for(var/obj/machinery/abductor/console/C in GLOB.machines)
if(C.team == team_number)
return C
/datum/outfit/abductor/proc/link_to_console(mob/living/carbon/human/H, team_number)
if(!team_number && isabductor(H))
var/datum/species/abductor/S = H.dna.species
team_number = S.team
var/datum/antagonist/abductor/A = H.mind.has_antag_datum(ANTAG_DATUM_ABDUCTOR)
if(!team_number && A)
team_number = A.team.team_number
if(!team_number)
team_number = 1
var/obj/machinery/abductor/console/console = get_team_console(team_number)
var/obj/machinery/abductor/console/console = get_abductor_console(team_number)
if(console)
var/obj/item/clothing/suit/armor/abductor/vest/V = locate() in H
if(V)

View File

@@ -1,6 +1,6 @@
/obj/machinery/computer/camera_advanced/abductor
name = "Human Observation Console"
var/team = 0
var/team_number = 0
networks = list("SS13","Abductor")
var/datum/action/innate/teleport_in/tele_in_action = new
var/datum/action/innate/teleport_out/tele_out_action = new
@@ -9,7 +9,7 @@
var/datum/action/innate/vest_disguise_swap/vest_disguise_action = new
var/datum/action/innate/set_droppoint/set_droppoint_action = new
var/obj/machinery/abductor/console/console
z_lock = ZLEVEL_STATION_PRIMARY
station_lock_override = TRUE
icon = 'icons/obj/abductor.dmi'
icon_state = "camera"

View File

@@ -1,8 +1,13 @@
/proc/get_abductor_console(team_number)
for(var/obj/machinery/abductor/console/C in GLOB.machines)
if(C.team_number == team_number)
return C
//Common
/obj/machinery/abductor
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
var/team = 0
var/team_number = 0
//Console
@@ -139,21 +144,21 @@
return INITIALIZE_HINT_LATELOAD
/obj/machinery/abductor/console/LateInitialize()
if(!team)
if(!team_number)
return
for(var/obj/machinery/abductor/pad/p in GLOB.machines)
if(p.team == team)
if(p.team_number == team_number)
pad = p
break
for(var/obj/machinery/abductor/experiment/e in GLOB.machines)
if(e.team == team)
if(e.team_number == team_number)
experiment = e
e.console = src
for(var/obj/machinery/computer/camera_advanced/abductor/c in GLOB.machines)
if(c.team == team)
if(c.team_number == team_number)
camera = c
c.console = src

View File

@@ -13,7 +13,7 @@
var/flash = " - || - "
var/obj/machinery/abductor/console/console
var/message_cooldown = 0
var/breakout_time = 0.75
var/breakout_time = 450
/obj/machinery/abductor/experiment/MouseDrop_T(mob/target, mob/user)
if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user) || !ishuman(target))
@@ -50,9 +50,9 @@
user.changeNext_move(CLICK_CD_BREAKOUT)
user.last_special = world.time + CLICK_CD_BREAKOUT
user.visible_message("<span class='notice'>You see [user] kicking against the door of [src]!</span>", \
"<span class='notice'>You lean on the back of [src] and start pushing the door open... (this will take about [(breakout_time<1) ? "[breakout_time*60] seconds" : "[breakout_time] minute\s"].)</span>", \
"<span class='notice'>You lean on the back of [src] and start pushing the door open... (this will take about [DisplayTimeText(breakout_time)].)</span>", \
"<span class='italics'>You hear a metallic creaking from [src].</span>")
if(do_after(user,(breakout_time*60*10), target = src)) //minutes * 60seconds * 10deciseconds
if(do_after(user,(breakout_time), target = src))
if(!user || user.stat != CONSCIOUS || user.loc != src || state_open)
return
user.visible_message("<span class='warning'>[user] successfully broke out of [src]!</span>", \

Some files were not shown because too many files have changed in this diff Show More