This commit is contained in:
Navier
2020-08-15 23:13:41 -04:00
1991 changed files with 81350 additions and 60294 deletions
+4 -10
View File
@@ -9,8 +9,6 @@
icon_state = "bus"
density = TRUE
circuit = /obj/item/circuitboard/machine/ntnet_relay
ui_x = 400
ui_y = 300
var/datum/ntnet/NTNet = null // This is mostly for backwards reference and to allow varedit modifications from ingame.
var/enabled = 1 // Set to 0 if the relay was turned off
@@ -64,15 +62,12 @@
SSnetworks.station_network.add_log("Quantum relay switched from overload recovery mode to normal operation mode.")
..()
/obj/machinery/ntnet_relay/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/ntnet_relay/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "ntnet_relay", "NTNet Quantum Relay", ui_x, ui_y, master_ui, state)
ui = new(user, src, "NtnetRelay")
ui.open()
/obj/machinery/ntnet_relay/ui_data(mob/user)
var/list/data = list()
data["enabled"] = enabled
@@ -81,7 +76,6 @@
data["dos_crashed"] = dos_failure
return data
/obj/machinery/ntnet_relay/ui_act(action, params)
if(..())
return
@@ -118,4 +112,4 @@
D.target = null
D.error = "Connection to quantum relay severed"
return ..()
return ..()
+18 -11
View File
@@ -71,22 +71,22 @@
/obj/machinery/vr_sleeper/update_icon_state()
icon_state = "[initial(icon_state)][state_open ? "-open" : ""]"
/obj/machinery/vr_sleeper/open_machine()
if(state_open)
return
if(occupant)
SStgui.close_user_uis(occupant, src)
return ..()
/obj/machinery/vr_sleeper/MouseDrop_T(mob/target, mob/user)
if(user.lying || !iscarbon(target) || !Adjacent(target) || !user.canUseTopic(src, BE_CLOSE, TRUE, NO_TK))
return
close_machine(target)
ui_interact(user)
/obj/machinery/vr_sleeper/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/vr_sleeper/ui_state(mob/user)
if(user == occupant)
return GLOB.contained_state
return GLOB.default_state
/obj/machinery/vr_sleeper/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "vr_sleeper", "VR Sleeper", 475, 340, master_ui, state)
ui = new(user, src, "VrSleeper", "VR Sleeper")
ui.open()
/obj/machinery/vr_sleeper/ui_act(action, params)
@@ -130,11 +130,13 @@
/obj/machinery/vr_sleeper/ui_data(mob/user)
var/list/data = list()
var/is_living
if(vr_mob && !QDELETED(vr_mob))
is_living = isliving(vr_mob)
data["can_delete_avatar"] = TRUE
data["vr_avatar"] = list("name" = vr_mob.name)
data["isliving"] = istype(vr_mob)
if(data["isliving"])
data["isliving"] = is_living
if(is_living)
var/status
switch(vr_mob.stat)
if(CONSCIOUS)
@@ -146,6 +148,11 @@
if(SOFT_CRIT)
status = "Barely Conscious"
data["vr_avatar"] += list("status" = status, "health" = vr_mob.health, "maxhealth" = vr_mob.maxHealth)
else
data["can_delete_avatar"] = FALSE
data["vr_avatar"] = FALSE
data["isliving"] = FALSE
data["toggle_open"] = state_open
data["emagged"] = you_die_in_the_game_you_die_for_real
data["isoccupant"] = (user == occupant)
+5
View File
@@ -40,6 +40,11 @@
if(M.client)
body += "<br>\[<b>First Seen:</b> [M.client.player_join_date]\]\[<b>Byond account registered on:</b> [M.client.account_join_date]\]"
body += "<br><br><b>CentCom Galactic Ban DB: </b> "
if(CONFIG_GET(string/centcom_ban_db))
body += "<a href='?_src_=holder;[HrefToken()];centcomlookup=[M.client.ckey]'>Search</a>"
else
body += "<i>Disabled</i>"
body += "<br><br><b>Show related accounts by:</b> "
body += "\[ <a href='?_src_=holder;[HrefToken()];showrelatedacc=cid;client=[REF(M.client)]'>CID</a> | "
body += "<a href='?_src_=holder;[HrefToken()];showrelatedacc=ip;client=[REF(M.client)]'>IP</a> \]"
+1 -1
View File
@@ -62,7 +62,7 @@ GLOBAL_PROTECT(admin_verbs_admin)
/client/proc/cmd_admin_check_player_exp, /* shows players by playtime */
/client/proc/toggle_combo_hud, // toggle display of the combination pizza antag and taco sci/med/eng hud
/client/proc/toggle_AI_interact, /*toggle admin ability to interact with machines as an AI*/
/client/proc/open_shuttle_manipulator, /* Opens shuttle manipulator UI */
/datum/admins/proc/open_shuttlepanel, /* Opens shuttle manipulator UI */
/client/proc/respawn_character,
/client/proc/secrets,
/client/proc/toggle_hear_radio, /*allows admins to hide all radio output*/
+1 -1
View File
@@ -150,7 +150,7 @@ GLOBAL_LIST(round_end_notifiees)
/datum/tgs_chat_command/wheelofsalt/Run(datum/tgs_chat_user/sender, params)
var/saltresult = "The wheel of salt [pick("clatters","screams","vibrates","clanks","resonates","groans","moans","squeaks","emits a[pick(" god-forsaken"," lewd"," creepy"," generic","n orgasmic"," demonic")] [pick("airhorn","bike horn","trumpet","clown","latex","vore","dog","laughing")] noise")] as it spins violently... And it seems the salt of the day is the "
var/saltprimarysubject = "[pick("combat","medical","grab","furry","wall","orgasm","cat","ERP","lizard","dog","latex","vision cone","atmospherics","table","chem","vore","dogborg","Skylar Lineman","Mekhi Anderson","Peppermint","rework","cum","dick","cockvore","Medihound","sleeper","belly sleeper","door wires","flightsuit","coder privilege","Developer abuse","ban reason","github self merge","red panda","beret","male catgirl","powergame","hexacrocin","Discord server","Clitadel","Cargonia","Solarian Republic","Main and RP merger","bluespace","salt","chem dispenser theft","Botany","moth","BWOINK","anal vore","stamina","Mason Jakops","mining","noodle","milf","Lavaland","Necropolis","Ashwalker","Chase Redtail","Drew Mint","Pavel Marsk","Joker Amari","Durgit","chaplain","Antag","nanite","Syndicate","Nar-Sie","Ratvar","Cult","maint","Foam-Force","AI","cyborg","ghost","clockwork","cyberpunk","vaporwave","Clown","Leon Beech","Mime","security","research","Megafauna","Bubblegum","Ash Drake","Legion","Colossus","White Shuttle","Changeling","Cowboy","Space Ninja","Poly","Revolutionary","Skyrim","forbidden fruits","xenomorph","blob","Nuclear Operative","crossdressing")]"
var/saltprimarysubject = "[pick("combat","medical","grab","furry","wall","orgasm","cat","ERP","lizard","dog","latex","vision cone","atmospherics","table","chem","vore","dogborg","Skylar Lineman","Mekhi Anderson","Peppermint","rework","cum","dick","cockvore","Medihound","sleeper","belly sleeper","door wires","flightsuit","coder privilege","Developer abuse","ban reason","github self merge","red panda","beret","male catgirl","powergame","hexacrocin removal","Discord server","Clitadel","Cargonia","Solarian Republic","Main and RP merger","bluespace","salt","chem dispenser theft","Botany","moth","BWOINK","anal vore","stamina","Mason Jakops","mining","noodle","milf","Lavaland","Necropolis","Ashwalker","Chase Redtail","Drew Mint","Pavel Marsk","Joker Amari","Durgit","chaplain","Antag","nanite","Syndicate","Nar-Sie","Ratvar","Cult","maint","Foam-Force","AI","cyborg","ghost","clockwork","cyberpunk","vaporwave","Clown","Leon Beech","Mime","security","research","Megafauna","Bubblegum","Ash Drake","Legion","Colossus","White Shuttle","Changeling","Cowboy","Space Ninja","Poly","Revolutionary","Skyrim","forbidden fruits","xenomorph","blob","Nuclear Operative","crossdressing")]"
var/saltsecondarysubject = "[pick("rework","changes","r34","ban","removal","addition","leak","proposal","fanart","introduction","tabling","ERP","bikeshedding","crossdressing","sprites","semen keg","argument","theft","nerf","screeching","salt","creampie","lewding","murder","kissing","marriage","replacement","fucking","ship","netflix adaptation","dance","remaster","system","voyeur","decoration","pre-order","bukkake","seduction","worship","gangbang","handholding")]"
if(prob(10))
saltresult += "@here for your salt, all day every day"
+67 -3
View File
@@ -863,7 +863,7 @@
if(jobban_isbanned(M, ROLE_TRAITOR) || isbanned_dept)
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=traitor;jobban4=[REF(M)]'><font color=red>Traitor</font></a></td>"
else
dat += "<td width='20%'><a href='?src=[REF(src)];jobban3=traitor;jobban4=[REF(M)]'>Traitor</a></td>"
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=traitor;jobban4=[REF(M)]'>Traitor</a></td>"
//Changeling
if(jobban_isbanned(M, ROLE_CHANGELING) || isbanned_dept)
@@ -2184,8 +2184,18 @@
if(!ishuman(H))
to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.")
return
var/obj/item/reagent_containers/food/snacks/cookie/cookie = new(H)
//let's keep it simple
//milk to plasmemes and skeletons, meat to lizards, electricity bars to ethereals, cookies to everyone else
var/cookiealt = /obj/item/reagent_containers/food/snacks/cookie
if(isskeleton(H))
cookiealt = /obj/item/reagent_containers/food/condiment/milk
else if(isplasmaman(H))
cookiealt = /obj/item/reagent_containers/food/condiment/milk
else if(isethereal(H))
cookiealt = /obj/item/reagent_containers/food/snacks/energybar
else if(islizard(H))
cookiealt = /obj/item/reagent_containers/food/snacks/meat/slab
var/obj/item/cookie = new cookiealt(H)
if(H.put_in_hands(cookie))
H.update_inv_hands()
else
@@ -2834,6 +2844,60 @@
usr << browse(dat.Join("<br>"), "window=related_[C];size=420x300")
else if(href_list["centcomlookup"])
if(!check_rights(R_ADMIN))
return
if(!CONFIG_GET(string/centcom_ban_db))
to_chat(usr, "<span class='warning'>Centcom Galactic Ban DB is disabled!</span>")
return
var/ckey = href_list["centcomlookup"]
// Make the request
var/datum/http_request/request = new()
request.prepare(RUSTG_HTTP_METHOD_GET, "[CONFIG_GET(string/centcom_ban_db)]/[ckey]", "", "")
request.begin_async()
UNTIL(request.is_complete() || !usr)
if (!usr)
return
var/datum/http_response/response = request.into_response()
var/list/bans
var/list/dat = list("<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><body>")
if(response.errored)
dat += "<br>Failed to connect to CentCom."
else if(response.status_code != 200)
dat += "<br>Failed to connect to CentCom. Status code: [response.status_code]"
else
if(response.body == "[]")
dat += "<center><b>0 bans detected for [ckey]</b></center>"
else
bans = json_decode(response["body"])
dat += "<center><b>[bans.len] ban\s detected for [ckey]</b></center>"
for(var/list/ban in bans)
dat += "<b>Server: </b> [sanitize(ban["sourceName"])]<br>"
dat += "<b>RP Level: </b> [sanitize(ban["sourceRoleplayLevel"])]<br>"
dat += "<b>Type: </b> [sanitize(ban["type"])]<br>"
dat += "<b>Banned By: </b> [sanitize(ban["bannedBy"])]<br>"
dat += "<b>Reason: </b> [sanitize(ban["reason"])]<br>"
dat += "<b>Datetime: </b> [sanitize(ban["bannedOn"])]<br>"
var/expiration = ban["expires"]
dat += "<b>Expires: </b> [expiration ? "[sanitize(expiration)]" : "Permanent"]<br>"
if(ban["type"] == "job")
dat += "<b>Jobs: </b> "
var/list/jobs = ban["jobs"]
dat += sanitize(jobs.Join(", "))
dat += "<br>"
dat += "<hr>"
dat += "<br></body>"
var/datum/browser/popup = new(usr, "centcomlookup-[ckey]", "<div align='center'>Central Command Galactic Ban Database</div>", 700, 600)
popup.set_content(dat.Join())
popup.open(0)
else if(href_list["modantagrep"])
if(!check_rights(R_ADMIN))
return
+2 -2
View File
@@ -666,8 +666,8 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
obj_count_finished = select_refs
var/n = 0
for(var/i in found)
if(++n == 20000)
text_list += "<br><font color='red'><b>TRUNCATED - 20000 OBJECT LIMIT HIT</b></font>"
if(++n == 2500)
text_list += "<br><font color='red'><b>TRUNCATED - 2500 OBJECT LIMIT HIT</b></font>"
SDQL_print(i, text_list, print_nulls)
select_refs[REF(i)] = TRUE
SDQL2_TICK_CHECK
+1 -2
View File
@@ -371,8 +371,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
return
var/msg = "<font color='red' size='4'><b>- AdminHelp marked as IC issue by [usr?.client?.holder?.fakekey? usr.client.holder.fakekey : "an administrator"]! -</b></font><br>"
msg += "<font color='red'><b>Losing is part of the game!</b></font><br>"
msg += "<font color='red'>It is also possible that your ahelp is unable to be answered properly, due to events occurring in the round.</font>"
msg += "<font color='red'>Your ahelp is unable to be answered properly due to events occurring in the round. Your question probably has an IC answer, which means you should deal with it IC!</font>"
if(initiator)
to_chat(initiator, msg)
+5 -5
View File
@@ -55,7 +55,7 @@
if(AH)
message_admins("[key_name_admin(src)] has started replying to [key_name(C, 0, 0)]'s admin help.")
var/msg = input(src,"Message:", "Private message to [key_name(C, 0, 0)]") as message|null
var/msg = stripped_multiline_input(src,"Message:", "Private message to [key_name(C, 0, 0)]")
if (!msg)
message_admins("[key_name_admin(src)] has cancelled their reply to [key_name(C, 0, 0)]'s admin help.")
return
@@ -87,10 +87,10 @@
if(irc)
if(!ircreplyamount) //to prevent people from spamming irc
if(!ircreplyamount) //to prevent people from spamming irc/discord
return
if(!msg)
msg = input(src,"Message:", "Private message to Administrator") as text|null
msg = stripped_multiline_input(src,"Message:", "Private message to Administrator")
if(!msg)
return
@@ -112,7 +112,7 @@
//get message text, limit it's length.and clean/escape html
if(!msg)
msg = input(src,"Message:", "Private message to [key_name(recipient, 0, 0)]") as message|null
msg = stripped_multiline_input(src,"Message:", "Private message to [key_name(recipient, 0, 0)]")
msg = trim(msg)
if(!msg)
return
@@ -191,7 +191,7 @@
spawn() //so we don't hold the caller proc up
var/sender = src
var/sendername = key
var/reply = input(recipient, msg,"Admin PM from-[sendername]", "") as text|null //show message and await a reply
var/reply = stripped_multiline_input(recipient, msg,"Admin PM from-[sendername]", "") //show message and await a reply
if(recipient && reply)
if(sender)
recipient.cmd_admin_pm(sender,reply) //sender is still about, let's reply to them
+1 -1
View File
@@ -36,7 +36,7 @@
/datum/borgpanel/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "borgopanel", "Borg Panel", 700, 700, master_ui, state)
ui = new(user, src, ui_key, "BorgPanel", "Borg Panel", 700, 700, master_ui, state)
ui.open()
/datum/borgpanel/ui_data(mob/user)
-31
View File
@@ -22,37 +22,6 @@
show_air_status_to(target, usr)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Air Status") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/fix_next_move()
set category = "Debug"
set name = "Unfreeze Everyone"
var/largest_move_time = 0
var/largest_click_time = 0
var/mob/largest_move_mob = null
var/mob/largest_click_mob = null
for(var/mob/M in world)
if(!M.client)
continue
if(M.next_move >= largest_move_time)
largest_move_mob = M
if(M.next_move > world.time)
largest_move_time = M.next_move - world.time
else
largest_move_time = 1
if(M.next_click >= largest_click_time)
largest_click_mob = M
if(M.next_click > world.time)
largest_click_time = M.next_click - world.time
else
largest_click_time = 0
log_admin("DEBUG: [key_name(M)] next_move = [M.next_move] lastDblClick = [M.next_click] world.time = [world.time]")
M.next_move = 1
M.next_click = 0
message_admins("[ADMIN_LOOKUPFLW(largest_move_mob)] had the largest move delay with [largest_move_time] frames / [DisplayTimeText(largest_move_time)]!")
message_admins("[ADMIN_LOOKUPFLW(largest_click_mob)] had the largest click delay with [largest_click_time] frames / [DisplayTimeText(largest_click_time)]!")
message_admins("world.time = [world.time]")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Unfreeze Everyone") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
/client/proc/radio_report()
set category = "Debug"
set name = "Radio report"
+132 -10
View File
@@ -421,7 +421,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
new_character.real_name = record_found.fields["name"]
new_character.gender = record_found.fields["gender"]
new_character.age = record_found.fields["age"]
new_character.hardset_dna(record_found.fields["identity"], record_found.fields["enzymes"], record_found.fields["name"], record_found.fields["blood_type"], new record_found.fields["species"], record_found.fields["features"])
new_character.hardset_dna(record_found.fields["identity"], record_found.fields["enzymes"], null, record_found.fields["name"], record_found.fields["blood_type"], new record_found.fields["species"], record_found.fields["features"])
else
var/datum/preferences/A = new()
A.copy_to(new_character)
@@ -1058,13 +1058,6 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
var/datum/atom_hud/A = GLOB.huds[ANTAG_HUD_TRAITOR]
return A.hudusers[mob]
/client/proc/open_shuttle_manipulator()
set category = "Admin"
set name = "Shuttle Manipulator"
set desc = "Opens the shuttle manipulator UI."
for(var/obj/machinery/shuttle_manipulator/M in GLOB.machines)
M.ui_interact(usr)
/client/proc/run_weather()
set category = "Fun"
@@ -1276,7 +1269,23 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
if(!check_rights(R_ADMIN) || !check_rights(R_FUN))
return
var/list/punishment_list = list(ADMIN_PUNISHMENT_PIE, ADMIN_PUNISHMENT_CUSTOM_PIE, ADMIN_PUNISHMENT_FIREBALL, ADMIN_PUNISHMENT_LIGHTNING, ADMIN_PUNISHMENT_BRAINDAMAGE, ADMIN_PUNISHMENT_BSA, ADMIN_PUNISHMENT_GIB, ADMIN_PUNISHMENT_SUPPLYPOD_QUICK, ADMIN_PUNISHMENT_SUPPLYPOD, ADMIN_PUNISHMENT_MAZING, ADMIN_PUNISHMENT_ROD)
var/list/punishment_list = list(ADMIN_PUNISHMENT_PIE,
ADMIN_PUNISHMENT_CUSTOM_PIE,
ADMIN_PUNISHMENT_FIREBALL,
ADMIN_PUNISHMENT_LIGHTNING,
ADMIN_PUNISHMENT_BRAINDAMAGE,
ADMIN_PUNISHMENT_BSA,
ADMIN_PUNISHMENT_GIB,
ADMIN_PUNISHMENT_SUPPLYPOD_QUICK,
ADMIN_PUNISHMENT_SUPPLYPOD,
ADMIN_PUNISHMENT_MAZING,
ADMIN_PUNISHMENT_ROD,
ADMIN_PUNISHMENT_SHOES,
ADMIN_PUNISHMENT_PICKLE,
ADMIN_PUNISHMENT_FRY,
ADMIN_PUNISHMENT_CRACK,
ADMIN_PUNISHMENT_BLEED,
ADMIN_PUNISHMENT_SCARIFY)
var/punishment = input("Choose a punishment", "DIVINE SMITING") as null|anything in punishment_list
@@ -1341,7 +1350,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
if(ADMIN_PUNISHMENT_PIE)
var/obj/item/reagent_containers/food/snacks/pie/cream/nostun/creamy = new(get_turf(target))
creamy.splat(target)
if (ADMIN_PUNISHMENT_CUSTOM_PIE)
if(ADMIN_PUNISHMENT_CUSTOM_PIE)
var/obj/item/reagent_containers/food/snacks/pie/cream/nostun/A = new()
if(!A.reagents)
var/amount = input(usr, "Specify the reagent size of [A]", "Set Reagent Size", 50) as num|null
@@ -1354,9 +1363,122 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
if(amount)
A.reagents.add_reagent(chosen_id, amount)
A.splat(target)
if(ADMIN_PUNISHMENT_CRACK)
if(!iscarbon(target))
to_chat(usr,"<span class='warning'>This must be used on a carbon mob.</span>", confidential = TRUE)
return
var/mob/living/carbon/C = target
for(var/i in C.bodyparts)
var/obj/item/bodypart/squish_part = i
var/type_wound = pick(list(/datum/wound/blunt/critical, /datum/wound/blunt/severe, /datum/wound/blunt/critical, /datum/wound/blunt/severe, /datum/wound/blunt/moderate))
squish_part.force_wound_upwards(type_wound, smited=TRUE)
if(ADMIN_PUNISHMENT_BLEED)
if(!iscarbon(target))
to_chat(usr,"<span class='warning'>This must be used on a carbon mob.</span>", confidential = TRUE)
return
var/mob/living/carbon/C = target
for(var/i in C.bodyparts)
var/obj/item/bodypart/slice_part = i
var/type_wound = pick(list(/datum/wound/slash/severe, /datum/wound/slash/moderate))
slice_part.force_wound_upwards(type_wound, smited=TRUE)
type_wound = pick(list(/datum/wound/slash/critical, /datum/wound/slash/severe, /datum/wound/slash/moderate))
slice_part.force_wound_upwards(type_wound, smited=TRUE)
type_wound = pick(list(/datum/wound/slash/critical, /datum/wound/slash/severe))
slice_part.force_wound_upwards(type_wound, smited=TRUE)
if(ADMIN_PUNISHMENT_SCARIFY)
if(!iscarbon(target))
to_chat(usr,"<span class='warning'>This must be used on a carbon mob.</span>", confidential = TRUE)
return
var/mob/living/carbon/dude = target
dude.generate_fake_scars(rand(1, 4))
to_chat(dude, "<span class='warning'>You feel your body grow jaded and torn...</span>")
if(ADMIN_PUNISHMENT_PERFORATE)
if(!iscarbon(target))
to_chat(usr,"<span class='warning'>This must be used on a carbon mob.</span>", confidential = TRUE)
return
var/list/how_fucked_is_this_dude = list("A little", "A lot", "So fucking much", "FUCK THIS DUDE")
var/hatred = input("How much do you hate this guy?") in how_fucked_is_this_dude
var/repetitions
var/shots_per_limb_per_rep = 2
var/damage
switch(hatred)
if("A little")
repetitions = 1
damage = 5
if("A lot")
repetitions = 2
damage = 8
if("So fucking much")
repetitions = 3
damage = 10
if("FUCK THIS DUDE")
repetitions = 4
damage = 10
var/mob/living/carbon/dude = target
var/list/open_adj_turfs = get_adjacent_open_turfs(dude)
var/list/wound_bonuses = list(15, 70, 110, 250)
var/delay_per_shot = 1
var/delay_counter = 1
dude.Immobilize(5 SECONDS)
for(var/wound_bonus_rep in 1 to repetitions)
for(var/i in dude.bodyparts)
var/obj/item/bodypart/slice_part = i
var/shots_this_limb = 0
for(var/t in shuffle(open_adj_turfs))
var/turf/iter_turf = t
addtimer(CALLBACK(GLOBAL_PROC, .proc/firing_squad, dude, iter_turf, slice_part.body_zone, wound_bonuses[wound_bonus_rep], damage), delay_counter)
delay_counter += delay_per_shot
shots_this_limb++
if(shots_this_limb > shots_per_limb_per_rep)
break
if(ADMIN_PUNISHMENT_PICKLE)
target.turn_into_pickle()
if(ADMIN_PUNISHMENT_FRY)
target.fry()
if(ADMIN_PUNISHMENT_SHOES)
if(!iscarbon(target))
to_chat(usr,"<span class='warning'>This must be used on a carbon mob.</span>")
return
var/mob/living/carbon/C = target
var/obj/item/clothing/shoes/sick_kicks = C.shoes
if(!sick_kicks?.can_be_tied)
to_chat(usr,"<span class='warning'>[C] does not have knottable shoes!</span>")
return
sick_kicks.adjust_laces(SHOES_KNOTTED)
punish_log(target, punishment)
/**
* firing_squad is a proc for the :B:erforate smite to shoot each individual bullet at them, so that we can add actual delays without sleep() nonsense
*
* Hilariously, if you drag someone away mid smite, the bullets will still chase after them from the original spot, possibly hitting other people. Too funny to fix imo
*
* Arguments:
* * target- guy we're shooting obviously
* * source_turf- where the bullet begins, preferably on a turf next to the target
* * body_zone- which bodypart we're aiming for, if there is one there
* * wound_bonus- the wounding power we're assigning to the bullet, since we don't care about the base one
* * damage- the damage we're assigning to the bullet, since we don't care about the base one
*/
/proc/firing_squad(mob/living/carbon/target, turf/source_turf, body_zone, wound_bonus, damage)
if(!target.get_bodypart(body_zone))
return
playsound(target, 'sound/weapons/shot.ogg', 100)
var/obj/item/projectile/bullet/smite/divine_wrath = new(source_turf)
divine_wrath.damage = damage
divine_wrath.wound_bonus = wound_bonus
divine_wrath.original = target
divine_wrath.def_zone = body_zone
divine_wrath.spread = 0
divine_wrath.preparePixelProjectile(target, source_turf)
divine_wrath.fire()
/client/proc/punish_log(var/whom, var/punishment)
var/msg = "[key_name_admin(usr)] punished [key_name_admin(whom)] with [punishment]."
message_admins(msg)
+76
View File
@@ -0,0 +1,76 @@
/datum/admins/proc/open_shuttlepanel()
set category = "Admin"
set name = "Shuttle Manipulator"
set desc = "Opens the shuttle manipulator UI."
if(!check_rights(R_ADMIN))
return
SSshuttle.ui_interact(usr)
/obj/docking_port/mobile/proc/admin_fly_shuttle(mob/user)
var/list/options = list()
for(var/port in SSshuttle.stationary)
if (istype(port, /obj/docking_port/stationary/transit))
continue // please don't do this
var/obj/docking_port/stationary/S = port
if (canDock(S) == SHUTTLE_CAN_DOCK)
options[S.name || S.id] = S
options += "--------"
options += "Infinite Transit"
options += "Delete Shuttle"
options += "Into The Sunset (delete & greentext 'escape')"
var/selection = input(user, "Select where to fly [name || id]:", "Fly Shuttle") as null|anything in options
if(!selection)
return
switch(selection)
if("Infinite Transit")
destination = null
mode = SHUTTLE_IGNITING
setTimer(ignitionTime)
if("Delete Shuttle")
if(alert(user, "Really delete [name || id]?", "Delete Shuttle", "Cancel", "Really!") != "Really!")
return
jumpToNullSpace()
if("Into The Sunset (delete & greentext 'escape')")
if(alert(user, "Really delete [name || id] and greentext escape objectives?", "Delete Shuttle", "Cancel", "Really!") != "Really!")
return
intoTheSunset()
else
if(options[selection])
request(options[selection])
/obj/docking_port/mobile/emergency/admin_fly_shuttle(mob/user)
return // use the existing verbs for this
/obj/docking_port/mobile/arrivals/admin_fly_shuttle(mob/user)
switch(alert(user, "Would you like to fly the arrivals shuttle once or change its destination?", "Fly Shuttle", "Fly", "Retarget", "Cancel"))
if("Cancel")
return
if("Fly")
return ..()
var/list/options = list()
for(var/port in SSshuttle.stationary)
if (istype(port, /obj/docking_port/stationary/transit))
continue // please don't do this
var/obj/docking_port/stationary/S = port
if (canDock(S) == SHUTTLE_CAN_DOCK)
options[S.name || S.id] = S
var/selection = input(user, "Select the new arrivals destination:", "Fly Shuttle") as null|anything in options
if(!selection)
return
target_dock = options[selection]
if(!QDELETED(target_dock))
destination = target_dock
@@ -23,6 +23,7 @@ GLOBAL_LIST_EMPTY(antagonists)
var/show_name_in_check_antagonists = FALSE //Will append antagonist name in admin listings - use for categories that share more than one antag type
var/list/blacklisted_quirks = list(/datum/quirk/nonviolent,/datum/quirk/mute) // Quirks that will be removed upon gaining this antag. Pacifist and mute are default.
var/threat = 0 // Amount of threat this antag poses, for dynamic mode
var/show_to_ghosts = FALSE // Should this antagonist be shown as antag to ghosts? Shouldn't be used for stealthy antagonists like traitors
var/list/skill_modifiers
@@ -7,6 +7,7 @@
job_rank = ROLE_ABDUCTOR
show_in_antagpanel = FALSE //should only show subtypes
threat = 5
show_to_ghosts = TRUE
var/datum/team/abductor_team/team
var/sub_role
var/outfit
@@ -59,8 +60,10 @@
/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'>Try not to disturb the habitat, it could lead to dead specimens.</span>")
to_chat(owner.current, "<span class='notice'>You are an operative for your home planet's government. Your mission is to detain, experiment, and observe.</span>")
to_chat(owner.current, "<span class='notice'>Work together with your teammate to bring live subjects from the space station nearby onto your ship for experimentation.</span>")
to_chat(owner.current, "<span class='notice'>For the sake of the mission, do not damage the integrity of the station, do not kill anyone unless in self defense, always capture specimens first if you can, and do not steal equipment or belongings from abducted specimens.</span>")
to_chat(owner.current, "<span class='notice'>Your task is to observe and take notes of the effects of your experiments.</span>")
to_chat(owner.current, "<span class='notice'>[greet_text]</span>")
owner.announce_objectives()
@@ -0,0 +1,79 @@
GLOBAL_LIST_INIT(abductor_gear, subtypesof(/datum/abductor_gear))
/datum/abductor_gear
/// Name of the gear
var/name = "Generic Abductor Gear"
/// Description of the gear
var/description = "Generic description."
/// Unique ID of the gear
var/id = "abductor_generic"
/// Credit cost of the gear
var/cost = 1
/// Build path of the gear itself
var/build_path = null
/// Category of the gear
var/category = "Basic Gear"
/datum/abductor_gear/agent_helmet
name = "Agent Helmet"
description = "Abduct with style - spiky style. Prevents digital tracking."
id = "agent_helmet"
build_path = /obj/item/clothing/head/helmet/abductor
/datum/abductor_gear/agent_vest
name = "Agent Vest"
description = "A vest outfitted with advanced stealth technology. It has two modes - combat and stealth."
id = "agent_vest"
build_path = /obj/item/clothing/suit/armor/abductor/vest
/datum/abductor_gear/radio_silencer
name = "Radio Silencer"
description = "A compact device used to shut down communications equipment."
id = "radio_silencer"
build_path = /obj/item/abductor/silencer
/datum/abductor_gear/science_tool
name = "Science Tool"
description = "A dual-mode tool for retrieving specimens and scanning appearances. Scanning can be done through cameras."
id = "science_tool"
build_path = /obj/item/abductor/gizmo
/datum/abductor_gear/advanced_baton
name = "Advanced Baton"
description = "A quad-mode baton used for incapacitation and restraining of specimens."
id = "advanced_baton"
cost = 2
build_path = /obj/item/abductor/baton //does not exist?
/datum/abductor_gear/superlingual_matrix
name = "Superlingual Matrix"
description = "A mysterious structure that allows for instant communication between users. Pretty impressive until you need to eat something."
id = "superlingual_matrix"
build_path = /obj/item/organ/tongue/abductor
category = "Advanced Gear"
/datum/abductor_gear/mental_interface
name = "Mental Interface Device"
description = "A dual-mode tool for directly communicating with sentient brains. It can be used to send a direct message to a target, \
or to send a command to a test subject with a charged gland."
id = "mental_interface"
cost = 2
build_path = /obj/item/abductor/mind_device
category = "Advanced Gear"
/datum/abductor_gear/reagent_synthesizer
name = "Reagent Synthesizer"
description = "Synthesizes a variety of reagents using proto-matter."
id = "reagent_synthesizer"
cost = 2
build_path = /obj/item/abductor_machine_beacon/chem_dispenser
category = "Advanced Gear"
/datum/abductor_gear/shrink_ray
name = "Shrink Ray Blaster"
description = "This is a piece of frightening alien tech that enhances the magnetic pull of atoms in a localized space to temporarily make an object shrink. \
That or it's just space magic. Either way, it shrinks stuff."
id = "shrink_ray"
cost = 2
build_path = /obj/item/gun/energy/shrink_ray
category = "Advanced Gear"
@@ -23,102 +23,121 @@
var/obj/machinery/abductor/pad/pad
var/obj/machinery/computer/camera_advanced/abductor/camera
var/list/datum/icon_snapshot/disguises = list()
/// Currently selected gear category
var/selected_cat
/// Dictates if the compact mode of the interface is on or off
var/compact_mode = FALSE
/// Possible gear to be dispensed
var/list/possible_gear
/obj/machinery/abductor/console/attack_hand(mob/user)
/obj/machinery/abductor/console/Initialize(mapload)
. = ..()
if(.)
return
possible_gear = get_abductor_gear()
/**
* get_abductor_gear: Returns a list of a filtered abductor gear sorted by categories
*/
/obj/machinery/abductor/console/proc/get_abductor_gear()
var/list/filtered_modules = list()
for(var/path in GLOB.abductor_gear)
var/datum/abductor_gear/AG = new path
if(!filtered_modules[AG.category])
filtered_modules[AG.category] = list()
filtered_modules[AG.category][AG] = AG
return filtered_modules
/obj/machinery/abductor/console/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(!HAS_TRAIT(user, TRAIT_ABDUCTOR_TRAINING) && !HAS_TRAIT(user.mind, TRAIT_ABDUCTOR_TRAINING))
to_chat(user, "<span class='warning'>You start mashing alien buttons at random!</span>")
if(do_after(user,100, target = src))
TeleporterSend()
return
user.set_machine(src)
var/dat = ""
dat += "<H3> Abductsoft 3000 </H3>"
/obj/machinery/abductor/console/ui_status(mob/user)
if(!isabductor(user) && !isobserver(user))
return UI_CLOSE
return ..()
/obj/machinery/abductor/console/ui_state(mob/user)
return GLOB.physical_state
/obj/machinery/abductor/console/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "AbductorConsole", name)
ui.open()
/obj/machinery/abductor/console/ui_static_data(mob/user)
var/list/data = list()
data["categories"] = list()
for(var/category in possible_gear)
var/list/cat = list(
"name" = category,
"items" = (category == selected_cat ? list() : null))
for(var/gear in possible_gear[category])
var/datum/abductor_gear/AG = possible_gear[category][gear]
cat["items"] += list(list(
"name" = AG.name,
"cost" = AG.cost,
"desc" = AG.description,
))
data["categories"] += list(cat)
return data
/obj/machinery/abductor/console/ui_data(mob/user)
var/list/data = list()
data["compactMode"] = compact_mode
data["experiment"] = experiment ? TRUE : FALSE
if(experiment)
var/points = experiment.points
var/credits = experiment.credits
dat += "Collected Samples : [points] <br>"
dat += "Gear Credits: [credits] <br>"
dat += "<b>Transfer data in exchange for supplies:</b><br>"
dat += "<a href='?src=[REF(src)];dispense=baton'>Advanced Baton (2 Credits)</A><br>"
dat += "<a href='?src=[REF(src)];dispense=mind_device'>Mental Interface Device (2 Credits)</A><br>"
dat += "<a href='?src=[REF(src)];dispense=chem_dispenser'>Reagent Synthesizer (2 Credits)</A><br>"
dat += "<a href='?src=[REF(src)];dispense=helmet'>Agent Helmet (1 Credit)</A><br>"
dat += "<a href='?src=[REF(src)];dispense=vest'>Agent Vest (1 Credit)</A><br>"
dat += "<a href='?src=[REF(src)];dispense=silencer'>Radio Silencer (1 Credit)</A><br>"
dat += "<a href='?src=[REF(src)];dispense=tool'>Science Tool (1 Credit)</A><br>"
dat += "<a href='?src=[REF(src)];dispense=tongue'>Superlingual Matrix (1 Credit)</a><br>"
else
dat += "<span class='bad'>NO EXPERIMENT MACHINE DETECTED</span> <br>"
data["points"] = experiment.points
data["credits"] = experiment.credits
data["pad"] = pad ? TRUE : FALSE
if(pad)
dat += "<span class='bad'>Emergency Teleporter System.</span>"
dat += "<span class='bad'>Consider using primary observation console first.</span>"
dat += "<a href='?src=[REF(src)];teleporter_send=1'>Activate Teleporter</A><br>"
if(gizmo && gizmo.marked)
dat += "<a href='?src=[REF(src)];teleporter_retrieve=1'>Retrieve Mark</A><br>"
else
dat += "<span class='linkOff'>Retrieve Mark</span><br>"
else
dat += "<span class='bad'>NO TELEPAD DETECTED</span></br>"
data["gizmo"] = gizmo && gizmo.marked ? TRUE : FALSE
data["vest"] = vest ? TRUE : FALSE
if(vest)
dat += "<h4> Agent Vest Mode </h4><br>"
var/mode = vest.mode
if(mode == VEST_STEALTH)
dat += "<a href='?src=[REF(src)];flip_vest=1'>Combat</A>"
dat += "<span class='linkOff'>Stealth</span>"
else
dat += "<span class='linkOff'>Combat</span>"
dat += "<a href='?src=[REF(src)];flip_vest=1'>Stealth</A>"
data["vest_mode"] = vest.mode
data["vest_lock"] = HAS_TRAIT_FROM(vest, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT)
return data
dat+="<br>"
dat += "<a href='?src=[REF(src)];select_disguise=1'>Select Agent Vest Disguise</a><br>"
dat += "<a href='?src=[REF(src)];toggle_vest=1'>[HAS_TRAIT_FROM(vest, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT) ? "Unlock" : "Lock"] Vest</a><br>"
else
dat += "<span class='bad'>NO AGENT VEST DETECTED</span>"
var/datum/browser/popup = new(user, "computer", "Abductor Console", 400, 500)
popup.set_content(dat)
popup.open()
/obj/machinery/abductor/console/Topic(href, href_list)
if(..())
/obj/machinery/abductor/console/ui_act(action, list/params)
. = ..()
if(.)
return
usr.set_machine(src)
if(href_list["teleporter_send"])
TeleporterSend()
else if(href_list["teleporter_retrieve"])
TeleporterRetrieve()
else if(href_list["flip_vest"])
FlipVest()
else if(href_list["toggle_vest"])
if(vest)
switch(action)
if("buy")
var/item_name = params["name"]
var/list/buyable_items = list()
for(var/category in possible_gear)
buyable_items += possible_gear[category]
for(var/key in buyable_items)
var/datum/abductor_gear/AG = buyable_items[key]
if(AG.name == item_name)
Dispense(AG.build_path, AG.cost)
return TRUE
if("teleporter_send")
TeleporterSend()
return TRUE
if("teleporter_retrieve")
TeleporterRetrieve()
return TRUE
if("flip_vest")
FlipVest()
return TRUE
if("toggle_vest")
if(!vest)
return
vest.toggle_nodrop()
else if(href_list["select_disguise"])
SelectDisguise()
else if(href_list["dispense"])
switch(href_list["dispense"])
if("baton")
Dispense(/obj/item/abductor/baton,cost=2)
if("helmet")
Dispense(/obj/item/clothing/head/helmet/abductor)
if("silencer")
Dispense(/obj/item/abductor/silencer)
if("tool")
Dispense(/obj/item/abductor/gizmo)
if("vest")
Dispense(/obj/item/clothing/suit/armor/abductor/vest)
if("mind_device")
Dispense(/obj/item/abductor/mind_device,cost=2)
if("chem_dispenser")
Dispense(/obj/item/abductor_machine_beacon/chem_dispenser,cost=2)
if("tongue")
Dispense(/obj/item/organ/tongue/abductor)
updateUsrDialog()
return TRUE
if("select_disguise")
SelectDisguise()
return TRUE
if("select")
selected_cat = params["category"]
return TRUE
if("compact_toggle")
compact_mode = !compact_mode
return TRUE
/obj/machinery/abductor/console/proc/TeleporterRetrieve()
if(pad && gizmo && gizmo.marked)
@@ -159,7 +178,6 @@
pad.teleport_target = location
to_chat(user, "<span class='notice'>Location marked as test subject release point.</span>")
/obj/machinery/abductor/console/Initialize(mapload)
..()
return INITIALIZE_HINT_LATELOAD
@@ -22,41 +22,44 @@
gland_colors[i] = random_color()
amounts[i] = rand(1,5)
/obj/machinery/abductor/gland_dispenser/attack_hand(mob/user)
/obj/machinery/abductor/gland_dispenser/ui_status(mob/user)
if(!isabductor(user) && !isobserver(user))
return UI_CLOSE
return ..()
/obj/machinery/abductor/gland_dispenser/ui_state(mob/user)
return GLOB.physical_state
/obj/machinery/abductor/gland_dispenser/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "GlandDispenser", name)
ui.open()
/obj/machinery/abductor/gland_dispenser/ui_data(mob/user)
var/list/data = list()
data["glands"] = list()
for(var/gland_number=1,gland_number<=gland_colors.len,gland_number++)
var/list/gland_information = list(
"color" = gland_colors[gland_number],
"amount" = amounts[gland_number],
"id" = gland_number,
)
data["glands"] += list(gland_information)
return data
/obj/machinery/abductor/gland_dispenser/ui_act(action, list/params)
. = ..()
if(.)
return
if(!isabductor(user))
return
user.set_machine(src)
var/box_css = {"
<style>
a.box.gland {
float: left;
width: 20px;
height: 20px;
margin: 5px;
border-width: 1px;
border-style: solid;
border-color: rgba(0,0,0,.2);
text-align: center;
}
</style>"}
var/dat = ""
var/item_count = 0
for(var/i=1,i<=gland_colors.len,i++)
item_count++
var/g_color = gland_colors[i]
var/amount = amounts[i]
dat += "<a class='box gland' style='background-color:[g_color]' href='?src=[REF(src)];dispense=[i]'>[amount]</a>"
if(item_count == 4) // Four boxes per line
dat +="</br></br>"
item_count = 0
var/datum/browser/popup = new(user, "glands", "Gland Dispenser", 200, 200)
popup.add_head_content(box_css)
popup.set_content(dat)
popup.open()
return
switch(action)
if("dispense")
var/gland_id = text2num(params["gland_id"])
if(!gland_id)
return
Dispense(gland_id)
return TRUE
/obj/machinery/abductor/gland_dispenser/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/organ/heart/gland))
@@ -68,15 +71,6 @@
else
return ..()
/obj/machinery/abductor/gland_dispenser/Topic(href, href_list)
if(..())
return
usr.set_machine(src)
if(href_list["dispense"])
Dispense(text2num(href_list["dispense"]))
updateUsrDialog()
/obj/machinery/abductor/gland_dispenser/proc/Dispense(count)
if(amounts[count]>0)
amounts[count]--
@@ -15,19 +15,13 @@
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))
var/mob/living/L = user
if(user.stat || (isliving(user) && (!(L.mobility_flags & MOBILITY_STAND) || !(L.mobility_flags & MOBILITY_UI))) || !Adjacent(user) || !target.Adjacent(user) || !ishuman(target))
return
if(isabductor(target))
return
close_machine(target)
/obj/machinery/abductor/experiment/attack_hand(mob/user)
. = ..()
if(.)
return
experimentUI(user)
/obj/machinery/abductor/experiment/open_machine()
if(!state_open && !panel_open)
..()
@@ -47,11 +41,9 @@
to_chat(user, "<span class='warning'>[src]'s door won't budge!</span>")
/obj/machinery/abductor/experiment/container_resist(mob/living/user)
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 [DisplayTimeText(breakout_time)].)</span>", \
"<span class='italics'>You hear a metallic creaking from [src].</span>")
"<span class='hear'>You hear a metallic creaking from [src].</span>")
if(do_after(user,(breakout_time), target = src))
if(!user || user.stat != CONSCIOUS || user.loc != src || state_open)
return
@@ -59,122 +51,84 @@
"<span class='notice'>You successfully break out of [src]!</span>")
open_machine()
/obj/machinery/abductor/experiment/proc/dissection_icon(mob/living/carbon/human/H)
var/icon/photo = null
var/g = (H.dna.features["body_model"] == FEMALE) ? "f" : "m"
if(H.dna.species.use_skintones)
photo = icon("icon" = 'icons/mob/human.dmi', "icon_state" = "[H.skin_tone]_[g]")
else
photo = icon("icon" = 'icons/mob/human.dmi', "icon_state" = "[H.dna.species.id]_[g]")
photo.Blend("#[H.dna.features["mcolor"]]", ICON_MULTIPLY)
/obj/machinery/abductor/experiment/ui_status(mob/user)
if(user == occupant)
return UI_CLOSE
return ..()
var/icon/eyes
if(EYECOLOR in H.dna.species.species_traits)
eyes = icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "eyes")
eyes.Blend("#[H.eye_color]", ICON_MULTIPLY)
/obj/machinery/abductor/experiment/ui_state(mob/user)
return GLOB.physical_state
var/datum/sprite_accessory/S
S = GLOB.hair_styles_list[H.hair_style]
if(S && (HAIR in H.dna.species.species_traits))
var/icon/hair = icon("icon" = S.icon, "icon_state" = "[S.icon_state]")
hair.Blend("#[H.hair_color]", ICON_MULTIPLY)
eyes.Blend(hair, ICON_OVERLAY)
/obj/machinery/abductor/experiment/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "ProbingConsole", name)
ui.open()
S = GLOB.facial_hair_styles_list[H.facial_hair_style]
if(S && (FACEHAIR in H.dna.species.species_traits))
var/icon/facial = icon("icon" = S.icon, "icon_state" = "[S.icon_state]")
facial.Blend("#[H.facial_hair_color]", ICON_MULTIPLY)
eyes.Blend(facial, ICON_OVERLAY)
if(eyes)
photo.Blend(eyes, ICON_OVERLAY)
var/icon/splat = icon("icon" = 'icons/mob/dam_mob.dmi',"icon_state" = "chest30")
photo.Blend(splat,ICON_OVERLAY)
return photo
/obj/machinery/abductor/experiment/proc/experimentUI(mob/user)
var/dat
dat += "<h3> Experiment </h3>"
if(occupant)
var/obj/item/photo/P = new
P.picture = new
P.picture.picture_image = icon(dissection_icon(occupant), dir = SOUTH)
user << browse_rsc(P.picture.picture_image, "dissection_img")
dat += "<table><tr><td>"
dat += "<img src=dissection_img height=80 width=80>" //Avert your eyes
dat += "</td><td>"
dat += "<a href='?src=[REF(src)];experiment=1'>Probe</a><br>"
dat += "<a href='?src=[REF(src)];experiment=2'>Dissect</a><br>"
dat += "<a href='?src=[REF(src)];experiment=3'>Analyze</a><br>"
dat += "</td></tr></table>"
else
dat += "<span class='linkOff'>Experiment </span>"
if(!occupant)
dat += "<h3>Machine Unoccupied</h3>"
else
dat += "<h3>Subject Status : </h3>"
dat += "[occupant.name] => "
var/mob/living/mob_occupant = occupant
switch(mob_occupant.stat)
if(CONSCIOUS)
dat += "<span class='good'>Conscious</span>"
if(UNCONSCIOUS)
dat += "<span class='average'>Unconscious</span>"
else // DEAD
dat += "<span class='bad'>Deceased</span>"
dat += "<br>"
dat += "[flash]"
dat += "<br>"
dat += "<a href='?src=[REF(src)];refresh=1'>Scan</a>"
dat += "<a href='?src=[REF(src)];[state_open ? "close=1'>Close</a>" : "open=1'>Open</a>"]"
var/datum/browser/popup = new(user, "experiment", "Probing Console", 300, 300)
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
popup.set_content(dat)
popup.open()
/obj/machinery/abductor/experiment/Topic(href, href_list)
if(..() || usr == occupant)
return
usr.set_machine(src)
if(href_list["refresh"])
updateUsrDialog()
return
if(href_list["open"])
open_machine()
return
if(href_list["close"])
close_machine()
return
/obj/machinery/abductor/experiment/ui_data(mob/user)
var/list/data = list()
data["open"] = state_open
data["feedback"] = flash
data["occupant"] = occupant ? TRUE : FALSE
data["occupant_name"] = null
data["occupant_status"] = null
if(occupant)
var/mob/living/mob_occupant = occupant
if(mob_occupant.stat != DEAD)
if(href_list["experiment"])
flash = Experiment(occupant,href_list["experiment"],usr)
updateUsrDialog()
add_fingerprint(usr)
data["occupant_name"] = mob_occupant.name
data["occupant_status"] = mob_occupant.stat
return data
/obj/machinery/abductor/experiment/proc/Experiment(mob/occupant,type,mob/user)
/obj/machinery/abductor/experiment/ui_act(action, list/params)
. = ..()
if(.)
return
switch(action)
if("door")
if(state_open)
close_machine()
return TRUE
else
open_machine()
return TRUE
if("experiment")
if(!occupant)
return
var/mob/living/mob_occupant = occupant
if(mob_occupant.stat == DEAD)
return
flash = experiment(occupant, params["experiment_type"], usr)
return TRUE
/**
* experiment: Performs selected experiment on occupant mob, resulting in a point reward on success
*
* Arguments:
* * occupant The mob inside the machine
* * type The type of experiment to be performed
* * user The mob starting the experiment
*/
/obj/machinery/abductor/experiment/proc/experiment(mob/occupant, type, mob/user)
LAZYINITLIST(history)
var/mob/living/carbon/human/H = occupant
var/datum/antagonist/abductor/user_abductor = user.mind.has_antag_datum(/datum/antagonist/abductor)
if(!user_abductor)
return "<span class='bad'>Authorization failure. Contact mothership immidiately.</span>"
return "Authorization failure. Contact mothership immediately."
var/point_reward = 0
if(!H)
return "Invalid or missing specimen."
if(H in history)
return "<span class='bad'>Specimen already in database.</span>"
return "Specimen already in database."
if(H.stat == DEAD)
say("Specimen deceased - please provide fresh sample.")
return "<span class='bad'>Specimen deceased.</span>"
return "Specimen deceased."
var/obj/item/organ/heart/gland/GlandTest = locate() in H.internal_organs
if(!GlandTest)
say("Experimental dissection not detected!")
return "<span class='bad'>No glands detected!</span>"
if(H.mind != null && (H.voluntary_ghosted || (H.ckey != null)))
return "No glands detected!"
if(H.mind != null && H.ckey != null)
LAZYINITLIST(abductee_minds)
LAZYADD(history, H)
LAZYADD(abductee_minds, H.mind)
@@ -196,22 +150,27 @@
point_reward++
if(point_reward > 0)
open_machine()
SendBack(H)
playsound(src.loc, 'sound/machines/ding.ogg', 50, 1)
send_back(H)
playsound(src.loc, 'sound/machines/ding.ogg', 50, TRUE)
points += point_reward
credits += point_reward
return "<span class='good'>Experiment successful! [point_reward] new data-points collected.</span>"
return "Experiment successful! [point_reward] new data-points collected."
else
playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 1)
return "<span class='bad'>Experiment failed! No replacement organ detected.</span>"
playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, TRUE)
return "Experiment failed! No replacement organ detected."
else
say("Brain activity nonexistent - disposing sample...")
open_machine()
SendBack(H)
return "<span class='bad'>Specimen braindead - disposed.</span>"
send_back(H)
return "Specimen braindead - disposed."
/obj/machinery/abductor/experiment/proc/SendBack(mob/living/carbon/human/H)
/**
* send_back: Sends a mob back to a selected teleport location if safe
*
* Arguments:
* * H The human mob to be sent back
*/
/obj/machinery/abductor/experiment/proc/send_back(mob/living/carbon/human/H)
H.Sleeping(160)
H.uncuff()
if(console && console.pad && console.pad.teleport_target)
@@ -221,7 +180,6 @@
SSjob.SendToLateJoin(H, FALSE)
return
/obj/machinery/abductor/experiment/update_icon_state()
if(state_open)
icon_state = "experiment-open"
+1
View File
@@ -2,6 +2,7 @@
name = "Blob"
roundend_category = "blobs"
antagpanel_category = "Blob"
show_to_ghosts = TRUE
job_rank = ROLE_BLOB
threat = 20
var/datum/action/innate/blobpop/pop_action
@@ -45,11 +45,8 @@
desc = "A solid wall of slightly twitching tendrils with a reflective glow."
damaged_desc = "A wall of twitching tendrils with a reflective glow."
icon_state = "blob_glow"
flags_ricochet = RICOCHET_SHINY
point_return = 8
max_integrity = 100
brute_resist = 1
explosion_block = 2
/obj/structure/blob/shield/reflective/check_projectile_ricochet(obj/item/projectile/P)
return PROJECTILE_RICOCHET_FORCE
@@ -36,6 +36,6 @@
M.adjust_fire_stacks(round(reac_volume/10))
M.IgniteMob()
if(M)
M.apply_damage(0.8*reac_volume, BURN)
M.apply_damage(0.8*reac_volume, BURN, wound_bonus=CANT_WOUND)
if(iscarbon(M))
M.emote("scream")
@@ -22,7 +22,7 @@
M.reagents.add_reagent("frostoil", 0.3*reac_volume)
M.reagents.add_reagent("ice", 0.3*reac_volume)
M.reagents.add_reagent("cryogenic_poison", 0.3*reac_volume)
M.apply_damage(0.2*reac_volume, BRUTE)
M.apply_damage(0.2*reac_volume, BRUTE, wound_bonus=CANT_WOUND)
/datum/reagent/blob/cryogenic_poison/on_mob_life(mob/living/carbon/M)
M.adjustBruteLoss(0.3*REAGENTS_EFFECT_MULTIPLIER, 0)
@@ -30,4 +30,4 @@
if(prob(reac_volume*2))
M.emp_act(EMP_LIGHT)
if(M)
M.apply_damage(reac_volume, BURN)
M.apply_damage(reac_volume, BURN, wound_bonus=CANT_WOUND)
@@ -33,8 +33,8 @@
if(ROLE_BLOB in L.faction) //no friendly fire
continue
var/aoe_volume = ..(L, TOUCH, initial_volume, 0, L.get_permeability_protection(), O)
L.apply_damage(0.4*aoe_volume, BRUTE)
L.apply_damage(0.4*aoe_volume, BRUTE, wound_bonus=CANT_WOUND)
if(M)
M.apply_damage(0.6*reac_volume, BRUTE)
M.apply_damage(0.6*reac_volume, BRUTE, wound_bonus=CANT_WOUND)
else
M.apply_damage(0.6*reac_volume, BRUTE)
M.apply_damage(0.6*reac_volume, BRUTE, wound_bonus=CANT_WOUND)
@@ -33,6 +33,6 @@
/datum/reagent/blob/networked_fibers/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
reac_volume = ..()
M.apply_damage(0.6*reac_volume, BRUTE)
M.apply_damage(0.6*reac_volume, BRUTE, wound_bonus=CANT_WOUND)
if(M)
M.apply_damage(0.6*reac_volume, BURN)
M.apply_damage(0.6*reac_volume, BURN, wound_bonus=CANT_WOUND)
@@ -44,7 +44,7 @@
T.MakeSlippery(TURF_WET_LUBE, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS)
M.adjust_fire_stacks(-(reac_volume / 10))
M.ExtinguishMob()
M.apply_damage(0.4*reac_volume, BRUTE)
M.apply_damage(0.4*reac_volume, BRUTE, wound_bonus=CANT_WOUND)
if(M)
M.apply_damage(0.4*reac_volume, OXY)
if(M)
@@ -31,4 +31,4 @@
/datum/reagent/blob/replicating_foam/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
reac_volume = ..()
M.apply_damage(0.7*reac_volume, BRUTE)
M.apply_damage(0.7*reac_volume, BRUTE, wound_bonus=CANT_WOUND)
@@ -32,4 +32,4 @@
/datum/reagent/blob/shifting_fragments/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
reac_volume = ..()
M.apply_damage(0.7*reac_volume, BRUTE)
M.apply_damage(0.7*reac_volume, BRUTE, wound_bonus=CANT_WOUND)
@@ -30,9 +30,9 @@
/datum/reagent/blob/synchronous_mesh/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
reac_volume = ..()
M.apply_damage(0.2*reac_volume, BRUTE)
M.apply_damage(0.2*reac_volume, BRUTE, wound_bonus=CANT_WOUND)
if(M && reac_volume)
for(var/obj/structure/blob/B in range(1, M)) //if the target is completely surrounded, this is 2.4*reac_volume bonus damage, total of 2.6*reac_volume
if(M)
B.blob_attack_animation(M) //show them they're getting a bad time
M.apply_damage(0.3*reac_volume, BRUTE)
M.apply_damage(0.3*reac_volume, BRUTE, wound_bonus=CANT_WOUND)
@@ -225,7 +225,7 @@
/obj/structure/blob/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/analyzer))
user.changeNext_move(CLICK_CD_MELEE)
user.DelayNextAction(CLICK_CD_MELEE)
to_chat(user, "<b>The analyzer beeps once, then reports:</b><br>")
SEND_SOUND(user, sound('sound/machines/ping.ogg'))
if(overmind)
@@ -10,26 +10,24 @@
//
// Show as dead when...
/datum/antagonist/bloodsucker/proc/LifeTick()// Should probably run from life.dm, same as handle_changeling, but will be an utter pain to move
set waitfor = FALSE // Don't make on_gain() wait for this function to finish. This lets this code run on the side.
var/notice_healing
while(owner && !AmFinalDeath()) // owner.has_antag_datum(ANTAG_DATUM_BLOODSUCKER) == src
if(owner.current.stat == CONSCIOUS && !poweron_feed && !HAS_TRAIT(owner.current, TRAIT_FAKEDEATH)) // Deduct Blood
AddBloodVolume(passive_blood_drain) // -.1 currently
if(HandleHealing(1)) // Heal
if(!notice_healing && owner.current.blood_volume > 0)
to_chat(owner, "<span class='notice'>The power of your blood begins knitting your wounds...</span>")
notice_healing = TRUE
else if(notice_healing == TRUE)
notice_healing = FALSE // Apply Low Blood Effects
HandleStarving() // Death
HandleDeath() // Standard Update
update_hud()// Daytime Sleep in Coffin
if(SSticker.mode.is_daylight() && !HAS_TRAIT_FROM(owner.current, TRAIT_FAKEDEATH, "bloodsucker"))
if(istype(owner.current.loc, /obj/structure/closet/crate/coffin))
Torpor_Begin()
// Wait before next pass
sleep(10)
/datum/antagonist/bloodsucker/proc/LifeTick() //Runs from BiologicalLife, handles all the bloodsucker constant proccesses
if(!owner || AmFinalDeath())
return
if(owner.current.stat == CONSCIOUS && !poweron_feed && !HAS_TRAIT(owner.current, TRAIT_FAKEDEATH)) // Deduct Blood
AddBloodVolume(passive_blood_drain) // -.1 currently
if(HandleHealing(1)) // Heal
if(!notice_healing && owner.current.blood_volume > 0)
to_chat(owner, "<span class='notice'>The power of your blood begins knitting your wounds...</span>")
notice_healing = TRUE
else if(notice_healing)
notice_healing = FALSE // Apply Low Blood Effects
HandleStarving() // Death
HandleDeath() // Standard Update
update_hud()// Daytime Sleep in Coffin
if(SSticker.mode.is_daylight() && !HAS_TRAIT_FROM(owner.current, TRAIT_FAKEDEATH, "bloodsucker"))
if(istype(owner.current.loc, /obj/structure/closet/crate/coffin))
Torpor_Begin()
// Wait before next pass
FreeAllVassals() // Free my Vassals! (if I haven't yet)
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -272,13 +270,15 @@
/datum/antagonist/bloodsucker/AmFinalDeath()
return owner && owner.AmFinalDeath()
/datum/antagonist/changeling/AmFinalDeath()
return owner && owner.AmFinalDeath()
/datum/mind/proc/AmFinalDeath()
return !current || QDELETED(current) || !isliving(current) || isbrain(current) || !get_turf(current) // NOTE: "isliving()" is not the same as STAT == CONSCIOUS. This is to make sure you're not a BORG (aka silicon)
/datum/antagonist/bloodsucker/proc/FinalDeath()
//Dont bother if we are already supposed to be dead
if(FinalDeath)
return
FinalDeath = TRUE //We are now supposed to die. Lets not spam it.
if(!iscarbon(owner.current)) //Check for non carbons.
owner.current.gib()
return
@@ -37,6 +37,8 @@
var/had_toxlover
var/level_bloodcost
var/passive_blood_drain = -0.1 //The amount of blood we loose each bloodsucker life() tick
var/notice_healing //Var to see if you are healing for preventing spam of the chat message inform the user of such
var/FinalDeath //Have we reached final death? Used to prevent spam.
// LISTS
var/static/list/defaultTraits = list (TRAIT_STABLEHEART, TRAIT_NOBREATH, TRAIT_SLEEPIMMUNE, TRAIT_NOCRITDAMAGE, TRAIT_RESISTCOLD, TRAIT_RADIMMUNE, TRAIT_NIGHT_VISION, \
TRAIT_NOSOFTCRIT, TRAIT_NOHARDCRIT, TRAIT_AGEUSIA, TRAIT_COLDBLOODED, TRAIT_NONATURALHEAL, TRAIT_NOMARROW, TRAIT_NOPULSE, TRAIT_VIRUSIMMUNE, TRAIT_NODECAP, TRAIT_NOGUT)
@@ -50,7 +52,6 @@
AssignStarterPowersAndStats()// Give Powers & Stats
forge_bloodsucker_objectives()// Objectives & Team
update_bloodsucker_icons_added(owner.current, "bloodsucker") // Add Antag HUD
LifeTick() // Run Life Function
. = ..()
@@ -683,6 +684,8 @@
owner.current.hud_used.sunlight_display.invisibility = INVISIBILITY_ABSTRACT
/datum/antagonist/bloodsucker/proc/update_hud(updateRank=FALSE)
if(FinalDeath)
return
// No Hud? Get out.
if(!owner.current.hud_used)
return
@@ -110,7 +110,7 @@
if (bloodsuckerdatum && bloodsuckerdatum.coffin == src)
bloodsuckerdatum.coffin = null
bloodsuckerdatum.lair = null
to_chat(resident, "<span class='danger'><span class='italics'>You sense that the link with your coffin, your sacred place of rest, has been brokem! You will need to seek another.</span></span>")
to_chat(resident, "<span class='danger'><span class='italics'>You sense that the link with your coffin, your sacred place of rest, has been broken! You will need to seek another.</span></span>")
resident = null // Remove resident. Because this object isnt removed from the game immediately (GC?) we need to give them a way to see they don't have a home anymore.
/obj/structure/closet/crate/coffin/can_open(mob/living/user)
@@ -217,7 +217,7 @@
return FALSE
return ..()
/obj/structure/bloodsucker/vassalrack/attack_hand(mob/user)
/obj/structure/bloodsucker/vassalrack/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
//. = ..() // Taken from sacrificial altar in divine.dm
//if(.)
// return
@@ -361,9 +361,9 @@
torture_time -= I.force / 4
torture_dmg_brute += I.force / 4
//torture_dmg_burn += I.
if(I.sharpness == IS_SHARP)
if(I.sharpness == SHARP_EDGED)
torture_time -= 1
else if(I.sharpness == IS_SHARP_ACCURATE)
else if(I.sharpness == SHARP_POINTY)
torture_time -= 2
if(istype(I, /obj/item/weldingtool))
var/obj/item/weldingtool/welder = I
@@ -469,7 +469,7 @@
. += {"<span class='cult'>This is a magical candle which drains at the sanity of the fools who havent yet accepted your master, as long as it is active.\n
You can turn it on and off by clicking on it while you are next to it</span>"} */
/obj/structure/bloodsucker/candelabrum/attack_hand(mob/user)
/obj/structure/bloodsucker/candelabrum/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
var/datum/antagonist/vassal/T = user.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
if(AmBloodsucker(user) || istype(T))
toggle()
@@ -226,7 +226,9 @@
playsound(get_turf(target), 'sound/effects/splat.ogg', 40, 1)
if(ishuman(target))
var/mob/living/carbon/human/H = target
H.bleed_rate += 5
var/obj/item/bodypart/head_part = H.get_bodypart(BODY_ZONE_HEAD)
if(head_part)
head_part.generic_bleedstacks += 5
target.add_splatter_floor(get_turf(target))
user.add_mob_blood(target) // Put target's blood on us. The donor goes in the ( )
target.add_mob_blood(target)
@@ -128,11 +128,9 @@
target.face_atom(L)
target.Stun(power_time)
to_chat(L, "<span class='notice'>[target] is fixed in place by your hypnotic gaze.</span>")
target.next_move = world.time + power_time // <--- Use direct change instead. We want an unmodified delay to their next move // target.changeNext_move(power_time) // check click.dm
target.mob_transforming = TRUE // <--- Fuck it. We tried using next_move, but they could STILL resist. We're just doing a hard freeze.
target.DelayNextAction(power_time)
spawn(power_time)
if(istype(target) && success)
target.mob_transforming = FALSE
if(istype(L) && target.stat == CONSCIOUS && (target in L.fov_view(10))) // They Woke Up! (Notice if within view)
to_chat(L, "<span class='warning'>[target] has snapped out of their trance.</span>")
@@ -27,8 +27,9 @@
C.blood_volume -= 0.2
C.adjustStaminaLoss(-15)
// Stop Bleeding
if(istype(H) && H.bleed_rate > 0 && rand(20) == 0)
H.bleed_rate --
if(istype(H) && H.is_bleeding() && rand(20) == 0)
for(var/obj/item/bodypart/part in H.bodyparts)
part.generic_bleedstacks --
C.Jitter(5)
sleep(10)
// DONE!
@@ -81,9 +81,7 @@
var/mist_delay = max(5, 20 - level_current * 2.5) // Level up and do this faster.
// Freeze Me
user.next_move = world.time + mist_delay
user.Stun(mist_delay, ignore_canstun = TRUE)
user.mob_transforming = TRUE
user.density = FALSE
var/invis_was = user.invisibility
user.invisibility = INVISIBILITY_MAXIMUM
@@ -96,7 +94,6 @@
// Move & Freeze
if(isturf(target_turf))
do_teleport(owner, target_turf, no_effects=TRUE, channel = TELEPORT_CHANNEL_QUANTUM) // in teleport.dm?
user.next_move = world.time + mist_delay / 2
user.Stun(mist_delay / 2, ignore_canstun = TRUE)
// Wait...
@@ -104,9 +101,7 @@
// Un-Hide & Freeze
user.dir = get_dir(my_turf, target_turf)
user.next_move = world.time + mist_delay / 2
user.Stun(mist_delay / 2, ignore_canstun = TRUE)
user.mob_transforming = FALSE
user.density = 1
user.invisibility = invis_was
@@ -13,10 +13,13 @@
changeling = null
. = ..()
/datum/cellular_emporium/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/datum/cellular_emporium/ui_state(mob/user)
return GLOB.always_state
/datum/cellular_emporium/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "cellular_emporium", name, 900, 480, master_ui, state)
ui = new(user, src, "CellularEmporium", name)
ui.open()
/datum/cellular_emporium/ui_data(mob/user)
@@ -20,6 +20,8 @@
var/datum/changelingprofile/first_prof = null
var/dna_max = 6 //How many extra DNA strands the changeling can store for transformation.
var/absorbedcount = 0
/// did we get succed by another changeling
var/hostile_absorbed = FALSE
var/trueabsorbs = 0//dna gained using absorb, not dna sting
var/chem_charges = 20
var/chem_storage = 75
@@ -92,7 +92,7 @@
var/datum/antagonist/changeling/target_ling = target.mind.has_antag_datum(/datum/antagonist/changeling)
if(target_ling)//If the target was a changeling, suck out their extra juice and objective points!
if(target_ling && !target_ling.hostile_absorbed)//If the target was a changeling, suck out their extra juice and objective points!
to_chat(user, "<span class='boldnotice'>[target] was one of us. We have absorbed their power.</span>")
target_ling.remove_changeling_powers()
changeling.geneticpoints += round(target_ling.geneticpoints/2)
@@ -102,6 +102,7 @@
changeling.chem_storage += round(target_ling.chem_storage/2)
changeling.chem_charges += min(target_ling.chem_charges, changeling.chem_storage)
target_ling.chem_charges = 0
target_ling.hostile_absorbed = TRUE
target_ling.chem_storage = 0
changeling.absorbedcount += (target_ling.absorbedcount)
target_ling.stored_profiles.len = 1
@@ -1,6 +1,6 @@
/obj/effect/proc_holder/changeling/fleshmend
name = "Fleshmend"
desc = "Our flesh rapidly regenerates, healing our burns, bruises, and shortness of breath. Functions while unconscious. This ability is loud, and might cause our blood to react violently to heat."
desc = "Our flesh rapidly regenerates, healing our burns, bruises, and shortness of breath, as well as hiding all of our scars. Costs 20 chemicals."
helptext = "If we are on fire, the healing effect will not function. Does not regrow limbs or restore lost blood."
chemical_cost = 20
loudness = 2
@@ -164,7 +164,9 @@
armour_penetration = 20
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
sharpness = IS_SHARP
sharpness = SHARP_EDGED
wound_bonus = -60
bare_wound_bonus = 20
var/can_drop = FALSE
var/fake = FALSE
total_mass = TOTAL_MASS_HAND_REPLACEMENT
@@ -29,6 +29,9 @@
C.emote("scream")
C.regenerate_limbs(1)
C.regenerate_organs()
for(var/i in C.all_wounds)
var/datum/wound/iter_wound = i
iter_wound.remove_wound()
if(!user.getorganslot(ORGAN_SLOT_BRAIN))
var/obj/item/organ/brain/B
if(C.has_dna() && C.dna.species.mutant_brain)
@@ -36,9 +36,10 @@
. = ..()
if(!.)
return
if(HAS_TRAIT(user, CHANGELING_DRAIN) || ((user.stat != DEAD) && !(HAS_TRAIT(user, TRAIT_DEATHCOMA))))
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(!changeling)
return FALSE
if(changeling.hostile_absorbed || ((user.stat != DEAD) && !(HAS_TRAIT(user, TRAIT_DEATHCOMA))))
changeling.purchasedpowers -= src
return FALSE
@@ -17,8 +17,7 @@
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/clothing/glasses/changeling/attack_hand(mob/user)
/obj/item/clothing/glasses/changeling/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling))
to_chat(user, "<span class='notice'>You reabsorb [src] into your body.</span>")
qdel(src)
@@ -33,8 +32,7 @@
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/clothing/under/changeling/attack_hand(mob/user)
/obj/item/clothing/under/changeling/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling))
to_chat(user, "<span class='notice'>You reabsorb [src] into your body.</span>")
qdel(src)
@@ -50,8 +48,7 @@
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/clothing/suit/changeling/attack_hand(mob/user)
/obj/item/clothing/suit/changeling/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling))
to_chat(user, "<span class='notice'>You reabsorb [src] into your body.</span>")
qdel(src)
@@ -65,8 +62,7 @@
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/clothing/head/changeling/attack_hand(mob/user)
/obj/item/clothing/head/changeling/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling))
to_chat(user, "<span class='notice'>You reabsorb [src] into your body.</span>")
qdel(src)
@@ -81,8 +77,7 @@
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/clothing/shoes/changeling/attack_hand(mob/user)
/obj/item/clothing/shoes/changeling/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling))
to_chat(user, "<span class='notice'>You reabsorb [src] into your body.</span>")
qdel(src)
@@ -97,8 +92,7 @@
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/clothing/gloves/changeling/attack_hand(mob/user)
/obj/item/clothing/gloves/changeling/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling))
to_chat(user, "<span class='notice'>You reabsorb [src] into your body.</span>")
qdel(src)
@@ -113,8 +107,7 @@
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/clothing/mask/changeling/attack_hand(mob/user)
/obj/item/clothing/mask/changeling/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling))
to_chat(user, "<span class='notice'>You reabsorb [src] into your body.</span>")
qdel(src)
@@ -131,8 +124,7 @@
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/changeling/attack_hand(mob/user)
/obj/item/changeling/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling))
to_chat(user, "<span class='notice'>You reabsorb [src] into your body.</span>")
qdel(src)
@@ -36,8 +36,7 @@
return
. = ..()
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/effect/clockwork/city_of_cogs_rift/attack_hand(atom/movable/AM)
/obj/effect/clockwork/city_of_cogs_rift/on_attack_hand(atom/movable/AM)
beckon(AM)
/obj/effect/clockwork/city_of_cogs_rift/Bumped(atom/movable/AM)
@@ -27,8 +27,7 @@
/obj/effect/clockwork/sigil/attack_tk(mob/user)
return //you can't tk stomp sigils, but you can hit them with something
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/effect/clockwork/sigil/attack_hand(mob/user)
/obj/effect/clockwork/sigil/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(iscarbon(user) && !user.stat)
if(is_servant_of_ratvar(user) && user.a_intent != INTENT_HARM)
return ..()
@@ -217,6 +216,20 @@
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
var/power_mod = 0.005
for(var/t in spiral_range_turfs(SIGIL_ACCESS_RANGE, src))
var/turf/T = t
for(var/M in T)
var/atom/movable/A = M
power_drained += A.power_drain(TRUE)
CHECK_TICK
adjust_clockwork_power(power_drained * power_mod * 15)
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
@@ -392,3 +405,49 @@
animation_number = initial(animation_number)
sigil_active = FALSE
animate(src, alpha = initial(alpha), time = 10, flags = ANIMATION_END_NOW)
/obj/effect/clockwork/sigil/rite
name = "radiant sigil"
desc = "A glowing sigil glowing with barely-contained power."
clockwork_desc = "A sigil that will allow you to perform certain rites on it, provided you have access to sufficient power and materials."
icon_state = "sigiltransmission" //am big lazy - recolored transmission sigil
sigil_name = "Sigil of Rites"
alpha = 255
var/performing_rite = FALSE
color = "#ffe63a"
light_color = "#ffe63a"
light_range = 1
light_power = 2
/obj/effect/clockwork/sigil/rite/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
if(!is_servant_of_ratvar(user))
return
if(!GLOB.all_clockwork_rites.len) //Did we already generate the list?
generate_all_rites()
if(performing_rite)
to_chat(user, "<span class='warning'>Someone is already performing a rite here!")
return
var/list/possible_rites = list()
for(var/datum/clockwork_rite/R in GLOB.all_clockwork_rites)
possible_rites[R] = R
var/input_key = input(user, "Choose a rite", "Choosing a rite") as null|anything in possible_rites
if(!input_key)
return
var/datum/clockwork_rite/CR = possible_rites[input_key]
if(!CR)
return
var/choice = alert(user, "What to do with this rite?", "What to do?", "Cast", "Show Info", "Cancel")
switch(choice)
if("Cast")
CR.try_cast(src, user)
if("Show Info")
var/infotext = CR.build_info()
to_chat(user, infotext)
/obj/effect/clockwork/sigil/rite/proc/generate_all_rites() //The first time someone uses a sigil of rites, all the rites are actually generated. No need to have a bunch of random datums laying around all the time.
for(var/V in subtypesof(/datum/clockwork_rite))
var/datum/clockwork_rite/R = new V
GLOB.all_clockwork_rites += R
@@ -14,6 +14,8 @@
var/uses = 1 //How many objects or mobs can go through the portal
var/obj/effect/clockwork/spatial_gateway/linked_gateway //The gateway linked to this one
var/timerid
var/is_stable = FALSE
var/busy = FALSE //If someone is already working on closing the gateway, only needed for stable gateways but in the parent to not need typecasting
/obj/effect/clockwork/spatial_gateway/Initialize()
. = ..()
@@ -31,11 +33,16 @@
clockwork_desc = "A gateway in reality. It can both send and receive objects."
else
clockwork_desc = "A gateway in reality. It can only [sender ? "send" : "receive"] objects."
timerid = QDEL_IN(src, lifetime)
if(is_stable)
return
timerid = QDEL_IN(src, lifetime) //We only need this if the gateway is not stable
//set up a gateway with another gateway
/obj/effect/clockwork/spatial_gateway/proc/setup_gateway(obj/effect/clockwork/spatial_gateway/gatewayB, set_duration, set_uses, two_way)
if(!gatewayB || !set_duration || !uses)
if(!gatewayB)
return FALSE
if((!set_duration || !uses) && !is_stable)
return FALSE
linked_gateway = gatewayB
gatewayB.linked_gateway = src
@@ -55,7 +62,7 @@
/obj/effect/clockwork/spatial_gateway/examine(mob/user)
. = ..()
if(is_servant_of_ratvar(user) || isobserver(user))
. += "<span class='brass'>It has [uses] use\s remaining.</span>"
. += "<span class='brass'> [is_stable ? "It is stabilised and can be used as much as is neccessary." : "It has [uses] use\s remaining."]</span>"
//ATTACK GHOST IGNORING PARENT RETURN VALUE
/obj/effect/clockwork/spatial_gateway/attack_ghost(mob/user)
@@ -63,8 +70,7 @@
user.forceMove(get_turf(linked_gateway))
..()
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/effect/clockwork/spatial_gateway/attack_hand(mob/living/user)
/obj/effect/clockwork/spatial_gateway/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
if(!uses)
return FALSE
if(user.pulling && user.a_intent == INTENT_GRAB && isliving(user.pulling))
@@ -122,9 +128,9 @@
/obj/effect/clockwork/spatial_gateway/Bumped(atom/movable/AM)
..()
if(!QDELETED(AM))
pass_through_gateway(AM, FALSE)
pass_through_gateway(AM)
/obj/effect/clockwork/spatial_gateway/proc/pass_through_gateway(atom/movable/A, no_cost)
/obj/effect/clockwork/spatial_gateway/proc/pass_through_gateway(atom/movable/A, no_cost = FALSE)
if(!linked_gateway)
qdel(src)
return FALSE
@@ -198,6 +204,10 @@
return procure_gateway(invoker, time_duration, gateway_uses, two_way)
var/istargetobelisk = istype(target, /obj/structure/destructible/clockwork/powered/clockwork_obelisk)
var/issrcobelisk = istype(src, /obj/structure/destructible/clockwork/powered/clockwork_obelisk)
if(!issrcobelisk && target.z != invoker.z && (is_reebe(invoker.z) || is_reebe(target.z)) && !GLOB.ratvar_awakens) //You need obilisks to get from and to reebe. Costs alot of power, unless you use stable gateways.
to_chat(invoker, "<span class='heavy brass'>The distance between reebe and the mortal realm is far too vast to bridge with a gateway your slab can create, my child. \
Use an obilisk instead!</span>")
return procure_gateway(invoker, time_duration, gateway_uses, two_way)
if(issrcobelisk)
if(!anchored)
to_chat(invoker, "<span class='warning'>[src] is no longer secured!</span>")
@@ -218,12 +228,63 @@
gateway_uses = round(gateway_uses * (2 * efficiency), 1)
time_duration = round(time_duration * (2 * efficiency), 1)
CO.active = TRUE //you'd be active in a second but you should update immediately
invoker.visible_message("<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 [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))
if(issrcobelisk && istargetobelisk && src.z != target.z && (is_reebe(src.z) || is_reebe(target.z)))
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 stable two-way rift between reebe and the mortal realm.</span>")
var/obj/effect/clockwork/spatial_gateway/stable/stable_S1 = new(get_turf(src))
var/obj/effect/clockwork/spatial_gateway/stable/stable_S2 = new(get_turf(target))
stable_S1.setup_gateway(stable_S2)
stable_S2.visible_message("<span class='warning'>The air in front of [target] ripples before suddenly tearing open!</span>")
else
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 [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))
//Set up the portals now that they've spawned
S1.setup_gateway(S2, time_duration, gateway_uses, two_way)
S2.visible_message("<span class='warning'>The air in front of [target] ripples before suddenly tearing open!</span>")
//Set up the portals now that they've spawned
S1.setup_gateway(S2, time_duration, gateway_uses, two_way)
S2.visible_message("<span class='warning'>The air in front of [target] ripples before suddenly tearing open!</span>")
return TRUE
//Stable Gateway: Used to travel to and from reebe without any further powercost. Needs a clockwork obilisk to keep active, but stays active as long as it is not deactivated via an null rod or a slab, or the obilisk is destroyed
/obj/effect/clockwork/spatial_gateway/stable
name = "stable gateway"
is_stable = TRUE
/obj/effect/clockwork/spatial_gateway/stable/ex_act(severity)
if(severity == 1)
start_shutdown() //Yes, you can chain devastation-level explosions to delay a gateway shutdown, if you somehow manage to do it without breaking the obelisk. Is it worth it? Probably not.
return TRUE
return FALSE
/obj/effect/clockwork/spatial_gateway/stable/setup_gateway(obj/effect/clockwork/spatial_gateway/stable/gatewayB) //Reduced setup call due to some things being irrelevant for stable gateways
return ..(gatewayB, 1, 1, TRUE) //Uses and time irrelevant due to is_stable
/obj/effect/clockwork/spatial_gateway/stable/attackby(obj/item/I, mob/living/user, params)
if(!istype(I, /obj/item/clockwork/slab) || !is_servant_of_ratvar(user) || busy)
return ..()
busy = TRUE
linked_gateway.busy = TRUE
user.visible_message("<span class='warning'>The rift begins to ripple as [user] points [user.p_their()] slab at it!</span>", "<span class='brass'> You begin to shutdown the stabilised gateway with your slab.</span>")
linked_gateway.visible_message("<span class='warning'[linked_gateway] begins to ripple, but nothing comes through...</span>")
var/datum/beam/B = user.Beam(src, icon_state = "nzcrentrs_power", maxdistance = 50, time = 80) //Not too fancy, but this'll do.. for now.
if(do_after(user, 80, target = src)) //Eight seconds to initiate the closing, then another two before is closes.
to_chat(user, "<span class='brass'>You successfully set the gateway to shutdown in another two seconds.</span>")
start_shutdown()
qdel(B)
busy = FALSE
linked_gateway.busy = FALSE
return TRUE
/obj/effect/clockwork/spatial_gateway/stable/proc/start_shutdown()
deltimer(timerid)
deltimer(linked_gateway.timerid)
timerid = QDEL_IN(src, 20)
linked_gateway.timerid = QDEL_IN(linked_gateway, 20)
animate(src, alpha = 0, transform = matrix()*2, time = 20, flags = ANIMATION_END_NOW)
animate(linked_gateway, alpha = 0, transform = matrix()*2, time = 20, flags = ANIMATION_END_NOW)
src.visible_message("<span class='warning'>[src] begins to destabilise!</span>")
linked_gateway.visible_message("<span class='warning'>[linked_gateway] begins to destabilise!</span>")
/obj/effect/clockwork/spatial_gateway/stable/pass_through_gateway(atom/movable/A, no_cost = TRUE)
return ..()
@@ -0,0 +1,196 @@
//This file is for clock rites, mainly used by the Sigil of Rites in clock_sigils.dm
//The rites themselves are in this file to prevent bloating the other file too much, aswell as for easier access
#define INFINITE -1
//The base clockwork rite. This should never be visible
/datum/clockwork_rite
var/name = "Rite of THE frog" //The name of the rite
var/desc = "This rite is used to summon the legendary frog whose-name-shall-not-be-spoken, ender of many worlds." //What does this rite do? Shown to cultists if they choose 'Show Info' after selecting the rite.
var/list/required_ingredients = list(/obj/item/clockwork) //What does this rite require?
var/power_cost = 0 //How much power does this rite cost.. or does it even add power?
var/requires_human = FALSE //Does the rite require a ../carbon/human on the rune?
var/must_be_servant = TRUE //If the above is true, does the human need to be a servant?
var/target_can_be_invoker = TRUE //Does this rite work if the invoker is also the target?
var/cast_time = 0 //How long does the rite take to cast?
var/limit = INFINITE //How often can this rite be used per round? Set this to INFINITE for unlimited, 0 for disallowed, anything above 0 for a limit
var/times_used = 0 //How often has the rite already been used this shift?
var/rite_cast_sound = 'sound/items/bikehorn.ogg' //The sound played when successfully casting the rite. If it honks, the one adding the rite forgot to set one (or was just lazy).
/datum/clockwork_rite/proc/try_cast(var/obj/effect/clockwork/sigil/rite/R, var/mob/living/invoker) //Performs a ton of checks to see if the invoker can cast the rite
if(!istype(R))
return FALSE
if(!R || !R.loc)
return FALSE
var/turf/T = R.loc
if(!T) //Uh oh something is fucky
return FALSE
if(limit != INFINITE && times_used >= limit) //Is the limit on casts exceeded?
to_chat(invoker, "<span class='brass'>There are no more uses left for this rite!</span>")
return FALSE
var/mob/living/carbon/human/H //This is only used if requires_human is TRUE
if(requires_human) //In case this requires a target
for(var/mob/living/carbon/human/possible_H in T)
if((!must_be_servant || is_servant_of_ratvar(possible_H)) && (target_can_be_invoker || invoker != possible_H))
H = possible_H
break
if(!H)
to_chat(invoker, "<span class='brass'>There is no target for the rite on the sigil!</span>")
return FALSE
if(required_ingredients.len) //In case this requires materials
var/is_missing_materials = FALSE
for(var/I in required_ingredients)
var/obj/item/Material = locate(I) in T
if(!Material)
is_missing_materials = TRUE
break
if(is_missing_materials)
var/still_required_string = ""
for(var/i = 1 to required_ingredients.len)
var/obj/O = required_ingredients[i]
if(i != 1)
still_required_string += ", "
still_required_string += "a [initial(O.name)]"
to_chat(invoker, "<span class='brass'>There are still materials missing for this rite. You require [still_required_string].</span>")
return FALSE
if(power_cost) //If this costs power
if(!get_clockwork_power(power_cost))
to_chat(invoker, "<span class='brass'>There is not enough power for this rite!</span>")
return FALSE
R.performing_rite = TRUE
if(!do_after(invoker, cast_time, target = R))
to_chat(invoker, "<span class='warning'>Your rite is disrupted.</span>")
R.performing_rite = FALSE
return FALSE
. = cast(invoker, T, H)
if(!.)
to_chat(invoker, "<span class='warning'> You fail casting [name]</span>")
post_cast(FALSE)
else
to_chat(invoker, "<span class='warning'>You successfully cast [name]</span>")
post_cast(TRUE)
R.performing_rite = FALSE
return
/datum/clockwork_rite/proc/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target) //Casts the rite and uses up ingredients. Doublechecks some things to prevent bypassing some restrictions via funky timing or badminnery.
if(!T || !invoker)
return FALSE
if(requires_human && !target)
return FALSE
if(power_cost && !get_clockwork_power(power_cost))
return FALSE
adjust_clockwork_power(-power_cost)
if(limit != INFINITE && times_used >= limit)
return FALSE
if(required_ingredients.len)
var/is_missing_materials = FALSE
for(var/I in required_ingredients)
var/obj/item/Material = locate(I) in T
if(!Material)
is_missing_materials = TRUE
break
qdel(Material)
if(is_missing_materials)
return FALSE
playsound(T, rite_cast_sound, 50, 2)
return TRUE
/datum/clockwork_rite/proc/post_cast(var/cast_succeeded)
if(cast_succeeded)
times_used++
return TRUE
/datum/clockwork_rite/proc/build_info() //Constructs the info text of a given rite, based on the vars of the rite
. = ""
. += "<span class='brass'>This is the <b>[name]</b>.\n"
. += "[desc]\n"
. += "It requires: "
if(required_ingredients.len)
var/material_string = ""
for(var/i = 1 to required_ingredients.len)
var/obj/O = required_ingredients[i]
if(i != 1)
material_string += ", "
material_string += "a [initial(O.name)]"
. += "[material_string].\n"
else
. += "</span><span class='inathneq_small'><b>no</b><span class='brass'> materials.\n"
. += "It [power_cost >= 0 ? "costs" : "generates"]<span class='inathneq_small'><b> [power_cost ? "[power_cost]" : "no"] </b><span class='brass'>power.\n"
. += "It requires <span class='inathneq_small'><b>[requires_human ? " a human" : " no"]</b><span class='brass'> target.\n"
if(requires_human)
. += "The target <span class='inathneq_small'><b>[must_be_servant ? "cannot be" : "can be"] </b><span class='brass'> a nonservant.\n"
. += "The target <span class='inathneq_small'><b>[target_can_be_invoker ? "can be" : "cannot be"]</b><span class='brass'> the invoker.\n"
. += "It requires <span class='inathneq_small'><b>[cast_time/10]</b><span class='brass'> seconds to cast.\n"
. += "It has been used <span class='inathneq_small'><b>[times_used]</b><span class='brass'> time[times_used != 1 ? "s" : ""], out of <span class='inathneq_small'><b>[limit != INFINITE ? "[limit]" : "infinite"]</b><span class='brass'> available uses.</span>"
//Adds a organ or cybernetic implant to a servant without the need for surgery. Cannot be used with brains for.. reasons.
/datum/clockwork_rite/advancement
name = "Rite of Advancement"
desc = "This rite is used to augment a servant with organs or cybernetic implants. The organ of choice, aswell as the servant and the required ingredients must be placed on the sigil for this rite to take place."
required_ingredients = list(/obj/item/assembly/prox_sensor, /obj/item/stock_parts/cell)
power_cost = 500
requires_human = TRUE
cast_time = 40
rite_cast_sound = 'sound/magic/blind.ogg'
/datum/clockwork_rite/advancement/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target)
var/obj/item/organ/O = locate(/obj/item/organ) in T
if(!O)
return FALSE
if(istype(O, /obj/item/organ/brain)) //NOPE
return FALSE
. = ..()
if(!.)
return FALSE
O.Insert(target)
new /obj/effect/temp_visual/ratvar/sigil/transgression(T)
//Heals all wounds (not damage) on the target, causing toxloss proportional to amount of wounds healed. 10 damage per wound.
/datum/clockwork_rite/treat_wounds
name = "Rite of Woundmending"
desc = "This rite is used to heal wounds of the servant on the rune. It causes toxins damage proportional to the amount of wounds healed. This can be lethal if performed on an critically injured target."
required_ingredients = list(/obj/item/stock_parts/cell, /obj/item/healthanalyzer, /obj/item/reagent_containers/food/drinks/bottle/holyoil)
power_cost = 300
requires_human = TRUE
must_be_servant = FALSE
target_can_be_invoker = FALSE
cast_time = 80
rite_cast_sound = 'sound/magic/staff_healing.ogg'
/datum/clockwork_rite/treat_wounds/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target)
if(!target)
return FALSE
if(!target.all_wounds.len)
to_chat(invoker, "<span class='inathneq_small'>This one does not require mending.</span>")
return FALSE
.= ..()
if(!.)
return FALSE
target.adjustToxLoss(10 * target.all_wounds.len)
QDEL_LIST(target.all_wounds)
to_chat(target, "<span class='warning'>You feel your wounds heal, but are overcome with deep nausea.</span>")
new /obj/effect/temp_visual/ratvar/sigil/vitality(T)
//Summons a brass claw implant on the sigil, which can extend a claw that benefits from repeatedly attacking a single target. Can only be cast a limited amount of times.
/datum/clockwork_rite/summon_claw
name = "Rite of the Claw"
desc = "Summons a special arm implant that, when added to a servant's limb, will allow them to extend and retract a claw at will. Don't leave any implants you want to keep on this rune when casting the rite."
required_ingredients = list(/obj/item/stock_parts/cell, /obj/item/organ/cyberimp, /obj/item/assembly/flash)
power_cost = 1000
cast_time = 60
limit = 4
rite_cast_sound = 'sound/magic/clockwork/fellowship_armory.ogg'
/datum/clockwork_rite/summon_claw/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target)
. = ..()
if(!.)
return FALSE
var/obj/item/organ/cyberimp/arm/clockwork/claw/CL = new /obj/item/organ/cyberimp/arm/clockwork/claw(T)
CL.visible_message("<span class='warning'>[CL] materialises out of thin air!")
new /obj/effect/temp_visual/ratvar/sigil/transmission(T,2)
#undef INFINITE
@@ -38,10 +38,11 @@
set_slab.update_quickbind()
/proc/generate_all_scripture()
if(!GLOB.all_scripture.len)
for(var/V in sortList(subtypesof(/datum/clockwork_scripture), /proc/cmp_clockscripture_priority))
var/datum/clockwork_scripture/S = new V
GLOB.all_scripture[S.type] = S
if(GLOB.all_scripture.len)
return
for(var/V in sortList(subtypesof(/datum/clockwork_scripture) - list(/datum/clockwork_scripture/channeled, /datum/clockwork_scripture/create_object, /datum/clockwork_scripture/create_object/construct), /proc/cmp_clockscripture_priority))
var/datum/clockwork_scripture/S = new V
GLOB.all_scripture[S.type] = S
//changes construction value
/proc/change_construction_value(amount)
@@ -0,0 +1,32 @@
//This file is for snowflakey clock augmentations and clock-themed cybernetic implants.
//The base clockie arm implant, which only clock cultist can use unless it is emagged. THIS SHOULD NEVER ACTUALLY EXIST
/obj/item/organ/cyberimp/arm/clockwork
name = "clock-themed arm-mounted implant"
var/clockwork_desc = "According to Ratvar, this really shouldn't exist. Tell Him about this immediately."
syndicate_implant = TRUE
icon_state = "clock_arm_implant"
/obj/item/organ/cyberimp/arm/clockwork/ui_action_click()
if(is_servant_of_ratvar(owner) || (obj_flags & EMAGGED)) //If you somehow manage to steal a clockie's implant AND have an emag AND manage to get it implanted for yourself, good on ya!
return ..()
to_chat(owner, "<span class='warning'>The implant refuses to activate..</span>")
/obj/item/organ/cyberimp/arm/clockwork/examine(mob/user)
if((is_servant_of_ratvar(user) || isobserver(user)) && clockwork_desc)
desc = clockwork_desc
. = ..()
desc = initial(desc)
/obj/item/organ/cyberimp/arm/clockwork/emag_act()
if(obj_flags & EMAGGED)
return
obj_flags |= EMAGGED
to_chat(usr, "<span class='notice'>You emag [src], hoping it'll achieve something..</span>")
//Brass claw implant. Holds the brass claw from brass_claw.dm and can extend / retract it at will.
/obj/item/organ/cyberimp/arm/clockwork/claw
name = "brass claw implant"
desc = "Yikes, the claw attached to this looks pretty darn sharp."
clockwork_desc = "This implant, when added to a servant's arm, allows them to extend and retract a claw at will, though this is mildly painful to do. It will refuse to work for any non-servants."
contents = newlist(/obj/item/clockwork/brass_claw)
@@ -29,7 +29,7 @@
owner.visible_message("<span class='danger'>[owner]'s [weapon.name] flickers and disappears!</span>")
to_chat(owner, "<span class='brass'>You dismiss [weapon].</span>")
QDEL_NULL(weapon)
weapon_reset(RATVARIAN_SPEAR_COOLDOWN * 0.5)
weapon_reset(RATVARIAN_WEAPON_COOLDOWN * 0.5)
return
else
weapon.visible_message("<span class='warning'>[weapon] suddenly flickers and disappears!</span>")
@@ -0,0 +1,51 @@
//Brass claw, an armblade-like weapon used by a clock implant. Stealthy if retracted, very obvious if active.
//Bit weaker than an armblade strength-wise but gains combo on consecutive attacks against the same target, which causes bonus damage
/obj/item/clockwork/brass_claw
name = "brass claw"
desc = "A very sharp claw made out of brass."
clockwork_desc = "A incredibly sharp claw made out of brass. It is quite effective at crippling enemies, though very obvious when extended.\nGains combo on consecutive attacks against a target, causing bonus damage."
icon_state = "brass_claw" //Codersprite moment
item_state = "brass_claw"
lefthand_file = 'icons/mob/inhands/antag/clockwork_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/clockwork_righthand.dmi'
w_class = WEIGHT_CLASS_HUGE
force = 15 //Doesn't generate vitality like the spear does / has somewhat less damage, but quite good at wounding and gets through armor pretty well. Also gains 2 bonus damage per consecutive attack on the same target
throwforce = 0 //haha yes lets be safe about this
throw_range = 0
throw_speed = 0
armour_penetration = 20
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
sharpness = SHARP_EDGED
wound_bonus = 5
bare_wound_bonus = 15
total_mass = TOTAL_MASS_HAND_REPLACEMENT
var/mob/living/last_attacked
var/combo = 0
var/damage_per_combo = 2
var/maximum_combo_damage = 18 //33 damage on max stacks. Usually the target will already be dead by then but if they somehow aren't, better to have this capped
/obj/item/clockwork/brass_claw/Initialize()
. = ..()
AddComponent(/datum/component/butchering, 60, 80)
/obj/item/clockwork/brass_claw/examine(mob/user)
if(is_servant_of_ratvar(user))
clockwork_desc += "\n<span class='brass'>It has </span><span class='inathneq_small'><b>[combo]</span></b><span class='brass'> combo stacks built up against the current target, causing </span><span class='inathneq_small'><b>[min(maximum_combo_damage, combo * damage_per_combo)]</span></b><span class='brass'> bonus damage.</span>"
. = ..()
clockwork_desc = initial(clockwork_desc)
/obj/item/clockwork/brass_claw/attack(mob/living/target, mob/living/carbon/human/user)
. = ..()
if(QDELETED(target) || target.anti_magic_check(chargecost = 0) || is_servant_of_ratvar(target))
return
if(target != last_attacked) //Loses all combat on switching targets
last_attacked = target
combo = 0
else
if(!iscultist(target)) //Hostile cultists being hit stacks up combo far faster than usual
combo++
else
combo += 3
target.adjustBruteLoss(min(maximum_combo_damage, combo * damage_per_combo))
@@ -8,10 +8,12 @@
force = 15 //Extra damage is dealt to targets in attack()
throwforce = 25
armour_penetration = 10
sharpness = IS_SHARP_ACCURATE
sharpness = SHARP_POINTY
attack_verb = list("stabbed", "poked", "slashed")
hitsound = 'sound/weapons/bladeslice.ogg'
w_class = WEIGHT_CLASS_BULKY
block_parry_data = /datum/block_parry_data/ratvarian_spear
item_flags = ITEM_CAN_PARRY
var/bonus_burn = 5
/obj/item/clockwork/weapon/ratvarian_spear/ratvar_act()
@@ -43,7 +45,7 @@
else if(iscultist(target) || isconstruct(target))
to_chat(target, "<span class='userdanger'>Your body flares with agony at [src]'s presence!</span>")
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
GLOB.clockwork_vitality += max(0, target.adjustFireLoss(bonus_damage)) //adds the damage done to existing vitality
/obj/item/clockwork/weapon/ratvarian_spear/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
var/turf/T = get_turf(hit_atom)
@@ -78,5 +80,17 @@
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)
action.weapon_reset(RATVARIAN_SPEAR_COOLDOWN)
action.weapon_reset(RATVARIAN_WEAPON_COOLDOWN)
//A very short, very effective parry that counts on you predicting when the enemy will attack.
/datum/block_parry_data/ratvarian_spear
parry_time_windup = 0 //Very good for predicting
parry_time_active = 3 //Very short
parry_time_spindown = 1
parry_time_perfect = 2
parry_efficiency_perfect = 110 //Very low leeway for counterattacks...
parry_efficiency_considered_successful = 0.8
parry_efficiency_to_counterattack = 1
parry_cooldown = 15 //But also very low cooldown..
parry_failed_stagger_duration = 2 SECONDS //And relatively small penalties for failing.
parry_failed_clickcd_duration = 1 SECONDS
@@ -8,7 +8,7 @@
resistance_flags = FIRE_PROOF | ACID_PROOF
flags_inv = HIDEEARS|HIDEHAIR|HIDEFACE|HIDESNOUT
mutantrace_variation = STYLE_MUZZLE
armor = list("melee" = 50, "bullet" = 70, "laser" = -25, "energy" = 0, "bomb" = 60, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
armor = list("melee" = 50, "bullet" = 70, "laser" = 0, "energy" = 0, "bomb" = 60, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100, "magic" = 60, "wound" = 65)
/obj/item/clothing/head/helmet/clockwork/Initialize()
. = ..()
@@ -21,17 +21,17 @@
/obj/item/clothing/head/helmet/clockwork/ratvar_act()
if(GLOB.ratvar_awakens)
armor = getArmor(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100)
armor = getArmor(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100, magic = 100, wound = 100)
clothing_flags |= STOPSPRESSUREDAMAGE
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
else if(GLOB.ratvar_approaches)
armor = getArmor(melee = 70, bullet = 80, laser = -15, energy = 25, bomb = 70, bio = 0, rad = 0, fire = 100, acid = 100)
armor = getArmor(melee = 70, bullet = 80, laser = 10, energy = 25, bomb = 70, bio = 0, rad = 0, fire = 100, acid = 100,, magic = 70, wound = 75)
clothing_flags |= STOPSPRESSUREDAMAGE
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
else
armor = getArmor(melee = 60, bullet = 70, laser = -25, energy = 0, bomb = 60, bio = 0, rad = 0, fire = 100, acid = 100)
armor = getArmor(melee = 60, bullet = 70, laser = 0, energy = 0, bomb = 60, bio = 0, rad = 0, fire = 100, acid = 100, magic = 60, wound = 65)
clothing_flags &= ~STOPSPRESSUREDAMAGE
max_heat_protection_temperature = initial(max_heat_protection_temperature)
min_cold_protection_temperature = initial(min_cold_protection_temperature)
@@ -68,7 +68,7 @@
cold_protection = CHEST|GROIN|LEGS
heat_protection = CHEST|GROIN|LEGS
resistance_flags = FIRE_PROOF | ACID_PROOF
armor = list("melee" = 60, "bullet" = 70, "laser" = -25, "energy" = 0, "bomb" = 60, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
armor = list("melee" = 60, "bullet" = 70, "laser" = 0, "energy" = 0, "bomb" = 60, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100, "magic" = 60, "wound" = 65)
allowed = list(/obj/item/clockwork, /obj/item/clothing/glasses/wraith_spectacles, /obj/item/clothing/glasses/judicial_visor, /obj/item/mmi/posibrain/soul_vessel, /obj/item/reagent_containers/food/drinks/bottle/holyoil)
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_SNEK_TAURIC
@@ -83,17 +83,17 @@
/obj/item/clothing/suit/armor/clockwork/ratvar_act()
if(GLOB.ratvar_awakens)
armor = getArmor(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100)
armor = getArmor(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100, magic = 100, wound = 100)
clothing_flags |= STOPSPRESSUREDAMAGE
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
else if(GLOB.ratvar_approaches)
armor = getArmor(melee = 70, bullet = 80, laser = -15, energy = 25, bomb = 70, bio = 0, rad = 0, fire = 100, acid = 100)
armor = getArmor(melee = 70, bullet = 80, laser = 10, energy = 25, bomb = 70, bio = 0, rad = 0, fire = 100, acid = 100, magic = 70, wound = 75)
clothing_flags |= STOPSPRESSUREDAMAGE
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
else
armor = getArmor(melee = 60, bullet = 70, laser = -25, energy = 0, bomb = 60, bio = 0, rad = 0, fire = 100, acid = 100)
armor = getArmor(melee = 60, bullet = 70, laser = 0, energy = 0, bomb = 60, bio = 0, rad = 0, fire = 100, acid = 100, magic = 60, wound = 65)
clothing_flags &= ~STOPSPRESSUREDAMAGE
max_heat_protection_temperature = initial(max_heat_protection_temperature)
min_cold_protection_temperature = initial(min_cold_protection_temperature)
@@ -135,7 +135,7 @@
siemens_coefficient = 0
permeability_coefficient = 0.05
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" = 80, "bullet" = 70, "laser" = 0, "energy" = 0, "bomb" = 60, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100, "magic" = 70, "wound" = 85)
/obj/item/clothing/gloves/clockwork/Initialize()
. = ..()
@@ -153,7 +153,7 @@
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
else
armor = getArmor(melee = 80, bullet = 70, laser = -25, energy = 0, bomb = 60, bio = 0, rad = 0, fire = 100, acid = 100)
armor = getArmor(melee = 80, bullet = 70, laser = 0, energy = 0, bomb = 60, bio = 0, rad = 0, fire = 100, acid = 100, magic = 70, wound = 85)
clothing_flags &= ~STOPSPRESSUREDAMAGE
max_heat_protection_temperature = initial(max_heat_protection_temperature)
min_cold_protection_temperature = initial(min_cold_protection_temperature)
@@ -1,10 +1,10 @@
/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 contains information, recites scripture, and is your most vital tool as a Servant.<br>\
clockwork_desc = "A link between you and the Celestial Derelict. It contains information, recites scripture, and is your most vital tool as a Servant.\
It can be used to link traps and triggers by attacking them with the slab. Keep in mind that traps linked with one another will activate in tandem!"
icon_state = "dread_ipad"
icon_state = "clockwork_slab"
lefthand_file = 'icons/mob/inhands/antag/clockwork_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/clockwork_righthand.dmi'
var/inhand_overlay //If applicable, this overlay will be applied to the slab's inhand
@@ -15,13 +15,13 @@
var/busy //If the slab is currently being used by something
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/selected_scripture = SCRIPTURE_DRIVER
// var/selected_scripture = SCRIPTURE_DRIVER //handled UI side
var/obj/effect/proc_holder/slab/slab_ability //the slab's current bound ability, for certain scripture
var/recollecting = FALSE //if we're looking at fancy recollection
var/recollecting = TRUE //if we're looking at fancy recollection. tutorial enabled by default
var/recollection_category = "Default"
var/list/quickbound = list(/datum/clockwork_scripture/abscond, \
var/list/quickbound = list(/datum/clockwork_scripture/spatial_gateway, \
/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
@@ -36,6 +36,11 @@
speed_multiplier = 0
no_cost = TRUE
/obj/item/clockwork/slab/debug/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
if(!is_servant_of_ratvar(user))
add_servant_of_ratvar(user)
return ..()
/obj/item/clockwork/slab/traitor
var/spent = FALSE
@@ -54,12 +59,6 @@
to_chat(user, "<span class='userdanger'>[src] falls dark. It appears you weren't worthy.</span>")
return ..()
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/clockwork/slab/debug/attack_hand(mob/living/user)
if(!is_servant_of_ratvar(user))
add_servant_of_ratvar(user)
return ..()
/obj/item/clockwork/slab/cyborg //three scriptures, plus a spear and fabricator
clockwork_desc = "A divine link to the Celestial Derelict, allowing for limited recital of scripture."
quickbound = list(/datum/clockwork_scripture/ranged_ability/judicial_marker, /datum/clockwork_scripture/ranged_ability/linked_vanguard, \
@@ -67,29 +66,32 @@
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 //three scriptures, plus a fabricator
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/create_object/replicant, /datum/clockwork_scripture/create_object/sigil_of_transmission, /datum/clockwork_scripture/create_object/stargazer)
/obj/item/clockwork/slab/cyborg/engineer //six scriptures, plus a fabricator. Might revert this if its too OP, I just thought that engineering borgs should get the all the structures
quickbound = list(/datum/clockwork_scripture/spatial_gateway, /datum/clockwork_scripture/create_object/replicant, /datum/clockwork_scripture/create_object/sigil_of_transmission, /datum/clockwork_scripture/create_object/stargazer, \
/datum/clockwork_scripture/create_object/ocular_warden, /datum/clockwork_scripture/create_object/clockwork_obelisk, /datum/clockwork_scripture/create_object/mania_motor)
/obj/item/clockwork/slab/cyborg/medical //five scriptures, plus a spear
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)
/obj/item/clockwork/slab/cyborg/medical //six scriptures, plus a spear
quickbound = list(/datum/clockwork_scripture/spatial_gateway, /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/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/abscond, /datum/clockwork_scripture/ranged_ability/hateful_manacles, /datum/clockwork_scripture/ranged_ability/judicial_marker)
/obj/item/clockwork/slab/cyborg/security //four scriptures, plus a spear
quickbound = list(/datum/clockwork_scripture/spatial_gateway, /datum/clockwork_scripture/channeled/volt_blaster, /datum/clockwork_scripture/ranged_ability/hateful_manacles, \
/datum/clockwork_scripture/ranged_ability/judicial_marker, /datum/clockwork_scripture/channeled/belligerent)
/obj/item/clockwork/slab/cyborg/peacekeeper //four scriptures, plus a spear
quickbound = list(/datum/clockwork_scripture/spatial_gateway, /datum/clockwork_scripture/channeled/volt_blaster, /datum/clockwork_scripture/ranged_ability/hateful_manacles, \
/datum/clockwork_scripture/ranged_ability/judicial_marker, /datum/clockwork_scripture/channeled/belligerent)
/*//this module was commented out so why wasn't this?
/obj/item/clockwork/slab/cyborg/janitor //six scriptures, plus a fabricator
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/create_object/replicant, /datum/clockwork_scripture/create_object/sigil_of_transgression, \
quickbound = list(/datum/clockwork_scripture/spatial_gateway, /datum/clockwork_scripture/create_object/replicant, /datum/clockwork_scripture/create_object/sigil_of_transgression, \
/datum/clockwork_scripture/create_object/stargazer, /datum/clockwork_scripture/create_object/ocular_warden, /datum/clockwork_scripture/create_object/mania_motor)
*/
/obj/item/clockwork/slab/cyborg/service //six scriptures, plus xray vision
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/create_object/replicant,/datum/clockwork_scripture/create_object/stargazer, \
quickbound = list(/datum/clockwork_scripture/spatial_gateway, /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/abscond, /datum/clockwork_scripture/ranged_ability/linked_vanguard, /datum/clockwork_scripture/spatial_gateway)
/obj/item/clockwork/slab/cyborg/miner //three scriptures, plus a spear and xray vision
quickbound = list(/datum/clockwork_scripture/spatial_gateway, /datum/clockwork_scripture/ranged_ability/linked_vanguard, /datum/clockwork_scripture/channeled/belligerent, /datum/clockwork_scripture/channeled/volt_blaster)
/obj/item/clockwork/slab/cyborg/access_display(mob/living/user)
if(!GLOB.ratvar_awakens)
@@ -140,14 +142,15 @@
/obj/item/clockwork/slab/examine(mob/user)
. = ..()
if(is_servant_of_ratvar(user) || isobserver(user))
if(LAZYLEN(quickbound))
for(var/i in 1 to quickbound.len)
if(!quickbound[i])
continue
var/datum/clockwork_scripture/quickbind_slot = quickbound[i]
. += "<b>Quickbind</b> button: <span class='[get_component_span(initial(quickbind_slot.primary_component))]'>[initial(quickbind_slot.name)]</span>."
. += "<b>Available power:</b> <span class='bold brass'>[DisplayPower(get_clockwork_power())].</span>"
if(!is_servant_of_ratvar(user) || !isobserver(user))
return
if(LAZYLEN(quickbound))
for(var/i in 1 to quickbound.len)
if(!quickbound[i])
continue
var/datum/clockwork_scripture/quickbind_slot = quickbound[i]
. += "Quickbind button: <span class='[get_component_span(initial(quickbind_slot.primary_component))]'>[initial(quickbind_slot.name)]</span>."
. += "Available power: <span class='bold brass'>[DisplayPower(get_clockwork_power())].</span>"
//Slab actions; Hierophant, Quickbind
/obj/item/clockwork/slab/ui_action_click(mob/user, action)
@@ -165,18 +168,19 @@
user.emote("scream")
user.apply_damage(5, BURN, BODY_ZONE_L_ARM)
user.apply_damage(5, BURN, BODY_ZONE_R_ARM)
return 0
return FALSE
if(!is_servant_of_ratvar(user))
to_chat(user, "<span class='warning'>The information on [src]'s display shifts rapidly. After a moment, your head begins to pound, and you tear your eyes away.</span>")
user.confused += 5
user.dizziness += 5
return 0
if(user.confused || user.dizziness)
user.confused += 5
user.dizziness += 5
return FALSE
if(busy)
to_chat(user, "<span class='warning'>[src] refuses to work, displaying the message: \"[busy]!\"</span>")
return 0
return FALSE
if(!no_cost && !can_recite_scripture(user))
to_chat(user, "<span class='nezbere'>[src] hums fitfully in your hands, but doesn't seem to do anything...</span>")
return 0
return FALSE
access_display(user)
/obj/item/clockwork/slab/AltClick(mob/living/user)
@@ -192,14 +196,6 @@
ui_interact(user)
return TRUE
/obj/item/clockwork/slab/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "clockwork_slab", name, 800, 420, master_ui, state)
ui.set_autoupdate(FALSE) //we'll update this occasionally, but not as often as possible
ui.set_style("clockwork")
ui.open()
/obj/item/clockwork/slab/proc/recite_scripture(datum/clockwork_scripture/scripture, mob/living/user)
if(!scripture || !user || !user.canUseTopic(src) || (!no_cost && !can_recite_scripture(user)))
return FALSE
@@ -207,294 +203,156 @@
to_chat(user, "<span class='warning'>You need to hold the slab in your active hand to recite scripture!</span>")
return FALSE
var/initial_tier = initial(scripture.tier)
if(initial_tier != SCRIPTURE_PERIPHERAL)
if(!GLOB.ratvar_awakens && !no_cost && !SSticker.scripture_states[initial_tier])
to_chat(user, "<span class='warning'>That scripture is not unlocked, and cannot be recited!</span>")
return FALSE
if(initial_tier == SCRIPTURE_PERIPHERAL)
to_chat(user, "<span class='warning'>Nice try using href exploits</span>")
return
if(!GLOB.ratvar_awakens && !no_cost && !SSticker.scripture_states[initial_tier])
to_chat(user, "<span class='warning'>That scripture is not unlocked, and cannot be recited!</span>")
return FALSE
var/datum/clockwork_scripture/scripture_to_recite = new scripture
scripture_to_recite.slab = src
scripture_to_recite.invoker = user
scripture_to_recite.run_scripture()
return TRUE
//Guide to Serving Ratvar
/obj/item/clockwork/slab/proc/recollection()
var/list/textlist = list("If you're seeing this, file a bug report.")
if(GLOB.ratvar_awakens)
textlist = list("<font color=#BE8700 size=3><b>")
for(var/i in 1 to 100)
textlist += "HONOR RATVAR "
textlist += "</b></font>"
else
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>\
\
<i>Disclaimer: Many objects, terms, and phrases, such as Servant, Cache, and Slab, are capitalized like proper nouns. This is a quirk of the Ratvarian language; \
do not let it confuse you! You are free to use the names in pronoun form when speaking in normal languages.<br>")
return textlist.Join()
//Gets text for a certain section. "Default" is used for when you first open Recollection.
//Current sections (make sure to update this if you add one:
//- Basics
//- Terminology
//- Components
//- Scripture
//- Power
//- Conversion
/obj/item/clockwork/slab/proc/get_recollection_text(section)
var/list/dat = list()
switch(section)
/*
* Gets text for a certain section. "Default" is used for when you first open Recollection.
* Current sections (make sure to update this if you add one:
* Basics
* Terminology
* Components
* Scripture
* Power
* Conversion
* * what - What section?
*/
/obj/item/clockwork/slab/proc/get_recollection(what) //Now DMDOC compliant!*
. = list()
switch(what) //need someone to rewrite info for this.
if("Default")
dat += "You can browse the above sections as you please. They're designed to be read in order, but feel free to pick and choose between them."
if("Getting Started")
dat += "<font color=#BE8700 size=3>Getting Started</font><br><br>"
dat += "Welcome, Servant! This section houses the utmost basics of being a Servant of Ratvar, and is much more informal than the other sections. Being a Servant of \
Ratvar is a very complex role, with many systems, objects, and resources to use effectively and creatively.<br><br>"
dat += "This section of your clockwork slab covers everything that Servants have to be aware of, but is a long read because of how in-depth the systems are. Knowing \
how to use the tools at your disposal makes all the difference between a clueless Servant and a great one.<br><br>"
dat += "If this is your first time being a Servant, relax. It's very much possible that you'll fail, but it's impossible to learn without making mistakes. For the time \
being, use the Hierophant Network button in the top left-hand corner of your screen to try and get in touch with your fellow Servants; ignore the others for now. This button \
will let you send messages across space and time to all other Servants. This makes it great for coordinating, and you should use it often! <i>Note:</i> Using \
this will cause you to whisper your message aloud, so doing so in a public place is very suspicious and you should try to restrict it to private use.<br><br>"
dat += "If you aren't willing or don't have the time to read through every section, you can still help your teammates! Ask if they've set up a base. If they have, head there \
and ask however you can help; chances are there's always something. If not, it's your job as a Servant to get one up and running! Try to find a secluded, low-traffic area, \
like the auxiliary base or somewhere deep in maintenance. You'll want to go into the Drivers section of the slab and look for <i>Tinkerer's Cache.</i> Find a nice spot and \
create one. This serves as a storage for <i>components,</i> the cult's primary resource. (Your slab's probably produced a few by now.) By attacking that cache with this \
slab, you'll offload all your components into it, and all Servants will be able to use those components from any distance - all Tinkerer's Caches are linked!<br><br>"
dat += "Once you have a base up and running, contact your fellows and let them know. You should come back here often to drop off the slab's components, and your fellows \
should do the same, either in this cache or in ones of their own.<br><br>"
dat += "If you think you're confident in taking further steps to help the cult, feel free to move onto the other sections. If not, let your allies know that you're new and \
would appreciate the help they might offer you. Most experienced Servants would be happy to help; if everyone is inexperienced, then you'll have to step out of your comfort \
zone and read onto the other sections. It's very likely that you might fail, but don't worry too much about it; you can't learn effectively without making mistakes.<br><br>"
dat += "For now, welcome! If you're looking to learn, you should start with the <b>Basics</b> section, then move onto <b>Components</b> and <b>Scripture</b>. At the very \
least, you should read the <b><i>Conversion</i></b> section, as it outlines the most important aspects of being a Servant. Good luck!<br><br>"
dat += "<font color=#BE8700 size=3>-=-=-=-=-=-</font>"
.["title"] = "Default"
.["info"] = "Hello servant! Currently these categories dosen't work!"
/*
if("Basics")
dat += "<font color=#BE8700 size=3>Servant Basics</font><br><br>"
dat += "The first thing any Servant should know is their slab, inside and out. The clockwork slab is by far your most important tool. It allows you to speak with your \
fellow Servants, create components that fuel many of your abilities, use those abilities, and should be kept safe and hidden on your person at all times. If you have not \
done so already, it's a good idea to check for any fellow Servants using the Hierophant Network button in the top-left corner of your screen; due to the cult's nature, \
teamwork is an instrumental component of your success.<br><br>" //get it? component? ha!
dat += "As a Servant of Ratvar, the tools you are given focus around building and maintaining bases and outposts. A great deal of your power comes from stationary \
structures, and without constructing a base somewhere, it's essentially impossible to succeed. Finding a good spot to build a base can be difficult, and it's recommended \
that you choose an area in low-traffic part of the station (such as the auxiliary base). Make sure to disconnect any cameras in the area beforehand.<br><br>"
dat += "Because of how complex being a Servant is, it isn't possible to fit much information into this section. It's highly recommended that you read the <b>Components</b> \
and <b>Scripture</b> sections next. Not knowing how these two systems work will cripple both you and your fellows, and lead to a frustrating experience for everyone.<br><br>"
dat += "<font color=#BE8700 size=3>-=-=-=-=-=-</font>"
.["title"] = "Basics"
.["info"] = "# MARKDOWN WITH HTML?"
if("Terminology")
dat += "<font color=#BE8700 size=3>Common Servant Terminology</font><br>"
dat += "<i>This isn't intended to be read all at once; you are advised to treat it moreso as a glossary.</i><br><br>"
dat += "<font color=#BE8700 size=3>General</font><br>"
dat += "<font color=#BE8700><b>Servant:</b></font> A person or robot who serves Ratvar. You are one of these.<br>"
dat += "<font color=#BE8700><b>Cache:</b></font> A <i>Tinkerer's Cache</i>, which is a structure that stores and creates components.<br>"
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=#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>"
dat += "<font color=#BE8700><b>Visor:</b></font> A judicial visor, which is a pair of glasses that can smite an area for a brief stun and delayed explosion.<br>"
dat += "<font color=#BE8700><b>Wraith Specs:</b></font> Wraith spectacles, which provide true sight (X-ray, night vision) but damage the wearer's eyes.<br>"
dat += "<font color=#BE8700><b>Spear:</b></font> A Ratvarian spear, which is a very powerful melee weapon that produces Vitality.<br>"
dat += "<font color=#BE8700><b>Fabricator:</b></font> A replica fabricator, which converts objects into clockwork versions.<br><br>"
dat += "<font color=#BE8700 size=3>Constructs</font><br>"
dat += "<font color=#BE8700><b>Marauder:</b></font> A clockwork marauder, which is a powerful bodyguard that hides in its owner.<br><br>"
dat += "<font color=#BE8700 size=3>Structures (* = requires power)</font><br>"
dat += "<font color=#BE8700><b>Warden:</b></font> An ocular warden, which is a ranged turret that damages non-Servants that see it.<br>"
dat += "<font color=#BE8700><b>Prism*:</b></font> A prolonging prism, which delays the shuttle for two minutes at a huge power cost.<br><br>"
dat += "<font color=#BE8700><b>Motor*:</b></font> A mania motor, which serves as area-denial through negative effects and eventual conversion.<br>"
dat += "<font color=#BE8700><b>Daemon*:</b></font> A tinkerer's daemon, which quickly creates components.<br>"
dat += "<font color=#BE8700><b>Obelisk*:</b></font> A clockwork obelisk, which can broadcast large messages and allows limited teleportation.<br>"
dat += "<font color=#BE8700 size=3>Sigils</font><br>"
dat += "<i>Note: Sigils can be stacked on top of one another, making certain sigils very effective when paired!</i><br>"
dat += "<font color=#BE8700><b>Transgression:</b></font> Stuns the first non-Servant to cross it for ten seconds and blinds others nearby. Disappears on use.<br>"
dat += "<font color=#BE8700><b>Submission:</b></font> Converts the first non-Servant to stand on the sigil for seven seconds. Disappears on use.<br>"
dat += "<font color=#BE8700><b>Matrix:</b></font> Drains health from non-Servants, producing Vitality. Can heal and revive Servants.<br>"
dat += "<font color=#BE8700><b>Accession:</b></font> Identical to the Sigil of Submission, but doesn't disappear on use. It can also convert a single mindshielded target, but will disappear after doing this.<br>"
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>"
.["title"] = "Terminology"
.["info"] = "# MARKDOWN WITH HTML?"
if("Components")
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 += "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>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 \
added to through various ways. Anything that needs components will first draw them from the global storage before attempting to draw them from the slab. Most methods of \
component production add to the global storage. You can also offload components from your slab into the global storage by using it on a Tinkerer's Cache, a structure whose \
primary purpose is to do just that (although it will also slowly produce components when placed near a brass wall.)<br><br>"
dat += "<font color=#BE8700 size=3>-=-=-=-=-=-</font>"
.["title"] = "Default"
.["info"] = "# MARKDOWN WITH HTML?"
if("Scripture")
dat += "<font color=#BE8700 size=3>The Ancient Scripture</font><br><br>"
dat += "If you have experience with the Nar'Sian cult (or the \"blood cult\") then you will know of runes. They are the manifestations of the Geometer's power, and where most \
of the cult's supernatural ability comes from. The Servant equivalent of runes is called <b>scripture</b>, and unlike runes, scripture is loaded into your clockwork slab.<br><br>"
dat += "Each piece of scripture has widely-varying effects. Your most important scripture, <i>Geis</i>, is obvious and suspicious, but charges your slab with energy and allows \
you to attack a non-Servant in melee range to restrain them and begin converting them into a Servant. This is just one example; each piece of scripture can be simple or \
complex, be obvious or have hidden mechanics that can only be found through trial and error.<br><br>"
dat += "Any given piece of scripture has a component cost listed in its \"Recite\" button. The acronyms for the components should be obvious if you've read about components \
already; reciting this piece of scripture will consume the listed components, first from the global storage and then from your slab. Note that failing to recite a piece of \
scripture will <i>not</i> consume the components required to recite it.<br><br>"
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 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>"
.["title"] = "Default"
.["info"] = "# MARKDOWN WITH HTML?"
if("Power")
dat += "<font color=#BE8700 size=3>Power! Unlimited Power!</font><br><br>"
dat += "In the early stages of the cult, the only resource that must be actively worried about is components. However, as new scripture is unlocked, a new resource \
becomes necessary: <b>power</b>. Almost all clockwork structures require power to function in some way. There is nothing special about this power; it's mere electricity, \
and can be harnessed in several ways.<br><br>"
dat += "To begin with, if there is no other source of power nearby, structures will draw from the area's APC, assuming it has one. This is inefficient and ill-advised as \
anything but a last resort. Instead, it is recommended that a <b>Sigil of Transmission</b> is created. This sigil serves as both battery and power generator for nearby clockwork \
structures, and those structures will happily draw power from the sigil before they resort to APCs.<br><br>"
dat += "Generating power is less easy. The most reliable and efficient way is using brass sheets; attacking a sigil of transmission with brass sheets will convert them \
to power, at a rate of <b>[DisplayPower(POWER_FLOOR)]</b> per sheet. (Brass sheets are created from replica fabricators, which are explained more in detail in the <b>Conversion</b> section.) \
Activating a sigil of transmission will also cause it to drain power from the nearby area, which, while effective, serves as an obvious tell that there is something wrong.<br><br>"
dat += "Without power, many structures will not function, making a base vulnerable to attack. For this reason, it is critical that you keep an eye on your power reserves and \
ensure that they remain comfortably high.<br><br>"
dat += "<font color=#BE8700 size=3>-=-=-=-=-=-</font>"
.["title"] = "Power"
.["info"] = "# MARKDOWN WITH HTML?"
if("Conversion")
dat += "<font color=#BE8700 size=3>Growing the Ranks</font><br><br>"
dat += "Because the Servants of Ratvar are a cult, the main method to gain more power is to \"enlighten\" normal crew into new Servants. When a crewmember is converted, \
they become a full-fledged Servant, ready and willing to serve the cause of Ratvar. It should also be noted that <i>silicon crew, such as cyborgs and the AI, can be \
converted just like normal crew</i> and will gain special abilities; this is covered later. This section will also cover converting the station's structure itself; walls, \
floors, windows, tables, and other objects can all be converted into clockwork versions, and serve an important purpose.<br><br>"
dat += "<font color=#BE8700><b>A Note on Geis:</b></font> There are several ways to convert humans and silicons. However, the most important tool to making them work is \
<b>Geis</b>, a Driver-tier scripture. Using it whispers an invocation very quickly and charges your slab with power. In addition to <i>making the slab visible in your hand,</i> \
you can now use it on a target within melee range to bind and mute them. It is by far your most reliable tool for capturing potential converts and targets, though it is incredibly \
obvious. In addition, you are unable to take any actions other than moving while your target is bound. The binding will last for 25 seconds and mute for about 13 seconds, though \
allies can use Geis to refresh these effects.<br><br>"
dat += "<font color=#BE8700><b>Converting:</b></font> The two methods of conversion are the <b>sigil of submission</b>, whose purpose is to do so, and the <b>mania motor.</b> \
The sigil of submission is a sigil that, when stood on by a non-Servant for eight seconds, will convert that non-Servant. This is the only practical way to convert targets. \
Sigils of submission are cheap, early, and permanent! Make sure sigils of submission are placed only in bases or otherwise hidden spots, or with a sigil of transgression on them. \
The mania motor, however, is generally unreliable and unlocked later, only converting those who stand near it for an extended period.<br><br>"
dat += "<font color=#BE8700><b>Converting Humans:</b></font> For obvious reasons, humans are the most common conversion target. Because every crew member is different, and \
may be armed with different equipment, you should take precautions to ensure that they aren't able to resist. If able, removing a headset is essential, as is restraining \
them through handcuffs, cable ties, or other restraints. Some crew, like security, are also implanted with mindshield implants; these will prevent conversion and must be \
surgically removed before they are an eligible convert. <i>Note:</i> The captain is <i>never</i> an eligible convert and should instead be killed or imprisoned. If security \
begins administering mindshield implants, this will greatly inhibit conversion. Also note that mindshield implants can be broken by a sigil of accession automatically, but \
the sigil will disappear.<br><br>"
dat += "<font color=#BE8700><b>Converting Silicons:</b></font> Due to their robotic nature, silicons are generally more predictable than humans in terms of conversion. \
However, they are also much, much harder to subdue, especially cyborgs. The easiest way to convert a cyborg is by using Geis to restrain them, then dragging them to a sigil \
of submission. If you stack a sigil of transgression and a sigil of submission, a crossing cyborg will be stunned and helpless to escape before they are converted.<br><br>"
dat += "Converting AIs is very often the hardest task of the cult, and has been the downfall of countless successful Servants. Their omnipresence across the station, \
coupled with their secure location and ability to lock themselves securely, makes them a powerful target. However, once the AI itself is reached, it is usually completely \
helpless to resist its own conversion. A very common tactic is to take advantage of a converted cyborg to rush the AI before it is able to react.<br><br>"
dat += "Even once an AI is converted, care must be taken to ensure that it remains hidden. Not only does the AI's core become brassy and thus obvious to an outside \
observer, but <i>the AI loses the ability to speak in anything but Ratvarian.</i> For this reason, it has to remain completely silent over common radio channels if stealth \
is at all a priority. This is suspicious and will rapidly lead to the crew checking on it, which usually results in the cult's outing. It is, however, necessary to convert \
all AIs present on the station before the Ark becomes invokable, so this must be done at some point.<br><br>"
dat += "<font color=#BE8700><b>Converting the Station:</b></font> Converted objects all serve a purpose and are important to the cult's success. To convert objects, \
a Servant needs to use a <b>replica fabricator,</b> a handheld tool that uses power to replace objects with clockwork versions. Different clockwork objects have different \
effects and are often crucial. The most noteworthy are <b>clockwork walls,</b> which automatically \"link\" to any nearby Tinkerer's Caches, causing them to <b>slowly \
generate components.</b> This is incredibly useful for obvious reasons, and creating a clockwork wall near every Tinkerer's Cache should be prioritized. Clockwork floors \
will slowly heal any toxin damage suffered by Servants standing on them, and clockwork airlocks can only be opened by Servants.<br><br>"
dat += "The replica fabricator itself is also worth noting. In addition to replacing objects, it can also create brass sheets at the cost of power by using the \
fabricator in-hand. It can also be used to repair any damaged clockwork structures.<br><br>"
dat += "Replacing objects is almost as, if not as important as, converting new Servants. A base is impossible to manage without clockwork walls at the very least, and \
once the cult has been outed and the crew are actively searching, there is little reason not to use as many as possible.<br><br>"
dat += "<font color=#BE8700 size=3>-=-=-=-=-=-</font>"
.["title"] = "Conversion"
.["info"] = "# MARKDOWN WITH HTML?"
*/
else
dat += "<font color=#BE8700 size=3>404: [section ? section : "Section"] Not Found!</font><br><br>\
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.
/obj/item/clockwork/slab/proc/get_recollection_quickbinds()
var/list/dat = list()
dat += "<font color=#BE8700 size=3>Quickbound Scripture</font><br>\
<i>You can have up to five scriptures bound to action buttons for easy use.</i><br><br>"
if(LAZYLEN(quickbound))
for(var/i in 1 to maximum_quickbound)
if(LAZYLEN(quickbound) < i || !quickbound[i])
dat += "A <b>Quickbind</b> slot, currently set to <b><font color=#BE8700>Nothing</font></b>.<br>"
else
var/datum/clockwork_scripture/quickbind_slot = quickbound[i]
dat += "A <b>Quickbind</b> slot, currently set to <b><font color=[get_component_color_bright(initial(quickbind_slot.primary_component))]>[initial(quickbind_slot.name)]</font></b>.<br>"
return dat.Join()
return null //error text handled tgui side. should not cause BSOD
/obj/item/clockwork/slab/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "ClockworkSlab", name)
ui.open()
/obj/item/clockwork/slab/ui_data(mob/user) //we display a lot of data via TGUI
var/list/data = list()
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)
data["tier_info"] = "<font color=#B18B25><b>These scriptures are permanently unlocked.</b></font>"
if(SCRIPTURE_SCRIPT)
if(SSticker.scripture_states[SCRIPTURE_SCRIPT])
data["tier_info"] = "<font color=#B18B25><b>These scriptures are permanently unlocked.</b></font>"
else
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 permanently unlocked.</b></font>"
else
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["scripturecolors"] = "<font color=#DAAA18>Scriptures in <b>yellow</b> are related to construction and building.</font><br>\
<font color=#6E001A>Scriptures in <b>red</b> are related to attacking and offense.</font><br>\
<font color=#1E8CE1>Scriptures in <b>blue</b> are related to healing and defense.</font><br>\
<font color=#AF0AAF>Scriptures in <b>purple</b> are niche but still important!</font><br>\
<font color=#DAAA18><i>Scriptures with italicized names are important to success.</i></font>"
generate_all_scripture()
data["scripture"] = list()
for(var/s in GLOB.all_scripture)
. = list()
.["recollection"] = recollecting
.["power"] = DisplayPower(get_clockwork_power())
.["power_unformatted"] = get_clockwork_power()
.["HONOR_RATVAR"] = GLOB.ratvar_awakens
.["scripture"] = list()
for(var/s in GLOB.all_scripture) //don't block this, even when ratvar spawns for roundend griff.
var/datum/clockwork_scripture/S = GLOB.all_scripture[s]
if(S.tier == selected_scripture) //display only scriptures of the selected tier
var/scripture_color = get_component_color_bright(S.primary_component)
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" = "([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)
if(S.important)
temp_info["name"] = "<i>[temp_info["name"]]</i>"
var/found = quickbound.Find(S.type)
if(found)
temp_info["bound"] = "<b>[found]</b>"
if(S.invokers_required > 1)
temp_info["invokers"] = "<font color=#B18B25>Invokers: <b>[S.invokers_required]</b></font>"
data["scripture"] += list(temp_info)
data["recollection"] = recollecting
if(recollecting)
data["recollection_categories"] = GLOB.ratvar_awakens ? list() : list(\
list("name" = "Getting Started", "desc" = "First-time servant? Read this first."), \
list("name" = "Basics", "desc" = "A primer on how to play as a servant."), \
list("name" = "Terminology", "desc" = "Common acronyms, words, and terms."), \
list("name" = "Components", "desc" = "Information on components, your primary resource."), \
list("name" = "Scripture", "desc" = "Information on scripture, ancient tools used by the cult."), \
list("name" = "Power", "desc" = "The power system that certain objects use to function."), \
list("name" = "Conversion", "desc" = "Converting the crew, cyborgs, and very walls to your cause."), \
)
data["rec_text"] = recollection()
data["rec_section"] = GLOB.ratvar_awakens ? "" : get_recollection_text(recollection_category)
data["rec_binds"] = GLOB.ratvar_awakens ? "" : get_recollection_quickbinds()
return data
if(S.tier == SCRIPTURE_PERIPHERAL) // This tier is skiped because this contains basetype stuff
continue
var/list/data = list()
data["name"] = S.name
data["descname"] = S.descname
data["tip"] = "[S.desc]\n[S.usage_tip]"
data["required"] = "([DisplayPower(S.power_cost)][S.special_power_text ? "+ [replacetext(S.special_power_text, "POWERCOST", "[DisplayPower(S.special_power_cost)]")]" : ""])"
data["required_unformatted"] = S.power_cost
data["type"] = "[S.type]"
data["quickbind"] = S.quickbind //this is if it cant quickbind (bool)
data["fontcolor"] = get_component_color_bright(S.primary_component)
data["important"] = S.important //italic!
var/found = quickbound.Find(S.type)
if(found)
data["bound"] = found //number (pos) on where is it on the list
if(S.invokers_required > 1)
data["invokers"] = "Invokers: [S.invokers_required]"
.["rec_binds"] = list()
for(var/i in 1 to maximum_quickbound)
if(LAZYLEN(quickbound) < i || !quickbound[i])
.["rec_binds"] += list(list()) //a blank json.
else
var/datum/clockwork_scripture/quickbind_slot = quickbound[i]
.["rec_binds"] += list(list(
"name" = initial(quickbind_slot.name),
"color" = get_component_color_bright(initial(quickbind_slot.primary_component))
))
.["scripture"][S.tier] += list(data)
/obj/item/clockwork/slab/ui_static_data(mob/user)
. = list()
.["tier_infos"] = list() //HEY!! WHEN ADDING NEW TIER, ADD IT HERE
.["tier_infos"][SCRIPTURE_PERIPHERAL] = list(
"requirement" = "Breaking the code DM side. Report to coggerbus if this appears!!",
"ready" = FALSE //just in case. Should NOT exist at all
)
.["tier_infos"][SCRIPTURE_DRIVER] = list(
"requirement" = "None, this is already unlocked",
"ready" = TRUE //to bold it on JS side, and to say "These scriptures are permanently unlocked."
)
.["tier_infos"][SCRIPTURE_SCRIPT] = list(
"requirement" = "These scriptures will automatically unlock when the Ark is halfway ready or if [DisplayPower(SCRIPT_UNLOCK_THRESHOLD)] of power is reached.",
"ready" = SSticker.scripture_states[SCRIPTURE_SCRIPT] //huh, on the gamemode ticker? okay...
)
.["tier_infos"][SCRIPTURE_APPLICATION] = list(
"requirement" = "Unlock these optional scriptures by converting another servant or if [DisplayPower(APPLICATION_UNLOCK_THRESHOLD)] of power is reached..",
"ready" = SSticker.scripture_states[SCRIPTURE_APPLICATION]
)
.["tier_infos"][SCRIPTURE_JUDGEMENT] = list(
"requirement" = "Unlock powerful equipment and structures by converting five servants or if [DisplayPower(JUDGEMENT_UNLOCK_THRESHOLD)] of power is reached..",
"ready" = SSticker.scripture_states[SCRIPTURE_JUDGEMENT]
)
.["recollection_categories"] = list()
if(GLOB.ratvar_awakens)
return
.["recollection_categories"] = list(
list("name" = "Getting Started", "desc" = "First-time servant? Read this first."),
list("name" = "Basics", "desc" = "A primer on how to play as a servant."),
list("name" = "Terminology", "desc" = "Common acronyms, words, and terms."),
list("name" = "Components", "desc" = "Information on components, your primary resource."),
list("name" = "Scripture", "desc" = "Information on scripture, ancient tools used by the cult."),
list("name" = "Power", "desc" = "The power system that certain objects use to function."),
list("name" = "Conversion", "desc" = "Converting the crew, cyborgs, and very walls to your cause.")
)
.["rec_section"] = get_recollection(recollection_category)
generate_all_scripture()
//needs a new place to live, preferably when clockcult unlocks/downgrades a tier. Smart enough to earlyreturn.
/obj/item/clockwork/slab/ui_act(action, params)
switch(action)
if("toggle")
recollecting = !recollecting
if("recite")
INVOKE_ASYNC(src, .proc/recite_scripture, text2path(params["category"]), usr, FALSE)
if("select")
selected_scripture = params["category"]
INVOKE_ASYNC(src, .proc/recite_scripture, text2path(params["script"]), usr, FALSE)
if("bind")
var/datum/clockwork_scripture/path = text2path(params["category"]) //we need a path and not a string
var/datum/clockwork_scripture/path = text2path(params["script"]) //we need a path and not a string
if(!ispath(path, /datum/clockwork_scripture) || !initial(path.quickbind) || initial(path.tier) == SCRIPTURE_PERIPHERAL) //fuck you href bus
to_chat(usr, "<span class='warning'>Nice try using href exploits</span>")
return
var/found_index = quickbound.Find(path)
if(found_index) //hey, we already HAVE this bound
if(LAZYLEN(quickbound) == found_index) //if it's the last scripture, remove it instead of leaving a null
@@ -512,8 +370,8 @@
quickbind_to_slot(path, target_index)
if("rec_category")
recollection_category = params["category"]
ui_interact(usr)
return 1
update_static_data()
return TRUE
/obj/item/clockwork/slab/proc/quickbind_to_slot(datum/clockwork_scripture/scripture, index) //takes a typepath(typecast for initial()) and binds it to a slot
if(!ispath(scripture) || !scripture || (scripture in quickbound))
@@ -31,8 +31,7 @@
. = ..()
clockwork_desc = initial(clockwork_desc)
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/clockwork/construct_chassis/attack_hand(mob/living/user)
/obj/item/clockwork/construct_chassis/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
if(w_class >= WEIGHT_CLASS_HUGE)
to_chat(user, "<span class='warning'>[src] is too cumbersome to carry! Drag it around instead!</span>")
return
@@ -90,17 +89,17 @@
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
var/infinite_resources = TRUE
var/infinite_resources = FALSE //No.
var/static/obj/item/seasonal_hat //Share it with all other scarabs, since we're from the same cult!
/obj/item/clockwork/construct_chassis/cogscarab/Initialize()
. = ..()
if(GLOB.servants_active)
infinite_resources = FALSE //For any that are somehow spawned in late
infinite_resources = FALSE //This check is relatively irrelevant until *someone* makes the infinite resources var default to true again, so, leaving it in.
/obj/item/clockwork/construct_chassis/cogscarab/pre_spawn()
if(infinite_resources)
//During rounds where they can't interact with the station, let them experiment with builds
//During rounds where they can't interact with the station, let them experiment with builds, if an admin allows them to.
construct_type = /mob/living/simple_animal/drone/cogscarab/ratvar
if(!seasonal_hat)
var/obj/item/drone_shell/D = locate() in GLOB.poi_list
@@ -30,6 +30,7 @@
var/obj/item/stock_parts/cell/cell = apc.cell
if(cell && (cell.charge / cell.maxcharge > COG_MAX_SIPHON_THRESHOLD))
cell.use(1)
apc.cog_drained++
adjust_clockwork_power(2) //Power is shared, so only do it once; this runs very quickly so it's about 10 W/second
else
adjust_clockwork_power(1) //Continue generating power when the cell has run dry; 5 W/second
@@ -114,24 +114,27 @@
superheat_wall(A)
return
if(modifiers["middle"] || modifiers["ctrl"])
issue_command(A)
INVOKE_ASYNC(src, .proc/issue_command, A)
return
if(GLOB.ark_of_the_clockwork_justiciar == A)
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
if(G.recalling)
return
if(!G.recalls_remaining)
to_chat(src, "<span class='warning'>The Ark can no longer recall!</span>")
return
if(alert(src, "Initiate mass recall?", "Mass Recall", "Yes", "No") != "Yes" || QDELETED(src) || QDELETED(G) || !G.obj_integrity)
return
G.initiate_mass_recall() //wHOOPS LOOKS LIKE A HULK GOT THROUGH
INVOKE_ASYNC(src, .proc/attempt_recall, G)
else if(istype(A, /obj/structure/destructible/clockwork/trap/trigger))
var/obj/structure/destructible/clockwork/trap/trigger/T = A
T.visible_message("<span class='danger'>[T] clunks as it's activated remotely.</span>")
to_chat(src, "<span class='brass'>You activate [T].</span>")
T.activate()
/mob/camera/eminence/proc/attempt_recall(obj/structure/destructible/clockwork/massive/celestial_gateway/G)
if(G.recalling)
return
if(!G.recalls_remaining)
to_chat(src, "<span class='warning'>The Ark can no longer recall!</span>")
return
if(alert(src, "Initiate mass recall?", "Mass Recall", "Yes", "No") != "Yes" || QDELETED(src) || QDELETED(G) || !G.obj_integrity)
return
G.initiate_mass_recall() //wHOOPS LOOKS LIKE A HULK GOT THROUGH
/mob/camera/eminence/ratvar_act()
name = "\improper Radiance"
real_name = "\improper Radiance"
@@ -124,3 +124,437 @@
#undef MARAUDER_SLOWDOWN_PERCENTAGE
#undef MARAUDER_SHIELD_REGEN_TIME
//Clockwork guardian: Slow but with high damage, resides inside of a servant. Created via the Memory Allocation scripture.
/mob/living/simple_animal/hostile/clockwork/marauder/guardian
name = "clockwork guardian"
desc = "A stalwart apparition of a soldier, blazing with crimson flames. It's armed with a gladius and shield and stands ready by its master."
icon_state = "clockwork_marauder"
health = 300
maxHealth = 300
speed = 1
obj_damage = 40
melee_damage_lower = 12
melee_damage_upper = 12
attack_verb_continuous = "slashes"
attack_verb_simple = "slash"
attack_sound = 'sound/weapons/bladeslice.ogg'
weather_immunities = list("lava")
movement_type = FLYING
AIStatus = AI_OFF //this has to be manually set so that the guardian doesn't start bashing the host, how annoying -_-
loot = list(/obj/item/clockwork/component/geis_capacitor/fallen_armor)
max_shield_health = 0
shield_health = 0
var/true_name = "Meme Master 69" //Required to call forth the guardian
var/global/list/possible_true_names = list("Servant", "Warden", "Serf", "Page", "Usher", "Knave", "Vassal", "Escort")
var/mob/living/host //The mob that the guardian is living inside of
var/recovering = FALSE //If the guardian 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 guardian</span><b>, a living extension of Sevtug's will. As a guardian, 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, as well as The Clockwork Justiciar, Ratvar. You can use <span class='sevtug_small'><i>The Hierophant Network</i></span> to communicate silently with your master and their allies, \
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>"
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/Initialize()
. = ..()
true_name = pick(possible_true_names)
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/BiologicalLife(seconds, times_fired)
..()
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 guardian is now strong enough to come forward again!</span>")
recovering = FALSE
else
if(GLOB.ratvar_awakens) //If Ratvar is alive, guardians 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/guardian/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>")
. = ..()
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/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 > GUARDIAN_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/guardian/Process_Spacemove(movement_dir = 0)
return 1
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/bind_to_host(mob/living/new_host)
if(!new_host)
return FALSE
host = new_host
var/datum/action/innate/summon_guardian/SG = new()
SG.linked_guardian = src
SG.Grant(host)
var/datum/action/innate/linked_minds/LM = new()
LM.linked_guardian = src
LM.Grant(host)
return TRUE
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/unbind_from_host()
if(host)
for(var/datum/action/innate/summon_guardian/SG in host.actions)
qdel(SG)
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/guardian/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 <= GUARDIAN_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/guardian/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
if(amount > 0)
for(var/mob/living/L in view(2, src))
if(L.is_holding_item_of_type(/obj/item/nullrod))
to_chat(src, "<span class='userdanger'>The presence of a brandished holy artifact weakens your armor!</span>")
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/guardian/update_health_hud()
if(hud_used && hud_used.healths)
if(istype(hud_used, /datum/hud/marauder))
var/datum/hud/marauder/G = 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"
G.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/guardian/proc/update_stats()
if(GLOB.ratvar_awakens)
speed = 0
melee_damage_lower = 20
melee_damage_upper = 20
attack_verb_continuous = "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
attack_verb_continuous = "viciously slashes"
if(70 to 40)
speed = initial(speed)
melee_damage_lower = initial(melee_damage_lower)
melee_damage_upper = initial(melee_damage_upper)
attack_verb_continuous = initial(attack_verb_continuous)
if(40 to 30) //Damage decrease, but not speed
speed = initial(speed)
melee_damage_lower = 10
melee_damage_upper = 10
attack_verb_continuous = "lightly slashes"
if(30 to 20) //Speed decrease
speed = 2
melee_damage_lower = 8
melee_damage_upper = 8
attack_verb_continuous = "lightly slashes"
if(20 to 10) //Massive speed decrease and weak melee attacks
speed = 3
melee_damage_lower = 6
melee_damage_upper = 6
attack_verb_continuous = "weakly slashes"
if(10 to 0) //We are super weak and going to die
speed = 4
melee_damage_lower = 4
melee_damage_upper = 4
attack_verb_continuous = "taps"
//ATTACKING, BLOCKING, and COUNTERING
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/AttackingTarget()
if(is_in_host())
return FALSE
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/bullet_act(obj/item/projectile/Proj)
if(blockOrCounter(null, Proj))
return
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/hitby(atom/movable/AM, skipcatch, hitpush, blocked, atom/movable/AM, datum/thrownthing/throwingdatum)
if(blockOrCounter(null, AM))
return
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/attack_animal(mob/living/simple_animal/M)
if(istype(M, /mob/living/simple_animal/hostile/clockwork/marauder/guardian) || !blockOrCounter(M, M)) //we don't want infinite blockcounter loops if fighting another guardian
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/attack_paw(mob/living/carbon/monkey/M)
if(!blockOrCounter(M, M))
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/attack_alien(mob/living/carbon/alien/humanoid/M)
if(!blockOrCounter(M, M))
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/attack_slime(mob/living/simple_animal/slime/M)
if(!blockOrCounter(M, M))
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/attack_hand(mob/living/carbon/human/M)
if(!blockOrCounter(M, M))
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/nullrod) || !blockOrCounter(user, I))
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/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))
. = TRUE
if(target)
target.do_attack_animation(src)
target.DelayNextAction(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/previousattack_verb_continuous = attack_verb_continuous
attack_verb_continuous = "counters"
UnarmedAttack(target)
attack_verb_continuous = previousattack_verb_continuous
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/guardian/handle_inherent_channels(message, message_mode)
if(host && (is_in_host() || message_mode == MODE_BINARY))
guardian_comms(message)
return TRUE
return ..()
*/
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/guardian_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/guardian/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 > GUARDIAN_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/guardian/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 > GUARDIAN_EMERGE_THRESHOLD) //if above 20 health, fails
to_chat(src, "<span class='warning'>Your host must be at [GUARDIAN_EMERGE_THRESHOLD]% or less health to emerge like this!</span>")
return FALSE
return emerge_from_host(FALSE)
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/emerge_from_host(hostchosen, force) //Notice that this is a proc rather than a verb - guardians 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) //guardian 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/guardian/get_alt_name()
return " ([text2ratvar(true_name)])"
/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/is_in_host() //Checks if the guardian is inside of their host
return host && loc == host
//HOST ACTIONS
//Summon guardian action: Calls forth or recalls your guardian
/datum/action/innate/summon_guardian
name = "Force Guardian to Emerge/Recall"
desc = "Allows you to force your clockwork guardian 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/guardian/linked_guardian
var/list/defend_phrases = list("Defend me", "Come forth", "Assist me", "Protect me", "Give aid", "Help me")
var/list/return_phrases = list("Return", "Return to me", "Your job is done", "You have served", "Come back", "Retreat")
/datum/action/innate/summon_guardian/IsAvailable()
if(!linked_guardian)
return FALSE
if(isliving(owner))
var/mob/living/L = owner
if(!L.can_speak_vocal() || L.stat)
return FALSE
return ..()
/datum/action/innate/summon_guardian/Activate()
if(linked_guardian.is_in_host())
clockwork_say(owner, text2ratvar("[pick(defend_phrases)], [linked_guardian.true_name]!"))
linked_guardian.emerge_from_host(TRUE)
else
clockwork_say(owner, text2ratvar("[pick(return_phrases)], [linked_guardian.true_name]!"))
linked_guardian.return_to_host()
return TRUE
//Linked Minds action: talks to your guardian
/datum/action/innate/linked_minds
name = "Linked Minds"
desc = "Allows you to silently communicate with your guardian."
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/guardian/linked_guardian
/datum/action/innate/linked_minds/IsAvailable()
if(!linked_guardian)
return FALSE
return ..()
/datum/action/innate/linked_minds/Activate()
var/message = stripped_input(owner, "Enter a message to tell your guardian.", "Telepathy")
if(!owner || !message)
return FALSE
if(!linked_guardian)
to_chat(owner, "<span class='warning'>Your guardian 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_guardian, "[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_guardian] ([linked_guardian.true_name])</span><span class='sevtug_small'>):</span> [message]")
return TRUE
@@ -3,8 +3,9 @@ Tiers and Requirements
Pieces of scripture require certain follower counts, contruction value, and active caches in order to recite.
Drivers: Unlocked by default
Scripts: 5 servants and a cache
Applications: 8 servants, 3 caches, and 100 CV
Scripts: 35k power or one convert
Applications: 50k or three converts
Judgement 5 converts
*/
/datum/clockwork_scripture
@@ -129,11 +130,11 @@ Applications: 8 servants, 3 caches, and 100 CV
SEND_SOUND(invoker, sound('sound/magic/clockwork/invoke_general.ogg'))
return TRUE
/datum/clockwork_scripture/proc/check_offstation_penalty()
/datum/clockwork_scripture/proc/check_offstation_penalty()//don't cast spells away from the station
var/turf/T = get_turf(invoker)
if(!T || (!is_centcom_level(T.z) && !is_station_level(T.z) && !is_mining_level(T.z) && !is_reebe(T.z)))
channel_time *= 2
power_cost *= 2
channel_time *= 3
power_cost *= 3
return TRUE
/datum/clockwork_scripture/proc/check_special_requirements() //Special requirements for scriptures, checked multiple times during invocation
@@ -1,5 +1,5 @@
//////////////////
// APPLICATIONS //
// APPLICATIONS // For various structures and base building, as well as advanced power generation.
//////////////////
@@ -23,6 +23,37 @@
quickbind = TRUE
quickbind_desc = "Creates a Sigil of Transmission, which can drain and will store power for clockwork structures."
//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
power_cost = 300
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 = 4
important = TRUE
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/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)
to_chat(invoker, "<span class='inathneq'>\"It is too late to construct one of these, champion.\"</span>")
return FALSE
var/turf/T = get_turf(invoker)
if(!T || !is_station_level(T.z))
to_chat(invoker, "<span class='inathneq'>\"You must be on the station to construct one of these, champion.\"</span>")
return FALSE
return ..()
//Mania Motor: Creates a malevolent transmitter that will broadcast the whispers of Sevtug into the minds of nearby nonservants, causing a variety of mental effects at a power cost.
/datum/clockwork_scripture/create_object/mania_motor
@@ -44,6 +75,7 @@
sort_priority = 2
quickbind = TRUE
quickbind_desc = "Creates a Mania Motor, which causes minor damage and negative mental effects in non-Servants."
requires_full_power = TRUE
//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.
@@ -67,6 +99,64 @@
quickbind = TRUE
quickbind_desc = "Creates a Clockwork Obelisk, which can send messages or open Spatial Gateways with power."
//Memory Allocation: Finds a willing ghost and makes them into a clockwork guardian for the invoker.
/datum/clockwork_scripture/memory_allocation
descname = "Personal Guardian, A Peice Of Your Mind."
name = "Memory Allocation"
desc = "Allocates part of your consciousness to a Clockwork Guardian, a variant of Marauder 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
power_cost = 8000
usage_tip = "guardians are useful as personal bodyguards and frontline warriors."
tier = SCRIPTURE_APPLICATION
primary_component = GEIS_CAPACITOR
sort_priority = 5
/datum/clockwork_scripture/memory_allocation/check_special_requirements()
for(var/mob/living/simple_animal/hostile/clockwork/marauder/guardian/M in GLOB.all_clockwork_mobs)
if(M.host == invoker)
to_chat(invoker, "<span class='warning'>You can only house one guardian at a time!</span>")
return FALSE
return TRUE
/datum/clockwork_scripture/memory_allocation/scripture_effects()
return create_guardian()
/datum/clockwork_scripture/memory_allocation/proc/create_guardian()
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.Knockdown(100)
invoker.apply_damage(50, BRUTE, "head")//Sevtug leaves a gaping hole in your face if interrupted.
slab.busy = null
return FALSE
clockwork_say(invoker, text2ratvar("...the mind made..."))
//invoker.notransform = FALSE
slab.busy = "Guardian Selection in progress"
if(!check_special_requirements())
return FALSE
to_chat(invoker, "<span class='warning'>The tendril shivers slightly as it selects a guardian...</span>")
var/list/marauder_candidates = pollGhostCandidates("Do you want to play as the clockwork guardian of [invoker.real_name]?", ROLE_SERVANT_OF_RATVAR, null, FALSE, 50, POLL_IGNORE_HOLOPARASITE)
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/guardian/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 guardian, has taken up residence in your mind. Communicate with it via the \"Linked Minds\" action button.</span>")
return TRUE
//Clockwork Marauder: Creates a construct shell for a clockwork marauder, a well-rounded frontline fighter.
/datum/clockwork_scripture/create_object/construct/clockwork_marauder
@@ -81,7 +171,7 @@
tier = SCRIPTURE_APPLICATION
one_per_tile = TRUE
primary_component = BELLIGERENT_EYE
sort_priority = 4
sort_priority = 6
quickbind = TRUE
quickbind_desc = "Creates a clockwork marauder, used for frontline combat."
object_path = /obj/item/clockwork/construct_chassis/clockwork_marauder
@@ -117,14 +207,13 @@
/datum/clockwork_scripture/create_object/summon_arbiter
descname = "Powerful Assault Mech"
name = "Summon Neovgre, the Anima Bulwark"
desc = "Calls forth the mighty Anima Bulwark, a weapon of unmatched power,\
mech with superior defensive and offensive capabilities. It will \
desc = "Calls forth the mighty Anima Bulwark, a mech with superior defensive and offensive capabilities. It will \
steadily regenerate HP and triple its regeneration speed while standing \
on a clockwork tile. It will automatically draw power from nearby sigils of \
transmission should the need arise. Its Arbiter laser cannon can decimate foes \
from a range and is capable of smashing through any barrier presented to it. \
Be warned, choosing to pilot Neovgre is a lifetime commitment, once you are \
in you cannot leave and when it is destroyed it will explode catastrophically with you inside."
Be warned however, choosing to pilot Neovgre is a lifetime commitment, once you are \
in you cannot leave and when it is destroyed it will explode catastrophically, with you inside."
invocations = list("By the strength of the alloy...!!", "...call forth the Arbiter!!")
channel_time = 200 // This is a strong fucking weapon, 20 seconds channel time is getting off light I tell ya.
power_cost = 75000 //75 KW
@@ -134,7 +223,7 @@
object_path = /obj/mecha/combat/neovgre
tier = SCRIPTURE_APPLICATION
primary_component = BELLIGERENT_EYE
sort_priority = 2
sort_priority = 7
creator_message = "<span class='brass'>Neovgre, the Anima Bulwark towers over you... your enemies reckoning has come.</span>"
/datum/clockwork_scripture/create_object/summon_arbiter/check_special_requirements()
@@ -1,5 +1,5 @@
/////////////////
// CYBORG ONLY //
// CYBORG ONLY // Cyborgs only, fleshed ones.
/////////////////
//Linked Vanguard: grants Vanguard to the invoker and a target
@@ -1,5 +1,5 @@
/////////////
// DRIVERS //
// DRIVERS // Starter spells
/////////////
//Stargazer: Creates a stargazer, a cheap power generator that utilizes starlight.
@@ -97,7 +97,7 @@
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 = 20 // I think making kindle channel a third of the time less is a good make up for the fact that it silences people for such a little amount of time.
channel_time = 15 // I think making kindle channel a third of the time less is a good make up for the fact that it silences people for such a little amount of time.
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
@@ -113,7 +113,6 @@
quickbind = TRUE
quickbind_desc = "Stuns and mutes a target from a short range."
//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"
@@ -192,43 +191,39 @@
Click your slab to cancel.</b></span>"
/*//commenting this out until its reworked to actually do random teleports
//Abscond: Used to return to Reebe.
/datum/clockwork_scripture/abscond
descname = "Return to Reebe"
descname = "Safety warp, teleports you somewhere random. moderately high power cost to use."
name = "Abscond"
desc = "Yanks you through space, returning you to home base."
desc = "Yanks you through space, putting you in hopefully a safe location."
invocations = list("As we bid farewell, and return to the stars...", "...we shall find our way home.")
whispered = TRUE
channel_time = 50
power_cost = 5
special_power_text = "POWERCOST to bring pulled creature"
special_power_cost = ABSCOND_ABDUCTION_COST
channel_time = 3.5
power_cost = 10000
usage_tip = "This can't be used while on Reebe, for obvious reasons."
tier = SCRIPTURE_DRIVER
primary_component = GEIS_CAPACITOR
sort_priority = 9
important = TRUE
quickbind = TRUE
quickbind_desc = "Returns you to Reebe."
quickbind_desc = "Teleports you somewhere random, or to an active Ark if one exists. Use in emergencies."
var/client_color
requires_full_power = TRUE
/datum/clockwork_scripture/abscond/check_special_requirements()
if(is_reebe(invoker.z))
to_chat(invoker, "<span class='danger'>You're already at Reebe.</span>")
to_chat(invoker, "<span class='danger'>You're at Reebe, attempting to warp in the void could cause you to share your masters fate of banishment!.</span>")
return
if(!isturf(invoker.loc))
to_chat(invoker, "<span class='danger'>You must be visible to return!</span>")
to_chat(invoker, "<span class='danger'>You must be visible to warp!</span>")
return
return TRUE
/datum/clockwork_scripture/abscond/recital()
client_color = invoker.client.color
animate(invoker.client, color = "#AF0AAF", time = 50)
. = ..()
/datum/clockwork_scripture/abscond/scripture_effects()
var/mob/living/pulled_mob = (invoker.pulling && isliving(invoker.pulling) && get_clockwork_power(ABSCOND_ABDUCTION_COST)) ? invoker.pulling : null
var/turf/T
if(GLOB.ark_of_the_clockwork_justiciar)
T = get_step(GLOB.ark_of_the_clockwork_justiciar, SOUTH)
@@ -237,21 +232,12 @@
if(!do_teleport(invoker, T, channel = TELEPORT_CHANNEL_CULT, forced = TRUE))
return
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>")
"<span class='bold sevtug_small'>You feel a dizzying sense of vertigo as you're yanked through the fabric of reality!</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(pulled_mob && do_teleport(pulled_mob, T, channel = TELEPORT_CHANNEL_CULT, forced = TRUE))
adjust_clockwork_power(-special_power_cost)
invoker.start_pulling(pulled_mob) //forcemove resets pulls, so we need to re-pull
if(invoker.client)
animate(invoker.client, color = client_color, time = 25)
/datum/clockwork_scripture/abscond/scripture_fail()
if(invoker && invoker.client)
animate(invoker.client, color = client_color, time = 10)
do_sparks(5, TRUE, T)*/
//Replicant: Creates a new clockwork slab.
@@ -265,7 +251,7 @@
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>"
usage_tip = "This is inefficient as a way to produce components, as the slab produced must be held by someone with no other slabs to produce components."
usage_tip = "This is inefficient as a way to produce power, as the slab produced must be held by someone with no other slabs to produce any."
tier = SCRIPTURE_DRIVER
space_allowed = TRUE
primary_component = GEIS_CAPACITOR
@@ -293,3 +279,50 @@
sort_priority = 11
quickbind = TRUE
quickbind_desc = "Creates a pair of Wraith Spectacles, which grant true sight but cause gradual vision loss."
//Spatial Gateway: Allows the invoker to teleport themselves and any nearby allies to a conscious servant or clockwork obelisk.
/datum/clockwork_scripture/spatial_gateway
descname = "Teleport Gate"
name = "Spatial Gateway"
desc = "Tears open a miniaturized gateway in spacetime to any conscious servant that can transport objects or creatures to its destination. \
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 = 30
power_cost = 400
whispered = TRUE
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."
tier = SCRIPTURE_DRIVER
primary_component = GEIS_CAPACITOR
sort_priority = 9
quickbind = TRUE
quickbind_desc = "Allows you to create a one-way Spatial Gateway to a living Servant or Clockwork Obelisk."
/datum/clockwork_scripture/spatial_gateway/check_special_requirements()
if(!isturf(invoker.loc))
to_chat(invoker, "<span class='warning'>You must not be inside an object to use this scripture!</span>")
return FALSE
var/other_servants = 0
for(var/mob/living/L in GLOB.alive_mob_list)
if(is_servant_of_ratvar(L) && !L.stat && L != invoker)
other_servants++
for(var/obj/structure/destructible/clockwork/powered/clockwork_obelisk/O in GLOB.all_clockwork_objects)
if(O.anchored)
other_servants++
if(!other_servants)
to_chat(invoker, "<span class='warning'>There are no other conscious servants or anchored clockwork obelisks!</span>")
return FALSE
return TRUE
/datum/clockwork_scripture/spatial_gateway/scripture_effects()
var/portal_uses = 0
var/duration = 0
for(var/mob/living/L in range(1, invoker))
if(!L.stat && is_servant_of_ratvar(L))
portal_uses++
duration += 40 //4 seconds
if(GLOB.ratvar_awakens)
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)
@@ -0,0 +1,44 @@
///////////////
// JUDGEMENT // For the big game changing things. TODO: Summonable generals, just need mob sprites for them.
///////////////
//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 power.\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! WE 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
power_cost = 70000 //70 KW. It's literally the thing wrenching the god out of another dimension why wouldn't it be costly.
invokers_required = 6
multiple_invokers_used = TRUE
object_path = /obj/structure/destructible/clockwork/massive/celestial_gateway
creator_message = "<span class='heavy_brass'>The Ark swirls into existance before you with the help of the Generals. After all this time, he shall, finally, be free</span>"
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_APPLICATION
sort_priority = 8
requires_full_power = TRUE
/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, there is no point in that.\"</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 || !is_station_level(T.z) || 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 ..()
@@ -1,5 +1,5 @@
/////////////
// SCRIPTS //
// SCRIPTS // Various miscellanious spells for offense/defense/construction.
/////////////
@@ -81,6 +81,25 @@
return /obj/effect/clockwork/sigil/vitality/neutered
return ..()
//Sigil of Rites: Creates a sigil that allows to perform certain rites on it. More information on these can be found in clock_rites.dm, they usually require power, materials and sometimes a target.
/datum/clockwork_scripture/create_object/sigil_of_rites
descname = "Sigil, Access to rites"
name = "Sigil of Rites"
desc = "Places a sigil that, when interacted with, will allow for a variety of rites to be performed on the sigil. These usually require power cells, clockwork power, and some other components."
invocations = list("Engine, allow us..", ".. to be blessed with your rites.")
channel_time = 80
power_cost = 1400
invokers_required = 2
multiple_invokers_used = TRUE
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/rite
creator_message = "<span class='brass'>A sigil of Rites appears beneath you. It will allow you to perform certain rites, given sufficient materials and power.</span>"
usage_tip = "It may be useful to coordinate to acquire needed materials quickly."
tier = SCRIPTURE_SCRIPT
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 4
//Judicial Visor: Creates a judicial visor, which can smite an area.
/datum/clockwork_scripture/create_object/judicial_visor
descname = "Delayed Area Knockdown Glasses"
@@ -96,7 +115,7 @@
tier = SCRIPTURE_SCRIPT
space_allowed = TRUE
primary_component = BELLIGERENT_EYE
sort_priority = 4
sort_priority = 5
quickbind = TRUE
quickbind_desc = "Creates a Judicial Visor, which can smite an area, applying Belligerent and briefly stunning."
@@ -115,7 +134,7 @@
tier = SCRIPTURE_SCRIPT
space_allowed = TRUE
primary_component = VANGUARD_COGWHEEL
sort_priority = 6
sort_priority = 7
quickbind = TRUE
quickbind_desc = "Creates a Ratvarian shield, which can absorb energy from attacks for use in powerful bashes."
@@ -131,7 +150,7 @@
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 = VANGUARD_COGWHEEL
sort_priority = 7
sort_priority = 8
important = TRUE
quickbind = TRUE
quickbind_desc = "Permanently binds clockwork armor and a Ratvarian spear to you."
@@ -217,53 +236,6 @@
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
descname = "Teleport Gate"
name = "Spatial Gateway"
desc = "Tears open a miniaturized gateway in spacetime to any conscious servant that can transport objects or creatures to its destination. \
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
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."
tier = SCRIPTURE_SCRIPT
primary_component = GEIS_CAPACITOR
sort_priority = 9
quickbind = TRUE
quickbind_desc = "Allows you to create a one-way Spatial Gateway to a living Servant or Clockwork Obelisk."
/datum/clockwork_scripture/spatial_gateway/check_special_requirements()
if(!isturf(invoker.loc))
to_chat(invoker, "<span class='warning'>You must not be inside an object to use this scripture!</span>")
return FALSE
var/other_servants = 0
for(var/mob/living/L in GLOB.alive_mob_list)
if(is_servant_of_ratvar(L) && !L.stat && L != invoker)
other_servants++
for(var/obj/structure/destructible/clockwork/powered/clockwork_obelisk/O in GLOB.all_clockwork_objects)
if(O.anchored)
other_servants++
if(!other_servants)
to_chat(invoker, "<span class='warning'>There are no other conscious servants or anchored clockwork obelisks!</span>")
return FALSE
return TRUE
/datum/clockwork_scripture/spatial_gateway/scripture_effects()
var/portal_uses = 0
var/duration = 0
for(var/mob/living/L in range(1, invoker))
if(!L.stat && is_servant_of_ratvar(L))
portal_uses++
duration += 40 //4 seconds
if(GLOB.ratvar_awakens)
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)
//Mending Mantra: Channeled for up to ten times over twenty seconds to repair structures and heal allies
/datum/clockwork_scripture/channeled/mending_mantra
descname = "Channeled, Area Healing and Repair"
@@ -276,7 +248,7 @@
usage_tip = "This is a very effective way to rapidly reinforce a base after an attack."
tier = SCRIPTURE_SCRIPT
primary_component = VANGUARD_COGWHEEL
sort_priority = 8
sort_priority = 9
quickbind = TRUE
quickbind_desc = "Repairs nearby structures and constructs. Servants wearing clockwork armor will also be healed.<br><b>Maximum 10 chants.</b>"
var/heal_attempts = 4
@@ -389,7 +361,7 @@
usage_tip = "Though it requires you to stand still, this scripture can do massive damage."
tier = SCRIPTURE_SCRIPT
primary_component = BELLIGERENT_EYE
sort_priority = 5
sort_priority = 6
quickbind = TRUE
quickbind_desc = "Allows you to fire energy rays at target locations.<br><b>Maximum 5 chants.</b>"
var/static/list/nzcrentr_insults = list("You're not very good at aiming.", "You hunt badly.", "What a waste of energy.", "Almost funny to watch.",
@@ -438,7 +410,7 @@
usage_tip = "It may be useful to end channelling early if the burning becomes too much to handle.."
tier = SCRIPTURE_SCRIPT
primary_component = GEIS_CAPACITOR
sort_priority = 10
sort_priority = 11
quickbind = TRUE
quickbind_desc = "Quickly drains power in an area around the invoker, causing burns proportional to the amount of energy drained.<br><b>Maximum of 20 chants.</b>"
@@ -487,3 +459,26 @@
invoker.light_range = 0
invoker.update_light()
return ..()
//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!", "Kneel before The Justiciar!", "Halt their charges!", "Cease the tides!")
chant_amount = 15
chant_interval = 20
channel_time = 20
power_cost = 300
usage_tip = "Useful for crowd control in a populated area and disrupting mass movement."
tier = SCRIPTURE_DRIVER
primary_component = BELLIGERENT_EYE
sort_priority = 1
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
@@ -1,5 +1,3 @@
#define ARK_GRACE_PERIOD 300 //In seconds, how long the crew has before the Ark truly "begins"
/proc/clockwork_ark_active() //A helper proc so the Ark doesn't have to be typecast every time it's checked; returns null if there is no Ark and its active var otherwise
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
if(!G)
@@ -11,7 +9,7 @@
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."
its instability will alert the entire area, so be prepared to defend it at all costs."
max_integrity = 500
mouse_opacity = MOUSE_OPACITY_OPAQUE
icon = 'icons/effects/clockwork_effects.dmi'
@@ -22,9 +20,8 @@
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/initial_activation_delay = 5 //How many seconds the Ark will have initially taken to activate
var/seconds_until_activation = 5 //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
@@ -38,10 +35,21 @@
/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/on_attack_hand(mob/user, act_intent, unarmed_attack_flags)
if(!active && is_servant_of_ratvar(user) && user.canUseTopic(src, !issilicon(user), NO_DEXTERY))
if(alert(user, "Are you sure you want to activate the ark? Once enabled, there will be no turning back.", "Enabling the ark", "Activate!", "Cancel") == "Activate!")
if(active)
return
log_game("[key_name(user)] has activated an Ark of the Clockwork Justicar at [COORD(src)].")
START_PROCESSING(SSprocessing, src)
SSshuttle.registerHostileEnvironment(src)
else
to_chat(user, "<span class='brass'>You decide against activating the ark.. for now.</span>")
/obj/structure/destructible/clockwork/massive/celestial_gateway/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
. = ..()
@@ -49,7 +57,7 @@
flick("clockwork_gateway_damaged", glow)
playsound(src, 'sound/machines/clockcult/ark_damage.ogg', 75, FALSE)
if(last_scream < world.time)
audible_message("<span class='boldwarning'>An unearthly screaming sound resonates throughout Reebe!</span>")
audible_message("<span class='boldwarning'>An unearthly screaming sound resonates throughout the area!</span>")
for(var/V in GLOB.player_list)
var/mob/M = V
var/turf/T = get_turf(M)
@@ -60,31 +68,19 @@
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/final_countdown(ark_time) //WE'RE LEAVING TOGETHEEEEEEEEER
if(!ark_time)
ark_time = 30 //minutes
initial_activation_delay = ark_time * 60
seconds_until_activation = ark_time * 60 //60 seconds in a minute * number of minutes
ark_time = 5 //5 minutes
for(var/obj/item/clockwork/construct_chassis/cogscarab/C in GLOB.all_clockwork_objects)
C.infinite_resources = FALSE
GLOB.servants_active = TRUE
SSshuttle.registerHostileEnvironment(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! You will be transported there soon!</span>")
for(var/mob/M in GLOB.player_list)
var/turf/T = get_turf(M)
if(is_servant_of_ratvar(M) || isobserver(M) || (T && T.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
visible_message("<span class='boldwarning'>[src] shudders and roars to life, its parts beginning to whirr and screech!</span>")
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 for an attack on [station_name()]." : ""]", \
"Central Command Higher Dimensional Affairs", 'sound/magic/clockwork/ark_activation.ogg')
not a drill.", "Central Command Higher Dimensional Affairs", 'sound/magic/clockwork/ark_activation_sequence.ogg')
set_security_level("delta")
for(var/V in SSticker.mode.servants_of_ratvar)
var/datum/mind/M = V
@@ -92,15 +88,6 @@
continue
if(ishuman(M.current))
M.current.add_overlay(mutable_appearance('icons/effects/genetics.dmi', "servitude", -MUTATIONS_LAYER))
for(var/V in GLOB.brass_recipes)
var/datum/stack_recipe/R = V
if(!R)
continue
if(R.title == "wall gear")
R.time *= 2 //Building walls becomes slower when the Ark activates
mass_recall()
recalls_remaining++ //So it doesn't use up a charge
var/turf/T = get_turf(src)
var/list/open_turfs = list()
for(var/turf/open/OT in orange(1, T))
@@ -110,14 +97,35 @@
for(var/mob/living/L in T)
L.forceMove(pick(open_turfs))
/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()
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)
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))
open_turfs |= OT
if(open_turfs.len)
for(var/mob/living/L in T)
L.forceMove(pick(open_turfs))
glow = new(get_turf(src))
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)
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/initiate_mass_recall()
recalling = TRUE
@@ -141,35 +149,22 @@
transform = matrix() * 2
animate(src, transform = matrix() * 0.5, time = 30, flags = ANIMATION_END_NOW)
/obj/structure/destructible/clockwork/massive/celestial_gateway/Destroy()
obj/structure/destructible/clockwork/massive/celestial_gateway/Destroy()
STOP_PROCESSING(SSprocessing, src)
if(!purpose_fulfilled)
var/area/gate_area = get_area(src)
hierophant_message("<span class='large_brass'><b>An Ark of the Clockwork Justicar has fallen at [gate_area.map_name]!</b></span>")
send_to_playing_players(sound(null, 0, channel = CHANNEL_JUSTICAR_ARK))
var/was_stranded = SSshuttle.emergency.mode == SHUTTLE_STRANDED
SSshuttle.clearHostileEnvironment(src)
if(!purpose_fulfilled && istype(SSticker.mode, /datum/game_mode/clockwork_cult))
hierophant_message("<span class='bold large_brass'>The Ark has fallen!</span>")
sound_to_playing_players(null, channel = CHANNEL_JUSTICAR_ARK)
SSticker.force_ending = TRUE //rip
if(!was_stranded && !purpose_fulfilled)
priority_announce("Massive energy anomaly no longer on short-range scanners, bluespace distortions still detected.","Central Command Higher Dimensional Affairs")
if(glow)
qdel(glow)
glow = null
if(countdown)
qdel(countdown)
countdown = null
for(var/mob/L in GLOB.player_list)
var/turf/T = get_turf(L)
if(T && T.z == z)
var/atom/movable/target = L
if(isobj(L.loc))
target = L.loc
target.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)
@@ -203,8 +198,6 @@
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/get_arrival_time(var/deciseconds = TRUE)
if(seconds_until_activation)
. = seconds_until_activation
else if(grace_period)
. = grace_period
else if(GATEWAY_RATVAR_ARRIVAL - progress_in_seconds > 0)
. = round(max((GATEWAY_RATVAR_ARRIVAL - progress_in_seconds) / (GATEWAY_SUMMON_RATE), 0), 1)
if(deciseconds)
@@ -213,8 +206,6 @@
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/get_arrival_text(s_on_time)
if(seconds_until_activation)
return "[get_arrival_time()][s_on_time ? "S" : ""]"
if(grace_period)
return "[get_arrival_time()][s_on_time ? "S" : ""]"
. = "IMMINENT"
if(!obj_integrity)
. = "DETONATING"
@@ -229,17 +220,14 @@
if(!active)
. += "<span class='big'><b>Time until the Ark's activation:</b> [DisplayTimeText(get_arrival_time())]</span>"
else
if(grace_period)
. += "<span class='big'><b>Crew grace period time remaining:</b> [DisplayTimeText(get_arrival_time())]</span>"
else
. += "<span class='big'><b>Time until Ratvar's arrival:</b> [DisplayTimeText(get_arrival_time())]</span>"
switch(progress_in_seconds)
if(-INFINITY to GATEWAY_REEBE_FOUND)
. += "<span class='heavy_brass'>The Ark is feeding power into the bluespace field.</span>"
if(GATEWAY_REEBE_FOUND to GATEWAY_RATVAR_COMING)
. += "<span class='heavy_brass'>The field is ripping open a copy of itself in Ratvar's prison.</span>"
if(GATEWAY_RATVAR_COMING to INFINITY)
. += "<span class='heavy_brass'>With the bluespace field established, Ratvar is preparing to come through!</span>"
. += "<span class='big'><b>Time until Ratvar's arrival:</b> [DisplayTimeText(get_arrival_time())]</span>"
switch(progress_in_seconds)
if(-INFINITY to GATEWAY_REEBE_FOUND)
. += "<span class='heavy_brass'>The Ark is feeding power into the bluespace field.</span>"
if(GATEWAY_REEBE_FOUND to GATEWAY_RATVAR_COMING)
. += "<span class='heavy_brass'>The field is ripping open a copy of itself in Ratvar's prison.</span>"
if(GATEWAY_RATVAR_COMING to INFINITY)
. += "<span class='heavy_brass'>With the bluespace field established, Ratvar is preparing to come through!</span>"
else
if(!active)
. += "<span class='warning'>Whatever it is, it doesn't seem to be active.</span>"
@@ -253,20 +241,14 @@
. += "<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()
let_slip_the_dogs()
return
if(!first_sound_played || prob(7))
for(var/mob/M in GLOB.player_list)
@@ -285,6 +267,9 @@
if(!step_away(O, src, 2) || get_dist(O, src) < 2)
O.take_damage(50, BURN, "bomb")
O.update_icon()
conversion_pulse() //Converts the nearby area into clockcult-style
for(var/V in GLOB.player_list)
var/mob/M = V
var/turf/T = get_turf(M)
@@ -292,29 +277,24 @@
M.forceMove(get_step(src, SOUTH))
M.overlay_fullscreen("flash", /obj/screen/fullscreen/flash)
M.clear_fullscreen("flash", 5)
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)
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 = 20, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_charging.ogg', TRUE))
sound_to_playing_players(volume = 10, 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)
sound_to_playing_players(volume = 25, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_active.ogg', TRUE))
sound_to_playing_players(volume = 30, 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)
sound_to_playing_players(volume = 30, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_closing.ogg', TRUE))
sound_to_playing_players(volume = 70, 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"
@@ -334,7 +314,6 @@
GLOB.clockwork_gateway_activated = TRUE
var/turf/T = SSmapping.get_station_center()
new /obj/structure/destructible/clockwork/massive/ratvar(T)
SSticker.force_ending = TRUE
var/x0 = T.x
var/y0 = T.y
for(var/I in spiral_range_turfs(255, T, tick_checked = TRUE))
@@ -349,6 +328,17 @@
T.ratvar_act(dist)
CHECK_TICK
//Converts nearby turfs into their clockwork equivalent, with ever-increasing range the closer the ark is to summoning Ratvar
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/conversion_pulse()
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
var/dist = cheap_hypotenuse(T.x, T.y, x, y)
if(dist < convert_dist)
T.ratvar_act(FALSE, TRUE, 3)
//ATTACK GHOST IGNORING PARENT RETURN VALUE
/obj/structure/destructible/clockwork/massive/celestial_gateway/attack_ghost(mob/user)
if(!IsAdminGhost(user))
@@ -361,9 +351,9 @@
if(alert(user, "You're REALLY SURE? This cannot be undone.", name, "Yes - Activate the Ark", "No") == "Yes - Activate the Ark")
message_admins("<span class='danger'>Admin [key_name_admin(user)] started the Ark's countdown!</span>")
log_admin("Admin [key_name(user)] started the Ark's countdown on a non-clockcult mode!")
to_chat(user, "<span class='userdanger'>The gamemode is now being treated as clockwork cult, and the Ark is counting down from 30 \
to_chat(user, "<span class='userdanger'>The gamemode is now being treated as clockwork cult, and the Ark is counting down from 5 \
minutes. You will need to create servant players yourself.</span>")
final_countdown(35)
final_countdown(5)
@@ -41,14 +41,19 @@
affected += try_use_power(MIN_CLOCKCULT_POWER*4)
return affected
/obj/structure/destructible/clockwork/powered/clockwork_obelisk/attack_hand(mob/living/user)
/obj/structure/destructible/clockwork/powered/clockwork_obelisk/Destroy()
for(var/obj/effect/clockwork/spatial_gateway/SG in loc)
SG.ex_act(EXPLODE_DEVASTATE)
return ..()
/obj/structure/destructible/clockwork/powered/clockwork_obelisk/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
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")
var/choice = alert(user,"You place your hand on [src]...",,"Hierophant Broadcast","Spatial Gateway","Cancel") //Will create a stable gateway instead if between two obelisks one of which is onstation and the other on reebe
switch(choice)
if("Hierophant Broadcast")
if(active)
@@ -96,7 +101,7 @@
if(!anchored)
return
var/obj/effect/clockwork/spatial_gateway/SG = locate(/obj/effect/clockwork/spatial_gateway) in loc
if(SG && SG.timerid) //it's a valid gateway, we're active
if(SG && (SG.timerid || SG.is_stable)) //it's a valid gateway, we're active
icon_state = active_icon
density = FALSE
active = TRUE
@@ -11,7 +11,7 @@
var/selection_timer //Timer ID; this is canceled if the vote is canceled
var/kingmaking
/obj/structure/destructible/clockwork/eminence_spire/attack_hand(mob/living/user)
/obj/structure/destructible/clockwork/eminence_spire/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -27,9 +27,6 @@
if(C.clock_team.eminence)
to_chat(user, "<span class='warning'>There's already an Eminence!</span>")
return
if(!GLOB.servants_active)
to_chat(user, "<span class='warning'>The Ark isn't active!</span>")
return
if(eminence_nominee) //This could be one large proc, but is split into three for ease of reading
if(eminence_nominee == user)
cancelation(user)
@@ -1,3 +1,5 @@
//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
@@ -58,7 +60,7 @@
. += "<span class='brass'>There are <b>[time_remaining]</b> second[time_remaining != 1 ? "s" : ""] remaining to vote.</span>"
. += "<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)
/obj/structure/destructible/clockwork/heralds_beacon/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -108,5 +110,4 @@
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)
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
G.recalls_remaining++
@@ -30,7 +30,7 @@
toggle()
return TRUE
/obj/structure/destructible/clockwork/powered/mania_motor/attack_hand(mob/living/user)
/obj/structure/destructible/clockwork/powered/mania_motor/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -0,0 +1,134 @@
//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/power_refund = 250
var/static/delay_cost = 2000 //Updated power values for new-newclock. Easier to activate and sustain, you are quite literally pointing the entire station towards you as opposed to blood-delay after all.
var/static/delay_cost_increase = 1000
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)
. += "<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)
. += "<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>"
. += "<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/on_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 power used.</span>")
adjust_clockwork_power(power_refund)
take_damage(max_integrity)
return 0
if(active)
return 0
var/turf/T = get_turf(src)
if(!T || !is_station_level(T.z))
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 || !is_station_level(own_turf.z))
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, 0.5)
var/mean_x = LERP(lowest_x, highest_x, 0.5)
if(prob(50))
mean_y = CEILING(mean_y, 1)
else
mean_y = FLOOR(mean_y, 1) //Yes, I know round(mean_y) does the same, just left as FLOOR for consistancy sake
if(prob(50))
mean_x = CEILING(mean_x, 1)
else
mean_x = FLOOR(mean_x, 1)
var/turf/semi_random_center_turf = locate(mean_x, mean_y, z)
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)
@@ -101,8 +101,8 @@
return
clashing = TRUE
GLOB.cult_narsie.clashing = TRUE
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>")
to_chat(world, "<span class='bold brass'><font size=5>\"[pick("YOU.", "BLOOD GOD!!", "FACE ME, COWARD!")]\"</font></span>")
to_chat(world, "<span class='bold cult'><font size=5>\"[pick("Ratvar?! How?!", "YOU. BANISHED ONCE. KILLED NOW.", "SCRAP HEAP!!")]\"</font></span>")
clash_of_the_titans(GLOB.cult_narsie) // >:(
return TRUE
@@ -137,15 +137,16 @@
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.", "ROT.")]\"</font></span>\n\
send_to_playing_players("<span class='heavy_brass'><font size=5>\"[pick("DIE.", "ROT FOR CENTURIES, AS I HAVE!.","PERISH, HEATHEN.", "DIE, MONSTER, YOU DON'T BELONG IN THIS WORLD.")]\"</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)
sound_to_playing_players('sound/magic/abomscream.ogg', 50)
clashing = FALSE
qdel(narsie)
if("Nar'Sie")
send_to_playing_players("<span class='cult'><font size=5>\"<b>[pick("Ha.", "Ra'sha fonn dest.", "You fool. To come here.")]</b>\"</font></span>") //Broken English
sound_to_playing_players('sound/magic/demon_attack1.ogg')
sound_to_playing_players('sound/magic/clockwork/anima_fragment_death.ogg', 62)
send_to_playing_players("<span class='cult'><font size=5>\"<b>[pick("Ha.", "Ra'sha fonn dest.", "You fool. To come here.")]</b>\"</font></span>\n\
<span class='heavy_brass'><font size=5>\"[pick("NO, YOUR SHADOWS SHALL NO-", "ZNL GUR FGERNZF BS GVZR PNEEL ZL RKVFG-", "MY LIGHT CANNO-")]\"</font></span>")
sound_to_playing_players('sound/magic/demon_attack1.ogg', 50)
sound_to_playing_players('sound/machines/clockcult/ratvar_scream.ogg', 80)
narsie.clashing = FALSE
qdel(src)
@@ -6,7 +6,7 @@
max_integrity = 75
icon_state = "lever"
/obj/structure/destructible/clockwork/trap/trigger/lever/attack_hand(mob/living/user)
/obj/structure/destructible/clockwork/trap/trigger/lever/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -6,7 +6,7 @@
max_integrity = 15 //Fragile!
icon_state = "repeater"
/obj/structure/destructible/clockwork/trap/trigger/repeater/attack_hand(mob/living/user)
/obj/structure/destructible/clockwork/trap/trigger/repeater/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
+9 -5
View File
@@ -34,6 +34,8 @@
w_class = WEIGHT_CLASS_SMALL
force = 15
throwforce = 25
wound_bonus = -30
bare_wound_bonus = 30
armour_penetration = 35
actions_types = list(/datum/action/item_action/cult_dagger)
@@ -51,10 +53,12 @@
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
flags_1 = CONDUCT_1
sharpness = IS_SHARP
sharpness = SHARP_EDGED
w_class = WEIGHT_CLASS_BULKY
force = 30
force = 30 // whoever balanced this got beat in the head by a bible too many times good lord
throwforce = 10
wound_bonus = -80
bare_wound_bonus = 30
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "rended")
@@ -110,7 +114,7 @@
armour_penetration = 45
throw_speed = 1
throw_range = 3
sharpness = IS_SHARP
sharpness = SHARP_EDGED
light_color = "#ff0000"
attack_verb = list("cleaved", "slashed", "torn", "hacked", "ripped", "diced", "carved")
icon_state = "cultbastard"
@@ -254,7 +258,7 @@
/datum/action/innate/cult/spin2win/Activate()
cooldown = world.time + sword.spin_cooldown
holder.changeNext_move(50)
holder.DelayNextAction(50)
holder.apply_status_effect(/datum/status_effect/sword_spin)
sword.spinning = TRUE
sword.block_chance = 100
@@ -704,7 +708,7 @@
armour_penetration = 30
block_chance = 30
attack_verb = list("attacked", "impaled", "stabbed", "torn", "gored")
sharpness = IS_SHARP
sharpness = SHARP_EDGED
hitsound = 'sound/weapons/bladeslice.ogg'
var/datum/action/innate/cult/spear/spear_act
var/wielded = FALSE // track wielded status on item
@@ -44,15 +44,16 @@
/obj/structure/destructible/cult/attack_animal(mob/living/simple_animal/M)
if(istype(M, /mob/living/simple_animal/hostile/construct/builder))
if(obj_integrity < max_integrity)
M.changeNext_move(CLICK_CD_MELEE)
M.DelayNextAction(CLICK_CD_MELEE)
obj_integrity = min(max_integrity, obj_integrity + 5)
Beam(M, icon_state="sendbeam", time=4)
M.visible_message("<span class='danger'>[M] repairs \the <b>[src]</b>.</span>", \
"<span class='cult'>You repair <b>[src]</b>, leaving [p_they()] at <b>[round(obj_integrity * 100 / max_integrity)]%</b> stability.</span>")
return TRUE
else
to_chat(M, "<span class='cult'>You cannot repair [src], as [p_theyre()] undamaged!</span>")
else
..()
return ..()
/obj/structure/destructible/cult/attackby(obj/I, mob/user, params)
if(istype(I, /obj/item/melee/cultblade/dagger) && iscultist(user))
+1 -1
View File
@@ -67,7 +67,7 @@ Runes can either be invoked by one's self or with many different cultists. Each
to_chat(user, "<span class='danger'>You disrupt the magic of [src] with [I].</span>")
qdel(src)
/obj/effect/rune/attack_hand(mob/living/user)
/obj/effect/rune/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
+1
View File
@@ -92,6 +92,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
//Don't delete upon mind destruction, otherwise soul re-selling will break.
delete_on_mind_deletion = FALSE
threat = 5
show_to_ghosts = TRUE
var/obligation
var/ban
var/bane
@@ -144,7 +144,7 @@
/mob/living/carbon/true_devil/resist_fire()
//They're immune to fire.
/mob/living/carbon/true_devil/attack_hand(mob/living/carbon/human/M)
/mob/living/carbon/true_devil/on_attack_hand(mob/living/carbon/human/M)
. = ..()
if(.)
switch(M.a_intent)
@@ -2,6 +2,7 @@
name = "Sentient Disease"
roundend_category = "diseases"
antagpanel_category = "Disease"
show_to_ghosts = TRUE
var/disease_name = ""
/datum/antagonist/disease/on_gain()
@@ -291,16 +291,19 @@ the new instance inside the host to be updated to the template's stats.
/mob/camera/disease/ClickOn(var/atom/A, params)
if(freemove && ishuman(A))
var/mob/living/carbon/human/H = A
if(alert(src, "Select [H.name] as your initial host?", "Select Host", "Yes", "No") != "Yes")
return
if(!freemove)
return
if(QDELETED(H) || !force_infect(H))
to_chat(src, "<span class='warning'>[H ? H.name : "Host"] cannot be infected.</span>")
confirm_initial_infection(A)
else
..()
/mob/camera/disease/proc/confirm_initial_infection(mob/living/carbon/human/H)
set waitfor = FALSE
if(alert(src, "Select [H.name] as your initial host?", "Select Host", "Yes", "No") != "Yes")
return
if(!freemove)
return
if(QDELETED(H) || !force_infect(H))
to_chat(src, "<span class='warning'>[H ? H.name : "Host"] cannot be infected.</span>")
/mob/camera/disease/proc/adapt_cooldown()
to_chat(src, "<span class='notice'>You have altered your genetic structure. You will be unable to adapt again for [DisplayTimeText(adaptation_cooldown)].</span>")
next_adaptation_time = world.time + adaptation_cooldown
+1
View File
@@ -12,6 +12,7 @@
var/list/name_source
threat = -5
show_in_antagpanel = FALSE
show_to_ghosts = TRUE
antag_moodlet = /datum/mood_event/focused
/datum/antagonist/ert/on_gain()
@@ -9,6 +9,7 @@
roundend_category = "monkeys"
antagpanel_category = "Monkey"
threat = 3
show_to_ghosts = TRUE
var/datum/team/monkey/monkey_team
var/monkey_only = TRUE
@@ -3,3 +3,4 @@
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
threat = 5
show_to_ghosts = TRUE
+1
View File
@@ -3,6 +3,7 @@
antagpanel_category = "Ninja"
job_rank = ROLE_NINJA
show_name_in_check_antagonists = TRUE
show_to_ghosts = TRUE
antag_moodlet = /datum/mood_event/focused
threat = 8
var/helping_station = FALSE
@@ -8,13 +8,12 @@
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
var/timer_set = 90
var/default_timer_set = 90
var/minimum_timer_set = 90
var/maximum_timer_set = 3600
ui_style = "nanotrasen"
var/numeric_input = ""
var/ui_mode = NUKEUI_AWAIT_DISK
var/timing = FALSE
var/exploding = FALSE
var/exploded = FALSE
@@ -31,7 +30,6 @@
var/interior = ""
var/proper_bomb = TRUE //Please
var/obj/effect/countdown/nuclearbomb/countdown
var/nuclear_cooldown //used to stop global spam.
/obj/machinery/nuclearbomb/Initialize()
. = ..()
@@ -74,15 +72,16 @@
/obj/machinery/nuclearbomb/syndicate/get_cinematic_type(off_station)
var/datum/game_mode/nuclear/NM = SSticker.mode
switch(off_station)
if(FALSE)
if(0)
if(istype(NM) && !NM.nuke_team.syndies_escaped())
return CINEMATIC_ANNIHILATION
else
return CINEMATIC_NUKE_WIN
if(NUKE_MISS_STATION)
if(1)
return CINEMATIC_NUKE_MISS
else
if(2)
return CINEMATIC_NUKE_FAR
return CINEMATIC_NUKE_FAR
/obj/machinery/nuclearbomb/proc/disk_check(obj/item/disk/nuclear/D)
if(D.fake)
@@ -191,7 +190,7 @@
icon_state = "nuclearbomb_exploding"
/obj/machinery/nuclearbomb/update_overlays()
. = ..()
. += ..()
update_icon_interior()
update_icon_lights()
@@ -233,7 +232,7 @@
explode()
else
var/volume = (get_time_left() <= 20 ? 30 : 5)
playsound(loc, 'sound/items/timer.ogg', volume, 0)
playsound(loc, 'sound/items/timer.ogg', volume, FALSE)
/obj/machinery/nuclearbomb/proc/update_ui_mode()
if(exploded)
@@ -258,18 +257,18 @@
ui_mode = NUKEUI_AWAIT_TIMER
/obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key="main", datum/tgui/ui=null, force_open=0, datum/tgui/master_ui=null, datum/ui_state/state=GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/nuclearbomb/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "nuclear_bomb", name, 350, 442, master_ui, state)
ui.set_style(ui_style)
ui = new(user, src, "NuclearBomb", name)
ui.open()
/obj/machinery/nuclearbomb/ui_data(mob/user)
var/list/data = list()
data["disk_present"] = auth
var/hidden_code = (ui_mode == NUKEUI_AWAIT_CODE && numeric_input != "ERROR")
var/current_code = ""
if(hidden_code)
while(length(current_code) < length(numeric_input))
@@ -386,14 +385,13 @@
if("anchor")
if(auth && yes_code)
playsound(src, 'sound/machines/nuke/general_beep.ogg', 50, FALSE)
set_anchor(usr)
set_anchor()
else
playsound(src, 'sound/machines/nuke/angry_beep.ogg', 50, FALSE)
/obj/machinery/nuclearbomb/proc/set_anchor(mob/user)
if((istype(get_area(src), /area/space) || isinspace()) && !anchored)
to_chat(user, "<span class='warning'>This is not a suitable platform for anchoring [src]!</span>")
/obj/machinery/nuclearbomb/proc/set_anchor()
if(isinspace() && !anchored)
to_chat(usr, "<span class='warning'>There is nothing to anchor to!</span>")
else
anchored = !anchored
@@ -414,9 +412,6 @@
if(safety)
to_chat(usr, "<span class='danger'>The safety is still on.</span>")
return
if(!timing && nuclear_cooldown > world.time)
to_chat(usr, "<span class='danger'>[src]'s timer protocols are currently on cooldown, please stand by.</span>")
return
timing = !timing
if(timing)
previous_level = NUM2SECLEVEL(GLOB.security_level)
@@ -425,12 +420,6 @@
S.switch_mode_to(TRACK_INFILTRATOR)
countdown.start()
set_security_level("delta")
nuclear_cooldown = world.time + 15 SECONDS
if(GLOB.war_declared)
var/area/A = get_area(src)
priority_announce("Alert: Unexpected increase in radiation levels near [A.name] ([src.x],[src.y],[src.z]). Please send an authorized radiation specialist to investigate.", "Sensory Nuclear Indexer Telemetry Calculation Helper")
else
detonation_timer = null
set_security_level(previous_level)
@@ -481,12 +470,19 @@
var/off_station = FALSE
var/turf/bomb_location = get_turf(src)
if(!bomb_location || !is_station_level(bomb_location.z))
off_station = NUKE_MISS_STATION
var/area/A = get_area(bomb_location)
if(bomb_location && is_station_level(bomb_location.z))
if(istype(A, /area/space))
off_station = NUKE_NEAR_MISS
if((bomb_location.x < (128-NUKERANGE)) || (bomb_location.x > (128+NUKERANGE)) || (bomb_location.y < (128-NUKERANGE)) || (bomb_location.y > (128+NUKERANGE)))
off_station = NUKE_NEAR_MISS
else if(bomb_location.onSyndieBase())
off_station = NUKE_SYNDICATE_BASE
else
off_station = NUKE_MISS_STATION
if(!off_station)
if(off_station < 2) //can only launch when nuke is on syndie base or space
SSshuttle.registerHostileEnvironment(src)
SSshuttle.lockdown = TRUE
@@ -500,13 +496,13 @@
INVOKE_ASYNC(GLOBAL_PROC,.proc/KillEveryoneOnZLevel, z)
/obj/machinery/nuclearbomb/proc/get_cinematic_type(off_station)
if(!off_station)
if(off_station < 2)
return CINEMATIC_SELFDESTRUCT
else
return CINEMATIC_SELFDESTRUCT_MISS
/obj/machinery/nuclearbomb/beer
name = "Nanotrasen-brand nuclear fission explosive"
name = "\improper Nanotrasen-brand nuclear fission explosive"
desc = "One of the more successful achievements of the Nanotrasen Corporate Warfare Division, their nuclear fission explosives are renowned for being cheap to produce and devastatingly effective. Signs explain that though this particular device has been decommissioned, every Nanotrasen station is equipped with an equivalent one, just in case. All Captains carefully guard the disk needed to detonate them - at least, the sign says they do. There seems to be a tap on the back."
proper_bomb = FALSE
var/obj/structure/reagent_dispensers/beerkeg/keg
@@ -519,9 +515,9 @@
/obj/machinery/nuclearbomb/beer/examine(mob/user)
. = ..()
if(keg.reagents.total_volume)
. += "<span class='notice'>It has [keg.reagents.total_volume] unit\s left.</span>"
to_chat(user, "<span class='notice'>It has [keg.reagents.total_volume] unit\s left.</span>")
else
. += "<span class='danger'>It's empty.</span>"
to_chat(user, "<span class='danger'>It's empty.</span>")
/obj/machinery/nuclearbomb/beer/attackby(obj/item/W, mob/user, params)
if(W.is_refillable())
@@ -533,6 +529,8 @@
return ..()
/obj/machinery/nuclearbomb/beer/actually_explode()
//Unblock roundend, we're not actually exploding.
SSticker.roundend_check_paused = FALSE
var/turf/bomb_location = get_turf(src)
if(!bomb_location)
disarm()
@@ -581,7 +579,7 @@
This is here to make the tiles around the station mininuke change when it's armed.
*/
/obj/machinery/nuclearbomb/selfdestruct/set_anchor(mob/user)
/obj/machinery/nuclearbomb/selfdestruct/set_anchor()
return
/obj/machinery/nuclearbomb/selfdestruct/set_active()
@@ -639,18 +637,19 @@ This is here to make the tiles around the station mininuke change when it's arme
if(newturf && lastlocation == newturf)
if(last_disk_move < world.time - 5000 && prob((world.time - 5000 - last_disk_move)*0.0001))
var/datum/round_event_control/operative/loneop = locate(/datum/round_event_control/operative) in SSevents.control
if(istype(loneop))
if(istype(loneop) && loneop.occurrences < loneop.max_occurrences)
loneop.weight += 1
if(loneop.weight % 5 == 0)
if(loneop.weight % 5 == 0 && SSticker.totalPlayers > 1) //players count now
message_admins("[src] is stationary in [ADMIN_VERBOSEJMP(newturf)]. The weight of Lone Operative is now [loneop.weight].")
log_game("[src] is stationary for too long in [loc_name(newturf)], and has increased the weight of the Lone Operative event to [loneop.weight].")
else
lastlocation = newturf
last_disk_move = world.time
var/datum/round_event_control/operative/loneop = locate(/datum/round_event_control/operative) in SSevents.control
if(istype(loneop) && prob(loneop.weight))
if(istype(loneop) && loneop.occurrences < loneop.max_occurrences && prob(loneop.weight))
loneop.weight = max(loneop.weight - 1, 0)
if(loneop.weight % 5 == 0)
if(loneop.weight % 5 == 0 && SSticker.totalPlayers > 1)
message_admins("[src] is on the move (currently in [ADMIN_VERBOSEJMP(newturf)]). The weight of Lone Operative is now [loneop.weight].")
log_game("[src] being on the move has reduced the weight of the Lone Operative event to [loneop.weight].")
@@ -659,9 +658,19 @@ This is here to make the tiles around the station mininuke change when it's arme
if(!fake)
return
if(isobserver(user) || HAS_TRAIT(user, TRAIT_DISK_VERIFIER) || (user.mind && HAS_TRAIT(user.mind, TRAIT_DISK_VERIFIER)))
if(isobserver(user) || HAS_TRAIT(user.mind, TRAIT_DISK_VERIFIER))
. += "<span class='warning'>The serial numbers on [src] are incorrect.</span>"
/*
* You can't accidentally eat the nuke disk, bro
*/
/*
/obj/item/disk/nuclear/on_accidental_consumption(mob/living/carbon/M, mob/living/carbon/user, obj/item/source_item, discover_after = TRUE)
M.visible_message("<span class='warning'>[M] looks like [M.p_theyve()] just bitten into something important.</span>", \
"<span class='warning'>Wait, is this the nuke disk?</span>")
return discover_after
*/
/obj/item/disk/nuclear/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/claymore/highlander) && !fake)
var/obj/item/claymore/highlander/H = I
@@ -684,7 +693,7 @@ This is here to make the tiles around the station mininuke change when it's arme
/obj/item/disk/nuclear/suicide_act(mob/user)
user.visible_message("<span class='suicide'>[user] is going delta! It looks like [user.p_theyre()] trying to commit suicide!</span>")
playsound(src, 'sound/machines/alarm.ogg', 50, -1, 1)
playsound(src, 'sound/machines/alarm.ogg', 50, -1, TRUE)
for(var/i in 1 to 100)
addtimer(CALLBACK(user, /atom/proc/add_atom_colour, (i % 2)? "#00FF00" : "#FF0000", ADMIN_COLOUR_PRIORITY), i)
addtimer(CALLBACK(src, .proc/manual_suicide, user), 101)
@@ -692,7 +701,7 @@ This is here to make the tiles around the station mininuke change when it's arme
/obj/item/disk/nuclear/proc/manual_suicide(mob/living/user)
user.remove_atom_colour(ADMIN_COLOUR_PRIORITY)
user.visible_message("<span class='suicide'>[user] was destroyed by the nuclear blast!</span>")
user.visible_message("<span class='suicide'>[user] is destroyed by the nuclear blast!</span>")
user.adjustOxyLoss(200)
user.death(0)
@@ -6,6 +6,7 @@
antag_moodlet = /datum/mood_event/focused
threat = 10
skill_modifiers = list(/datum/skill_modifier/job/level/wiring)
show_to_ghosts = TRUE
var/datum/team/nuclear/nuke_team
var/always_new_team = FALSE //If not assigned a team by default ops will try to join existing ones, set this to TRUE to always create new team.
var/send_to_spawnpoint = TRUE //Should the user be moved to default spawnpoint.
@@ -4,6 +4,7 @@
show_in_antagpanel = FALSE
var/datum/objective/mission
var/datum/team/ert/ert_team
show_to_ghosts = TRUE
/datum/antagonist/official/greet()
to_chat(owner, "<B><font size=3 color=red>You are a CentCom Official.</font></B>")
@@ -4,6 +4,7 @@
roundend_category = "space pirates"
antagpanel_category = "Pirate"
threat = 5
show_to_ghosts = TRUE
var/datum/team/pirate/crew
/datum/antagonist/pirate/greet()
@@ -28,6 +28,7 @@
throwforce = 0
blood_volume = 0
has_field_of_vision = FALSE //we are a spoopy ghost
rad_flags = RAD_NO_CONTAMINATE | RAD_PROTECT_CONTENTS
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
@@ -112,7 +113,7 @@
if(stasis)
return
if(revealed && essence <= 0)
death()
INVOKE_ASYNC(src, .proc/death)
if(unreveal_time && world.time >= unreveal_time)
unreveal_time = 0
revealed = FALSE
@@ -17,6 +17,7 @@
//Harvest; activated ly clicking the target, will try to drain their essence.
/mob/living/simple_animal/revenant/proc/Harvest(mob/living/carbon/human/target)
set waitfor = FALSE
if(!castcheck(0))
return
if(draining)
@@ -3,6 +3,7 @@
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
threat = 5
show_to_ghosts = TRUE
/datum/antagonist/revenant/greet()
owner.announce_objectives()
+2
View File
@@ -1,6 +1,8 @@
/datum/antagonist/santa
name = "Santa"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
show_to_ghosts = TRUE
/datum/antagonist/santa/on_gain()
. = ..()
@@ -34,8 +34,11 @@
healable = 0
environment_smash = ENVIRONMENT_SMASH_STRUCTURES
obj_damage = 50
melee_damage_lower = 30
melee_damage_upper = 30
melee_damage_lower = 22.5 // reduced from 30 to 22.5 with wounds since they get big buffs to slicing wounds
melee_damage_upper = 22.5
wound_bonus = -10
bare_wound_bonus = 0
sharpness = SHARP_EDGED
see_in_dark = 8
blood_volume = 0 //No bleeding on getting shot, for skeddadles
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
@@ -43,13 +46,25 @@
var/playstyle_string = "<span class='big bold'>You are a slaughter demon,</span><B> a terrible creature from another realm. You have a single desire: To kill. \
You may use the \"Blood Crawl\" ability near blood pools to travel through them, appearing and disappearing from the station at will. \
Pulling a dead or unconscious mob while you enter a pool will pull them in with you, allowing you to feast and regain your health. \
You move quickly upon leaving a pool of blood, but the material world will soon sap your strength and leave you sluggish. </B>"
You move quickly upon leaving a pool of blood, but the material world will soon sap your strength and leave you sluggish. \
You gain strength the more attacks you land on live humanoids, though this resets when you return to the blood zone. You can also \
launch a devastating slam attack with ctrl+shift+click, capable of smashing bones in one strike.</B>"
loot = list(/obj/effect/decal/cleanable/blood, \
/obj/effect/decal/cleanable/blood/innards, \
/obj/item/organ/heart/demon)
del_on_death = 1
deathmessage = "screams in anger as it collapses into a puddle of viscera!"
// How long it takes for the alt-click slam attack to come off cooldown
var/slam_cooldown_time = 45 SECONDS
// The actual instance var for the cooldown
var/slam_cooldown = 0
// How many times we have hit humanoid targets since we last bloodcrawled, scaling wounding power
var/current_hitstreak = 0
// How much both our wound_bonus and bare_wound_bonus go up per hitstreak hit
var/wound_bonus_per_hit = 5
// How much our wound_bonus hitstreak bonus caps at (peak demonry)
var/wound_bonus_hitstreak_max = 12
/mob/living/simple_animal/slaughter/Initialize()
..()
@@ -58,6 +73,33 @@
if(istype(loc, /obj/effect/dummy/phased_mob/slaughter))
bloodspell.phased = TRUE
/mob/living/simple_animal/slaughter/CtrlShiftClickOn(atom/A)
if(!isliving(A))
return ..()
if(slam_cooldown + slam_cooldown_time > world.time)
to_chat(src, "<span class='warning'>Your slam ability is still on cooldown!</span>")
return
face_atom(A)
var/mob/living/victim = A
victim.take_bodypart_damage(brute=20, wound_bonus=wound_bonus) // don't worry, there's more punishment when they hit something
visible_message("<span class='danger'>[src] slams into [victim] with monstrous strength!</span>", "<span class='danger'>You slam into [victim] with monstrous strength!</span>", ignored_mobs=victim)
to_chat(victim, "<span class='userdanger'>[src] slams into you with monstrous strength, sending you flying like a ragdoll!</span>")
var/turf/yeet_target = get_edge_target_turf(victim, dir)
victim.throw_at(yeet_target, 10, 5, src)
slam_cooldown = world.time
log_combat(src, victim, "slaughter slammed")
/mob/living/simple_animal/slaughter/UnarmedAttack(atom/A, proximity)
if(iscarbon(A))
var/mob/living/carbon/target = A
if(target.stat != DEAD && target.mind && current_hitstreak < wound_bonus_hitstreak_max)
current_hitstreak++
wound_bonus += wound_bonus_per_hit
bare_wound_bonus += wound_bonus_per_hit
return ..()
/obj/effect/decal/cleanable/blood/innards
icon = 'icons/obj/surgery.dmi'
name = "pile of viscera"
@@ -6,6 +6,7 @@
threat = 10
job_rank = ROLE_ALIEN
show_in_antagpanel = FALSE
show_to_ghosts = TRUE
/datum/antagonist/slaughter/on_gain()
forge_objectives()
@@ -14,6 +15,7 @@
/datum/antagonist/slaughter/greet()
. = ..()
owner.announce_objectives()
to_chat(owner, "<span class='warning'>You have a powerful alt-attack that slams people backwards that you can activate by shift+ctrl+clicking your target!</span>")
/datum/antagonist/slaughter/proc/forge_objectives()
if(summoner)
@@ -23,6 +23,18 @@
/datum/antagonist/survivalist/guns
greet_message = "Your own safety matters above all else, and the only way to ensure your safety is to stockpile weapons! Grab as many guns as possible, and don't let anyone take them!"
/datum/antagonist/survivalist/guns/forge_objectives()
var/datum/objective/steal_five_of_type/summon_guns/guns = new
guns.owner = owner
objectives += guns
..()
/datum/antagonist/survivalist/magic
name = "Amateur Magician"
greet_message = "This magic stuff is... so powerful. You want more. More! They want your power. They can't have it! Don't let them have it!"
/datum/antagonist/survivalist/magic/forge_objectives()
var/datum/objective/steal_five_of_type/summon_magic/magic = new
magic.owner = owner
objectives += magic
..()
+7 -7
View File
@@ -36,7 +36,7 @@
if(A)
notify_ghosts("A swarmer shell has been created in [A.name].", 'sound/effects/bin_close.ogg', source = src, action = NOTIFY_ATTACK, flashwindow = FALSE, ignore_dnr_observers = TRUE)
/obj/effect/mob_spawn/swarmer/attack_hand(mob/living/user)
/obj/effect/mob_spawn/swarmer/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -158,10 +158,11 @@
face_atom(A)
if(!isturf(loc))
return
if(next_move > world.time)
if(!CheckActionCooldown())
return
if(!A.Adjacent(src))
return
DelayNextAction()
A.swarmer_act(src)
/atom/proc/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
@@ -399,13 +400,13 @@
return FALSE
/obj/structure/lattice/catwalk/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
. = ..()
var/turf/here = get_turf(src)
for(var/A in here.contents)
var/obj/structure/cable/C = A
if(istype(C))
to_chat(S, "<span class='warning'>Disrupting the power grid would bring no benefit to us. Aborting.</span>")
return FALSE
return ..()
/obj/item/deactivated_swarmer/IntegrateAmount()
return 50
@@ -486,7 +487,7 @@
var/obj/O = target
if(O.resistance_flags & INDESTRUCTIBLE)
return FALSE
for(var/mob/living/L in GetAllContents())
for(var/mob/living/L in target.GetAllContents())
if(!ispAI(L) && !isbrain(L))
to_chat(src, "<span class='warning'>An organism has been detected inside this object. Aborting.</span>")
return FALSE
@@ -497,7 +498,7 @@
if(resource_gain)
resources += resource_gain
do_attack_animation(target)
changeNext_move(CLICK_CD_MELEE)
DelayNextAction(CLICK_CD_MELEE)
var/obj/effect/temp_visual/swarmer/integrate/I = new /obj/effect/temp_visual/swarmer/integrate(get_turf(target))
I.pixel_x = target.pixel_x
I.pixel_y = target.pixel_y
@@ -517,10 +518,9 @@
/mob/living/simple_animal/hostile/swarmer/proc/DisIntegrate(atom/movable/target)
new /obj/effect/temp_visual/swarmer/disintegration(get_turf(target))
do_attack_animation(target)
changeNext_move(CLICK_CD_MELEE)
DelayNextAction(CLICK_CD_MELEE)
target.ex_act(EXPLODE_LIGHT)
/mob/living/simple_animal/hostile/swarmer/proc/DisperseTarget(mob/living/target)
if(target == src)
return

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