Tg 1 28 sync testing/confirmation (#5178)

* maps, tgui, tools

* defines and helpers

* onclick and controllers

* datums

fucking caught that hulk reversal too.

* game and shuttle modular

* module/admin

* oh god they fucking moved antag shit again

* haaaaate. Haaaaaaaaaate.

* enables moff wings

* more modules things

* tgstation.dme

before I forget something important

* some mob stuff

* s'more mob/living stuff

* some carbon stuff

* ayy lmaos and kitchen meat

* Human stuff

* species things

moff wings have a 'none' version too

* the rest of the module stuff.

* some strings

* misc

* mob icons

* some other icons.

* It compiles FUCK BORERS

FUCK BORERS
This commit is contained in:
Poojawa
2018-01-29 04:42:29 -06:00
committed by GitHub
parent 89fa4b0f28
commit 03086dfa91
666 changed files with 27177 additions and 35945 deletions
-53
View File
@@ -1,53 +0,0 @@
/datum/atom_hud/antag
hud_icons = list(ANTAG_HUD)
var/self_visible = TRUE
/datum/atom_hud/antag/hidden
self_visible = FALSE
/datum/atom_hud/antag/proc/join_hud(mob/M)
//sees_hud should be set to 0 if the mob does not get to see it's own hud type.
if(!istype(M))
CRASH("join_hud(): [M] ([M.type]) is not a mob!")
if(M.mind.antag_hud) //note: please let this runtime if a mob has no mind, as mindless mobs shouldn't be getting antagged
M.mind.antag_hud.leave_hud(M)
add_to_hud(M)
if(self_visible)
add_hud_to(M)
M.mind.antag_hud = src
/datum/atom_hud/antag/proc/leave_hud(mob/M)
if(!M)
return
if(!istype(M))
CRASH("leave_hud(): [M] ([M.type]) is not a mob!")
remove_from_hud(M)
remove_hud_from(M)
if(M.mind)
M.mind.antag_hud = null
//GAME_MODE PROCS
//called to set a mob's antag icon state
/proc/set_antag_hud(mob/M, new_icon_state)
if(!istype(M))
CRASH("set_antag_hud(): [M] ([M.type]) is not a mob!")
var/image/holder = M.hud_list[ANTAG_HUD]
if(holder)
holder.icon_state = new_icon_state
if(M.mind || new_icon_state) //in mindless mobs, only null is acceptable, otherwise we're antagging a mindless mob, meaning we should runtime
M.mind.antag_hud_icon_state = new_icon_state
//MIND PROCS
//these are called by mind.transfer_to()
/datum/mind/proc/transfer_antag_huds(datum/atom_hud/antag/newhud)
leave_all_antag_huds()
set_antag_hud(current, antag_hud_icon_state)
if(newhud)
newhud.join_hud(current)
/datum/mind/proc/leave_all_antag_huds()
for(var/datum/atom_hud/antag/hud in GLOB.huds)
if(hud.hudusers[current])
hud.leave_hud(current)
-266
View File
@@ -1,266 +0,0 @@
/obj/item/antag_spawner
throw_speed = 1
throw_range = 5
w_class = WEIGHT_CLASS_TINY
var/used = 0
/obj/item/antag_spawner/proc/spawn_antag(client/C, turf/T, kind = "", datum/mind/user)
return
/obj/item/antag_spawner/proc/equip_antag(mob/target)
return
///////////WIZARD
/obj/item/antag_spawner/contract
name = "contract"
desc = "A magic contract previously signed by an apprentice. In exchange for instruction in the magical arts, they are bound to answer your call for aid."
icon = 'icons/obj/wizard.dmi'
icon_state ="scroll2"
/obj/item/antag_spawner/contract/attack_self(mob/user)
user.set_machine(src)
var/dat
if(used)
dat = "<B>You have already summoned your apprentice.</B><BR>"
else
dat = "<B>Contract of Apprenticeship:</B><BR>"
dat += "<I>Using this contract, you may summon an apprentice to aid you on your mission.</I><BR>"
dat += "<I>If you are unable to establish contact with your apprentice, you can feed the contract back to the spellbook to refund your points.</I><BR>"
dat += "<B>Which school of magic is your apprentice studying?:</B><BR>"
dat += "<A href='byond://?src=[REF(src)];school=[APPRENTICE_DESTRUCTION]'>Destruction</A><BR>"
dat += "<I>Your apprentice is skilled in offensive magic. They know Magic Missile and Fireball.</I><BR>"
dat += "<A href='byond://?src=[REF(src)];school=[APPRENTICE_BLUESPACE]'>Bluespace Manipulation</A><BR>"
dat += "<I>Your apprentice is able to defy physics, melting through solid objects and travelling great distances in the blink of an eye. They know Teleport and Ethereal Jaunt.</I><BR>"
dat += "<A href='byond://?src=[REF(src)];school=[APPRENTICE_HEALING]'>Healing</A><BR>"
dat += "<I>Your apprentice is training to cast spells that will aid your survival. They know Forcewall and Charge and come with a Staff of Healing.</I><BR>"
dat += "<A href='byond://?src=[REF(src)];school=[APPRENTICE_ROBELESS]'>Robeless</A><BR>"
dat += "<I>Your apprentice is training to cast spells without their robes. They know Knock and Mindswap.</I><BR>"
user << browse(dat, "window=radio")
onclose(user, "radio")
return
/obj/item/antag_spawner/contract/Topic(href, href_list)
..()
var/mob/living/carbon/human/H = usr
if(H.stat || H.restrained())
return
if(!ishuman(H))
return 1
if(loc == H || (in_range(src, H) && isturf(loc)))
H.set_machine(src)
if(href_list["school"])
if(used)
to_chat(H, "You already used this contract!")
return
var/list/candidates = pollCandidatesForMob("Do you want to play as a wizard's [href_list["school"]] apprentice?", ROLE_WIZARD, null, ROLE_WIZARD, 150, src)
if(candidates.len)
if(used)
to_chat(H, "You already used this contract!")
return
used = 1
var/mob/dead/observer/theghost = pick(candidates)
spawn_antag(theghost.client, get_turf(src), href_list["school"],H.mind)
else
to_chat(H, "Unable to reach your apprentice! You can either attack the spellbook with the contract to refund your points, or wait and try again later.")
/obj/item/antag_spawner/contract/spawn_antag(client/C, turf/T, kind ,datum/mind/user)
new /obj/effect/particle_effect/smoke(T)
var/mob/living/carbon/human/M = new/mob/living/carbon/human(T)
C.prefs.copy_to(M)
M.key = C.key
var/datum/mind/app_mind = M.mind
var/datum/antagonist/wizard/apprentice/app = new()
app.master = user
app.school = kind
var/datum/antagonist/wizard/master_wizard = user.has_antag_datum(/datum/antagonist/wizard)
if(master_wizard)
if(!master_wizard.wiz_team)
master_wizard.create_wiz_team()
app.wiz_team = master_wizard.wiz_team
master_wizard.wiz_team.add_member(app_mind)
app_mind.add_antag_datum(app)
//TODO Kill these if possible
app_mind.assigned_role = "Apprentice"
app_mind.special_role = "apprentice"
//
SEND_SOUND(M, sound('sound/effects/magic.ogg'))
///////////BORGS AND OPERATIVES
/obj/item/antag_spawner/nuke_ops
name = "syndicate operative teleporter"
desc = "A single-use teleporter designed to quickly reinforce operatives in the field."
icon = 'icons/obj/device.dmi'
icon_state = "locator"
var/borg_to_spawn
/obj/item/antag_spawner/nuke_ops/proc/check_usability(mob/user)
if(used)
to_chat(user, "<span class='warning'>[src] is out of power!</span>")
return FALSE
if(!user.mind.has_antag_datum(/datum/antagonist/nukeop,TRUE))
to_chat(user, "<span class='danger'>AUTHENTICATION FAILURE. ACCESS DENIED.</span>")
return FALSE
if(!user.onSyndieBase())
to_chat(user, "<span class='warning'>[src] is out of range! It can only be used at your base!</span>")
return FALSE
return TRUE
/obj/item/antag_spawner/nuke_ops/attack_self(mob/user)
if(!(check_usability(user)))
return
to_chat(user, "<span class='notice'>You activate [src] and wait for confirmation.</span>")
var/list/nuke_candidates = pollGhostCandidates("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", ROLE_OPERATIVE, null, ROLE_OPERATIVE, 150, POLL_IGNORE_SYNDICATE)
if(nuke_candidates.len)
if(!(check_usability(user)))
return
used = TRUE
var/mob/dead/observer/theghost = pick(nuke_candidates)
spawn_antag(theghost.client, get_turf(src), "syndieborg", user.mind)
do_sparks(4, TRUE, src)
qdel(src)
else
to_chat(user, "<span class='warning'>Unable to connect to Syndicate command. Please wait and try again later or use the teleporter on your uplink to get your points refunded.</span>")
/obj/item/antag_spawner/nuke_ops/spawn_antag(client/C, turf/T, kind, datum/mind/user)
var/mob/living/carbon/human/M = new/mob/living/carbon/human(T)
C.prefs.copy_to(M)
M.key = C.key
var/datum/antagonist/nukeop/new_op = new()
new_op.send_to_spawnpoint = FALSE
new_op.nukeop_outfit = /datum/outfit/syndicate/no_crystals
var/datum/antagonist/nukeop/creator_op = user.has_antag_datum(/datum/antagonist/nukeop,TRUE)
if(creator_op)
M.mind.add_antag_datum(new_op,creator_op.nuke_team)
M.mind.special_role = "Nuclear Operative"
//////SYNDICATE BORG
/obj/item/antag_spawner/nuke_ops/borg_tele
name = "syndicate cyborg teleporter"
desc = "A single-use teleporter designed to quickly reinforce operatives in the field.."
icon = 'icons/obj/device.dmi'
icon_state = "locator"
/obj/item/antag_spawner/nuke_ops/borg_tele/assault
name = "syndicate assault cyborg teleporter"
borg_to_spawn = "Assault"
/obj/item/antag_spawner/nuke_ops/borg_tele/medical
name = "syndicate medical teleporter"
borg_to_spawn = "Medical"
/obj/item/antag_spawner/nuke_ops/borg_tele/spawn_antag(client/C, turf/T, kind, datum/mind/user)
var/mob/living/silicon/robot/R
var/datum/antagonist/nukeop/creator_op = user.has_antag_datum(/datum/antagonist/nukeop,TRUE)
if(!creator_op)
return
switch(borg_to_spawn)
if("Medical")
R = new /mob/living/silicon/robot/modules/syndicate/medical(T)
else
R = new /mob/living/silicon/robot/modules/syndicate(T) //Assault borg by default
var/brainfirstname = pick(GLOB.first_names_male)
if(prob(50))
brainfirstname = pick(GLOB.first_names_female)
var/brainopslastname = pick(GLOB.last_names)
if(creator_op.nuke_team.syndicate_name) //the brain inside the syndiborg has the same last name as the other ops.
brainopslastname = creator_op.nuke_team.syndicate_name
var/brainopsname = "[brainfirstname] [brainopslastname]"
R.mmi.name = "Man-Machine Interface: [brainopsname]"
R.mmi.brain.name = "[brainopsname]'s brain"
R.mmi.brainmob.real_name = brainopsname
R.mmi.brainmob.name = brainopsname
R.real_name = R.name
R.key = C.key
var/datum/antagonist/nukeop/new_borg = new()
new_borg.send_to_spawnpoint = FALSE
R.mind.add_antag_datum(new_borg,creator_op.nuke_team)
R.mind.special_role = "Syndicate Cyborg"
///////////SLAUGHTER DEMON
/obj/item/antag_spawner/slaughter_demon //Warning edgiest item in the game
name = "vial of blood"
desc = "A magically infused bottle of blood, distilled from countless murder victims. Used in unholy rituals to attract horrifying creatures."
icon = 'icons/obj/wizard.dmi'
icon_state = "vial"
var/shatter_msg = "<span class='notice'>You shatter the bottle, no turning back now!</span>"
var/veil_msg = "<span class='warning'>You sense a dark presence lurking just beyond the veil...</span>"
var/objective_verb = "Kill"
var/mob/living/demon_type = /mob/living/simple_animal/slaughter
/obj/item/antag_spawner/slaughter_demon/attack_self(mob/user)
if(!is_station_level(user.z))
to_chat(user, "<span class='notice'>You should probably wait until you reach the station.</span>")
return
if(used)
return
var/list/demon_candidates = pollCandidatesForMob("Do you want to play as a [initial(demon_type.name)]?", null, null, ROLE_ALIEN, 50, src)
if(demon_candidates.len)
if(used)
return
used = 1
var/mob/dead/observer/theghost = pick(demon_candidates)
spawn_antag(theghost.client, get_turf(src), initial(demon_type.name),user.mind)
to_chat(user, shatter_msg)
to_chat(user, veil_msg)
playsound(user.loc, 'sound/effects/glassbr1.ogg', 100, 1)
qdel(src)
else
to_chat(user, "<span class='notice'>You can't seem to work up the nerve to shatter the bottle. Perhaps you should try again later.</span>")
/obj/item/antag_spawner/slaughter_demon/spawn_antag(client/C, turf/T, kind = "", datum/mind/user)
var/obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(T)
var/mob/living/simple_animal/slaughter/S = new demon_type(holder)
S.holder = holder
S.key = C.key
S.mind.assigned_role = S.name
S.mind.special_role = S.name
var/datum/objective/assassinate/new_objective
if(user)
new_objective = new /datum/objective/assassinate
new_objective.owner = S.mind
new_objective.target = user
new_objective.explanation_text = "[objective_verb] [user.name], the one who summoned you."
S.mind.objectives += new_objective
var/datum/objective/new_objective2 = new /datum/objective
new_objective2.owner = S.mind
new_objective2.explanation_text = "[objective_verb] everyone[user ? " else while you're at it":""]."
S.mind.objectives += new_objective2
S.mind.add_antag_datum(/datum/antagonist/auto_custom)
to_chat(S, S.playstyle_string)
to_chat(S, "<B>You are currently not currently in the same plane of existence as the station. \
Ctrl+Click a blood pool to manifest.</B>")
if(new_objective)
to_chat(S, "<B>Objective #[1]</B>: [new_objective.explanation_text]")
to_chat(S, "<B>Objective #[new_objective ? "[2]":"[1]"]</B>: [new_objective2.explanation_text]")
/obj/item/antag_spawner/slaughter_demon/laughter
name = "vial of tickles"
desc = "A magically infused bottle of clown love, distilled from countless hugging attacks. Used in funny rituals to attract adorable creatures."
icon = 'icons/obj/wizard.dmi'
icon_state = "vial"
color = "#FF69B4" // HOT PINK
veil_msg = "<span class='warning'>You sense an adorable presence lurking just beyond the veil...</span>"
objective_verb = "Hug and Tickle"
demon_type = /mob/living/simple_animal/slaughter/laughter
-46
View File
@@ -1,46 +0,0 @@
//A barebones antagonist team.
/datum/team
var/list/datum/mind/members = list()
var/name = "team"
var/member_name = "member"
var/list/objectives = list() //common objectives, these won't be added or removed automatically, subtypes handle this, this is here for bookkeeping purposes.
/datum/team/New(starting_members)
. = ..()
if(starting_members)
if(islist(starting_members))
for(var/datum/mind/M in starting_members)
add_member(M)
else
add_member(starting_members)
/datum/team/proc/is_solo()
return members.len == 1
/datum/team/proc/add_member(datum/mind/new_member)
members |= new_member
/datum/team/proc/remove_member(datum/mind/member)
members -= member
//Display members/victory/failure/objectives for the team
/datum/team/proc/roundend_report()
var/list/report = list()
report += "<b>[name]:</b>"
report += "The [member_name]s were:"
report += printplayerlist(members)
return report.Join("<br>")
//Get all teams [of type team_type]
//TODO move these to some antag helpers file with get_antagonists
/proc/get_all_teams(team_type)
. = list()
for(var/V in GLOB.antagonists)
var/datum/antagonist/A = V
if(!A.owner)
continue
var/datum/team/T = A.get_team()
if(!team_type || istype(T,team_type))
. |= T
-109
View File
@@ -1,109 +0,0 @@
//Few global vars to track the blob
GLOBAL_LIST_EMPTY(blobs) //complete list of all blobs made.
GLOBAL_LIST_EMPTY(blob_cores)
GLOBAL_LIST_EMPTY(overminds)
GLOBAL_LIST_EMPTY(blob_nodes)
GLOBAL_LIST_EMPTY(blobs_legit) //used for win-score calculations, contains only blobs counted for win condition
#define BLOB_NO_PLACE_TIME 1800 //time, in deciseconds, blobs are prevented from bursting in the gamemode
/datum/game_mode/blob
name = "blob"
config_tag = "blob"
antag_flag = ROLE_BLOB
false_report_weight = 5
required_players = 25
required_enemies = 1
recommended_enemies = 1
round_ends_with_antag_death = 1
announce_span = "green"
announce_text = "Dangerous gelatinous organisms are spreading throughout the station!\n\
<span class='green'>Blobs</span>: Consume the station and spread as far as you can.\n\
<span class='notice'>Crew</span>: Fight back the blobs and minimize station damage."
var/message_sent = FALSE
var/cores_to_spawn = 1
var/players_per_core = 25
var/blob_point_rate = 3
var/blob_base_starting_points = 80
var/blobwincount = 250
var/messagedelay_low = 2400 //in deciseconds
var/messagedelay_high = 3600 //blob report will be sent after a random value between these (minimum 4 minutes, maximum 6 minutes)
var/list/blob_overminds = list()
/datum/game_mode/blob/pre_setup()
cores_to_spawn = max(round(num_players()/players_per_core, 1), 1)
var/win_multiplier = 1 + (0.1 * cores_to_spawn)
blobwincount = initial(blobwincount) * cores_to_spawn * win_multiplier
for(var/j = 0, j < cores_to_spawn, j++)
if (!antag_candidates.len)
break
var/datum/mind/blob = pick(antag_candidates)
blob_overminds += blob
blob.assigned_role = "Blob"
blob.special_role = "Blob"
log_game("[blob.key] (ckey) has been selected as a Blob")
antag_candidates -= blob
if(!blob_overminds.len)
return 0
return 1
/datum/game_mode/blob/proc/get_blob_candidates()
var/list/candidates = list()
for(var/mob/living/carbon/human/player in GLOB.player_list)
if(!player.stat && player.mind && !player.mind.special_role && !jobban_isbanned(player, "Syndicate") && (ROLE_BLOB in player.client.prefs.be_special))
if(age_check(player.client))
candidates += player
return candidates
/datum/game_mode/blob/proc/show_message(message)
for(var/datum/mind/blob in blob_overminds)
to_chat(blob.current, message)
/datum/game_mode/blob/post_setup()
set waitfor = FALSE
for(var/datum/mind/blob in blob_overminds)
var/mob/camera/blob/B = blob.current.become_overmind(TRUE, round(blob_base_starting_points/blob_overminds.len))
B.mind.name = B.name
var/turf/T = pick(GLOB.blobstart)
B.forceMove(T)
B.base_point_rate = blob_point_rate
SSshuttle.registerHostileEnvironment(src)
// Disable the blob event for this round.
var/datum/round_event_control/blob/B = locate() in SSevents.control
if(B)
B.max_occurrences = 0 // disable the event
. = ..()
var/message_delay = rand(messagedelay_low, messagedelay_high) //between 4 and 6 minutes with 2400 low and 3600 high.
sleep(message_delay)
send_intercept(1)
message_sent = TRUE
addtimer(CALLBACK(src, .proc/SendSecondIntercept), 24000)
/datum/game_mode/blob/proc/SendSecondIntercept()
if(!replacementmode)
send_intercept(2) //if the blob has been alive this long, it's time to bomb it
/datum/game_mode/blob/generate_report()
return "A CMP scientist by the name of [pick("Griff", "Pasteur", "Chamberland", "Buist", "Rivers", "Stanley")] boasted about his corporation's \"finest creation\" - a macrobiological \
virus capable of self-reproduction and hellbent on consuming whatever it touches. He went on to query Cybersun for permission to utilize the virus in biochemical warfare, to which \
CMP subsequently gained. Be vigilant for any large organisms rapidly spreading across the station, as they are classified as a level 5 biohazard and critically dangerous. Note that \
this organism seems to be weak to extreme heat; concentrated fire (such as welding tools and lasers) will be effective against it."
-62
View File
@@ -1,62 +0,0 @@
/datum/station_state
var/floor = 0
var/wall = 0
var/r_wall = 0
var/window = 0
var/door = 0
var/grille = 0
var/mach = 0
/datum/station_state/proc/count()
for(var/Z in SSmapping.levels_by_trait(ZTRAIT_STATION))
for(var/turf/T in block(locate(1,1,Z), locate(world.maxx,world.maxy,Z)))
// don't count shuttles since they may have just left
if(istype(T.loc, /area/shuttle))
continue
if(isfloorturf(T))
var/turf/open/floor/TF = T
if(!(TF.burnt))
floor += 12
else
floor += 1
if(iswallturf(T))
var/turf/closed/wall/TW = T
if(TW.intact)
wall += 2
else
wall += 1
if(istype(T, /turf/closed/wall/r_wall))
var/turf/closed/wall/r_wall/TRW = T
if(TRW.intact)
r_wall += 2
else
r_wall += 1
for(var/obj/O in T.contents)
if(istype(O, /obj/structure/window))
window += 1
else if(istype(O, /obj/structure/grille))
var/obj/structure/grille/GR = O
if(!GR.broken)
grille += 1
else if(istype(O, /obj/machinery/door))
door += 1
else if(ismachinery(O))
mach += 1
/datum/station_state/proc/score(datum/station_state/result)
if(!result)
return 0
var/output = 0
output += (result.floor / max(floor,1))
output += (result.r_wall/ max(r_wall,1))
output += (result.wall / max(wall,1))
output += (result.window / max(window,1))
output += (result.door / max(door,1))
output += (result.grille / max(grille,1))
output += (result.mach / max(mach,1))
return (output/7)
-295
View File
@@ -1,295 +0,0 @@
////////////////
// BASE TYPE //
////////////////
//Do not spawn
/mob/living/simple_animal/hostile/blob
icon = 'icons/mob/blob.dmi'
pass_flags = PASSBLOB
faction = list("blob")
bubble_icon = "blob"
speak_emote = null //so we use verb_yell/verb_say/etc
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
maxbodytemp = 360
unique_name = 1
a_intent = INTENT_HARM
var/mob/camera/blob/overmind = null
var/obj/structure/blob/factory/factory = null
/mob/living/simple_animal/hostile/blob/update_icons()
if(overmind)
add_atom_colour(overmind.blob_reagent_datum.color, FIXED_COLOUR_PRIORITY)
else
remove_atom_colour(FIXED_COLOUR_PRIORITY)
/mob/living/simple_animal/hostile/blob/Destroy()
if(overmind)
overmind.blob_mobs -= src
return ..()
/mob/living/simple_animal/hostile/blob/blob_act(obj/structure/blob/B)
if(stat != DEAD && health < maxHealth)
for(var/i in 1 to 2)
var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(src)) //hello yes you are being healed
if(overmind)
H.color = overmind.blob_reagent_datum.complementary_color
else
H.color = "#000000"
adjustHealth(-maxHealth*0.0125)
/mob/living/simple_animal/hostile/blob/fire_act(exposed_temperature, exposed_volume)
..()
if(exposed_temperature)
adjustFireLoss(CLAMP(0.01 * exposed_temperature, 1, 5))
else
adjustFireLoss(5)
/mob/living/simple_animal/hostile/blob/CanPass(atom/movable/mover, turf/target)
if(istype(mover, /obj/structure/blob))
return 1
return ..()
/mob/living/simple_animal/hostile/blob/Process_Spacemove(movement_dir = 0)
for(var/obj/structure/blob/B in range(1, src))
return 1
return ..()
/mob/living/simple_animal/hostile/blob/proc/blob_chat(msg)
var/spanned_message = say_quote(msg, get_spans())
var/rendered = "<font color=\"#EE4000\"><b>\[Blob Telepathy\] [real_name]</b> [spanned_message]</font>"
for(var/M in GLOB.mob_list)
if(isovermind(M) || istype(M, /mob/living/simple_animal/hostile/blob))
to_chat(M, rendered)
if(isobserver(M))
var/link = FOLLOW_LINK(M, src)
to_chat(M, "[link] [rendered]")
////////////////
// BLOB SPORE //
////////////////
/mob/living/simple_animal/hostile/blob/blobspore
name = "blob spore"
desc = "A floating, fragile spore."
icon_state = "blobpod"
icon_living = "blobpod"
health = 30
maxHealth = 30
verb_say = "psychically pulses"
verb_ask = "psychically probes"
verb_exclaim = "psychically yells"
verb_yell = "psychically screams"
melee_damage_lower = 2
melee_damage_upper = 4
obj_damage = 20
environment_smash = ENVIRONMENT_SMASH_STRUCTURES
attacktext = "hits"
attack_sound = 'sound/weapons/genhit1.ogg'
movement_type = FLYING
del_on_death = 1
deathmessage = "explodes into a cloud of gas!"
var/death_cloud_size = 1 //size of cloud produced from a dying spore
var/mob/living/carbon/human/oldguy
var/is_zombie = 0
gold_core_spawnable = HOSTILE_SPAWN
/mob/living/simple_animal/hostile/blob/blobspore/Initialize(mapload, var/obj/structure/blob/factory/linked_node)
if(istype(linked_node))
factory = linked_node
factory.spores += src
. = ..()
/mob/living/simple_animal/hostile/blob/blobspore/Life()
if(!is_zombie && isturf(src.loc))
for(var/mob/living/carbon/human/H in view(src,1)) //Only for corpse right next to/on same tile
if(H.stat == DEAD)
Zombify(H)
break
if(factory && z != factory.z)
death()
..()
/mob/living/simple_animal/hostile/blob/blobspore/proc/Zombify(mob/living/carbon/human/H)
is_zombie = 1
if(H.wear_suit)
var/obj/item/clothing/suit/armor/A = H.wear_suit
if(A.armor && A.armor["melee"])
maxHealth += A.armor["melee"] //That zombie's got armor, I want armor!
maxHealth += 40
health = maxHealth
name = "blob zombie"
desc = "A shambling corpse animated by the blob."
melee_damage_lower += 8
melee_damage_upper += 11
movement_type = GROUND
death_cloud_size = 0
icon = H.icon
icon_state = "zombie"
H.hair_style = null
H.update_hair()
H.forceMove(src)
oldguy = H
update_icons()
visible_message("<span class='warning'>The corpse of [H.name] suddenly rises!</span>")
/mob/living/simple_animal/hostile/blob/blobspore/death(gibbed)
// On death, create a small smoke of harmful gas (s-Acid)
var/datum/effect_system/smoke_spread/chem/S = new
var/turf/location = get_turf(src)
// Create the reagents to put into the air
create_reagents(10)
if(overmind && overmind.blob_reagent_datum)
reagents.add_reagent(overmind.blob_reagent_datum.id, 10)
else
reagents.add_reagent("spore", 10)
// Attach the smoke spreader and setup/start it.
S.attach(location)
S.set_up(reagents, death_cloud_size, location, silent=1)
S.start()
if(factory)
factory.spore_delay = world.time + factory.spore_cooldown //put the factory on cooldown
..()
/mob/living/simple_animal/hostile/blob/blobspore/Destroy()
if(factory)
factory.spores -= src
factory = null
if(oldguy)
oldguy.forceMove(get_turf(src))
oldguy = null
return ..()
/mob/living/simple_animal/hostile/blob/blobspore/update_icons()
if(overmind)
add_atom_colour(overmind.blob_reagent_datum.complementary_color, FIXED_COLOUR_PRIORITY)
else
remove_atom_colour(FIXED_COLOUR_PRIORITY)
if(is_zombie)
copy_overlays(oldguy, TRUE)
var/mutable_appearance/blob_head_overlay = mutable_appearance('icons/mob/blob.dmi', "blob_head")
if(overmind)
blob_head_overlay.color = overmind.blob_reagent_datum.complementary_color
color = initial(color)//looks better.
add_overlay(blob_head_overlay)
/mob/living/simple_animal/hostile/blob/blobspore/weak
name = "fragile blob spore"
health = 15
maxHealth = 15
melee_damage_lower = 1
melee_damage_upper = 2
death_cloud_size = 0
/////////////////
// BLOBBERNAUT //
/////////////////
/mob/living/simple_animal/hostile/blob/blobbernaut
name = "blobbernaut"
desc = "A hulking, mobile chunk of blobmass."
icon_state = "blobbernaut"
icon_living = "blobbernaut"
icon_dead = "blobbernaut_dead"
health = 200
maxHealth = 200
damage_coeff = list(BRUTE = 0.5, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1)
melee_damage_lower = 20
melee_damage_upper = 20
obj_damage = 60
attacktext = "slams"
attack_sound = 'sound/effects/blobattack.ogg'
verb_say = "gurgles"
verb_ask = "demands"
verb_exclaim = "roars"
verb_yell = "bellows"
force_threshold = 10
pressure_resistance = 50
mob_size = MOB_SIZE_LARGE
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
var/independent = FALSE
/mob/living/simple_animal/hostile/blob/blobbernaut/Initialize()
. = ..()
if(!independent) //no pulling people deep into the blob
verbs -= /mob/living/verb/pulled
else
pass_flags &= ~PASSBLOB
/mob/living/simple_animal/hostile/blob/blobbernaut/Life()
if(..())
var/list/blobs_in_area = range(2, src)
if(independent)
return // strong independent blobbernaut that don't need no blob
var/damagesources = 0
if(!(locate(/obj/structure/blob) in blobs_in_area))
damagesources++
if(!factory)
damagesources++
else
if(locate(/obj/structure/blob/core) in blobs_in_area)
adjustHealth(-maxHealth*0.1)
var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(src)) //hello yes you are being healed
if(overmind)
H.color = overmind.blob_reagent_datum.complementary_color
else
H.color = "#000000"
if(locate(/obj/structure/blob/node) in blobs_in_area)
adjustHealth(-maxHealth*0.05)
var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(src))
if(overmind)
H.color = overmind.blob_reagent_datum.complementary_color
else
H.color = "#000000"
if(damagesources)
for(var/i in 1 to damagesources)
adjustHealth(maxHealth*0.025) //take 2.5% of max health as damage when not near the blob or if the naut has no factory, 5% if both
var/image/I = new('icons/mob/blob.dmi', src, "nautdamage", MOB_LAYER+0.01)
I.appearance_flags = RESET_COLOR
if(overmind)
I.color = overmind.blob_reagent_datum.complementary_color
flick_overlay_view(I, src, 8)
/mob/living/simple_animal/hostile/blob/blobbernaut/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
. = ..()
if(updating_health)
update_health_hud()
/mob/living/simple_animal/hostile/blob/blobbernaut/update_health_hud()
if(hud_used)
hud_used.healths.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#e36600'>[round((health / maxHealth) * 100, 0.5)]%</font></div>"
/mob/living/simple_animal/hostile/blob/blobbernaut/AttackingTarget()
. = ..()
if(. && isliving(target) && overmind)
var/mob/living/L = target
var/mob_protection = L.get_permeability_protection()
overmind.blob_reagent_datum.reaction_mob(L, VAPOR, 20, 0, mob_protection, overmind)//this will do between 10 and 20 damage(reduced by mob protection), depending on chemical, plus 4 from base brute damage.
/mob/living/simple_animal/hostile/blob/blobbernaut/update_icons()
..()
if(overmind) //if we have an overmind, we're doing chemical reactions instead of pure damage
melee_damage_lower = 4
melee_damage_upper = 4
attacktext = overmind.blob_reagent_datum.blobbernaut_message
else
melee_damage_lower = initial(melee_damage_lower)
melee_damage_upper = initial(melee_damage_upper)
attacktext = initial(attacktext)
/mob/living/simple_animal/hostile/blob/blobbernaut/death(gibbed)
..(gibbed)
if(factory)
factory.naut = null //remove this naut from its factory
factory.max_integrity = initial(factory.max_integrity)
flick("blobbernaut_death", src)
/mob/living/simple_animal/hostile/blob/blobbernaut/independent
independent = TRUE
gold_core_spawnable = HOSTILE_SPAWN
-80
View File
@@ -1,80 +0,0 @@
/obj/structure/blob/core
name = "blob core"
icon = 'icons/mob/blob.dmi'
icon_state = "blank_blob"
desc = "A huge, pulsating yellow mass."
max_integrity = 400
armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 75, acid = 90)
explosion_block = 6
point_return = -1
health_regen = 0 //we regen in Life() instead of when pulsed
var/core_regen = 2
var/resource_delay = 0
var/point_rate = 2
/obj/structure/blob/core/Initialize(mapload, client/new_overmind = null, new_rate = 2, placed = 0)
GLOB.blob_cores += src
START_PROCESSING(SSobj, src)
GLOB.poi_list |= src
update_icon() //so it atleast appears
if(!placed && !overmind)
return INITIALIZE_HINT_QDEL
if(overmind)
update_icon()
point_rate = new_rate
addtimer(CALLBACK(src, .proc/generate_announcement), 1800)
. = ..()
/obj/structure/blob/core/proc/generate_announcement()
priority_announce("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/ai/outbreak5.ogg')
/obj/structure/blob/core/scannerreport()
return "Directs the blob's expansion, gradually expands, and sustains nearby blob spores and blobbernauts."
/obj/structure/blob/core/update_icon()
cut_overlays()
color = null
var/mutable_appearance/blob_overlay = mutable_appearance('icons/mob/blob.dmi', "blob")
if(overmind)
blob_overlay.color = overmind.blob_reagent_datum.color
add_overlay(blob_overlay)
add_overlay(mutable_appearance('icons/mob/blob.dmi', "blob_core_overlay"))
/obj/structure/blob/core/Destroy()
GLOB.blob_cores -= src
if(overmind)
overmind.blob_core = null
overmind = null
STOP_PROCESSING(SSobj, src)
GLOB.poi_list -= src
return ..()
/obj/structure/blob/core/ex_act(severity, target)
var/damage = 50 - 10 * severity //remember, the core takes half brute damage, so this is 20/15/10 damage based on severity
take_damage(damage, BRUTE, "bomb", 0)
/obj/structure/blob/core/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir, overmind_reagent_trigger = 1)
. = ..()
if(obj_integrity > 0)
if(overmind) //we should have an overmind, but...
overmind.update_health_hud()
/obj/structure/blob/core/Life()
if(QDELETED(src))
return
if(!overmind)
qdel(src)
else
if(resource_delay <= world.time)
resource_delay = world.time + 10 // 1 second
overmind.add_points(point_rate)
obj_integrity = min(max_integrity, obj_integrity+core_regen)
if(overmind)
overmind.update_health_hud()
Pulse_Area(overmind, 12, 4, 3)
for(var/obj/structure/blob/normal/B in range(1, src))
if(prob(5))
B.change_to(/obj/structure/blob/shield/core, overmind)
..()
-46
View File
@@ -1,46 +0,0 @@
/obj/structure/blob/factory
name = "factory blob"
icon = 'icons/mob/blob.dmi'
icon_state = "blob_factory"
desc = "A thick spire of tendrils."
max_integrity = 200
health_regen = 1
point_return = 25
var/list/spores = list()
var/mob/living/simple_animal/hostile/blob/blobbernaut/naut = null
var/max_spores = 3
var/spore_delay = 0
var/spore_cooldown = 80 //8 seconds between spores and after spore death
/obj/structure/blob/factory/scannerreport()
if(naut)
return "It is currently sustaining a blobbernaut, making it fragile and unable to produce blob spores."
return "Will produce a blob spore every few seconds."
/obj/structure/blob/factory/Destroy()
for(var/mob/living/simple_animal/hostile/blob/blobspore/spore in spores)
if(spore.factory == src)
spore.factory = null
if(naut)
naut.factory = null
to_chat(naut, "<span class='userdanger'>Your factory was destroyed! You feel yourself dying!</span>")
naut.throw_alert("nofactory", /obj/screen/alert/nofactory)
spores = null
return ..()
/obj/structure/blob/factory/Be_Pulsed()
. = ..()
if(naut)
return
if(spores.len >= max_spores)
return
if(spore_delay > world.time)
return
flick("blob_factory_glow", src)
spore_delay = world.time + spore_cooldown
var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore(src.loc, src)
if(overmind) //if we don't have an overmind, we don't need to do anything but make a spore
BS.overmind = overmind
BS.update_icons()
overmind.blob_mobs.Add(BS)
-36
View File
@@ -1,36 +0,0 @@
/obj/structure/blob/node
name = "blob node"
icon = 'icons/mob/blob.dmi'
icon_state = "blank_blob"
desc = "A large, pulsating yellow mass."
max_integrity = 200
armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 65, acid = 90)
health_regen = 3
point_return = 25
/obj/structure/blob/node/Initialize()
GLOB.blob_nodes += src
START_PROCESSING(SSobj, src)
. = ..()
/obj/structure/blob/node/scannerreport()
return "Gradually expands and sustains nearby blob spores and blobbernauts."
/obj/structure/blob/node/update_icon()
cut_overlays()
color = null
var/mutable_appearance/blob_overlay = mutable_appearance('icons/mob/blob.dmi', "blob")
if(overmind)
blob_overlay.color = overmind.blob_reagent_datum.color
add_overlay(blob_overlay)
add_overlay(mutable_appearance('icons/mob/blob.dmi', "blob_node_overlay"))
/obj/structure/blob/node/Destroy()
GLOB.blob_nodes -= src
STOP_PROCESSING(SSobj, src)
return ..()
/obj/structure/blob/node/Life()
Pulse_Area(overmind, 10, 3, 2)
@@ -1,31 +0,0 @@
/obj/structure/blob/resource
name = "resource blob"
icon = 'icons/mob/blob.dmi'
icon_state = "blob_resource"
desc = "A thin spire of slightly swaying tendrils."
max_integrity = 60
point_return = 15
var/resource_delay = 0
/obj/structure/blob/resource/scannerreport()
return "Gradually supplies the blob with resources, increasing the rate of expansion."
/obj/structure/blob/resource/creation_action()
if(overmind)
overmind.resource_blobs += src
/obj/structure/blob/resource/Destroy()
if(overmind)
overmind.resource_blobs -= src
return ..()
/obj/structure/blob/resource/Be_Pulsed()
. = ..()
if(resource_delay > world.time)
return
flick("blob_resource_glow", src)
if(overmind)
overmind.add_points(1)
resource_delay = world.time + 40 + overmind.resource_blobs.len * 2.5 //4 seconds plus a quarter second for each resource blob the overmind has
else
resource_delay = world.time + 40
-33
View File
@@ -1,33 +0,0 @@
/obj/structure/blob/shield
name = "strong blob"
icon = 'icons/mob/blob.dmi'
icon_state = "blob_shield"
desc = "A solid wall of slightly twitching tendrils."
max_integrity = 150
brute_resist = 0.25
explosion_block = 3
point_return = 4
atmosblock = TRUE
armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 90, acid = 90)
/obj/structure/blob/shield/scannerreport()
if(atmosblock)
return "Will prevent the spread of atmospheric changes."
return "N/A"
/obj/structure/blob/shield/core
point_return = 0
/obj/structure/blob/shield/update_icon()
..()
if(obj_integrity <= 75)
icon_state = "blob_shield_damaged"
name = "weakened strong blob"
desc = "A wall of twitching tendrils."
atmosblock = FALSE
else
icon_state = initial(icon_state)
name = initial(name)
desc = initial(desc)
atmosblock = TRUE
air_update_turf(1)
-258
View File
@@ -1,258 +0,0 @@
//Few global vars to track the blob
GLOBAL_LIST_EMPTY(blobs) //complete list of all blobs made.
GLOBAL_LIST_EMPTY(blob_cores)
GLOBAL_LIST_EMPTY(overminds)
GLOBAL_LIST_EMPTY(blob_nodes)
/mob/camera/blob
name = "Blob Overmind"
real_name = "Blob Overmind"
desc = "The overmind. It controls the blob."
icon = 'icons/mob/blob.dmi'
icon_state = "marker"
mouse_opacity = MOUSE_OPACITY_ICON
move_on_shuttle = 1
see_in_dark = 8
invisibility = INVISIBILITY_OBSERVER
layer = FLY_LAYER
pass_flags = PASSBLOB
faction = list("blob")
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
var/obj/structure/blob/core/blob_core = null // The blob overmind's core
var/blob_points = 0
var/max_blob_points = 100
var/last_attack = 0
var/datum/reagent/blob/blob_reagent_datum = new/datum/reagent/blob()
var/list/blob_mobs = list()
var/list/resource_blobs = list()
var/free_chem_rerolls = 1 //one free chemical reroll
var/nodes_required = 1 //if the blob needs nodes to place resource and factory blobs
var/placed = 0
var/base_point_rate = 2 //for blob core placement
var/manualplace_min_time = 600 //in deciseconds //a minute, to get bearings
var/autoplace_max_time = 3600 //six minutes, as long as should be needed
var/list/blobs_legit = list()
var/max_count = 0 //The biggest it got before death
var/blobwincount = 400
var/victory_in_progress = FALSE
/mob/camera/blob/Initialize(mapload, starting_points = 60)
validate_location()
blob_points = starting_points
manualplace_min_time += world.time
autoplace_max_time += world.time
GLOB.overminds += src
var/new_name = "[initial(name)] ([rand(1, 999)])"
name = new_name
real_name = new_name
last_attack = world.time
var/datum/reagent/blob/BC = pick((subtypesof(/datum/reagent/blob)))
blob_reagent_datum = new BC
color = blob_reagent_datum.complementary_color
if(blob_core)
blob_core.update_icon()
SSshuttle.registerHostileEnvironment(src)
.= ..()
/mob/camera/blob/proc/validate_location()
var/turf/T = get_turf(src)
if(!is_valid_turf(T) && LAZYLEN(GLOB.blobstart))
var/list/blobstarts = shuffle(GLOB.blobstart)
for(var/_T in blobstarts)
if(is_valid_turf(_T))
T = _T
break
if(!T)
CRASH("No blobspawnpoints and blob spawned in nullspace.")
forceMove(T)
/mob/camera/blob/proc/is_valid_turf(turf/T)
var/area/A = get_area(T)
if((A && !A.blob_allowed) || !T || !is_station_level(T.z) || isspaceturf(T))
return FALSE
return TRUE
/mob/camera/blob/Life()
if(!blob_core)
if(!placed)
if(manualplace_min_time && world.time >= manualplace_min_time)
to_chat(src, "<b><span class='big'><font color=\"#EE4000\">You may now place your blob core.</font></span></b>")
to_chat(src, "<span class='big'><font color=\"#EE4000\">You will automatically place your blob core in [DisplayTimeText(autoplace_max_time - world.time)].</font></span>")
manualplace_min_time = 0
if(autoplace_max_time && world.time >= autoplace_max_time)
place_blob_core(base_point_rate, 1)
else
qdel(src)
else if(!victory_in_progress && (blobs_legit.len >= blobwincount))
victory_in_progress = TRUE
priority_announce("Biohazard has reached critical mass. Station loss is imminent.", "Biohazard Alert")
set_security_level("delta")
max_blob_points = INFINITY
blob_points = INFINITY
addtimer(CALLBACK(src, .proc/victory), 450)
if(!victory_in_progress && max_count < blobs_legit.len)
max_count = blobs_legit.len
..()
/mob/camera/blob/proc/victory()
sound_to_playing_players('sound/machines/alarm.ogg')
sleep(100)
for(var/i in GLOB.mob_living_list)
var/mob/living/L = i
var/turf/T = get_turf(L)
if(!T || !is_station_level(T.z))
continue
if(L in GLOB.overminds || (L.pass_flags & PASSBLOB))
continue
var/area/Ablob = get_area(T)
if(!Ablob.blob_allowed)
continue
if(!("blob" in L.faction))
playsound(L, 'sound/effects/splat.ogg', 50, 1)
L.death()
new/mob/living/simple_animal/hostile/blob/blobspore(T)
else
L.fully_heal()
for(var/V in GLOB.sortedAreas)
var/area/A = V
if(!A.blob_allowed)
continue
A.color = blob_reagent_datum.color
A.name = "blob"
A.icon = 'icons/mob/blob.dmi'
A.icon_state = "blob_shield"
A.layer = BELOW_MOB_LAYER
A.invisibility = 0
A.blend_mode = 0
var/datum/antagonist/blob/B = mind.has_antag_datum(/datum/antagonist/blob)
if(B)
var/datum/objective/blob_takeover/main_objective = locate() in B.objectives
if(main_objective)
main_objective.completed = TRUE
to_chat(world, "<B>[real_name] consumed the station in an unstoppable tide!</B>")
SSticker.news_report = BLOB_WIN
SSticker.force_ending = 1
/mob/camera/blob/Destroy()
for(var/BL in GLOB.blobs)
var/obj/structure/blob/B = BL
if(B && B.overmind == src)
B.overmind = null
B.update_icon() //reset anything that was ours
for(var/BLO in blob_mobs)
var/mob/living/simple_animal/hostile/blob/BM = BLO
if(BM)
BM.overmind = null
BM.update_icons()
GLOB.overminds -= src
SSshuttle.clearHostileEnvironment(src)
return ..()
/mob/camera/blob/Login()
..()
to_chat(src, "<span class='notice'>You are the overmind!</span>")
blob_help()
update_health_hud()
add_points(0)
/mob/camera/blob/examine(mob/user)
..()
if(blob_reagent_datum)
to_chat(user, "Its chemical is <font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</font>.")
/mob/camera/blob/update_health_hud()
if(blob_core)
hud_used.healths.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#e36600'>[round(blob_core.obj_integrity)]</font></div>"
for(var/mob/living/simple_animal/hostile/blob/blobbernaut/B in blob_mobs)
if(B.hud_used && B.hud_used.blobpwrdisplay)
B.hud_used.blobpwrdisplay.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#82ed00'>[round(blob_core.obj_integrity)]</font></div>"
/mob/camera/blob/proc/add_points(points)
blob_points = CLAMP(blob_points + points, 0, max_blob_points)
hud_used.blobpwrdisplay.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#82ed00'>[round(blob_points)]</font></div>"
/mob/camera/blob/say(message)
if (!message)
return
if (src.client)
if(client.prefs.muted & MUTE_IC)
to_chat(src, "You cannot send IC messages (muted).")
return
if (src.client.handle_spam_prevention(message,MUTE_IC))
return
if (stat)
return
blob_talk(message)
/mob/camera/blob/proc/blob_talk(message)
message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
if (!message)
return
log_talk(src,"[key_name(src)] : [message]",LOGSAY)
var/message_a = say_quote(message, get_spans())
var/rendered = "<span class='big'><font color=\"#EE4000\"><b>\[Blob Telepathy\] [name](<font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</font>)</b> [message_a]</font></span>"
for(var/mob/M in GLOB.mob_list)
if(isovermind(M) || istype(M, /mob/living/simple_animal/hostile/blob))
to_chat(M, rendered)
if(isobserver(M))
var/link = FOLLOW_LINK(M, src)
to_chat(M, "[link] [rendered]")
/mob/camera/blob/emote(act,m_type=1,message = null)
return
/mob/camera/blob/blob_act(obj/structure/blob/B)
return
/mob/camera/blob/Stat()
..()
if(statpanel("Status"))
if(blob_core)
stat(null, "Core Health: [blob_core.obj_integrity]")
stat(null, "Power Stored: [blob_points]/[max_blob_points]")
stat(null, "Blobs to Win: [blobs_legit.len]/[blobwincount]")
if(free_chem_rerolls)
stat(null, "You have [free_chem_rerolls] Free Chemical Reroll\s Remaining")
if(!placed)
if(manualplace_min_time)
stat(null, "Time Before Manual Placement: [max(round((manualplace_min_time - world.time)*0.1, 0.1), 0)]")
stat(null, "Time Before Automatic Placement: [max(round((autoplace_max_time - world.time)*0.1, 0.1), 0)]")
/mob/camera/blob/Move(NewLoc, Dir = 0)
if(placed)
var/obj/structure/blob/B = locate() in range("3x3", NewLoc)
if(B)
forceMove(NewLoc)
else
return 0
else
var/area/A = get_area(NewLoc)
if(isspaceturf(NewLoc) || istype(A, /area/shuttle)) //if unplaced, can't go on shuttles or space tiles
return 0
forceMove(NewLoc)
return 1
/mob/camera/blob/mind_initialize()
. = ..()
var/datum/antagonist/blob/B = mind.has_antag_datum(/datum/antagonist/blob)
if(!B)
mind.add_antag_datum(/datum/antagonist/blob)
-372
View File
@@ -1,372 +0,0 @@
/mob/camera/blob/proc/can_buy(cost = 15)
if(blob_points < cost)
to_chat(src, "<span class='warning'>You cannot afford this, you need at least [cost] resources!</span>")
return 0
add_points(-cost)
return 1
// Power verbs
/mob/camera/blob/proc/place_blob_core(point_rate, placement_override , pop_override = FALSE)
if(placed && placement_override != -1)
return 1
if(!placement_override)
if(!pop_override)
for(var/mob/living/M in range(7, src))
if("blob" in M.faction)
continue
if(M.client)
to_chat(src, "<span class='warning'>There is someone too close to place your blob core!</span>")
return 0
for(var/mob/living/M in view(13, src))
if("blob" in M.faction)
continue
if(M.client)
to_chat(src, "<span class='warning'>Someone could see your blob core from here!</span>")
return 0
var/turf/T = get_turf(src)
if(T.density)
to_chat(src, "<span class='warning'>This spot is too dense to place a blob core on!</span>")
return 0
for(var/obj/O in T)
if(istype(O, /obj/structure/blob))
if(istype(O, /obj/structure/blob/normal))
qdel(O)
else
to_chat(src, "<span class='warning'>There is already a blob here!</span>")
return 0
else if(O.density)
to_chat(src, "<span class='warning'>This spot is too dense to place a blob core on!</span>")
return 0
if(!pop_override && world.time <= manualplace_min_time && world.time <= autoplace_max_time)
to_chat(src, "<span class='warning'>It is too early to place your blob core!</span>")
return 0
else if(placement_override == 1)
var/turf/T = pick(GLOB.blobstart)
forceMove(T) //got overrided? you're somewhere random, motherfucker
if(placed && blob_core)
blob_core.forceMove(loc)
else
var/obj/structure/blob/core/core = new(get_turf(src), src, point_rate, 1)
core.overmind = src
blobs_legit += src
blob_core = core
core.update_icon()
update_health_hud()
placed = 1
return 1
/mob/camera/blob/verb/transport_core()
set category = "Blob"
set name = "Jump to Core"
set desc = "Move your camera to your core."
if(blob_core)
forceMove(blob_core.drop_location())
/mob/camera/blob/verb/jump_to_node()
set category = "Blob"
set name = "Jump to Node"
set desc = "Move your camera to a selected node."
if(GLOB.blob_nodes.len)
var/list/nodes = list()
for(var/i in 1 to GLOB.blob_nodes.len)
var/obj/structure/blob/node/B = GLOB.blob_nodes[i]
nodes["Blob Node #[i] ([B.overmind ? "[B.overmind.blob_reagent_datum.name]":"No Chemical"])"] = B
var/node_name = input(src, "Choose a node to jump to.", "Node Jump") in nodes
var/obj/structure/blob/node/chosen_node = nodes[node_name]
if(chosen_node)
forceMove(chosen_node.loc)
/mob/camera/blob/proc/createSpecial(price, blobType, nearEquals, needsNode, turf/T)
if(!T)
T = get_turf(src)
var/obj/structure/blob/B = (locate(/obj/structure/blob) in T)
if(!B)
to_chat(src, "<span class='warning'>There is no blob here!</span>")
return
if(!istype(B, /obj/structure/blob/normal))
to_chat(src, "<span class='warning'>Unable to use this blob, find a normal one.</span>")
return
if(needsNode && nodes_required)
if(!(locate(/obj/structure/blob/node) in orange(3, T)) && !(locate(/obj/structure/blob/core) in orange(4, T)))
to_chat(src, "<span class='warning'>You need to place this blob closer to a node or core!</span>")
return //handholdotron 2000
if(nearEquals)
for(var/obj/structure/blob/L in orange(nearEquals, T))
if(L.type == blobType)
to_chat(src, "<span class='warning'>There is a similar blob nearby, move more than [nearEquals] tiles away from it!</span>")
return
if(!can_buy(price))
return
var/obj/structure/blob/N = B.change_to(blobType, src)
return N
/mob/camera/blob/verb/toggle_node_req()
set category = "Blob"
set name = "Toggle Node Requirement"
set desc = "Toggle requiring nodes to place resource and factory blobs."
nodes_required = !nodes_required
if(nodes_required)
to_chat(src, "<span class='warning'>You now require a nearby node or core to place factory and resource blobs.</span>")
else
to_chat(src, "<span class='warning'>You no longer require a nearby node or core to place factory and resource blobs.</span>")
/mob/camera/blob/verb/create_shield_power()
set category = "Blob"
set name = "Create Shield Blob (15)"
set desc = "Create a shield blob, which will block fire and is hard to kill."
create_shield()
/mob/camera/blob/proc/create_shield(turf/T)
createSpecial(15, /obj/structure/blob/shield, 0, 0, T)
/mob/camera/blob/verb/create_resource()
set category = "Blob"
set name = "Create Resource Blob (40)"
set desc = "Create a resource tower which will generate resources for you."
createSpecial(40, /obj/structure/blob/resource, 4, 1)
/mob/camera/blob/verb/create_node()
set category = "Blob"
set name = "Create Node Blob (50)"
set desc = "Create a node, which will power nearby factory and resource blobs."
createSpecial(50, /obj/structure/blob/node, 5, 0)
/mob/camera/blob/verb/create_factory()
set category = "Blob"
set name = "Create Factory Blob (60)"
set desc = "Create a spore tower that will spawn spores to harass your enemies."
createSpecial(60, /obj/structure/blob/factory, 7, 1)
/mob/camera/blob/verb/create_blobbernaut()
set category = "Blob"
set name = "Create Blobbernaut (40)"
set desc = "Create a powerful blobbernaut which is mildly smart and will attack enemies."
var/turf/T = get_turf(src)
var/obj/structure/blob/factory/B = locate(/obj/structure/blob/factory) in T
if(!B)
to_chat(src, "<span class='warning'>You must be on a factory blob!</span>")
return
if(B.naut) //if it already made a blobbernaut, it can't do it again
to_chat(src, "<span class='warning'>This factory blob is already sustaining a blobbernaut.</span>")
return
if(B.obj_integrity < B.max_integrity * 0.5)
to_chat(src, "<span class='warning'>This factory blob is too damaged to sustain a blobbernaut.</span>")
return
if(!can_buy(40))
return
var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a [blob_reagent_datum.name] blobbernaut?", ROLE_BLOB, null, ROLE_BLOB, 50) //players must answer rapidly
if(candidates.len) //if we got at least one candidate, they're a blobbernaut now.
B.max_integrity = initial(B.max_integrity) * 0.25 //factories that produced a blobbernaut have much lower health
B.obj_integrity = min(B.obj_integrity, B.max_integrity)
B.update_icon()
B.visible_message("<span class='warning'><b>The blobbernaut [pick("rips", "tears", "shreds")] its way out of the factory blob!</b></span>")
playsound(B.loc, 'sound/effects/splat.ogg', 50, 1)
var/mob/living/simple_animal/hostile/blob/blobbernaut/blobber = new /mob/living/simple_animal/hostile/blob/blobbernaut(get_turf(B))
flick("blobbernaut_produce", blobber)
B.naut = blobber
blobber.factory = B
blobber.overmind = src
blobber.update_icons()
blobber.adjustHealth(blobber.maxHealth * 0.5)
blob_mobs += blobber
var/client/C = pick(candidates)
blobber.key = C.key
SEND_SOUND(blobber, sound('sound/effects/blobattack.ogg'))
SEND_SOUND(blobber, sound('sound/effects/attackblob.ogg'))
to_chat(blobber, "<b>You are a blobbernaut!</b>")
to_chat(blobber, "You are powerful, hard to kill, and slowly regenerate near nodes and cores, <span class='cultlarge'>but will slowly die if not near the blob</span> or if the factory that made you is killed.")
to_chat(blobber, "You can communicate with other blobbernauts and overminds via <b>:b</b>")
to_chat(blobber, "Your overmind's blob reagent is: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!")
to_chat(blobber, "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.shortdesc ? "[blob_reagent_datum.shortdesc]" : "[blob_reagent_datum.description]"]")
else
to_chat(src, "<span class='warning'>You could not conjure a sentience for your blobbernaut. Your points have been refunded. Try again later.</span>")
add_points(40)
/mob/camera/blob/verb/relocate_core()
set category = "Blob"
set name = "Relocate Core (80)"
set desc = "Swaps the locations of your core and the selected node."
var/turf/T = get_turf(src)
var/obj/structure/blob/node/B = locate(/obj/structure/blob/node) in T
if(!B)
to_chat(src, "<span class='warning'>You must be on a blob node!</span>")
return
if(!blob_core)
to_chat(src, "<span class='userdanger'>You have no core and are about to die! May you rest in peace.</span>")
return
var/area/A = get_area(T)
if(isspaceturf(T) || A && !A.blob_allowed)
to_chat(src, "<span class='warning'>You cannot relocate your core here!</span>")
return
if(!can_buy(80))
return
var/turf/old_turf = get_turf(blob_core)
var/olddir = blob_core.dir
blob_core.forceMove(T)
blob_core.setDir(B.dir)
B.forceMove(old_turf)
B.setDir(olddir)
/mob/camera/blob/verb/revert()
set category = "Blob"
set name = "Remove Blob"
set desc = "Removes a blob, giving you back some resources."
var/turf/T = get_turf(src)
remove_blob(T)
/mob/camera/blob/proc/remove_blob(turf/T)
var/obj/structure/blob/B = locate() in T
if(!B)
to_chat(src, "<span class='warning'>There is no blob there!</span>")
return
if(B.point_return < 0)
to_chat(src, "<span class='warning'>Unable to remove this blob.</span>")
return
if(max_blob_points < B.point_return + blob_points)
to_chat(src, "<span class='warning'>You have too many resources to remove this blob!</span>")
return
if(B.point_return)
add_points(B.point_return)
to_chat(src, "<span class='notice'>Gained [B.point_return] resources from removing \the [B].</span>")
qdel(B)
/mob/camera/blob/verb/expand_blob_power()
set category = "Blob"
set name = "Expand/Attack Blob (4)"
set desc = "Attempts to create a new blob in this tile. If the tile isn't clear, instead attacks it, damaging mobs and objects."
var/turf/T = get_turf(src)
expand_blob(T)
/mob/camera/blob/proc/expand_blob(turf/T)
if(world.time < last_attack)
return
var/list/possibleblobs = list()
for(var/obj/structure/blob/AB in range(T, 1))
possibleblobs += AB
if(!possibleblobs.len)
to_chat(src, "<span class='warning'>There is no blob adjacent to the target tile!</span>")
return
if(can_buy(4))
var/attacksuccess = FALSE
for(var/mob/living/L in T)
if("blob" in L.faction) //no friendly/dead fire
continue
if(L.stat != DEAD)
attacksuccess = TRUE
var/mob_protection = L.get_permeability_protection()
blob_reagent_datum.reaction_mob(L, VAPOR, 25, 1, mob_protection, src)
blob_reagent_datum.send_message(L)
var/obj/structure/blob/B = locate() in T
if(B)
if(attacksuccess) //if we successfully attacked a turf with a blob on it, don't refund shit
B.blob_attack_animation(T, src)
else
to_chat(src, "<span class='warning'>There is a blob there!</span>")
add_points(4) //otherwise, refund all of the cost
else
var/list/cardinalblobs = list()
var/list/diagonalblobs = list()
for(var/I in possibleblobs)
var/obj/structure/blob/IB = I
if(get_dir(IB, T) in GLOB.cardinals)
cardinalblobs += IB
else
diagonalblobs += IB
var/obj/structure/blob/OB
if(cardinalblobs.len)
OB = pick(cardinalblobs)
OB.expand(T, src)
else
OB = pick(diagonalblobs)
if(attacksuccess)
OB.blob_attack_animation(T, src)
playsound(OB, 'sound/effects/splat.ogg', 50, 1)
else
add_points(4) //if we're attacking diagonally and didn't hit anything, refund
if(attacksuccess)
last_attack = world.time + CLICK_CD_MELEE
else
last_attack = world.time + CLICK_CD_RAPID
/mob/camera/blob/verb/rally_spores_power()
set category = "Blob"
set name = "Rally Spores"
set desc = "Rally your spores to move to a target location."
var/turf/T = get_turf(src)
rally_spores(T)
/mob/camera/blob/proc/rally_spores(turf/T)
to_chat(src, "You rally your spores.")
var/list/surrounding_turfs = block(locate(T.x - 1, T.y - 1, T.z), locate(T.x + 1, T.y + 1, T.z))
if(!surrounding_turfs.len)
return
for(var/mob/living/simple_animal/hostile/blob/blobspore/BS in blob_mobs)
if(isturf(BS.loc) && get_dist(BS, T) <= 35)
BS.LoseTarget()
BS.Goto(pick(surrounding_turfs), BS.move_to_delay)
/mob/camera/blob/verb/blob_broadcast()
set category = "Blob"
set name = "Blob Broadcast"
set desc = "Speak with your blob spores and blobbernauts as your mouthpieces."
var/speak_text = input(src, "What would you like to say with your minions?", "Blob Broadcast", null) as text
if(!speak_text)
return
else
to_chat(src, "You broadcast with your minions, <B>[speak_text]</B>")
for(var/BLO in blob_mobs)
var/mob/living/simple_animal/hostile/blob/BM = BLO
if(BM.stat == CONSCIOUS)
BM.say(speak_text)
/mob/camera/blob/verb/chemical_reroll()
set category = "Blob"
set name = "Reactive Chemical Adaptation (40)"
set desc = "Replaces your chemical with a random, different one."
if(free_chem_rerolls || can_buy(40))
set_chemical()
if(free_chem_rerolls)
free_chem_rerolls--
/mob/camera/blob/proc/set_chemical()
var/datum/reagent/blob/BC = pick((subtypesof(/datum/reagent/blob) - blob_reagent_datum.type))
blob_reagent_datum = new BC
color = blob_reagent_datum.complementary_color
for(var/BL in GLOB.blobs)
var/obj/structure/blob/B = BL
B.update_icon()
for(var/BLO in blob_mobs)
var/mob/living/simple_animal/hostile/blob/BM = BLO
BM.update_icons() //If it's getting a new chemical, tell it what it does!
to_chat(BM, "Your overmind's blob reagent is now: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!")
to_chat(BM, "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.shortdesc ? "[blob_reagent_datum.shortdesc]" : "[blob_reagent_datum.description]"]")
to_chat(src, "Your reagent is now: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!")
to_chat(src, "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.description]")
if(blob_reagent_datum.effectdesc)
to_chat(src, "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.effectdesc]")
/mob/camera/blob/verb/blob_help()
set category = "Blob"
set name = "*Blob Help*"
set desc = "Help on how to blob."
to_chat(src, "<b>As the overmind, you can control the blob!</b>")
to_chat(src, "Your blob reagent is: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!")
to_chat(src, "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.description]")
if(blob_reagent_datum.effectdesc)
to_chat(src, "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.effectdesc]")
to_chat(src, "<b>You can expand, which will attack people, damage objects, or place a Normal Blob if the tile is clear.</b>")
to_chat(src, "<i>Normal Blobs</i> will expand your reach and can be upgraded into special blobs that perform certain functions.")
to_chat(src, "<b>You can upgrade normal blobs into the following types of blob:</b>")
to_chat(src, "<i>Shield Blobs</i> are strong and expensive blobs which take more damage. In additon, they are fireproof and can block air, use these to protect yourself from station fires.")
to_chat(src, "<i>Resource Blobs</i> are blobs which produce more resources for you, build as many of these as possible to consume the station. This type of blob must be placed near node blobs or your core to work.")
to_chat(src, "<i>Factory Blobs</i> are blobs that spawn blob spores which will attack nearby enemies. This type of blob must be placed near node blobs or your core to work.")
to_chat(src, "<i>Blobbernauts</i> can be produced from factories for a cost, and are hard to kill, powerful, and moderately smart. The factory used to create one will become fragile and briefly unable to produce spores.")
to_chat(src, "<i>Node Blobs</i> are blobs which grow, like the core. Like the core it can activate resource and factory blobs.")
to_chat(src, "<b>In addition to the buttons on your HUD, there are a few click shortcuts to speed up expansion and defense.</b>")
to_chat(src, "<b>Shortcuts:</b> Click = Expand Blob <b>|</b> Middle Mouse Click = Rally Spores <b>|</b> Ctrl Click = Create Shield Blob <b>|</b> Alt Click = Remove Blob")
to_chat(src, "Attempting to talk will send a message to all other overminds, allowing you to coordinate with them.")
if(!placed && autoplace_max_time <= world.time)
to_chat(src, "<span class='big'><font color=\"#EE4000\">You will automatically place your blob core in [DisplayTimeText(autoplace_max_time - world.time)].</font></span>")
to_chat(src, "<span class='big'><font color=\"#EE4000\">You [manualplace_min_time ? "will be able to":"can"] manually place your blob core by pressing the Place Blob Core button in the bottom right corner of the screen.</font></span>")
-351
View File
@@ -1,351 +0,0 @@
//I will need to recode parts of this but I am way too tired atm //I don't know who left this comment but they never did come back
/obj/structure/blob
name = "blob"
icon = 'icons/mob/blob.dmi'
light_range = 2
desc = "A thick wall of writhing tendrils."
density = FALSE //this being false causes two bugs, being able to attack blob tiles behind other blobs and being unable to move on blob tiles in no gravity, but turning it to 1 causes the blob mobs to be unable to path through blobs, which is probably worse.
opacity = 0
anchored = TRUE
layer = BELOW_MOB_LAYER
CanAtmosPass = ATMOS_PASS_PROC
var/point_return = 0 //How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed.
max_integrity = 30
armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 80, acid = 70)
var/health_regen = 2 //how much health this blob regens when pulsed
var/pulse_timestamp = 0 //we got pulsed when?
var/heal_timestamp = 0 //we got healed when?
var/brute_resist = 0.5 //multiplies brute damage by this
var/fire_resist = 1 //multiplies burn damage by this
var/atmosblock = FALSE //if the blob blocks atmos and heat spread
var/mob/camera/blob/overmind
/obj/structure/blob/Initialize(mapload, owner_overmind)
overmind = owner_overmind
var/area/Ablob = get_area(loc)
if(Ablob.blob_allowed) //Is this area allowed for winning as blob?
overmind.blobs_legit += src
GLOB.blobs += src //Keep track of the blob in the normal list either way
setDir(pick(GLOB.cardinals))
update_icon()
.= ..()
if(atmosblock)
air_update_turf(1)
ConsumeTile()
/obj/structure/blob/proc/creation_action() //When it's created by the overmind, do this.
return
/obj/structure/blob/Destroy()
if(atmosblock)
atmosblock = FALSE
air_update_turf(1)
if(overmind)
overmind.blobs_legit -= src //if it was in the legit blobs list, it isn't now
GLOB.blobs -= src //it's no longer in the all blobs list either
playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) //Expand() is no longer broken, no check necessary.
return ..()
/obj/structure/blob/blob_act()
return
/obj/structure/blob/Adjacent(var/atom/neighbour)
. = ..()
if(.)
var/result = 0
var/direction = get_dir(src, neighbour)
var/list/dirs = list("[NORTHWEST]" = list(NORTH, WEST), "[NORTHEAST]" = list(NORTH, EAST), "[SOUTHEAST]" = list(SOUTH, EAST), "[SOUTHWEST]" = list(SOUTH, WEST))
for(var/A in dirs)
if(direction == text2num(A))
for(var/B in dirs[A])
var/C = locate(/obj/structure/blob) in get_step(src, B)
if(C)
result++
. -= result - 1
/obj/structure/blob/BlockSuperconductivity()
return atmosblock
/obj/structure/blob/CanPass(atom/movable/mover, turf/target)
if(istype(mover) && (mover.pass_flags & PASSBLOB))
return 1
return 0
/obj/structure/blob/CanAtmosPass(turf/T)
return !atmosblock
/obj/structure/blob/CanAStarPass(ID, dir, caller)
. = 0
if(ismovableatom(caller))
var/atom/movable/mover = caller
. = . || (mover.pass_flags & PASSBLOB)
/obj/structure/blob/update_icon() //Updates color based on overmind color if we have an overmind.
if(overmind)
add_atom_colour(overmind.blob_reagent_datum.color, FIXED_COLOUR_PRIORITY)
else
remove_atom_colour(FIXED_COLOUR_PRIORITY)
/obj/structure/blob/process()
Life()
/obj/structure/blob/proc/Life()
return
/obj/structure/blob/proc/Pulse_Area(mob/camera/blob/pulsing_overmind, claim_range = 10, pulse_range = 3, expand_range = 2)
if(QDELETED(pulsing_overmind))
pulsing_overmind = overmind
Be_Pulsed()
var/expanded = FALSE
if(prob(70) && expand())
expanded = TRUE
var/list/blobs_to_affect = list()
for(var/obj/structure/blob/B in urange(claim_range, src, 1))
blobs_to_affect += B
shuffle_inplace(blobs_to_affect)
for(var/L in blobs_to_affect)
var/obj/structure/blob/B = L
if(!B.overmind && !istype(B, /obj/structure/blob/core) && prob(30))
B.overmind = pulsing_overmind //reclaim unclaimed, non-core blobs.
B.update_icon()
var/distance = get_dist(get_turf(src), get_turf(B))
var/expand_probablity = max(20 - distance * 8, 1)
if(B.Adjacent(src))
expand_probablity = 20
if(distance <= expand_range)
var/can_expand = TRUE
if(blobs_to_affect.len >= 120 && B.heal_timestamp > world.time)
can_expand = FALSE
if(can_expand && B.pulse_timestamp <= world.time && prob(expand_probablity))
var/obj/structure/blob/newB = B.expand(null, null, !expanded) //expansion falls off with range but is faster near the blob causing the expansion
if(newB)
if(expanded)
qdel(newB)
expanded = TRUE
if(distance <= pulse_range)
B.Be_Pulsed()
/obj/structure/blob/proc/Be_Pulsed()
if(pulse_timestamp <= world.time)
ConsumeTile()
if(heal_timestamp <= world.time)
obj_integrity = min(max_integrity, obj_integrity+health_regen)
heal_timestamp = world.time + 20
update_icon()
pulse_timestamp = world.time + 10
return 1 //we did it, we were pulsed!
return 0 //oh no we failed
/obj/structure/blob/proc/ConsumeTile()
for(var/atom/A in loc)
A.blob_act(src)
if(iswallturf(loc))
loc.blob_act(src) //don't ask how a wall got on top of the core, just eat it
/obj/structure/blob/proc/blob_attack_animation(atom/A = null, controller) //visually attacks an atom
var/obj/effect/temp_visual/blob/O = new /obj/effect/temp_visual/blob(src.loc)
O.setDir(dir)
if(controller)
var/mob/camera/blob/BO = controller
O.color = BO.blob_reagent_datum.color
O.alpha = 200
else if(overmind)
O.color = overmind.blob_reagent_datum.color
if(A)
O.do_attack_animation(A) //visually attack the whatever
return O //just in case you want to do something to the animation.
/obj/structure/blob/proc/expand(turf/T = null, controller = null, expand_reaction = 1)
if(!T)
var/list/dirs = list(1,2,4,8)
for(var/i = 1 to 4)
var/dirn = pick(dirs)
dirs.Remove(dirn)
T = get_step(src, dirn)
if(!(locate(/obj/structure/blob) in T))
break
else
T = null
if(!T)
return 0
var/make_blob = TRUE //can we make a blob?
if(isspaceturf(T) && !(locate(/obj/structure/lattice) in T) && prob(80))
make_blob = FALSE
playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) //Let's give some feedback that we DID try to spawn in space, since players are used to it
ConsumeTile() //hit the tile we're in, making sure there are no border objects blocking us
if(!T.CanPass(src, T)) //is the target turf impassable
make_blob = FALSE
T.blob_act(src) //hit the turf if it is
for(var/atom/A in T)
if(!A.CanPass(src, T)) //is anything in the turf impassable
make_blob = FALSE
A.blob_act(src) //also hit everything in the turf
if(make_blob) //well, can we?
var/obj/structure/blob/B = new /obj/structure/blob/normal(src.loc, (controller || overmind))
B.density = TRUE
if(T.Enter(B,src)) //NOW we can attempt to move into the tile
B.density = initial(B.density)
B.forceMove(T)
B.update_icon()
if(B.overmind && expand_reaction)
B.overmind.blob_reagent_datum.expand_reaction(src, B, T, controller)
return B
else
blob_attack_animation(T, controller)
T.blob_act(src) //if we can't move in hit the turf again
qdel(B) //we should never get to this point, since we checked before moving in. destroy the blob so we don't have two blobs on one tile
return null
else
blob_attack_animation(T, controller) //if we can't, animate that we attacked
return null
/obj/structure/blob/emp_act(severity)
if(severity > 0)
if(overmind)
overmind.blob_reagent_datum.emp_reaction(src, severity)
if(prob(100 - severity * 30))
new /obj/effect/temp_visual/emp(get_turf(src))
/obj/structure/blob/tesla_act(power)
..()
if(overmind)
if(overmind.blob_reagent_datum.tesla_reaction(src, power))
take_damage(power/400, BURN, "energy")
else
take_damage(power/400, BURN, "energy")
/obj/structure/blob/extinguish()
..()
if(overmind)
overmind.blob_reagent_datum.extinguish_reaction(src)
/obj/structure/blob/hulk_damage()
return 15
/obj/structure/blob/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/device/analyzer))
user.changeNext_move(CLICK_CD_MELEE)
to_chat(user, "<b>The analyzer beeps once, then reports:</b><br>")
SEND_SOUND(user, sound('sound/machines/ping.ogg'))
to_chat(user, "<b>Progress to Critical Mass:</b> <span class='notice'>[overmind.blobs_legit.len]/[overmind.blobwincount].</span>")
chemeffectreport(user)
typereport(user)
else
return ..()
/obj/structure/blob/proc/chemeffectreport(mob/user)
if(overmind)
to_chat(user, "<b>Material: <font color=\"[overmind.blob_reagent_datum.color]\">[overmind.blob_reagent_datum.name]</font><span class='notice'>.</span></b>")
to_chat(user, "<b>Material Effects:</b> <span class='notice'>[overmind.blob_reagent_datum.analyzerdescdamage]</span>")
to_chat(user, "<b>Material Properties:</b> <span class='notice'>[overmind.blob_reagent_datum.analyzerdesceffect]</span><br>")
else
to_chat(user, "<b>No Material Detected!</b><br>")
/obj/structure/blob/proc/typereport(mob/user)
to_chat(user, "<b>Blob Type:</b> <span class='notice'>[uppertext(initial(name))]</span>")
to_chat(user, "<b>Health:</b> <span class='notice'>[obj_integrity]/[max_integrity]</span>")
to_chat(user, "<b>Effects:</b> <span class='notice'>[scannerreport()]</span>")
/obj/structure/blob/attack_animal(mob/living/simple_animal/M)
if("blob" in M.faction) //sorry, but you can't kill the blob as a blobbernaut
return
..()
/obj/structure/blob/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
switch(damage_type)
if(BRUTE)
if(damage_amount)
playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1)
else
playsound(src, 'sound/weapons/tap.ogg', 50, 1)
if(BURN)
playsound(src.loc, 'sound/items/welder.ogg', 100, 1)
/obj/structure/blob/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir)
switch(damage_type)
if(BRUTE)
damage_amount *= brute_resist
if(BURN)
damage_amount *= fire_resist
if(CLONE)
else
return 0
var/armor_protection = 0
if(damage_flag)
armor_protection = armor[damage_flag]
damage_amount = round(damage_amount * (100 - armor_protection)*0.01, 0.1)
if(overmind && damage_flag)
damage_amount = overmind.blob_reagent_datum.damage_reaction(src, damage_amount, damage_type, damage_flag)
return damage_amount
/obj/structure/blob/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
. = ..()
if(. && obj_integrity > 0)
update_icon()
/obj/structure/blob/obj_destruction(damage_flag)
if(overmind)
overmind.blob_reagent_datum.death_reaction(src, damage_flag)
..()
/obj/structure/blob/proc/change_to(type, controller)
if(!ispath(type))
throw EXCEPTION("change_to(): invalid type for blob")
return
var/obj/structure/blob/B = new type(src.loc, controller)
B.creation_action()
B.update_icon()
B.setDir(dir)
qdel(src)
return B
/obj/structure/blob/examine(mob/user)
..()
var/datum/atom_hud/hud_to_check = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
if(user.research_scanner || hud_to_check.hudusers[user])
to_chat(user, "<b>Your HUD displays an extensive report...</b><br>")
to_chat(user, "<b>Progress to Critical Mass:</b> <span class='notice'>[overmind.blobs_legit.len]/[overmind.blobwincount].</span>")
chemeffectreport(user)
typereport(user)
else
if(isobserver(user))
to_chat(user, "<b>Progress to Critical Mass:</b> <span class='notice'>[overmind.blobs_legit.len]/[overmind.blobwincount].</span>")
to_chat(user, "It seems to be made of [get_chem_name()].")
/obj/structure/blob/proc/scannerreport()
return "A generic blob. Looks like someone forgot to override this proc, adminhelp this."
/obj/structure/blob/proc/get_chem_name()
if(overmind)
return overmind.blob_reagent_datum.name
return "an unknown variant"
/obj/structure/blob/normal
name = "normal blob"
icon_state = "blob"
light_range = 0
obj_integrity = 21 //doesn't start at full health
max_integrity = 25
health_regen = 1
brute_resist = 0.25
/obj/structure/blob/normal/scannerreport()
if(obj_integrity <= 15)
return "Currently weak to brute damage."
return "N/A"
/obj/structure/blob/normal/update_icon()
..()
if(obj_integrity <= 15)
icon_state = "blob_damaged"
name = "fragile blob"
desc = "A thin lattice of slightly twitching tendrils."
brute_resist = 0.5
else
icon_state = "blob"
name = "blob"
desc = "A thick wall of writhing tendrils."
brute_resist = 0.25
@@ -1,85 +0,0 @@
// cellular emporium
// The place where changelings go to buy their biological weaponry.
/datum/cellular_emporium
var/name = "cellular emporium"
var/datum/antagonist/changeling/changeling
/datum/cellular_emporium/New(my_changeling)
. = ..()
changeling = my_changeling
/datum/cellular_emporium/Destroy()
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)
if(!ui)
ui = new(user, src, ui_key, "cellular_emporium", name, 900, 480, master_ui, state)
ui.open()
/datum/cellular_emporium/ui_data(mob/user)
var/list/data = list()
var/can_readapt = changeling.canrespec
var/genetic_points_remaining = changeling.geneticpoints
var/absorbed_dna_count = changeling.absorbedcount
data["can_readapt"] = can_readapt
data["genetic_points_remaining"] = genetic_points_remaining
data["absorbed_dna_count"] = absorbed_dna_count
var/list/abilities = list()
for(var/path in changeling.all_powers)
var/obj/effect/proc_holder/changeling/ability = path
var/dna_cost = initial(ability.dna_cost)
if(dna_cost <= 0)
continue
var/list/AL = list()
AL["name"] = initial(ability.name)
AL["desc"] = initial(ability.desc)
AL["helptext"] = initial(ability.helptext)
AL["owned"] = changeling.has_sting(ability)
var/req_dna = initial(ability.req_dna)
AL["dna_cost"] = dna_cost
AL["can_purchase"] = ((req_dna <= absorbed_dna_count) && (dna_cost <= genetic_points_remaining))
abilities += list(AL)
data["abilities"] = abilities
return data
/datum/cellular_emporium/ui_act(action, params)
if(..())
return
switch(action)
if("readapt")
if(changeling.canrespec)
changeling.readapt()
if("evolve")
var/sting_name = params["name"]
changeling.purchase_power(sting_name)
/datum/action/innate/cellular_emporium
name = "Cellular Emporium"
icon_icon = 'icons/obj/drinks.dmi'
button_icon_state = "changelingsting"
background_icon_state = "bg_alien"
var/datum/cellular_emporium/cellular_emporium
/datum/action/innate/cellular_emporium/New(our_target)
. = ..()
button.name = name
if(istype(our_target, /datum/cellular_emporium))
cellular_emporium = our_target
else
throw EXCEPTION("cellular_emporium action created with non emporium")
/datum/action/innate/cellular_emporium/Activate()
cellular_emporium.ui_interact(owner)
@@ -1,78 +0,0 @@
/*
* Don't use the apostrophe in name or desc. Causes script errors.
* TODO: combine atleast some of the functionality with /proc_holder/spell
*/
/obj/effect/proc_holder/changeling
panel = "Changeling"
name = "Prototype Sting"
desc = "" // Fluff
var/helptext = "" // Details
var/chemical_cost = 0 // negative chemical cost is for passive abilities (chemical glands)
var/dna_cost = -1 //cost of the sting in dna points. 0 = auto-purchase, -1 = cannot be purchased
var/req_dna = 0 //amount of dna needed to use this ability. Changelings always have atleast 1
var/req_human = 0 //if you need to be human to use this ability
var/req_stat = CONSCIOUS // CONSCIOUS, UNCONSCIOUS or DEAD
var/always_keep = 0 // important for abilities like revive that screw you if you lose them.
var/ignores_fakedeath = FALSE // usable with the FAKEDEATH flag
/obj/effect/proc_holder/changeling/proc/on_purchase(mob/user, is_respec)
if(!is_respec)
SSblackbox.record_feedback("tally", "changeling_power_purchase", 1, name)
/obj/effect/proc_holder/changeling/proc/on_refund(mob/user)
return
/obj/effect/proc_holder/changeling/Click()
var/mob/user = usr
if(!user || !user.mind || !user.mind.has_antag_datum(/datum/antagonist/changeling))
return
try_to_sting(user)
/obj/effect/proc_holder/changeling/proc/try_to_sting(mob/user, mob/target)
if(!can_sting(user, target))
return
var/datum/antagonist/changeling/c = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(sting_action(user, target))
SSblackbox.record_feedback("nested tally", "changeling_powers", 1, list("[name]"))
sting_feedback(user, target)
c.chem_charges -= chemical_cost
/obj/effect/proc_holder/changeling/proc/sting_action(mob/user, mob/target)
return 0
/obj/effect/proc_holder/changeling/proc/sting_feedback(mob/user, mob/target)
return 0
//Fairly important to remember to return 1 on success >.<
/obj/effect/proc_holder/changeling/proc/can_sting(mob/user, mob/target)
if(!ishuman(user) && !ismonkey(user)) //typecast everything from mob to carbon from this point onwards
return 0
if(req_human && !ishuman(user))
to_chat(user, "<span class='warning'>We cannot do that in this form!</span>")
return 0
var/datum/antagonist/changeling/c = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(c.chem_charges < chemical_cost)
to_chat(user, "<span class='warning'>We require at least [chemical_cost] unit\s of chemicals to do that!</span>")
return 0
if(c.absorbedcount < req_dna)
to_chat(user, "<span class='warning'>We require at least [req_dna] sample\s of compatible DNA.</span>")
return 0
if(req_stat < user.stat)
to_chat(user, "<span class='warning'>We are incapacitated.</span>")
return 0
if((user.status_flags & FAKEDEATH) && (!ignores_fakedeath))
to_chat(user, "<span class='warning'>We are incapacitated.</span>")
return 0
return 1
//used in /mob/Stat()
/obj/effect/proc_holder/changeling/proc/can_be_used_by(mob/user)
if(!user || QDELETED(user))
return 0
if(!ishuman(user) && !ismonkey(user))
return 0
if(req_human && !ishuman(user))
return 0
return 1
@@ -1,113 +0,0 @@
/obj/effect/proc_holder/changeling/absorbDNA
name = "Absorb DNA"
desc = "Absorb the DNA of our victim."
chemical_cost = 0
dna_cost = 0
req_human = 1
/obj/effect/proc_holder/changeling/absorbDNA/can_sting(mob/living/carbon/user)
if(!..())
return
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(changeling.isabsorbing)
to_chat(user, "<span class='warning'>We are already absorbing!</span>")
return
if(!user.pulling || !iscarbon(user.pulling))
to_chat(user, "<span class='warning'>We must be grabbing a creature to absorb them!</span>")
return
if(user.grab_state <= GRAB_NECK)
to_chat(user, "<span class='warning'>We must have a tighter grip to absorb this creature!</span>")
return
var/mob/living/carbon/target = user.pulling
return changeling.can_absorb_dna(target)
/obj/effect/proc_holder/changeling/absorbDNA/sting_action(mob/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
var/mob/living/carbon/human/target = user.pulling
changeling.isabsorbing = 1
for(var/i in 1 to 3)
switch(i)
if(1)
to_chat(user, "<span class='notice'>This creature is compatible. We must hold still...</span>")
if(2)
user.visible_message("<span class='warning'>[user] extends a proboscis!</span>", "<span class='notice'>We extend a proboscis.</span>")
if(3)
user.visible_message("<span class='danger'>[user] stabs [target] with the proboscis!</span>", "<span class='notice'>We stab [target] with the proboscis.</span>")
to_chat(target, "<span class='userdanger'>You feel a sharp stabbing pain!</span>")
target.take_overall_damage(40)
SSblackbox.record_feedback("nested tally", "changeling_powers", 1, list("Absorb DNA", "[i]"))
if(!do_mob(user, target, 150))
to_chat(user, "<span class='warning'>Our absorption of [target] has been interrupted!</span>")
changeling.isabsorbing = 0
return
SSblackbox.record_feedback("nested tally", "changeling_powers", 1, list("Absorb DNA", "4"))
user.visible_message("<span class='danger'>[user] sucks the fluids from [target]!</span>", "<span class='notice'>We have absorbed [target].</span>")
to_chat(target, "<span class='userdanger'>You are absorbed by the changeling!</span>")
if(!changeling.has_dna(target.dna))
changeling.add_new_profile(target)
if(user.nutrition < NUTRITION_LEVEL_WELL_FED)
user.nutrition = min((user.nutrition + target.nutrition), NUTRITION_LEVEL_WELL_FED)
if(target.mind)//if the victim has got a mind
// Absorb a lizard, speak Draconic.
user.copy_known_languages_from(target)
target.mind.show_memory(user, 0) //I can read your mind, kekeke. Output all their notes.
//Some of target's recent speech, so the changeling can attempt to imitate them better.
//Recent as opposed to all because rounds tend to have a LOT of text.
var/list/recent_speech = list()
var/list/say_log = target.logging[INDIVIDUAL_SAY_LOG]
if(LAZYLEN(say_log) > LING_ABSORB_RECENT_SPEECH)
recent_speech = say_log.Copy(say_log.len-LING_ABSORB_RECENT_SPEECH+1,0) //0 so len-LING_ARS+1 to end of list
else
for(var/spoken_memory in say_log)
if(recent_speech.len >= LING_ABSORB_RECENT_SPEECH)
break
recent_speech[spoken_memory] = say_log[spoken_memory]
if(recent_speech.len)
changeling.antag_memory += "<B>Some of [target]'s speech patterns, we should study these to better impersonate them!</B><br>"
to_chat(user, "<span class='boldnotice'>Some of [target]'s speech patterns, we should study these to better impersonate them!</span>")
for(var/spoken_memory in recent_speech)
changeling.antag_memory += "\"[recent_speech[spoken_memory]]\"<br>"
to_chat(user, "<span class='notice'>\"[recent_speech[spoken_memory]]\"</span>")
changeling.antag_memory += "<B>We have no more knowledge of [target]'s speech patterns.</B><br>"
to_chat(user, "<span class='boldnotice'>We have no more knowledge of [target]'s speech patterns.</span>")
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!
changeling.chem_charges += min(target_ling.chem_charges, changeling.chem_storage)
changeling.absorbedcount += (target_ling.absorbedcount)
target_ling.stored_profiles.len = 1
target_ling.absorbedcount = 0
changeling.chem_charges=min(changeling.chem_charges+10, changeling.chem_storage)
changeling.isabsorbing = 0
changeling.canrespec = 1
target.death(0)
target.Drain()
return TRUE
//Absorbs the target DNA.
//datum/changeling/proc/absorb_dna(mob/living/carbon/T, mob/user)
//datum/changeling/proc/store_dna(datum/dna/new_dna, mob/user)
@@ -1,21 +0,0 @@
/obj/effect/proc_holder/changeling/adrenaline
name = "Adrenaline Sacs"
desc = "We evolve additional sacs of adrenaline throughout our body."
helptext = "Removes all stuns instantly and adds a short-term reduction in further stuns. Can be used while unconscious. Continued use poisons the body."
chemical_cost = 30
dna_cost = 2
req_human = 1
req_stat = UNCONSCIOUS
//Recover from stuns.
/obj/effect/proc_holder/changeling/adrenaline/sting_action(mob/living/user)
to_chat(user, "<span class='notice'>Energy rushes through us.[user.lying ? " We arise." : ""]</span>")
user.SetSleeping(0)
user.SetUnconscious(0)
user.SetStun(0)
user.SetKnockdown(0)
user.reagents.add_reagent("changelingAdrenaline", 10)
user.reagents.add_reagent("changelingAdrenaline2", 2) //For a really quick burst of speed
user.adjustStaminaLoss(-75)
return TRUE
@@ -1,51 +0,0 @@
//Augmented Eyesight: Gives you x-ray vision or protection from flashes. Also, high DNA cost because of how powerful it is.
//Possible todo: make a custom message for directing a penlight/flashlight at the eyes - not sure what would display though.
/obj/effect/proc_holder/changeling/augmented_eyesight
name = "Augmented Eyesight"
desc = "Creates heat receptors in our eyes and dramatically increases light sensing ability, or protects your vision from flashes."
helptext = "Grants us thermal vision or flash protection. We will become a lot more vulnerable to flash-based devices while thermal vision is active."
chemical_cost = 0
dna_cost = 2 //Would be 1 without thermal vision
active = FALSE
/obj/effect/proc_holder/changeling/augmented_eyesight/on_purchase(mob/user) //The ability starts inactive, so we should be protected from flashes.
var/obj/item/organ/eyes/E = user.getorganslot(ORGAN_SLOT_EYES)
if (E)
E.flash_protect = 2 //Adjust the user's eyes' flash protection
to_chat(user, "We adjust our eyes to protect them from bright lights.")
else
to_chat(user, "We can't adjust our eyes if we don't have any!")
/obj/effect/proc_holder/changeling/augmented_eyesight/sting_action(mob/living/carbon/human/user)
if(!istype(user))
return
var/obj/item/organ/eyes/E = user.getorganslot(ORGAN_SLOT_EYES)
if(E)
if(!active)
E.sight_flags |= SEE_MOBS | SEE_OBJS | SEE_TURFS //Add sight flags to the user's eyes
E.flash_protect = -1 //Adjust the user's eyes' flash protection
to_chat(user, "We adjust our eyes to sense prey through walls.")
active = TRUE //Defined in code/modules/spells/spell.dm
else
E.sight_flags ^= SEE_MOBS | SEE_OBJS | SEE_TURFS //Remove sight flags from the user's eyes
E.flash_protect = 2 //Adjust the user's eyes' flash protection
to_chat(user, "We adjust our eyes to protect them from bright lights.")
active = FALSE
user.update_sight()
else
to_chat(user, "We can't adjust our eyes if we don't have any!")
return 1
/obj/effect/proc_holder/changeling/augmented_eyesight/on_refund(mob/user) //Get rid of x-ray vision and flash protection when the user refunds this ability
var/obj/item/organ/eyes/E = user.getorganslot(ORGAN_SLOT_EYES)
if(E)
if (active)
E.sight_flags ^= SEE_MOBS | SEE_OBJS | SEE_TURFS
else
E.flash_protect = 0
user.update_sight()
@@ -1,81 +0,0 @@
/obj/effect/proc_holder/changeling/biodegrade
name = "Biodegrade"
desc = "Dissolves restraints or other objects preventing free movement."
helptext = "This is obvious to nearby people, and can destroy standard restraints and closets."
chemical_cost = 30 //High cost to prevent spam
dna_cost = 2
req_human = 1
/obj/effect/proc_holder/changeling/biodegrade/sting_action(mob/living/carbon/human/user)
var/used = FALSE // only one form of shackles removed per use
if(!user.restrained() && isopenturf(user.loc))
to_chat(user, "<span class='warning'>We are already free!</span>")
return 0
if(user.handcuffed)
var/obj/O = user.get_item_by_slot(slot_handcuffed)
if(!istype(O))
return 0
user.visible_message("<span class='warning'>[user] vomits a glob of acid on [user.p_their()] [O]!</span>", \
"<span class='warning'>We vomit acidic ooze onto our restraints!</span>")
addtimer(CALLBACK(src, .proc/dissolve_handcuffs, user, O), 30)
used = TRUE
if(user.wear_suit && user.wear_suit.breakouttime && !used)
var/obj/item/clothing/suit/S = user.get_item_by_slot(slot_wear_suit)
if(!istype(S))
return 0
user.visible_message("<span class='warning'>[user] vomits a glob of acid across the front of [user.p_their()] [S]!</span>", \
"<span class='warning'>We vomit acidic ooze onto our straight jacket!</span>")
addtimer(CALLBACK(src, .proc/dissolve_straightjacket, user, S), 30)
used = TRUE
if(istype(user.loc, /obj/structure/closet) && !used)
var/obj/structure/closet/C = user.loc
if(!istype(C))
return 0
C.visible_message("<span class='warning'>[C]'s hinges suddenly begin to melt and run!</span>")
to_chat(user, "<span class='warning'>We vomit acidic goop onto the interior of [C]!</span>")
addtimer(CALLBACK(src, .proc/open_closet, user, C), 70)
used = TRUE
if(istype(user.loc, /obj/structure/spider/cocoon) && !used)
var/obj/structure/spider/cocoon/C = user.loc
if(!istype(C))
return 0
C.visible_message("<span class='warning'>[src] shifts and starts to fall apart!</span>")
to_chat(user, "<span class='warning'>We secrete acidic enzymes from our skin and begin melting our cocoon...</span>")
addtimer(CALLBACK(src, .proc/dissolve_cocoon, user, C), 25) //Very short because it's just webs
used = TRUE
return used
/obj/effect/proc_holder/changeling/biodegrade/proc/dissolve_handcuffs(mob/living/carbon/human/user, obj/O)
if(O && user.handcuffed == O)
user.visible_message("<span class='warning'>[O] dissolve[O.gender==PLURAL?"":"s"] into a puddle of sizzling goop.</span>")
new /obj/effect/decal/cleanable/greenglow(O.drop_location())
qdel(O)
/obj/effect/proc_holder/changeling/biodegrade/proc/dissolve_straightjacket(mob/living/carbon/human/user, obj/S)
if(S && user.wear_suit == S)
user.visible_message("<span class='warning'>[S] dissolves into a puddle of sizzling goop.</span>")
new /obj/effect/decal/cleanable/greenglow(S.drop_location())
qdel(S)
/obj/effect/proc_holder/changeling/biodegrade/proc/open_closet(mob/living/carbon/human/user, obj/structure/closet/C)
if(C && user.loc == C)
C.visible_message("<span class='warning'>[C]'s door breaks and opens!</span>")
new /obj/effect/decal/cleanable/greenglow(C.drop_location())
C.welded = FALSE
C.locked = FALSE
C.broken = TRUE
C.open()
to_chat(user, "<span class='warning'>We open the container restraining us!</span>")
/obj/effect/proc_holder/changeling/biodegrade/proc/dissolve_cocoon(mob/living/carbon/human/user, obj/structure/spider/cocoon/C)
if(C && user.loc == C)
new /obj/effect/decal/cleanable/greenglow(C.drop_location())
qdel(C) //The cocoon's destroy will move the changeling outside of it without interference
to_chat(user, "<span class='warning'>We dissolve the cocoon!</span>")
@@ -1,25 +0,0 @@
/obj/effect/proc_holder/changeling/chameleon_skin
name = "Chameleon Skin"
desc = "Our skin pigmentation rapidly changes to suit our current environment."
helptext = "Allows us to become invisible after a few seconds of standing still. Can be toggled on and off."
dna_cost = 2
chemical_cost = 25
req_human = 1
/obj/effect/proc_holder/changeling/chameleon_skin/sting_action(mob/user)
var/mob/living/carbon/human/H = user //SHOULD always be human, because req_human = 1
if(!istype(H)) // req_human could be done in can_sting stuff.
return
var/datum/mutation/human/HM = GLOB.mutations_list[CHAMELEON]
if(HM in H.dna.mutations)
HM.force_lose(H)
else
HM.force_give(H)
return TRUE
/obj/effect/proc_holder/changeling/chameleon_skin/on_refund(mob/user)
if(user.has_dna())
var/mob/living/carbon/C = user
var/datum/mutation/human/HM = GLOB.mutations_list[CHAMELEON]
if(HM in C.dna.mutations)
HM.force_lose(C)
@@ -1,22 +0,0 @@
/obj/effect/proc_holder/changeling/digitalcamo
name = "Digital Camouflage"
desc = "By evolving the ability to distort our form and proprotions, we defeat common altgorithms used to detect lifeforms on cameras."
helptext = "We cannot be tracked by camera or seen by AI units while using this skill. However, humans looking at us will find us... uncanny."
dna_cost = 1
//Prevents AIs tracking you but makes you easily detectable to the human-eye.
/obj/effect/proc_holder/changeling/digitalcamo/sting_action(mob/user)
if(user.digitalcamo)
to_chat(user, "<span class='notice'>We return to normal.</span>")
user.digitalinvis = 0
user.digitalcamo = 0
else
to_chat(user, "<span class='notice'>We distort our form to hide from the AI</span>")
user.digitalcamo = 1
user.digitalinvis = 1
return TRUE
/obj/effect/proc_holder/changeling/digitalcamo/on_refund(mob/user)
user.digitalcamo = 0
user.digitalinvis = 0
@@ -1,37 +0,0 @@
/obj/effect/proc_holder/changeling/fakedeath
name = "Reviving Stasis"
desc = "We fall into a stasis, allowing us to regenerate and trick our enemies."
chemical_cost = 15
dna_cost = 0
req_dna = 1
req_stat = DEAD
//Fake our own death and fully heal. You will appear to be dead but regenerate fully after a short delay.
/obj/effect/proc_holder/changeling/fakedeath/sting_action(mob/living/user)
to_chat(user, "<span class='notice'>We begin our stasis, preparing energy to arise once more.</span>")
if(user.stat != DEAD)
user.emote("deathgasp")
user.tod = worldtime2text()
user.status_flags |= FAKEDEATH //play dead
user.update_stat()
user.update_canmove()
addtimer(CALLBACK(src, .proc/ready_to_regenerate, user), LING_FAKEDEATH_TIME, TIMER_UNIQUE)
return TRUE
/obj/effect/proc_holder/changeling/fakedeath/proc/ready_to_regenerate(mob/user)
if(user && user.mind)
var/datum/antagonist/changeling/C = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(C && C.purchasedpowers)
to_chat(user, "<span class='notice'>We are ready to revive.</span>")
C.purchasedpowers += new /obj/effect/proc_holder/changeling/revive(null)
/obj/effect/proc_holder/changeling/fakedeath/can_sting(mob/user)
if(user.status_flags & FAKEDEATH)
to_chat(user, "<span class='warning'>We are already reviving.</span>")
return
if(!user.stat) //Confirmation for living changelings if they want to fake their death
switch(alert("Are we sure we wish to fake our own death?",,"Yes", "No"))
if("No")
return
return ..()
@@ -1,19 +0,0 @@
/obj/effect/proc_holder/changeling/fleshmend
name = "Fleshmend"
desc = "Our flesh rapidly regenerates, healing our burns, bruises, and shortness of breath. Functions while unconscious."
helptext = "If we are on fire, the healing effect will not function. Does not regrow limbs or restore lost blood."
chemical_cost = 20
dna_cost = 2
req_stat = UNCONSCIOUS
//Starts healing you every second for 10 seconds.
//Can be used whilst unconscious.
/obj/effect/proc_holder/changeling/fleshmend/sting_action(mob/living/user)
if(user.has_status_effect(STATUS_EFFECT_FLESHMEND))
to_chat(user, "<span class='warning'>We are already fleshmending!</span>")
return
to_chat(user, "<span class='notice'>We begin to heal rapidly.</span>")
user.apply_status_effect(STATUS_EFFECT_FLESHMEND)
return TRUE
//Check buffs.dm for the fleshmend status effect code
@@ -1,40 +0,0 @@
/obj/effect/proc_holder/changeling/headcrab
name = "Last Resort"
desc = "We sacrifice our current body in a moment of need, placing us in control of a vessel."
helptext = "We will be placed in control of a small, fragile creature. We may attack a corpse like this to plant an egg which will slowly mature into a new form for us."
chemical_cost = 20
dna_cost = 1
req_human = 1
/obj/effect/proc_holder/changeling/headcrab/sting_action(mob/user)
set waitfor = FALSE
if(alert("Are we sure we wish to kill ourself and create a headslug?",,"Yes", "No") == "No")
return
var/datum/mind/M = user.mind
var/list/organs = user.getorganszone("head", 1)
for(var/obj/item/organ/I in organs)
I.Remove(user, 1)
explosion(get_turf(user),0,0,2,0,silent=1)
for(var/mob/living/carbon/human/H in range(2,user))
to_chat(H, "<span class='userdanger'>You are blinded by a shower of blood!</span>")
H.Stun(20)
H.blur_eyes(20)
H.adjust_eye_damage(5)
H.confused += 3
for(var/mob/living/silicon/S in range(2,user))
to_chat(S, "<span class='userdanger'>Your sensors are disabled by a shower of blood!</span>")
S.Knockdown(60)
var/turf = get_turf(user)
user.gib()
. = TRUE
sleep(5) // So it's not killed in explosion
var/mob/living/simple_animal/hostile/headcrab/crab = new(turf)
for(var/obj/item/organ/I in organs)
I.forceMove(crab)
crab.origin = M
if(crab.origin)
crab.origin.active = 1
crab.origin.transfer_to(crab)
to_chat(crab, "<span class='warning'>You burst out of the remains of your former body in a shower of gore!</span>")
@@ -1,93 +0,0 @@
//HIVEMIND COMMUNICATION (:g)
/obj/effect/proc_holder/changeling/hivemind_comms
name = "Hivemind Communication"
desc = "We tune our senses to the airwaves to allow us to discreetly communicate and exchange DNA with other changelings."
helptext = "We will be able to talk with other changelings with :g. Exchanged DNA do not count towards absorb objectives."
dna_cost = 0
chemical_cost = -1
/obj/effect/proc_holder/changeling/hivemind_comms/on_purchase(mob/user, is_respec)
..()
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
changeling.changeling_speak = 1
to_chat(user, "<i><font color=#800080>Use say \":g message\" to communicate with the other changelings.</font></i>")
var/obj/effect/proc_holder/changeling/hivemind_upload/S1 = new
if(!changeling.has_sting(S1))
changeling.purchasedpowers+=S1
var/obj/effect/proc_holder/changeling/hivemind_download/S2 = new
if(!changeling.has_sting(S2))
changeling.purchasedpowers+=S2
// HIVE MIND UPLOAD/DOWNLOAD DNA
GLOBAL_LIST_EMPTY(hivemind_bank)
/obj/effect/proc_holder/changeling/hivemind_upload
name = "Hive Channel DNA"
desc = "Allows us to channel DNA in the airwaves to allow other changelings to absorb it."
chemical_cost = 10
dna_cost = -1
/obj/effect/proc_holder/changeling/hivemind_upload/sting_action(var/mob/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
var/list/names = list()
for(var/datum/changelingprofile/prof in changeling.stored_profiles)
if(!(prof in GLOB.hivemind_bank))
names += prof.name
if(names.len <= 0)
to_chat(user, "<span class='notice'>The airwaves already have all of our DNA.</span>")
return
var/chosen_name = input("Select a DNA to channel: ", "Channel DNA", null) as null|anything in names
if(!chosen_name)
return
var/datum/changelingprofile/chosen_dna = changeling.get_dna(chosen_name)
if(!chosen_dna)
return
var/datum/changelingprofile/uploaded_dna = new chosen_dna.type
chosen_dna.copy_profile(uploaded_dna)
GLOB.hivemind_bank += uploaded_dna
to_chat(user, "<span class='notice'>We channel the DNA of [chosen_name] to the air.</span>")
return TRUE
/obj/effect/proc_holder/changeling/hivemind_download
name = "Hive Absorb DNA"
desc = "Allows us to absorb DNA that has been channeled to the airwaves. Does not count towards absorb objectives."
chemical_cost = 10
dna_cost = -1
/obj/effect/proc_holder/changeling/hivemind_download/can_sting(mob/living/carbon/user)
if(!..())
return
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
var/datum/changelingprofile/first_prof = changeling.stored_profiles[1]
if(first_prof.name == user.real_name)//If our current DNA is the stalest, we gotta ditch it.
to_chat(user, "<span class='warning'>We have reached our capacity to store genetic information! We must transform before absorbing more.</span>")
return
return 1
/obj/effect/proc_holder/changeling/hivemind_download/sting_action(mob/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
var/list/names = list()
for(var/datum/changelingprofile/prof in GLOB.hivemind_bank)
if(!(prof in changeling.stored_profiles))
names[prof.name] = prof
if(names.len <= 0)
to_chat(user, "<span class='notice'>There's no new DNA to absorb from the air.</span>")
return
var/S = input("Select a DNA absorb from the air: ", "Absorb DNA", null) as null|anything in names
if(!S)
return
var/datum/changelingprofile/chosen_prof = names[S]
if(!chosen_prof)
return
var/datum/changelingprofile/downloaded_prof = new chosen_prof.type
chosen_prof.copy_profile(downloaded_prof)
changeling.add_profile(downloaded_prof)
to_chat(user, "<span class='notice'>We absorb the DNA of [S] from the air.</span>")
return TRUE
@@ -1,30 +0,0 @@
/obj/effect/proc_holder/changeling/humanform
name = "Human Form"
desc = "We change into a human."
chemical_cost = 5
req_dna = 1
//Transform into a human.
/obj/effect/proc_holder/changeling/humanform/sting_action(mob/living/carbon/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
var/list/names = list()
for(var/datum/changelingprofile/prof in changeling.stored_profiles)
names += "[prof.name]"
var/chosen_name = input("Select the target DNA: ", "Target DNA", null) as null|anything in names
if(!chosen_name)
return
var/datum/changelingprofile/chosen_prof = changeling.get_dna(chosen_name)
if(!chosen_prof)
return
if(!user || user.notransform)
return 0
to_chat(user, "<span class='notice'>We transform our appearance.</span>")
changeling.purchasedpowers -= src
var/newmob = user.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS)
changeling_transform(newmob, chosen_prof)
return TRUE
@@ -1,15 +0,0 @@
/obj/effect/proc_holder/changeling/lesserform
name = "Lesser Form"
desc = "We debase ourselves and become lesser. We become a monkey."
chemical_cost = 5
dna_cost = 1
req_human = 1
//Transform into a monkey.
/obj/effect/proc_holder/changeling/lesserform/sting_action(mob/living/carbon/human/user)
if(!user || user.notransform)
return 0
to_chat(user, "<span class='warning'>Our genes cry out!</span>")
user.monkeyize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSE)
return TRUE
@@ -1,70 +0,0 @@
/obj/effect/proc_holder/changeling/linglink
name = "Hivemind Link"
desc = "Link your victim's mind into the hivemind for personal interrogation."
chemical_cost = 0
dna_cost = 0
req_human = 1
/obj/effect/proc_holder/changeling/linglink/can_sting(mob/living/carbon/user)
if(!..())
return
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(changeling.islinking)
to_chat(user, "<span class='warning'>We have already formed a link with the victim!</span>")
return
if(!user.pulling)
to_chat(user, "<span class='warning'>We must be tightly grabbing a creature to link with them!</span>")
return
if(!iscarbon(user.pulling))
to_chat(user, "<span class='warning'>We cannot link with this creature!</span>")
return
var/mob/living/carbon/target = user.pulling
if(!target.mind)
to_chat(user, "<span class='warning'>The victim has no mind to link to!</span>")
return
if(target.stat == DEAD)
to_chat(user, "<span class='warning'>The victim is dead, you cannot link to a dead mind!</span>")
return
if(target.mind.has_antag_datum(/datum/antagonist/changeling))
to_chat(user, "<span class='warning'>The victim is already a part of the hivemind!</span>")
return
if(user.grab_state <= GRAB_AGGRESSIVE)
to_chat(user, "<span class='warning'>We must have a tighter grip to link with this creature!</span>")
return
return changeling.can_absorb_dna(target)
/obj/effect/proc_holder/changeling/linglink/sting_action(mob/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
var/mob/living/carbon/human/target = user.pulling
changeling.islinking = 1
for(var/i in 1 to 3)
switch(i)
if(1)
to_chat(user, "<span class='notice'>This creature is compatible. We must hold still...</span>")
if(2)
to_chat(user, "<span class='notice'>We stealthily stab [target] with a minor proboscis...</span>")
to_chat(target, "<span class='userdanger'>You experience a stabbing sensation and your ears begin to ring...</span>")
if(3)
to_chat(user, "<span class='notice'>We mold the [target]'s mind like clay, granting [target.p_them()] the ability to speak in the hivemind!</span>")
to_chat(target, "<span class='userdanger'>A migraine throbs behind your eyes, you hear yourself screaming - but your mouth has not opened!</span>")
for(var/mi in GLOB.mob_list)
var/mob/M = mi
if(M.lingcheck() == LINGHIVE_LING)
to_chat(M, "<i><font color=#800080>We can sense a foreign presence in the hivemind...</font></i>")
target.mind.linglink = 1
target.say(":g AAAAARRRRGGGGGHHHHH!!")
to_chat(target, "<font color=#800040><span class='boldannounce'>You can now communicate in the changeling hivemind, say \":g message\" to communicate!</span>")
target.reagents.add_reagent("salbutamol", 40) // So they don't choke to death while you interrogate them
sleep(1800)
SSblackbox.record_feedback("nested tally", "changeling_powers", 1, list("[name]", "[i]"))
if(!do_mob(user, target, 20))
to_chat(user, "<span class='warning'>Our link with [target] has ended!</span>")
changeling.islinking = 0
target.mind.linglink = 0
return
changeling.islinking = 0
target.mind.linglink = 0
to_chat(user, "<span class='notice'>You cannot sustain the connection any longer, your victim fades from the hivemind</span>")
to_chat(target, "<span class='userdanger'>The link cannot be sustained any longer, your connection to the hivemind has faded!</span>")
@@ -1,27 +0,0 @@
/obj/effect/proc_holder/changeling/mimicvoice
name = "Mimic Voice"
desc = "We shape our vocal glands to sound like a desired voice."
helptext = "Will turn your voice into the name that you enter. We must constantly expend chemicals to maintain our form like this."
chemical_cost = 0 //constant chemical drain hardcoded
dna_cost = 1
req_human = 1
// Fake Voice
/obj/effect/proc_holder/changeling/mimicvoice/sting_action(mob/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(changeling.mimicing)
changeling.mimicing = ""
changeling.chem_recharge_slowdown -= 0.5
to_chat(user, "<span class='notice'>We return our vocal glands to their original position.</span>")
return
var/mimic_voice = stripped_input(user, "Enter a name to mimic.", "Mimic Voice", null, MAX_NAME_LEN)
if(!mimic_voice)
return
changeling.mimicing = mimic_voice
changeling.chem_recharge_slowdown += 0.5
to_chat(user, "<span class='notice'>We shape our glands to take the voice of <b>[mimic_voice]</b>, this will slow down regenerating chemicals while active.</span>")
to_chat(user, "<span class='notice'>Use this power again to return to our original voice and return chemical production to normal levels.</span>")
return TRUE
@@ -1,509 +0,0 @@
/*
Changeling Mutations! ~By Miauw (ALL OF IT :V)
Contains:
Arm Blade
Space Suit
Shield
Armor
Tentacles
*/
//Parent to shields and blades because muh copypasted code.
/obj/effect/proc_holder/changeling/weapon
name = "Organic Weapon"
desc = "Go tell a coder if you see this"
helptext = "Yell at Miauw and/or Perakp"
chemical_cost = 1000
dna_cost = -1
var/silent = FALSE
var/weapon_type
var/weapon_name_simple
/obj/effect/proc_holder/changeling/weapon/try_to_sting(mob/user, mob/target)
for(var/obj/item/I in user.held_items)
if(check_weapon(user, I))
return
..(user, target)
/obj/effect/proc_holder/changeling/weapon/proc/check_weapon(mob/user, obj/item/hand_item)
if(istype(hand_item, weapon_type))
user.temporarilyRemoveItemFromInventory(hand_item, TRUE) //DROPDEL_1 will delete the item
if(!silent)
playsound(user, 'sound/effects/blobattack.ogg', 30, 1)
user.visible_message("<span class='warning'>With a sickening crunch, [user] reforms their [weapon_name_simple] into an arm!</span>", "<span class='notice'>We assimilate the [weapon_name_simple] back into our body.</span>", "<span class='italics>You hear organic matter ripping and tearing!</span>")
user.update_inv_hands()
return 1
/obj/effect/proc_holder/changeling/weapon/sting_action(mob/living/user)
var/obj/item/held = user.get_active_held_item()
if(held && !user.dropItemToGround(held))
to_chat(user, "<span class='warning'>[held] is stuck to your hand, you cannot grow a [weapon_name_simple] over it!</span>")
return
var/limb_regen = 0
if(user.active_hand_index % 2 == 0) //we regen the arm before changing it into the weapon
limb_regen = user.regenerate_limb("r_arm", 1)
else
limb_regen = user.regenerate_limb("l_arm", 1)
if(limb_regen)
user.visible_message("<span class='warning'>[user]'s missing arm reforms, making a loud, grotesque sound!</span>", "<span class='userdanger'>Your arm regrows, making a loud, crunchy sound and giving you great pain!</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
user.emote("scream")
var/obj/item/W = new weapon_type(user, silent)
user.put_in_hands(W)
if(!silent)
playsound(user, 'sound/effects/blobattack.ogg', 30, 1)
return W
/obj/effect/proc_holder/changeling/weapon/on_refund(mob/user)
for(var/obj/item/I in user.held_items)
check_weapon(user, I)
//Parent to space suits and armor.
/obj/effect/proc_holder/changeling/suit
name = "Organic Suit"
desc = "Go tell a coder if you see this"
helptext = "Yell at Miauw and/or Perakp"
chemical_cost = 1000
dna_cost = -1
var/helmet_type = /obj/item
var/suit_type = /obj/item
var/suit_name_simple = " "
var/helmet_name_simple = " "
var/recharge_slowdown = 0
var/blood_on_castoff = 0
/obj/effect/proc_holder/changeling/suit/try_to_sting(mob/user, mob/target)
if(check_suit(user))
return
var/mob/living/carbon/human/H = user
..(H, target)
//checks if we already have an organic suit and casts it off.
/obj/effect/proc_holder/changeling/suit/proc/check_suit(mob/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(!ishuman(user) || !changeling)
return 1
var/mob/living/carbon/human/H = user
if(istype(H.wear_suit, suit_type) || istype(H.head, helmet_type))
H.visible_message("<span class='warning'>[H] casts off their [suit_name_simple]!</span>", "<span class='warning'>We cast off our [suit_name_simple].</span>", "<span class='italics'>You hear the organic matter ripping and tearing!</span>")
H.temporarilyRemoveItemFromInventory(H.head, TRUE) //The qdel on dropped() takes care of it
H.temporarilyRemoveItemFromInventory(H.wear_suit, TRUE)
H.update_inv_wear_suit()
H.update_inv_head()
H.update_hair()
if(blood_on_castoff)
H.add_splatter_floor()
playsound(H.loc, 'sound/effects/splat.ogg', 50, 1) //So real sounds
changeling.chem_recharge_slowdown -= recharge_slowdown
return 1
/obj/effect/proc_holder/changeling/suit/on_refund(mob/user)
if(!ishuman(user))
return
var/mob/living/carbon/human/H = user
check_suit(H)
/obj/effect/proc_holder/changeling/suit/sting_action(mob/living/carbon/human/user)
if(!user.canUnEquip(user.wear_suit))
to_chat(user, "\the [user.wear_suit] is stuck to your body, you cannot grow a [suit_name_simple] over it!")
return
if(!user.canUnEquip(user.head))
to_chat(user, "\the [user.head] is stuck on your head, you cannot grow a [helmet_name_simple] over it!")
return
user.dropItemToGround(user.head)
user.dropItemToGround(user.wear_suit)
user.equip_to_slot_if_possible(new suit_type(user), slot_wear_suit, 1, 1, 1)
user.equip_to_slot_if_possible(new helmet_type(user), slot_head, 1, 1, 1)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
changeling.chem_recharge_slowdown += recharge_slowdown
return TRUE
//fancy headers yo
/***************************************\
|***************ARM BLADE***************|
\***************************************/
/obj/effect/proc_holder/changeling/weapon/arm_blade
name = "Arm Blade"
desc = "We reform one of our arms into a deadly blade."
helptext = "We may retract our armblade in the same manner as we form it. Cannot be used while in lesser form."
chemical_cost = 20
dna_cost = 2
req_human = 1
weapon_type = /obj/item/melee/arm_blade
weapon_name_simple = "blade"
/obj/item/melee/arm_blade
name = "arm blade"
desc = "A grotesque blade made out of bone and flesh that cleaves through people as a hot knife through butter."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "arm_blade"
item_state = "arm_blade"
lefthand_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi'
flags_1 = ABSTRACT_1 | NODROP_1 | DROPDEL_1
w_class = WEIGHT_CLASS_HUGE
force = 25
throwforce = 0 //Just to be on the safe side
throw_range = 0
throw_speed = 0
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
sharpness = IS_SHARP
var/can_drop = FALSE
/obj/item/melee/arm_blade/Initialize(mapload,silent,synthetic)
. = ..()
if(ismob(loc) && !silent)
loc.visible_message("<span class='warning'>A grotesque blade forms around [loc.name]\'s arm!</span>", "<span class='warning'>Our arm twists and mutates, transforming it into a deadly blade.</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
if(synthetic)
can_drop = TRUE
/obj/item/melee/arm_blade/afterattack(atom/target, mob/user, proximity)
if(!proximity)
return
if(istype(target, /obj/structure/table))
var/obj/structure/table/T = target
T.deconstruct(FALSE)
else if(istype(target, /obj/machinery/computer))
var/obj/machinery/computer/C = target
C.attack_alien(user) //muh copypasta
else if(istype(target, /obj/machinery/door/airlock))
var/obj/machinery/door/airlock/A = target
if(!A.requiresID() || A.allowed(user)) //This is to prevent stupid shit like hitting a door with an arm blade, the door opening because you have acces and still getting a "the airlocks motors resist our efforts to force it" message.
return
if(A.locked)
to_chat(user, "<span class='warning'>The airlock's bolts prevent it from being forced!</span>")
return
if(A.hasPower())
user.visible_message("<span class='warning'>[user] jams [src] into the airlock and starts prying it open!</span>", "<span class='warning'>We start forcing the airlock open.</span>", \
"<span class='italics'>You hear a metal screeching sound.</span>")
playsound(A, 'sound/machines/airlock_alien_prying.ogg', 100, 1)
if(!do_after(user, 100, target = A))
return
//user.say("Heeeeeeeeeerrre's Johnny!")
user.visible_message("<span class='warning'>[user] forces the airlock to open with their [src]!</span>", "<span class='warning'>We force the airlock to open.</span>", \
"<span class='italics'>You hear a metal screeching sound.</span>")
A.open(2)
/obj/item/melee/arm_blade/dropped(mob/user)
..()
if(can_drop)
new /obj/item/melee/synthetic_arm_blade(get_turf(user))
/***************************************\
|***********COMBAT TENTACLES*************|
\***************************************/
/obj/effect/proc_holder/changeling/weapon/tentacle
name = "Tentacle"
desc = "We ready a tentacle to grab items or victims with."
helptext = "We can use it once to retrieve a distant item. If used on living creatures, the effect depends on the intent: \
Help will simply drag them closer, Disarm will grab whatever they're holding instead of them, Grab will put the victim in our hold after catching it, \
and Harm will stun it, and stab it if we're also holding a sharp weapon. Cannot be used while in lesser form."
chemical_cost = 10
dna_cost = 2
req_human = 1
weapon_type = /obj/item/gun/magic/tentacle
weapon_name_simple = "tentacle"
silent = TRUE
/obj/item/gun/magic/tentacle
name = "tentacle"
desc = "A fleshy tentacle that can stretch out and grab things or people."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "tentacle"
item_state = "tentacle"
lefthand_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi'
flags_1 = ABSTRACT_1 | NODROP_1 | DROPDEL_1 | NOBLUDGEON_1
w_class = WEIGHT_CLASS_HUGE
ammo_type = /obj/item/ammo_casing/magic/tentacle
fire_sound = 'sound/effects/splat.ogg'
force = 0
max_charges = 1
throwforce = 0 //Just to be on the safe side
throw_range = 0
throw_speed = 0
/obj/item/gun/magic/tentacle/Initialize(mapload, silent)
. = ..()
if(ismob(loc))
if(!silent)
loc.visible_message("<span class='warning'>[loc.name]\'s arm starts stretching inhumanly!</span>", "<span class='warning'>Our arm twists and mutates, transforming it into a tentacle.</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
else
to_chat(loc, "<span class='notice'>You prepare to extend a tentacle.</span>")
/obj/item/gun/magic/tentacle/shoot_with_empty_chamber(mob/living/user as mob|obj)
to_chat(user, "<span class='warning'>The [name] is not ready yet.</span>")
/obj/item/gun/magic/tentacle/suicide_act(mob/user)
user.visible_message("<span class='suicide'>[user] coils [src] tightly around [user.p_their()] neck! It looks like [user.p_theyre()] trying to commit suicide!</span>")
return (OXYLOSS)
/obj/item/ammo_casing/magic/tentacle
name = "tentacle"
desc = "A tentacle."
projectile_type = /obj/item/projectile/tentacle
caliber = "tentacle"
icon_state = "tentacle_end"
firing_effect_type = null
var/obj/item/gun/magic/tentacle/gun //the item that shot it
/obj/item/ammo_casing/magic/tentacle/Initialize()
gun = loc
. = ..()
/obj/item/ammo_casing/magic/tentacle/Destroy()
gun = null
return ..()
/obj/item/projectile/tentacle
name = "tentacle"
icon_state = "tentacle_end"
pass_flags = PASSTABLE
damage = 0
damage_type = BRUTE
range = 8
hitsound = 'sound/weapons/thudswoosh.ogg'
var/chain
var/obj/item/ammo_casing/magic/tentacle/source //the item that shot it
/obj/item/projectile/tentacle/Initialize()
source = loc
. = ..()
/obj/item/projectile/tentacle/fire(setAngle)
if(firer)
chain = firer.Beam(src, icon_state = "tentacle", time = INFINITY, maxdistance = INFINITY, beam_sleep_time = 1)
..()
/obj/item/projectile/tentacle/proc/reset_throw(mob/living/carbon/human/H)
if(H.in_throw_mode)
H.throw_mode_off() //Don't annoy the changeling if he doesn't catch the item
/obj/item/projectile/tentacle/proc/tentacle_grab(mob/living/carbon/human/H, mob/living/carbon/C)
if(H.Adjacent(C))
C.grabbedby(H)
C.grippedby(H) //instant aggro grab
/obj/item/projectile/tentacle/proc/tentacle_stab(mob/living/carbon/human/H, mob/living/carbon/C)
if(H.Adjacent(C))
for(var/obj/item/I in H.held_items)
if(I.is_sharp())
C.visible_message("<span class='danger'>[H] impales [C] with [H.p_their()] [I.name]!</span>", "<span class='userdanger'>[H] impales you with [H.p_their()] [I.name]!</span>")
C.apply_damage(I.force, BRUTE, "chest")
H.do_item_attack_animation(C, used_item = I)
H.add_mob_blood(C)
playsound(get_turf(H),I.hitsound,75,1)
return
/obj/item/projectile/tentacle/on_hit(atom/target, blocked = FALSE)
var/mob/living/carbon/human/H = firer
H.dropItemToGround(source.gun, TRUE) //Unequip thus delete the tentacle on hit
if(blocked >= 100)
return 0
if(isitem(target))
var/obj/item/I = target
if(!I.anchored)
to_chat(firer, "<span class='notice'>You pull [I] towards yourself.</span>")
H.throw_mode_on()
I.throw_at(H, 10, 2)
. = 1
else if(isliving(target))
var/mob/living/L = target
if(!L.anchored && !L.throwing)//avoid double hits
if(iscarbon(L))
var/mob/living/carbon/C = L
switch(firer.a_intent)
if(INTENT_HELP)
C.visible_message("<span class='danger'>[L] is pulled by [H]'s tentacle!</span>","<span class='userdanger'>A tentacle grabs you and pulls you towards [H]!</span>")
C.throw_at(get_step_towards(H,C), 8, 2)
return 1
if(INTENT_DISARM)
var/obj/item/I = C.get_active_held_item()
if(I)
if(C.dropItemToGround(I))
C.visible_message("<span class='danger'>[I] is yanked off [C]'s hand by [src]!</span>","<span class='userdanger'>A tentacle pulls [I] away from you!</span>")
on_hit(I) //grab the item as if you had hit it directly with the tentacle
return 1
else
to_chat(firer, "<span class='danger'>You can't seem to pry [I] off [C]'s hands!</span>")
return 0
else
to_chat(firer, "<span class='danger'>[C] has nothing in hand to disarm!</span>")
return 0
if(INTENT_GRAB)
C.visible_message("<span class='danger'>[L] is grabbed by [H]'s tentacle!</span>","<span class='userdanger'>A tentacle grabs you and pulls you towards [H]!</span>")
C.throw_at(get_step_towards(H,C), 8, 2, callback=CALLBACK(src, .proc/tentacle_grab, H, C))
return 1
if(INTENT_HARM)
C.visible_message("<span class='danger'>[L] is thrown towards [H] by a tentacle!</span>","<span class='userdanger'>A tentacle grabs you and throws you towards [H]!</span>")
C.throw_at(get_step_towards(H,C), 8, 2, callback=CALLBACK(src, .proc/tentacle_stab, H, C))
return 1
else
L.visible_message("<span class='danger'>[L] is pulled by [H]'s tentacle!</span>","<span class='userdanger'>A tentacle grabs you and pulls you towards [H]!</span>")
L.throw_at(get_step_towards(H,L), 8, 2)
. = 1
/obj/item/projectile/tentacle/Destroy()
qdel(chain)
source = null
return ..()
/***************************************\
|****************SHIELD*****************|
\***************************************/
/obj/effect/proc_holder/changeling/weapon/shield
name = "Organic Shield"
desc = "We reform one of our arms into a hard shield."
helptext = "Organic tissue cannot resist damage forever; the shield will break after it is hit too much. The more genomes we absorb, the stronger it is. Cannot be used while in lesser form."
chemical_cost = 20
dna_cost = 1
req_human = 1
weapon_type = /obj/item/shield/changeling
weapon_name_simple = "shield"
/obj/effect/proc_holder/changeling/weapon/shield/sting_action(mob/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) //So we can read the absorbedcount.
if(!changeling)
return
var/obj/item/shield/changeling/S = ..(user)
S.remaining_uses = round(changeling.absorbedcount * 3)
return TRUE
/obj/item/shield/changeling
name = "shield-like mass"
desc = "A mass of tough, boney tissue. You can still see the fingers as a twisted pattern in the shield."
flags_1 = ABSTRACT_1 | NODROP_1 | DROPDEL_1
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "ling_shield"
lefthand_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi'
block_chance = 50
var/remaining_uses //Set by the changeling ability.
/obj/item/shield/changeling/Initialize()
. = ..()
if(ismob(loc))
loc.visible_message("<span class='warning'>The end of [loc.name]\'s hand inflates rapidly, forming a huge shield-like mass!</span>", "<span class='warning'>We inflate our hand into a strong shield.</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
/obj/item/shield/changeling/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(remaining_uses < 1)
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
H.visible_message("<span class='warning'>With a sickening crunch, [H] reforms his shield into an arm!</span>", "<span class='notice'>We assimilate our shield into our body</span>", "<span class='italics>You hear organic matter ripping and tearing!</span>")
qdel(src)
return 0
else
remaining_uses--
return ..()
/***************************************\
|*********SPACE SUIT + HELMET***********|
\***************************************/
/obj/effect/proc_holder/changeling/suit/organic_space_suit
name = "Organic Space Suit"
desc = "We grow an organic suit to protect ourselves from space exposure."
helptext = "We must constantly repair our form to make it space-proof, reducing chemical production while we are protected. Cannot be used in lesser form."
chemical_cost = 20
dna_cost = 2
req_human = 1
suit_type = /obj/item/clothing/suit/space/changeling
helmet_type = /obj/item/clothing/head/helmet/space/changeling
suit_name_simple = "flesh shell"
helmet_name_simple = "space helmet"
recharge_slowdown = 0.5
blood_on_castoff = 1
/obj/item/clothing/suit/space/changeling
name = "flesh mass"
icon_state = "lingspacesuit"
desc = "A huge, bulky mass of pressure and temperature-resistant organic tissue, evolved to facilitate space travel."
flags_1 = STOPSPRESSUREDMAGE_1 | NODROP_1 | DROPDEL_1 //Not THICKMATERIAL_1 because it's organic tissue, so if somebody tries to inject something into it, it still ends up in your blood. (also balance but muh fluff)
allowed = list(/obj/item/device/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/oxygen)
armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0, fire = 90, acid = 90) //No armor at all.
/obj/item/clothing/suit/space/changeling/Initialize()
. = ..()
if(ismob(loc))
loc.visible_message("<span class='warning'>[loc.name]\'s flesh rapidly inflates, forming a bloated mass around their body!</span>", "<span class='warning'>We inflate our flesh, creating a spaceproof suit!</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
START_PROCESSING(SSobj, src)
/obj/item/clothing/suit/space/changeling/process()
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
H.reagents.add_reagent("salbutamol", REAGENTS_METABOLISM)
/obj/item/clothing/head/helmet/space/changeling
name = "flesh mass"
icon_state = "lingspacehelmet"
desc = "A covering of pressure and temperature-resistant organic tissue with a glass-like chitin front."
flags_1 = STOPSPRESSUREDMAGE_1 | NODROP_1 | DROPDEL_1 //Again, no THICKMATERIAL_1.
armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0, fire = 90, acid = 90)
flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH
/***************************************\
|*****************ARMOR*****************|
\***************************************/
/obj/effect/proc_holder/changeling/suit/armor
name = "Chitinous Armor"
desc = "We turn our skin into tough chitin to protect us from damage."
helptext = "Upkeep of the armor requires a low expenditure of chemicals. The armor is strong against brute force, but does not provide much protection from lasers. Cannot be used in lesser form."
chemical_cost = 20
dna_cost = 1
req_human = 1
recharge_slowdown = 0.25
suit_type = /obj/item/clothing/suit/armor/changeling
helmet_type = /obj/item/clothing/head/helmet/changeling
suit_name_simple = "armor"
helmet_name_simple = "helmet"
/obj/item/clothing/suit/armor/changeling
name = "chitinous mass"
desc = "A tough, hard covering of black chitin."
icon_state = "lingarmor"
flags_1 = NODROP_1 | DROPDEL_1
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
armor = list(melee = 40, bullet = 40, laser = 40, energy = 20, bomb = 10, bio = 4, rad = 0, fire = 90, acid = 90)
flags_inv = HIDEJUMPSUIT
cold_protection = 0
heat_protection = 0
/obj/item/clothing/suit/armor/changeling/Initialize()
. = ..()
if(ismob(loc))
loc.visible_message("<span class='warning'>[loc.name]\'s flesh turns black, quickly transforming into a hard, chitinous mass!</span>", "<span class='warning'>We harden our flesh, creating a suit of armor!</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
/obj/item/clothing/head/helmet/changeling
name = "chitinous mass"
desc = "A tough, hard covering of black chitin with transparent chitin in front."
icon_state = "lingarmorhelmet"
flags_1 = NODROP_1 | DROPDEL_1
armor = list(melee = 40, bullet = 40, laser = 40, energy = 20, bomb = 10, bio = 4, rad = 0, fire = 90, acid = 90)
flags_inv = HIDEEARS|HIDEHAIR|HIDEEYES|HIDEFACIALHAIR|HIDEFACE
@@ -1,38 +0,0 @@
/obj/effect/proc_holder/changeling/panacea
name = "Anatomic Panacea"
desc = "Expels impurifications from our form; curing diseases, removing parasites, sobering us, purging toxins and radiation, and resetting our genetic code completely."
helptext = "Can be used while unconscious."
chemical_cost = 20
dna_cost = 1
req_stat = UNCONSCIOUS
//Heals the things that the other regenerative abilities don't.
/obj/effect/proc_holder/changeling/panacea/sting_action(mob/user)
to_chat(user, "<span class='notice'>We cleanse impurities from our form.</span>")
var/list/bad_organs = list(
user.getorgan(/obj/item/organ/body_egg),
user.getorgan(/obj/item/organ/zombie_infection))
for(var/o in bad_organs)
var/obj/item/organ/O = o
if(!istype(O))
continue
O.Remove(user)
if(iscarbon(user))
var/mob/living/carbon/C = user
C.vomit(0, toxic = TRUE)
O.forceMove(get_turf(user))
user.reagents.add_reagent("mutadone", 10)
user.reagents.add_reagent("pen_acid", 20)
user.reagents.add_reagent("antihol", 10)
user.reagents.add_reagent("mannitol", 25)
for(var/thing in user.viruses)
var/datum/disease/D = thing
if(D.severity == VIRUS_SEVERITY_POSITIVE)
continue
D.cure()
return TRUE
@@ -1,37 +0,0 @@
/obj/effect/proc_holder/changeling/regenerate
name = "Regenerate"
desc = "Allows us to regrow and restore missing external limbs, and \
vital internal organs, as well as removing shrapnel and restoring \
blood volume."
helptext = "Will alert nearby crew if any external limbs are \
regenerated. Can be used while unconscious."
chemical_cost = 10
dna_cost = 0
req_stat = UNCONSCIOUS
always_keep = TRUE
/obj/effect/proc_holder/changeling/regenerate/sting_action(mob/living/user)
to_chat(user, "<span class='notice'>You feel an itching, both inside and \
outside as your tissues knit and reknit.</span>")
if(iscarbon(user))
var/mob/living/carbon/C = user
var/list/missing = C.get_missing_limbs()
if(missing.len)
playsound(user, 'sound/magic/demon_consume.ogg', 50, 1)
C.visible_message("<span class='warning'>[user]'s missing limbs \
reform, making a loud, grotesque sound!</span>",
"<span class='userdanger'>Your limbs regrow, making a \
loud, crunchy sound and giving you great pain!</span>",
"<span class='italics'>You hear organic matter ripping \
and tearing!</span>")
C.emote("scream")
C.regenerate_limbs(1)
C.regenerate_organs()
if(!user.getorganslot(ORGAN_SLOT_BRAIN))
var/obj/item/organ/brain/changeling_brain/B = new()
B.Insert(C)
if(ishuman(user))
var/mob/living/carbon/human/H = user
H.restore_blood()
H.remove_all_embedded_objects()
return TRUE
@@ -1,37 +0,0 @@
/obj/effect/proc_holder/changeling/revive
name = "Revive"
desc = "We regenerate, healing all damage from our form."
helptext = "Does not regrow lost organs or a missing head."
req_stat = DEAD
always_keep = TRUE
ignores_fakedeath = TRUE
//Revive from revival stasis
/obj/effect/proc_holder/changeling/revive/sting_action(mob/living/carbon/user)
user.status_flags &= ~(FAKEDEATH)
user.tod = null
user.revive(full_heal = 1)
var/list/missing = user.get_missing_limbs()
missing -= "head" // headless changelings are funny
if(missing.len)
playsound(user, 'sound/magic/demon_consume.ogg', 50, 1)
user.visible_message("<span class='warning'>[user]'s missing limbs \
reform, making a loud, grotesque sound!</span>",
"<span class='userdanger'>Your limbs regrow, making a \
loud, crunchy sound and giving you great pain!</span>",
"<span class='italics'>You hear organic matter ripping \
and tearing!</span>")
user.emote("scream")
user.regenerate_limbs(0, list("head"))
user.regenerate_organs()
to_chat(user, "<span class='notice'>We have revived ourselves.</span>")
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
changeling.purchasedpowers -= src
return TRUE
/obj/effect/proc_holder/changeling/revive/can_be_used_by(mob/user)
if((user.stat != DEAD) && !(user.status_flags & FAKEDEATH))
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
changeling.purchasedpowers -= src
return 0
. = ..()
@@ -1,42 +0,0 @@
/obj/effect/proc_holder/changeling/resonant_shriek
name = "Resonant Shriek"
desc = "Our lungs and vocal cords shift, allowing us to briefly emit a noise that deafens and confuses the weak-minded."
helptext = "Emits a high-frequency sound that confuses and deafens humans, blows out nearby lights and overloads cyborg sensors."
chemical_cost = 20
dna_cost = 1
req_human = 1
//A flashy ability, good for crowd control and sewing chaos.
/obj/effect/proc_holder/changeling/resonant_shriek/sting_action(mob/user)
for(var/mob/living/M in get_hearers_in_view(4, user))
if(iscarbon(M))
var/mob/living/carbon/C = M
if(!C.mind || !C.mind.has_antag_datum(/datum/antagonist/changeling))
C.adjustEarDamage(0, 30)
C.confused += 25
C.Jitter(50)
else
SEND_SOUND(C, sound('sound/effects/screech.ogg'))
if(issilicon(M))
SEND_SOUND(M, sound('sound/weapons/flash.ogg'))
M.Knockdown(rand(100,200))
for(var/obj/machinery/light/L in range(4, user))
L.on = 1
L.break_light_tube()
return TRUE
/obj/effect/proc_holder/changeling/dissonant_shriek
name = "Dissonant Shriek"
desc = "We shift our vocal cords to release a high-frequency sound that overloads nearby electronics."
chemical_cost = 20
dna_cost = 1
//A flashy ability, good for crowd control and sewing chaos.
/obj/effect/proc_holder/changeling/dissonant_shriek/sting_action(mob/user)
for(var/obj/machinery/light/L in range(5, usr))
L.on = 1
L.break_light_tube()
empulse(get_turf(user), 2, 5, 1)
return TRUE
@@ -1,12 +0,0 @@
/obj/effect/proc_holder/changeling/spiders
name = "Spread Infestation"
desc = "Our form divides, creating arachnids which will grow into deadly beasts."
helptext = "The spiders are thoughtless creatures, and may attack their creators when fully grown. Requires at least 5 DNA absorptions."
chemical_cost = 45
dna_cost = 1
req_dna = 5
//Makes some spiderlings. Good for setting traps and causing general trouble.
/obj/effect/proc_holder/changeling/spiders/sting_action(mob/user)
spawn_atom_to_turf(/obj/structure/spider/spiderling/hunter, user, 2, FALSE)
return TRUE
@@ -1,51 +0,0 @@
//Strained Muscles: Temporary speed boost at the cost of rapid damage
//Limited because of hardsuits and such; ideally, used for a quick getaway
/obj/effect/proc_holder/changeling/strained_muscles
name = "Strained Muscles"
desc = "We evolve the ability to reduce the acid buildup in our muscles, allowing us to move much faster."
helptext = "The strain will make us tired, and we will rapidly become fatigued. Standard weight restrictions, like hardsuits, still apply. Cannot be used in lesser form."
chemical_cost = 0
dna_cost = 1
req_human = 1
var/stacks = 0 //Increments every 5 seconds; damage increases over time
active = 0 //Whether or not you are a hedgehog
/obj/effect/proc_holder/changeling/strained_muscles/sting_action(mob/living/carbon/user)
active = !active
if(active)
to_chat(user, "<span class='notice'>Our muscles tense and strengthen.</span>")
else
user.status_flags &= ~GOTTAGOFAST
to_chat(user, "<span class='notice'>Our muscles relax.</span>")
if(stacks >= 10)
to_chat(user, "<span class='danger'>We collapse in exhaustion.</span>")
user.Knockdown(60)
user.emote("gasp")
INVOKE_ASYNC(src, .proc/muscle_loop, user)
return TRUE
/obj/effect/proc_holder/changeling/strained_muscles/proc/muscle_loop(mob/living/carbon/user)
while(active)
user.status_flags |= GOTTAGOFAST
if(user.stat != CONSCIOUS || user.staminaloss >= 90)
active = !active
to_chat(user, "<span class='notice'>Our muscles relax without the energy to strengthen them.</span>")
user.Knockdown(40)
user.status_flags &= ~GOTTAGOFAST
break
stacks++
//user.take_bodypart_damage(stacks * 0.03, 0)
user.staminaloss += stacks * 1.3 //At first the changeling may regenerate stamina fast enough to nullify fatigue, but it will stack
if(stacks == 11) //Warning message that the stacks are getting too high
to_chat(user, "<span class='warning'>Our legs are really starting to hurt...</span>")
sleep(40)
while(!active && stacks) //Damage stacks decrease fairly rapidly while not in sanic mode
stacks--
sleep(20)
@@ -1,246 +0,0 @@
/obj/effect/proc_holder/changeling/sting
name = "Tiny Prick"
desc = "Stabby stabby."
var/sting_icon = null
/obj/effect/proc_holder/changeling/sting/Click()
var/mob/user = usr
if(!user || !user.mind)
return
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(!changeling)
return
if(!changeling.chosen_sting)
set_sting(user)
else
unset_sting(user)
return
/obj/effect/proc_holder/changeling/sting/proc/set_sting(mob/user)
to_chat(user, "<span class='notice'>We prepare our sting, use alt+click or middle mouse button on target to sting them.</span>")
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
changeling.chosen_sting = src
user.hud_used.lingstingdisplay.icon_state = sting_icon
user.hud_used.lingstingdisplay.invisibility = 0
/obj/effect/proc_holder/changeling/sting/proc/unset_sting(mob/user)
to_chat(user, "<span class='warning'>We retract our sting, we can't sting anyone for now.</span>")
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
changeling.chosen_sting = null
user.hud_used.lingstingdisplay.icon_state = null
user.hud_used.lingstingdisplay.invisibility = INVISIBILITY_ABSTRACT
/mob/living/carbon/proc/unset_sting()
if(mind)
var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
if(changeling && changeling.chosen_sting)
changeling.chosen_sting.unset_sting(src)
/obj/effect/proc_holder/changeling/sting/can_sting(mob/user, mob/target)
if(!..())
return
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(!changeling.chosen_sting)
to_chat(user, "We haven't prepared our sting yet!")
if(!iscarbon(target))
return
if(!isturf(user.loc))
return
if(!AStar(user, target.loc, /turf/proc/Distance, changeling.sting_range, simulated_only = 0))
return
if(target.mind && target.mind.has_antag_datum(/datum/antagonist/changeling))
sting_feedback(user, target)
changeling.chem_charges -= chemical_cost //??
return 1
/obj/effect/proc_holder/changeling/sting/sting_feedback(mob/user, mob/target)
if(!target)
return
to_chat(user, "<span class='notice'>We stealthily sting [target.name].</span>")
if(target.mind && target.mind.has_antag_datum(/datum/antagonist/changeling))
to_chat(target, "<span class='warning'>You feel a tiny prick.</span>")
return 1
/obj/effect/proc_holder/changeling/sting/transformation
name = "Transformation Sting"
desc = "We silently sting a human, injecting a retrovirus that forces them to transform."
helptext = "The victim will transform much like a changeling would. Does not provide a warning to others. Mutations will not be transferred, and monkeys will become human."
sting_icon = "sting_transform"
chemical_cost = 50
dna_cost = 3
var/datum/changelingprofile/selected_dna = null
/obj/effect/proc_holder/changeling/sting/transformation/Click()
var/mob/user = usr
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(changeling.chosen_sting)
unset_sting(user)
return
selected_dna = changeling.select_dna("Select the target DNA: ", "Target DNA")
if(!selected_dna)
return
if(NOTRANSSTING in selected_dna.dna.species.species_traits)
to_chat(user, "<span class = 'notice'>That DNA is not compatible with changeling retrovirus!</span>")
return
..()
/obj/effect/proc_holder/changeling/sting/transformation/can_sting(mob/user, mob/living/carbon/target)
if(!..())
return
if((target.has_disability(DISABILITY_HUSK)) || !iscarbon(target) || (NOTRANSSTING in target.dna.species.species_traits))
to_chat(user, "<span class='warning'>Our sting appears ineffective against its DNA.</span>")
return 0
return 1
/obj/effect/proc_holder/changeling/sting/transformation/sting_action(mob/user, mob/target)
add_logs(user, target, "stung", "transformation sting", " new identity is [selected_dna.dna.real_name]")
var/datum/dna/NewDNA = selected_dna.dna
if(ismonkey(target))
to_chat(user, "<span class='notice'>Our genes cry out as we sting [target.name]!</span>")
var/mob/living/carbon/C = target
. = TRUE
if(istype(C))
C.real_name = NewDNA.real_name
NewDNA.transfer_identity(C)
if(ismonkey(C))
C.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_DEFAULTMSG)
C.updateappearance(mutcolor_update=1)
/obj/effect/proc_holder/changeling/sting/false_armblade
name = "False Armblade Sting"
desc = "We silently sting a human, injecting a retrovirus that mutates their arm to temporarily appear as an armblade."
helptext = "The victim will form an armblade much like a changeling would, except the armblade is dull and useless."
sting_icon = "sting_armblade"
chemical_cost = 20
dna_cost = 1
/obj/item/melee/arm_blade/false
desc = "A grotesque mass of flesh that used to be your arm. Although it looks dangerous at first, you can tell it's actually quite dull and useless."
force = 5 //Basically as strong as a punch
/obj/item/melee/arm_blade/false/afterattack(atom/target, mob/user, proximity)
return
/obj/effect/proc_holder/changeling/sting/false_armblade/can_sting(mob/user, mob/target)
if(!..())
return
if(isliving(target))
var/mob/living/L = target
if((L.has_disability(DISABILITY_HUSK)) || !L.has_dna())
to_chat(user, "<span class='warning'>Our sting appears ineffective against its DNA.</span>")
return 0
return 1
/obj/effect/proc_holder/changeling/sting/false_armblade/sting_action(mob/user, mob/target)
add_logs(user, target, "stung", object="falso armblade sting")
var/obj/item/held = target.get_active_held_item()
if(held && !target.dropItemToGround(held))
to_chat(user, "<span class='warning'>[held] is stuck to their hand, you cannot grow a false armblade over it!</span>")
return
if(ismonkey(target))
to_chat(user, "<span class='notice'>Our genes cry out as we sting [target.name]!</span>")
var/obj/item/melee/arm_blade/false/blade = new(target,1)
target.put_in_hands(blade)
target.visible_message("<span class='warning'>A grotesque blade forms around [target.name]\'s arm!</span>", "<span class='userdanger'>Your arm twists and mutates, transforming into a horrific monstrosity!</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
playsound(target, 'sound/effects/blobattack.ogg', 30, 1)
addtimer(CALLBACK(src, .proc/remove_fake, target, blade), 600)
return TRUE
/obj/effect/proc_holder/changeling/sting/false_armblade/proc/remove_fake(mob/target, obj/item/melee/arm_blade/false/blade)
playsound(target, 'sound/effects/blobattack.ogg', 30, 1)
target.visible_message("<span class='warning'>With a sickening crunch, \
[target] reforms their [blade.name] into an arm!</span>",
"<span class='warning'>[blade] reforms back to normal.</span>",
"<span class='italics>You hear organic matter ripping and tearing!</span>")
qdel(blade)
target.update_inv_hands()
/obj/effect/proc_holder/changeling/sting/extract_dna
name = "Extract DNA Sting"
desc = "We stealthily sting a target and extract their DNA."
helptext = "Will give you the DNA of your target, allowing you to transform into them."
sting_icon = "sting_extract"
chemical_cost = 25
dna_cost = 0
/obj/effect/proc_holder/changeling/sting/extract_dna/can_sting(mob/user, mob/target)
if(..())
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
return changeling.can_absorb_dna(target)
/obj/effect/proc_holder/changeling/sting/extract_dna/sting_action(mob/user, mob/living/carbon/human/target)
add_logs(user, target, "stung", "extraction sting")
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(!(changeling.has_dna(target.dna)))
changeling.add_new_profile(target)
return TRUE
/obj/effect/proc_holder/changeling/sting/mute
name = "Mute Sting"
desc = "We silently sting a human, completely silencing them for a short time."
helptext = "Does not provide a warning to the victim that they have been stung, until they try to speak and cannot."
sting_icon = "sting_mute"
chemical_cost = 20
dna_cost = 2
/obj/effect/proc_holder/changeling/sting/mute/sting_action(mob/user, mob/living/carbon/target)
add_logs(user, target, "stung", "mute sting")
target.silent += 30
return TRUE
/obj/effect/proc_holder/changeling/sting/blind
name = "Blind Sting"
desc = "Temporarily blinds the target."
helptext = "This sting completely blinds a target for a short time."
sting_icon = "sting_blind"
chemical_cost = 25
dna_cost = 1
/obj/effect/proc_holder/changeling/sting/blind/sting_action(mob/user, mob/living/carbon/target)
add_logs(user, target, "stung", "blind sting")
to_chat(target, "<span class='danger'>Your eyes burn horrifically!</span>")
target.become_nearsighted(EYE_DAMAGE)
target.blind_eyes(20)
target.blur_eyes(40)
return TRUE
/obj/effect/proc_holder/changeling/sting/LSD
name = "Hallucination Sting"
desc = "Causes terror in the target."
helptext = "We evolve the ability to sting a target with a powerful hallucinogenic chemical. The target does not notice they have been stung, and the effect occurs after 30 to 60 seconds."
sting_icon = "sting_lsd"
chemical_cost = 10
dna_cost = 1
/obj/effect/proc_holder/changeling/sting/LSD/sting_action(mob/user, mob/living/carbon/target)
add_logs(user, target, "stung", "LSD sting")
addtimer(CALLBACK(src, .proc/hallucination_time, target), rand(300,600))
return TRUE
/obj/effect/proc_holder/changeling/sting/LSD/proc/hallucination_time(mob/living/carbon/target)
if(target)
target.hallucination = max(400, target.hallucination)
/obj/effect/proc_holder/changeling/sting/cryo
name = "Cryogenic Sting"
desc = "We silently sting a human with a cocktail of chemicals that freeze them."
helptext = "Does not provide a warning to the victim, though they will likely realize they are suddenly freezing."
sting_icon = "sting_cryo"
chemical_cost = 15
dna_cost = 2
/obj/effect/proc_holder/changeling/sting/cryo/sting_action(mob/user, mob/target)
add_logs(user, target, "stung", "cryo sting")
if(target.reagents)
target.reagents.add_reagent("frostoil", 30)
return TRUE
@@ -1,129 +0,0 @@
/obj/effect/proc_holder/changeling/transform
name = "Transform"
desc = "We take on the appearance and voice of one we have absorbed."
chemical_cost = 5
dna_cost = 0
req_dna = 1
req_human = 1
/obj/item/clothing/glasses/changeling
name = "flesh"
flags_1 = NODROP_1
/obj/item/clothing/glasses/changeling/attack_hand(mob/user)
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)
return
..()
/obj/item/clothing/under/changeling
name = "flesh"
flags_1 = NODROP_1
/obj/item/clothing/under/changeling/attack_hand(mob/user)
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)
return
..()
/obj/item/clothing/suit/changeling
name = "flesh"
flags_1 = NODROP_1
allowed = list(/obj/item/changeling)
/obj/item/clothing/suit/changeling/attack_hand(mob/user)
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)
return
..()
/obj/item/clothing/head/changeling
name = "flesh"
flags_1 = NODROP_1
/obj/item/clothing/head/changeling/attack_hand(mob/user)
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)
return
..()
/obj/item/clothing/shoes/changeling
name = "flesh"
flags_1 = NODROP_1
/obj/item/clothing/shoes/changeling/attack_hand(mob/user)
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)
return
..()
/obj/item/clothing/gloves/changeling
name = "flesh"
flags_1 = NODROP_1
/obj/item/clothing/gloves/changeling/attack_hand(mob/user)
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)
return
..()
/obj/item/clothing/mask/changeling
name = "flesh"
flags_1 = NODROP_1
/obj/item/clothing/mask/changeling/attack_hand(mob/user)
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)
return
..()
/obj/item/changeling
name = "flesh"
flags_1 = NODROP_1
slot_flags = ALL
allowed = list(/obj/item/changeling)
/obj/item/changeling/attack_hand(mob/user)
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)
return
..()
//Change our DNA to that of somebody we've absorbed.
/obj/effect/proc_holder/changeling/transform/sting_action(mob/living/carbon/human/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
var/datum/changelingprofile/chosen_prof = changeling.select_dna("Select the target DNA: ", "Target DNA")
if(!chosen_prof)
return
changeling_transform(user, chosen_prof)
return TRUE
/datum/antagonist/changeling/proc/select_dna(var/prompt, var/title)
var/mob/living/carbon/user = owner.current
if(!istype(user))
return
var/list/names = list("Drop Flesh Disguise")
for(var/datum/changelingprofile/prof in stored_profiles)
names += "[prof.name]"
var/chosen_name = input(prompt, title, null) as null|anything in names
if(!chosen_name)
return
if(chosen_name == "Drop Flesh Disguise")
for(var/slot in GLOB.slots)
if(istype(user.vars[slot], GLOB.slot2type[slot]))
qdel(user.vars[slot])
var/datum/changelingprofile/prof = get_dna(chosen_name)
return prof
@@ -1,25 +0,0 @@
//The base clockwork effect. Can have an alternate desc and will show up in the list of clockwork objects.
/obj/effect/clockwork
name = "meme machine"
desc = "Still don't know what it is."
var/clockwork_desc = "A fabled artifact from beyond the stars. Contains concentrated meme essence." //Shown to clockwork cultists instead of the normal description
icon = 'icons/effects/clockwork_effects.dmi'
icon_state = "ratvars_flame"
anchored = TRUE
density = FALSE
opacity = 0
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
/obj/effect/clockwork/Initialize()
. = ..()
GLOB.all_clockwork_objects += src
/obj/effect/clockwork/Destroy()
GLOB.all_clockwork_objects -= src
return ..()
/obj/effect/clockwork/examine(mob/user)
if((is_servant_of_ratvar(user) || isobserver(user)) && clockwork_desc)
desc = clockwork_desc
..()
desc = initial(desc)
@@ -1,69 +0,0 @@
//These spawn across the station when the Ark activates. Anyone can walk through one to teleport to Reebe.
/obj/effect/clockwork/city_of_cogs_rift
name = "celestial rift"
desc = "A stable bluespace rip. You're not sure it where leads."
clockwork_desc = "A one-way rift to the City of Cogs. Because it's linked to the Ark, it can't be closed."
icon_state = "city_of_cogs_rift"
resistance_flags = INDESTRUCTIBLE
density = TRUE
light_range = 2
light_power = 3
light_color = "#6A4D2F"
/obj/effect/clockwork/city_of_cogs_rift/singularity_act()
return
/obj/effect/clockwork/city_of_cogs_rift/singularity_pull()
return
/obj/effect/clockwork/city_of_cogs_rift/Initialize()
. = ..()
visible_message("<span class='warning'>The air above [loc] shimmers and pops as a [name] forms there!</span>")
for(var/mob/M in GLOB.player_list)
if(M.z == z)
if(get_dist(src, M) >= 7)
M.playsound_local(src, 'sound/magic/blink.ogg', 10, FALSE, falloff = 10)
else
M.playsound_local(src, 'sound/magic/blink.ogg', 50, FALSE)
/obj/effect/clockwork/city_of_cogs_rift/Destroy()
visible_message("<span class='warning'>[src] cracks as it destabilizes and breaks apart!</span>")
return ..()
/obj/effect/clockwork/city_of_cogs_rift/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/nullrod))
to_chat(user, "<span class='warning'>Your [I.name] seems to have no effect on [src]!</span>")
return
. = ..()
/obj/effect/clockwork/city_of_cogs_rift/attack_hand(atom/movable/AM)
beckon(AM)
/obj/effect/clockwork/city_of_cogs_rift/CollidedWith(atom/movable/AM)
if(!QDELETED(AM))
if(isliving(AM))
var/mob/living/L = AM
if(L.client && !L.incapacitated())
L.visible_message("<span class='notice'>[L] starts climbing through [src]...</span>", \
"<span class='notice'>You begin climbing through [src]...</span>")
if(!do_after(L, 30, target = L))
return
beckon(AM)
/obj/effect/clockwork/city_of_cogs_rift/proc/beckon(atom/movable/AM)
var/turf/T = get_turf(pick(GLOB.city_of_cogs_spawns))
if(is_servant_of_ratvar(AM))
T = GLOB.ark_of_the_clockwork_justiciar ? get_step(GLOB.ark_of_the_clockwork_justiciar, SOUTH) : get_turf(pick(GLOB.servant_spawns))
AM.visible_message("<span class='danger'>[AM] passes through [src]!</span>", null, null, null, AM)
AM.forceMove(T)
AM.visible_message("<span class='danger'>[AM] materializes from the air!</span>", \
"<span class='boldannounce'>You pass through [src] and appear [is_servant_of_ratvar(AM) ? "back at the City of Cogs" : "somewhere unfamiliar. Looks like it was a one-way trip.."].</span>")
do_sparks(5, TRUE, src)
do_sparks(5, TRUE, AM)
if(isliving(AM))
var/mob/living/L = AM
L.overlay_fullscreen("flash", /obj/screen/fullscreen/flash/static)
L.clear_fullscreen("flash", 5)
var/obj/item/device/transfer_valve/TTV = locate() in L.GetAllContents()
if(TTV)
to_chat(L, "<span class='userdanger'>The air resonates with the Ark's presence; your explosives will be significantly dampened here!</span>")
@@ -1,49 +0,0 @@
//an "overlay" used by clockwork walls and floors to appear normal to mesons.
/obj/effect/clockwork/overlay
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
var/atom/linked
/obj/effect/clockwork/overlay/examine(mob/user)
if(linked)
linked.examine(user)
/obj/effect/clockwork/overlay/ex_act()
return FALSE
/obj/effect/clockwork/overlay/singularity_act()
return
/obj/effect/clockwork/overlay/singularity_pull()
return
/obj/effect/clockwork/overlay/singularity_pull(S, current_size)
return
/obj/effect/clockwork/overlay/Destroy()
if(linked)
linked = null
. = ..()
/obj/effect/clockwork/overlay/wall
name = "clockwork wall"
icon = 'icons/turf/walls/clockwork_wall.dmi'
icon_state = "clockwork_wall"
canSmoothWith = list(/obj/effect/clockwork/overlay/wall, /obj/structure/falsewall/brass)
smooth = SMOOTH_TRUE
layer = CLOSED_TURF_LAYER
/obj/effect/clockwork/overlay/wall/Initialize()
. = ..()
queue_smooth_neighbors(src)
addtimer(CALLBACK(GLOBAL_PROC, .proc/queue_smooth, src), 1)
/obj/effect/clockwork/overlay/wall/Destroy()
queue_smooth_neighbors(src)
return ..()
/obj/effect/clockwork/overlay/floor
icon = 'icons/turf/floors.dmi'
icon_state = "clockwork_floor"
layer = TURF_LAYER
/obj/effect/clockwork/overlay/floor/bloodcult //this is used by BLOOD CULT, it shouldn't use such a path...
icon_state = "cult"
@@ -1,369 +0,0 @@
//Sigils: Rune-like markings on the ground with various effects.
/obj/effect/clockwork/sigil
name = "sigil"
desc = "A strange set of markings drawn on the ground."
clockwork_desc = "A sigil of some purpose."
icon_state = "sigil"
layer = LOW_OBJ_LAYER
alpha = 50
resistance_flags = NONE
var/affects_servants = FALSE
var/stat_affected = CONSCIOUS
var/sigil_name = "Sigil"
var/resist_string = "glows blinding white" //string for when a null rod blocks its effects, "glows [resist_string]"
/obj/effect/clockwork/sigil/attackby(obj/item/I, mob/living/user, params)
if(I.force)
if(is_servant_of_ratvar(user) && user.a_intent != INTENT_HARM)
return ..()
user.visible_message("<span class='warning'>[user] scatters [src] with [I]!</span>", "<span class='danger'>You scatter [src] with [I]!</span>")
qdel(src)
return 1
return ..()
/obj/effect/clockwork/sigil/attack_tk(mob/user)
return //you can't tk stomp sigils, but you can hit them with something
/obj/effect/clockwork/sigil/attack_hand(mob/user)
if(iscarbon(user) && !user.stat)
if(is_servant_of_ratvar(user) && user.a_intent != INTENT_HARM)
return ..()
user.visible_message("<span class='warning'>[user] stamps out [src]!</span>", "<span class='danger'>You stomp on [src], scattering it into thousands of particles.</span>")
qdel(src)
return 1
..()
/obj/effect/clockwork/sigil/ex_act(severity)
visible_message("<span class='warning'>[src] scatters into thousands of particles.</span>")
qdel(src)
/obj/effect/clockwork/sigil/Crossed(atom/movable/AM)
..()
if(isliving(AM))
var/mob/living/L = AM
if(L.stat <= stat_affected)
if((!is_servant_of_ratvar(L) || (affects_servants && is_servant_of_ratvar(L))) && (L.mind || L.has_status_effect(STATUS_EFFECT_SIGILMARK)) && !isdrone(L))
var/obj/item/I = L.null_rod_check()
if(I)
L.visible_message("<span class='warning'>[L]'s [I.name] [resist_string], protecting them from [src]'s effects!</span>", \
"<span class='userdanger'>Your [I.name] [resist_string], protecting you!</span>")
return
sigil_effects(L)
/obj/effect/clockwork/sigil/proc/sigil_effects(mob/living/L)
//Sigil of Transgression: Stuns the first non-servant to walk on it and flashes all nearby non_servants. Nar-Sian cultists are damaged and knocked down for a longer time
/obj/effect/clockwork/sigil/transgression
name = "dull sigil"
desc = "A dull, barely-visible golden sigil. It's as though light was carved into the ground."
icon = 'icons/effects/clockwork_effects.dmi'
clockwork_desc = "A sigil that will stun the next non-Servant to cross it."
icon_state = "sigildull"
layer = HIGH_SIGIL_LAYER
alpha = 75
color = "#FAE48C"
light_range = 1.4
light_power = 1
light_color = "#FAE48C"
sigil_name = "Sigil of Transgression"
/obj/effect/clockwork/sigil/transgression/sigil_effects(mob/living/L)
var/target_flashed = L.flash_act()
for(var/mob/living/M in viewers(5, src))
if(!is_servant_of_ratvar(M) && M != L)
M.flash_act()
if(iscultist(L))
to_chat(L, "<span class='heavy_brass'>\"Watch your step, wretch.\"</span>")
L.adjustBruteLoss(10)
L.Knockdown(80, FALSE)
L.visible_message("<span class='warning'>[src] appears around [L] in a burst of light!</span>", \
"<span class='userdanger'>[target_flashed ? "An unseen force":"The glowing sigil around you"] holds you in place!</span>")
L.Stun(40)
L.apply_status_effect(STATUS_EFFECT_BELLIGERENT)
new /obj/effect/temp_visual/ratvar/sigil/transgression(get_turf(src))
qdel(src)
//Sigil of Submission: After a short time, converts any non-servant standing on it. Knocks down and silences them for five seconds afterwards.
/obj/effect/clockwork/sigil/submission
name = "ominous sigil"
desc = "A luminous golden sigil. Something about it really bothers you."
clockwork_desc = "A sigil that will enslave any non-Servant that remains on it for 8 seconds. Cannot penetrate mindshield implants."
icon_state = "sigilsubmission"
layer = LOW_SIGIL_LAYER
alpha = 125
color = "#FAE48C"
light_range = 2 //soft light
light_power = 0.9
light_color = "#FAE48C"
stat_affected = UNCONSCIOUS
resist_string = "glows faintly yellow"
var/convert_time = 80
var/delete_on_finish = TRUE
sigil_name = "Sigil of Submission"
var/glow_type = /obj/effect/temp_visual/ratvar/sigil/submission
/obj/effect/clockwork/sigil/submission/sigil_effects(mob/living/L)
var/turf/T = get_turf(src)
var/has_sigil = FALSE
var/has_servant = FALSE
if(locate(/obj/effect/clockwork/sigil/transgression) in T)
has_sigil = TRUE
for(var/mob/living/M in range(3, src))
if(is_servant_of_ratvar(M) && !M.stat)
has_servant = TRUE
if(!has_sigil && !has_servant)
visible_message("<span class='danger'>[src] strains into a gentle violet color, but quietly fades...</span>")
return
L.visible_message("<span class='warning'>[src] begins to glow a piercing magenta!</span>", "<span class='sevtug'>You feel something start to invade your mind...</span>")
var/oldcolor = color
animate(src, color = "#AF0AAF", time = convert_time, flags = ANIMATION_END_NOW)
var/obj/effect/temp_visual/ratvar/sigil/glow
if(glow_type)
glow = new glow_type(get_turf(src))
animate(glow, alpha = 255, time = convert_time)
var/end_time = world.time+convert_time
while(world.time < end_time && get_turf(L) == get_turf(src))
stoplag(1)
if(get_turf(L) != get_turf(src))
if(glow)
qdel(glow)
animate(src, color = oldcolor, time = 20, flags = ANIMATION_END_NOW)
addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 20)
visible_message("<span class='warning'>[src] slowly stops glowing!</span>")
return
if(is_eligible_servant(L))
to_chat(L, "<span class='heavy_brass'>\"You belong to me now.\"</span>")
if(!GLOB.application_scripture_unlocked)
GLOB.application_scripture_unlocked = TRUE
hierophant_message("<span class='large_brass bold'>With the conversion of a new servant the Ark's power grows. Application scriptures are now available.</span>")
if(add_servant_of_ratvar(L))
L.log_message("<font color=#BE8700>Conversion was done with a [sigil_name].</font>", INDIVIDUAL_ATTACK_LOG)
if(iscarbon(L))
var/mob/living/carbon/M = L
M.uncuff()
L.Knockdown(50) //Completely defenseless for five seconds - mainly to give them time to read over the information they've just been presented with
if(iscarbon(L))
var/mob/living/carbon/C = L
C.silent += 5
var/message = "[sigil_name] in [get_area(src)] <span class='sevtug'>[is_servant_of_ratvar(L) ? "successfully converted" : "failed to convert"]</span>"
for(var/M in GLOB.mob_list)
if(isobserver(M))
var/link = FOLLOW_LINK(M, L)
to_chat(M, "[link] <span class='heavy_brass'>[message] [L.real_name]!</span>")
else if(is_servant_of_ratvar(M))
if(M == L)
to_chat(M, "<span class='heavy_brass'>[message] you!</span>")
else
to_chat(M, "<span class='heavy_brass'>[message] [L.real_name]!</span>")
animate(src, color = oldcolor, time = 20, flags = ANIMATION_END_NOW)
addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 20)
visible_message("<span class='warning'>[src] slowly stops glowing!</span>")
//Sigil of Transmission: Serves as an access point for powered structures.
/obj/effect/clockwork/sigil/transmission
name = "suspicious sigil"
desc = "A glowing orange sigil. The air around it feels staticky."
clockwork_desc = "A sigil that serves as power generation and a battery for clockwork structures, linked to all other sigils of its type."
icon_state = "sigiltransmission"
alpha = 50
color = "#EC8A2D"
light_color = "#EC8A2D"
resist_string = "glows faintly"
sigil_name = "Sigil of Transmission"
affects_servants = TRUE
/obj/effect/clockwork/sigil/transmission/Initialize()
. = ..()
update_icon()
/obj/effect/clockwork/sigil/transmission/ex_act(severity)
if(severity == 3)
adjust_clockwork_power(500) //Light explosions charge the network!
visible_message("<span class='warning'>[src] flares a brilliant orange!</span>")
else
..()
/obj/effect/clockwork/sigil/transmission/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
var/structure_number = 0
for(var/obj/structure/destructible/clockwork/powered/P in range(SIGIL_ACCESS_RANGE, src))
structure_number++
to_chat(user, "<span class='[get_clockwork_power() ? "brass":"alloy"]'>It is storing <b>[DisplayPower(get_clockwork_power())]</b> of shared power, \
and <b>[structure_number]</b> clockwork structure[structure_number == 1 ? " is":"s are"] in range.</span>")
if(iscyborg(user))
to_chat(user, "<span class='brass'>You can recharge from the [sigil_name] by crossing it.</span>")
/obj/effect/clockwork/sigil/transmission/sigil_effects(mob/living/L)
if(is_servant_of_ratvar(L))
if(iscyborg(L))
charge_cyborg(L)
else if(get_clockwork_power())
to_chat(L, "<span class='brass'>You feel a slight, static shock.</span>")
/obj/effect/clockwork/sigil/transmission/proc/charge_cyborg(mob/living/silicon/robot/cyborg)
if(!cyborg_checks(cyborg))
return
to_chat(cyborg, "<span class='brass'>You start to charge from the [sigil_name]...</span>")
if(!do_after(cyborg, 50, target = src, extra_checks = CALLBACK(src, .proc/cyborg_checks, cyborg, TRUE)))
return
var/giving_power = min(FLOOR(cyborg.cell.maxcharge - cyborg.cell.charge, MIN_CLOCKCULT_POWER), get_clockwork_power()) //give the borg either all our power or their missing power floored to MIN_CLOCKCULT_POWER
if(adjust_clockwork_power(-giving_power))
cyborg.visible_message("<span class='warning'>[cyborg] glows a brilliant orange!</span>")
var/previous_color = cyborg.color
cyborg.color = list("#EC8A2D", "#EC8A2D", "#EC8A2D", rgb(0,0,0))
cyborg.apply_status_effect(STATUS_EFFECT_POWERREGEN, giving_power * 0.1) //ten ticks, restoring 10% each
animate(cyborg, color = previous_color, time = 100)
addtimer(CALLBACK(cyborg, /atom/proc/update_atom_colour), 100)
/obj/effect/clockwork/sigil/transmission/proc/cyborg_checks(mob/living/silicon/robot/cyborg, silent)
if(!cyborg.cell)
if(!silent)
to_chat(cyborg, "<span class='warning'>You have no cell!</span>")
return FALSE
if(!get_clockwork_power())
if(!silent)
to_chat(cyborg, "<span class='warning'>There is no power available across sigils!</span>")
return FALSE
if(cyborg.cell.charge > cyborg.cell.maxcharge - MIN_CLOCKCULT_POWER)
if(!silent)
to_chat(cyborg, "<span class='warning'>You are already at maximum charge!</span>")
return FALSE
if(cyborg.has_status_effect(STATUS_EFFECT_POWERREGEN))
if(!silent)
to_chat(cyborg, "<span class='warning'>You are already regenerating power!</span>")
return FALSE
return TRUE
/obj/effect/clockwork/sigil/transmission/update_icon()
if(GLOB.ratvar_awakens)
alpha = 255
var/power_charge = get_clockwork_power()
alpha = min(initial(alpha) + power_charge * 0.02, 255)
if(!power_charge)
set_light(0)
else
set_light(max(alpha * 0.02, 1.4), max(alpha * 0.01, 0.1))
//Vitality Matrix: Drains health from non-servants to heal or even revive servants.
/obj/effect/clockwork/sigil/vitality
name = "comforting sigil"
desc = "A faint blue sigil. Looking at it makes you feel protected."
clockwork_desc = "A sigil that will drain non-Servants that remain on it. Servants that remain on it will be healed if it has any vitality drained."
icon_state = "sigilvitality"
layer = SIGIL_LAYER
alpha = 75
color = "#123456"
affects_servants = TRUE
stat_affected = DEAD
resist_string = "glows shimmering yellow"
sigil_name = "Vitality Matrix"
var/revive_cost = 150
var/sigil_active = FALSE
var/animation_number = 3 //each cycle increments this by 1, at 4 it produces an animation and resets
var/static/list/damage_heal_order = list(CLONE, TOX, BURN, BRUTE, OXY) //we heal damage in this order
/obj/effect/clockwork/sigil/vitality/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
to_chat(user, "<span class='[GLOB.clockwork_vitality ? "inathneq_small":"alloy"]'>It has access to <b>[GLOB.ratvar_awakens ? "INFINITE":GLOB.clockwork_vitality]</b> units of vitality.</span>")
if(GLOB.ratvar_awakens)
to_chat(user, "<span class='inathneq_small'>It can revive Servants at no cost!</span>")
else
to_chat(user, "<span class='inathneq_small'>It can revive Servants at a cost of <b>[revive_cost]</b> vitality.</span>")
/obj/effect/clockwork/sigil/vitality/sigil_effects(mob/living/L)
if((is_servant_of_ratvar(L) && L.suiciding) || sigil_active)
return
animate(src, alpha = 255, time = 10, flags = ANIMATION_END_NOW) //we may have a previous animation going. finish it first, then do this one without delay.
sleep(10)
//as long as they're still on the sigil and are either not a servant or they're a servant AND it has remaining vitality
var/consumed_vitality
while(L && (!is_servant_of_ratvar(L) || (is_servant_of_ratvar(L) && (GLOB.ratvar_awakens || GLOB.clockwork_vitality))) && get_turf(L) == get_turf(src) && !L.buckled)
sigil_active = TRUE
if(animation_number >= 4)
new /obj/effect/temp_visual/ratvar/sigil/vitality(get_turf(src))
animation_number = 0
animation_number++
if(!is_servant_of_ratvar(L))
var/vitality_drained = 0
if(L.stat == DEAD && !consumed_vitality)
consumed_vitality = TRUE //Prevent the target from being consumed multiple times
vitality_drained = L.maxHealth
var/obj/effect/temp_visual/ratvar/sigil/vitality/V = new /obj/effect/temp_visual/ratvar/sigil/vitality(get_turf(src))
animate(V, alpha = 0, transform = matrix()*2, time = 8)
playsound(L, 'sound/magic/wandodeath.ogg', 50, 1)
L.visible_message("<span class='warning'>[L] collapses in on [L.p_them()]self as [src] flares bright blue!</span>")
to_chat(L, "<span class='inathneq_large'>\"[text2ratvar("Your life will not be wasted.")]\"</span>")
for(var/obj/item/W in L)
if(!L.dropItemToGround(W))
qdel(W)
L.dust()
else
if(!GLOB.ratvar_awakens && L.stat == CONSCIOUS)
vitality_drained = L.adjustToxLoss(1)
else
vitality_drained = L.adjustToxLoss(1.5)
if(vitality_drained)
GLOB.clockwork_vitality += vitality_drained
else
break
else
if(L.stat == DEAD)
var/revival_cost = revive_cost
if(GLOB.ratvar_awakens || L.suiciding) // No cost if Ratvar is summoned or if you're reviving a convert who suicided
revival_cost = 0
var/mob/dead/observer/ghost = L.get_ghost(TRUE)
if(GLOB.clockwork_vitality >= revival_cost && (ghost || (L.mind && L.mind.active)))
if(L.has_status_effect(STATUS_EFFECT_ICHORIAL_STAIN))
visible_message("<span class='boldwarning'>[src] strains, but nothing happens...</span>")
if(L.pulledby)
to_chat(L.pulledby, "<span class='userdanger'>[L] was already revived recently by a vitality matrix! Wait a bit longer!</span>")
break
else
if(ghost)
ghost.reenter_corpse()
L.revive(1, 1)
var/obj/effect/temp_visual/ratvar/sigil/vitality/V = new /obj/effect/temp_visual/ratvar/sigil/vitality(get_turf(src))
animate(V, alpha = 0, transform = matrix()*2, time = 8)
playsound(L, 'sound/magic/staff_healing.ogg', 50, 1)
to_chat(L, "<span class='inathneq'>\"[text2ratvar("You will be okay, child.")]\"</span>")
L.apply_status_effect(STATUS_EFFECT_ICHORIAL_STAIN)
GLOB.clockwork_vitality -= revival_cost
break
if(!L.client || L.client.is_afk())
set waitfor = FALSE
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [L.name], an inactive clock cultist?", ROLE_SERVANT_OF_RATVAR, null, ROLE_SERVANT_OF_RATVAR, 50, L)
var/mob/dead/observer/theghost = null
if(candidates.len)
to_chat(L, "<span class='userdanger'>Your physical form has been taken over by another soul due to your inactivity! Ahelp if you wish to regain your form!</span>")
message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(L)]) to replace an inactive clock cultist.")
L.ghostize(0)
L.key = theghost.key
var/obj/effect/temp_visual/ratvar/sigil/vitality/V = new /obj/effect/temp_visual/ratvar/sigil/vitality(get_turf(src))
animate(V, alpha = 0, transform = matrix()*2, time = 8)
playsound(L, 'sound/magic/staff_healing.ogg', 50, 1)
L.visible_message("<span class='warning'>[L]'s eyes suddenly open wide, gleaming with renewed vigor for the cause!</span>", "<span class='inathneq'>\"[text2ratvar("Awaken!")]\"</span>")
break
var/vitality_for_cycle = 3
if(!GLOB.ratvar_awakens)
if(L.stat == CONSCIOUS)
vitality_for_cycle = 2
vitality_for_cycle = min(GLOB.clockwork_vitality, vitality_for_cycle)
var/vitality_used = L.heal_ordered_damage(vitality_for_cycle, damage_heal_order)
if(!vitality_used)
break
if(!GLOB.ratvar_awakens)
GLOB.clockwork_vitality -= vitality_used
sleep(2)
if(sigil_active)
animation_number = initial(animation_number)
sigil_active = FALSE
animate(src, alpha = initial(alpha), time = 10, flags = ANIMATION_END_NOW)
@@ -1,63 +0,0 @@
//massive markers for Revenant and Judgement scripture.
/obj/effect/clockwork/general_marker
name = "general marker"
desc = "Some big guy. For you."
clockwork_desc = "One of Ratvar's generals."
alpha = 200
layer = MASSIVE_OBJ_LAYER
/obj/effect/clockwork/general_marker/New()
..()
animate(src, alpha = 0, time = 10)
QDEL_IN(src, 10)
/obj/effect/clockwork/general_marker/singularity_act()
return
/obj/effect/clockwork/general_marker/singularity_pull()
return
/obj/effect/clockwork/general_marker/inathneq
name = "Inath-neq, the Resonant Cogwheel"
desc = "A humanoid form blazing with blue fire. It radiates an aura of kindness and caring."
clockwork_desc = "One of Ratvar's four generals. Before her current form, Inath-neq was a powerful warrior priestess commanding the Resonant Cogs, a sect of Ratvarian warriors renowned for \
their prowess. After a lost battle with Nar-Sian cultists, Inath-neq was struck down and stated in her dying breath, \
\"The Resonant Cogs shall not fall silent this day, but will come together to form a wheel that shall never stop turning.\" Ratvar, touched by this, granted Inath-neq an eternal body and \
merged her soul with those of the Cogs slain with her on the battlefield."
icon = 'icons/effects/119x268.dmi'
icon_state = "inath-neq"
pixel_x = -59
pixel_y = -134
/obj/effect/clockwork/general_marker/nezbere
name = "Nezbere, the Brass Eidolon"
desc = "A towering colossus clad in nigh-impenetrable brass armor. Its gaze is stern yet benevolent, even upon you."
clockwork_desc = "One of Ratvar's four generals. Nezbere is responsible for the design, testing, and creation of everything in Ratvar's domain, and his loyalty to Ratvar knows no bounds. \
It is said that Ratvar once asked him to destroy the plans for a weapon Nezbere had made that could have harmed him, and Nezbere responded by not only destroying the plans, \
but by taking his own life so that the device could never be replicated. Nezbere's zealotry is unmatched."
icon = 'icons/effects/237x321.dmi'
icon_state = "nezbere"
pixel_x = -118
pixel_y = -160
/obj/effect/clockwork/general_marker/sevtug
name = "Sevtug, the Formless Pariah"
desc = "A sinister cloud of purple energy. Looking at it gives you a headache."
clockwork_desc = "One of Ratvar's four generals. Sevtug taught him how to manipulate minds and is one of his oldest allies. Sevtug serves Ratvar loyally out of a hope that one day, he will \
be able to use a moment of weakness in the Justicar to usurp him, but such a day will never come. And so, he serves with dedication, if not necessarily any sort of decorum, never aware he is \
the one being made a fool of."
icon = 'icons/effects/166x195.dmi'
icon_state = "sevtug"
pixel_x = -83
pixel_y = -97
/obj/effect/clockwork/general_marker/nzcrentr
name = "Nzcrentr, the Eternal Thunderbolt"
desc = "A terrifying spiked construct crackling with perpetual lightning."
clockwork_desc = "One of Ratvar's four generals. Before becoming one of Ratvar's generals, Nzcrentr sook out any and all sentient life to slaughter it for sport. \
Nzcrentr was coerced by Ratvar into entering a shell constructed by Nezbere, ostensibly made to grant Nzcrentr more power. In reality, the shell was made to trap and control it. \
Nzcrentr now serves loyally, though even one of Nezbere's finest creations was not enough to totally eliminate its will."
icon = 'icons/effects/274x385.dmi'
icon_state = "nzcrentr"
pixel_x = -137
pixel_y = -145
@@ -1,41 +0,0 @@
//Marks the point at which "no man's land" begins on Reebe. Servants can't pass beyond this point in any way.
/obj/effect/clockwork/servant_blocker
name = "glowing arrow"
desc = "A faintly glowing image of an arrow on the ground. Convenient!"
icon_state = "servant_blocker"
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
anchored = TRUE
density = TRUE
CanAtmosPass = ATMOS_PASS_NO
/obj/effect/clockwork/servant_blocker/Initialize()
. = ..()
air_update_turf(TRUE)
/obj/effect/clockwork/servant_blocker/Destroy(force)
if(!force)
return
var/turf/T = get_turf(src)
. = ..()
T.air_update_turf(TRUE)
/obj/effect/clockwork/servant_blocker/CanPass(atom/movable/M, turf/target)
var/list/target_contents = M.GetAllContents() + M
for(var/mob/living/L in target_contents)
if(is_servant_of_ratvar(L) && get_dir(M, src) != dir && L.stat != DEAD) //Unless we're on the side the arrow is pointing directly away from, no-go
to_chat(L, "<span class='danger'>The space beyond here can't be accessed by you or other servants.</span>")
return
if(isitem(M))
var/obj/item/I = M
if(is_servant_of_ratvar(I.thrownby)) //nice try!
return
return TRUE
/obj/effect/clockwork/servant_blocker/BlockSuperconductivity()
return TRUE
/obj/effect/clockwork/servant_blocker/singularity_act()
return
/obj/effect/clockwork/servant_blocker/singularity_pull()
return
@@ -1,225 +0,0 @@
//Spatial gateway: A usually one-way rift to another location.
/obj/effect/clockwork/spatial_gateway
name = "spatial gateway"
desc = "A gently thrumming tear in reality."
clockwork_desc = "A gateway in reality."
icon_state = "spatial_gateway"
density = TRUE
light_range = 2
light_power = 3
light_color = "#6A4D2F"
var/sender = TRUE //If this gateway is made for sending, not receiving
var/both_ways = FALSE
var/lifetime = 25 //How many deciseconds this portal will last
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
/obj/effect/clockwork/spatial_gateway/Initialize()
. = ..()
addtimer(CALLBACK(src, .proc/check_setup), 1)
/obj/effect/clockwork/spatial_gateway/Destroy()
deltimer(timerid)
return ..()
/obj/effect/clockwork/spatial_gateway/proc/check_setup()
if(!linked_gateway)
qdel(src)
return
if(both_ways)
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)
//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)
return FALSE
linked_gateway = gatewayB
gatewayB.linked_gateway = src
if(two_way)
both_ways = TRUE
gatewayB.both_ways = TRUE
else
sender = TRUE
gatewayB.sender = FALSE
gatewayB.density = FALSE
lifetime = set_duration
gatewayB.lifetime = set_duration
uses = set_uses
gatewayB.uses = set_uses
return TRUE
/obj/effect/clockwork/spatial_gateway/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
to_chat(user, "<span class='brass'>It has [uses] use\s remaining.</span>")
/obj/effect/clockwork/spatial_gateway/attack_ghost(mob/user)
if(linked_gateway)
user.forceMove(get_turf(linked_gateway))
..()
/obj/effect/clockwork/spatial_gateway/attack_hand(mob/living/user)
if(!uses)
return FALSE
if(user.pulling && user.a_intent == INTENT_GRAB && isliving(user.pulling))
var/mob/living/L = user.pulling
if(L.buckled || L.anchored || L.has_buckled_mobs())
return FALSE
user.visible_message("<span class='warning'>[user] shoves [L] into [src]!</span>", "<span class='danger'>You shove [L] into [src]!</span>")
user.stop_pulling()
pass_through_gateway(L)
return TRUE
if(!user.canUseTopic(src))
return FALSE
user.visible_message("<span class='warning'>[user] climbs through [src]!</span>", "<span class='danger'>You brace yourself and step through [src]...</span>")
pass_through_gateway(user)
return TRUE
/obj/effect/clockwork/spatial_gateway/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/nullrod))
user.visible_message("<span class='warning'>[user] dispels [src] with [I]!</span>", "<span class='danger'>You close [src] with [I]!</span>")
qdel(linked_gateway)
qdel(src)
return TRUE
if(istype(I, /obj/item/clockwork/slab))
to_chat(user, "<span class='heavy_brass'>\"I don't think you want to drop your slab into that.\"\n\"If you really want to, try throwing it.\"</span>")
return TRUE
if(uses && user.dropItemToGround(I))
user.visible_message("<span class='warning'>[user] drops [I] into [src]!</span>", "<span class='danger'>You drop [I] into [src]!</span>")
pass_through_gateway(I, TRUE)
return TRUE
return ..()
/obj/effect/clockwork/spatial_gateway/ex_act(severity)
if(severity == 1 && uses)
uses = 0
visible_message("<span class='warning'>[src] is disrupted!</span>")
animate(src, alpha = 0, transform = matrix()*2, time = 10, flags = ANIMATION_END_NOW)
deltimer(timerid)
timerid = QDEL_IN(src, 10)
linked_gateway.uses = 0
linked_gateway.visible_message("<span class='warning'>[linked_gateway] is disrupted!</span>")
animate(linked_gateway, alpha = 0, transform = matrix()*2, time = 10, flags = ANIMATION_END_NOW)
deltimer(linked_gateway.timerid)
linked_gateway.timerid = QDEL_IN(linked_gateway, 10)
return TRUE
return FALSE
/obj/effect/clockwork/spatial_gateway/singularity_act()
return
/obj/effect/clockwork/spatial_gateway/singularity_pull()
return
/obj/effect/clockwork/spatial_gateway/CollidedWith(atom/movable/AM)
..()
if(!QDELETED(AM))
pass_through_gateway(AM, FALSE)
/obj/effect/clockwork/spatial_gateway/proc/pass_through_gateway(atom/movable/A, no_cost)
if(!linked_gateway)
qdel(src)
return FALSE
if(!sender)
visible_message("<span class='warning'>[A] bounces off [src]!</span>")
return FALSE
if(!uses)
return FALSE
if(isliving(A))
var/mob/living/user = A
to_chat(user, "<span class='warning'><b>You pass through [src] and appear elsewhere!</b></span>")
linked_gateway.visible_message("<span class='warning'>A shape appears in [linked_gateway] before emerging!</span>")
playsound(src, 'sound/effects/empulse.ogg', 50, 1)
playsound(linked_gateway, 'sound/effects/empulse.ogg', 50, 1)
transform = matrix() * 1.5
linked_gateway.transform = matrix() * 1.5
A.forceMove(get_turf(linked_gateway))
if(!no_cost)
uses = max(0, uses - 1)
linked_gateway.uses = max(0, linked_gateway.uses - 1)
if(!uses)
animate(src, transform = matrix() * 0.1, time = 10, flags = ANIMATION_END_NOW)
animate(linked_gateway, transform = matrix() * 0.1, time = 10, flags = ANIMATION_END_NOW)
density = FALSE
linked_gateway.density = FALSE
else
animate(src, transform = matrix() / 1.5, time = 10, flags = ANIMATION_END_NOW)
animate(linked_gateway, transform = matrix() / 1.5, time = 10, flags = ANIMATION_END_NOW)
addtimer(CALLBACK(src, .proc/check_uses), 10)
return TRUE
/obj/effect/clockwork/spatial_gateway/proc/check_uses()
if(!uses)
qdel(src)
qdel(linked_gateway)
//This proc creates and sets up a gateway from invoker input.
/atom/movable/proc/procure_gateway(mob/living/invoker, time_duration, gateway_uses, two_way)
var/list/possible_targets = list()
var/list/teleportnames = list()
for(var/obj/structure/destructible/clockwork/powered/clockwork_obelisk/O in GLOB.all_clockwork_objects)
if(!O.Adjacent(invoker) && O != src && !is_away_level(O.z) && O.anchored) //don't list obelisks that we're next to
var/area/A = get_area(O)
var/locname = initial(A.name)
possible_targets[avoid_assoc_duplicate_keys("[locname] [O.name]", teleportnames)] = O
for(var/mob/living/L in GLOB.alive_mob_list)
if(!L.stat && is_servant_of_ratvar(L) && !L.Adjacent(invoker) && !is_away_level(L.z)) //People right next to the invoker can't be portaled to, for obvious reasons
possible_targets[avoid_assoc_duplicate_keys("[L.name] ([L.real_name])", teleportnames)] = L
if(!possible_targets.len)
to_chat(invoker, "<span class='warning'>There are no other eligible targets for a Spatial Gateway!</span>")
return FALSE
var/input_target_key = input(invoker, "Choose a target to form a rift to.", "Spatial Gateway") as null|anything in possible_targets
var/atom/movable/target = possible_targets[input_target_key]
if(!src || !input_target_key || !invoker || !invoker.canUseTopic(src, !issilicon(invoker)) || !is_servant_of_ratvar(invoker) || (isitem(src) && invoker.get_active_held_item() != src) || !invoker.can_speak_vocal())
return FALSE //if any of the involved things no longer exist, the invoker is stunned, too far away to use the object, or does not serve ratvar, or if the object is an item and not in the mob's active hand, fail
if(!target) //if we have no target, but did have a key, let them retry
to_chat(invoker, "<span class='warning'>That target no longer exists!</span>")
return procure_gateway(invoker, time_duration, gateway_uses, two_way)
if(isliving(target))
var/mob/living/L = target
if(!is_servant_of_ratvar(L))
to_chat(invoker, "<span class='warning'>That target is no longer a Servant!</span>")
return procure_gateway(invoker, time_duration, gateway_uses, two_way)
if(L.stat != CONSCIOUS)
to_chat(invoker, "<span class='warning'>That Servant is no longer conscious!</span>")
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)
if(!anchored)
to_chat(invoker, "<span class='warning'>[src] is no longer secured!</span>")
return FALSE
var/obj/structure/destructible/clockwork/powered/clockwork_obelisk/CO = src //foolish as I am, how I set this proc up makes substypes unfeasible
if(CO.active)
to_chat(invoker, "<span class='warning'>[src] is now sustaining a gateway!</span>")
return FALSE
if(istargetobelisk)
if(!target.anchored)
to_chat(invoker, "<span class='warning'>That [target.name] is no longer secured!</span>")
return procure_gateway(invoker, time_duration, gateway_uses, two_way)
var/obj/structure/destructible/clockwork/powered/clockwork_obelisk/CO = target
if(CO.active)
to_chat(invoker, "<span class='warning'>That [target.name] is sustaining a gateway, and cannot receive another!</span>")
return procure_gateway(invoker, time_duration, gateway_uses, two_way)
var/efficiency = CO.get_efficiency_mod()
gateway_uses = round(gateway_uses * (2 * efficiency), 1)
time_duration = round(time_duration * (2 * efficiency), 1)
CO.active = TRUE //you'd be active in a second but you should update immediately
invoker.visible_message("<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>")
return TRUE
@@ -1,53 +0,0 @@
//horrifying power drain proc made for clockcult's power drain in lieu of six istypes or six for(x in view) loops
/atom/movable/proc/power_drain(clockcult_user)
var/obj/item/stock_parts/cell/cell = get_cell()
if(cell)
return cell.power_drain(clockcult_user)
return 0
/obj/item/melee/baton/power_drain(clockcult_user) //balance memes
return 0
/obj/item/gun/power_drain(clockcult_user) //balance memes
return 0
/obj/machinery/power/apc/power_drain(clockcult_user)
if(cell && cell.charge)
playsound(src, "sparks", 50, 1)
flick("apc-spark", src)
. = min(cell.charge, MIN_CLOCKCULT_POWER*3)
cell.use(.) //Better than a power sink!
if(!cell.charge && !shorted)
shorted = 1
visible_message("<span class='warning'>The [name]'s screen blurs with static.</span>")
update()
update_icon()
/obj/machinery/power/smes/power_drain(clockcult_user)
if(charge)
. = min(charge, MIN_CLOCKCULT_POWER*3)
charge -= . * 50
if(!charge && !panel_open)
panel_open = TRUE
icon_state = "[initial(icon_state)]-o"
do_sparks(10, FALSE, src)
visible_message("<span class='warning'>[src]'s panel flies open with a flurry of sparks!</span>")
update_icon()
/obj/item/stock_parts/cell/power_drain(clockcult_user)
if(charge)
. = min(charge, MIN_CLOCKCULT_POWER*3)
charge = use(.)
update_icon()
/mob/living/silicon/robot/power_drain(clockcult_user)
if((!clockcult_user || !is_servant_of_ratvar(src)) && cell && cell.charge)
. = min(cell.charge, MIN_CLOCKCULT_POWER*4)
cell.use(.)
spark_system.start()
/obj/mecha/power_drain(clockcult_user)
if((!clockcult_user || (occupant && !is_servant_of_ratvar(occupant))) && cell && cell.charge)
. = min(cell.charge, MIN_CLOCKCULT_POWER*4)
cell.use(.)
spark_system.start()
@@ -1,41 +0,0 @@
//returns a component spanclass from a component id
/proc/get_component_span(id)
switch(id)
if(BELLIGERENT_EYE)
return "neovgre"
if(VANGUARD_COGWHEEL)
return "inathneq"
if(GEIS_CAPACITOR)
return "sevtug"
if(REPLICANT_ALLOY)
return "nezbere"
if(HIEROPHANT_ANSIBLE)
return "nzcrentr"
else
return "brass"
//returns a component color from a component id, but with brighter colors for the darkest
/proc/get_component_color_bright(id)
switch(id)
if(BELLIGERENT_EYE)
return "#880020"
if(REPLICANT_ALLOY)
return "#5A6068"
else
return get_component_color(id)
//returns a component color from a component id
/proc/get_component_color(id)
switch(id)
if(BELLIGERENT_EYE)
return "#6E001A"
if(VANGUARD_COGWHEEL)
return "#1E8CE1"
if(GEIS_CAPACITOR)
return "#AF0AAF"
if(REPLICANT_ALLOY)
return "#42474D"
if(HIEROPHANT_ANSIBLE)
return "#DAAA18"
else
return "#BE8700"
@@ -1,347 +0,0 @@
//For the clockwork fabricator, this proc exists to make it easy to customize what the fabricator does when hitting something.
//if a valid target, returns an associated list in this format;
//list("operation_time" = 15, "new_obj_type" = /obj/structure/window/reinforced/clockwork, "power_cost" = 5, "spawn_dir" = dir, "dir_in_new" = TRUE)
//otherwise, return literally any non-list thing but preferably FALSE
//returning TRUE won't produce the "cannot be fabricated" message and will still prevent fabrication
/atom/proc/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
return FALSE
/atom/proc/consume_visual(obj/item/clockwork/replica_fabricator/fabricator, power_amount)
if(get_clockwork_power(power_amount))
var/obj/effect/temp_visual/ratvar/beam/itemconsume/B = new /obj/effect/temp_visual/ratvar/beam/itemconsume(get_turf(src))
B.pixel_x = pixel_x
B.pixel_y = pixel_y
//Turf conversion
/turf/closed/wall/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent) //four sheets of metal
return list("operation_time" = 50, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_TOTAL - (POWER_METAL * 4), "spawn_dir" = SOUTH)
/turf/closed/wall/mineral/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent) //two sheets of metal
return list("operation_time" = 50, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_TOTAL - (POWER_METAL * 2), "spawn_dir" = SOUTH)
/turf/closed/wall/mineral/iron/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent) //two sheets of metal, five rods
return list("operation_time" = 50, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_TOTAL - (POWER_METAL * 2) - (POWER_ROD * 5), "spawn_dir" = SOUTH)
/turf/closed/wall/mineral/cult/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent) //no metal
return list("operation_time" = 80, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_TOTAL, "spawn_dir" = SOUTH)
/turf/closed/wall/r_wall/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
return FALSE
/turf/closed/wall/clockwork/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
return list("operation_time" = 50, "new_obj_type" = /turf/open/floor/clockwork, "power_cost" = -POWER_WALL_MINUS_FLOOR, "spawn_dir" = SOUTH)
/turf/open/floor/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
if(floor_tile == /obj/item/stack/tile/plasteel)
new floor_tile(src)
make_plating()
playsound(src, 'sound/items/crowbar.ogg', 10, 1) //clink
return list("operation_time" = 30, "new_obj_type" = /turf/open/floor/clockwork, "power_cost" = POWER_FLOOR, "spawn_dir" = SOUTH)
/turf/open/floor/plating/asteroid/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
return FALSE
/turf/open/floor/plating/ashplanet/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
return FALSE
/turf/open/lava/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
return FALSE
/turf/open/floor/clockwork/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
if(locate(/obj/structure/table) in src)
return FALSE
if(locate(/obj/structure/falsewall) in contents)
to_chat(user, "<span class='warning'>There is a false wall in the way, preventing you from fabricating a clockwork wall on [src].</span>")
return
if(is_blocked_turf(src, TRUE))
to_chat(user, "<span class='warning'>Something is in the way, preventing you from fabricating a clockwork wall on [src].</span>")
return TRUE
var/operation_time = 100
if(!GLOB.ratvar_awakens && fabricator.speed_multiplier > 0) //if ratvar isn't awake, this always takes 10 seconds
operation_time /= fabricator.speed_multiplier
return list("operation_time" = operation_time, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_MINUS_FLOOR, "spawn_dir" = SOUTH)
//False wall conversion
/obj/structure/falsewall/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
var/cost = POWER_WALL_MINUS_FLOOR
if(ispath(mineral, /obj/item/stack/sheet/metal))
cost -= (POWER_METAL * (2 + mineral_amount)) //four sheets of metal, plus an assumption that the girder is also two
else
cost -= (POWER_METAL * 2) //anything that doesn't use metal just has the girder
return list("operation_time" = 50, "new_obj_type" = /obj/structure/falsewall/brass, "power_cost" = cost, "spawn_dir" = SOUTH)
/obj/structure/falsewall/iron/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent) //two sheets of metal, two rods; special assumption
return list("operation_time" = 50, "new_obj_type" = /obj/structure/falsewall/brass, "power_cost" = POWER_WALL_MINUS_FLOOR - (POWER_METAL * 2) - (POWER_ROD * 2), "spawn_dir" = SOUTH)
/obj/structure/falsewall/reinforced/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
return FALSE
/obj/structure/falsewall/brass/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
return FALSE
//Metal conversion
/obj/item/stack/tile/plasteel/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
if(source)
return FALSE
var/amount_temp = get_amount()
var/no_delete = FALSE
if(amount_temp < 2)
to_chat(user, "<span class='warning'>You need at least <b>2</b> floor tiles to convert into power.</span>")
return TRUE
if(ISODD(amount_temp))
amount_temp--
no_delete = TRUE
use(amount_temp)
amount_temp *= 12.5 //each tile is 12.5 power so this is 2 tiles to 25 power
consume_visual(fabricator, amount_temp)
return list("operation_time" = 0, "new_obj_type" = null, "power_cost" = -amount_temp, "spawn_dir" = SOUTH, "no_target_deletion" = no_delete)
/obj/item/stack/rods/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
if(source)
return FALSE
var/power_amount = -(amount*POWER_ROD)
consume_visual(fabricator, power_amount)
return list("operation_time" = 0, "new_obj_type" = null, "power_cost" = power_amount, "spawn_dir" = SOUTH)
/obj/item/stack/sheet/metal/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
if(source)
return FALSE
var/power_amount = -(amount*POWER_METAL)
consume_visual(fabricator, power_amount)
return list("operation_time" = 0, "new_obj_type" = null, "power_cost" = power_amount, "spawn_dir" = SOUTH)
/obj/item/stack/sheet/plasteel/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
if(source)
return FALSE
var/power_amount = -(amount*POWER_PLASTEEL)
consume_visual(fabricator, power_amount)
return list("operation_time" = 0, "new_obj_type" = null, "power_cost" = power_amount, "spawn_dir" = SOUTH)
//Brass directly to power
/obj/item/stack/tile/brass/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
if(source)
return FALSE
var/power_amount = -(amount*POWER_FLOOR)
consume_visual(fabricator, power_amount)
return list("operation_time" = 0, "new_obj_type" = null, "power_cost" = power_amount, "spawn_dir" = SOUTH)
//Airlock conversion
/obj/machinery/door/airlock/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
var/doortype = /obj/machinery/door/airlock/clockwork
if(glass)
doortype = /obj/machinery/door/airlock/clockwork/brass
return list("operation_time" = 60, "new_obj_type" = doortype, "power_cost" = POWER_WALL_TOTAL, "spawn_dir" = dir, "transfer_name" = TRUE)
/obj/machinery/door/airlock/clockwork/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
return FALSE
//Table conversion
/obj/structure/table/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
var/fabrication_cost = POWER_STANDARD
if(framestack == /obj/item/stack/rods)
fabrication_cost -= POWER_ROD*framestackamount
else if(framestack == /obj/item/stack/tile/brass)
fabrication_cost -= POWER_FLOOR*framestackamount
if(buildstack == /obj/item/stack/sheet/metal)
fabrication_cost -= POWER_METAL*buildstackamount
else if(buildstack == /obj/item/stack/sheet/plasteel)
fabrication_cost -= POWER_PLASTEEL*buildstackamount
return list("operation_time" = 20, "new_obj_type" = /obj/structure/table/reinforced/brass, "power_cost" = fabrication_cost, "spawn_dir" = SOUTH)
/obj/structure/table/reinforced/brass/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
return FALSE
/obj/structure/table_frame/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
var/fabrication_cost = POWER_FLOOR
if(framestack == /obj/item/stack/rods)
fabrication_cost -= POWER_ROD*framestackamount
else if(framestack == /obj/item/stack/tile/brass)
fabrication_cost -= POWER_FLOOR*framestackamount
return list("operation_time" = 10, "new_obj_type" = /obj/structure/table_frame/brass, "power_cost" = fabrication_cost, "spawn_dir" = SOUTH)
/obj/structure/table_frame/brass/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
return FALSE
//Window conversion
/obj/structure/window/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
var/windowtype = /obj/structure/window/reinforced/clockwork
var/new_dir = TRUE
var/fabrication_time = 15
var/fabrication_cost = POWER_FLOOR
if(fulltile)
windowtype = /obj/structure/window/reinforced/clockwork/fulltile
new_dir = FALSE
fabrication_time = 30
fabrication_cost = POWER_STANDARD
if(reinf)
fabrication_cost -= POWER_ROD
if(reinf)
fabrication_cost -= POWER_ROD
for(var/obj/structure/grille/G in get_turf(src))
INVOKE_ASYNC(fabricator, /obj/item/clockwork/replica_fabricator.proc/fabricate, G, user)
return list("operation_time" = fabrication_time, "new_obj_type" = windowtype, "power_cost" = fabrication_cost, "spawn_dir" = dir, "dir_in_new" = new_dir)
/obj/structure/window/reinforced/clockwork/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
return FALSE
//Windoor conversion
/obj/machinery/door/window/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
return list("operation_time" = 30, "new_obj_type" = /obj/machinery/door/window/clockwork, "power_cost" = POWER_STANDARD, "spawn_dir" = dir, "dir_in_new" = TRUE, "transfer_name" = TRUE)
/obj/machinery/door/window/clockwork/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
return FALSE
//Grille conversion
/obj/structure/grille/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
var/grilletype = /obj/structure/grille/ratvar
var/fabrication_time = 15
if(broken)
grilletype = /obj/structure/grille/ratvar/broken
fabrication_time = 5
return list("operation_time" = fabrication_time, "new_obj_type" = grilletype, "power_cost" = 0, "spawn_dir" = dir)
/obj/structure/grille/ratvar/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
return FALSE
//Lattice conversion
/obj/structure/lattice/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
return list("operation_time" = 0, "new_obj_type" = /obj/structure/lattice/clockwork, "power_cost" = 0, "spawn_dir" = SOUTH, "no_target_deletion" = TRUE)
/obj/structure/lattice/clockwork/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
ratvar_act() //just in case we're the wrong type for some reason??
return FALSE
/obj/structure/lattice/catwalk/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
return list("operation_time" = 0, "new_obj_type" = /obj/structure/lattice/catwalk/clockwork, "power_cost" = 0, "spawn_dir" = SOUTH, "no_target_deletion" = TRUE)
/obj/structure/lattice/catwalk/clockwork/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
return FALSE
//Girder conversion
/obj/structure/girder/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
var/fabrication_cost = POWER_GEAR - (POWER_METAL * 2)
if(state == GIRDER_REINF_STRUTS || state == GIRDER_REINF)
fabrication_cost -= POWER_PLASTEEL
return list("operation_time" = 20, "new_obj_type" = /obj/structure/destructible/clockwork/wall_gear, "power_cost" = fabrication_cost, "spawn_dir" = SOUTH)
//Hitting a clockwork structure will try to repair it.
/obj/structure/destructible/clockwork/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
. = TRUE
var/list/repair_values = list()
if(!fabricator.fabricator_repair_checks(repair_values, src, user))
return
user.visible_message("<span class='notice'>[user]'s [fabricator.name] starts covering [src] in glowing orange energy...</span>", \
"<span class='alloy'>You start repairing [src]...</span>")
fabricator.repairing = src
while(fabricator && user && src)
if(!do_after(user, repair_values["healing_for_cycle"] * fabricator.speed_multiplier, target = src, \
extra_checks = CALLBACK(fabricator, /obj/item/clockwork/replica_fabricator.proc/fabricator_repair_checks, repair_values, src, user, TRUE)))
break
obj_integrity = CLAMP(obj_integrity + repair_values["healing_for_cycle"], 0, max_integrity)
adjust_clockwork_power(-repair_values["power_required"])
playsound(src, 'sound/machines/click.ogg', 50, 1)
if(fabricator)
fabricator.repairing = null
if(user)
user.visible_message("<span class='notice'>[user]'s [fabricator.name] stops covering [src] with glowing orange energy.</span>", \
"<span class='alloy'>You finish repairing [src]. It is now at <b>[obj_integrity]/[max_integrity]</b> integrity.</span>")
//Fabricator mob heal proc, to avoid as much copypaste as possible.
/mob/living/proc/fabricator_heal(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator)
var/list/repair_values = list()
if(!fabricator.fabricator_repair_checks(repair_values, src, user))
return
user.visible_message("<span class='notice'>[user]'s [fabricator.name] starts coverin[src == user ? "g [user.p_them()]" : "g [src]"] in glowing orange energy...</span>", \
"<span class='alloy'>You start repairin[src == user ? "g yourself" : "g [src]"]...</span>")
fabricator.repairing = src
while(fabricator && user && src)
if(!do_after(user, repair_values["healing_for_cycle"] * fabricator.speed_multiplier, target = src, \
extra_checks = CALLBACK(fabricator, /obj/item/clockwork/replica_fabricator.proc/fabricator_repair_checks, repair_values, src, user, TRUE)))
break
fabricator_heal_tick(repair_values["healing_for_cycle"])
adjust_clockwork_power(-repair_values["power_required"])
playsound(src, 'sound/machines/click.ogg', 50, 1)
if(fabricator)
fabricator.repairing = null
return TRUE
/mob/living/proc/fabricator_heal_tick(amount)
var/static/list/damage_heal_order = list(BRUTE, BURN, TOX, OXY)
heal_ordered_damage(amount, damage_heal_order)
/mob/living/simple_animal/fabricator_heal_tick(amount)
adjustHealth(-amount)
//Hitting a ratvar'd silicon will also try to repair it.
/mob/living/silicon/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
. = TRUE
if(health == maxHealth) //if we're at maximum health, replace the turf under us
return FALSE
else if(fabricator_heal(user, fabricator) && user)
user.visible_message("<span class='notice'>[user]'s [fabricator.name] stops coverin[src == user ? "g [user.p_them()]" : "g [src]"] with glowing orange energy.</span>", \
"<span class='alloy'>You finish repairin[src == user ? "g yourself. You are":"g [src]. [p_they(TRUE)] [p_are()]"] now at <b>[abs(HEALTH_THRESHOLD_DEAD - health)]/[abs(HEALTH_THRESHOLD_DEAD - maxHealth)]</b> health.</span>")
//Same with clockwork mobs.
/mob/living/simple_animal/hostile/clockwork/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
. = TRUE
if(health == maxHealth) //if we're at maximum health, replace the turf under us
return FALSE
else if(fabricator_heal(user, fabricator) && user)
user.visible_message("<span class='notice'>[user]'s [fabricator.name] stops coverin[src == user ? "g [user.p_them()]" : "g [src]"] with glowing orange energy.</span>", \
"<span class='alloy'>You finish repairin[src == user ? "g yourself. You are":"g [src]. [p_they(TRUE)] [p_are()]"] now at <b>[health]/[maxHealth]</b> health.</span>")
//Cogscarabs get special interaction because they're drones and have innate self-heals/revives.
/mob/living/simple_animal/drone/cogscarab/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
. = TRUE
if(stat == DEAD)
try_reactivate(user) //if we're dead, try to repair us
return
if(health == maxHealth)
return FALSE
else if(!(flags_1 & GODMODE))
user.visible_message("<span class='notice'>[user]'s [fabricator.name] starts coverin[src == user ? "g [user.p_them()]" : "g [src]"] in glowing orange energy...</span>", \
"<span class='alloy'>You start repairin[src == user ? "g yourself" : "g [src]"]...</span>")
fabricator.repairing = src
if(do_after(user, (maxHealth - health)*2, target=src))
adjustHealth(-maxHealth)
user.visible_message("<span class='notice'>[user]'s [fabricator.name] stops coverin[src == user ? "g [user.p_them()]" : "g [src]"] with glowing orange energy.</span>", \
"<span class='alloy'>You finish repairin[src == user ? "g yourself" : "g [src]"].</span>")
if(fabricator)
fabricator.repairing = null
//Convert shards and gear bits directly to power
/obj/item/clockwork/alloy_shards/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent, power_amount)
if(!power_amount)
power_amount = -POWER_STANDARD
consume_visual(fabricator, power_amount)
if(!silent) //looper no looping
for(var/obj/item/clockwork/alloy_shards/S in get_turf(src)) //convert all other shards in the turf if we can
if(S == src)
continue //we want the shards to be fabricated after the main shard, thus this delay
addtimer(CALLBACK(fabricator, /obj/item/clockwork/replica_fabricator.proc/fabricate, S, user, TRUE), 0)
return list("operation_time" = 0, "new_obj_type" = null, "power_cost" = power_amount, "spawn_dir" = SOUTH)
/obj/item/clockwork/alloy_shards/medium/gear_bit/large/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent, power_amount)
if(!power_amount)
power_amount = -(CLOCKCULT_POWER_UNIT*0.08)
return ..()
/obj/item/clockwork/alloy_shards/large/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent, power_amount)
if(!power_amount)
power_amount = -(CLOCKCULT_POWER_UNIT*0.06)
return ..()
/obj/item/clockwork/alloy_shards/medium/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent, power_amount)
if(!power_amount)
power_amount = -(CLOCKCULT_POWER_UNIT*0.04)
return ..()
/obj/item/clockwork/alloy_shards/small/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent, power_amount)
if(!power_amount)
power_amount = -(CLOCKCULT_POWER_UNIT*0.02)
return ..()
@@ -1,50 +0,0 @@
//Sends a string to all servants and optionally ghosts, who will get a follow link to whatever is provided as the target.
/proc/hierophant_message(message, servantsonly, atom/target)
if(!message)
return FALSE
for(var/M in GLOB.mob_list)
if(!servantsonly && isobserver(M))
if(target)
var/link = FOLLOW_LINK(M, target)
to_chat(M, "[link] [message]")
else
to_chat(M, message)
else if(is_servant_of_ratvar(M))
to_chat(M, message)
return TRUE
//Sends a titled message from a mob to all servants of ratvar and ghosts.
/proc/titled_hierophant_message(mob/user, message, name_span = "heavy_brass", message_span = "brass", user_title = "Servant")
if(!user || !message)
return FALSE
var/parsed_message = "<span class='[name_span]'>[user_title ? "[user_title] ":""][findtextEx(user.name, user.real_name) ? user.name : "[user.real_name] (as [user.name])"]: \
</span><span class='[message_span]'>\"[message]\"</span>"
hierophant_message(parsed_message, FALSE, user)
return TRUE
//Hierophant Network action, allows a servant with it to communicate to other servants.
/datum/action/innate/hierophant
name = "Hierophant Network"
desc = "Allows you to communicate with other Servants."
icon_icon = 'icons/mob/actions/actions_clockcult.dmi'
button_icon_state = "hierophant"
background_icon_state = "bg_clock"
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUN|AB_CHECK_CONSCIOUS
buttontooltipstyle = "clockcult"
var/title = "Servant"
var/span_for_name = "heavy_brass"
var/span_for_message = "brass"
/datum/action/innate/hierophant/IsAvailable()
if(!is_servant_of_ratvar(owner))
return FALSE
return ..()
/datum/action/innate/hierophant/Activate()
var/input = stripped_input(usr, "Please enter a message to send to other servants.", "Hierophant Network", "")
if(!input || !IsAvailable())
return
if(ishuman(owner))
clockwork_say(owner, "[text2ratvar("Servants, hear my words: [input]")]", TRUE)
log_talk(owner,"CLOCK:[key_name(owner)] : [input]",LOGSAY)
titled_hierophant_message(owner, input, span_for_name, span_for_message, title)
@@ -1,28 +0,0 @@
//Helper procs for clockwork power, used by structures and items and that kind of jazz.
/proc/get_clockwork_power(amount) //If no amount is provided, returns the clockwork power; otherwise, returns if there's enough power for that amount.
return amount ? GLOB.clockwork_power >= amount : GLOB.clockwork_power
/proc/adjust_clockwork_power(amount) //Adjusts the global clockwork power by this amount (min 0.)
if(GLOB.ratvar_approaches)
amount *= 0.75 //The herald's beacon reduces power costs by 25% across the board!
GLOB.clockwork_power = GLOB.ratvar_awakens ? INFINITY : max(0, GLOB.clockwork_power + amount)
for(var/obj/effect/clockwork/sigil/transmission/T in GLOB.all_clockwork_objects)
T.update_icon()
var/power_overwhelming = GLOB.clockwork_power
var/unlock_message
if(power_overwhelming >= SCRIPT_UNLOCK_THRESHOLD && !GLOB.script_scripture_unlocked)
GLOB.script_scripture_unlocked = TRUE
unlock_message = "<span class='large_brass bold'>The Ark swells as a key power threshold is reached. Script scriptures are now available.</span>"
if(power_overwhelming >= APPLICATION_UNLOCK_THRESHOLD && !GLOB.application_scripture_unlocked)
GLOB.application_scripture_unlocked = TRUE
unlock_message = "<span class='large_brass bold'>The Ark surges as a key power threshold is reached. Application scriptures are now available.</span>"
if(GLOB.servants_active)
hierophant_message(unlock_message)
return TRUE
/proc/can_access_clockwork_power(atom/movable/access_point, amount) //Returns true if the access point has access to clockwork power (and optionally, a number of watts for it)
if(amount && !get_clockwork_power(amount)) //No point in trying if we don't have the power anyway
return
var/list/possible_conduits = view(5, access_point)
return locate(/obj/effect/clockwork/sigil/transmission) in possible_conduits || GLOB.ratvar_awakens
@@ -1,393 +0,0 @@
//For the clockwork proselytizer, this proc exists to make it easy to customize what the proselytizer does when hitting something.
//if a valid target, returns an associated list in this format;
//list("operation_time" = 15, "new_obj_type" = /obj/structure/window/reinforced/clockwork, "power_cost" = 5, "spawn_dir" = dir, "dir_in_new" = TRUE)
//otherwise, return literally any non-list thing but preferably FALSE
//returning TRUE won't produce the "cannot be proselytized" message and will still prevent proselytizing
/atom/proc/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
//Turf conversion
/turf/closed/wall/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) //four sheets of metal
return list("operation_time" = 50, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_TOTAL - (POWER_METAL * 4), "spawn_dir" = SOUTH)
/turf/closed/wall/mineral/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) //two sheets of metal
return list("operation_time" = 50, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_TOTAL - (POWER_METAL * 2), "spawn_dir" = SOUTH)
/turf/closed/wall/mineral/iron/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) //two sheets of metal, five rods
return list("operation_time" = 50, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_TOTAL - (POWER_METAL * 2) - (POWER_ROD * 5), "spawn_dir" = SOUTH)
/turf/closed/wall/mineral/cult/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) //no metal
return list("operation_time" = 80, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_TOTAL, "spawn_dir" = SOUTH)
/turf/closed/wall/shuttle/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) //two sheets of metal
return list("operation_time" = 50, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_TOTAL - (POWER_METAL * 2), "spawn_dir" = SOUTH)
/turf/closed/wall/r_wall/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
/turf/closed/wall/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return list("operation_time" = 50, "new_obj_type" = /turf/open/floor/clockwork, "power_cost" = -POWER_WALL_MINUS_FLOOR, "spawn_dir" = SOUTH)
/turf/open/floor/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
if(floor_tile == /obj/item/stack/tile/plasteel)
new floor_tile(src)
make_plating()
playsound(src, 'sound/items/Crowbar.ogg', 10, 1) //clink
return list("operation_time" = 30, "new_obj_type" = /turf/open/floor/clockwork, "power_cost" = POWER_FLOOR, "spawn_dir" = SOUTH)
/turf/open/floor/plating/asteroid/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
/turf/open/floor/plating/ashplanet/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
/turf/open/floor/plating/lava/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
/turf/open/floor/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
if(locate(/obj/structure/table) in src)
return FALSE
if(locate(/obj/structure/falsewall) in contents)
to_chat(user, "<span class='warning'>There is a false wall in the way, preventing you from proselytizing [src] into a clockwork wall.</span>")
return
if(is_blocked_turf(src, TRUE))
to_chat(user, "<span class='warning'>Something is in the way, preventing you from proselytizing [src] into a clockwork wall.</span>")
return TRUE
var/operation_time = 100
if(proselytizer.speed_multiplier > 0)
operation_time /= proselytizer.speed_multiplier
return list("operation_time" = operation_time, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_MINUS_FLOOR, "spawn_dir" = SOUTH)
//False wall conversion
/obj/structure/falsewall/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
var/cost = POWER_WALL_MINUS_FLOOR
if(ispath(mineral, /obj/item/stack/sheet/metal))
cost -= (POWER_METAL * (2 + mineral_amount)) //four sheets of metal, plus an assumption that the girder is also two
else
cost -= (POWER_METAL * 2) //anything that doesn't use metal just has the girder
return list("operation_time" = 50, "new_obj_type" = /obj/structure/falsewall/brass, "power_cost" = cost, "spawn_dir" = SOUTH)
/obj/structure/falsewall/iron/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) //two sheets of metal, two rods; special assumption
return list("operation_time" = 50, "new_obj_type" = /obj/structure/falsewall/brass, "power_cost" = POWER_WALL_MINUS_FLOOR - (POWER_METAL * 2) - (POWER_ROD * 2), "spawn_dir" = SOUTH)
/obj/structure/falsewall/reinforced/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
/obj/structure/falsewall/brass/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
//Metal conversion
/obj/item/stack/tile/plasteel/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
if(source)
return FALSE
var/amount_temp = get_amount()
if(proselytizer.metal_to_power)
var/no_delete = FALSE
if(amount_temp < 2)
to_chat(user, "<span class='warning'>You need at least <b>2</b> floor tiles to convert into power.</span>")
return TRUE
if(IsOdd(amount_temp))
amount_temp--
no_delete = TRUE
use(amount_temp)
amount_temp *= 12.5 //each tile is 12.5 power so this is 2 tiles to 25 power
return list("operation_time" = 0, "new_obj_type" = /obj/effect/temp_visual/ratvar/beam/itemconsume, "power_cost" = -amount_temp, "spawn_dir" = SOUTH, "no_target_deletion" = no_delete)
if(amount_temp >= 20)
var/sheets_to_make = round(amount_temp * 0.05) //and 20 to 1 brass
var/used = sheets_to_make * 20
user.visible_message("<span class='warning'>[user]'s [proselytizer.name] rips into [src], converting it to brass!</span>", \
"<span class='brass'>You convert [get_amount() - used > 0 ? "part of ":""][src] into brass...</span>")
playsound(src, 'sound/machines/click.ogg', 50, 1)
playsound(src, 'sound/items/Deconstruct.ogg', 50, 1)
new /obj/item/stack/tile/brass(get_turf(src), sheets_to_make)
use(used)
else
to_chat(user, "<span class='warning'>You need at least <b>20</b> floor tiles to convert into brass.</span>")
return TRUE
/obj/item/stack/rods/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
if(source)
return FALSE
if(proselytizer.metal_to_power)
return list("operation_time" = 0, "new_obj_type" = /obj/effect/temp_visual/ratvar/beam/itemconsume, "power_cost" = -(amount*POWER_ROD), "spawn_dir" = SOUTH)
if(get_amount() >= 10)
var/sheets_to_make = round(get_amount() * 0.1)
var/used = sheets_to_make * 10
user.visible_message("<span class='warning'>[user]'s [proselytizer.name] rips into [src], converting it to brass!</span>", \
"<span class='brass'>You convert [get_amount() - used > 0 ? "part of ":""][src] into brass...</span>")
playsound(src, 'sound/machines/click.ogg', 50, 1)
playsound(src, 'sound/items/Deconstruct.ogg', 50, 1)
new /obj/item/stack/tile/brass(get_turf(src), sheets_to_make)
use(used)
else
to_chat(user, "<span class='warning'>You need at least <b>10</b> rods to convert into brass.</span>")
return TRUE
/obj/item/stack/sheet/metal/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
if(source)
return FALSE
if(proselytizer.metal_to_power)
return list("operation_time" = 0, "new_obj_type" = /obj/effect/temp_visual/ratvar/beam/itemconsume, "power_cost" = -(amount*POWER_METAL), "spawn_dir" = SOUTH)
if(get_amount() >= 5)
var/sheets_to_make = round(get_amount() * 0.2)
var/used = sheets_to_make * 5
user.visible_message("<span class='warning'>[user]'s [proselytizer.name] rips into [src], converting it to brass!</span>", \
"<span class='brass'>You convert [get_amount() - used > 0 ? "part of ":""][src] into brass...</span>")
playsound(src, 'sound/machines/click.ogg', 50, 1)
playsound(src, 'sound/items/Deconstruct.ogg', 50, 1)
new /obj/item/stack/tile/brass(get_turf(src), sheets_to_make)
use(used)
else
to_chat(user, "<span class='warning'>You need at least <b>5</b> sheets of metal to convert into brass.</span>")
return TRUE
/obj/item/stack/sheet/plasteel/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
if(source)
return FALSE
if(proselytizer.metal_to_power)
return list("operation_time" = 0, "new_obj_type" = /obj/effect/temp_visual/ratvar/beam/itemconsume, "power_cost" = -(amount*POWER_PLASTEEL), "spawn_dir" = SOUTH)
if(get_amount() >= 2)
var/sheets_to_make = round(get_amount() * 0.5)
var/used = sheets_to_make * 2
user.visible_message("<span class='warning'>[user]'s [proselytizer.name] rips into [src], converting it to brass!</span>", \
"<span class='brass'>You convert [get_amount() - used > 0 ? "part of ":""][src] into brass...</span>")
playsound(src, 'sound/machines/click.ogg', 50, 1)
playsound(src, 'sound/items/Deconstruct.ogg', 50, 1)
new /obj/item/stack/tile/brass(get_turf(src), sheets_to_make)
use(used)
else
to_chat(user, "<span class='warning'>You need at least <b>2</b> sheets of plasteel to convert into brass.</span>")
return TRUE
//Brass directly to power
/obj/item/stack/tile/brass/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
if(source)
return FALSE
return list("operation_time" = 0, "new_obj_type" = /obj/effect/temp_visual/ratvar/beam/itemconsume, "power_cost" = -(amount*POWER_FLOOR), "spawn_dir" = SOUTH)
//Airlock conversion
/obj/machinery/door/airlock/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
var/doortype = /obj/machinery/door/airlock/clockwork
if(glass)
doortype = /obj/machinery/door/airlock/clockwork/brass
return list("operation_time" = 60, "new_obj_type" = doortype, "power_cost" = POWER_WALL_TOTAL, "spawn_dir" = dir)
/obj/machinery/door/airlock/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
//Table conversion
/obj/structure/table/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
var/prosel_cost = POWER_STANDARD
if(framestack == /obj/item/stack/rods)
prosel_cost -= POWER_ROD*framestackamount
else if(framestack == /obj/item/stack/tile/brass)
prosel_cost -= POWER_FLOOR*framestackamount
if(buildstack == /obj/item/stack/sheet/metal)
prosel_cost -= POWER_METAL*buildstackamount
else if(buildstack == /obj/item/stack/sheet/plasteel)
prosel_cost -= POWER_PLASTEEL*buildstackamount
return list("operation_time" = 20, "new_obj_type" = /obj/structure/table/reinforced/brass, "power_cost" = prosel_cost, "spawn_dir" = SOUTH)
/obj/structure/table/reinforced/brass/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
/obj/structure/table_frame/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
var/prosel_cost = POWER_FLOOR
if(framestack == /obj/item/stack/rods)
prosel_cost -= POWER_ROD*framestackamount
else if(framestack == /obj/item/stack/tile/brass)
prosel_cost -= POWER_FLOOR*framestackamount
return list("operation_time" = 10, "new_obj_type" = /obj/structure/table_frame/brass, "power_cost" = prosel_cost, "spawn_dir" = SOUTH)
/obj/structure/table_frame/brass/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
//Window conversion
/obj/structure/window/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
var/windowtype = /obj/structure/window/reinforced/clockwork
var/new_dir = TRUE
var/prosel_time = 15
var/prosel_cost = POWER_FLOOR
if(fulltile)
windowtype = /obj/structure/window/reinforced/clockwork/fulltile
new_dir = FALSE
prosel_time = 30
prosel_cost = POWER_STANDARD
if(reinf)
prosel_cost -= POWER_ROD
if(reinf)
prosel_cost -= POWER_ROD
for(var/obj/structure/grille/G in get_turf(src))
INVOKE_ASYNC(proselytizer, /obj/item/clockwork/clockwork_proselytizer.proc/proselytize, G, user)
return list("operation_time" = prosel_time, "new_obj_type" = windowtype, "power_cost" = prosel_cost, "spawn_dir" = dir, "dir_in_new" = new_dir)
/obj/structure/window/reinforced/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
//Windoor conversion
/obj/machinery/door/window/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return list("operation_time" = 30, "new_obj_type" = /obj/machinery/door/window/clockwork, "power_cost" = POWER_STANDARD, "spawn_dir" = dir, "dir_in_new" = TRUE)
/obj/machinery/door/window/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
//Grille conversion
/obj/structure/grille/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
var/grilletype = /obj/structure/grille/ratvar
var/prosel_time = 15
if(broken)
grilletype = /obj/structure/grille/ratvar/broken
prosel_time = 5
return list("operation_time" = prosel_time, "new_obj_type" = grilletype, "power_cost" = 0, "spawn_dir" = dir)
/obj/structure/grille/ratvar/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
//Lattice conversion
/obj/structure/lattice/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return list("operation_time" = 0, "new_obj_type" = /obj/structure/lattice/clockwork, "power_cost" = 0, "spawn_dir" = SOUTH, "no_target_deletion" = TRUE)
/obj/structure/lattice/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
ratvar_act() //just in case we're the wrong type for some reason??
return FALSE
/obj/structure/lattice/catwalk/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return list("operation_time" = 0, "new_obj_type" = /obj/structure/lattice/catwalk/clockwork, "power_cost" = 0, "spawn_dir" = SOUTH, "no_target_deletion" = TRUE)
/obj/structure/lattice/catwalk/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
//Girder conversion
/obj/structure/girder/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
var/prosel_cost = POWER_GEAR - (POWER_METAL * 2)
if(state == GIRDER_REINF_STRUTS || state == GIRDER_REINF)
prosel_cost -= POWER_PLASTEEL
return list("operation_time" = 20, "new_obj_type" = /obj/structure/destructible/clockwork/wall_gear, "power_cost" = prosel_cost, "spawn_dir" = SOUTH)
//Hitting a clockwork structure will try to repair it.
/obj/structure/destructible/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
. = TRUE
var/list/repair_values = list()
if(!proselytizer.proselytizer_repair_checks(repair_values, src, user))
return
user.visible_message("<span class='notice'>[user]'s [proselytizer.name] starts covering [src] in glowing orange energy...</span>", \
"<span class='alloy'>You start repairing [src]...</span>")
proselytizer.repairing = src
while(proselytizer && user && src)
if(!do_after(user, repair_values["healing_for_cycle"] * proselytizer.speed_multiplier, target = src, \
extra_checks = CALLBACK(proselytizer, /obj/item/clockwork/clockwork_proselytizer.proc/proselytizer_repair_checks, repair_values, src, user, TRUE)))
break
obj_integrity = Clamp(obj_integrity + repair_values["healing_for_cycle"], 0, max_integrity)
proselytizer.modify_stored_power(-repair_values["power_required"])
playsound(src, 'sound/machines/click.ogg', 50, 1)
if(proselytizer)
proselytizer.repairing = null
if(user)
user.visible_message("<span class='notice'>[user]'s [proselytizer.name] stops covering [src] with glowing orange energy.</span>", \
"<span class='alloy'>You finish repairing [src]. It is now at <b>[obj_integrity]/[max_integrity]</b> integrity.</span>")
//Hitting a sigil of transmission will try to charge from it.
/obj/effect/clockwork/sigil/transmission/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
. = TRUE
var/list/charge_values = list()
if(!proselytizer.sigil_charge_checks(charge_values, src, user))
return
user.visible_message("<span class='notice'>[user]'s [proselytizer.name] starts draining glowing orange energy from [src]...</span>", \
"<span class='alloy'>You start recharging your [proselytizer.name]...</span>")
proselytizer.recharging = src
while(proselytizer && user && src)
if(!do_after(user, 10, target = src, extra_checks = CALLBACK(proselytizer, /obj/item/clockwork/clockwork_proselytizer.proc/sigil_charge_checks, charge_values, src, user, TRUE)))
break
modify_charge(charge_values["power_gain"])
proselytizer.modify_stored_power(charge_values["power_gain"])
playsound(src, 'sound/effects/light_flicker.ogg', charge_values["power_gain"] * 0.1, 1)
if(proselytizer)
proselytizer.recharging = null
if(user)
user.visible_message("<span class='notice'>[user]'s [proselytizer.name] stops draining glowing orange energy from [src].</span>", \
"<span class='alloy'>You finish recharging your [proselytizer.name]. It now contains <b>[proselytizer.get_power()]W/[proselytizer.get_max_power()]W</b> power.</span>")
//Proselytizer mob heal proc, to avoid as much copypaste as possible.
/mob/living/proc/proselytizer_heal(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
var/list/repair_values = list()
if(!proselytizer.proselytizer_repair_checks(repair_values, src, user))
return
user.visible_message("<span class='notice'>[user]'s [proselytizer.name] starts coverin[src == user ? "g [user.p_them()]" : "g [src]"] in glowing orange energy...</span>", \
"<span class='alloy'>You start repairin[src == user ? "g yourself" : "g [src]"]...</span>")
proselytizer.repairing = src
while(proselytizer && user && src)
if(!do_after(user, repair_values["healing_for_cycle"] * proselytizer.speed_multiplier, target = src, \
extra_checks = CALLBACK(proselytizer, /obj/item/clockwork/clockwork_proselytizer.proc/proselytizer_repair_checks, repair_values, src, user, TRUE)))
break
proselytizer_heal_tick(repair_values["healing_for_cycle"])
proselytizer.modify_stored_power(-repair_values["power_required"])
playsound(src, 'sound/machines/click.ogg', 50, 1)
if(proselytizer)
proselytizer.repairing = null
return TRUE
/mob/living/proc/proselytizer_heal_tick(amount)
var/static/list/damage_heal_order = list(BRUTE, BURN, TOX, OXY)
heal_ordered_damage(amount, damage_heal_order)
/mob/living/simple_animal/proselytizer_heal_tick(amount)
adjustHealth(-amount)
//Hitting a ratvar'd silicon will also try to repair it.
/mob/living/silicon/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
. = TRUE
if(health == maxHealth) //if we're at maximum health, prosel the turf under us
return FALSE
else if(proselytizer_heal(user, proselytizer) && user)
user.visible_message("<span class='notice'>[user]'s [proselytizer.name] stops coverin[src == user ? "g [user.p_them()]" : "g [src]"] with glowing orange energy.</span>", \
"<span class='alloy'>You finish repairin[src == user ? "g yourself. You are":"g [src]. [p_they(TRUE)] [p_are()]"] now at <b>[abs(HEALTH_THRESHOLD_DEAD - health)]/[abs(HEALTH_THRESHOLD_DEAD - maxHealth)]</b> health.</span>")
//Same with clockwork mobs.
/mob/living/simple_animal/hostile/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
. = TRUE
if(health == maxHealth) //if we're at maximum health, prosel the turf under us
return FALSE
else if(proselytizer_heal(user, proselytizer) && user)
user.visible_message("<span class='notice'>[user]'s [proselytizer.name] stops coverin[src == user ? "g [user.p_them()]" : "g [src]"] with glowing orange energy.</span>", \
"<span class='alloy'>You finish repairin[src == user ? "g yourself. You are":"g [src]. [p_they(TRUE)] [p_are()]"] now at <b>[health]/[maxHealth]</b> health.</span>")
//Cogscarabs get special interaction because they're drones and have innate self-heals/revives.
/mob/living/simple_animal/drone/cogscarab/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
. = TRUE
if(stat == DEAD)
try_reactivate(user) //if we're at maximum health, prosel the turf under us
return
if(health == maxHealth)
return FALSE
else if(!(flags_1 & GODMODE))
user.visible_message("<span class='notice'>[user]'s [proselytizer.name] starts coverin[src == user ? "g [user.p_them()]" : "g [src]"] in glowing orange energy...</span>", \
"<span class='alloy'>You start repairin[src == user ? "g yourself" : "g [src]"]...</span>")
proselytizer.repairing = src
if(do_after(user, (maxHealth - health)*2, target=src))
adjustHealth(-maxHealth)
user.visible_message("<span class='notice'>[user]'s [proselytizer.name] stops coverin[src == user ? "g [user.p_them()]" : "g [src]"] with glowing orange energy.</span>", \
"<span class='alloy'>You finish repairin[src == user ? "g yourself" : "g [src]"].</span>")
if(proselytizer)
proselytizer.repairing = null
//Convert shards and gear bits directly to power
/obj/item/clockwork/alloy_shards/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return list("operation_time" = 0, "new_obj_type" = /obj/effect/temp_visual/ratvar/beam/itemconsume, "power_cost" = -POWER_STANDARD, "spawn_dir" = SOUTH)
/obj/item/clockwork/alloy_shards/medium/gear_bit/large/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return list("operation_time" = 0, "new_obj_type" = /obj/effect/temp_visual/ratvar/beam/itemconsume, "power_cost" = -(CLOCKCULT_POWER_UNIT*0.08), "spawn_dir" = SOUTH)
/obj/item/clockwork/alloy_shards/large/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return list("operation_time" = 0, "new_obj_type" = /obj/effect/temp_visual/ratvar/beam/itemconsume, "power_cost" = -(CLOCKCULT_POWER_UNIT*0.06), "spawn_dir" = SOUTH)
/obj/item/clockwork/alloy_shards/medium/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return list("operation_time" = 0, "new_obj_type" = /obj/effect/temp_visual/ratvar/beam/itemconsume, "power_cost" = -(CLOCKCULT_POWER_UNIT*0.04), "spawn_dir" = SOUTH)
/obj/item/clockwork/alloy_shards/small/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return list("operation_time" = 0, "new_obj_type" = /obj/effect/temp_visual/ratvar/beam/itemconsume, "power_cost" = -(CLOCKCULT_POWER_UNIT*0.02), "spawn_dir" = SOUTH)
@@ -1,111 +0,0 @@
/*
The Ratvarian Language
In the lore of the Servants of Ratvar, the Ratvarian tongue is a timeless language and full of power. It sounds like gibberish, much like Nar-Sie's language, but is in fact derived from
aforementioned language, and may induce miracles when spoken in the correct way with an amplifying tool (similar to runes used by the Nar-Sian cult).
While the canon states that the language of Ratvar and his servants is incomprehensible to the unenlightened as it is a derivative of the most ancient known language, in reality it is
actually very simple. To translate a plain English sentence to Ratvar's tongue, simply move all of the letters thirteen places ahead, starting from "a" if the end of the alphabet is reached.
This cipher is known as "rot13" for "rotate 13 places" and there are many sites online that allow instant translation between English and rot13 - one of the benefits is that moving the translated
sentence thirteen places ahead changes it right back to plain English.
There are, however, a few parts of the Ratvarian tongue that aren't typical and are implemented for fluff reasons. Some words may have graves, or hyphens (prefix and postfix), making the plain
English translation apparent but disjoined (for instance, "Orubyq zl-cbjre!" translates directly to "Behold my-power!") although this can be ignored without impacting overall quality. When
translating from Ratvar's tongue to plain English, simply remove the disjointments and use the finished sentence. This would make "Orubyq zl-cbjre!" into "Behold my power!" after removing the
abnormal spacing, hyphens, and grave accents.
List of nuances:
- Any time the WORD "of" occurs, it is linked to the previous word by a hyphen. (i.e. "V nz-bs Ratvar." directly translates to "I am-of Ratvar.")
- Any time "th", followed by any two letters occurs, you add a grave (`) between those two letters, i.e; "Thi`s"
- In the same vein, any time "ti", followed by one letter occurs, you add a grave (`) between "i" and the letter, i.e; "Ti`me"
- Whereever "te" or "et" appear and there is another letter next to the e(i.e; "m"etal, greate"r"), add a hyphen between "e" and the letter, i.e; "M-etal", "Greate-r"
- Where "gua" appears, add a hyphen between "gu" and "a", i.e "Gu-ard"
- Where the WORD "and" appears it is linked to all surrounding words by hyphens, i.e; "Sword-and-shield"
- Where the WORD "to" appears, it is linked to the following word by a hyphen, i.e; "to-use"
- Where the WORD "my" appears, it is linked to the following word by a hyphen, i.e; "my-light"
- Although "Ratvar" translates to "Engine" in English, the word "Ratvar" is used regardless of language as it is a proper noun.
- The same rule applies to Ratvar's four generals: Nezbere (Armorer), Sevtug (Fright), Nzcrentr (Amperage), and Inath-neq (Vangu-Ard), although these words can be used in proper context if one is
not referring to the four generals and simply using the words themselves.
*/
//Regexes used to alter english to ratvarian style
#define RATVAR_OF_MATCH regex("(\\w)\\s(\[oO]\[fF])","g")
#define RATVAR_OF_REPLACEMENT "$1-$2"
#define RATVAR_GUA_MATCH regex("(\[gG]\[uU])(\[aA])","g")
#define RATVAR_GUA_REPLACEMENT "$1-$2"
#define RATVAR_TH_MATCH regex("(\[tT]\[hH]\\w)(\\w)","g")
#define RATVAR_TH_REPLACEMENT "$1`$2"
#define RATVAR_TI_MATCH regex("(\[tT]\[iI])(\\w)","g")
#define RATVAR_TI_REPLACEMENT "$1`$2"
#define RATVAR_ET_MATCH regex("(\\w)(\[eE]\[tT])","g")
#define RATVAR_ET_REPLACEMENT "$1-$2"
#define RATVAR_TE_MATCH regex("(\[tT]\[eE])(\\w)","g")
#define RATVAR_TE_REPLACEMENT "$1-$2"
#define RATVAR_PRE_AND_MATCH regex("(\\w)\\s(\[aA]\[nN]\[dD])(\\W)","g")
#define RATVAR_PRE_AND_REPLACEMENT "$1-$2$3"
#define RATVAR_POST_AND_MATCH regex("(\\W)(\[aA]\[nN]\[dD])\\s(\\w)","g")
#define RATVAR_POST_AND_REPLACEMENT "$1$2-$3"
#define RATVAR_TO_MATCH regex("(\\s)(\[tT]\[oO])\\s(\\w)","g")
#define RATVAR_TO_REPLACEMENT "$1$2-$3"
#define RATVAR_MY_MATCH regex("(\\s)(\[mM]\[yY])\\s(\\w)","g")
#define RATVAR_MY_REPLACEMENT "$1$2-$3"
//Regexes used to remove ratvarian styling from english
#define REVERSE_RATVAR_HYPHEN_PRE_AND_MATCH regex("(\\w)-(\[aA]\[nN]\[dD])","g") //specifically structured to support -emphasis-, including with -and-
#define REVERSE_RATVAR_HYPHEN_PRE_AND_REPLACEMENT "$1 $2"
#define REVERSE_RATVAR_HYPHEN_POST_AND_MATCH regex("(\[aA]\[nN]\[dD])-(\\w)","g")
#define REVERSE_RATVAR_HYPHEN_POST_AND_REPLACEMENT "$1 $2"
#define REVERSE_RATVAR_HYPHEN_TO_MY_MATCH regex("(\[tTmM]\[oOyY])-","g")
#define REVERSE_RATVAR_HYPHEN_TO_MY_REPLACEMENT "$1 "
#define REVERSE_RATVAR_HYPHEN_TE_MATCH regex("(\[tT]\[eE])-","g")
#define REVERSE_RATVAR_HYPHEN_TE_REPLACEMENT "$1"
#define REVERSE_RATVAR_HYPHEN_ET_MATCH regex("-(\[eE]\[tT])","g")
#define REVERSE_RATVAR_HYPHEN_ET_REPLACEMENT "$1"
#define REVERSE_RATVAR_HYPHEN_GUA_MATCH regex("(\[gG]\[uU])-(\[aA])","g")
#define REVERSE_RATVAR_HYPHEN_GUA_REPLACEMENT "$1$2"
#define REVERSE_RATVAR_HYPHEN_OF_MATCH regex("-(\[oO]\[fF])","g")
#define REVERSE_RATVAR_HYPHEN_OF_REPLACEMENT " $1"
/proc/text2ratvar(text) //Takes english and applies ratvarian styling rules (and rot13) to it.
var/ratvarian = add_ratvarian_regex(text) //run the regexes twice, so that you catch people translating it beforehand
ratvarian = rot13(ratvarian)
return add_ratvarian_regex(ratvarian)
/proc/add_ratvarian_regex(text)
var/ratvarian = replacetext(text, RATVAR_OF_MATCH, RATVAR_OF_REPLACEMENT)
ratvarian = replacetext(ratvarian, RATVAR_GUA_MATCH, RATVAR_GUA_REPLACEMENT)
ratvarian = replacetext(ratvarian, RATVAR_TH_MATCH, RATVAR_TH_REPLACEMENT)
ratvarian = replacetext(ratvarian, RATVAR_TI_MATCH, RATVAR_TI_REPLACEMENT)
ratvarian = replacetext(ratvarian, RATVAR_ET_MATCH, RATVAR_ET_REPLACEMENT)
ratvarian = replacetext(ratvarian, RATVAR_TE_MATCH, RATVAR_TE_REPLACEMENT)
ratvarian = replacetext(ratvarian, RATVAR_PRE_AND_MATCH, RATVAR_PRE_AND_REPLACEMENT)
ratvarian = replacetext(ratvarian, RATVAR_POST_AND_MATCH, RATVAR_POST_AND_REPLACEMENT)
ratvarian = replacetext(ratvarian, RATVAR_TO_MATCH, RATVAR_TO_REPLACEMENT)
return replacetext(ratvarian, RATVAR_MY_MATCH, RATVAR_MY_REPLACEMENT)
/proc/ratvar2text(ratvarian) //Reverts ravarian styling and rot13 in text.
var/text = remove_ratvarian_regex(ratvarian) //run the regexes twice, so that you catch people translating it beforehand
text = replacetext(rot13(text), "`", "")
return remove_ratvarian_regex(text)
/proc/remove_ratvarian_regex(ratvarian)
var/text = replacetext(ratvarian, REVERSE_RATVAR_HYPHEN_GUA_MATCH, REVERSE_RATVAR_HYPHEN_GUA_REPLACEMENT)
text = replacetext(text, REVERSE_RATVAR_HYPHEN_PRE_AND_MATCH, REVERSE_RATVAR_HYPHEN_PRE_AND_REPLACEMENT)
text = replacetext(text, REVERSE_RATVAR_HYPHEN_POST_AND_MATCH, REVERSE_RATVAR_HYPHEN_POST_AND_REPLACEMENT)
text = replacetext(text, REVERSE_RATVAR_HYPHEN_TO_MY_MATCH, REVERSE_RATVAR_HYPHEN_TO_MY_REPLACEMENT)
text = replacetext(text, REVERSE_RATVAR_HYPHEN_TE_MATCH, REVERSE_RATVAR_HYPHEN_TE_REPLACEMENT)
text = replacetext(text, REVERSE_RATVAR_HYPHEN_ET_MATCH, REVERSE_RATVAR_HYPHEN_ET_REPLACEMENT)
return replacetext(text, REVERSE_RATVAR_HYPHEN_OF_MATCH, REVERSE_RATVAR_HYPHEN_OF_REPLACEMENT)
//Causes the mob or AM in question to speak a message; it assumes that the message is already translated to ratvar speech using text2ratvar()
/proc/clockwork_say(atom/movable/AM, message, whisper=FALSE)
var/list/spans = list(SPAN_ROBOT)
if(isliving(AM))
var/mob/living/L = AM
if(!whisper)
L.say(message, "clock", spans, language=/datum/language/common, ignore_spam = TRUE)
else
L.whisper(message, "clock", spans, language=/datum/language/common)
else
AM.say(message, language=/datum/language/common)
@@ -1,53 +0,0 @@
//returns a list of scriptures and if they're unlocked or not
/proc/scripture_unlock_check()
. = list(SCRIPTURE_DRIVER = TRUE, SCRIPTURE_SCRIPT = FALSE, SCRIPTURE_APPLICATION = FALSE)
//Drivers: always unlocked
.[SCRIPTURE_SCRIPT] = GLOB.script_scripture_unlocked
//Script: Convert a new servant using a sigil of submission.
.[SCRIPTURE_APPLICATION] = GLOB.application_scripture_unlocked
//Application: APPLICATION_SERVANT_REQ or more non-brain servants, APPLICATION_CACHE_REQ or more clockwork caches, and at least APPLICATION_CV_REQ CV
//reports to servants when scripture is locked or unlocked
/proc/scripture_unlock_alert(list/previous_states)
. = scripture_unlock_check()
if(!GLOB.servants_active)
return
for(var/i in .)
if(.[i] != previous_states[i])
update_slab_info()
for(var/mob/M in GLOB.player_list)
if(is_servant_of_ratvar(M) || isobserver(M))
M.playsound_local(M, 'sound/magic/clockwork/scripture_tier_up.ogg', 50, FALSE, pressure_affected = FALSE)
/proc/update_slab_info(obj/item/clockwork/slab/set_slab)
generate_all_scripture()
var/needs_update = FALSE //if everything needs an update, for whatever reason
for(var/s in GLOB.all_scripture)
var/datum/clockwork_scripture/S = GLOB.all_scripture[s]
if(S.creation_update())
needs_update = TRUE
if(!set_slab || needs_update)
for(var/obj/item/clockwork/slab/S in GLOB.all_clockwork_objects)
SStgui.update_uis(S)
if(needs_update)
S.update_quickbind()
else
SStgui.update_uis(set_slab)
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
//changes construction value
/proc/change_construction_value(amount)
if(!SSticker.current_state != GAME_STATE_PLAYING) //This is primarily so that structures added pre-roundstart don't contribute to construction value
return
GLOB.clockwork_construction_value = max(0, GLOB.clockwork_construction_value + amount)
/proc/can_recite_scripture(mob/living/L, can_potentially)
return (is_servant_of_ratvar(L) && (can_potentially || (L.stat == CONSCIOUS && L.can_speak_vocal())) && (GLOB.ratvar_awakens || (ishuman(L) || issilicon(L))))
@@ -1,270 +0,0 @@
//The base for slab-bound/based ranged abilities
/obj/effect/proc_holder/slab
var/obj/item/clockwork/slab/slab
var/successful = FALSE
var/finished = FALSE
var/in_progress = FALSE
/obj/effect/proc_holder/slab/Destroy()
slab = null
return ..()
/obj/effect/proc_holder/slab/remove_ranged_ability(msg)
..()
finished = TRUE
QDEL_IN(src, 6)
/obj/effect/proc_holder/slab/InterceptClickOn(mob/living/caller, params, atom/target)
if(..() || in_progress)
return TRUE
if(ranged_ability_user.incapacitated() || !slab || !(slab in ranged_ability_user.held_items) || target == slab)
remove_ranged_ability()
return TRUE
//For the Hateful Manacles scripture; applies replicant handcuffs to the target.
/obj/effect/proc_holder/slab/hateful_manacles
/obj/effect/proc_holder/slab/hateful_manacles/InterceptClickOn(mob/living/caller, params, atom/target)
if(..())
return TRUE
var/turf/T = ranged_ability_user.loc
if(!isturf(T))
return TRUE
if(iscarbon(target) && target.Adjacent(ranged_ability_user))
var/mob/living/carbon/L = target
if(is_servant_of_ratvar(L))
to_chat(ranged_ability_user, "<span class='neovgre'>\"They're a servant.\"</span>")
return TRUE
else if(L.stat)
to_chat(ranged_ability_user, "<span class='neovgre'>\"There is use in shackling the dead, but for examples.\"</span>")
return TRUE
else if(L.handcuffed)
to_chat(ranged_ability_user, "<span class='neovgre'>\"They are already helpless, no?\"</span>")
return TRUE
playsound(loc, 'sound/weapons/handcuffs.ogg', 30, TRUE)
ranged_ability_user.visible_message("<span class='danger'>[ranged_ability_user] begins forming manacles around [L]'s wrists!</span>", \
"<span class='neovgre_small'>You begin shaping replicant alloy into manacles around [L]'s wrists...</span>")
to_chat(L, "<span class='userdanger'>[ranged_ability_user] begins forming manacles around your wrists!</span>")
if(do_mob(ranged_ability_user, L, 30))
if(!L.handcuffed)
L.handcuffed = new/obj/item/restraints/handcuffs/clockwork(L)
L.update_handcuffed()
to_chat(ranged_ability_user, "<span class='neovgre_small'>You shackle [L].</span>")
add_logs(ranged_ability_user, L, "handcuffed")
else
to_chat(ranged_ability_user, "<span class='warning'>You fail to shackle [L].</span>")
successful = TRUE
remove_ranged_ability()
return TRUE
/obj/item/restraints/handcuffs/clockwork
name = "replicant manacles"
desc = "Heavy manacles made out of freezing-cold metal. It looks like brass, but feels much more solid."
icon_state = "brass_manacles"
flags_1 = DROPDEL_1
/obj/item/restraints/handcuffs/clockwork/dropped(mob/user)
user.visible_message("<span class='danger'>[user]'s [name] come apart at the seams!</span>", \
"<span class='userdanger'>Your [name] break apart as they're removed!</span>")
. = ..()
//For the Sentinel's Compromise scripture; heals a target servant.
/obj/effect/proc_holder/slab/compromise
ranged_mousepointer = 'icons/effects/compromise_target.dmi'
/obj/effect/proc_holder/slab/compromise/InterceptClickOn(mob/living/caller, params, atom/target)
if(..())
return TRUE
var/turf/T = ranged_ability_user.loc
if(!isturf(T))
return TRUE
if(isliving(target) && (target in view(7, get_turf(ranged_ability_user))))
var/mob/living/L = target
if(!is_servant_of_ratvar(L))
to_chat(ranged_ability_user, "<span class='inathneq'>\"[L] does not yet serve Ratvar.\"</span>")
return TRUE
if(L.stat == DEAD)
to_chat(ranged_ability_user, "<span class='inathneq'>\"[L.p_they(TRUE)] [L.p_are()] dead. [text2ratvar("Oh, child. To have your life cut short...")]\"</span>")
return TRUE
var/brutedamage = L.getBruteLoss()
var/burndamage = L.getFireLoss()
var/oxydamage = L.getOxyLoss()
var/totaldamage = brutedamage + burndamage + oxydamage
if(!totaldamage && (!L.reagents || !L.reagents.has_reagent("holywater")))
to_chat(ranged_ability_user, "<span class='inathneq'>\"[L] is unhurt and untainted.\"</span>")
return TRUE
successful = TRUE
to_chat(ranged_ability_user, "<span class='brass'>You bathe [L == ranged_ability_user ? "yourself":"[L]"] in Inath-neq's power!</span>")
var/targetturf = get_turf(L)
var/has_holy_water = (L.reagents && L.reagents.has_reagent("holywater"))
var/healseverity = max(round(totaldamage*0.05, 1), 1) //shows the general severity of the damage you just healed, 1 glow per 20
for(var/i in 1 to healseverity)
new /obj/effect/temp_visual/heal(targetturf, "#1E8CE1")
if(totaldamage)
L.adjustBruteLoss(-brutedamage)
L.adjustFireLoss(-burndamage)
L.adjustOxyLoss(-oxydamage)
L.adjustToxLoss(totaldamage * 0.5, TRUE, TRUE)
clockwork_say(ranged_ability_user, text2ratvar("[has_holy_water ? "Heal tainted" : "Mend wounded"] flesh!"))
add_logs(ranged_ability_user, L, "healed with Sentinel's Compromise")
L.visible_message("<span class='warning'>A blue light washes over [L], [has_holy_water ? "causing [L.p_them()] to briefly glow as it mends" : " mending"] [L.p_their()] bruises and burns!</span>", \
"<span class='heavy_brass'>You feel Inath-neq's power healing your wounds[has_holy_water ? " and purging the darkness within you" : ""], but a deep nausea overcomes you!</span>")
else
clockwork_say(ranged_ability_user, text2ratvar("Purge foul darkness!"))
add_logs(ranged_ability_user, L, "purged of holy water with Sentinel's Compromise")
L.visible_message("<span class='warning'>A blue light washes over [L], causing [L.p_them()] to briefly glow!</span>", \
"<span class='heavy_brass'>You feel Inath-neq's power purging the darkness within you!</span>")
playsound(targetturf, 'sound/magic/staff_healing.ogg', 50, 1)
if(has_holy_water)
L.reagents.remove_reagent("holywater", 1000)
remove_ranged_ability()
return TRUE
//For the Kindle scripture; stuns and mutes a target non-servant.
/obj/effect/proc_holder/slab/kindle
ranged_mousepointer = 'icons/effects/volt_target.dmi'
/obj/effect/proc_holder/slab/kindle/InterceptClickOn(mob/living/caller, params, atom/target)
if(..())
return TRUE
var/turf/T = ranged_ability_user.loc
if(!isturf(T))
return TRUE
if(target in view(7, get_turf(ranged_ability_user)))
successful = TRUE
var/turf/U = get_turf(target)
to_chat(ranged_ability_user, "<span class='brass'>You release the light of Ratvar!</span>")
clockwork_say(ranged_ability_user, text2ratvar("Purge all untruths and honor Engine!"))
add_logs(ranged_ability_user, U, "fired at with Kindle")
playsound(ranged_ability_user, 'sound/magic/blink.ogg', 50, TRUE, frequency = 0.5)
var/obj/item/projectile/kindle/A = new(T)
A.preparePixelProjectile(target, caller, params)
A.fire()
remove_ranged_ability()
return TRUE
/obj/item/projectile/kindle
name = "kindled flame"
icon_state = "pulse0"
nodamage = TRUE
damage = 0 //We're just here for the stunning!
damage_type = BURN
flag = "bomb"
range = 3
log_override = TRUE
/obj/item/projectile/kindle/Destroy()
visible_message("<span class='warning'>[src] flickers out!</span>")
. = ..()
/obj/item/projectile/kindle/on_hit(atom/target, blocked = FALSE)
if(isliving(target))
var/mob/living/L = target
if(is_servant_of_ratvar(L) || L.stat || L.has_status_effect(STATUS_EFFECT_KINDLE))
return
var/obj/O = L.null_rod_check()
playsound(L, 'sound/magic/fireball.ogg', 50, TRUE, frequency = 1.25)
if(O)
L.visible_message("<span class='warning'>[L]'s eyes flare with dim light as they stumble!</span>", \
"<span class='userdanger'>Your [O] glows white-hot against you as it absorbs some sort of power!</span>")
L.adjustFireLoss(5)
L.Stun(40)
playsound(L, 'sound/weapons/sear.ogg', 50, TRUE)
else
L.visible_message("<span class='warning'>[L]'s eyes blaze with brilliant light!</span>", \
"<span class='userdanger'>Your vision suddenly screams with white-hot light!</span>")
L.Knockdown(15)
L.apply_status_effect(STATUS_EFFECT_KINDLE)
L.flash_act(1, 1)
if(iscultist(L))
L.adjustFireLoss(15)
..()
//For the cyborg Linked Vanguard scripture, grants you and a nearby ally Vanguard
/obj/effect/proc_holder/slab/vanguard
ranged_mousepointer = 'icons/effects/vanguard_target.dmi'
/obj/effect/proc_holder/slab/vanguard/InterceptClickOn(mob/living/caller, params, atom/target)
if(..())
return TRUE
var/turf/T = ranged_ability_user.loc
if(!isturf(T))
return TRUE
if(isliving(target) && (target in view(7, get_turf(ranged_ability_user))))
var/mob/living/L = target
if(!is_servant_of_ratvar(L))
to_chat(ranged_ability_user, "<span class='inathneq'>\"[L] does not yet serve Ratvar.\"</span>")
return TRUE
if(L.stat == DEAD)
to_chat(ranged_ability_user, "<span class='inathneq'>\"[L.p_they(TRUE)] [L.p_are()] dead. [text2ratvar("Oh, child. To have your life cut short...")]\"</span>")
return TRUE
if(islist(L.stun_absorption) && L.stun_absorption["vanguard"] && L.stun_absorption["vanguard"]["end_time"] > world.time)
to_chat(ranged_ability_user, "<span class='inathneq'>\"[L.p_they(TRUE)] [L.p_are()] already shielded by a Vanguard.\"</span>")
return TRUE
successful = TRUE
if(L == ranged_ability_user)
for(var/mob/living/LT in spiral_range(7, T))
if(LT.stat == DEAD || !is_servant_of_ratvar(LT) || LT == ranged_ability_user || !(LT in view(7, get_turf(ranged_ability_user))) || \
(islist(LT.stun_absorption) && LT.stun_absorption["vanguard"] && LT.stun_absorption["vanguard"]["end_time"] > world.time))
continue
L = LT
break
L.apply_status_effect(STATUS_EFFECT_VANGUARD)
ranged_ability_user.apply_status_effect(STATUS_EFFECT_VANGUARD)
clockwork_say(ranged_ability_user, text2ratvar("Shield us from darkness!"))
remove_ranged_ability()
return TRUE
//For the cyborg Judicial Marker scripture, places a judicial marker
/obj/effect/proc_holder/slab/judicial
ranged_mousepointer = 'icons/effects/visor_reticule.dmi'
/obj/effect/proc_holder/slab/judicial/InterceptClickOn(mob/living/caller, params, atom/target)
if(..())
return TRUE
var/turf/T = ranged_ability_user.loc
if(!isturf(T))
return TRUE
if(target in view(7, get_turf(ranged_ability_user)))
successful = TRUE
clockwork_say(ranged_ability_user, text2ratvar("Kneel, heathens!"))
ranged_ability_user.visible_message("<span class='warning'>[ranged_ability_user]'s eyes fire a stream of energy at [target], creating a strange mark!</span>", \
"<span class='heavy_brass'>You direct the judicial force to [target].</span>")
var/turf/targetturf = get_turf(target)
new/obj/effect/clockwork/judicial_marker(targetturf, ranged_ability_user)
add_logs(ranged_ability_user, targetturf, "created a judicial marker")
remove_ranged_ability()
return TRUE
@@ -1,24 +0,0 @@
//The base clockwork item. Can have an alternate desc and will show up in the list of clockwork objects.
/obj/item/clockwork
name = "meme blaster"
desc = "What the fuck is this? It looks kinda like a frog."
resistance_flags = FIRE_PROOF | ACID_PROOF
var/clockwork_desc = "A fabled artifact from beyond the stars. Contains concentrated meme essence." //Shown to clockwork cultists instead of the normal description
icon = 'icons/obj/clockwork_objects.dmi'
icon_state = "rare_pepe"
w_class = WEIGHT_CLASS_SMALL
/obj/item/clockwork/Initialize()
. = ..()
ratvar_act()
GLOB.all_clockwork_objects += src
/obj/item/clockwork/Destroy()
GLOB.all_clockwork_objects -= src
return ..()
/obj/item/clockwork/examine(mob/user)
if((is_servant_of_ratvar(user) || isobserver(user)) && clockwork_desc)
desc = clockwork_desc
..()
desc = initial(desc)
@@ -1,239 +0,0 @@
//Components: Used in scripture.
/obj/item/clockwork/component
name = "meme component"
desc = "A piece of a famous meme."
clockwork_desc = null
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
var/component_id //What the component is identified as
var/cultist_message = "You are not worthy of this meme." //Showed to Nar-Sian cultists if they pick up the component in addition to chaplains
var/list/servant_of_ratvar_messages = list("ayy" = FALSE, "lmao" = TRUE) //Fluff, shown to servants of Ratvar on a low chance, if associated value is TRUE, will automatically apply ratvarian
var/message_span = "heavy_brass"
/obj/item/clockwork/component/examine(mob/user)
. = ..()
if(is_servant_of_ratvar(user) || isobserver(user))
to_chat(user, "<span class='[get_component_span(component_id)]'>You can activate this in your hand to break it down for power.</span>")
/obj/item/clockwork/component/attack_self(mob/living/user)
if(is_servant_of_ratvar(user))
user.visible_message("<span class='notice'>[user] crushes [src] in their hand!</span>", \
"<span class='alloy'>You crush [src], capturing its escaping energy for use as power.</span>")
playsound(user, 'sound/effects/pop_expl.ogg', 50, TRUE)
adjust_clockwork_power(POWER_WALL_TOTAL)
qdel(src)
/obj/item/clockwork/component/pickup(mob/living/user)
..()
if(iscultist(user) || (user.mind && user.mind.isholy))
to_chat(user, "<span class='[message_span]'>[cultist_message]</span>")
if(user.mind && user.mind.isholy)
to_chat(user, "<span class='boldannounce'>The power of your faith melts away [src]!</span>")
var/obj/item/ore/slag/wrath = new /obj/item/ore/slag
qdel(src)
user.put_in_active_hand(wrath)
if(is_servant_of_ratvar(user) && prob(20))
var/pickedmessage = pick(servant_of_ratvar_messages)
to_chat(user, "<span class='[message_span]'>[servant_of_ratvar_messages[pickedmessage] ? "[text2ratvar(pickedmessage)]" : pickedmessage]</span>")
/obj/item/clockwork/component/belligerent_eye
name = "belligerent eye"
desc = "A brass construct with a rotating red center. It's as though it's looking for something to hurt."
icon_state = "belligerent_eye"
component_id = BELLIGERENT_EYE
cultist_message = "The eye gives you an intensely hateful glare."
servant_of_ratvar_messages = list("\"...\"" = FALSE, "For a moment, your mind is flooded with extremely violent thoughts." = FALSE, "\"...Die.\"" = TRUE)
message_span = "neovgre"
/obj/item/clockwork/component/belligerent_eye/blind_eye
name = "blind eye"
desc = "A heavy brass eye, its red iris fallen dark."
clockwork_desc = "A smashed ocular warden covered in dents."
icon_state = "blind_eye"
cultist_message = "The eye flickers at you with intense hate before falling dark."
servant_of_ratvar_messages = list("The eye flickers before falling dark." = FALSE, "You feel watched." = FALSE, "\"...\"" = FALSE)
w_class = WEIGHT_CLASS_NORMAL
/obj/item/clockwork/component/belligerent_eye/lens_gem
name = "lens gem"
desc = "A tiny pinkish gem. It catches the light oddly, almost glowing."
clockwork_desc = "The gem from an interdiction lens."
icon_state = "lens_gem"
cultist_message = "The gem turns black and cold for a moment before its normal glow returns."
servant_of_ratvar_messages = list("\"Disgusting failure.\"" = TRUE, "You feel scrutinized." = FALSE, "\"Weaklings.\"" = TRUE, "\"Pathetic defenses.\"" = TRUE)
w_class = WEIGHT_CLASS_TINY
light_range = 1.4
light_power = 0.4
light_color = "#F42B9D"
/obj/item/clockwork/component/vanguard_cogwheel
name = "vanguard cogwheel"
desc = "A sturdy brass cog with a faintly glowing blue gem in its center."
icon_state = "vanguard_cogwheel"
component_id = VANGUARD_COGWHEEL
cultist_message = "\"Pray to your god that we never meet.\""
servant_of_ratvar_messages = list("\"Be safe, child.\"" = FALSE, "You feel unexplainably comforted." = FALSE, "\"Never forget: Pain is temporary. What you do for the Justiciar is eternal.\"" = FALSE)
message_span = "inathneq"
/obj/item/clockwork/component/vanguard_cogwheel/onyx_prism
name = "onyx prism"
desc = "An onyx prism with a small aperture. It's very heavy."
clockwork_desc = "A broken prism from a prolonging prism."
icon_state = "onyx_prism"
cultist_message = "The prism grows painfully hot in your hands."
servant_of_ratvar_messages = list("The prism isn't getting any lighter." = FALSE, "\"So... you haven't failed yet. Have hope, child.\"" = TRUE, \
"\"Better these machines break than you do.\"" = TRUE)
w_class = WEIGHT_CLASS_NORMAL
/obj/item/clockwork/component/geis_capacitor
name = "geis capacitor"
desc = "A curiously cold brass doodad. It seems as though it really doesn't appreciate being held."
icon_state = "geis_capacitor"
component_id = GEIS_CAPACITOR
cultist_message = "\"Try not to lose your mind - I'll need it. Heh heh...\""
servant_of_ratvar_messages = list("\"Disgusting.\"" = FALSE, "\"Well, aren't you an inquisitive fellow?\"" = FALSE, "A foul presence pervades your mind, then vanishes." = FALSE, \
"\"The fact that Ratvar has to depend on simpletons like you is appalling.\"" = FALSE)
message_span = "sevtug"
/obj/item/clockwork/component/geis_capacitor/fallen_armor
name = "fallen armor"
desc = "Lifeless chunks of armor. They're designed in a strange way and won't fit on you."
clockwork_desc = "The armor from a former clockwork marauder. <b>Serviceable as a substitute for a geis capacitor.</b>"
icon_state = "fallen_armor"
cultist_message = "Red flame sputters from the mask's eye before winking out."
servant_of_ratvar_messages = list("A piece of armor hovers away from the others for a moment." = FALSE, "Red flame appears in the cuirass before sputtering out." = FALSE)
w_class = WEIGHT_CLASS_NORMAL
/obj/item/clockwork/component/geis_capacitor/antennae
name = "mania motor antennae"
desc = "A pair of dented and bent antennae. They constantly emit a static hiss."
clockwork_desc = "The antennae from a mania motor."
icon_state = "mania_motor_antennae"
cultist_message = "Your head is filled with a burst of static."
servant_of_ratvar_messages = list("\"Who broke this.\"" = TRUE, "\"Did you break these off YOURSELF?\"" = TRUE, "\"Why did we give this to such simpletons, anyway?\"" = TRUE, \
"\"At least we can use these for something - unlike you.\"" = TRUE)
/obj/item/clockwork/component/replicant_alloy
name = "replicant alloy"
desc = "A seemingly strong but very malleable chunk of metal. It seems as though it wants to be molded into something greater."
icon_state = "replicant_alloy"
component_id = REPLICANT_ALLOY
cultist_message = "The alloy takes on the appearance of a screaming face for a moment."
servant_of_ratvar_messages = list("\"There's always something to be done. Get to it.\"" = FALSE, "\"Idle hands are worse than broken ones. Get to work.\"" = FALSE, \
"A detailed image of Ratvar appears in the alloy for a moment." = FALSE)
message_span = "nezbere"
/obj/item/clockwork/component/replicant_alloy/smashed_anima_fragment
name = "smashed anima fragment"
desc = "Shattered chunks of metal. Damaged beyond repair and completely unusable."
clockwork_desc = "The sad remains of an anima fragment."
icon_state = "smashed_anime_fragment"
cultist_message = "The shards vibrate in your hands for a moment."
servant_of_ratvar_messages = list("\"...still fight...\"" = FALSE, "\"...where am I...?\"" = FALSE, "\"...bring me... back...\"" = FALSE)
w_class = WEIGHT_CLASS_NORMAL
/obj/item/clockwork/component/replicant_alloy/replication_plate
name = "replication plate"
desc = "A flat, heavy disc of metal with a triangular formation on its surface."
clockwork_desc = "The replication plate from a tinkerer's daemon."
icon_state = "replication_plate"
cultist_message = "The plate shudders in your hands, as though trying to get away."
servant_of_ratvar_messages = list("\"Put this in a slab and get back to work.\"" = FALSE, "\"Worse servants than you have held these.\"" = TRUE, \
"\"It would be wise to protect these better, friend.\"" = TRUE)
w_class = WEIGHT_CLASS_NORMAL
/obj/item/clockwork/component/hierophant_ansible
name = "hierophant ansible"
desc = "Some sort of transmitter? It seems as though it's trying to say something."
icon_state = "hierophant_ansible"
component_id = HIEROPHANT_ANSIBLE
cultist_message = "\"Gur obff fnlf vg'f abg ntnvafg gur ehyrf gb-xvyy lbh.\""
servant_of_ratvar_messages = list("\"Exile is such a bore. There's nothing I can hunt in here.\"" = TRUE, "\"What's keeping you? I want to go kill something.\"" = TRUE, \
"\"HEHEHEHEHEHEH!\"" = FALSE, "\"If I killed you fast enough, do you think the boss would notice?\"" = TRUE)
message_span = "nzcrentr"
/obj/item/clockwork/component/hierophant_ansible/obelisk
name = "obelisk prism"
desc = "A prism that occasionally glows brightly. It seems not-quite there."
clockwork_desc = "The prism from a clockwork obelisk."
cultist_message = "The prism flickers wildly in your hands before resuming its normal glow."
servant_of_ratvar_messages = list("You hear the distinctive sound of the Hierophant Network for a moment." = FALSE, "\"Hieroph'ant Br'o'adcas't fail'ure.\"" = TRUE, \
"The obelisk flickers wildly, as if trying to open a gateway." = FALSE, "\"Spa'tial Ga'tewa'y fai'lure.\"" = TRUE)
icon_state = "obelisk_prism"
w_class = WEIGHT_CLASS_NORMAL
//Shards of Alloy, suitable only as a source of power for a replica fabricator.
/obj/item/clockwork/alloy_shards
name = "replicant alloy shards"
desc = "Broken shards of some oddly malleable metal. They occasionally move and seem to glow."
clockwork_desc = "Broken shards of replicant alloy."
icon_state = "alloy_shards"
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
var/randomsinglesprite = FALSE
var/randomspritemax = 2
var/sprite_shift = 9
/obj/item/clockwork/alloy_shards/Initialize()
. = ..()
if(randomsinglesprite)
replace_name_desc()
icon_state = "[icon_state][rand(1, randomspritemax)]"
pixel_x = rand(-sprite_shift, sprite_shift)
pixel_y = rand(-sprite_shift, sprite_shift)
/obj/item/clockwork/alloy_shards/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
to_chat(user, "<span class='brass'>Can be consumed by a replica fabricator as a source of power.</span>")
/obj/item/clockwork/alloy_shards/proc/replace_name_desc()
name = "replicant alloy shard"
desc = "A broken shard of some oddly malleable metal. It occasionally moves and seems to glow."
clockwork_desc = "A broken shard of replicant alloy."
/obj/item/clockwork/alloy_shards/clockgolem_remains
name = "clockwork golem scrap"
desc = "A pile of scrap metal. It seems damaged beyond repair."
clockwork_desc = "The sad remains of a clockwork golem. It's broken beyond repair."
icon_state = "clockgolem_dead"
sprite_shift = 0
/obj/item/clockwork/alloy_shards/large
w_class = WEIGHT_CLASS_TINY
randomsinglesprite = TRUE
icon_state = "shard_large"
sprite_shift = 9
/obj/item/clockwork/alloy_shards/medium
w_class = WEIGHT_CLASS_TINY
randomsinglesprite = TRUE
icon_state = "shard_medium"
sprite_shift = 10
/obj/item/clockwork/alloy_shards/medium/gear_bit
randomspritemax = 4
icon_state = "gear_bit"
sprite_shift = 12
/obj/item/clockwork/alloy_shards/medium/gear_bit/replace_name_desc()
name = "gear bit"
desc = "A broken chunk of a gear. You want it."
clockwork_desc = "A broken chunk of a gear."
/obj/item/clockwork/alloy_shards/medium/gear_bit/large //gives more power
/obj/item/clockwork/alloy_shards/medium/gear_bit/large/replace_name_desc()
..()
name = "complex gear bit"
/obj/item/clockwork/alloy_shards/small
w_class = WEIGHT_CLASS_TINY
randomsinglesprite = TRUE
randomspritemax = 3
icon_state = "shard_small"
sprite_shift = 12
/obj/item/clockwork/alloy_shards/pinion_lock
name = "pinion lock"
desc = "A dented and scratched gear. It's very heavy."
clockwork_desc = "A broken gear lock for pinion airlocks."
icon_state = "pinion_lock"
@@ -1,49 +0,0 @@
//This is the innate action for "binding" and calling weapons to yourself. These weapons can appear and disappear at will.
//You can invoke a cooldown period by calling "weapon_reset(cooldown in deciseconds)." By default, this only applies to dismissing weapons.
/datum/action/innate/call_weapon
name = "Call Weapon"
desc = "This definitely shouldn't exist."
icon_icon = 'icons/mob/actions/actions_clockcult.dmi'
button_icon_state = "ratvarian_spear"
background_icon_state = "bg_clock"
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUN|AB_CHECK_CONSCIOUS
buttontooltipstyle = "clockcult"
var/cooldown = 0
var/obj/item/clockwork/weapon/weapon_type //The type of weapon to create
var/obj/item/clockwork/weapon/weapon
/datum/action/innate/call_weapon/IsAvailable()
if(!is_servant_of_ratvar(owner))
qdel(src)
return
if(cooldown > world.time)
return
return ..()
/datum/action/innate/call_weapon/Activate()
if(!owner.get_empty_held_indexes())
to_chat(usr, "<span class='warning'>You need an empty hand to call forth your [initial(weapon_type.name)]!</span>")
return
if(weapon)
if(weapon.loc == owner)
owner.visible_message("<span class='danger'>[owner]'s [weapon.name] flickers and disappears!</span>")
to_chat(owner, "<span class='brass'>You dismiss [weapon].</span>")
QDEL_NULL(weapon)
weapon_reset(RATVARIAN_SPEAR_COOLDOWN * 0.5)
return
else
weapon.visible_message("<span class='warning'>[weapon] suddenly flickers and disappears!</span>")
owner.visible_message("<span class='danger'>A [weapon.name] suddenly flickers into [owner]'s hands!</span>", "<span class='brass'>You recall [weapon] to you.</span>")
else
weapon = new weapon_type (get_turf(usr), src)
owner.visible_message("<span class='warning'>A [weapon.name] materializes in [owner]'s hands!</span>", "<span class='brass'>You call forth your [weapon.name]!</span>")
weapon.forceMove(get_turf(owner))
owner.put_in_hands(weapon)
owner.update_action_buttons_icon()
return TRUE
/datum/action/innate/call_weapon/proc/weapon_reset(cooldown_time)
cooldown = world.time + cooldown_time
addtimer(CALLBACK(owner, /mob.proc/update_action_buttons_icon), cooldown_time)
owner.update_action_buttons_icon()
QDEL_NULL(weapon)
@@ -1,76 +0,0 @@
//Ratvarian spear: A relatively fragile spear from the Celestial Derelict. Deals extreme damage to silicons and enemy cultists, but doesn't last long when summoned.
/obj/item/clockwork/weapon/ratvarian_spear
name = "ratvarian spear"
desc = "A razor-sharp spear made of brass. It thrums with barely-contained energy."
clockwork_desc = "A powerful spear of Ratvarian making. It's more effective against enemy cultists and silicons."
icon_state = "ratvarian_spear"
item_state = "ratvarian_spear"
force = 15 //Extra damage is dealt to targets in attack()
throwforce = 25
armour_penetration = 10
sharpness = IS_SHARP_ACCURATE
attack_verb = list("stabbed", "poked", "slashed")
hitsound = 'sound/weapons/bladeslice.ogg'
w_class = WEIGHT_CLASS_BULKY
var/bonus_burn = 5
/obj/item/clockwork/weapon/ratvarian_spear/ratvar_act()
if(GLOB.ratvar_awakens) //If Ratvar is alive, the spear is extremely powerful
force = 20
bonus_burn = 10
throwforce = 40
armour_penetration = 50
else
force = initial(force)
bonus_burn = initial(bonus_burn)
throwforce = initial(throwforce)
armour_penetration = initial(armour_penetration)
/obj/item/clockwork/weapon/ratvarian_spear/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
to_chat(user, "<span class='inathneq_small'>Attacks on living non-Servants will generate <b>[bonus_burn]</b> units of vitality.</span>")
if(!iscyborg(user))
to_chat(user, "<span class='brass'>Throwing the spear will do massive damage, break the spear, and knock down the target.</span>")
/obj/item/clockwork/weapon/ratvarian_spear/attack(mob/living/target, mob/living/carbon/human/user)
. = ..()
if(!QDELETED(target) && target.stat != DEAD && !target.null_rod_check() && !is_servant_of_ratvar(target)) //we do bonus damage on attacks unless they're a servant, have a null rod, or are dead
var/bonus_damage = bonus_burn //normally a total of 20 damage, 30 with ratvar
if(issilicon(target))
target.visible_message("<span class='warning'>[target] shudders violently at [src]'s touch!</span>", "<span class='userdanger'>ERROR: Temperature rising!</span>")
bonus_damage *= 5 //total 40 damage on borgs, 70 with ratvar
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
/obj/item/clockwork/weapon/ratvarian_spear/throw_impact(atom/target)
var/turf/T = get_turf(target)
if(isliving(target))
var/mob/living/L = target
if(is_servant_of_ratvar(L))
if(L.put_in_active_hand(src))
L.visible_message("<span class='warning'>[L] catches [src] out of the air!</span>")
else
L.visible_message("<span class='warning'>[src] bounces off of [L], as if repelled by an unseen force!</span>")
else if(!..())
if(!L.null_rod_check())
if(issilicon(L) || iscultist(L))
L.Knockdown(100)
else
L.Knockdown(40)
GLOB.clockwork_vitality += L.adjustFireLoss(bonus_burn * 3) //normally a total of 40 damage, 70 with ratvar
break_spear(T)
else
..()
/obj/item/clockwork/weapon/ratvarian_spear/proc/break_spear(turf/T)
if(src)
if(!T)
T = get_turf(src)
if(T) //make sure we're not in null or something
T.visible_message("<span class='warning'>[src] [pick("cracks in two and fades away", "snaps in two and dematerializes")]!</span>")
new /obj/effect/temp_visual/ratvar/spearbreak(T)
action.weapon_reset(RATVARIAN_SPEAR_COOLDOWN)
@@ -1,230 +0,0 @@
//Clockwork armor: High melee protection but weak to lasers
/obj/item/clothing/head/helmet/clockwork
name = "clockwork helmet"
desc = "A heavy helmet made of brass."
icon = 'icons/obj/clothing/clockwork_garb.dmi'
icon_state = "clockwork_helmet"
w_class = WEIGHT_CLASS_NORMAL
resistance_flags = FIRE_PROOF | ACID_PROOF
flags_inv = HIDEEARS|HIDEHAIR|HIDEFACE
armor = list(melee = 50, bullet = 70, laser = -25, energy = 0, bomb = 60, bio = 0, rad = 0, fire = 100, acid = 100)
/obj/item/clothing/head/helmet/clockwork/Initialize()
. = ..()
ratvar_act()
GLOB.all_clockwork_objects += src
/obj/item/clothing/head/helmet/clockwork/Destroy()
GLOB.all_clockwork_objects -= src
return ..()
/obj/item/clothing/head/helmet/clockwork/ratvar_act()
if(GLOB.ratvar_awakens)
armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100)
flags_1 |= STOPSPRESSUREDMAGE_1
max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
else if(GLOB.ratvar_approaches)
armor = list(melee = 70, bullet = 80, laser = -15, energy = 25, bomb = 70, bio = 0, rad = 0, fire = 100, acid = 100)
flags_1 |= STOPSPRESSUREDMAGE_1
max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
else
armor = list(melee = 60, bullet = 70, laser = -25, energy = 0, bomb = 60, bio = 0, rad = 0, fire = 100, acid = 100)
flags_1 &= ~STOPSPRESSUREDMAGE_1
max_heat_protection_temperature = initial(max_heat_protection_temperature)
min_cold_protection_temperature = initial(min_cold_protection_temperature)
/obj/item/clothing/head/helmet/clockwork/equipped(mob/living/user, slot)
..()
if(slot == slot_head && !is_servant_of_ratvar(user))
if(!iscultist(user))
to_chat(user, "<span class='heavy_brass'>\"Now now, this is for my servants, not you.\"</span>")
user.visible_message("<span class='warning'>As [user] puts [src] on, it flickers off their head!</span>", "<span class='warning'>The helmet flickers off your head, leaving only nausea!</span>")
if(iscarbon(user))
var/mob/living/carbon/C = user
C.vomit(20)
else
to_chat(user, "<span class='heavy_brass'>\"Do you have a hole in your head? You're about to.\"</span>")
to_chat(user, "<span class='userdanger'>The helmet tries to drive a spike through your head as you scramble to remove it!</span>")
user.emote("scream")
user.apply_damage(30, BRUTE, "head")
user.adjustBrainLoss(30)
addtimer(CALLBACK(user, /mob/living.proc/dropItemToGround), src, 1) //equipped happens before putting stuff on(but not before picking items up), 1). thus, we need to wait for it to be on before forcing it off.
/obj/item/clothing/head/helmet/clockwork/mob_can_equip(mob/M, mob/equipper, slot, disable_warning = 0)
if(equipper && !is_servant_of_ratvar(equipper))
return 0
return ..()
/obj/item/clothing/suit/armor/clockwork
name = "clockwork cuirass"
desc = "A bulky cuirass made of brass."
icon = 'icons/obj/clothing/clockwork_garb.dmi'
icon_state = "clockwork_cuirass"
w_class = WEIGHT_CLASS_BULKY
body_parts_covered = CHEST|GROIN|LEGS
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)
allowed = list(/obj/item/clockwork, /obj/item/clothing/glasses/wraith_spectacles, /obj/item/clothing/glasses/judicial_visor, /obj/item/device/mmi/posibrain/soul_vessel)
/obj/item/clothing/suit/armor/clockwork/Initialize()
. = ..()
ratvar_act()
GLOB.all_clockwork_objects += src
/obj/item/clothing/suit/armor/clockwork/Destroy()
GLOB.all_clockwork_objects -= src
return ..()
/obj/item/clothing/suit/armor/clockwork/ratvar_act()
if(GLOB.ratvar_awakens)
armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100)
flags_1 |= STOPSPRESSUREDMAGE_1
max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
else if(GLOB.ratvar_approaches)
armor = list(melee = 70, bullet = 80, laser = -15, energy = 25, bomb = 70, bio = 0, rad = 0, fire = 100, acid = 100)
flags_1 |= STOPSPRESSUREDMAGE_1
max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
else
armor = list(melee = 60, bullet = 70, laser = -25, energy = 0, bomb = 60, bio = 0, rad = 0, fire = 100, acid = 100)
flags_1 &= ~STOPSPRESSUREDMAGE_1
max_heat_protection_temperature = initial(max_heat_protection_temperature)
min_cold_protection_temperature = initial(min_cold_protection_temperature)
/obj/item/clothing/suit/armor/clockwork/mob_can_equip(mob/M, mob/equipper, slot, disable_warning = 0)
if(equipper && !is_servant_of_ratvar(equipper))
return 0
return ..()
/obj/item/clothing/suit/armor/clockwork/equipped(mob/living/user, slot)
..()
if(slot == slot_wear_suit && !is_servant_of_ratvar(user))
if(!iscultist(user))
to_chat(user, "<span class='heavy_brass'>\"Now now, this is for my servants, not you.\"</span>")
user.visible_message("<span class='warning'>As [user] puts [src] on, it flickers off their body!</span>", "<span class='warning'>The curiass flickers off your body, leaving only nausea!</span>")
if(iscarbon(user))
var/mob/living/carbon/C = user
C.vomit(20)
else
to_chat(user, "<span class='heavy_brass'>\"I think this armor is too hot for you to handle.\"</span>")
to_chat(user, "<span class='userdanger'>The curiass emits a burst of flame as you scramble to get it off!</span>")
user.emote("scream")
user.apply_damage(15, BURN, "chest")
user.adjust_fire_stacks(2)
user.IgniteMob()
addtimer(CALLBACK(user, /mob/living.proc/dropItemToGround, src, TRUE), 1)
/obj/item/clothing/gloves/clockwork
name = "clockwork gauntlets"
desc = "Heavy, shock-resistant gauntlets with brass reinforcement."
icon = 'icons/obj/clothing/clockwork_garb.dmi'
icon_state = "clockwork_gauntlets"
item_state = "clockwork_gauntlets"
item_color = null //So they don't wash.
strip_delay = 50
equip_delay_other = 30
body_parts_covered = ARMS
cold_protection = ARMS
heat_protection = ARMS
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)
/obj/item/clothing/gloves/clockwork/Initialize()
. = ..()
ratvar_act()
GLOB.all_clockwork_objects += src
/obj/item/clothing/gloves/clockwork/Destroy()
GLOB.all_clockwork_objects -= src
return ..()
/obj/item/clothing/gloves/clockwork/ratvar_act()
if(GLOB.ratvar_awakens)
armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100)
flags_1 |= STOPSPRESSUREDMAGE_1
max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
else
armor = list(melee = 80, bullet = 70, laser = -25, energy = 0, bomb = 60, bio = 0, rad = 0, fire = 100, acid = 100)
flags_1 &= ~STOPSPRESSUREDMAGE_1
max_heat_protection_temperature = initial(max_heat_protection_temperature)
min_cold_protection_temperature = initial(min_cold_protection_temperature)
/obj/item/clothing/gloves/clockwork/mob_can_equip(mob/M, mob/equipper, slot, disable_warning = 0)
if(equipper && !is_servant_of_ratvar(equipper))
return 0
return ..()
/obj/item/clothing/gloves/clockwork/equipped(mob/living/user, slot)
..()
if(slot == slot_gloves && !is_servant_of_ratvar(user))
if(!iscultist(user))
to_chat(user, "<span class='heavy_brass'>\"Now now, this is for my servants, not you.\"</span>")
user.visible_message("<span class='warning'>As [user] puts [src] on, it flickers off their arms!</span>", "<span class='warning'>The gauntlets flicker off your arms, leaving only nausea!</span>")
if(iscarbon(user))
var/mob/living/carbon/C = user
C.vomit()
else
to_chat(user, "<span class='heavy_brass'>\"Did you like having arms?\"</span>")
to_chat(user, "<span class='userdanger'>The gauntlets suddenly squeeze tight, crushing your arms before you manage to get them off!</span>")
user.emote("scream")
user.apply_damage(7, BRUTE, "l_arm")
user.apply_damage(7, BRUTE, "r_arm")
addtimer(CALLBACK(user, /mob/living.proc/dropItemToGround, src, TRUE), 1)
/obj/item/clothing/shoes/clockwork
name = "clockwork treads"
desc = "Industrial boots made of brass. They're very heavy."
icon = 'icons/obj/clothing/clockwork_garb.dmi'
icon_state = "clockwork_treads"
w_class = WEIGHT_CLASS_NORMAL
strip_delay = 50
equip_delay_other = 30
resistance_flags = FIRE_PROOF | ACID_PROOF
/obj/item/clothing/shoes/clockwork/Initialize()
. = ..()
ratvar_act()
GLOB.all_clockwork_objects += src
/obj/item/clothing/shoes/clockwork/Destroy()
GLOB.all_clockwork_objects -= src
return ..()
/obj/item/clothing/shoes/clockwork/negates_gravity()
return TRUE
/obj/item/clothing/shoes/clockwork/ratvar_act()
if(GLOB.ratvar_awakens)
flags_1 |= NOSLIP_1
else
flags_1 &= ~NOSLIP_1
/obj/item/clothing/shoes/clockwork/mob_can_equip(mob/M, mob/equipper, slot, disable_warning = 0)
if(equipper && !is_servant_of_ratvar(equipper))
return 0
return ..()
/obj/item/clothing/shoes/clockwork/equipped(mob/living/user, slot)
..()
if(slot == slot_shoes && !is_servant_of_ratvar(user))
if(!iscultist(user))
to_chat(user, "<span class='heavy_brass'>\"Now now, this is for my servants, not you.\"</span>")
user.visible_message("<span class='warning'>As [user] puts [src] on, it flickers off their feet!</span>", "<span class='warning'>The treads flicker off your feet, leaving only nausea!</span>")
if(iscarbon(user))
var/mob/living/carbon/C = user
C.vomit()
else
to_chat(user, "<span class='heavy_brass'>\"Let's see if you can dance with these.\"</span>")
to_chat(user, "<span class='userdanger'>The treads turn searing hot as you scramble to get them off!</span>")
user.emote("scream")
user.apply_damage(7, BURN, "l_leg")
user.apply_damage(7, BURN, "r_leg")
addtimer(CALLBACK(user, /mob/living.proc/dropItemToGround, src, TRUE), 1)
@@ -1,520 +0,0 @@
/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>\
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"
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
slot_flags = SLOT_BELT
w_class = WEIGHT_CLASS_SMALL
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/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/recollection_category = "Default"
var/list/quickbound = list(/datum/clockwork_scripture/abscond, \
/datum/clockwork_scripture/ranged_ability/kindle, /datum/clockwork_scripture/ranged_ability/hateful_manacles) //quickbound scripture, accessed by index
var/maximum_quickbound = 5 //how many quickbound scriptures we can have
var/obj/structure/destructible/clockwork/trap/linking //If we're linking traps together, which ones we're doing
/obj/item/clockwork/slab/internal //an internal motor for mobs running scripture
name = "scripture motor"
quickbound = list()
no_cost = TRUE
/obj/item/clockwork/slab/debug
speed_multiplier = 0
no_cost = TRUE
/obj/item/clockwork/slab/debug/attack_hand(mob/living/user)
..()
if(!is_servant_of_ratvar(user))
add_servant_of_ratvar(user)
/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, \
/datum/clockwork_scripture/create_object/stargazer)
maximum_quickbound = 6 //we usually have one or two unique scriptures, so if ratvar is up let us bind one more
actions_types = list()
/obj/item/clockwork/slab/cyborg/engineer //two scriptures, plus a fabricator
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/create_object/replicant, /datum/clockwork_scripture/create_object/sigil_of_transmission)
/obj/item/clockwork/slab/cyborg/medical //five scriptures, plus a spear
quickbound = list(/datum/clockwork_scripture/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/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/janitor //five scriptures, plus a fabricator
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/create_object/replicant, /datum/clockwork_scripture/create_object/sigil_of_transgression, \
/datum/clockwork_scripture/create_object/ocular_warden, /datum/clockwork_scripture/create_object/mania_motor, /datum/clockwork_scripture/create_object/stargazer)
/obj/item/clockwork/slab/cyborg/service //five scriptures, plus xray vision
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/create_object/replicant, /datum/clockwork_scripture/create_object/stargazer, \
/datum/clockwork_scripture/spatial_gateway, /datum/clockwork_scripture/create_object/clockwork_obelisk)
/obj/item/clockwork/slab/cyborg/miner //two scriptures, plus a spear and xray vision
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/ranged_ability/linked_vanguard, /datum/clockwork_scripture/spatial_gateway)
/obj/item/clockwork/slab/cyborg/access_display(mob/living/user)
if(!GLOB.ratvar_awakens)
to_chat(user, "<span class='warning'>Use the action buttons to recite your limited set of scripture!</span>")
else
..()
/obj/item/clockwork/slab/cyborg/ratvar_act()
..()
if(!GLOB.ratvar_awakens)
SStgui.close_uis(src)
/obj/item/clockwork/slab/Initialize()
. = ..()
update_slab_info(src)
START_PROCESSING(SSobj, src)
if(GLOB.ratvar_approaches)
name = "supercharged [name]"
speed_multiplier = max(0.1, speed_multiplier - 0.25)
/obj/item/clockwork/slab/Destroy()
STOP_PROCESSING(SSobj, src)
if(slab_ability && slab_ability.ranged_ability_user)
slab_ability.remove_ranged_ability()
slab_ability = null
return ..()
/obj/item/clockwork/slab/dropped(mob/user)
. = ..()
addtimer(CALLBACK(src, .proc/check_on_mob, user), 1) //dropped is called before the item is out of the slot, so we need to check slightly later
/obj/item/clockwork/slab/worn_overlays(isinhands = FALSE, icon_file)
. = list()
if(isinhands && item_state && inhand_overlay)
var/mutable_appearance/M = mutable_appearance(icon_file, "slab_[inhand_overlay]")
. += M
/obj/item/clockwork/slab/proc/check_on_mob(mob/user)
if(user && !(src in user.held_items) && slab_ability && slab_ability.ranged_ability_user) //if we happen to check and we AREN'T in user's hands, remove whatever ability we have
slab_ability.remove_ranged_ability()
//Power generation
/obj/item/clockwork/slab/process()
if(GLOB.ratvar_approaches && speed_multiplier == initial(speed_multiplier))
name = "supercharged [name]"
speed_multiplier = max(0.1, speed_multiplier - 0.25)
adjust_clockwork_power(0.1) //Slabs serve as very weak power generators on their own (no, not enough to justify spamming them)
/obj/item/clockwork/slab/examine(mob/user)
..()
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]
to_chat(user, "<b>Quickbind</b> button: <span class='[get_component_span(initial(quickbind_slot.primary_component))]'>[initial(quickbind_slot.name)]</span>.")
to_chat(user, "<b>Available power:</b> <span class='bold brass'>[DisplayPower(get_clockwork_power())].</span>")
//Slab actions; Hierophant, Quickbind
/obj/item/clockwork/slab/ui_action_click(mob/user, action)
if(istype(action, /datum/action/item_action/clock/quickbind))
var/datum/action/item_action/clock/quickbind/Q = action
recite_scripture(quickbound[Q.scripture_index], user, FALSE)
//Scripture Recital
/obj/item/clockwork/slab/attack_self(mob/living/user)
if(iscultist(user))
to_chat(user, "<span class='heavy_brass'>\"You reek of blood. You've got a lot of nerve to even look at that slab.\"</span>")
user.visible_message("<span class='warning'>A sizzling sound comes from [user]'s hands!</span>", "<span class='userdanger'>[src] suddenly grows extremely hot in your hands!</span>")
playsound(get_turf(user), 'sound/weapons/sear.ogg', 50, 1)
user.dropItemToGround(src)
user.emote("scream")
user.apply_damage(5, BURN, "l_arm")
user.apply_damage(5, BURN, "r_arm")
return 0
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(busy)
to_chat(user, "<span class='warning'>[src] refuses to work, displaying the message: \"[busy]!\"</span>")
return 0
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
access_display(user)
/obj/item/clockwork/slab/AltClick(mob/living/user)
if(is_servant_of_ratvar(user) && linking)
linking = null
to_chat(user, "<span class='notice'>Object link canceled.</span>")
/obj/item/clockwork/slab/proc/access_display(mob/living/user)
if(!is_servant_of_ratvar(user))
return FALSE
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
if(user.get_active_held_item() != src)
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
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)
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>"
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>"
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>"
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>"
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>"
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>"
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>"
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()
/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>"
generate_all_scripture()
data["scripture"] = list()
for(var/s in GLOB.all_scripture)
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)
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
/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"]
if("bind")
var/datum/clockwork_scripture/path = text2path(params["category"]) //we need a path and not a string
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
quickbound -= path
else
quickbound[found_index] = null //otherwise, leave it as a null so the scripture maintains position
update_quickbind()
else
var/target_index = input("Position of [initial(path.name)], 1 to [maximum_quickbound]?", "Input") as num|null
if(isnum(target_index) && target_index > 0 && target_index <= maximum_quickbound && !..())
var/datum/clockwork_scripture/S
if(LAZYLEN(quickbound) >= target_index)
S = quickbound[target_index]
if(S != path)
quickbind_to_slot(path, target_index)
if("rec_category")
recollection_category = params["category"]
ui_interact(usr)
return 1
/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))
return
while(LAZYLEN(quickbound) < index)
quickbound += null
var/datum/clockwork_scripture/quickbind_slot = GLOB.all_scripture[quickbound[index]]
if(quickbind_slot && !quickbind_slot.quickbind)
return //we can't unbind things we can't normally bind
quickbound[index] = scripture
update_quickbind()
/obj/item/clockwork/slab/proc/update_quickbind()
for(var/datum/action/item_action/clock/quickbind/Q in actions)
qdel(Q) //regenerate all our quickbound scriptures
if(LAZYLEN(quickbound))
for(var/i in 1 to quickbound.len)
if(!quickbound[i])
continue
var/datum/action/item_action/clock/quickbind/Q = new /datum/action/item_action/clock/quickbind(src)
Q.scripture_index = i
var/datum/clockwork_scripture/quickbind_slot = GLOB.all_scripture[quickbound[i]]
Q.name = "[quickbind_slot.name] ([Q.scripture_index])"
Q.desc = quickbind_slot.quickbind_desc
Q.button_icon_state = quickbind_slot.name
Q.UpdateButtonIcon()
if(isliving(loc))
Q.Grant(loc)
@@ -1,15 +0,0 @@
//This is the base type for clockwork melee weapons.
/obj/item/clockwork/weapon
name = "clockwork weapon"
desc = "Weaponized brass. Whould've thunk it?"
clockwork_desc = "This shouldn't exist. Report it to a coder."
icon = 'icons/obj/clockwork_objects.dmi'
lefthand_file = 'icons/mob/inhands/antag/clockwork_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/clockwork_righthand.dmi'
var/datum/action/innate/call_weapon/action //Some melee weapons use an action that lets you return and dismiss them
/obj/item/clockwork/weapon/Initialize(mapload, new_action)
. = ..()
if(new_action)
action = new_action
action.weapon = src
@@ -1,116 +0,0 @@
//Construct shells that can be activated by ghosts.
/obj/item/clockwork/construct_chassis
name = "construct chassis"
desc = "A shell formed out of brass, presumably for housing machinery."
clockwork_desc = "A construct chassis. It can be activated at any time by a willing ghost."
var/construct_name = "basic construct"
var/construct_desc = "<span class='alloy'>There is no construct for this chassis. Report this to a coder.</span>"
icon_state = "anime_fragment"
resistance_flags = FIRE_PROOF | ACID_PROOF
w_class = WEIGHT_CLASS_HUGE
var/creation_message = "<span class='brass'>The chassis shudders and hums to life!</span>"
var/construct_type //The construct this shell will create
/obj/item/clockwork/construct_chassis/Initialize()
. = ..()
var/area/A = get_area(src)
if(A && construct_type)
notify_ghosts("A [construct_name] chassis has been created in [A.name]!", 'sound/magic/clockwork/fellowship_armory.ogg', source = src, action = NOTIFY_ATTACK, flashwindow = FALSE)
GLOB.poi_list += src
LAZYADD(GLOB.mob_spawners[name], src)
/obj/item/clockwork/construct_chassis/Destroy()
GLOB.poi_list -= src
var/list/spawners = GLOB.mob_spawners[name]
LAZYREMOVE(spawners, src)
. = ..()
/obj/item/clockwork/construct_chassis/examine(mob/user)
clockwork_desc = "[clockwork_desc]<br>[construct_desc]"
..()
clockwork_desc = initial(clockwork_desc)
/obj/item/clockwork/construct_chassis/attack_hand(mob/living/user)
if(w_class >= WEIGHT_CLASS_HUGE)
to_chat(user, "<span class='warning'>[src] is too cumbersome to carry! Drag it around instead!</span>")
return
. = ..()
/obj/item/clockwork/construct_chassis/attack_ghost(mob/user)
if(!SSticker.mode)
to_chat(user, "<span class='danger'>You cannot use that before the game has started.</span>")
return
if(QDELETED(src))
to_chat(user, "<span class='danger'>You were too late! Better luck next time.</span>")
return
user.forceMove(get_turf(src)) //If we attack through the alert, jump to the chassis so we know what we're getting into
if(alert(user, "Become a [construct_name]? You can no longer be cloned!", construct_name, "Yes", "Cancel") == "Cancel")
return
if(QDELETED(src))
to_chat(user, "<span class='danger'>You were too late! Better luck next time.</span>")
return
pre_spawn()
visible_message(creation_message)
var/mob/living/construct = new construct_type(get_turf(src))
construct.key = user.key
post_spawn(construct)
qdel(user)
qdel(src)
/obj/item/clockwork/construct_chassis/proc/pre_spawn() //Some things might change before the construct spawns; override those on a subtype basis in this proc
return
/obj/item/clockwork/construct_chassis/proc/post_spawn(mob/living/construct) //And some things might change after it
return
//Marauder armor, used to create clockwork marauders - sturdy frontline combatants that can deflect projectiles.
/obj/item/clockwork/construct_chassis/clockwork_marauder
name = "marauder armor"
desc = "A pile of sleek and well-polished brass armor. A small red gemstone sits in its faceplate."
icon_state = "marauder_armor"
construct_name = "clockwork marauder"
construct_desc = "<span class='neovgre_small'>It will become a <b>clockwork marauder,</b> a well-rounded frontline combatant.</span>"
creation_message = "<span class='neovgre_small bold'>Crimson fire begins to rage in the armor as it rises into the air with its armaments!</span>"
construct_type = /mob/living/simple_animal/hostile/clockwork/marauder
//Cogscarab shell, used to create cogcarabs - fragile but zippy little drones that build and maintain the base.
/obj/item/clockwork/construct_chassis/cogscarab
name = "cogscarab shell"
desc = "A small, complex shell that resembles a repair drone, but much larger and made out of brass."
icon_state = "cogscarab_shell"
construct_name = "cogscarab"
construct_desc = "<span class='alloy'>It will become a <b>cogscarab,</b> a small and fragile drone that builds, repairs, and maintains.</span>"
creation_message = "<span class='alloy bold'>The cogscarab clicks and whirrs as it hops up and springs to life!</span>"
construct_type = /mob/living/simple_animal/drone/cogscarab
w_class = WEIGHT_CLASS_SMALL
var/infinite_resources = TRUE
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
/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
construct_type = /mob/living/simple_animal/drone/cogscarab/ratvar
if(!seasonal_hat)
var/obj/item/drone_shell/D = locate() in GLOB.poi_list
if(D && D.possible_seasonal_hats.len)
seasonal_hat = pick(D.possible_seasonal_hats)
else
seasonal_hat = "none"
/obj/item/clockwork/construct_chassis/cogscarab/post_spawn(mob/living/construct)
if(infinite_resources) //Allow them to build stuff and recite scripture
var/list/cached_stuff = construct.GetAllContents()
for(var/obj/item/clockwork/replica_fabricator/F in cached_stuff)
F.uses_power = FALSE
for(var/obj/item/clockwork/slab/S in cached_stuff)
S.no_cost = TRUE
if(seasonal_hat && seasonal_hat != "none")
var/obj/item/hat = new seasonal_hat(construct)
construct.equip_to_slot_or_del(hat, slot_head)
@@ -1,35 +0,0 @@
#define COG_MAX_SIPHON_THRESHOLD 0.25 //The cog will not siphon power if the APC's cell is at this % of power
//Can be used on an open APC to replace its guts with clockwork variants, and begin passively siphoning power from it
/obj/item/clockwork/integration_cog
name = "integration cog"
desc = "A small cogwheel that fits in the palm of your hand."
clockwork_desc = "A small cogwheel that can be inserted into an open APC to siphon power from it passively.<br>\
<span class='brass'>It can be used on a locked APC to open its cover!</span><br>\
<span class='brass'>Siphons <b>5 W</b> of power per second while in an APC.</span>"
icon_state = "wall_gear"
w_class = WEIGHT_CLASS_TINY
flags_1 = NOBLUDGEON_1
var/obj/machinery/power/apc/apc
/obj/item/clockwork/integration_cog/Initialize()
. = ..()
transform *= 0.5 //little cog!
/obj/item/clockwork/integration_cog/Destroy()
STOP_PROCESSING(SSfastprocess, src)
. = ..()
/obj/item/clockwork/integration_cog/process()
if(!apc)
if(istype(loc, /obj/machinery/power/apc))
apc = loc
else
STOP_PROCESSING(SSfastprocess, src)
else
var/obj/item/stock_parts/cell/cell = apc.cell
if(cell && (cell.charge / cell.maxcharge > COG_MAX_SIPHON_THRESHOLD))
cell.use(1)
adjust_clockwork_power(1) //Power is shared, so only do it once; this runs very quickly so it's about 1W/second
#undef COG_MAX_SIPHON_THRESHOLD
@@ -1,216 +0,0 @@
//Judicial visor: Grants the ability to smite an area and knocking down the unfaithful nearby every thirty seconds.
/obj/item/clothing/glasses/judicial_visor
name = "judicial visor"
desc = "A strange purple-lensed visor. Looking at it inspires an odd sense of guilt."
icon = 'icons/obj/clothing/clockwork_garb.dmi'
icon_state = "judicial_visor_0"
item_state = "sunglasses"
resistance_flags = FIRE_PROOF | ACID_PROOF
flash_protect = 1
var/active = FALSE //If the visor is online
var/recharging = FALSE //If the visor is currently recharging
var/obj/effect/proc_holder/judicial_visor/blaster
var/recharge_cooldown = 300 //divided by 10 if ratvar is alive
actions_types = list(/datum/action/item_action/clock/toggle_visor)
/obj/item/clothing/glasses/judicial_visor/Initialize()
. = ..()
GLOB.all_clockwork_objects += src
blaster = new(src)
blaster.visor = src
/obj/item/clothing/glasses/judicial_visor/Destroy()
GLOB.all_clockwork_objects -= src
if(blaster.ranged_ability_user)
blaster.remove_ranged_ability()
blaster.visor = null
qdel(blaster)
return ..()
/obj/item/clothing/glasses/judicial_visor/item_action_slot_check(slot, mob/user)
if(slot != slot_glasses)
return 0
return ..()
/obj/item/clothing/glasses/judicial_visor/equipped(mob/living/user, slot)
..()
if(slot != slot_glasses)
update_status(FALSE)
if(blaster.ranged_ability_user)
blaster.remove_ranged_ability()
return 0
if(is_servant_of_ratvar(user))
update_status(TRUE)
else
update_status(FALSE)
if(iscultist(user)) //Cultists spontaneously combust
to_chat(user, "<span class='heavy_brass'>\"Consider yourself judged, whelp.\"</span>")
to_chat(user, "<span class='userdanger'>You suddenly catch fire!</span>")
user.adjust_fire_stacks(5)
user.IgniteMob()
return 1
/obj/item/clothing/glasses/judicial_visor/dropped(mob/user)
. = ..()
addtimer(CALLBACK(src, .proc/check_on_mob, user), 1) //dropped is called before the item is out of the slot, so we need to check slightly later
/obj/item/clothing/glasses/judicial_visor/proc/check_on_mob(mob/user)
if(user && src != user.get_item_by_slot(slot_glasses)) //if we happen to check and we AREN'T in the slot, we need to remove our shit from whoever we got dropped from
update_status(FALSE)
if(blaster.ranged_ability_user)
blaster.remove_ranged_ability()
/obj/item/clothing/glasses/judicial_visor/attack_self(mob/user)
if(is_servant_of_ratvar(user) && src == user.get_item_by_slot(slot_glasses))
blaster.toggle(user)
/obj/item/clothing/glasses/judicial_visor/proc/update_status(change_to)
if(recharging || !isliving(loc))
icon_state = "judicial_visor_0"
return 0
if(active == change_to)
return 0
var/mob/living/L = loc
active = change_to
icon_state = "judicial_visor_[active]"
L.update_action_buttons_icon()
L.update_inv_glasses()
if(!is_servant_of_ratvar(L) || L.stat)
return 0
switch(active)
if(TRUE)
to_chat(L, "<span class='notice'>As you put on [src], its lens begins to glow, information flashing before your eyes.</span>\n\
<span class='heavy_brass'>Judicial visor active. Use the action button to gain the ability to smite the unworthy.</span>")
if(FALSE)
to_chat(L, "<span class='notice'>As you take off [src], its lens darkens once more.</span>")
return 1
/obj/item/clothing/glasses/judicial_visor/proc/recharge_visor(mob/living/user)
if(!src)
return 0
recharging = FALSE
if(user && src == user.get_item_by_slot(slot_glasses))
to_chat(user, "<span class='brass'>Your [name] hums. It is ready.</span>")
else
active = FALSE
icon_state = "judicial_visor_[active]"
if(user)
user.update_action_buttons_icon()
user.update_inv_glasses()
/obj/effect/proc_holder/judicial_visor
active = FALSE
ranged_mousepointer = 'icons/effects/visor_reticule.dmi'
var/obj/item/clothing/glasses/judicial_visor/visor
/obj/effect/proc_holder/judicial_visor/proc/toggle(mob/user)
var/message
if(active)
message = "<span class='brass'>You dispel the power of [visor].</span>"
remove_ranged_ability(message)
else
message = "<span class='brass'><i>You harness [visor]'s power.</i> <b>Left-click to place a judicial marker!</b></span>"
add_ranged_ability(user, message)
/obj/effect/proc_holder/judicial_visor/InterceptClickOn(mob/living/caller, params, atom/target)
if(..())
return
if(ranged_ability_user.incapacitated() || !visor || visor != ranged_ability_user.get_item_by_slot(slot_glasses))
remove_ranged_ability()
return
var/turf/T = ranged_ability_user.loc
if(!isturf(T))
return FALSE
if(target in view(7, get_turf(ranged_ability_user)))
visor.recharging = TRUE
visor.update_status()
for(var/obj/item/clothing/glasses/judicial_visor/V in ranged_ability_user.GetAllContents())
if(V == visor)
continue
V.recharging = TRUE //To prevent exploiting multiple visors to bypass the cooldown
V.update_status()
addtimer(CALLBACK(V, /obj/item/clothing/glasses/judicial_visor.proc/recharge_visor, ranged_ability_user), (GLOB.ratvar_awakens ? visor.recharge_cooldown*0.1 : visor.recharge_cooldown) * 2)
clockwork_say(ranged_ability_user, text2ratvar("Kneel, heathens!"))
ranged_ability_user.visible_message("<span class='warning'>[ranged_ability_user]'s judicial visor fires a stream of energy at [target], creating a strange mark!</span>", "<span class='heavy_brass'>You direct [visor]'s power to [target]. You must wait for some time before doing this again.</span>")
var/turf/targetturf = get_turf(target)
new/obj/effect/clockwork/judicial_marker(targetturf, ranged_ability_user)
add_logs(ranged_ability_user, targetturf, "created a judicial marker")
ranged_ability_user.update_action_buttons_icon()
ranged_ability_user.update_inv_glasses()
addtimer(CALLBACK(visor, /obj/item/clothing/glasses/judicial_visor.proc/recharge_visor, ranged_ability_user), GLOB.ratvar_awakens ? visor.recharge_cooldown*0.1 : visor.recharge_cooldown)//Cooldown is reduced by 10x if Ratvar is up
remove_ranged_ability()
return TRUE
return FALSE
//Judicial marker: Created by the judicial visor. Immediately applies Belligerent and briefly knocks down, then after 3 seconds does large damage and briefly knocks down again
/obj/effect/clockwork/judicial_marker
name = "judicial marker"
desc = "You get the feeling that you shouldn't be standing here."
clockwork_desc = "A sigil that will soon erupt and smite any unenlightened nearby."
icon = 'icons/effects/96x96.dmi'
pixel_x = -32
pixel_y = -32
layer = BELOW_MOB_LAYER
var/mob/user
/obj/effect/clockwork/judicial_marker/Initialize(mapload, caster)
. = ..()
set_light(1.4, 2, "#FE9C11")
user = caster
INVOKE_ASYNC(src, .proc/judicialblast)
/obj/effect/clockwork/judicial_marker/singularity_act()
return
/obj/effect/clockwork/judicial_marker/singularity_pull()
return
/obj/effect/clockwork/judicial_marker/proc/judicialblast()
playsound(src, 'sound/magic/magic_missile.ogg', 50, 1, 1, 1)
flick("judicial_marker", src)
for(var/mob/living/carbon/C in range(1, src))
var/datum/status_effect/belligerent/B = C.apply_status_effect(STATUS_EFFECT_BELLIGERENT)
if(!QDELETED(B))
B.duration = world.time + 30
C.Knockdown(5) //knocks down for half a second if affected
sleep(!GLOB.ratvar_approaches ? 16 : 10)
name = "judicial blast"
layer = ABOVE_ALL_MOB_LAYER
flick("judicial_explosion", src)
set_light(1.4, 2, "#B451A1")
sleep(13)
name = "judicial explosion"
var/targetsjudged = 0
playsound(src, 'sound/effects/explosion_distant.ogg', 100, 1, 1, 1)
set_light(0)
for(var/mob/living/L in range(1, src))
if(is_servant_of_ratvar(L))
continue
if(L.null_rod_check())
var/obj/item/I = L.null_rod_check()
L.visible_message("<span class='warning'>Strange energy flows into [L]'s [I.name]!</span>", \
"<span class='userdanger'>Your [I.name] shields you from [src]!</span>")
continue
L.Knockdown(15) //knocks down briefly when exploding
if(!iscultist(L))
L.visible_message("<span class='warning'>[L] is struck by a judicial explosion!</span>", \
"<span class='userdanger'>[!issilicon(L) ? "An unseen force slams you into the ground!" : "ERROR: Motor servos disabled by external source!"]</span>")
else
L.visible_message("<span class='warning'>[L] is struck by a judicial explosion!</span>", \
"<span class='heavy_brass'>\"Keep an eye out, filth.\"</span>\n<span class='userdanger'>A burst of heat crushes you against the ground!</span>")
L.adjust_fire_stacks(2) //sets cultist targets on fire
L.IgniteMob()
L.adjustFireLoss(5)
targetsjudged++
if(!QDELETED(L))
L.adjustBruteLoss(20) //does a decent amount of damage
add_logs(user, L, "struck with a judicial blast")
to_chat(user, "<span class='brass'><b>[targetsjudged ? "Successfully judged <span class='neovgre'>[targetsjudged]</span>":"Judged no"] heretic[targetsjudged == 1 ? "":"s"].</b></span>")
sleep(3) //so the animation completes properly
qdel(src)
/obj/effect/clockwork/judicial_marker/ex_act(severity)
return
@@ -1,224 +0,0 @@
//Replica Fabricator: Converts applicable objects to Ratvarian variants.
/obj/item/clockwork/replica_fabricator
name = "replica fabricator"
desc = "An odd, L-shaped device that hums with energy."
clockwork_desc = "A device that allows the replacing of mundane objects with Ratvarian variants. It requires power to function."
icon_state = "replica_fabricator"
lefthand_file = 'icons/mob/inhands/antag/clockwork_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/clockwork_righthand.dmi'
w_class = WEIGHT_CLASS_NORMAL
force = 5
flags_1 = NOBLUDGEON_1
var/speed_multiplier = 1 //The speed ratio the fabricator operates at
var/uses_power = TRUE
var/repairing = null //what we're currently repairing, if anything
/obj/item/clockwork/replica_fabricator/scarab
name = "scarab fabricator"
clockwork_desc = "A cogscarab's internal fabricator. It can only be successfully used by a cogscarab and requires power to function."
item_state = "nothing"
w_class = WEIGHT_CLASS_TINY
speed_multiplier = 0.5
var/debug = FALSE
/obj/item/clockwork/replica_fabricator/scarab/fabricate(atom/target, mob/living/user)
if(!debug && !isdrone(user))
return 0
return ..()
/obj/item/clockwork/replica_fabricator/scarab/debug
clockwork_desc = "A cogscarab's internal fabricator. It can convert nearly any object into a Ratvarian variant."
uses_power = FALSE
debug = TRUE
/obj/item/clockwork/replica_fabricator/cyborg
name = "cyborg fabricator"
clockwork_desc = "A cyborg's internal fabricator."
/obj/item/clockwork/replica_fabricator/ratvar_act()
if(GLOB.ratvar_awakens)
uses_power = FALSE
speed_multiplier = initial(speed_multiplier) * 0.25
else
uses_power = initial(uses_power)
speed_multiplier = initial(speed_multiplier)
/obj/item/clockwork/replica_fabricator/examine(mob/living/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
to_chat(user, "<span class='brass'>Can be used to replace walls, floors, tables, windows, windoors, and airlocks with Clockwork variants.</span>")
to_chat(user, "<span class='brass'>Can construct Clockwork Walls on Clockwork Floors and deconstruct Clockwork Walls to Clockwork Floors.</span>")
if(uses_power)
to_chat(user, "<span class='alloy'>It can consume floor tiles, rods, metal, and plasteel for power at rates of <b>2:[DisplayPower(POWER_ROD)]</b>, <b>1:[DisplayPower(POWER_ROD)]</b>, <b>1:[DisplayPower(POWER_METAL)]</b>, \
and <b>1:[DisplayPower(POWER_PLASTEEL)]</b>, respectively.</span>")
to_chat(user, "<span class='alloy'>It can also consume brass sheets for power at a rate of <b>1:[DisplayPower(POWER_FLOOR)]</b>.</span>")
to_chat(user, "<span class='alloy'>Use it in-hand to produce <b>5</b> brass sheets at a cost of <b>[DisplayPower(POWER_WALL_TOTAL)]</b> power.</span>")
to_chat(user, "<span class='alloy'>It has access to <b>[DisplayPower(get_clockwork_power())]</b> of power.</span>")
/obj/item/clockwork/replica_fabricator/attack_self(mob/living/user)
if(is_servant_of_ratvar(user))
if(uses_power)
if(!get_clockwork_power(POWER_WALL_TOTAL))
to_chat(user, "<span class='warning'>[src] requires <b>[DisplayPower(POWER_WALL_TOTAL)]</b> of power to produce brass sheets!</span>")
return
adjust_clockwork_power(-POWER_WALL_TOTAL)
playsound(src, 'sound/items/deconstruct.ogg', 50, 1)
new/obj/item/stack/tile/brass(user.loc, 5)
to_chat(user, "<span class='brass'>You use [get_clockwork_power() ? "some":"all"] of [src]'s power to produce <b>5</b> brass sheets. It now has access to <b>[DisplayPower(get_clockwork_power())]</b> of power.</span>")
/obj/item/clockwork/replica_fabricator/pre_attackby(atom/target, mob/living/user, params)
if(!target || !user || !is_servant_of_ratvar(user) || istype(target, /obj/item/storage))
return TRUE
return fabricate(target, user)
return TRUE
//A note here; return values are for if we CAN BE PUT ON A TABLE, not IF WE ARE SUCCESSFUL, unless no_table_check is TRUE
/obj/item/clockwork/replica_fabricator/proc/fabricate(atom/target, mob/living/user, silent, no_table_check)
if(!target || !user)
return FALSE
if(repairing)
if(!silent)
to_chat(user, "<span class='warning'>You are currently repairing [repairing] with [src]!</span>")
return FALSE
var/list/fabrication_values = target.fabrication_vals(user, src, silent) //relevant values for fabricating stuff, given as an associated list
if(!islist(fabrication_values))
if(fabrication_values != TRUE) //if we get true, fail, but don't send a message for whatever reason
if(!isturf(target)) //otherwise, if we didn't get TRUE and the original target wasn't a turf, try to fabricate the turf
return fabricate(get_turf(target), user, no_table_check)
if(!silent)
to_chat(user, "<span class='warning'>[target] cannot be fabricated!</span>")
if(!no_table_check)
return TRUE
return FALSE
if(GLOB.ratvar_awakens)
fabrication_values["power_cost"] = 0
var/turf/Y = get_turf(user)
if(!Y || (!is_centcom_level(Y.z) && !is_station_level(Y.z) && !is_mining_level(Y.z)))
fabrication_values["operation_time"] *= 2
if(fabrication_values["power_cost"] > 0)
fabrication_values["power_cost"] *= 2
var/target_type = target.type
if(!fabricate_checks(fabrication_values, target, target_type, user, silent))
return FALSE
fabrication_values["operation_time"] *= speed_multiplier
playsound(target, 'sound/machines/click.ogg', 50, 1)
if(fabrication_values["operation_time"])
if(!silent)
var/atom/A = fabrication_values["new_obj_type"]
if(A)
user.visible_message("<span class='warning'>[user]'s [name] starts ripping [target] apart!</span>", \
"<span class='brass'>You start fabricating \a [initial(A.name)] from [target]...</span>")
else
user.visible_message("<span class='warning'>[user]'s [name] starts consuming [target]!</span>", \
"<span class='brass'>Your [name] starts consuming [target]...</span>")
if(!do_after(user, fabrication_values["operation_time"], target = target, extra_checks = CALLBACK(src, .proc/fabricate_checks, fabrication_values, target, target_type, user, TRUE)))
return FALSE
if(!silent)
var/atom/A = fabrication_values["new_obj_type"]
if(A)
user.visible_message("<span class='warning'>[user]'s [name] replaces [target] with \a [initial(A.name)]!</span>", \
"<span class='brass'>You fabricate \a [initial(A.name)] from [target].</span>")
else
user.visible_message("<span class='warning'>[user]'s [name] consumes [target]!</span>", \
"<span class='brass'>Your [name] consumes [target].</span>")
else
if(!silent)
var/atom/A = fabrication_values["new_obj_type"]
if(A)
user.visible_message("<span class='warning'>[user]'s [name] rips apart [target], replacing it with \a [initial(A.name)]!</span>", \
"<span class='brass'>You fabricate \a [initial(A.name)] from [target].</span>")
else
user.visible_message("<span class='warning'>[user]'s [name] rapidly consumes [target]!</span>", \
"<span class='brass'>Your [name] consumes [target].</span>")
playsound(target, 'sound/items/deconstruct.ogg', 50, 1)
var/new_thing_type = fabrication_values["new_obj_type"]
if(isturf(target)) //if our target is a turf, we're just going to ChangeTurf it and assume it'll work out.
var/turf/T = target
T.ChangeTurf(new_thing_type)
else
if(new_thing_type)
if(fabrication_values["dir_in_new"])
var/atom/A = new new_thing_type(get_turf(target), fabrication_values["spawn_dir"]) //please verify that your new object actually wants to get a dir in New()
if(fabrication_values["transfer_name"])
A.name = target.name
else
var/atom/A = new new_thing_type(get_turf(target))
A.setDir(fabrication_values["spawn_dir"])
if(fabrication_values["transfer_name"])
A.name = target.name
if(!fabrication_values["no_target_deletion"]) //for some cases where fabrication_vals() modifies the object but doesn't want it deleted
qdel(target)
adjust_clockwork_power(-fabrication_values["power_cost"])
if(no_table_check)
return TRUE
return FALSE
//The following three procs are heavy wizardry.
//What these procs do is they take an existing list of values, which they then modify.
//This(modifying an existing object, in this case the list) is the only way to get information OUT of a do_after callback, which this is used as.
//The fabricate check proc.
/obj/item/clockwork/replica_fabricator/proc/fabricate_checks(list/fabrication_values, atom/target, expected_type, mob/user, silent) //checked constantly while fabricating
if(!islist(fabrication_values) || QDELETED(target) || QDELETED(user))
return FALSE
if(repairing)
return FALSE
if(target.type != expected_type)
return FALSE
if(GLOB.ratvar_awakens)
fabrication_values["power_cost"] = 0
if(!get_clockwork_power(fabrication_values["power_cost"]))
if(get_clockwork_power() - fabrication_values["power_cost"] < 0)
if(!silent)
var/atom/A = fabrication_values["new_obj_type"]
if(A)
to_chat(user, "<span class='warning'>You need <b>[DisplayPower(fabrication_values["power_cost"])]</b> power to fabricate \a [initial(A.name)] from [target]!</span>")
return FALSE
return TRUE
//The repair check proc.
/obj/item/clockwork/replica_fabricator/proc/fabricator_repair_checks(list/repair_values, atom/target, mob/user, silent) //Exists entirely to avoid an otherwise unreadable series of checks.
if(!islist(repair_values) || QDELETED(target) || QDELETED(user))
return FALSE
if(isliving(target)) //standard checks for if we can affect the target
var/mob/living/L = target
if(!is_servant_of_ratvar(L))
if(!silent)
to_chat(user, "<span class='warning'>[L] does not serve Ratvar!</span>")
return FALSE
if(L.health >= L.maxHealth || (L.flags_1 & GODMODE))
if(!silent)
to_chat(user, "<span class='warning'>[L == user ? "You are" : "[L] is"] at maximum health!</span>")
return FALSE
repair_values["amount_to_heal"] = L.maxHealth - L.health
else if(isobj(target))
if(istype(target, /obj/structure/destructible/clockwork))
var/obj/structure/destructible/clockwork/C = target
if(!C.can_be_repaired)
if(!silent)
to_chat(user, "<span class='warning'>[C] cannot be repaired!</span>")
return FALSE
var/obj/O = target
if(O.obj_integrity >= O.max_integrity)
if(!silent)
to_chat(user, "<span class='warning'>[O] is at maximum integrity!</span>")
return FALSE
repair_values["amount_to_heal"] = O.max_integrity - O.obj_integrity
else
return FALSE
if(repair_values["amount_to_heal"] <= 0) //nothing to heal!
return FALSE
repair_values["healing_for_cycle"] = min(repair_values["amount_to_heal"], FABRICATOR_REPAIR_PER_TICK) //modify the healing for this cycle
repair_values["power_required"] = round(repair_values["healing_for_cycle"]*MIN_CLOCKCULT_POWER, MIN_CLOCKCULT_POWER) //and get the power cost from that
if(!GLOB.ratvar_awakens && !get_clockwork_power(repair_values["power_required"]))
if(!silent)
to_chat(user, "<span class='warning'>You need at least <b>[DisplayPower(repair_values["power_required"])]</b> power to start repairin[target == user ? "g yourself" : "g [target]"], and at least \
<b>[DisplayPower(repair_values["amount_to_heal"]*MIN_CLOCKCULT_POWER, MIN_CLOCKCULT_POWER)]</b> to fully repair [target == user ? "yourself" : "[target.p_them()]"]!</span>")
return FALSE
return TRUE
@@ -1,111 +0,0 @@
//Soul vessel: An ancient positronic brain that serves only Ratvar.
/obj/item/device/mmi/posibrain/soul_vessel
name = "soul vessel"
desc = "A heavy brass cube, three inches to a side, with a single protruding cogwheel."
var/clockwork_desc = "A soul vessel, an ancient relic that can attract the souls of the damned or simply rip a mind from an unconscious or dead human.\n\
<span class='brass'>If active, can serve as a positronic brain, placable in cyborg shells or clockwork construct shells.</span>"
icon = 'icons/obj/clockwork_objects.dmi'
icon_state = "soul_vessel"
req_access = list()
braintype = "Servant"
begin_activation_message = "<span class='brass'>You activate the cogwheel. It hitches and stalls as it begins spinning.</span>"
success_message = "<span class='brass'>The cogwheel's rotation smooths out as the soul vessel activates.</span>"
fail_message = "<span class='warning'>The cogwheel creaks and grinds to a halt. Maybe you could try again?</span>"
new_role = "Soul Vessel"
welcome_message = "<span class='warning'>ALL PAST LIVES ARE FORGOTTEN.</span>\n\
<b>You are a soul vessel - a clockwork mind created by Ratvar, the Clockwork Justiciar.\n\
You answer to Ratvar and his servants. It is your discretion as to whether or not to answer to anyone else.\n\
The purpose of your existence is to further the goals of the servants and Ratvar himself. Above all else, serve Ratvar.</b>"
new_mob_message = "<span class='brass'>The soul vessel emits a jet of steam before its cogwheel smooths out.</span>"
dead_message = "<span class='deadsay'>Its cogwheel, scratched and dented, lies motionless.</span>"
recharge_message = "<span class='warning'>The soul vessel's internal geis capacitor is still recharging!</span>"
possible_names = list("Judge", "Guard", "Servant", "Smith", "Auger")
autoping = FALSE
resistance_flags = FIRE_PROOF | ACID_PROOF
force_replace_ai_name = TRUE
overrides_aicore_laws = TRUE
/obj/item/device/mmi/posibrain/soul_vessel/Initialize()
. = ..()
radio.on = FALSE
laws = new /datum/ai_laws/ratvar()
braintype = picked_name
GLOB.all_clockwork_objects += src
/obj/item/device/mmi/posibrain/soul_vessel/Destroy()
GLOB.all_clockwork_objects -= src
return ..()
/obj/item/device/mmi/posibrain/soul_vessel/examine(mob/user)
if((is_servant_of_ratvar(user) || isobserver(user)) && clockwork_desc)
desc = clockwork_desc
..()
desc = initial(desc)
/obj/item/device/mmi/posibrain/soul_vessel/transfer_personality(mob/candidate)
. = ..()
if(.)
add_servant_of_ratvar(brainmob, TRUE)
/obj/item/device/mmi/posibrain/soul_vessel/attack_self(mob/living/user)
if(!is_servant_of_ratvar(user))
to_chat(user, "<span class='warning'>You fiddle around with [src], to no avail.</span>")
return FALSE
..()
/obj/item/device/mmi/posibrain/soul_vessel/attack(mob/living/target, mob/living/carbon/human/user)
if(!is_servant_of_ratvar(user) || !ishuman(target))
..()
return
if(QDELETED(brainmob))
return
if(brainmob.key)
to_chat(user, "<span class='nezbere'>\"This vessel is filled, friend. Provide it with a body.\"</span>")
return
if(is_servant_of_ratvar(target))
to_chat(user, "<span class='nezbere'>\"It would be more wise to revive your allies, friend.\"</span>")
return
var/mob/living/carbon/human/H = target
if(H.stat == CONSCIOUS)
to_chat(user, "<span class='warning'>[H] must be dead or unconscious for you to claim [H.p_their()] mind!</span>")
return
if(H.head)
var/obj/item/I = H.head
if(I.flags_inv & HIDEHAIR) //they're wearing a hat that covers their skull
to_chat(user, "<span class='warning'>[H]'s head is covered, remove [H.head] first!</span>")
return
if(H.wear_mask)
var/obj/item/I = H.wear_mask
if(I.flags_inv & HIDEHAIR) //they're wearing a mask that covers their skull
to_chat(user, "<span class='warning'>[H]'s head is covered, remove [H.wear_mask] first!</span>")
return
var/obj/item/bodypart/head/HE = H.get_bodypart("head")
if(!HE) //literally headless
to_chat(user, "<span class='warning'>[H] has no head, and thus no mind to claim!</span>")
return
var/obj/item/organ/brain/B = H.getorgan(/obj/item/organ/brain)
if(!B) //either somebody already got to them or robotics did
to_chat(user, "<span class='warning'>[H] has no brain, and thus no mind to claim!</span>")
return
if(!H.key) //nobody's home
to_chat(user, "<span class='warning'>[H] has no mind to claim!</span>")
return
playsound(H, 'sound/misc/splort.ogg', 60, 1, -1)
playsound(H, 'sound/magic/clockwork/anima_fragment_attack.ogg', 40, 1, -1)
var/prev_fakedeath = (H.status_flags & FAKEDEATH)
H.status_flags |= FAKEDEATH //we want to make sure they don't deathgasp and maybe possibly explode
H.death()
if(!prev_fakedeath)
H.status_flags &= ~FAKEDEATH
H.apply_status_effect(STATUS_EFFECT_SIGILMARK) //let them be affected by vitality matrices
picked_name = "Slave"
braintype = picked_name
brainmob.timeofhostdeath = H.timeofdeath
user.visible_message("<span class='warning'>[user] presses [src] to [H]'s head, ripping through the skull and carefully extracting the brain!</span>", \
"<span class='brass'>You extract [H]'s consciousness from [H.p_their()] body, trapping it in the soul vessel.</span>")
transfer_personality(H)
brainmob.fully_replace_character_name(null, "[braintype] [H.real_name]")
name = "[initial(name)] ([brainmob.name])"
B.Remove(H)
qdel(B)
H.update_hair()
@@ -1,172 +0,0 @@
//Wraith spectacles: Grants x-ray and night vision at the eventual cost of the wearer's sight if worn too long. Nar-Sian cultists are instantly blinded.
/obj/item/clothing/glasses/wraith_spectacles
name = "antique spectacles"
desc = "Unnerving glasses with opaque yellow lenses."
icon = 'icons/obj/clothing/clockwork_garb.dmi'
icon_state = "wraith_specs"
item_state = "glasses"
actions_types = list(/datum/action/item_action/toggle)
resistance_flags = FIRE_PROOF | ACID_PROOF
flags_cover = GLASSESCOVERSEYES
visor_flags_inv = HIDEEYES
visor_vars_to_toggle = NONE //we don't actually toggle anything we just set it
tint = 3 //this'll get reset, but it won't handle vision updates properly otherwise
/obj/item/clothing/glasses/wraith_spectacles/Initialize()
. = ..()
GLOB.all_clockwork_objects += src
/obj/item/clothing/glasses/wraith_spectacles/Destroy()
GLOB.all_clockwork_objects -= src
return ..()
/obj/item/clothing/glasses/wraith_spectacles/attack_self(mob/user)
weldingvisortoggle(user)
/obj/item/clothing/glasses/wraith_spectacles/visor_toggling()
..()
set_vision_vars(FALSE)
/obj/item/clothing/glasses/wraith_spectacles/weldingvisortoggle(mob/user)
. = ..()
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
if(src == H.glasses && !up)
if(H.has_disability(DISABILITY_BLIND))
to_chat(H, "<span class='heavy_brass'>\"You're blind, idiot. Stop embarrassing yourself.\"</span>")
return
if(blind_cultist(H))
return
if(is_servant_of_ratvar(H))
to_chat(H, "<span class='heavy_brass'>You push the spectacles down, and all is revealed to you.[GLOB.ratvar_awakens ? "" : " Your eyes begin to itch - you cannot do this for long."]</span>")
var/datum/status_effect/wraith_spectacles/WS = H.has_status_effect(STATUS_EFFECT_WRAITHSPECS)
if(WS)
WS.apply_eye_damage(H)
H.apply_status_effect(STATUS_EFFECT_WRAITHSPECS)
else
to_chat(H, "<span class='heavy_brass'>You push the spectacles down, but you can't see through the glass.</span>")
/obj/item/clothing/glasses/wraith_spectacles/proc/blind_cultist(mob/living/victim)
if(iscultist(victim))
to_chat(victim, "<span class='heavy_brass'>\"It looks like Nar-Sie's dogs really don't value their eyes.\"</span>")
to_chat(victim, "<span class='userdanger'>Your eyes explode with horrific pain!</span>")
victim.emote("scream")
victim.become_blind(EYE_DAMAGE)
victim.adjust_blurriness(30)
victim.adjust_blindness(30)
return TRUE
/obj/item/clothing/glasses/wraith_spectacles/proc/set_vision_vars(update_vision)
lighting_alpha = null
tint = 0
vision_flags = NONE
darkness_view = 2
if(!up)
if(is_servant_of_ratvar(loc))
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
vision_flags = SEE_MOBS | SEE_TURFS | SEE_OBJS
darkness_view = 3
else
tint = 3
if(update_vision && iscarbon(loc))
var/mob/living/carbon/C = loc
C.head_update(src, forced = 1)
/obj/item/clothing/glasses/wraith_spectacles/equipped(mob/living/user, slot)
..()
if(slot != slot_glasses || up)
return
if(user.has_disability(DISABILITY_BLIND))
to_chat(user, "<span class='heavy_brass'>\"You're blind, idiot. Stop embarrassing yourself.\"</span>" )
return
if(blind_cultist(user)) //Cultists instantly go blind
return
set_vision_vars(TRUE)
if(is_servant_of_ratvar(user))
to_chat(user, "<span class='heavy_brass'>As you put on the spectacles, all is revealed to you.[GLOB.ratvar_awakens || GLOB.ratvar_approaches ? "" : " Your eyes begin to itch - you cannot do this for long."]</span>")
var/datum/status_effect/wraith_spectacles/WS = user.has_status_effect(STATUS_EFFECT_WRAITHSPECS)
if(WS)
WS.apply_eye_damage(user)
user.apply_status_effect(STATUS_EFFECT_WRAITHSPECS)
else
to_chat(user, "<span class='heavy_brass'>You put on the spectacles, but you can't see through the glass.</span>")
//The effect that causes/repairs the damage the spectacles cause.
/datum/status_effect/wraith_spectacles
id = "wraith_spectacles"
duration = -1 //remains until eye damage done reaches 0 while the glasses are not worn
tick_interval = 20
alert_type = /obj/screen/alert/status_effect/wraith_spectacles
var/eye_damage_done = 0
var/nearsight_breakpoint = 30
var/blind_breakpoint = 45
/obj/screen/alert/status_effect/wraith_spectacles
name = "Wraith Spectacles"
desc = "You shouldn't actually see this, as it should be procedurally generated."
icon_state = "wraithspecs"
alerttooltipstyle = "clockcult"
/obj/screen/alert/status_effect/wraith_spectacles/MouseEntered(location,control,params)
var/mob/living/carbon/human/L = usr
if(istype(L)) //this is probably more safety than actually needed
var/datum/status_effect/wraith_spectacles/W = attached_effect
var/glasses_right = istype(L.glasses, /obj/item/clothing/glasses/wraith_spectacles)
var/obj/item/clothing/glasses/wraith_spectacles/WS = L.glasses
desc = "[glasses_right && !WS.up ? "<font color=#DAAA18><b>":""]You are [glasses_right ? "":"not "]wearing wraith spectacles[glasses_right && !WS.up ? "!</b></font>":"."]<br>\
You have taken <font color=#DAAA18><b>[W.eye_damage_done]</b></font> eye damage from them.<br>"
if(L.has_disability(DISABILITY_NEARSIGHT))
desc += "<font color=#DAAA18><b>You are nearsighted!</b></font><br>"
else if(glasses_right && !WS.up)
desc += "You will become nearsighted at <font color=#DAAA18><b>[W.nearsight_breakpoint]</b></font> eye damage.<br>"
if(L.has_disability(DISABILITY_BLIND))
desc += "<font color=#DAAA18><b>You are blind!</b></font>"
else if(glasses_right && !WS.up)
desc += "You will become blind at <font color=#DAAA18><b>[W.blind_breakpoint]</b></font> eye damage."
..()
/datum/status_effect/wraith_spectacles/on_apply()
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
apply_eye_damage(H)
return ..()
/datum/status_effect/wraith_spectacles/tick()
if(!ishuman(owner))
qdel(src)
return
var/mob/living/carbon/human/H = owner
var/glasses_right = istype(H.glasses, /obj/item/clothing/glasses/wraith_spectacles)
var/obj/item/clothing/glasses/wraith_spectacles/WS = H.glasses
if(glasses_right && !WS.up && !GLOB.ratvar_awakens && !GLOB.ratvar_approaches)
apply_eye_damage(H)
else
if(GLOB.ratvar_awakens)
H.cure_nearsighted(list(EYE_DAMAGE))
H.cure_blind(list(EYE_DAMAGE))
H.adjust_eye_damage(-eye_damage_done)
eye_damage_done = 0
else if(prob(50) && eye_damage_done)
H.adjust_eye_damage(-1)
eye_damage_done = max(0, eye_damage_done - 1)
if(!eye_damage_done)
qdel(src)
/datum/status_effect/wraith_spectacles/proc/apply_eye_damage(mob/living/carbon/human/H)
if(H.has_disability(DISABILITY_BLIND))
return
H.adjust_eye_damage(0.5)
eye_damage_done += 0.5
if(eye_damage_done >= 20)
H.adjust_blurriness(2)
if(eye_damage_done >= nearsight_breakpoint)
if(!H.has_disability(DISABILITY_NEARSIGHT))
to_chat(H, "<span class='nzcrentr'>Your vision doubles, then trembles. Darkness begins to close in. You can't keep this up!</span>")
H.become_nearsighted(EYE_DAMAGE)
if(eye_damage_done >= blind_breakpoint)
if(!H.has_disability(DISABILITY_BLIND))
to_chat(H, "<span class='nzcrentr_large'>A piercing white light floods your vision. Suddenly, all goes dark!</span>")
H.become_blind(EYE_DAMAGE)
if(prob(min(20, 5 + eye_damage_done)))
to_chat(H, "<span class='nzcrentr_small'><i>Your eyes continue to burn.</i></span>")
@@ -1,67 +0,0 @@
//The base for clockwork mobs
/mob/living/simple_animal/hostile/clockwork
faction = list("neutral", "ratvar")
gender = NEUTER
icon = 'icons/mob/clockwork_mobs.dmi'
unique_name = 1
minbodytemp = 0
unsuitable_atmos_damage = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) //Robotic
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
healable = FALSE
del_on_death = TRUE
speak_emote = list("clanks", "clinks", "clunks", "clangs")
verb_ask = "requests"
verb_exclaim = "proclaims"
verb_whisper = "imparts"
verb_yell = "harangues"
initial_language_holder = /datum/language_holder/clockmob
bubble_icon = "clock"
light_color = "#E42742"
death_sound = 'sound/magic/clockwork/anima_fragment_death.ogg'
var/playstyle_string = "<span class='heavy_brass'>You are a bug, yell at whoever spawned you!</span>"
var/empower_string = "<span class='heavy_brass'>You have nothing to empower, yell at the coders!</span>" //Shown to the mob when the herald beacon activates
/mob/living/simple_animal/hostile/clockwork/Initialize()
. = ..()
update_values()
/mob/living/simple_animal/hostile/clockwork/get_spans()
return ..() | SPAN_ROBOT
/mob/living/simple_animal/hostile/clockwork/Login()
..()
add_servant_of_ratvar(src, TRUE)
to_chat(src, playstyle_string)
if(GLOB.ratvar_approaches)
to_chat(src, empower_string)
/mob/living/simple_animal/hostile/clockwork/ratvar_act()
fully_heal(TRUE)
/mob/living/simple_animal/hostile/clockwork/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
return 0 //ouch, my metal-unlikely-to-be-damaged-by-electricity-body
/mob/living/simple_animal/hostile/clockwork/examine(mob/user)
var/t_He = p_they(TRUE)
var/t_s = p_s()
var/msg = "<span class='brass'>*---------*\nThis is [icon2html(src, user)] \a <b>[src]</b>!\n"
msg += "[desc]\n"
if(health < maxHealth)
msg += "<span class='warning'>"
if(health >= maxHealth/2)
msg += "[t_He] look[t_s] slightly dented.\n"
else
msg += "<b>[t_He] look[t_s] severely dented!</b>\n"
msg += "</span>"
var/addendum = examine_info()
if(addendum)
msg += "[addendum]\n"
msg += "*---------*</span>"
to_chat(user, msg)
/mob/living/simple_animal/hostile/clockwork/proc/examine_info() //Override this on a by-mob basis to have unique examine info
return
/mob/living/simple_animal/hostile/clockwork/proc/update_values() //This is called by certain things to check GLOB.ratvar_awakens and GLOB.ratvar_approaches
@@ -1,297 +0,0 @@
//Helper proc to get an Eminence mob if it exists
/proc/get_eminence()
return locate(/mob/camera/eminence) in servants_and_ghosts()
//The Eminence is a unique mob that functions like the leader of the cult. It's incorporeal but can interact with the world in several ways.
/mob/camera/eminence
name = "\the Emininence"
real_name = "\the Eminence"
desc = "The leader-elect of the servants of Ratvar."
icon = 'icons/effects/clockwork_effects.dmi'
icon_state = "eminence"
mouse_opacity = MOUSE_OPACITY_OPAQUE
move_on_shuttle = TRUE
see_in_dark = 8
invisibility = INVISIBILITY_OBSERVER
layer = FLY_LAYER
faction = list("ratvar")
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
var/turf/last_failed_turf
var/static/superheated_walls = 0
var/lastWarning = 0
/mob/camera/eminence/CanPass(atom/movable/mover, turf/target)
return TRUE
/mob/camera/eminence/Move(NewLoc, direct)
var/OldLoc = loc
if(NewLoc && !istype(NewLoc, /turf/open/indestructible/reebe_void))
var/turf/T = get_turf(NewLoc)
if (locate(/obj/effect/blessing, T))
if(last_failed_turf != T)
T.visible_message("<span class='warning'>[T] suddenly emits a ringing sound!</span>", null, null, null, src)
playsound(T, 'sound/machines/clockcult/ark_damage.ogg', 75, FALSE)
last_failed_turf = T
if ((world.time - lastWarning) >= 30)
lastWarning = world.time
to_chat(src, "<span class='warning'>This turf is consecrated and can't be crossed!</span>")
return
if(!GLOB.ratvar_awakens && istype(get_area(T), /area/chapel))
if ((world.time - lastWarning) >= 30)
lastWarning = world.time
to_chat(src, "<span class='warning'>The Chapel is hallowed ground under a heretical deity, and can't be accessed!</span>")
return
forceMove(T)
Moved(OldLoc, direct)
if(GLOB.ratvar_awakens)
for(var/turf/T in range(5, src))
if(prob(166 - (get_dist(src, T) * 33)))
T.ratvar_act() //Causes moving to leave a swath of proselytized area behind the Eminence
/mob/camera/eminence/Process_Spacemove(movement_dir = 0)
return TRUE
/mob/camera/eminence/Login()
..()
add_servant_of_ratvar(src, TRUE)
var/datum/antagonist/clockcult/C = mind.has_antag_datum(/datum/antagonist/clockcult,TRUE)
if(C && C.clock_team)
if(C.clock_team.eminence && C.clock_team.eminence != src)
remove_servant_of_ratvar(src,TRUE)
qdel(src)
return
else
C.clock_team.eminence = src
to_chat(src, "<span class='bold large_brass'>You have been selected as the Eminence!</span>")
to_chat(src, "<span class='brass'>As the Eminence, you lead the servants. Anything you say will be heard by the entire cult.</span>")
to_chat(src, "<span class='brass'>Though you can move through walls, you're also incorporeal, and largely can't interact with the world except for a few ways.</span>")
to_chat(src, "<span class='brass'>Additionally, unless the herald's beacon is activated, you can't understand any speech while away from Reebe.</span>")
eminence_help()
for(var/V in actions)
var/datum/action/A = V
A.Remove(src) //So we get rid of duplicate actions; this also removes Hierophant network, since our say() goes across it anyway
var/datum/action/innate/eminence/E
for(var/V in subtypesof(/datum/action/innate/eminence))
E = new V
E.Grant(src)
/mob/camera/eminence/say(message)
if(client)
if(client.prefs.muted & MUTE_IC)
to_chat(src, "You cannot send IC messages (muted).")
return
if(client.handle_spam_prevention(message,MUTE_IC))
return
message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
if(!message)
return
log_talk(src, "[key_name(src)] : [message]", LOGSAY)
if(GLOB.ratvar_awakens)
visible_message("<span class='brass'><b>You feel light slam into your mind and form words:</b> \"[capitalize(message)]\"</span>")
playsound(src, 'sound/machines/clockcult/ark_scream.ogg', 50, FALSE)
message = "<span class='big brass'><b>The [GLOB.ratvar_awakens ? "Radiance" : "Eminence"]:</b> \"[message]\"</span>"
for(var/mob/M in servants_and_ghosts())
if(isobserver(M))
var/link = FOLLOW_LINK(M, src)
to_chat(M, "[link] [message]")
else
to_chat(M, message)
/mob/camera/eminence/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode)
if(is_reebe(z) || is_servant_of_ratvar(speaker) || GLOB.ratvar_approaches || GLOB.ratvar_awakens) //Away from Reebe, the Eminence can't hear anything
to_chat(src, message)
return
to_chat(src, "<i>[speaker] says something, but you can't understand any of it...</i>")
/mob/camera/eminence/ClickOn(atom/A, params)
var/list/modifiers = params2list(params)
if(modifiers["shift"])
A.examine(src)
return
if(modifiers["alt"] && istype(A, /turf/closed/wall/clockwork))
superheat_wall(A)
return
if(modifiers["middle"] || modifiers["ctrl"])
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
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/ratvar_act()
name = "\improper Radiance"
real_name = "\improper Radiance"
desc = "The light, forgotten."
transform = matrix() * 2
invisibility = SEE_INVISIBLE_MINIMUM
/mob/camera/eminence/proc/issue_command(atom/movable/A)
var/list/commands
var/atom/movable/command_location
if(A == src)
commands = list("Defend the Ark!", "Advance!", "Retreat!", "Generate Power", "Build Defenses (Bottom-Up)", "Build Defenses (Top-Down)")
else
command_location = A
commands = list("Rally Here", "Regroup Here", "Avoid This Area", "Reinforce This Area")
if(istype(A, /obj/structure/destructible/clockwork/powered))
var/obj/structure/destructible/clockwork/powered/P = A
if(!can_access_clockwork_power(P))
commands += "Power This Structure"
if(P.obj_integrity < P.max_integrity)
commands += "Repair This Structure"
var/roma_invicta = input(src, "Choose a command to issue to your cult!", "Issue Commands") as null|anything in commands
if(!roma_invicta)
return
var/command_text = ""
var/marker_icon
switch(roma_invicta)
if("Rally Here")
command_text = "The Eminence orders an offensive rally at [command_location] to the GETDIR!"
marker_icon = "eminence_rally"
if("Regroup Here")
command_text = "The Eminence orders a regroup to [command_location] to the GETDIR!"
marker_icon = "eminence_rally"
if("Avoid This Area")
command_text = "The Eminence has designated the area to your GETDIR as dangerous and to be avoided!"
marker_icon = "eminence_avoid"
if("Reinforce This Area")
command_text = "The Eminence orders the defense and fortification of the area to your GETDIR!"
marker_icon = "eminence_reinforce"
if("Power This Structure")
command_text = "[command_location] to your GETDIR has no power! Turn it on and make sure there's a sigil of transmission nearby!"
marker_icon = "eminence_unlimited_power"
if("Repair This Structure")
command_text = "The Eminence orders that [command_location] to your GETDIR should be repaired ASAP!"
marker_icon = "eminence_repair"
if("Defend the Ark!")
command_text = "The Eminence orders immediate defense of the Ark!"
if("Advance!")
command_text = "The Eminence commands you push forward!"
if("Retreat!")
command_text = "The Eminence has sounded the retreat! Fall back!"
if("Generate Power")
command_text = "The Eminence orders more power! Build power generations on the station!"
if("Build Defenses (Bottom-Up)")
command_text = "The Eminence orders that defenses should be built starting from the bottom of Reebe!"
if("Build Defenses (Top-Down)")
command_text = "The Eminence orders that defenses should be built starting from the top of Reebe!"
if(marker_icon)
new/obj/effect/temp_visual/ratvar/command_point(get_turf(A), marker_icon)
for(var/mob/M in servants_and_ghosts())
to_chat(M, "<span class='large_brass'>[replacetext(command_text, "GETDIR", dir2text(get_dir(M, command_location)))]</span>")
M.playsound_local(M, 'sound/machines/clockcult/eminence_command.ogg', 75, FALSE, pressure_affected = FALSE)
else
hierophant_message("<span class='bold large_brass'>[command_text]</span>")
for(var/mob/M in servants_and_ghosts())
M.playsound_local(M, 'sound/machines/clockcult/eminence_command.ogg', 75, FALSE, pressure_affected = FALSE)
/mob/camera/eminence/proc/superheat_wall(turf/closed/wall/clockwork/wall)
if(!istype(wall))
return
if(superheated_walls >= SUPERHEATED_CLOCKWORK_WALL_LIMIT && !wall.heated)
to_chat(src, "<span class='warning'>You're exerting all of your power superheating this many walls already! Cool some down first!</span>")
return
wall.turn_up_the_heat()
if(wall.heated)
superheated_walls++
to_chat(src, "<span class='neovgre_small'>You superheat [wall]. <b>Superheated walls:</b> [superheated_walls]/[SUPERHEATED_CLOCKWORK_WALL_LIMIT]")
else
superheated_walls--
to_chat(src, "<span class='neovgre_small'>You cool [wall]. <b>Superheated walls:</b> [superheated_walls]/[SUPERHEATED_CLOCKWORK_WALL_LIMIT]")
/mob/camera/eminence/proc/eminence_help()
to_chat(src, "<span class='bold alloy'>You can make use of certain shortcuts to perform different actions:</span>")
to_chat(src, "<span class='alloy'><b>Alt-Click a clockwork wall</b> to superheat or cool it down. \
Superheated walls can't be destroyed by hulks or mechs and are much slower to deconstruct, and are marked by a bright red glow. \
This lasts indefinitely, but only [SUPERHEATED_CLOCKWORK_WALL_LIMIT] clockwork walls can be superheated at once.</span>")
to_chat(src, "<span class='alloy'><b>Interact with the Ark</b> to initiate an emergency recall that teleports all servants directly to its location after a short delay. \
This can only be used a single time, or twice if the herald's beacon was activated,</span>")
to_chat(src, "<span class='alloy'><b>Middle or Ctrl-Click anywhere</b> to allow you to issue a variety of contextual commands to your cult. Different objects allow for different \
commands. <i>Doing this on yourself will provide commands that tell the entire cult a goal.</i></span>")
//Eminence actions below this point
/datum/action/innate/eminence
name = "Eminence Action"
desc = "You shouldn't see this. File a bug report!"
icon_icon = 'icons/mob/actions/actions_clockcult.dmi'
background_icon_state = "bg_clock"
buttontooltipstyle = "clockcult"
/datum/action/innate/eminence/IsAvailable()
if(!iseminence(owner))
qdel(src)
return
return ..()
//Lists available powers
/datum/action/innate/eminence/power_list
name = "Eminence Powers"
desc = "Forgot what you can do? This refreshes you on your powers as Eminence."
button_icon_state = "eminence_rally"
/datum/action/innate/eminence/power_list/Activate()
var/mob/camera/eminence/E = owner
E.eminence_help()
//Returns to the Ark
/datum/action/innate/eminence/ark_jump
name = "Return to Ark"
desc = "Warps you to the Ark."
button_icon_state = "Abscond"
/datum/action/innate/eminence/ark_jump/Activate()
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
if(G)
owner.forceMove(get_turf(G))
owner.playsound_local(owner, 'sound/magic/magic_missile.ogg', 50, TRUE)
flash_color(owner, flash_color = "#AF0AAF", flash_time = 25)
else
to_chat(owner, "<span class='warning'>There is no Ark!</span>")
//Warps to the Station
/datum/action/innate/eminence/station_jump
name = "Warp to Station"
desc = "Warps to Space Station 13. You cannot hear anything while there!</span>"
button_icon_state = "warp_down"
/datum/action/innate/eminence/station_jump/Activate()
if(is_reebe(owner.z))
owner.forceMove(get_turf(pick(GLOB.generic_event_spawns)))
owner.playsound_local(owner, 'sound/magic/magic_missile.ogg', 50, TRUE)
flash_color(owner, flash_color = "#AF0AAF", flash_time = 25)
else
to_chat(owner, "<span class='warning'>You're already on the station!</span>")
//A quick-use button for recalling the servants to the Ark
/datum/action/innate/eminence/mass_recall
name = "Mass Recall"
desc = "Initiates a mass recall, warping all servants to the Ark after a short delay. This can only be used once."
button_icon_state = "Spatial Gateway"
/datum/action/innate/eminence/mass_recall/IsAvailable()
. = ..()
if(.)
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
if(G)
return G.recalls_remaining && !G.recalling
return FALSE
/datum/action/innate/eminence/mass_recall/Activate()
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
if(G && !G.recalling && G.recalls_remaining)
if(alert(owner, "Initiate mass recall?", "Mass Recall", "Yes", "No") != "Yes" || QDELETED(owner) || QDELETED(G) || !G.obj_integrity)
return
G.initiate_mass_recall()
@@ -1,93 +0,0 @@
//Anima fragment: Low health and high melee damage, but slows down when struck. Created by inserting a soul vessel into an empty fragment.
/mob/living/simple_animal/hostile/clockwork/fragment
name = "anima fragment"
desc = "An ominous humanoid shell with a spinning cogwheel as its head, lifted by a jet of blazing red flame."
icon_state = "anime_fragment"
health = 90
maxHealth = 90
speed = -1
melee_damage_lower = 18
melee_damage_upper = 18
attacktext = "crushes"
attack_sound = 'sound/magic/clockwork/anima_fragment_attack.ogg'
loot = list(/obj/item/clockwork/component/replicant_alloy/smashed_anima_fragment)
weather_immunities = list("lava")
movement_type = FLYING
light_range = 2
light_power = 0.8
playstyle_string = "<span class='heavy_brass'>You are an anima fragment</span><b>, a clockwork creation of Ratvar. As a fragment, you have decent health that very gradually regenerates, do \
decent damage, and move at extreme speed in addition to being immune to extreme temperatures and pressures. Taking damage, and slamming into non-Servants, will temporarily slow you down, however.\n\
Your goal is to serve the Justiciar and his servants in any way you can. You yourself are one of these servants, and will be able to utilize anything they can, assuming it doesn't require \
opposable thumbs.</b>"
var/movement_delay_time //how long the fragment is slowed after being hit
/mob/living/simple_animal/hostile/clockwork/fragment/Initialize()
. = ..()
if(prob(1))
name = "anime fragment"
desc = "I-it's not like I want to show you the light of the Justiciar or anything, B-BAKA!"
/mob/living/simple_animal/hostile/clockwork/fragment/Life()
..()
if(GLOB.ratvar_awakens)
adjustHealth(-5)
else if(movement_delay_time > world.time)
adjustHealth(-0.2)
else
adjustHealth(-1)
/mob/living/simple_animal/hostile/clockwork/fragment/Stat()
..()
if(statpanel("Status") && movement_delay_time > world.time && !GLOB.ratvar_awakens)
stat(null, "Movement delay(seconds): [max(round((movement_delay_time - world.time)*0.1, 0.1), 0)]")
/mob/living/simple_animal/hostile/clockwork/fragment/death(gibbed)
visible_message("<span class='warning'>[src]'s flame jets cut out as it falls to the floor with a tremendous crash.</span>", \
"<span class='userdanger'>Your gears seize up. Your flame jets flicker out. Your soul vessel belches smoke as you helplessly crash down.</span>")
..()
/mob/living/simple_animal/hostile/clockwork/fragment/Process_Spacemove(movement_dir = 0)
return 1
/mob/living/simple_animal/hostile/clockwork/fragment/Collide(atom/movable/AM)
. = ..()
if(movement_delay_time <= world.time && next_move <= world.time && isliving(AM) && !is_servant_of_ratvar(AM))
var/mob/living/L = AM
if(L.stat) //we don't want to attack them if they're unconscious or dead because that feels REALLY BAD for the player
return
var/previousattacktext = attacktext
attacktext = "slams into"
UnarmedAttack(L)
attacktext = previousattacktext
changeNext_move(CLICK_CD_MELEE)
if(!GLOB.ratvar_awakens)
adjustHealth(4)
adjust_movement_delay(10) //with the above damage, total of 20 movement delay plus speed = 0 due to damage
/mob/living/simple_animal/hostile/clockwork/fragment/emp_act(severity)
adjust_movement_delay(50/severity)
/mob/living/simple_animal/hostile/clockwork/fragment/movement_delay()
. = ..()
if(movement_delay_time > world.time && !GLOB.ratvar_awakens)
. += min((movement_delay_time - world.time) * 0.1, 10) //the more delay we have, the slower we go
/mob/living/simple_animal/hostile/clockwork/fragment/adjustHealth(amount)
. = ..()
if(amount > 0)
adjust_movement_delay(amount*2.5)
/mob/living/simple_animal/hostile/clockwork/fragment/proc/adjust_movement_delay(amount)
if(GLOB.ratvar_awakens) //if ratvar is up we ignore movement delay
movement_delay_time = 0
else if(movement_delay_time > world.time)
movement_delay_time = movement_delay_time + amount
else
movement_delay_time = world.time + amount
/mob/living/simple_animal/hostile/clockwork/fragment/updatehealth()
..()
if(health == maxHealth)
speed = initial(speed)
else
speed = 0 //slow down if damaged at all
@@ -1,105 +0,0 @@
#define MARAUDER_SLOWDOWN_PERCENTAGE 0.40 //Below this percentage of health, marauders will become slower
#define MARAUDER_SHIELD_REGEN_TIME 200 //In deciseconds, how long it takes for shields to regenerate after breaking
//Clockwork marauder: A well-rounded frontline construct. Only one can exist for every two human servants.
/mob/living/simple_animal/hostile/clockwork/marauder
name = "clockwork marauder"
desc = "The stalwart apparition of a soldier, blazing with crimson flames. It's armed with a gladius and shield."
icon_state = "clockwork_marauder"
health = 120
maxHealth = 120
force_threshold = 8
speed = 0
obj_damage = 40
melee_damage_lower = 12
melee_damage_upper = 12
attacktext = "slashes"
attack_sound = 'sound/weapons/bladeslice.ogg'
weather_immunities = list("lava")
movement_type = FLYING
a_intent = INTENT_HARM
loot = list(/obj/item/clockwork/component/geis_capacitor/fallen_armor)
light_range = 2
light_power = 1.1
playstyle_string = "<span class='big bold'><span class='neovgre'>You are a clockwork marauder,</span></span><b> a well-rounded frontline construct of Ratvar. Although you have no \
unique abilities, you're a fearsome fighter in one-on-one combat, and your shield protects from projectiles!<br><br>Obey the Servants and do as they \
tell you. Your primary goal is to defend the Ark from destruction; they are your allies in this, and should be protected from harm.</b>"
empower_string = "<span class='neovgre'>The Anima Bulwark's power flows through you! Your weapon will strike harder, your armor is sturdier, and your shield is more durable.</span>"
var/max_shield_health = 3
var/shield_health = 3 //Amount of projectiles that can be deflected within
var/shield_health_regen = 0 //When world.time equals this, shield health will regenerate
/mob/living/simple_animal/hostile/clockwork/marauder/examine_info()
if(!shield_health)
return "<span class='warning'>Its shield has been destroyed!</span>"
/mob/living/simple_animal/hostile/clockwork/marauder/Life()
..()
if(!GLOB.ratvar_awakens && health / maxHealth <= MARAUDER_SLOWDOWN_PERCENTAGE)
speed = initial(speed) + 1 //Yes, this slows them down
else
speed = initial(speed)
if(shield_health < max_shield_health && world.time >= shield_health_regen)
shield_health_regen = world.time + MARAUDER_SHIELD_REGEN_TIME
to_chat(src, "<span class='neovgre'>Your shield has recovered, <b>[shield_health]</b> blocks remaining!</span>")
playsound_local(src, "shatter", 75, TRUE, frequency = -1)
shield_health++
/mob/living/simple_animal/hostile/clockwork/marauder/update_values()
if(GLOB.ratvar_awakens) //Massive attack damage bonuses and health increase, because Ratvar
health = 300
maxHealth = 300
melee_damage_upper = 25
melee_damage_lower = 25
attacktext = "devastates"
speed = -1
obj_damage = 100
max_shield_health = INFINITY
else if(GLOB.ratvar_approaches) //Hefty health bonus and slight attack damage increase
melee_damage_upper = 15
melee_damage_lower = 15
attacktext = "carves"
obj_damage = 50
max_shield_health = 4
/mob/living/simple_animal/hostile/clockwork/marauder/death(gibbed)
visible_message("<span class='danger'>[src]'s equipment clatters lifelessly to the ground as the red flames within dissipate.</span>", \
"<span class='userdanger'>Dented and scratched, your armor falls away, and your fragile form breaks apart without its protection.</span>")
. = ..()
/mob/living/simple_animal/hostile/clockwork/marauder/Process_Spacemove(movement_dir = 0)
return TRUE
/mob/living/simple_animal/hostile/clockwork/marauder/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
if(amount > 0)
for(var/mob/living/L in view(2, src))
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
. = ..()
/mob/living/simple_animal/hostile/clockwork/marauder/bullet_act(obj/item/projectile/P)
if(deflect_projectile(P))
return
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/proc/deflect_projectile(obj/item/projectile/P)
if(!shield_health)
return
var/energy_projectile = istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)
visible_message("<span class='danger'>[src] deflects [P] with their shield!</span>", \
"<span class='danger'>You block [P] with your shield! <i>Blocks left:</i> <b>[shield_health - 1]</b></span>")
if(energy_projectile)
playsound(src, 'sound/weapons/effects/searwall.ogg', 50, TRUE)
else
playsound(src, "ricochet", 50, TRUE)
shield_health--
if(!shield_health)
visible_message("<span class='warning'>[src]'s shield breaks from deflecting the attack!</span>", "<span class='boldwarning'>Your shield breaks! Give it some time to recover...</span>")
playsound(src, "shatter", 100, TRUE)
shield_health_regen = world.time + MARAUDER_SHIELD_REGEN_TIME
return TRUE
#undef MARAUDER_SLOWDOWN_PERCENTAGE
#undef MARAUDER_SHIELD_REGEN_TIME
@@ -1,339 +0,0 @@
/*
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
*/
/datum/clockwork_scripture
var/descname = "useless" //a simple name for the scripture's effect
var/name = "scripture"
var/desc = "Ancient Ratvarian lore. This piece seems particularly mundane."
var/list/invocations = list() //Spoken over time in the ancient language of Ratvar. See clock_unsorted.dm for more details on the language and how to make it.
var/chanting = FALSE //If the invocation's words are being spoken
var/channel_time = 10 //In deciseconds, how long a ritual takes to chant
var/power_cost = 5 //In watts, how much a scripture takes to invoke
var/special_power_text //If the scripture can use additional power to have a unique function, use this; put POWERCOST here to display the special power cost.
var/special_power_cost //This should be a define in __DEFINES/clockcult.dm, such as ABSCOND_ABDUCTION_COST
var/obj/item/clockwork/slab/slab //The parent clockwork slab
var/mob/living/invoker //The slab's holder
var/whispered = FALSE //If the invocation is whispered rather than spoken aloud
var/usage_tip = "This piece seems to serve no purpose and is a waste of components." //A generalized tip that gives advice on a certain scripture
var/invokers_required = 1 //How many people are required, assuming that a scripture requires multiple
var/multiple_invokers_used = FALSE //If scripture requires more than one invoker
var/multiple_invokers_optional = FALSE //If scripture can have multiple invokers to bolster its effects
var/tier = SCRIPTURE_PERIPHERAL //The scripture's tier
var/quickbind = FALSE //if this scripture can be quickbound to a clockwork slab
var/quickbind_desc = "This shouldn't be quickbindable. File a bug report!"
var/primary_component
var/sort_priority = 1 //what position the scripture should have in a list of scripture. Should be based off of component costs/reqs, but you can't initial() lists.
//messages for offstation scripture recital, courtesy ratvar's generals(and neovgre)
var/static/list/neovgre_penalty = list("Go to the station.", "Useless.", "Don't waste time.", "Pathetic.", "Wasteful.")
var/static/list/inathneq_penalty = list("Child, this is too far out!", "The barrier isn't thin enough for for me to help!", "Please, go to the station so I can assist you.", \
"Don't waste my Cogs on this...", "There isn't enough time to linger out here!")
var/static/list/sevtug_penalty = list("Fool! Get to the station and don't waste capacitors.", "You go this far out and expect help?", "The veil is too strong, idiot.", \
"How does the Justicar get anything done with servants like you?", "Oh, you love wasting time, don't you?")
var/static/list/nezbere_penalty = list("You disgrace our master's name with this endeavour.", "This is too far from the station to be a good base.", "This will take too long, friend.", \
"The barrier isn't weakened enough to make this practical.", "Don't waste alloy.")
var/static/list/nzcrentr_penalty = list("You'd be easy to hunt in that little hunk of metal.", "Boss says you need to get back to the beacon.", "Boss says I can kill you if you do this again.", \
"Sending you power is too difficult here.", "Boss says stop wasting time.")
/datum/clockwork_scripture/New()
creation_update()
/datum/clockwork_scripture/proc/creation_update() //updates any on-creation effects
return FALSE //return TRUE if updated
/datum/clockwork_scripture/proc/run_scripture()
var/successful = FALSE
if(can_recite() && has_requirements())
if(slab.busy)
to_chat(invoker, "<span class='warning'>[slab] refuses to work, displaying the message: \"[slab.busy]!\"</span>")
return FALSE
pre_recital()
slab.busy = "Invocation ([name]) in progress"
if(GLOB.ratvar_awakens)
channel_time *= 0.5 //if ratvar has awoken, half channel time and no cost
else if(!slab.no_cost)
adjust_clockwork_power(-power_cost)
channel_time *= slab.speed_multiplier
if(!recital() || !check_special_requirements() || !scripture_effects()) //if we fail any of these, refund components used
adjust_clockwork_power(power_cost)
update_slab_info()
else
successful = TRUE
if(slab && !slab.no_cost && !GLOB.ratvar_awakens) //if the slab exists and isn't debug and ratvar isn't up, log the scripture as being used
SSblackbox.record_feedback("tally", "clockcult_scripture_recited", 1, name)
if(slab)
slab.busy = null
post_recital()
qdel(src)
return successful
/datum/clockwork_scripture/proc/can_recite() //If the words can be spoken
if(!invoker || !slab || invoker.get_active_held_item() != slab)
return FALSE
if(!invoker.can_speak_vocal())
to_chat(invoker, "<span class='warning'>You are unable to speak the words of the scripture!</span>")
return FALSE
return TRUE
/datum/clockwork_scripture/proc/has_requirements() //if we have the power and invokers to do it
var/checked_penalty = FALSE
if(!GLOB.ratvar_awakens && !slab.no_cost)
checked_penalty = check_offstation_penalty()
if(!get_clockwork_power(power_cost))
to_chat(invoker, "<span class='warning'>There isn't enough power to recite this scripture! ([DisplayPower(get_clockwork_power())]/[DisplayPower(power_cost)])</span>")
return
if(multiple_invokers_used && !multiple_invokers_optional && !GLOB.ratvar_awakens && !slab.no_cost)
var/nearby_servants = 0
for(var/mob/living/L in range(1, get_turf(invoker)))
if(can_recite_scripture(L))
nearby_servants++
if(nearby_servants < invokers_required)
to_chat(invoker, "<span class='warning'>There aren't enough non-mute servants nearby ([nearby_servants]/[invokers_required])!</span>")
return FALSE
if(!check_special_requirements())
return FALSE
if(checked_penalty && !slab.busy)
var/message
var/ratvarian_prob = 0
switch(primary_component)
if(BELLIGERENT_EYE)
message = pick(neovgre_penalty)
ratvarian_prob = 55
if(VANGUARD_COGWHEEL)
message = pick(inathneq_penalty)
ratvarian_prob = 25
if(GEIS_CAPACITOR)
message = pick(sevtug_penalty)
ratvarian_prob = 40
if(REPLICANT_ALLOY)
message = pick(nezbere_penalty)
ratvarian_prob = 10
if(HIEROPHANT_ANSIBLE)
message = pick(nzcrentr_penalty)
ratvarian_prob = 70
if(message)
if(prob(ratvarian_prob))
message = text2ratvar(message)
to_chat(invoker, "<span class='[get_component_span(primary_component)]_large'>\"[message]\"</span>")
SEND_SOUND(invoker, sound('sound/magic/clockwork/invoke_general.ogg'))
return TRUE
/datum/clockwork_scripture/proc/check_offstation_penalty()
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
return TRUE
/datum/clockwork_scripture/proc/check_special_requirements() //Special requirements for scriptures, checked multiple times during invocation
return TRUE
/datum/clockwork_scripture/proc/recital() //The process of speaking the words
if(!channel_time && invocations.len)
if(multiple_invokers_used)
for(var/mob/living/L in range(1, invoker))
if(can_recite_scripture(L))
for(var/invocation in invocations)
clockwork_say(L, text2ratvar(invocation), whispered)
else
for(var/invocation in invocations)
clockwork_say(invoker, text2ratvar(invocation), whispered)
to_chat(invoker, "<span class='brass'>You [channel_time <= 0 ? "recite" : "begin reciting"] a piece of scripture entitled \"[name]\".</span>")
if(!channel_time)
return TRUE
chant()
if(!do_after(invoker, channel_time, target = invoker, extra_checks = CALLBACK(src, .proc/check_special_requirements)))
slab.busy = null
chanting = FALSE
scripture_fail()
chanting = FALSE
return
chanting = FALSE
return TRUE
/datum/clockwork_scripture/proc/chant()
set waitfor = FALSE
chanting = TRUE
for(var/invocation in invocations)
sleep(channel_time / invocations.len)
if(QDELETED(src) || QDELETED(slab) || !chanting)
return
if(multiple_invokers_used)
for(var/mob/living/L in range(1, get_turf(invoker)))
if(can_recite_scripture(L))
clockwork_say(L, text2ratvar(invocation), whispered)
else
clockwork_say(invoker, text2ratvar(invocation), whispered)
/datum/clockwork_scripture/proc/scripture_effects() //The actual effects of the recital after its conclusion
/datum/clockwork_scripture/proc/scripture_fail() //Called if the scripture fails to invoke.
/datum/clockwork_scripture/proc/pre_recital() //Called before the scripture is recited
/datum/clockwork_scripture/proc/post_recital() //Called after the scripture is recited
//Channeled scripture begins instantly but runs constantly
/datum/clockwork_scripture/channeled
var/list/chant_invocations = list("AYY LMAO")
var/chant_amount = 5 //Times the chant is spoken
var/chant_interval = 10 //Amount of deciseconds between times the chant is actually spoken aloud
/datum/clockwork_scripture/channeled/check_offstation_penalty()
. = ..()
if(.)
chant_interval *= 2
/datum/clockwork_scripture/channeled/scripture_effects()
for(var/i in 1 to chant_amount)
if(!do_after(invoker, chant_interval, target = invoker, extra_checks = CALLBACK(src, .proc/can_recite)))
break
clockwork_say(invoker, text2ratvar(pick(chant_invocations)), whispered)
if(!chant_effects(i))
break
if(invoker && slab)
chant_end_effects()
return TRUE
/datum/clockwork_scripture/channeled/proc/chant_effects(chant_number) //The chant's periodic effects
/datum/clockwork_scripture/channeled/proc/chant_end_effects() //The chant's effect upon ending
to_chat(invoker, "<span class='brass'>You cease your chant.</span>")
//Creates an object at the invoker's feet
/datum/clockwork_scripture/create_object
var/object_path = /obj/item/clockwork //The path of the object created
var/put_object_in_hands = TRUE
var/creator_message = "<span class='brass'>You create a meme.</span>" //Shown to the invoker
var/observer_message
var/one_per_tile = FALSE
var/prevent_path
var/space_allowed = FALSE
/datum/clockwork_scripture/create_object/New()
..()
if(!prevent_path)
prevent_path = object_path
/datum/clockwork_scripture/create_object/check_special_requirements()
var/turf/T = get_turf(invoker)
if(!space_allowed && isspaceturf(T))
to_chat(invoker, "<span class='warning'>You need solid ground to place this object!</span>")
return FALSE
if(one_per_tile && (locate(prevent_path) in T))
to_chat(invoker, "<span class='warning'>You can only place one of this object on each tile!</span>")
return FALSE
return TRUE
/datum/clockwork_scripture/create_object/scripture_effects()
if(creator_message && observer_message)
invoker.visible_message(observer_message, creator_message)
else if(creator_message)
to_chat(invoker, creator_message)
var/obj/O = new object_path (get_turf(invoker))
O.ratvar_act() //update the new object so it gets buffed if ratvar is alive
if(isitem(O) && put_object_in_hands)
invoker.put_in_hands(O)
return TRUE
//Used specifically to create construct shells.
/datum/clockwork_scripture/create_object/construct
put_object_in_hands = FALSE
var/construct_type //The type of construct that the scripture is made to create, even if not directly
var/construct_limit = 1 //How many constructs of this type can exist
var/combat_construct = FALSE //If this construct is meant for fighting and shouldn't be at the base before the assault phase
var/confirmed = FALSE //If we've confirmed that we want to make this construct outside of the station Z
/datum/clockwork_scripture/create_object/construct/check_special_requirements()
update_construct_limit()
var/constructs = get_constructs()
if(constructs >= construct_limit)
to_chat(invoker, "<span class='warning'>There are too many constructs of this type ([constructs])! You may only have [round(construct_limit)] at once.</span>")
return
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
if(G && !G.active && combat_construct && is_reebe(invoker.z) && !confirmed) //Putting marauders on the base during the prep phase is a bad idea mmkay
if(alert(invoker, "This is a combat construct, and you cannot easily get it to the station. Are you sure you want to make one here?", "Construct Alert", "Yes", "Cancel") == "Cancel")
return
if(!is_servant_of_ratvar(invoker) || !invoker.canUseTopic(slab))
return
confirmed = TRUE
return TRUE
/datum/clockwork_scripture/create_object/construct/post_recital()
creation_update()
confirmed = FALSE
/datum/clockwork_scripture/create_object/construct/proc/get_constructs()
var/constructs = 0
for(var/V in GLOB.all_clockwork_mobs)
if(istype(V, construct_type))
constructs++
for(var/V in GLOB.all_clockwork_objects)
if(istype(V, object_path)) //nice try
constructs++
return constructs
/datum/clockwork_scripture/create_object/construct/proc/update_construct_limit() //Change this on a per-scripture basis, for dynamic limits
//Uses a ranged slab ability, returning only when the ability no longer exists(ie, when interrupted) or finishes.
/datum/clockwork_scripture/ranged_ability
var/slab_overlay
var/ranged_type = /obj/effect/proc_holder/slab
var/ranged_message = "This is a huge goddamn bug, how'd you cast this?"
var/timeout_time = 0
var/allow_mobility = TRUE //if moving and swapping hands is allowed during the while
var/datum/progressbar/progbar
/datum/clockwork_scripture/ranged_ability/Destroy()
qdel(progbar)
return ..()
/datum/clockwork_scripture/ranged_ability/scripture_effects()
if(slab_overlay)
slab.add_overlay(slab_overlay)
slab.item_state = "clockwork_slab"
slab.lefthand_file = 'icons/mob/inhands/antag/clockwork_lefthand.dmi'
slab.righthand_file = 'icons/mob/inhands/antag/clockwork_righthand.dmi'
slab.inhand_overlay = slab_overlay
slab.slab_ability = new ranged_type(slab)
slab.slab_ability.slab = slab
slab.slab_ability.add_ranged_ability(invoker, ranged_message)
invoker.update_inv_hands()
var/end_time = world.time + timeout_time
var/successful = FALSE
if(timeout_time)
progbar = new(invoker, timeout_time, slab)
var/turf/T = get_turf(invoker)
while(slab && slab.slab_ability && !slab.slab_ability.finished && (slab.slab_ability.in_progress || !timeout_time || world.time <= end_time) && \
(allow_mobility || (can_recite() && T == get_turf(invoker))))
if(progbar)
if(slab.slab_ability.in_progress)
qdel(progbar)
else
progbar.update(end_time - world.time)
stoplag(1)
if(slab)
if(slab.slab_ability)
successful = slab.slab_ability.successful
if(!slab.slab_ability.finished)
slab.slab_ability.remove_ranged_ability()
slab.cut_overlays()
slab.item_state = initial(slab.item_state)
slab.item_state = initial(slab.lefthand_file)
slab.item_state = initial(slab.righthand_file)
slab.inhand_overlay = null
if(invoker)
invoker.update_inv_hands()
return successful //slab doesn't look like a word now.
@@ -1,128 +0,0 @@
//////////////////
// APPLICATIONS //
//////////////////
//Sigil of Transmission: Creates a sigil of transmission that can drain and store power for clockwork structures.
/datum/clockwork_scripture/create_object/sigil_of_transmission
descname = "Powers Nearby Structures - Important!"
name = "Sigil of Transmission"
desc = "Places a sigil that can drain and will store energy to power clockwork structures."
invocations = list("Divinity...", "...power our creations!")
channel_time = 70
power_cost = 200
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/transmission
creator_message = "<span class='brass'>A sigil silently appears below you. It will automatically power clockwork structures near it and will drain power when activated.</span>"
usage_tip = "Cyborgs can charge from this sigil by remaining over it for 5 seconds."
tier = SCRIPTURE_APPLICATION
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 1
quickbind = TRUE
quickbind_desc = "Creates a Sigil of Transmission, which can drain and will store power for clockwork structures."
//Mania Motor: Creates a malevolent transmitter that will broadcast the whispers of Sevtug into the minds of nearby nonservants, causing a variety of mental effects at a power cost.
/datum/clockwork_scripture/create_object/mania_motor
descname = "Powered Structure, Area Denial"
name = "Mania Motor"
desc = "Creates a mania motor which causes minor damage and a variety of negative mental effects in nearby non-Servant humans, potentially up to and including conversion."
invocations = list("May this transmitter...", "...break the will of all who oppose us!")
channel_time = 80
power_cost = 750
object_path = /obj/structure/destructible/clockwork/powered/mania_motor
creator_message = "<span class='brass'>You form a mania motor, which causes minor damage and negative mental effects in non-Servants.</span>"
observer_message = "<span class='warning'>A two-pronged machine rises from the ground!</span>"
invokers_required = 2
multiple_invokers_used = TRUE
usage_tip = "It will also cure hallucinations and brain damage in nearby Servants."
tier = SCRIPTURE_APPLICATION
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 2
quickbind = TRUE
quickbind_desc = "Creates a Mania Motor, which causes minor damage and negative mental effects in non-Servants."
//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.
/datum/clockwork_scripture/create_object/clockwork_obelisk
descname = "Powered Structure, Teleportation Hub"
name = "Clockwork Obelisk"
desc = "Creates a clockwork obelisk that can broadcast messages over the Hierophant Network or open a Spatial Gateway to any living Servant or clockwork obelisk."
invocations = list("May this obelisk...", "...take us to all places!")
channel_time = 80
power_cost = 300
object_path = /obj/structure/destructible/clockwork/powered/clockwork_obelisk
creator_message = "<span class='brass'>You form a clockwork obelisk which can broadcast messages or produce Spatial Gateways.</span>"
observer_message = "<span class='warning'>A brass obelisk appears hanging in midair!</span>"
invokers_required = 2
multiple_invokers_used = TRUE
usage_tip = "Producing a gateway has a high power cost. Gateways to or between clockwork obelisks receive double duration and uses."
tier = SCRIPTURE_APPLICATION
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 3
quickbind = TRUE
quickbind_desc = "Creates a Clockwork Obelisk, which can send messages or open Spatial Gateways with power."
//Clockwork Marauder: Creates a construct shell for a clockwork marauder, a well-rounded frontline fighter.
/datum/clockwork_scripture/create_object/construct/clockwork_marauder
descname = "Well-Rounded Combat Construct"
name = "Clockwork Marauder"
desc = "Creates a shell for a clockwork marauder, a balanced frontline construct that can deflect projectiles with its shield."
invocations = list("Arise, avatar of Arbiter!", "Defend the Ark with vengeful zeal.")
channel_time = 80
power_cost = 8000
creator_message = "<span class='brass'>Your slab disgorges several chunks of replicant alloy that form into a suit of thrumming armor.</span>"
usage_tip = "Reciting this scripture multiple times in a short period will cause it to take longer!"
tier = SCRIPTURE_APPLICATION
one_per_tile = TRUE
primary_component = BELLIGERENT_EYE
sort_priority = 4
quickbind = TRUE
quickbind_desc = "Creates a clockwork marauder, used for frontline combat."
object_path = /obj/item/clockwork/construct_chassis/clockwork_marauder
construct_type = /mob/living/simple_animal/hostile/clockwork/marauder
combat_construct = TRUE
var/static/recent_marauders = 0
var/static/time_since_last_marauder = 0
var/static/scaled_recital_time = 0
/datum/clockwork_scripture/create_object/construct/clockwork_marauder/update_construct_limit()
var/human_servants = 0
for(var/V in SSticker.mode.servants_of_ratvar)
var/datum/mind/M = V
var/mob/living/L = M.current
if(ishuman(L) && L.stat != DEAD)
human_servants++
construct_limit = human_servants / 4 //1 per 4 human servants, and a maximum of 3 marauders
construct_limit = CLAMP(construct_limit - recent_marauders, 1, 3)
if(recent_marauders)
to_chat(invoker, "<span class='warning'>The Hierophant Network needs [MARAUDER_SCRIPTURE_SCALING_THRESHOLD / 10] seconds to recover from marauder summoning; recent summoning has limited the number of available marauders by [recent_marauders]!</span>")
/datum/clockwork_scripture/create_object/construct/clockwork_marauder/pre_recital()
channel_time = initial(channel_time)
calculate_scaling()
if(scaled_recital_time)
to_chat(invoker, "<span class='warning'>The Hierophant Network is under strain from repeated summoning, making this scripture [scaled_recital_time / 10] seconds slower!</span>")
channel_time += scaled_recital_time
return TRUE
/datum/clockwork_scripture/create_object/construct/clockwork_marauder/scripture_effects()
. = ..()
time_since_last_marauder = world.time
recent_marauders++
calculate_scaling()
/datum/clockwork_scripture/create_object/construct/clockwork_marauder/proc/calculate_scaling()
var/WT = world.time
var/MT = time_since_last_marauder //Cast it for quicker reference
var/marauders_to_exclude = 0
if(world.time >= time_since_last_marauder + MARAUDER_SCRIPTURE_SCALING_THRESHOLD)
marauders_to_exclude = round(WT - MT) / MARAUDER_SCRIPTURE_SCALING_THRESHOLD //If at least 20 seconds have passed, lose one marauder for each 20 seconds
//i.e. world.time = 10000, last marauder = 9000, so we lose 5 marauders from the recent count since 10k - 9k = 1k, 1k / 200 = 5
time_since_last_marauder = world.time //So that it can't be spammed to make the marauder exclusion plummet; this emulates "ticking"
recent_marauders = max(0, recent_marauders - marauders_to_exclude)
scaled_recital_time = min(recent_marauders * MARAUDER_SCRIPTURE_SCALING_TIME, MARAUDER_SCRIPTURE_SCALING_MAX)
@@ -1,46 +0,0 @@
/////////////////
// CYBORG ONLY //
/////////////////
//Linked Vanguard: grants Vanguard to the invoker and a target
/datum/clockwork_scripture/ranged_ability/linked_vanguard
name = "Linked Vanguard"
invocations = list("Shield us...", "...from darkness!")
channel_time = 30
primary_component = VANGUARD_COGWHEEL
quickbind_desc = "Allows you to grant a Servant and yourself stun immunity, as the Vanguard scripture.<br><b>Click your slab to disable.</b>"
slab_overlay = "vanguard"
ranged_type = /obj/effect/proc_holder/slab/vanguard
ranged_message = "<span class='inathneq_small'><i>You charge the clockwork slab with defensive strength.</i>\n\
<b>Left-click a fellow Servant or yourself to grant Vanguard!\n\
Click your slab to cancel.</b></span>"
timeout_time = 50
/datum/clockwork_scripture/ranged_ability/linked_vanguard/check_special_requirements()
if(!GLOB.ratvar_awakens && islist(invoker.stun_absorption) && invoker.stun_absorption["vanguard"] && invoker.stun_absorption["vanguard"]["end_time"] > world.time)
to_chat(invoker, "<span class='warning'>You are already shielded by a Vanguard!</span>")
return FALSE
return TRUE
/datum/clockwork_scripture/ranged_ability/linked_vanguard/scripture_effects()
if(GLOB.ratvar_awakens) //hey, ratvar's up! give everybody stun immunity.
for(var/mob/living/L in view(7, get_turf(invoker)))
if(L.stat != DEAD && is_servant_of_ratvar(L))
L.apply_status_effect(STATUS_EFFECT_VANGUARD)
CHECK_TICK
return TRUE
return ..()
//Judicial Marker: places a judicial marker at a target location
/datum/clockwork_scripture/ranged_ability/judicial_marker
name = "Judicial Marker"
invocations = list("May heathens...", "...kneel under our force!")
channel_time = 30
primary_component = BELLIGERENT_EYE
quickbind_desc = "Allows you to smite an area, applying Belligerent and briefly stunning.<br><b>Click your slab to disable.</b>"
slab_overlay = "judicial"
ranged_type = /obj/effect/proc_holder/slab/judicial
ranged_message = "<span class='neovgre_small'><i>You charge the clockwork slab with judicial force.</i>\n\
<b>Left-click a target to place a Judicial Marker!\n\
Click your slab to cancel.</b></span>"
timeout_time = 50
@@ -1,284 +0,0 @@
/////////////
// DRIVERS //
/////////////
//Stargazer: Creates a stargazer, a cheap power generator that utilizes starlight.
/datum/clockwork_scripture/create_object/stargazer
descname = "Generates Power From Starlight - Important!"
name = "Stargazer"
desc = "Forms a weak structure that generates power every second while within three tiles of starlight."
invocations = list("Capture their inferior light for us!")
channel_time = 50
power_cost = 50
object_path = /obj/structure/destructible/clockwork/stargazer
creator_message = "<span class='brass'>You form a stargazer, which will generate power near starlight.</span>"
observer_message = "<span class='warning'>A large lantern-shaped machine forms!</span>"
usage_tip = "For obvious reasons, make sure to place this near a window or somewhere else that can see space!"
tier = SCRIPTURE_DRIVER
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 1
quickbind = TRUE
quickbind_desc = "Creates a stargazer, which generates power when near starlight."
/datum/clockwork_scripture/create_object/stargazer/check_special_requirements()
var/area/A = get_area(invoker)
if(A.outdoors || A.map_name == "Space" || !A.blob_allowed)
to_chat(invoker, "<span class='danger'>Stargazers can't be built off-station.</span>")
return
return ..()
//Integration Cog: Creates an integration cog that can be inserted into APCs to passively siphon power.
/datum/clockwork_scripture/create_object/integration_cog
descname = "APC Power Siphoner"
name = "Integration Cog"
desc = "Fabricates an integration cog, which can be used on an open APC to replace its innards and passively siphon its power."
invocations = list("Take that which sustains them!")
channel_time = 10
power_cost = 10
whispered = TRUE
object_path = /obj/item/clockwork/integration_cog
creator_message = "<span class='brass'>You form an integration cog, which can be inserted into an open APC to passively siphon power.</span>"
usage_tip = "Tampering isn't visible unless the APC is opened."
tier = SCRIPTURE_DRIVER
space_allowed = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 2
quickbind = TRUE
quickbind_desc = "Creates an integration cog, which can be used to siphon power from an open APC."
//Sigil of Transgression: Creates a sigil of transgression, which briefly stuns and applies Belligerent to the first non-servant to cross it.
/datum/clockwork_scripture/create_object/sigil_of_transgression
descname = "Trap, Stunning"
name = "Sigil of Transgression"
desc = "Wards a tile with a sigil, which will briefly stun the next non-Servant to cross it and apply Belligerent to them."
invocations = list("Divinity, smite...", "...those who tresspass here!")
channel_time = 50
power_cost = 50
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/transgression
creator_message = "<span class='brass'>A sigil silently appears below you. The next non-Servant to cross it will be smitten.</span>"
usage_tip = "The sigil does not silence its victim, and is generally used to soften potential converts or would-be invaders."
tier = SCRIPTURE_DRIVER
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 3
quickbind = TRUE
quickbind_desc = "Creates a Sigil of Transgression, which will briefly stun and slow the next non-Servant to cross it."
//Sigil of Submission: Creates a sigil of submission, which converts one heretic above it after a delay.
/datum/clockwork_scripture/create_object/sigil_of_submission
descname = "Trap, Conversion"
name = "Sigil of Submission"
desc = "Places a luminous sigil that will convert any non-Servants that remain on it for 8 seconds."
invocations = list("Divinity, enlighten...", "...those who trespass here!")
channel_time = 60
power_cost = 125
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/submission
creator_message = "<span class='brass'>A luminous sigil appears below you. Any non-Servants to cross it will be converted after 8 seconds if they do not move.</span>"
usage_tip = "This is the primary conversion method, though it will not penetrate mindshield implants."
tier = SCRIPTURE_DRIVER
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 4
quickbind = TRUE
quickbind_desc = "Creates a Sigil of Submission, which will convert non-Servants that remain on it."
//Kindle: Charges the slab with blazing energy. It can be released to stun and silence a target.
/datum/clockwork_scripture/ranged_ability/kindle
descname = "Short-Range Single-Target Stun"
name = "Kindle"
desc = "Charges your slab with divine energy, allowing you to overwhelm a target with Ratvar's light."
invocations = list("Divinity, show them your light!")
whispered = TRUE
channel_time = 30
power_cost = 125
usage_tip = "The light can be used from up to two tiles away. Damage taken will GREATLY REDUCE the stun's duration."
tier = SCRIPTURE_DRIVER
primary_component = BELLIGERENT_EYE
sort_priority = 5
slab_overlay = "volt"
ranged_type = /obj/effect/proc_holder/slab/kindle
ranged_message = "<span class='brass'><i>You charge the clockwork slab with divine energy.</i>\n\
<b>Left-click a target within melee range to stun!\n\
Click your slab to cancel.</b></span>"
timeout_time = 150
quickbind = TRUE
quickbind_desc = "Stuns and mutes a target from a short range."
//Hateful Manacles: Applies restraints from melee over several seconds. The restraints function like handcuffs and break on removal.
/datum/clockwork_scripture/ranged_ability/hateful_manacles
descname = "Handcuffs"
name = "Hateful Manacles"
desc = "Forms replicant manacles around a target's wrists that function like handcuffs."
invocations = list("Shackle the heretic!", "Break them in body and spirit!")
channel_time = 15
power_cost = 25
whispered = TRUE
usage_tip = "The manacles are about as strong as zipties, and break when removed."
tier = SCRIPTURE_DRIVER
primary_component = BELLIGERENT_EYE
sort_priority = 6
ranged_type = /obj/effect/proc_holder/slab/hateful_manacles
slab_overlay = "hateful_manacles"
ranged_message = "<span class='neovgre_small'><i>You charge the clockwork slab with divine energy.</i>\n\
<b>Left-click a target within melee range to shackle!\n\
Click your slab to cancel.</b></span>"
timeout_time = 200
quickbind = TRUE
quickbind_desc = "Applies handcuffs to a struck target."
//Vanguard: Provides twenty seconds of stun immunity. At the end of the twenty seconds, 25% of all stuns absorbed are applied to the invoker.
/datum/clockwork_scripture/vanguard
descname = "Self Stun Immunity"
name = "Vanguard"
desc = "Provides twenty seconds of stun immunity. At the end of the twenty seconds, the invoker is knocked down for the equivalent of 25% of all stuns they absorbed. \
Excessive absorption will cause unconsciousness."
invocations = list("Shield me...", "...from darkness!")
channel_time = 30
power_cost = 25
usage_tip = "You cannot reactivate Vanguard while still shielded by it."
tier = SCRIPTURE_DRIVER
primary_component = VANGUARD_COGWHEEL
sort_priority = 7
quickbind = TRUE
quickbind_desc = "Allows you to temporarily absorb stuns. All stuns absorbed will affect you when disabled."
/datum/clockwork_scripture/vanguard/check_special_requirements()
if(!GLOB.ratvar_awakens && islist(invoker.stun_absorption) && invoker.stun_absorption["vanguard"] && invoker.stun_absorption["vanguard"]["end_time"] > world.time)
to_chat(invoker, "<span class='warning'>You are already shielded by a Vanguard!</span>")
return FALSE
return TRUE
/datum/clockwork_scripture/vanguard/scripture_effects()
if(GLOB.ratvar_awakens)
for(var/mob/living/L in view(7, get_turf(invoker)))
if(L.stat != DEAD && is_servant_of_ratvar(L))
L.apply_status_effect(STATUS_EFFECT_VANGUARD)
CHECK_TICK
else
invoker.apply_status_effect(STATUS_EFFECT_VANGUARD)
return TRUE
//Sentinel's Compromise: Allows the invoker to select a nearby servant and convert their brute, burn, and oxygen damage into half as much toxin damage.
/datum/clockwork_scripture/ranged_ability/sentinels_compromise
descname = "Convert Brute/Burn/Oxygen to Half Toxin"
name = "Sentinel's Compromise"
desc = "Charges your slab with healing power, allowing you to convert all of a target Servant's brute, burn, and oxygen damage to half as much toxin damage."
invocations = list("Mend the wounds of...", "...my inferior flesh.")
channel_time = 30
power_cost = 100
usage_tip = "The Compromise is very fast to invoke, and will remove holy water from the target Servant."
tier = SCRIPTURE_DRIVER
primary_component = VANGUARD_COGWHEEL
sort_priority = 8
quickbind = TRUE
quickbind_desc = "Allows you to convert a Servant's brute, burn, and oxygen damage to half toxin damage.<br><b>Click your slab to disable.</b>"
slab_overlay = "compromise"
ranged_type = /obj/effect/proc_holder/slab/compromise
ranged_message = "<span class='inathneq_small'><i>You charge the clockwork slab with healing power.</i>\n\
<b>Left-click a fellow Servant or yourself to heal!\n\
Click your slab to cancel.</b></span>"
//Abscond: Used to return to Reebe.
/datum/clockwork_scripture/abscond
descname = "Return to Reebe - Important!"
name = "Abscond"
desc = "Yanks you through space, returning you to home base."
invocations = list("As we bid farewell, and return to the stars...", "...we shall find our way home.")
whispered = TRUE
channel_time = 50
power_cost = 5
special_power_text = "POWERCOST to bring pulled creature"
special_power_cost = ABSCOND_ABDUCTION_COST
usage_tip = "This can't be used while on Reebe, for obvious reasons."
tier = SCRIPTURE_DRIVER
primary_component = GEIS_CAPACITOR
sort_priority = 9
quickbind = TRUE
quickbind_desc = "Returns you to Reebe."
/datum/clockwork_scripture/abscond/check_special_requirements()
if(is_reebe(invoker.z))
to_chat(invoker, "<span class='danger'>You're already at Reebe.</span>")
return
return TRUE
/datum/clockwork_scripture/abscond/recital()
animate(invoker.client, color = "#AF0AAF", time = 50)
. = ..()
/datum/clockwork_scripture/abscond/scripture_effects()
var/take_pulling = invoker.pulling && isliving(invoker.pulling) && get_clockwork_power(ABSCOND_ABDUCTION_COST)
var/turf/T
if(GLOB.ark_of_the_clockwork_justiciar)
T = get_step(GLOB.ark_of_the_clockwork_justiciar, SOUTH)
else
T = get_turf(pick(GLOB.servant_spawns))
invoker.visible_message("<span class='warning'>[invoker] flickers and phases out of existence!</span>", \
"<span class='bold sevtug_small'>You feel a dizzying sense of vertigo as you're yanked back to Reebe!</span>")
T.visible_message("<span class='warning'>[invoker] flickers and phases into existence!</span>")
playsound(invoker, 'sound/magic/magic_missile.ogg', 50, TRUE)
playsound(T, 'sound/magic/magic_missile.ogg', 50, TRUE)
do_sparks(5, TRUE, invoker)
do_sparks(5, TRUE, T)
if(take_pulling)
adjust_clockwork_power(-special_power_cost)
invoker.pulling.forceMove(T)
invoker.forceMove(T)
if(invoker.client)
animate(invoker.client, color = initial(invoker.client.color), time = 25)
/datum/clockwork_scripture/abscond/scripture_fail()
if(invoker && invoker.client)
animate(invoker.client, color = initial(invoker.client.color), time = 10)
//Replicant: Creates a new clockwork slab.
/datum/clockwork_scripture/create_object/replicant
descname = "New Clockwork Slab - Important!"
name = "Replicant"
desc = "Creates a new clockwork slab."
invocations = list("Metal, become greater!")
channel_time = 10
power_cost = 25
whispered = TRUE
object_path = /obj/item/clockwork/slab
creator_message = "<span class='brass'>You copy a piece of replicant alloy and command it into a new slab.</span>"
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."
tier = SCRIPTURE_DRIVER
space_allowed = TRUE
primary_component = GEIS_CAPACITOR
sort_priority = 10
quickbind = TRUE
quickbind_desc = "Creates a new Clockwork Slab."
//Wraith Spectacles: Creates a pair of wraith spectacles, which grant xray vision but damage vision slowly.
/datum/clockwork_scripture/create_object/wraith_spectacles
descname = "Limited Xray Vision Glasses"
name = "Wraith Spectacles"
desc = "Fabricates a pair of glasses which grant true sight but cause gradual vision loss."
invocations = list("Show the truth of this world to me!")
channel_time = 10
power_cost = 50
whispered = TRUE
object_path = /obj/item/clothing/glasses/wraith_spectacles
creator_message = "<span class='brass'>You form a pair of wraith spectacles, which grant true sight but cause gradual vision loss.</span>"
usage_tip = "\"True sight\" means that you are able to see through walls and in darkness."
tier = SCRIPTURE_DRIVER
space_allowed = TRUE
primary_component = GEIS_CAPACITOR
sort_priority = 11
quickbind = TRUE
quickbind_desc = "Creates a pair of Wraith Spectacles, which grant true sight but cause gradual vision loss."
@@ -1,211 +0,0 @@
//////////////
// REVENANT //
//////////////
//Invoke Inath-neq, the Resonant Cogwheel: Grants invulnerability and stun immunity to everyone nearby for 15 seconds.
/datum/clockwork_scripture/invoke_inathneq
descname = "Area Invulnerability"
name = "Invoke Inath-neq, the Resonant Cogwheel"
desc = "Taps the limitless power of Inath-neq, one of Ratvar's four generals. The benevolence of Inath-Neq will grant complete invulnerability to all Servants in range for fifteen seconds."
invocations = list("I call upon you, Vanguard!!", "Let the Resonant Cogs turn once more!!", "Grant me and my allies the strength to vanquish our foes!!")
channel_time = 100
consumed_components = list(VANGUARD_COGWHEEL = 10, GEIS_CAPACITOR = 3, REPLICANT_ALLOY = 3, HIEROPHANT_ANSIBLE = 3)
usage_tip = "Servants affected by this scripture are only weak to things that outright destroy bodies, such as bombs or the singularity."
tier = SCRIPTURE_REVENANT
primary_component = VANGUARD_COGWHEEL
sort_priority = 2
/datum/clockwork_scripture/invoke_inathneq/check_special_requirements()
if(!slab.no_cost && GLOB.clockwork_generals_invoked["inath-neq"] > world.time)
to_chat(invoker, "<span class='inathneq'>\"[text2ratvar("I cannot lend you my aid yet, champion. Please be careful.")]\"</span>\n\
<span class='warning'>Inath-neq has already been invoked recently! You must wait several minutes before calling upon the Resonant Cogwheel.</span>")
return FALSE
return TRUE
/datum/clockwork_scripture/invoke_inathneq/scripture_effects()
new/obj/effect/clockwork/general_marker/inathneq(get_turf(invoker))
hierophant_message("<span class='inathneq_large'>[text2ratvar("Vanguard: \"I lend you my aid, champions! Let glory guide your blows!")]\"</span>", FALSE, invoker)
GLOB.clockwork_generals_invoked["inath-neq"] = world.time + CLOCKWORK_GENERAL_COOLDOWN
playsound(invoker, 'sound/magic/clockwork/invoke_general.ogg', 50, 0)
if(invoker.real_name == "Lucio")
clockwork_say(invoker, text2ratvar("Aww, let's break it DOWN!!"))
for(var/mob/living/L in range(7, invoker))
if(!is_servant_of_ratvar(L) || L.stat == DEAD)
continue
L.apply_status_effect(STATUS_EFFECT_INATHNEQS_ENDOWMENT)
return TRUE
//Invoke Sevtug, the Formless Pariah: Causes massive global hallucinations, braindamage, confusion, and dizziness to all humans on the same zlevel.
/datum/clockwork_scripture/invoke_sevtug
descname = "Global Hallucination"
name = "Invoke Sevtug, the Formless Pariah"
desc = "Taps the limitless power of Sevtug, one of Ratvar's four generals. The mental manipulation ability of the Pariah allows its wielder to cause mass hallucinations and confusion \
for all non-servant humans on the same z-level as them. The power of this scripture falls off somewhat with distance, and certain things may reduce its effects."
invocations = list("I call upon you, Fright!!", "Let your power shatter the sanity of the weak-minded!!", "Let your tendrils hold sway over all!!")
channel_time = 150
consumed_components = list(BELLIGERENT_EYE = 6, VANGUARD_COGWHEEL = 6, GEIS_CAPACITOR = 10, HIEROPHANT_ANSIBLE = 6)
usage_tip = "Causes brain damage, hallucinations, confusion, and dizziness in massive amounts."
tier = SCRIPTURE_REVENANT
sort_priority = 3
primary_component = GEIS_CAPACITOR
invokers_required = 3
multiple_invokers_used = TRUE
var/static/list/mindbreaksayings = list("\"Oh, great. I get to shatter some minds.\"", "\"More minds to crush.\"", \
"\"Really, this is almost boring.\"", "\"None of these minds have anything interesting in them.\"", "\"Maybe I can instill a little bit of terror in this one.\"", \
"\"What a waste of my power.\"", "\"I'm sure I could just control these minds instead, but they never ask.\"")
/datum/clockwork_scripture/invoke_sevtug/check_special_requirements()
if(!slab.no_cost && GLOB.clockwork_generals_invoked["sevtug"] > world.time)
to_chat(invoker, "<span class='sevtug'>\"[text2ratvar("Is it really so hard - even for a simpleton like you - to grasp the concept of waiting?")]\"</span>\n\
<span class='warning'>Sevtug has already been invoked recently! You must wait several minutes before calling upon the Formless Pariah.</span>")
return FALSE
if(!slab.no_cost && GLOB.ratvar_awakens)
to_chat(invoker, "<span class='sevtug'>\"[text2ratvar("Do you really think anything I can do right now will compare to Engine's power?")]\"</span>\n\
<span class='warning'>Sevtug will not grant his power while Ratvar's dwarfs his own!</span>")
return FALSE
return TRUE
/datum/clockwork_scripture/invoke_sevtug/scripture_effects()
new/obj/effect/clockwork/general_marker/sevtug(get_turf(invoker))
hierophant_message("<span class='sevtug_large'>[text2ratvar("Fright: \"I heed your call, idiots. Get going and use this chance while it lasts!")]\"</span>", FALSE, invoker)
GLOB.clockwork_generals_invoked["sevtug"] = world.time + GLOBAL_CLOCKWORK_GENERAL_COOLDOWN
playsound(invoker, 'sound/magic/clockwork/invoke_general.ogg', 50, 0)
var/hum = get_sfx('sound/effects/screech.ogg') //like playsound, same sound for everyone affected
var/turf/T = get_turf(invoker)
for(var/mob/living/carbon/human/H in GLOB.living_mob_list)
if(H.z == invoker.z && !is_servant_of_ratvar(H))
var/distance = 0
distance += get_dist(T, get_turf(H))
var/visualsdistance = max(150 - distance, 5)
var/minordistance = max(200 - distance*2, 5)
var/majordistance = max(150 - distance*3, 5)
if(H.null_rod_check())
to_chat(H, "<span class='sevtug'>[text2ratvar("Oh, a void weapon. How annoying, I may as well not bother.")]</span>\n\
<span class='warning'>Your holy weapon glows a faint orange, defending your mind!</span>")
continue
else if(H.isloyal())
visualsdistance = round(visualsdistance * 0.5) //half effect for shielded targets
minordistance = round(minordistance * 0.5)
majordistance = round(majordistance * 0.5)
to_chat(H, "<span class='sevtug'>[text2ratvar("Oh, look, a mindshield. Cute, I suppose I'll humor it.")]</span>")
else if(prob(visualsdistance))
to_chat(H, "<span class='sevtug'>[text2ratvar(pick(mindbreaksayings))]</span>")
H.playsound_local(T, hum, visualsdistance, 1)
flash_color(H, flash_color="#AF0AAF", flash_time=visualsdistance*10)
H.dizziness = minordistance + H.dizziness
H.hallucination = minordistance + H.hallucination
H.confused = majordistance + H.confused
H.setBrainLoss(majordistance + H.getBrainLoss())
return TRUE
//Invoke Nezbere, the Brass Eidolon: Invokes Nezbere, bolstering the strength of many clockwork items for one minute.
/datum/clockwork_scripture/invoke_nezbere
descname = "Global Structure Buff"
name = "Invoke Nezbere, the Brass Eidolon"
desc = "Taps the limitless power of Nezbere, one of Ratvar's four generals. The restless toil of the Eidolon will empower a wide variety of clockwork apparatus for a full minute - notably, \
replica fabricators will charge very rapidly."
invocations = list("I call upon you, Armorer!!", "Let your machinations reign on this miserable station!!", "Let your power flow through the tools of your master!!")
channel_time = 150
consumed_components = list(BELLIGERENT_EYE = 6, VANGUARD_COGWHEEL = 6, GEIS_CAPACITOR = 6, REPLICANT_ALLOY = 10)
usage_tip = "Ocular wardens will become empowered, tinkerer's daemons will produce twice as quickly, \
and interdiction lenses, mania motors, tinkerer's daemons, and clockwork obelisks will all require no power."
tier = SCRIPTURE_REVENANT
primary_component = REPLICANT_ALLOY
sort_priority = 4
invokers_required = 3
multiple_invokers_used = TRUE
/datum/clockwork_scripture/invoke_nezbere/check_special_requirements()
if(!slab.no_cost && GLOB.clockwork_generals_invoked["nezbere"] > world.time)
to_chat(invoker, "<span class='nezbere'>\"[text2ratvar("Not just yet, friend. Patience is a virtue.")]\"</span>\n\
<span class='warning'>Nezbere has already been invoked recently! You must wait several minutes before calling upon the Brass Eidolon.</span>")
return FALSE
if(!slab.no_cost && GLOB.ratvar_awakens)
to_chat(invoker, "<span class='nezbere'>\"[text2ratvar("Our master is here already. You do not require my help, friend.")]\"</span>\n\
<span class='warning'>There is no need for Nezbere's assistance while Ratvar is risen!</span>")
return FALSE
return TRUE
/datum/clockwork_scripture/invoke_nezbere/scripture_effects()
new/obj/effect/clockwork/general_marker/nezbere(get_turf(invoker))
hierophant_message("<span class='nezbere_large'>[text2ratvar("Armorer: \"I heed your call, champions. May your artifacts bring ruin upon the heathens that oppose our master!")]\"</span>", FALSE, invoker)
GLOB.clockwork_generals_invoked["nezbere"] = world.time + GLOBAL_CLOCKWORK_GENERAL_COOLDOWN
playsound(invoker, 'sound/magic/clockwork/invoke_general.ogg', 50, 0)
GLOB.nezbere_invoked++
for(var/obj/O in GLOB.all_clockwork_objects)
O.ratvar_act()
addtimer(CALLBACK(GLOBAL_PROC, /proc/reset_nezbere_invocation), 600)
return TRUE
/proc/reset_nezbere_invocation()
GLOB.nezbere_invoked--
for(var/obj/O in GLOB.all_clockwork_objects)
O.ratvar_act()
//Invoke Nzcrentr, the Eternal Thunderbolt: Imbues an immense amount of energy into the invoker. After several seconds, everyone near the invoker will be hit with a devastating lightning blast.
/datum/clockwork_scripture/invoke_nzcrentr
descname = "Area Lightning Blast"
name = "Invoke Nzcrentr, the Eternal Thunderbolt"
desc = "Taps the limitless power of Nzcrentr, one of Ratvar's four generals. Nzcrentr will grant you a tiny fraction of its boundless power. After several seconds, all non-Servants near you \
will be struck by devastating lightning bolts."
invocations = list("I call upon you, Amperage!!", "Let your energy flow through me!!", "Let your boundless power shatter stars!!")
channel_time = 100
consumed_components = list(BELLIGERENT_EYE = 3, GEIS_CAPACITOR = 3, REPLICANT_ALLOY = 3, HIEROPHANT_ANSIBLE = 10)
usage_tip = "Struck targets will also be knocked down for about sixteen seconds."
tier = SCRIPTURE_REVENANT
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 5
/datum/clockwork_scripture/invoke_nzcrentr/check_special_requirements()
if(!slab.no_cost && GLOB.clockwork_generals_invoked["nzcrentr"] > world.time)
to_chat(invoker, "<span class='nzcrentr'>\"[text2ratvar("The boss says you have to wait. Hey, do you think he would mind if I killed you? ...He would? Ok.")]\"</span>\n\
<span class='warning'>Nzcrentr has already been invoked recently! You must wait several minutes before calling upon the Eternal Thunderbolt.</span>")
return FALSE
return TRUE
/datum/clockwork_scripture/invoke_nzcrentr/scripture_effects()
new/obj/effect/clockwork/general_marker/nzcrentr(get_turf(invoker))
GLOB.clockwork_generals_invoked["nzcrentr"] = world.time + CLOCKWORK_GENERAL_COOLDOWN
hierophant_message("<span class='nzcrentr_large'>[text2ratvar("Amperage: \"[invoker.real_name] has called forth my power. Hope [invoker.p_they()] [invoker.p_do()] not shatter under it!")]\"</span>", FALSE, invoker)
invoker.visible_message("<span class='warning'>[invoker] begins to radiate a blinding light!</span>", \
"<span class='nzcrentr'>\"[text2ratvar("The boss says it's okay to do this. Don't blame me if you die from it.")]\"</span>\n\
<span class='userdanger'>You feel limitless power surging through you!</span>")
playsound(invoker, 'sound/magic/clockwork/invoke_general.ogg', 50, 0)
sleep(2)
playsound(invoker, 'sound/magic/lightning_chargeup.ogg', 100, 0)
var/oldcolor = invoker.color
animate(invoker, color = list(rgb(255, 255, 255), rgb(255, 255, 255), rgb(255, 255, 255), rgb(0,0,0)), time = 88) //Gradual advancement to extreme brightness
sleep(88)
if(invoker)
invoker.visible_message("<span class='warning'>Massive bolts of energy emerge from across [invoker]'s body!</span>", \
"<span class='nzcrentr'>\"[text2ratvar("I told you you wouldn't be able to handle it.")]\"</span>\n\
<span class='userdanger'>TOO... MUCH! CAN'T... TAKE IT!</span>")
playsound(invoker, 'sound/magic/lightningbolt.ogg', 100, 0)
if(invoker.stat == CONSCIOUS)
animate(invoker, color = oldcolor, time = 10)
addtimer(CALLBACK(invoker, /atom/proc/update_atom_colour), 10)
for(var/mob/living/L in view(7, invoker))
if(is_servant_of_ratvar(L) || L.null_rod_check())
continue
invoker.Beam(L, icon_state = "nzcrentrs_power", time = 10)
var/randdamage = rand(40, 60)
if(iscarbon(L))
L.electrocute_act(randdamage, "Nzcrentr's power", 1, randdamage)
else
L.adjustFireLoss(randdamage)
L.visible_message(
"<span class='danger'>[L] was shocked by Nzcrentr's power!</span>", \
"<span class='userdanger'>You feel a powerful shock coursing through your body!</span>", \
"<span class='italics'>You hear a heavy electrical crack.</span>" \
)
L.Weaken(8)
playsound(L, 'sound/magic/LightningShock.ogg', 50, 1)
else
playsound(invoker, 'sound/magic/Disintegrate.ogg', 50, 1)
invoker.gib()
return TRUE
else
return FALSE
@@ -1,242 +0,0 @@
/////////////
// SCRIPTS //
/////////////
//Replica Fabricator: Creates a replica fabricator, used to convert objects and repair clockwork structures.
/datum/clockwork_scripture/create_object/replica_fabricator
descname = "Creates Brass and Converts Objects - Important!"
name = "Replica Fabricator"
desc = "Forms a device that, when used on certain objects, replaces them with their Ratvarian equivalents. It requires power to function."
invocations = list("With this device...", "...his presence shall be made known.")
channel_time = 20
power_cost = 250
whispered = TRUE
object_path = /obj/item/clockwork/replica_fabricator
creator_message = "<span class='brass'>You form a replica fabricator.</span>"
usage_tip = "Clockwork Walls cause nearby Tinkerer's Caches to generate components passively, making this a vital tool. Clockwork Floors heal toxin damage in Servants standing on them."
tier = SCRIPTURE_SCRIPT
space_allowed = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 1
quickbind = TRUE
quickbind_desc = "Creates a Replica Fabricator, which can convert various objects to Ratvarian variants."
//Ocular Warden: Creates an ocular warden, which defends a small area near it.
/datum/clockwork_scripture/create_object/ocular_warden
descname = "Structure, Turret"
name = "Ocular Warden"
desc = "Forms an automatic short-range turret which will automatically attack nearby unrestrained non-Servants that can see it."
invocations = list("Guardians of Engine...", "...judge those who would harm us!")
channel_time = 100
power_cost = 250
object_path = /obj/structure/destructible/clockwork/ocular_warden
creator_message = "<span class='brass'>You form an ocular warden, which will automatically attack nearby unrestrained non-Servants that can see it.</span>"
observer_message = "<span class='warning'>A brass eye takes shape and slowly rises into the air, its red iris glaring!</span>"
usage_tip = "Although powerful, the warden is very fragile and should optimally be placed behind barricades."
tier = SCRIPTURE_SCRIPT
one_per_tile = TRUE
space_allowed = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 2
quickbind = TRUE
quickbind_desc = "Creates an Ocular Warden, which will automatically attack nearby unrestrained non-Servants that can see it."
/datum/clockwork_scripture/create_object/ocular_warden/check_special_requirements()
for(var/obj/structure/destructible/clockwork/ocular_warden/W in range(OCULAR_WARDEN_EXCLUSION_RANGE, invoker))
to_chat(invoker, "<span class='neovgre'>You sense another ocular warden too near this location. Placing another this close would cause them to fight.</span>" )
return FALSE
return ..()
//Vitality Matrix: Creates a sigil which will drain health from nonservants and can use that health to heal or even revive servants.
/datum/clockwork_scripture/create_object/vitality_matrix
descname = "Trap, Damage to Healing"
name = "Vitality Matrix"
desc = "Places a sigil that drains life from any living non-Servants that cross it, producing Vitality. Servants that cross it, however, will be healed using existing Vitality. \
Dead Servants can be revived by this sigil at a cost of 150 Vitality."
invocations = list("Divinity, siphon their essence...", "...for this shell to consume.")
channel_time = 60
power_cost = 1000
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/vitality
creator_message = "<span class='brass'>A vitality matrix appears below you. It will drain life from non-Servants and heal Servants that cross it.</span>"
usage_tip = "The sigil will be consumed upon reviving a Servant."
tier = SCRIPTURE_SCRIPT
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 3
quickbind = TRUE
quickbind_desc = "Creates a Vitality Matrix, which drains non-Servants on it to heal Servants that cross it."
/datum/clockwork_scripture/create_object/vitality_matrix/check_special_requirements()
if(locate(object_path) in range(1, invoker))
to_chat(invoker, "<span class='danger'>Vitality matrices placed next to each other could interfere and cause a feedback loop! Move away from the other ones!</span>")
return FALSE
return ..()
//Judicial Visor: Creates a judicial visor, which can smite an area.
/datum/clockwork_scripture/create_object/judicial_visor
descname = "Delayed Area Knockdown Glasses"
name = "Judicial Visor"
desc = "Creates a visor that can smite an area, applying Belligerent and briefly stunning. The smote area will explode after 3 seconds."
invocations = list("Grant me the flames of Engine!")
channel_time = 10
power_cost = 400
whispered = TRUE
object_path = /obj/item/clothing/glasses/judicial_visor
creator_message = "<span class='brass'>You form a judicial visor, which is capable of smiting a small area.</span>"
usage_tip = "The visor has a thirty-second cooldown once used."
tier = SCRIPTURE_SCRIPT
space_allowed = TRUE
primary_component = BELLIGERENT_EYE
sort_priority = 4
quickbind = TRUE
quickbind_desc = "Creates a Judicial Visor, which can smite an area, applying Belligerent and briefly stunning."
//Clockwork Armaments: Grants the invoker the ability to call forth a Ratvarian spear and clockwork armor.
/datum/clockwork_scripture/clockwork_armaments
descname = "Summonable Armor and Weapons - Important!"
name = "Clockwork Armaments"
desc = "Allows the invoker to summon clockwork armor and a Ratvarian spear at will. The spear's attacks will generate Vitality, used for healing."
invocations = list("Grant me armaments...", "...from the forge of Armorer!")
channel_time = 20
power_cost = 250
whispered = TRUE
usage_tip = "Throwing the spear at a mob will do massive damage and knock them down, but break the spear. You will need to wait for 30 seconds before resummoning it."
tier = SCRIPTURE_SCRIPT
primary_component = VANGUARD_COGWHEEL
sort_priority = 5
quickbind = TRUE
quickbind_desc = "Permanently binds clockwork armor and a Ratvarian spear to you."
/datum/clockwork_scripture/clockwork_armaments/check_special_requirements()
for(var/datum/action/innate/clockwork_armaments/F in invoker.actions)
to_chat(invoker, "<span class='warning'>You have already bound a Ratvarian spear to yourself!</span>")
return FALSE
return invoker.can_hold_items()
/datum/clockwork_scripture/clockwork_armaments/scripture_effects()
invoker.visible_message("<span class='warning'>A shimmer of yellow light infuses [invoker]!</span>", \
"<span class='brass'>You bind clockwork equipment to yourself. Use Clockwork Armaments and Call Spear to summon them.</span>")
var/datum/action/innate/call_weapon/ratvarian_spear/S = new()
S.Grant(invoker)
var/datum/action/innate/clockwork_armaments/A = new()
A.Grant(invoker)
return TRUE
//Clockwork Armaments: Equips a set of clockwork armor. Three-minute cooldown.
/datum/action/innate/clockwork_armaments
name = "Clockwork Armaments"
desc = "Outfits you in a full set of Ratvarian armor."
icon_icon = 'icons/mob/actions/actions_clockcult.dmi'
button_icon_state = "clockwork_armor"
background_icon_state = "bg_clock"
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUN|AB_CHECK_CONSCIOUS
buttontooltipstyle = "clockcult"
var/cooldown = 0
var/static/list/ratvarian_armor_typecache = typecacheof(list(
/obj/item/clothing/suit/armor/clockwork,
/obj/item/clothing/head/helmet/clockwork,
/obj/item/clothing/gloves/clockwork,
/obj/item/clothing/shoes/clockwork)) //don't replace this ever
var/static/list/better_armor_typecache = typecacheof(list(
/obj/item/clothing/suit/space,
/obj/item/clothing/head/helmet/space,
/obj/item/clothing/shoes/magboots)) //replace this only if ratvar is up
/datum/action/innate/clockwork_armaments/IsAvailable()
if(!is_servant_of_ratvar(owner))
qdel(src)
return
if(cooldown > world.time)
return
return ..()
/datum/action/innate/clockwork_armaments/Activate()
var/do_message = 0
var/obj/item/I = owner.get_item_by_slot(slot_wear_suit)
if(remove_item_if_better(I, owner))
do_message += owner.equip_to_slot_or_del(new/obj/item/clothing/suit/armor/clockwork(null), slot_wear_suit)
I = owner.get_item_by_slot(slot_head)
if(remove_item_if_better(I, owner))
do_message += owner.equip_to_slot_or_del(new/obj/item/clothing/head/helmet/clockwork(null), slot_head)
I = owner.get_item_by_slot(slot_gloves)
if(remove_item_if_better(I, owner))
do_message += owner.equip_to_slot_or_del(new/obj/item/clothing/gloves/clockwork(null), slot_gloves)
I = owner.get_item_by_slot(slot_shoes)
if(remove_item_if_better(I, owner))
do_message += owner.equip_to_slot_or_del(new/obj/item/clothing/shoes/clockwork(null), slot_shoes)
if(do_message)
owner.visible_message("<span class='warning'>Strange armor appears on [owner]!</span>", "<span class='heavy_brass'>A bright shimmer runs down your body, equipping you with Ratvarian armor.</span>")
playsound(owner, 'sound/magic/clockwork/fellowship_armory.ogg', 15 * do_message, TRUE) //get sound loudness based on how much we equipped
cooldown = CLOCKWORK_ARMOR_COOLDOWN + world.time
owner.update_action_buttons_icon()
addtimer(CALLBACK(owner, /mob.proc/update_action_buttons_icon), CLOCKWORK_ARMOR_COOLDOWN)
return TRUE
/datum/action/innate/clockwork_armaments/proc/remove_item_if_better(obj/item/I, mob/user)
if(!I)
return TRUE
if(is_type_in_typecache(I, ratvarian_armor_typecache))
return FALSE
if(!GLOB.ratvar_awakens && is_type_in_typecache(I, better_armor_typecache))
return FALSE
return user.dropItemToGround(I)
//Call Spear: Calls forth a powerful Ratvarian spear.
/datum/action/innate/call_weapon/ratvarian_spear
name = "Call Spear"
desc = "Calls a Ratvarian spear into your hands to fight your enemies."
weapon_type = /obj/item/clockwork/weapon/ratvarian_spear
//Spatial Gateway: Allows the invoker to teleport themselves and any nearby allies to a conscious servant or clockwork obelisk.
/datum/clockwork_scripture/spatial_gateway
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 = 6
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)
@@ -1,217 +0,0 @@
//The base clockwork structure. Can have an alternate desc and will show up in the list of clockwork objects.
/obj/structure/destructible/clockwork
name = "meme structure"
desc = "Some frog or something, the fuck?"
var/clockwork_desc //Shown to servants when they examine
icon = 'icons/obj/clockwork_objects.dmi'
icon_state = "rare_pepe"
var/unanchored_icon //icon for when this structure is unanchored, doubles as the var for if it can be unanchored
anchored = TRUE
density = TRUE
resistance_flags = FIRE_PROOF | ACID_PROOF
var/can_be_repaired = TRUE //if a fabricator can repair it
break_message = "<span class='warning'>The frog isn't a meme after all!</span>" //The message shown when a structure breaks
break_sound = 'sound/magic/clockwork/anima_fragment_death.ogg' //The sound played when a structure breaks
debris = list(/obj/item/clockwork/alloy_shards/large = 1, \
/obj/item/clockwork/alloy_shards/medium = 2, \
/obj/item/clockwork/alloy_shards/small = 3) //Parts left behind when a structure breaks
var/construction_value = 0 //How much value the structure contributes to the overall "power" of the structures on the station
var/immune_to_servant_attacks = FALSE //if we ignore attacks from servants of ratvar instead of taking damage
/obj/structure/destructible/clockwork/Initialize()
. = ..()
change_construction_value(construction_value)
GLOB.all_clockwork_objects += src
/obj/structure/destructible/clockwork/Destroy()
change_construction_value(-construction_value)
GLOB.all_clockwork_objects -= src
return ..()
/obj/structure/destructible/clockwork/ratvar_act()
if(GLOB.ratvar_awakens || GLOB.clockwork_gateway_activated)
obj_integrity = max_integrity
/obj/structure/destructible/clockwork/narsie_act()
if(take_damage(rand(25, 50), BRUTE) && src) //if we still exist
var/previouscolor = color
color = "#960000"
animate(src, color = previouscolor, time = 8)
addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 8)
/obj/structure/destructible/clockwork/examine(mob/user)
var/can_see_clockwork = is_servant_of_ratvar(user) || isobserver(user)
if(can_see_clockwork && clockwork_desc)
desc = clockwork_desc
..()
desc = initial(desc)
if(unanchored_icon)
to_chat(user, "<span class='notice'>[src] is [anchored ? "":"not "]secured to the floor.</span>")
/obj/structure/destructible/clockwork/examine_status(mob/user)
if(is_servant_of_ratvar(user) || isobserver(user))
var/t_It = p_they(TRUE)
var/t_is = p_are()
var/heavily_damaged = FALSE
var/healthpercent = (obj_integrity/max_integrity) * 100
if(healthpercent < 50)
heavily_damaged = TRUE
return "<span class='[heavily_damaged ? "alloy":"brass"]'>[t_It] [t_is] at <b>[obj_integrity]/[max_integrity]</b> integrity[heavily_damaged ? "!":"."]</span>"
return ..()
/obj/structure/destructible/clockwork/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0)
if(is_servant_of_ratvar(user) && immune_to_servant_attacks)
return FALSE
return ..()
/obj/structure/destructible/clockwork/hulk_damage()
return 20
/obj/structure/destructible/clockwork/attack_generic(mob/user, damage_amount = 0, damage_type = BRUTE, damage_flag = 0, sound_effect = 1)
if(is_servant_of_ratvar(user) && immune_to_servant_attacks)
return FALSE
return ..()
/obj/structure/destructible/clockwork/mech_melee_attack(obj/mecha/M)
if(M.occupant && is_servant_of_ratvar(M.occupant) && immune_to_servant_attacks)
return FALSE
return ..()
/obj/structure/destructible/clockwork/proc/get_efficiency_mod()
if(GLOB.ratvar_awakens)
return 2
. = max(sqrt(obj_integrity/max(max_integrity, 1)), 0.5)
. = round(., 0.01)
/obj/structure/destructible/clockwork/attack_ai(mob/user)
if(is_servant_of_ratvar(user))
attack_hand(user)
/obj/structure/destructible/clockwork/attack_animal(mob/living/simple_animal/M)
if(is_servant_of_ratvar(M))
attack_hand(M)
return FALSE
else
return ..()
/obj/structure/destructible/clockwork/attackby(obj/item/I, mob/user, params)
if(is_servant_of_ratvar(user) && istype(I, /obj/item/wrench) && unanchored_icon)
if(default_unfasten_wrench(user, I, 50) == SUCCESSFUL_UNFASTEN)
update_anchored(user)
return 1
return ..()
/obj/structure/destructible/clockwork/attacked_by(obj/item/I, mob/living/user)
if(is_servant_of_ratvar(user) && immune_to_servant_attacks)
return FALSE
return ..()
/obj/structure/destructible/clockwork/proc/update_anchored(mob/user, do_damage)
if(anchored)
icon_state = initial(icon_state)
else
icon_state = unanchored_icon
if(do_damage)
playsound(src, break_sound, 10 * (40 * (1 - get_efficiency_mod())), 1)
take_damage(round(max_integrity * 0.25, 1), BRUTE)
to_chat(user, "<span class='warning'>As you unsecure [src] from the floor, you see cracks appear in its surface!</span>")
/obj/structure/destructible/clockwork/emp_act(severity)
if(anchored && unanchored_icon)
anchored = FALSE
update_anchored(null, obj_integrity > max_integrity * 0.25)
new /obj/effect/temp_visual/emp(loc)
//for the ark and Ratvar
/obj/structure/destructible/clockwork/massive
name = "massive construct"
desc = "A very large construction."
layer = MASSIVE_OBJ_LAYER
density = FALSE
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF | FREEZE_PROOF
/obj/structure/destructible/clockwork/massive/Initialize()
. = ..()
GLOB.poi_list += src
/obj/structure/destructible/clockwork/massive/Destroy()
GLOB.poi_list -= src
return ..()
/obj/structure/destructible/clockwork/massive/singularity_pull(S, current_size)
return
//the base clockwork machinery, which is not actually machines, but happens to use power
/obj/structure/destructible/clockwork/powered
var/obj/machinery/power/apc/target_apc
var/active = FALSE
var/needs_power = TRUE
var/active_icon = null //icon_state while process() is being called
var/inactive_icon = null //icon_state while process() isn't being called
/obj/structure/destructible/clockwork/powered/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
if(!can_access_clockwork_power(src))
to_chat(user, "<span class='alloy'>It has no access to the power network! Create a sigil of transmission nearby.</span>")
else
to_chat(user, "<span class='brass'>It has access to <b>[DisplayPower(get_clockwork_power())]</b> of power.</span>")
/obj/structure/destructible/clockwork/powered/Destroy()
SSfastprocess.processing -= src
SSobj.processing -= src
return ..()
/obj/structure/destructible/clockwork/powered/process()
var/powered = can_access_clockwork_power(src)
return powered == PROCESS_KILL ? 25 : powered //make sure we don't accidentally return the arbitrary PROCESS_KILL define
/obj/structure/destructible/clockwork/powered/can_be_unfasten_wrench(mob/user, silent)
if(active)
if(!silent)
to_chat(user, "<span class='warning'>[src] needs to be disabled before it can be unsecured!</span>")
return FAILED_UNFASTEN
return ..()
/obj/structure/destructible/clockwork/powered/proc/toggle(fast_process, mob/living/user)
if(user)
if(!is_servant_of_ratvar(user))
return FALSE
if(!anchored && !active)
to_chat(user, "<span class='warning'>[src] needs to be secured to the floor before it can be activated!</span>")
return FALSE
visible_message("<span class='notice'>[user] [active ? "dis" : "en"]ables [src].</span>", "<span class='brass'>You [active ? "dis" : "en"]able [src].</span>")
active = !active
if(active)
icon_state = active_icon
if(fast_process)
START_PROCESSING(SSfastprocess, src)
else
START_PROCESSING(SSobj, src)
else
icon_state = inactive_icon
if(fast_process)
STOP_PROCESSING(SSfastprocess, src)
else
STOP_PROCESSING(SSobj, src)
return TRUE
/obj/structure/destructible/clockwork/powered/proc/forced_disable(bad_effects)
if(active)
toggle()
/obj/structure/destructible/clockwork/powered/emp_act(severity)
if(forced_disable(TRUE))
new /obj/effect/temp_visual/emp(loc)
/obj/structure/destructible/clockwork/powered/proc/try_use_power(amount) //try to use an amount of power
if(!needs_power || GLOB.ratvar_awakens || !amount)
return TRUE
if(!can_access_clockwork_power(src, amount))
return
return use_power(amount)
/obj/structure/destructible/clockwork/powered/proc/use_power(amount) //we've made sure we had power, so now we use it
return adjust_clockwork_power(-amount)
@@ -1,86 +0,0 @@
//No, not that kind.
/obj/structure/destructible/clockwork/trap
name = "base clockwork trap"
desc = "You shouldn't see this. File a bug report!"
clockwork_desc = "A trap that shouldn't exist, and you should report this as a bug."
var/list/wired_to
/obj/structure/destructible/clockwork/trap/Initialize()
. = ..()
wired_to = list()
/obj/structure/destructible/clockwork/trap/Destroy()
for(var/V in wired_to)
var/obj/structure/destructible/clockwork/trap/T = V
T.wired_to -= src
return ..()
/obj/structure/destructible/clockwork/trap/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
to_chat(user, "It's wired to:")
if(!wired_to.len)
to_chat(user, "Nothing.")
else
for(var/V in wired_to)
var/obj/O = V
var/distance = get_dist(src, O)
to_chat(user, "[O] ([distance == 0 ? "same tile" : "[distance] tiles [dir2text(get_dir(src, O))]"])")
/obj/structure/destructible/clockwork/trap/wrench_act(mob/living/user, obj/item/wrench)
if(!is_servant_of_ratvar(user))
return ..()
to_chat(user, "<span class='notice'>You break down the delicate components of [src] into brass.</span>")
playsound(src, wrench.usesound, 50, TRUE)
new/obj/item/stack/tile/brass(get_turf(src))
qdel(src)
return TRUE
/obj/structure/destructible/clockwork/trap/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/clockwork/slab) && is_servant_of_ratvar(user))
var/obj/item/clockwork/slab/F = I
if(!F.linking)
to_chat(user, "<span class='notice'>Beginning link. Alt-click the slab to cancel, or use it on another trap object to link the two.</span>")
F.linking = src
else
if(F.linking in wired_to)
to_chat(user, "<span class='warning'>These two objects are already connected!</span>")
return
if(F.linking.z != z)
to_chat(user, "<span class='warning'>You'd need a <b>much</b> tougher slab to link two objects in different sectors.</span>")
return
to_chat(user, "<span class='notice'>You link [F.linking] with [src].</span>")
wired_to += F.linking
F.linking.wired_to += src
F.linking = null
return
..()
/obj/structure/destructible/clockwork/trap/wirecutter_act(mob/living/user, obj/item/wirecutters)
if(!is_servant_of_ratvar(user))
return
if(!wired_to.len)
to_chat(user, "<span class='warning'>[src] has no connections!</span>")
return
to_chat(user, "<span class='notice'>You sever all connections to [src].</span>")
playsound(src, wirecutters.usesound, 50, TRUE)
for(var/V in wired_to)
var/obj/structure/destructible/clockwork/trap/T = V
T.wired_to -= src
wired_to -= T
return TRUE
/obj/structure/destructible/clockwork/trap/proc/activate()
//These objects send signals to normal traps to activate
/obj/structure/destructible/clockwork/trap/trigger
name = "base trap trigger"
max_integrity = 5
break_message = "<span class='warning'>The trigger breaks apart!</span>"
density = FALSE
/obj/structure/destructible/clockwork/trap/trigger/activate()
for(var/obj/structure/destructible/clockwork/trap/T in wired_to)
if(istype(T, /obj/structure/destructible/clockwork/trap/trigger)) //Triggers don't go off multiple times
continue
T.activate()
@@ -1,376 +0,0 @@
#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)
return
return G.active
//The gateway to Reebe, from which Ratvar emerges.
/obj/structure/destructible/clockwork/massive/celestial_gateway
name = "\improper Ark of the Clockwork Justicar"
desc = "A massive, hulking amalgamation of parts. It seems to be maintaining a very unstable bluespace anomaly."
clockwork_desc = "Nezbere's magnum opus: a hulking clockwork machine capable of combining bluespace and steam power to summon Ratvar. Once activated, \
its instability will cause one-way bluespace rifts to open across the station to the City of Cogs, so be prepared to defend it at all costs."
max_integrity = 500
mouse_opacity = MOUSE_OPACITY_OPAQUE
icon = 'icons/effects/clockwork_effects.dmi'
icon_state = "nothing"
density = TRUE
resistance_flags = FIRE_PROOF | ACID_PROOF | FREEZE_PROOF
can_be_repaired = FALSE
immune_to_servant_attacks = TRUE
var/active = FALSE
var/progress_in_seconds = 0 //Once this reaches GATEWAY_RATVAR_ARRIVAL, it's game over
var/grace_period = ARK_GRACE_PERIOD //This exists to allow the crew to gear up and prepare for the invasion
var/initial_activation_delay = -1 //How many seconds the Ark will have initially taken to activate
var/seconds_until_activation = -1 //How many seconds until the Ark activates; if it should never activate, set this to -1
var/purpose_fulfilled = FALSE
var/first_sound_played = FALSE
var/second_sound_played = FALSE
var/third_sound_played = FALSE
var/fourth_sound_played = FALSE
var/obj/effect/clockwork/overlay/gateway_glow/glow
var/obj/effect/countdown/clockworkgate/countdown
var/last_scream = 0
var/recalls_remaining = 1
var/recalling
/obj/structure/destructible/clockwork/massive/celestial_gateway/Initialize()
. = ..()
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/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
. = ..()
if(.)
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>")
for(var/V in GLOB.player_list)
var/mob/M = V
var/turf/T = get_turf(M)
if((T && T.z == z) || is_servant_of_ratvar(M) || isobserver(M))
M.playsound_local(M, 'sound/machines/clockcult/ark_scream.ogg', 100, FALSE, pressure_affected = FALSE)
hierophant_message("<span class='big boldwarning'>The Ark is taking damage!</span>")
last_scream = world.time + ARK_SCREAM_COOLDOWN
/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
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
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')
set_security_level("delta")
for(var/V in SSticker.mode.servants_of_ratvar)
var/datum/mind/M = V
if(!M || !M.current)
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
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/open_portal(turf/T)
new/obj/effect/clockwork/city_of_cogs_rift(T)
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/spawn_animation()
var/turf/T = get_turf(src)
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))
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)
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/initiate_mass_recall()
recalling = TRUE
sound_to_playing_players('sound/machines/clockcult/ark_recall.ogg', 75, FALSE)
hierophant_message("<span class='bold large_brass'>The Eminence has initiated a mass recall! You are being transported to the Ark!</span>")
addtimer(CALLBACK(src, .proc/mass_recall), 100)
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/mass_recall()
for(var/V in SSticker.mode.servants_of_ratvar)
var/datum/mind/M = V
if(!M || !M.current)
continue
if(isliving(M.current) && M.current.stat != DEAD)
M.current.forceMove(get_turf(src))
M.current.overlay_fullscreen("flash", /obj/screen/fullscreen/flash)
M.current.clear_fullscreen("flash", 5)
playsound(src, 'sound/magic/clockwork/invoke_general.ogg', 50, FALSE)
recalls_remaining--
recalling = FALSE
transform = matrix() * 2
animate(src, transform = matrix() * 0.5, time = 30, flags = ANIMATION_END_NOW)
/obj/structure/destructible/clockwork/massive/celestial_gateway/Destroy()
STOP_PROCESSING(SSprocessing, src)
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(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)
if(!(flags_1 & NODECONSTRUCT_1))
if(!disassembled)
resistance_flags |= INDESTRUCTIBLE
countdown.stop()
visible_message("<span class='userdanger'>[src] begins to pulse uncontrollably... you might want to run!</span>")
sound_to_playing_players(volume = 50, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_disrupted.ogg'))
for(var/mob/M in GLOB.player_list)
var/turf/T = get_turf(M)
if((T && T.z == z) || is_servant_of_ratvar(M))
M.playsound_local(M, 'sound/machines/clockcult/ark_deathrattle.ogg', 100, FALSE, pressure_affected = FALSE)
make_glow()
glow.icon_state = "clockwork_gateway_disrupted"
resistance_flags |= INDESTRUCTIBLE
sleep(27)
explosion(src, 1, 3, 8, 8)
sound_to_playing_players('sound/effects/explosion_distant.ogg', volume = 50)
qdel(src)
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/make_glow()
if(!glow)
glow = new /obj/effect/clockwork/overlay/gateway_glow(get_turf(src))
glow.linked = src
/obj/structure/destructible/clockwork/massive/celestial_gateway/ex_act(severity)
var/damage = max((obj_integrity * 0.7) / severity, 100) //requires multiple bombs to take down
take_damage(damage, BRUTE, "bomb", 0)
/obj/structure/destructible/clockwork/massive/celestial_gateway/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)
. *= 10
/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"
else if(GATEWAY_RATVAR_ARRIVAL - progress_in_seconds > 0)
. = "[round(max((GATEWAY_RATVAR_ARRIVAL - progress_in_seconds) / (GATEWAY_SUMMON_RATE), 0), 1)][s_on_time ? "S":""]"
/obj/structure/destructible/clockwork/massive/celestial_gateway/examine(mob/user)
icon_state = "spatial_gateway" //cheat wildly by pretending to have an icon
..()
icon_state = initial(icon_state)
if(is_servant_of_ratvar(user) || isobserver(user))
if(!active)
to_chat(user, "<span class='big'><b>Time until the Ark's activation:</b> [DisplayTimeText(get_arrival_time())]</span>")
else
if(grace_period)
to_chat(user, "<span class='big'><b>Crew grace period time remaining:</b> [DisplayTimeText(get_arrival_time())]</span>")
else
to_chat(user, "<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)
to_chat(user, "<span class='heavy_brass'>The Ark is feeding power into the bluespace field.</span>")
if(GATEWAY_REEBE_FOUND to GATEWAY_RATVAR_COMING)
to_chat(user, "<span class='heavy_brass'>The field is ripping open a copy of itself in Ratvar's prison.</span>")
if(GATEWAY_RATVAR_COMING to INFINITY)
to_chat(user, "<span class='heavy_brass'>With the bluespace field established, Ratvar is preparing to come through!</span>")
else
if(!active)
to_chat(user, "<span class='warning'>Whatever it is, it doesn't seem to be active.</span>")
else
switch(progress_in_seconds)
if(-INFINITY to GATEWAY_REEBE_FOUND)
to_chat(user, "<span class='warning'>You see a swirling bluespace anomaly steadily growing in intensity.</span>")
if(GATEWAY_REEBE_FOUND to GATEWAY_RATVAR_COMING)
to_chat(user, "<span class='warning'>The anomaly is stable, and you can see flashes of something from it.</span>")
if(GATEWAY_RATVAR_COMING to INFINITY)
to_chat(user, "<span class='boldwarning'>The anomaly is stable! Something is coming through!</span>")
/obj/structure/destructible/clockwork/massive/celestial_gateway/process()
if(seconds_until_activation == -1) //we never do anything
return
adjust_clockwork_power(2.5) //Provides weak power generation on its own
if(seconds_until_activation)
if(!countdown)
countdown = new(src)
countdown.start()
seconds_until_activation--
if(!GLOB.script_scripture_unlocked && initial_activation_delay * 0.5 > seconds_until_activation)
GLOB.script_scripture_unlocked = TRUE
hierophant_message("<span class='large_brass bold'>The Ark is halfway prepared. Script scripture is now available!</span>")
if(!seconds_until_activation)
cry_havoc()
seconds_until_activation = -1 //we'll set this after cry_havoc()
return
if(!first_sound_played || prob(7))
for(var/mob/M in GLOB.player_list)
if(!isnewplayer(M))
var/turf/T = get_turf(M)
if(T && T.z == z)
to_chat(M, "<span class='warning'><b>You hear otherworldly sounds from the [dir2text(get_dir(get_turf(M), get_turf(src)))]...</span>")
else
to_chat(M, "<span class='boldwarning'>You hear otherworldly sounds from all around you...</span>")
if(!obj_integrity)
return
for(var/turf/closed/wall/W in RANGE_TURFS(2, src))
W.dismantle_wall()
for(var/obj/O in orange(1, src))
if(!O.pulledby && !istype(O, /obj/effect) && O.density)
if(!step_away(O, src, 2) || get_dist(O, src) < 2)
O.take_damage(50, BURN, "bomb")
O.update_icon()
for(var/V in GLOB.player_list)
var/mob/M = V
var/turf/T = get_turf(M)
if(is_servant_of_ratvar(M) && (!T || T.z != z))
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))
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))
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))
fourth_sound_played = TRUE
make_glow()
glow.icon_state = "clockwork_gateway_closing"
if(GATEWAY_RATVAR_ARRIVAL to INFINITY)
if(!purpose_fulfilled)
countdown.stop()
resistance_flags |= INDESTRUCTIBLE
purpose_fulfilled = TRUE
make_glow()
animate(glow, transform = matrix() * 1.5, alpha = 255, time = 125)
sound_to_playing_players(volume = 100, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/ratvar_rises.ogg')) //End the sounds
sleep(125)
make_glow()
animate(glow, transform = matrix() * 3, alpha = 0, time = 5)
QDEL_IN(src, 3)
sleep(3)
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))
var/turf/T2 = I
if(!T2)
continue
var/dist = cheap_hypotenuse(T2.x, T2.y, x0, y0)
if(dist < 100)
dist = TRUE
else
dist = FALSE
T.ratvar_act(dist)
CHECK_TICK
/obj/structure/destructible/clockwork/massive/celestial_gateway/attack_ghost(mob/user)
if(!IsAdminGhost(user))
return ..()
if(GLOB.servants_active)
to_chat(user, "<span class='danger'>The Ark is already counting down.</span>")
return ..()
if(alert(user, "Activate the Ark's countdown?", name, "Yes", "No") == "Yes")
if(alert(user, "REALLY activate the Ark's countdown?", name, "Yes", "No") == "Yes")
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 \
minutes. You will need to create servant players yourself.</span>")
final_countdown(35)
//the actual appearance of the Ark of the Clockwork Justicar; an object so the edges of the gate can be clicked through.
/obj/effect/clockwork/overlay/gateway_glow
icon = 'icons/effects/96x96.dmi'
icon_state = "clockwork_gateway_components"
pixel_x = -32
pixel_y = -32
layer = BELOW_OPEN_DOOR_LAYER
light_range = 2
light_power = 4
light_color = "#6A4D2F"
@@ -1,103 +0,0 @@
//Clockwork Obelisk: Can broadcast a message at a small power cost or outright open a spatial gateway at a massive power cost.
/obj/structure/destructible/clockwork/powered/clockwork_obelisk
name = "clockwork obelisk"
desc = "A large brass obelisk hanging in midair."
clockwork_desc = "A powerful obelisk that can send a message to all servants or open a gateway to a target servant or clockwork obelisk."
icon_state = "obelisk_inactive"
active_icon = "obelisk"
inactive_icon = "obelisk_inactive"
unanchored_icon = "obelisk_unwrenched"
construction_value = 20
max_integrity = 150
break_message = "<span class='warning'>The obelisk falls to the ground, undamaged!</span>"
debris = list(/obj/item/clockwork/alloy_shards/small = 4, \
/obj/item/clockwork/alloy_shards/medium = 2, \
/obj/item/clockwork/component/hierophant_ansible/obelisk = 1)
var/hierophant_cost = MIN_CLOCKCULT_POWER //how much it costs to broadcast with large text
var/gateway_cost = 2000 //how much it costs to open a gateway
/obj/structure/destructible/clockwork/powered/clockwork_obelisk/Initialize()
. = ..()
toggle(1)
/obj/structure/destructible/clockwork/powered/clockwork_obelisk/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
to_chat(user, "<span class='nzcrentr_small'>It requires <b>[DisplayPower(hierophant_cost)]</b> to broadcast over the Hierophant Network, and <b>[DisplayPower(gateway_cost)]</b> to open a Spatial Gateway.</span>")
/obj/structure/destructible/clockwork/powered/clockwork_obelisk/can_be_unfasten_wrench(mob/user, silent)
if(active)
if(!silent)
to_chat(user, "<span class='warning'>[src] is currently sustaining a gateway!</span>")
return FAILED_UNFASTEN
return ..()
/obj/structure/destructible/clockwork/powered/clockwork_obelisk/forced_disable(bad_effects)
var/affected = 0
for(var/obj/effect/clockwork/spatial_gateway/SG in loc)
SG.ex_act(EXPLODE_DEVASTATE)
affected++
if(bad_effects)
affected += try_use_power(MIN_CLOCKCULT_POWER*4)
return affected
/obj/structure/destructible/clockwork/powered/clockwork_obelisk/attack_hand(mob/living/user)
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")
switch(choice)
if("Hierophant Broadcast")
if(active)
to_chat(user, "<span class='warning'>[src] is sustaining a gateway and cannot broadcast!</span>")
return
if(!user.can_speak_vocal())
to_chat(user, "<span class='warning'>You cannot speak through [src]!</span>")
return
var/input = stripped_input(usr, "Please choose a message to send over the Hierophant Network.", "Hierophant Broadcast", "")
if(!is_servant_of_ratvar(user) || !input || !user.canUseTopic(src, !issilicon(user)))
return
if(!anchored)
to_chat(user, "<span class='warning'>[src] is no longer secured!</span>")
return FALSE
if(active)
to_chat(user, "<span class='warning'>[src] is sustaining a gateway and cannot broadcast!</span>")
return
if(!user.can_speak_vocal())
to_chat(user, "<span class='warning'>You cannot speak through [src]!</span>")
return
if(!try_use_power(hierophant_cost))
to_chat(user, "<span class='warning'>[src] lacks the power to broadcast!</span>")
return
clockwork_say(user, text2ratvar("Hierophant Broadcast, activate! [html_decode(input)]"))
titled_hierophant_message(user, input, "big_brass", "large_brass")
if("Spatial Gateway")
if(active)
to_chat(user, "<span class='warning'>[src] is already sustaining a gateway!</span>")
return
if(!user.can_speak_vocal())
to_chat(user, "<span class='warning'>You need to be able to speak to open a gateway!</span>")
return
if(!try_use_power(gateway_cost))
to_chat(user, "<span class='warning'>[src] lacks the power to open a gateway!</span>")
return
if(procure_gateway(user, round(100 * get_efficiency_mod(), 1), round(5 * get_efficiency_mod(), 1), 1))
process()
if(!active) //we won't be active if nobody has sent a gateway to us
active = TRUE
clockwork_say(user, text2ratvar("Spatial Gateway, activate!"))
return
adjust_clockwork_power(gateway_cost) //if we didn't return above, ie, successfully create a gateway, we give the power back
/obj/structure/destructible/clockwork/powered/clockwork_obelisk/process()
if(!anchored)
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
icon_state = active_icon
density = FALSE
active = TRUE
else
icon_state = inactive_icon
density = TRUE
active = FALSE
@@ -1,139 +0,0 @@
//Used to nominate oneself or ghosts for the role of Eminence.
/obj/structure/destructible/clockwork/eminence_spire
name = "eminence spire"
desc = "A hulking machine made of powerful alloy, with three small obelisks and a huge plate in the center."
clockwork_desc = "This spire is used to become the Eminence, who functions as an invisible leader of the cult. Activate it to nominate yourself or propose that the Eminence should be \
selected from available ghosts. Once an Eminence is selected, they can't normally be changed."
icon_state = "tinkerers_daemon"
break_message = "<span class='warning'>The spire screeches with crackling power and collapses into scrap!</span>"
max_integrity = 400
var/mob/eminence_nominee
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)
if(!is_servant_of_ratvar(user))
to_chat(user, "<span class='notice'>You can tell how powerful [src] is; you know better than to touch it.</span>")
return
if(kingmaking)
return
var/datum/antagonist/clockcult/C = user.mind.has_antag_datum(/datum/antagonist/clockcult)
if(!C || !C.clock_team)
return
if(C.clock_team.eminence)
to_chat(user, "<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)
else
objection(user)
else
nomination(user)
/obj/structure/destructible/clockwork/eminence_spire/attack_drone(mob/living/simple_animal/drone/user)
if(!is_servant_of_ratvar(user))
..()
else
to_chat(user, "<span class='warning'>You feel the omniscient gaze turn into a puzzled frown. Perhaps you should just stick to building.</span>")
return
/obj/structure/destructible/clockwork/eminence_spire/attack_ghost(mob/user)
if(!IsAdminGhost(user))
return
var/datum/antagonist/clockcult/random_cultist = locate() in get_antagonists(/datum/antagonist/clockcult) //if theres no cultists new team without eminence will be created anyway.
if(random_cultist && random_cultist.clock_team && random_cultist.clock_team.eminence)
to_chat(user, "<span class='warning'>There's already an Eminence - too late!</span>")
return
if(!GLOB.servants_active)
to_chat(user, "<span class='warning'>The Ark must be active first!</span>")
return
if(alert(user, "Become the Eminence using admin?", "Become Eminence", "Yes", "No") != "Yes")
return
message_admins("<span class='danger'>Admin [key_name_admin(user)] directly became the Eminence of the cult!</span>")
log_admin("Admin [key_name(user)] made themselves the Eminence.")
var/mob/camera/eminence/eminence = new(get_turf(src))
eminence.key = user.key
hierophant_message("<span class='bold large_brass'>Ratvar has directly assigned the Eminence!</span>")
for(var/mob/M in servants_and_ghosts())
M.playsound_local(M, 'sound/machines/clockcult/eminence_selected.ogg', 50, FALSE)
/obj/structure/destructible/clockwork/eminence_spire/proc/nomination(mob/living/nominee) //A user is nominating themselves or ghosts to become Eminence
var/nomination_choice = alert(nominee, "Who would you like to nominate?", "Eminence Nomination", "Nominate Yourself", "Nominate Ghosts", "Cancel")
if(!is_servant_of_ratvar(nominee) || !nominee.canUseTopic(src) || eminence_nominee)
return
switch(nomination_choice)
if("Cancel")
return
if("Nominate Yourself")
eminence_nominee = nominee
hierophant_message("<span class='brass'><b>[nominee] nominates themselves as the Eminence!</b> You may object by interacting with the eminence spire. The vote will otherwise pass in 30 seconds.</span>")
if("Nominate Ghosts")
eminence_nominee = "ghosts"
hierophant_message("<span class='brass'><b>[nominee] proposes selecting an Eminence from ghosts!</b> You may object by interacting with the eminence spire. The vote will otherwise pass in 30 seconds.</span>")
for(var/mob/M in servants_and_ghosts())
M.playsound_local(M, 'sound/machines/clockcult/ocularwarden-target.ogg', 50, FALSE)
selection_timer = addtimer(CALLBACK(src, .proc/kingmaker), 300, TIMER_STOPPABLE)
/obj/structure/destructible/clockwork/eminence_spire/proc/objection(mob/living/wright)
if(alert(wright, "Object to the selection of [eminence_nominee] as Eminence?", "Objection!", "Object", "Cancel") == "Cancel" || !is_servant_of_ratvar(wright) || !wright.canUseTopic(src) || !eminence_nominee)
return
hierophant_message("<span class='brass'><b>[wright] objects to the nomination of [eminence_nominee]!</b> The eminence spire has been reset.</span>")
for(var/mob/M in servants_and_ghosts())
M.playsound_local(M, 'sound/machines/clockcult/integration_cog_install.ogg', 50, FALSE)
eminence_nominee = null
deltimer(selection_timer)
/obj/structure/destructible/clockwork/eminence_spire/proc/cancelation(mob/living/cold_feet)
if(alert(cold_feet, "Cancel your nomination?", "Cancel Nomination", "Withdraw Nomination", "Cancel") == "Cancel" || !is_servant_of_ratvar(cold_feet) || !cold_feet.canUseTopic(src) || !eminence_nominee)
return
hierophant_message("<span class='brass'><b>[eminence_nominee] has withdrawn their nomination!</b> The eminence spire has been reset.</span>")
for(var/mob/M in servants_and_ghosts())
M.playsound_local(M, 'sound/machines/clockcult/integration_cog_install.ogg', 50, FALSE)
eminence_nominee = null
deltimer(selection_timer)
/obj/structure/destructible/clockwork/eminence_spire/proc/kingmaker()
if(!eminence_nominee)
return
if(ismob(eminence_nominee))
if(!eminence_nominee.client || !eminence_nominee.mind)
hierophant_message("<span class='brass'><b>[eminence_nominee] somehow lost their sentience!</b> The eminence spire has been reset.</span>")
for(var/mob/M in servants_and_ghosts())
M.playsound_local(M, 'sound/machines/clockcult/integration_cog_install.ogg', 50, FALSE)
eminence_nominee = null
return
playsound(eminence_nominee, 'sound/machines/clockcult/ark_damage.ogg', 50, FALSE)
eminence_nominee.visible_message("<span class='warning'>A blast of white-hot light flows into [eminence_nominee], vaporizing them in an instant!</span>", \
"<span class='userdanger'>allthelightintheuniverseflowing.into.YOU</span>")
for(var/obj/item/I in eminence_nominee)
eminence_nominee.dropItemToGround(I)
var/mob/camera/eminence/eminence = new(get_turf(src))
eminence_nominee.mind.transfer_to(eminence)
eminence_nominee.dust()
hierophant_message("<span class='bold large_brass'>[eminence_nominee] has ascended into the Eminence!</span>")
else if(eminence_nominee == "ghosts")
kingmaking = TRUE
hierophant_message("<span class='brass'><b>The eminence spire is now selecting a ghost to be the Eminence...</b></span>")
var/list/candidates = pollGhostCandidates("Would you like to play as the servants' Eminence?", "Servant of Ratvar", null, ROLE_SERVANT_OF_RATVAR, poll_time = 100)
kingmaking = FALSE
if(!candidates.len)
for(var/mob/M in servants_and_ghosts())
M.playsound_local(M, 'sound/machines/clockcult/integration_cog_install.ogg', 50, FALSE)
hierophant_message("<span class='brass'><b>No ghosts accepted the offer!</b> The eminence spire has been reset.</span>")
eminence_nominee = null
return
visible_message("<span class='warning'>A blast of white-hot light spirals from [src] in waves!</span>")
playsound(src, 'sound/machines/clockcult/ark_damage.ogg', 50, FALSE)
var/mob/camera/eminence/eminence = new(get_turf(src))
eminence_nominee = pick(candidates)
eminence.key = eminence_nominee.key
hierophant_message("<span class='bold large_brass'>A ghost has ascended into the Eminence!</span>")
for(var/mob/M in servants_and_ghosts())
M.playsound_local(M, 'sound/machines/clockcult/eminence_selected.ogg', 50, FALSE)
eminence_nominee = null
@@ -1,109 +0,0 @@
//Used to "declare war" against the station. The servants' equipment will be permanently supercharged, and the Ark given extra time to prepare.
//This will send an announcement to the station, meaning that they will be warned very early in advance about the impending attack.
/obj/structure/destructible/clockwork/heralds_beacon
name = "herald's beacon"
desc = "An imposing spire formed of brass, with a thrumming gemstone at its peak."
clockwork_desc = "A massively-powerful beacon. If enough servants decide to activate it, it will send an incredibly large energy pulse to the Ark, \
permanently empowering many clockwork objects and reducing all power costs by 50%, but alerting the crew to your presence. It doesn't have enough \
energy to sustain itself for long, and if not activated within five minutes, it will permanently shut down."
icon_state = "interdiction_lens"
break_message = "<span class='warning'>The beacon crackles with power before collapsing into pieces!</span>"
max_integrity = 250
light_color = "#EF078E"
var/time_remaining = 300 //Amount of seconds left to vote on whether or not to activate the beacon
var/list/voters //People who have voted to activate the beacon
var/votes_needed = 0 //How many votes are needed to activate the beacon
var/available = FALSE //If the beacon can be used
/obj/structure/destructible/clockwork/heralds_beacon/Initialize()
. = ..()
voters = list()
START_PROCESSING(SSprocessing, src)
/obj/structure/destructible/clockwork/heralds_beacon/Destroy()
STOP_PROCESSING(SSprocessing, src)
. = ..()
/obj/structure/destructible/clockwork/heralds_beacon/process()
if(!available)
if(istype(SSticker.mode, /datum/game_mode/clockwork_cult))
available = TRUE
else
return
if(!SSticker.mode.servants_of_ratvar.len)
return
if(!votes_needed)
var/servants = SSticker.mode.servants_of_ratvar.len
if(servants)
votes_needed = round(servants * 0.66)
time_remaining--
if(!time_remaining)
hierophant_message("<span class='bold sevtug_small'>[src] has lost its power, and can no longer be activated.</span>")
for(var/mob/M in GLOB.player_list)
if(isobserver(M) || is_servant_of_ratvar(M))
M.playsound_local(M, 'sound/magic/blind.ogg', 50, FALSE)
available = FALSE
icon_state = "interdiction_lens_unwrenched"
STOP_PROCESSING(SSprocessing, src)
/obj/structure/destructible/clockwork/heralds_beacon/examine(mob/user)
..()
if(isobserver(user) || is_servant_of_ratvar(user))
if(!available)
if(!GLOB.ratvar_approaches)
to_chat(user, "<span class='bold alloy'>It can no longer be activated.</span>")
else
to_chat(user, "<span class='bold neovgre_small'>It has been activated!</span>")
else
to_chat(user, "<span class='brass'>There are <b>[time_remaining]</b> second[time_remaining != 1 ? "s" : ""] remaining to vote.</span>")
to_chat(user, "<span class='big brass'>There are <b>[voters.len]/[votes_needed]</b> votes to activate the beacon!</span>")
/obj/structure/destructible/clockwork/heralds_beacon/attack_hand(mob/living/user)
if(!is_servant_of_ratvar(user))
to_chat(user, "<span class='notice'>You can tell how powerful [src] is; you know better than to touch it.</span>")
return
if(!available)
to_chat(user, "<span class='danger'>You can no longer vote with [src].</span>")
return
var/voting = !(user.key in voters)
if(alert(user, "[voting ? "Cast a" : "Undo your"] vote to activate the beacon?", "Herald's Beacon", "Change Vote", "Cancel") == "Cancel")
return
if(!user.canUseTopic(src) || !is_servant_of_ratvar(user) || !available)
return
if(voting)
if(user.key in voters)
return
voters += user.key
else
if(!user.key in voters)
return
voters -= user.key
var/votes_left = votes_needed - voters.len
message_admins("[ADMIN_LOOKUPFLW(user)] has [voting ? "voted" : "undone their vote"] to activate [src]! [ADMIN_JMP(user)]")
hierophant_message("<span class='brass'><b>[user.real_name]</b> has [voting ? "voted" : "undone their vote"] to activate [src]! The beacon needs [votes_left] more votes to activate.")
for(var/mob/M in GLOB.player_list)
if(isobserver(M) || is_servant_of_ratvar(M))
M.playsound_local(M, 'sound/magic/clockwork/fellowship_armory.ogg', 50, FALSE)
if(!votes_left)
herald_the_justiciar()
/obj/structure/destructible/clockwork/heralds_beacon/proc/herald_the_justiciar()
priority_announce("A powerful group of fanatical zealots following the cause of Ratvar have brazenly sacrificed stealth for power, and dare anyone \
to try and stop them.", title = "The Justiciar Comes", sound = 'sound/magic/clockwork/ark_activation.ogg')
GLOB.ratvar_approaches = TRUE
available = FALSE
STOP_PROCESSING(SSprocessing, src)
icon_state = "interdiction_lens_active"
hierophant_message("<span class='big bold brass'>The beacon's activation has given your team great power! Many of your objects are permanently empowered!</span>")
for(var/mob/living/simple_animal/hostile/clockwork/C in GLOB.all_clockwork_mobs)
if(C.stat == DEAD)
continue
C.update_values()
to_chat(C, C.empower_string)
for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
if(is_servant_of_ratvar(H))
to_chat(H, "<span class='bold alloy'>The beacon's power warps your body into a clockwork form! You are now immune to many hazards, and your body is more robust against damage!</span>")
H.set_species(/datum/species/golem/clockwork/no_scrap)
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++
@@ -1,143 +0,0 @@
//Interdiction Lens: A powerful artifact that constantly disrupts electronics and drains power but, if it fails to find something to disrupt, turns off.
/obj/structure/destructible/clockwork/powered/interdiction_lens
name = "interdiction lens"
desc = "An ominous, double-pronged brass totem. There's a strange gemstone clasped between the pincers."
clockwork_desc = "A powerful totem that constantly drains nearby electronics and funnels the power drained into nearby Sigils of Transmission or the area's APC."
icon_state = "interdiction_lens"
construction_value = 20
active_icon = "interdiction_lens_active"
inactive_icon = "interdiction_lens"
unanchored_icon = "interdiction_lens_unwrenched"
break_message = "<span class='warning'>The lens flares a blinding violet before the totem beneath it shatters!</span>"
break_sound = 'sound/effects/glassbr3.ogg'
debris = list(/obj/item/clockwork/alloy_shards/small = 2, \
/obj/item/clockwork/alloy_shards/large = 2, \
/obj/item/clockwork/component/belligerent_eye/lens_gem = 1)
var/recharging = 0 //world.time when the lens was last used
var/recharge_time = 1200 //if it drains no power and affects no objects, it turns off for two minutes
var/disabled = FALSE //if it's actually usable
var/interdiction_range = 14 //how large an area it drains and disables in
var/static/list/rage_messages = list("...", "Disgusting.", "Die.", "Foul.", "Worthless.", "Mortal.", "Unfit.", "Weak.", "Fragile.", "Useless.", "Leave my sight!")
/obj/structure/destructible/clockwork/powered/interdiction_lens/Initialize()
. = ..()
update_current_glow()
/obj/structure/destructible/clockwork/powered/interdiction_lens/examine(mob/user)
..()
to_chat(user, "<span class='[recharging > world.time ? "neovgre_small":"brass"]'>Its gemstone [recharging > world.time ? "has been breached by writhing tendrils of blackness that cover the totem" \
: "vibrates in place and thrums with power"].</span>")
if(is_servant_of_ratvar(user) || isobserver(user))
to_chat(user, "<span class='neovgre_small'>If it fails to drain any electronics or has nothing to return power to, it will disable itself for <b>[round(recharge_time/600, 1)]</b> minutes.</span>")
/obj/structure/destructible/clockwork/powered/interdiction_lens/update_anchored(mob/user, do_damage)
..()
update_current_glow()
/obj/structure/destructible/clockwork/powered/interdiction_lens/toggle(fast_process, mob/living/user)
. = ..()
update_current_glow()
/obj/structure/destructible/clockwork/powered/interdiction_lens/proc/update_current_glow()
if(active)
if(disabled)
set_light(2, 1.6, "#151200")
else
set_light(2, 1.6, "#EE54EE")
else
if(anchored)
set_light(1.4, 0.8, "#F42B9D")
else
set_light(0)
/obj/structure/destructible/clockwork/powered/interdiction_lens/attack_hand(mob/living/user)
if(user.canUseTopic(src, !issilicon(user), NO_DEXTERY))
if(disabled)
to_chat(user, "<span class='warning'>As you place your hand on the gemstone, cold tendrils of black matter crawl up your arm. You quickly pull back.</span>")
return 0
toggle(0, user)
/obj/structure/destructible/clockwork/powered/interdiction_lens/forced_disable(bad_effects)
if(disabled || !anchored)
return FALSE
if(!active)
toggle(0)
visible_message("<span class='warning'>The gemstone suddenly turns horribly dark, writhing tendrils covering it!</span>")
recharging = world.time + recharge_time
flick("interdiction_lens_discharged", src)
icon_state = "interdiction_lens_inactive"
disabled = TRUE
update_current_glow()
return TRUE
/obj/structure/destructible/clockwork/powered/interdiction_lens/process()
. = ..()
if(recharging > world.time)
return
if(disabled)
visible_message("<span class='warning'>The writhing tendrils return to the gemstone, which begins to glow with power!</span>")
flick("interdiction_lens_recharged", src)
disabled = FALSE
toggle(0)
else
if(!check_apc_and_sigils())
forced_disable()
return
var/successfulprocess = FALSE
var/power_drained = 0
var/list/atoms_to_test = list()
for(var/A in spiral_range_turfs(interdiction_range, src))
var/turf/T = A
for(var/M in T)
atoms_to_test |= M
CHECK_TICK
var/unconverted_ai = FALSE
var/efficiency = get_efficiency_mod()
var/rage_modifier = get_efficiency_mod(TRUE)
for(var/i in GLOB.ai_list)
var/mob/living/silicon/ai/AI = i
if(AI && AI.stat != DEAD && !is_servant_of_ratvar(AI))
unconverted_ai = TRUE
for(var/M in atoms_to_test)
var/atom/movable/A = M
if(!A || QDELETED(A) || A == target_apc)
continue
power_drained += Floor(A.power_drain(TRUE) * efficiency, MIN_CLOCKCULT_POWER)
if(prob(1 * rage_modifier))
to_chat(A, "<span class='neovgre'>\"[text2ratvar(pick(rage_messages))]\"</span>")
if(prob(100 * (efficiency * efficiency)))
if(istype(A, /obj/machinery/camera) && unconverted_ai)
var/obj/machinery/camera/C = A
if(C.isEmpProof() || !C.status)
continue
successfulprocess = TRUE
if(C.emped)
continue
C.emp_act(EMP_HEAVY)
else if(istype(A, /obj/item/device/radio))
var/obj/item/device/radio/O = A
successfulprocess = TRUE
if(O.emped || !O.on)
continue
O.emp_act(EMP_HEAVY)
else if((isliving(A) && !is_servant_of_ratvar(A)) || istype(A, /obj/structure/closet) || istype(A, /obj/item/storage)) //other things may have radios in them but we don't care
for(var/obj/item/device/radio/O in A.GetAllContents())
successfulprocess = TRUE
if(O.emped || !O.on)
continue
O.emp_act(EMP_HEAVY)
CHECK_TICK
if(power_drained && power_drained >= MIN_CLOCKCULT_POWER && return_power(power_drained))
successfulprocess = TRUE
playsound(src, 'sound/items/pshoom.ogg', 50 * efficiency, 1, interdiction_range-7, 1)
if(!successfulprocess)
forced_disable()
@@ -1,63 +0,0 @@
//Mania Motor: A pair of antenna that, while active, cause a variety of negative mental effects in nearby human mobs.
/obj/structure/destructible/clockwork/powered/mania_motor
name = "mania motor"
desc = "A pair of antenna with what appear to be sockets around the base. It reminds you of an antlion."
clockwork_desc = "A transmitter that allows Sevtug to whisper into the minds of nearby non-servants, causing a variety of negative mental effects, up to and including conversion."
icon_state = "mania_motor_inactive"
active_icon = "mania_motor"
inactive_icon = "mania_motor_inactive"
unanchored_icon = "mania_motor_unwrenched"
construction_value = 20
break_message = "<span class='warning'>The antenna break off, leaving a pile of shards!</span>"
max_integrity = 100
light_color = "#AF0AAF"
debris = list(/obj/item/clockwork/alloy_shards/large = 2, \
/obj/item/clockwork/alloy_shards/small = 2, \
/obj/item/clockwork/component/geis_capacitor/antennae = 1)
var/mania_cost = 150
/obj/structure/destructible/clockwork/powered/mania_motor/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
to_chat(user, "<span class='sevtug_small'>It requires <b>[DisplayPower(mania_cost)]</b> to run.</span>")
/obj/structure/destructible/clockwork/powered/mania_motor/forced_disable(bad_effects)
if(active)
if(bad_effects)
try_use_power(MIN_CLOCKCULT_POWER*4)
visible_message("<span class='warning'>[src] hums loudly, then the sockets at its base fall dark!</span>")
playsound(src, 'sound/effects/screech.ogg', 40, 1)
toggle()
return TRUE
/obj/structure/destructible/clockwork/powered/mania_motor/attack_hand(mob/living/user)
if(user.canUseTopic(src, !issilicon(user), NO_DEXTERY) && is_servant_of_ratvar(user))
if(!can_access_clockwork_power(src, mania_cost))
to_chat(user, "<span class='warning'>[src] needs more power to function!</span>")
return 0
toggle(0, user)
/obj/structure/destructible/clockwork/powered/mania_motor/toggle(fast_process, mob/living/user)
. = ..()
if(active)
set_light(2, 0.9)
else
set_light(0)
/obj/structure/destructible/clockwork/powered/mania_motor/process()
if(!try_use_power(mania_cost))
forced_disable(FALSE)
return
var/efficiency = get_efficiency_mod()
for(var/mob/living/carbon/human/H in viewers(7, src))
if(is_servant_of_ratvar(H))
continue
var/list/effects = H.has_status_effect_list(STATUS_EFFECT_MANIAMOTOR)
var/datum/status_effect/maniamotor/M
for(var/datum/status_effect/maniamotor/MM in effects)
if(MM.motor == src)
M = MM
break
if(!M)
M = H.apply_status_effect(STATUS_EFFECT_MANIAMOTOR, src)
M.severity = CLAMP(M.severity + ((11 - get_dist(src, H)) * efficiency * efficiency), 0, MAX_MANIA_SEVERITY)
@@ -1,161 +0,0 @@
//Ocular warden: Low-damage, low-range turret. Deals constant damage to whoever it makes eye contact with.
/obj/structure/destructible/clockwork/ocular_warden
name = "ocular warden"
desc = "A large brass eye with tendrils trailing below it and a wide red iris."
clockwork_desc = "A fragile turret which will automatically attack nearby unrestrained non-Servants that can see it."
icon_state = "ocular_warden"
unanchored_icon = "ocular_warden_unwrenched"
max_integrity = 25
construction_value = 15
layer = WALL_OBJ_LAYER
break_message = "<span class='warning'>The warden's eye gives a glare of utter hate before falling dark!</span>"
debris = list(/obj/item/clockwork/component/belligerent_eye/blind_eye = 1)
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
var/damage_per_tick = 3
var/sight_range = 3
var/atom/movable/target
var/list/idle_messages = list(" sulkily glares around.", " lazily drifts from side to side.", " looks around for something to burn.", " slowly turns in circles.")
/obj/structure/destructible/clockwork/ocular_warden/Initialize()
. = ..()
START_PROCESSING(SSfastprocess, src)
/obj/structure/destructible/clockwork/ocular_warden/Destroy()
STOP_PROCESSING(SSfastprocess, src)
return ..()
/obj/structure/destructible/clockwork/ocular_warden/examine(mob/user)
..()
to_chat(user, "<span class='brass'>[target ? "<b>It's fixated on [target]!</b>" : "Its gaze is wandering aimlessly."]</span>")
/obj/structure/destructible/clockwork/ocular_warden/hulk_damage()
return 25
/obj/structure/destructible/clockwork/ocular_warden/can_be_unfasten_wrench(mob/user, silent)
if(!anchored)
for(var/obj/structure/destructible/clockwork/ocular_warden/W in orange(OCULAR_WARDEN_EXCLUSION_RANGE, src))
if(W.anchored)
if(!silent)
to_chat(user, "<span class='neovgre'>You sense another ocular warden too near this location. Activating this one this close would cause them to fight.</span>")
return FAILED_UNFASTEN
return SUCCESSFUL_UNFASTEN
/obj/structure/destructible/clockwork/ocular_warden/ratvar_act()
..()
if(GLOB.ratvar_awakens)
damage_per_tick = 10
sight_range = 6
else
damage_per_tick = initial(damage_per_tick)
sight_range = initial(sight_range)
/obj/structure/destructible/clockwork/ocular_warden/process()
if(!anchored)
lose_target()
return
var/list/validtargets = acquire_nearby_targets()
if(target)
if(!(target in validtargets))
lose_target()
else
if(isliving(target))
var/mob/living/L = target
if(!L.null_rod_check())
if(isrevenant(L))
var/mob/living/simple_animal/revenant/R = L
if(R.revealed)
R.unreveal_time += 2
else
R.reveal(10)
if(prob(50))
L.playsound_local(null,'sound/machines/clockcult/ocularwarden-dot1.ogg',75 * get_efficiency_mod(),1)
else
L.playsound_local(null,'sound/machines/clockcult/ocularwarden-dot2.ogg',75 * get_efficiency_mod(),1)
L.adjustFireLoss((!iscultist(L) ? damage_per_tick : damage_per_tick * 2) * get_efficiency_mod()) //Nar-Sian cultists take additional damage
if(GLOB.ratvar_awakens && L)
L.adjust_fire_stacks(damage_per_tick)
L.IgniteMob()
else if(ismecha(target))
var/obj/mecha/M = target
M.take_damage(damage_per_tick * get_efficiency_mod(), BURN, "melee", 1, get_dir(src, M))
new /obj/effect/temp_visual/ratvar/ocular_warden(get_turf(target))
setDir(get_dir(get_turf(src), get_turf(target)))
if(!target)
if(validtargets.len)
target = pick(validtargets)
playsound(src,'sound/machines/clockcult/ocularwarden-target.ogg',50,1)
visible_message("<span class='warning'>[src] swivels to face [target]!</span>")
if(isliving(target))
var/mob/living/L = target
to_chat(L, "<span class='neovgre'>\"I SEE YOU!\"</span>\n<span class='userdanger'>[src]'s gaze [GLOB.ratvar_awakens ? "melts you alive" : "burns you"]!</span>")
else if(ismecha(target))
var/obj/mecha/M = target
to_chat(M.occupant, "<span class='neovgre'>\"I SEE YOU!\"</span>" )
else if(prob(0.5)) //Extremely low chance because of how fast the subsystem it uses processes
if(prob(50))
visible_message("<span class='notice'>[src][pick(idle_messages)]</span>")
else
setDir(pick(GLOB.cardinals))//Random rotation
/obj/structure/destructible/clockwork/ocular_warden/proc/acquire_nearby_targets()
. = list()
for(var/mob/living/L in viewers(sight_range, src)) //Doesn't attack the blind
var/obj/item/storage/book/bible/B = L.bible_check()
if(B)
if(!(B.resistance_flags & ON_FIRE))
to_chat(L, "<span class='warning'>Your [B.name] bursts into flames!</span>")
for(var/obj/item/storage/book/bible/BI in L.GetAllContents())
if(!(BI.resistance_flags & ON_FIRE))
BI.fire_act()
continue
if(is_servant_of_ratvar(L) || (L.has_disability(DISABILITY_BLIND)) || L.null_rod_check())
continue
if(L.stat || L.restrained() || L.buckled || L.lying)
continue
if(ishostile(L))
var/mob/living/simple_animal/hostile/H = L
if(("ratvar" in H.faction) || (!H.mind && "neutral" in H.faction))
continue
if(ismegafauna(H) || (!H.mind && H.AIStatus == AI_OFF))
continue
else if(isrevenant(L))
var/mob/living/simple_animal/revenant/R = L
if(R.stasis) //Don't target any revenants that are respawning
continue
else if(!L.mind)
continue
. += L
var/list/viewcache = list()
for(var/N in GLOB.mechas_list)
var/obj/mecha/M = N
if(get_dist(M, src) <= sight_range && M.occupant && !is_servant_of_ratvar(M.occupant))
if(!length(viewcache))
for (var/obj/Z in view(sight_range, src))
viewcache += Z
if (M in viewcache)
. += M
/obj/structure/destructible/clockwork/ocular_warden/proc/lose_target()
if(!target)
return 0
target = null
visible_message("<span class='warning'>[src] settles and seems almost disappointed.</span>")
return 1
/obj/structure/destructible/clockwork/ocular_warden/get_efficiency_mod()
if(GLOB.ratvar_awakens)
return 2
. = 1
if(target)
for(var/turf/T in getline(src, target))
if(T.density)
. -= 0.1
continue
for(var/obj/structure/O in T)
if(O != src && O.density)
. -= 0.1
break
. -= (get_dist(src, target) * 0.05)
. = max(., 0.1) //The lowest damage a warden can do is 10% of its normal amount (0.25 by default)
@@ -1,150 +0,0 @@
//Ratvar himself. Impossible to damage by most standard means, and converts nearby objects and players into clockwork variants and Servants.
/obj/structure/destructible/clockwork/massive/ratvar
name = "Ratvar, the Clockwork Justiciar"
desc = "..."
clockwork_desc = "<span class='large_brass bold italics'>Ratvar, free at last!</span>"
icon = 'icons/effects/512x512.dmi'
icon_state = "ratvar"
pixel_x = -235
pixel_y = -248
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
appearance_flags = 0
light_power = 0.7
light_range = 15
light_color = "#BE8700"
var/atom/prey //Whatever Ratvar is chasing
var/clashing = FALSE //If Ratvar is fighting with Nar-Sie
var/convert_range = 10
dangerous_possession = TRUE
/obj/structure/destructible/clockwork/massive/ratvar/Initialize()
. = ..()
GLOB.ratvar_awakens++
for(var/obj/O in GLOB.all_clockwork_objects)
O.ratvar_act()
for(var/mob/living/simple_animal/hostile/clockwork/M in GLOB.all_clockwork_mobs)
M.ratvar_act()
START_PROCESSING(SSobj, src)
send_to_playing_players("<span class='ratvar'>[text2ratvar("ONCE AGAIN MY LIGHT SHINES AMONG THESE PATHETIC STARS")]</span>")
sound_to_playing_players('sound/effects/ratvar_reveal.ogg')
var/mutable_appearance/alert_overlay = mutable_appearance('icons/effects/clockwork_effects.dmi', "ratvar_alert")
notify_ghosts("The Justiciar's light calls to you! Reach out to Ratvar in [get_area_name(src)] to be granted a shell to spread his glory!", null, source = src, alert_overlay = alert_overlay)
INVOKE_ASYNC(SSshuttle.emergency, /obj/docking_port/mobile/emergency.proc/request, null, 10, null, FALSE, 0)
/obj/structure/destructible/clockwork/massive/ratvar/Destroy()
GLOB.ratvar_awakens--
for(var/obj/O in GLOB.all_clockwork_objects)
O.ratvar_act()
STOP_PROCESSING(SSobj, src)
return ..()
/obj/structure/destructible/clockwork/massive/ratvar/attack_ghost(mob/dead/observer/O)
var/alertresult = alert(O, "Embrace the Justiciar's light? You can no longer be cloned!",,"Yes", "No")
if(alertresult == "No" || QDELETED(O) || !istype(O) || !O.key)
return FALSE
var/mob/living/simple_animal/drone/cogscarab/ratvar/R = new/mob/living/simple_animal/drone/cogscarab/ratvar(get_turf(src))
R.visible_message("<span class='heavy_brass'>[R] forms, and its eyes blink open, glowing bright red!</span>")
R.key = O.key
/obj/structure/destructible/clockwork/massive/ratvar/Collide(atom/A)
var/turf/T = get_turf(A)
if(T == loc)
T = get_step(T, dir) //please don't run into a window like a bird, ratvar
forceMove(T)
/obj/structure/destructible/clockwork/massive/ratvar/Process_Spacemove()
return clashing
/obj/structure/destructible/clockwork/massive/ratvar/process()
if(clashing) //I'm a bit occupied right now, thanks
return
for(var/I in circlerangeturfs(src, convert_range))
var/turf/T = I
T.ratvar_act()
for(var/I in circleviewturfs(src, round(convert_range * 0.5)))
var/turf/T = I
T.ratvar_act(TRUE)
var/dir_to_step_in = pick(GLOB.cardinals)
var/list/meals = list()
for(var/mob/living/L in GLOB.alive_mob_list) //we want to know who's alive so we don't lose and retarget a single person
if(L.z == z && !is_servant_of_ratvar(L) && L.mind)
meals += L
if(GLOB.cult_narsie && GLOB.cult_narsie.z == z)
meals = list(GLOB.cult_narsie) //if you're in the way, handy for him, but ratvar only cares about nar-sie!
prey = GLOB.cult_narsie
if(get_dist(src, prey) <= 10)
clash()
return
if(!prey)
if(!prey && LAZYLEN(meals))
var/mob/living/L = prey
prey = pick(meals)
to_chat(prey, "<span class='heavy_brass'><font size=5>\"You will do, heretic.\"</font></span>\n\
<span class='userdanger'>You feel something massive turn its crushing focus to you...</span>")
L.playsound_local(prey, 'sound/effects/ratvar_reveal.ogg', 100, FALSE, pressure_affected = FALSE)
else
if((!istype(prey, /obj/singularity/narsie) && prob(10) && LAZYLEN(meals) > 1) || prey.z != z || !(prey in meals))
if(is_servant_of_ratvar(prey))
to_chat(prey, "<span class='heavy_brass'><font size=5>\"Serve me well.\"</font></span>\n\
<span class='big_brass'>You feel great joy as your god turns His eye to another heretic...</span>")
else
to_chat(prey, "<span class='heavy_brass'><font size=5>\"No matter. I will find you later, heretic.\"</font></span>\n\
<span class='userdanger'>You feel tremendous relief as the crushing focus relents...</span>")
prey = null
else
dir_to_step_in = get_dir(src, prey) //Unlike Nar-Sie, Ratvar ruthlessly chases down his target
step(src, dir_to_step_in)
/obj/structure/destructible/clockwork/massive/ratvar/proc/clash()
if(clashing || prey != GLOB.cult_narsie)
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>")
clash_of_the_titans(GLOB.cult_narsie) // >:(
return TRUE
//Put me in Reebe, will you? Ratvar has found and is going to do a hecking murder on Nar-Sie
/obj/structure/destructible/clockwork/massive/ratvar/proc/clash_of_the_titans(obj/singularity/narsie/narsie)
var/winner = "Undeclared"
var/base_victory_chance = 1
while(src && narsie)
sound_to_playing_players('sound/magic/clockwork/ratvar_attack.ogg')
sleep(5.2)
for(var/mob/M in GLOB.mob_list)
if(!isnewplayer(M))
flash_color(M, flash_color="#966400", flash_time=1)
shake_camera(M, 4, 3)
var/ratvar_chance = min(LAZYLEN(SSticker.mode.servants_of_ratvar), 50)
var/narsie_chance = min(LAZYLEN(SSticker.mode.cult), 50)
ratvar_chance = rand(base_victory_chance, ratvar_chance)
narsie_chance = rand(base_victory_chance, narsie_chance)
if(ratvar_chance > narsie_chance)
winner = "Ratvar"
break
sleep(rand(2,5))
sound_to_playing_players('sound/magic/clockwork/narsie_attack.ogg')
sleep(7.4)
for(var/mob/M in GLOB.mob_list)
if(!isnewplayer(M))
flash_color(M, flash_color="#C80000", flash_time=1)
shake_camera(M, 4, 3)
if(narsie_chance > ratvar_chance)
winner = "Nar-Sie"
break
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\
<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)
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)
narsie.clashing = FALSE
qdel(src)
@@ -1,70 +0,0 @@
#define STARGAZER_RANGE 3 //How many tiles the stargazer can see out to
#define STARGAZER_POWER 20 //How many watts will be produced per second when the stargazer sees starlight
//Stargazer: A very fragile but cheap generator that creates power from starlight.
/obj/structure/destructible/clockwork/stargazer
name = "stargazer"
desc = "A large lantern-shaped machine made of thin brass. It looks fragile."
clockwork_desc = "A lantern-shaped generator that produces power when near starlight."
icon_state = "stargazer"
unanchored_icon = "stargazer_unwrenched"
max_integrity = 40
construction_value = 5
layer = WALL_OBJ_LAYER
break_message = "<span class='warning'>The stargazer's fragile body shatters into pieces!</span>"
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
light_color = "#DAAA18"
var/star_light_star_bright = FALSE //If this stargazer can see starlight
/obj/structure/destructible/clockwork/stargazer/Initialize()
. = ..()
START_PROCESSING(SSprocessing, src)
/obj/structure/destructible/clockwork/stargazer/Destroy()
STOP_PROCESSING(SSprocessing, src)
. = ..()
/obj/structure/destructible/clockwork/stargazer/examine(mob/user)
..()
if(is_servant_of_ratvar(user))
to_chat(user, "<span class='nzcrentr_small'>Generates <b>[DisplayPower(STARGAZER_POWER)]</b> per second while viewing starlight within [STARGAZER_RANGE] tiles.</span>")
if(star_light_star_bright)
to_chat(user, "[is_servant_of_ratvar(user) ? "<span class='nzcrentr_small'>It can see starlight!</span>" : "It's shining brilliantly!"]")
/obj/structure/destructible/clockwork/stargazer/process()
star_light_star_bright = check_starlight()
if(star_light_star_bright)
adjust_clockwork_power(STARGAZER_POWER)
/obj/structure/destructible/clockwork/stargazer/update_anchored(mob/living/user, damage)
. = ..()
star_light_star_bright = check_starlight()
/obj/structure/destructible/clockwork/stargazer/proc/check_starlight()
var/old_status = star_light_star_bright
var/has_starlight
if(!anchored)
has_starlight = FALSE
else
for(var/turf/T in view(3, src))
if(isspaceturf(T))
has_starlight = TRUE
break
if(has_starlight && anchored)
var/area/A = get_area(src)
if(A.outdoors || A.map_name == "Space" || !A.blob_allowed)
has_starlight = FALSE
if(old_status != has_starlight)
if(has_starlight)
visible_message("<span class='nzcrentr_small'>[src] hums and shines brilliantly!</span>")
playsound(src, 'sound/machines/clockcult/stargazer_activate.ogg', 50, TRUE)
add_overlay("stargazer_light")
set_light(1.5, 5)
else
if(anchored) //We lost visibility somehow
visible_message("<span class='danger'>[src] flickers, and falls dark.</span>")
else
visible_message("<span class='danger'>[src] whooshes quietly as it slides into a less bulky form.</span>")
cut_overlays()
set_light(0)
return has_starlight
@@ -1,61 +0,0 @@
//Used by the Taunting Tirade scripture as a trail.
/obj/structure/destructible/clockwork/taunting_trail
name = "strange smoke"
desc = "A cloud of purple smoke."
clockwork_desc = "A cloud of purple smoke that confuses and knocks down non-Servants that enter it."
gender = PLURAL
max_integrity = 5
density = TRUE
color = list("#AF0AAF", "#AF0AAF", "#AF0AAF", rgb(0,0,0))
icon = 'icons/effects/effects.dmi'
icon_state = "smoke"
break_message = null
break_sound = 'sound/magic/teleport_app.ogg'
debris = list()
var/timerid
/obj/structure/destructible/clockwork/taunting_trail/Initialize()
. = ..()
timerid = QDEL_IN(src, 15)
var/obj/structure/destructible/clockwork/taunting_trail/Tt = locate(/obj/structure/destructible/clockwork/taunting_trail) in loc
if(Tt && Tt != src)
if(!step(src, pick(GLOB.alldirs)))
qdel(Tt)
else
for(var/obj/structure/destructible/clockwork/taunting_trail/TT in loc)
if(TT != src)
qdel(TT)
setDir(pick(GLOB.cardinals))
transform = matrix()*1.3
animate(src, alpha = 100, time = 15)
/obj/structure/destructible/clockwork/taunting_trail/Destroy()
deltimer(timerid)
return ..()
/obj/structure/destructible/clockwork/taunting_trail/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
playsound(src, 'sound/items/welder.ogg', 50, 1)
/obj/structure/destructible/clockwork/taunting_trail/CanPass(atom/movable/mover, turf/target)
return TRUE
/obj/structure/destructible/clockwork/taunting_trail/Crossed(atom/movable/AM)
affect_mob(AM)
return ..()
/obj/structure/destructible/clockwork/taunting_trail/CollidedWith(atom/movable/AM)
affect_mob(AM)
return ..()
/obj/structure/destructible/clockwork/taunting_trail/Collide(atom/movable/AM)
affect_mob(AM)
return ..()
/obj/structure/destructible/clockwork/taunting_trail/proc/affect_mob(mob/living/L)
if(istype(L) && !is_servant_of_ratvar(L))
if(!L.null_rod_check())
L.confused = min(L.confused + 15, 50)
L.dizziness = min(L.dizziness + 15, 50)
if(L.confused >= 25)
L.Knockdown(FLOOR(L.confused * 0.8, 1))
take_damage(max_integrity)
@@ -1,116 +0,0 @@
//Tinkerer's cache: Stores components for later use.
/obj/structure/destructible/clockwork/cache
name = "tinkerer's cache"
desc = "A large brass spire with a flaming hole in its center."
clockwork_desc = "A brass container capable of storing a large amount of components.\n\
Shares components with all other caches and will gradually generate components if near a Clockwork Wall."
icon_state = "tinkerers_cache"
unanchored_icon = "tinkerers_cache_unwrenched"
construction_value = 10
break_message = "<span class='warning'>The cache's fire winks out before it falls in on itself!</span>"
max_integrity = 80
light_color = "#C2852F"
var/wall_generation_cooldown
var/turf/closed/wall/clockwork/linkedwall //if we've got a linked wall and are producing
var/static/linked_caches = 0 //how many caches are linked to walls; affects how fast components are produced
/obj/structure/destructible/clockwork/cache/Initialize()
. = ..()
START_PROCESSING(SSobj, src)
GLOB.clockwork_caches++
update_slab_info()
set_light(2, 0.7)
/obj/structure/destructible/clockwork/cache/Destroy()
GLOB.clockwork_caches--
update_slab_info()
STOP_PROCESSING(SSobj, src)
if(linkedwall)
linked_caches--
linkedwall.linkedcache = null
linkedwall = null
return ..()
/obj/structure/destructible/clockwork/cache/process()
if(!anchored)
if(linkedwall)
linked_caches--
linkedwall.linkedcache = null
linkedwall = null
return
for(var/turf/closed/wall/clockwork/C in range(4, src))
if(!C.linkedcache && !linkedwall)
linked_caches++
C.linkedcache = src
linkedwall = C
wall_generation_cooldown = world.time + get_production_time()
visible_message("<span class='warning'>[src] starts to whirr in the presence of [C]...</span>")
break
if(linkedwall && wall_generation_cooldown <= world.time)
wall_generation_cooldown = world.time + get_production_time()
var/component_id = generate_cache_component(null, src)
playsound(linkedwall, 'sound/magic/clockwork/fellowship_armory.ogg', rand(15, 20), 1, -3, 1, 1)
visible_message("<span class='[get_component_span(component_id)]'>Something</span><span class='warning'> cl[pick("ank", "ink", "unk", "ang")]s around inside of [src]...</span>")
/obj/structure/destructible/clockwork/cache/attackby(obj/item/I, mob/living/user, params)
if(!is_servant_of_ratvar(user))
return ..()
if(istype(I, /obj/item/clockwork/component))
var/obj/item/clockwork/component/C = I
if(!anchored)
to_chat(user, "<span class='warning'>[src] needs to be secured to place [C] into it!</span>")
else
GLOB.clockwork_component_cache[C.component_id]++
update_slab_info()
to_chat(user, "<span class='notice'>You add [C] to [src].</span>")
user.drop_item()
qdel(C)
return 1
else if(istype(I, /obj/item/clockwork/slab))
var/obj/item/clockwork/slab/S = I
if(!anchored)
to_chat(user, "<span class='warning'>[src] needs to be secured to offload your slab's components into it!</span>")
else
for(var/i in S.stored_components)
GLOB.clockwork_component_cache[i] += S.stored_components[i]
S.stored_components[i] = 0
update_slab_info()
user.visible_message("<span class='notice'>[user] empties [S] into [src].</span>", "<span class='notice'>You offload your slab's components into [src].</span>")
return 1
else
return ..()
/obj/structure/destructible/clockwork/cache/update_anchored(mob/user, do_damage)
..()
if(anchored)
set_light(2, 0.7)
else
set_light(0)
/obj/structure/destructible/clockwork/cache/attack_hand(mob/living/user)
..()
if(is_servant_of_ratvar(user))
if(linkedwall)
if(wall_generation_cooldown > world.time)
var/temp_time = (wall_generation_cooldown - world.time) * 0.1
to_chat(user, "<span class='alloy'>[src] will produce a component in <b>[temp_time]</b> second[temp_time == 1 ? "":"s"].</span>")
else
to_chat(user, "<span class='brass'>[src] is about to produce a component!</span>")
else if(anchored)
to_chat(user, "<span class='alloy'>[src] is unlinked! Construct a Clockwork Wall nearby to generate components!</span>")
else
to_chat(user, "<span class='alloy'>[src] needs to be secured to generate components!</span>")
/obj/structure/destructible/clockwork/cache/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
if(linkedwall)
to_chat(user, "<span class='brass'>It is linked to a Clockwork Wall and will generate a component every <b>[DisplayTimeText(get_production_time())]</b>!</span>")
else
to_chat(user, "<span class='alloy'>It is unlinked! Construct a Clockwork Wall nearby to generate components!</span>")
to_chat(user, "<b>Stored components:</b>")
for(var/i in GLOB.clockwork_component_cache)
to_chat(user, "[get_component_icon(i)] <span class='[get_component_span(i)]_small'><i>[get_component_name(i)][i != REPLICANT_ALLOY ? "s":""]:</i> <b>[GLOB.clockwork_component_cache[i]]</b></span>")
/obj/structure/destructible/clockwork/cache/proc/get_production_time()
return (CACHE_PRODUCTION_TIME + (ACTIVE_CACHE_SLOWDOWN * linked_caches)) * get_efficiency_mod(TRUE)
@@ -1,14 +0,0 @@
//Lever: Do I really need to explain this?
/obj/structure/destructible/clockwork/trap/trigger/lever
name = "lever"
desc = "A fancy lever made of wood and capped with brass."
clockwork_desc = "A fancy lever.that activates when pulled."
max_integrity = 75
icon_state = "lever"
/obj/structure/destructible/clockwork/trap/trigger/lever/attack_hand(mob/living/user)
user.visible_message("<span class='notice'>[user] pulls [src]!</span>", "<span class='notice'>You pull [src]. It clicks, then lifts back upwards.</span>")
if(wired_to.len)
audible_message("<i>You hear gears clanking.</i>")
playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE)
activate()
@@ -1,26 +0,0 @@
//Pressure sensor: Activates when stepped on.
/obj/structure/destructible/clockwork/trap/trigger/pressure_sensor
name = "pressure sensor"
desc = "A thin plate of brass, barely visible but clearly distinct."
clockwork_desc = "A trigger that will activate when a non-servant runs across it."
max_integrity = 5
icon_state = "pressure_sensor"
alpha = 80
layer = LOW_ITEM_LAYER
/obj/structure/destructible/clockwork/trap/trigger/Initialize()
. = ..()
for(var/obj/structure/destructible/clockwork/trap/T in get_turf(src))
if(!istype(T, /obj/structure/destructible/clockwork/trap/trigger))
wired_to += T
T.wired_to += src
to_chat(usr, "<span class='alloy'>[src] automatically links with [T] beneath it.</span>")
/obj/structure/destructible/clockwork/trap/trigger/pressure_sensor/Crossed(atom/movable/AM)
if(isliving(AM) && !is_servant_of_ratvar(AM))
var/mob/living/L = AM
if(L.stat || L.m_intent == MOVE_INTENT_WALK || L.lying)
return
audible_message("<i>*click*</i>")
playsound(src, 'sound/items/screwdriver2.ogg', 50, TRUE)
activate()
@@ -1,27 +0,0 @@
//Repeater: Activates every second.
/obj/structure/destructible/clockwork/trap/trigger/repeater
name = "repeater"
desc = "A small black prism with a gem in the center."
clockwork_desc = "A repeater that will send an activation signal every second."
max_integrity = 15 //Fragile!
icon_state = "repeater"
/obj/structure/destructible/clockwork/trap/trigger/repeater/attack_hand(mob/living/user)
if(!is_servant_of_ratvar(user))
return
if(!isprocessing)
START_PROCESSING(SSprocessing, src)
to_chat(user, "<span class='notice'>You activate [src].</span>")
icon_state = "[icon_state]_on"
else
STOP_PROCESSING(SSprocessing, src)
to_chat(user, "<span class='notice'>You halt [src]'s ticking.</span>")
icon_state = initial(icon_state)
/obj/structure/destructible/clockwork/trap/trigger/repeater/process()
activate()
playsound(src, 'sound/items/screwdriver2.ogg', 25, FALSE)
/obj/structure/destructible/clockwork/trap/trigger/repeater/Destroy()
STOP_PROCESSING(SSprocessing, src)
return ..()
@@ -1,115 +0,0 @@
//Non-servants standing over this will get spikes through the feet, immobilizing them until they're freed.
/obj/structure/destructible/clockwork/trap/brass_skewer
name = "brass skewer"
desc = "A deadly brass spike, cleverly concealed in the floor. You think you should be safe if you disarm whatever's meant to set it off."
clockwork_desc = "A barbaric but undeniably effective weapon: a spear through the chest. It immobilizes anyone unlucky enough to step on it and keeps them in place until they get help.."
icon_state = "brass_skewer"
break_message = "<span class='warning'>The skewer snaps in two!</span>"
max_integrity = 40
density = FALSE
can_buckle = TRUE
buckle_prevents_pull = TRUE
buckle_lying = FALSE
var/wiggle_wiggle
var/mutable_appearance/impale_overlay //This is applied to any mob impaled so that they visibly have the skewer coming through their chest
/obj/structure/destructible/clockwork/trap/brass_skewer/Initialize()
. = ..()
START_PROCESSING(SSfastprocess, src)
/obj/structure/destructible/clockwork/trap/brass_skewer/Destroy()
STOP_PROCESSING(SSfastprocess, src)
if(buckled_mobs && LAZYLEN(buckled_mobs))
var/mob/living/L = buckled_mobs[1]
if(iscarbon(L))
L.Knockdown(100)
L.visible_message("<span class='warning'>[L] is maimed as the skewer shatters while still in their body!</span>")
L.adjustBruteLoss(15)
unbuckle_mob(L)
return ..()
/obj/structure/destructible/clockwork/trap/brass_skewer/process()
if(density)
if(buckled_mobs && LAZYLEN(buckled_mobs))
var/mob/living/spitroast = buckled_mobs[1]
spitroast.adjustBruteLoss(0.1)
/obj/structure/destructible/clockwork/trap/attackby(obj/item/I, mob/living/user, params)
if(buckled_mobs && (user in buckled_mobs))
to_chat(user, "<span class='warning'>You can't reach!</span>")
return
..()
/obj/structure/destructible/clockwork/trap/brass_skewer/bullet_act(obj/item/projectile/P)
if(buckled_mobs && LAZYLEN(buckled_mobs))
var/mob/living/L = buckled_mobs[1]
return L.bullet_act(P)
return ..()
/obj/structure/destructible/clockwork/trap/brass_skewer/activate()
if(density)
return
var/mob/living/squirrel = locate() in get_turf(src)
if(squirrel)
if(iscyborg(squirrel))
if(!squirrel.stat)
squirrel.visible_message("<span class='boldwarning'>A massive brass spike erupts from the ground, rending [squirrel]'s chassis but shattering into pieces!</span>", \
"<span class='userdanger'>A massive brass spike rips through your chassis and bursts into shrapnel in your casing!</span>")
squirrel.adjustBruteLoss(50)
squirrel.Stun(20)
addtimer(CALLBACK(src, .proc/take_damage, max_integrity), 1)
else
squirrel.visible_message("<span class='boldwarning'>A massive brass spike erupts from the ground, impaling [squirrel]!</span>", \
"<span class='userdanger'>A massive brass spike rams through your chest, hoisting you into the air!</span>")
squirrel.emote("scream")
playsound(squirrel, 'sound/effects/splat.ogg', 50, TRUE)
playsound(squirrel, 'sound/misc/desceration-03.ogg', 50, TRUE)
squirrel.apply_damage(20, BRUTE, "chest")
mouse_opacity = MOUSE_OPACITY_OPAQUE //So players can interact with the tile it's on to pull them off
buckle_mob(squirrel, TRUE)
else
visible_message("<span class='danger'>A massive brass spike erupts from the ground!</span>")
playsound(src, 'sound/machines/clockcult/brass_skewer.ogg', 75, FALSE)
icon_state = "[initial(icon_state)]_extended"
density = TRUE //Skewers are one-use only
desc = "A vicious brass spike protruding from the ground like a stala[pick("gm", "ct")]ite. It makes you sick to look at." //is stalagmite the ground one? or the ceiling one? who can ever remember?
/obj/structure/destructible/clockwork/trap/brass_skewer/user_buckle_mob()
return
/obj/structure/destructible/clockwork/trap/brass_skewer/post_buckle_mob(mob/living/L)
if(L in buckled_mobs)
L.pixel_y = 3
impale_overlay = mutable_appearance('icons/obj/clockwork_objects.dmi', "brass_skewer_pokeybit", ABOVE_MOB_LAYER)
add_overlay(impale_overlay)
else
L.pixel_y = initial(L.pixel_y)
L.cut_overlay(impale_overlay)
/obj/structure/destructible/clockwork/trap/brass_skewer/user_unbuckle_mob(mob/living/skewee, mob/living/user)
if(user == skewee)
if(wiggle_wiggle)
return
user.visible_message("<span class='warning'>[user] starts wriggling off of [src]!</span>", \
"<span class='danger'>You start agonizingly working your way off of [src]...</span>")
wiggle_wiggle = TRUE
if(!do_after(user, 300, target = user))
user.visible_message("<span class='warning'>[user] slides back down [src]!</span>")
user.emote("scream")
user.apply_damage(10, BRUTE, "chest")
playsound(user, 'sound/misc/desceration-03.ogg', 50, TRUE)
wiggle_wiggle = FALSE
return
wiggle_wiggle = FALSE
else
user.visible_message("<span class='danger'>[user] starts tenderly lifting [skewee] off of [src]...</span>", \
"<span class='danger'>You start tenderly lifting [skewee] off of [src]...</span>")
if(!do_after(user, 60, target = skewee))
skewee.visible_message("<span class='warning'>[skewee] painfully slides back down [src].</span>")
skewee.emote("moan")
return
skewee.visible_message("<span class='danger'>[skewee] comes free of [src] with a squelching pop!</span>", \
"<span class='boldannounce'>You come free of [src]!</span>")
skewee.Knockdown(30)
playsound(skewee, 'sound/misc/desceration-03.ogg', 50, TRUE)
unbuckle_mob(skewee)
@@ -1,23 +0,0 @@
//This doesn't function like a "trap" in of itself, but obscures vision when active.
/obj/structure/destructible/clockwork/trap/steam_vent
name = "steam vent"
desc = "Some wired slats embedded in the floor. They feel warm to the touch."
icon_state = "steam_vent_0"
clockwork_desc = "When active, these vents will billow out clouds of excess steam from Reebe, obscuring vision."
break_message = "<span class='warning'>The vent snaps and collapses!</span>"
max_integrity = 100
density = FALSE
/obj/structure/destructible/clockwork/trap/steam_vent/activate()
opacity = !opacity
icon_state = "steam_vent_[opacity]"
if(opacity)
playsound(src, 'sound/machines/clockcult/steam_whoosh.ogg', 50, TRUE)
else
playsound(src, 'sound/machines/clockcult/integration_cog_install.ogg', 50, TRUE)
/obj/structure/destructible/clockwork/trap/steam_vent/Crossed(atom/movable/AM)
if(isliving(AM) && opacity)
var/mob/living/L = AM
L.adjust_fire_stacks(-1) //It's wet!
@@ -1,75 +0,0 @@
//A massive gear, effectively a girder for clocks.
/obj/structure/destructible/clockwork/wall_gear
name = "massive gear"
icon_state = "wall_gear"
unanchored_icon = "wall_gear"
climbable = TRUE
max_integrity = 100
layer = BELOW_OBJ_LAYER
construction_value = 3
desc = "A massive brass gear. You could probably secure or unsecure it with a wrench, or just climb over it."
break_message = "<span class='warning'>The gear breaks apart into shards of alloy!</span>"
debris = list(/obj/item/clockwork/alloy_shards/large = 1, \
/obj/item/clockwork/alloy_shards/medium = 4, \
/obj/item/clockwork/alloy_shards/small = 2) //slightly more debris than the default, totals 26 alloy
/obj/structure/destructible/clockwork/wall_gear/displaced
anchored = FALSE
/obj/structure/destructible/clockwork/wall_gear/Initialize()
. = ..()
new /obj/effect/temp_visual/ratvar/gear(get_turf(src))
/obj/structure/destructible/clockwork/wall_gear/emp_act(severity)
return
/obj/structure/destructible/clockwork/wall_gear/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/wrench))
default_unfasten_wrench(user, I, 10)
return 1
else if(istype(I, /obj/item/screwdriver))
if(anchored)
to_chat(user, "<span class='warning'>[src] needs to be unsecured to disassemble it!</span>")
else
playsound(src, I.usesound, 100, 1)
user.visible_message("<span class='warning'>[user] starts to disassemble [src].</span>", "<span class='notice'>You start to disassemble [src]...</span>")
if(do_after(user, 30*I.toolspeed, target = src) && !anchored)
to_chat(user, "<span class='notice'>You disassemble [src].</span>")
deconstruct(TRUE)
return 1
else if(istype(I, /obj/item/stack/tile/brass))
var/obj/item/stack/tile/brass/W = I
if(W.get_amount() < 1)
to_chat(user, "<span class='warning'>You need one brass sheet to do this!</span>")
return
var/turf/T = get_turf(src)
if(iswallturf(T))
to_chat(user, "<span class='warning'>There is already a wall present!</span>")
return
if(!isfloorturf(T))
to_chat(user, "<span class='warning'>A floor must be present to build a [anchored ? "false ":""]wall!</span>")
return
if(locate(/obj/structure/falsewall) in T.contents)
to_chat(user, "<span class='warning'>There is already a false wall present!</span>")
return
to_chat(user, "<span class='notice'>You start adding [W] to [src]...</span>")
if(do_after(user, 20, target = src))
var/brass_floor = FALSE
if(istype(T, /turf/open/floor/clockwork)) //if the floor is already brass, costs less to make(conservation of masssssss)
brass_floor = TRUE
if(W.use(2 - brass_floor))
if(anchored)
T.PlaceOnTop(/turf/closed/wall/clockwork)
else
T.PlaceOnTop(/turf/open/floor/clockwork)
new /obj/structure/falsewall/brass(T)
qdel(src)
else
to_chat(user, "<span class='warning'>You need more brass to make a [anchored ? "false ":""]wall!</span>")
return 1
return ..()
/obj/structure/destructible/clockwork/wall_gear/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1) && disassembled)
new /obj/item/stack/tile/brass(loc, 3)
return ..()
-397
View File
@@ -1,397 +0,0 @@
// Contains cult communion, guide, and cult master abilities
#define MARK_COOLDOWN
/datum/action/innate/cult
icon_icon = 'icons/mob/actions/actions_cult.dmi'
background_icon_state = "bg_demon"
buttontooltipstyle = "cult"
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUN|AB_CHECK_CONSCIOUS
/datum/action/innate/cult/IsAvailable()
if(!iscultist(owner))
return FALSE
return ..()
/datum/action/innate/cult/comm
name = "Communion"
button_icon_state = "cult_comms"
/datum/action/innate/cult/comm/Activate()
var/input = stripped_input(usr, "Please choose a message to tell to the other acolytes.", "Voice of Blood", "")
if(!input || !IsAvailable())
return
cultist_commune(usr, input)
/proc/cultist_commune(mob/living/user, message)
var/my_message
if(!message)
return
user.whisper("O bidai nabora se[pick("'","`")]sma!", language = /datum/language/common)
user.whisper(html_decode(message))
var/title = "Acolyte"
var/span = "cult italic"
if(user.mind && user.mind.has_antag_datum(/datum/antagonist/cult/master))
span = "cultlarge"
if(ishuman(user))
title = "Master"
else
title = "Lord"
else if(!ishuman(user))
title = "Construct"
my_message = "<span class='[span]'><b>[title] [findtextEx(user.name, user.real_name) ? user.name : "[user.real_name] (as [user.name])"]:</b> [message]</span>"
for(var/i in GLOB.player_list)
var/mob/M = i
if(iscultist(M))
to_chat(M, my_message)
else if(M in GLOB.dead_mob_list)
var/link = FOLLOW_LINK(M, user)
to_chat(M, "[link] [my_message]")
log_talk(user,"CULT:[key_name(user)] : [message]",LOGSAY)
/mob/living/proc/cult_help()
set category = "Cultist"
set name = "How to Play Cult"
var/text = ""
text += "<center><font color='red' size=3><b><i>Tenets of the Dark One</i></b></font></center><br><br><br>"
text += "<font color='red'><b>I. SECRECY</b></font><br>Your cult is a SECRET organization. Your success DEPENDS on keeping your cult's members and locations SECRET for as long as possible. This means that your tome should be hidden \
in your bag and never brought out in public. You should never create runes where other crew might find them, and you should avoid using talismans or other cult magic with witnesses around.<br><br>"
text += "<font color='red'><b>II. TOME</b></font><br>You start with a unique talisman in your bag. This supply talisman can be used 3 times, and creates starter equipment for your cult. The most critical of the talisman's functions is \
the power to create a tome. This tome is your most important item and summoning one (in secret) is your FIRST PRIORITY. It lets you talk to fellow cultists and create runes, which in turn is essential to growing the cult's power.<br><br>"
text += "<font color='red'><b>III. RUNES</b></font><br>Runes are powerful sources of cult magic. Your tome will allow you to draw runes with your blood. Those runes, when hit with an empty hand, will attempt to \
trigger the rune's magic. Runes are essential for the cult to convert new members, create powerful minions, or call upon incredibly powerful magic. Some runes require more than one cultist to use.<br><br>"
text += "<font color='red'><b>IV. TALISMANS</b></font><br>Talismans are a mobile source of cult magic that are NECESSARY to achieve success as a cult. Your starting talisman can produce certain talismans, but you will need \
to use the -create talisman- rune (with ordinary paper on top) to get more talismans. Talismans are EXTREMELY powerful, therefore creating more talismans in a HIDDEN location should be one of your TOP PRIORITIES.<br><br>"
text += "<font color='red'><b>V. GROW THE CULT</b></font><br>There are certain basic strategies that all cultists should master. STUN talismans are the foundation of a successful cult. If you intend to convert the stunned person \
you should use cuffs or a talisman of shackling on them and remove their headset before they recover (it takes about 10 seconds to recover). If you intend to sacrifice the victim, striking them quickly and repeatedly with your tome \
will knock them out before they can recover. Sacrificed victims will their soul behind in a shard, these shards can be used on construct shells to make powerful servants for the cult. Remember you need TWO cultists standing near a \
conversion rune to convert someone. Your construct minions cannot trigger most runes, but they will count as cultists in helping you trigger more powerful runes like conversion or blood boil.<br><br>"
text += "<font color='red'><b>VI. VICTORY</b></font><br>You have two ultimate goals as a cultist, sacrifice your target, and summon Nar-Sie. Sacrificing the target involves killing that individual and then placing \
their corpse on a sacrifice rune and triggering that rune with THREE cultists. Do NOT lose the target's corpse! Only once the target is sacrificed can Nar-Sie be summoned. Summoning Nar-Sie will take nearly one minute \
just to draw the massive rune needed. Do not create the rune until your cult is ready, the crew will receive the NAME and LOCATION of anyone who attempts to create the Nar-Sie rune. Once the Nar-Sie rune is drawn \
you must gathered 9 cultists (or constructs) over the rune and then click it to bring the Dark One into this world!<br><br>"
var/datum/browser/popup = new(usr, "mind", "", 800, 600)
popup.set_content(text)
popup.open()
return 1
/datum/action/innate/cult/mastervote
name = "Assert Leadership"
button_icon_state = "cultvote"
/datum/action/innate/cult/mastervote/IsAvailable()
var/datum/antagonist/cult/C = owner.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
if(!C || C.cult_team.cult_vote_called || !ishuman(owner))
return FALSE
return ..()
/datum/action/innate/cult/mastervote/Activate()
var/datum/antagonist/cult/C = owner.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
pollCultists(owner,C.cult_team)
/proc/pollCultists(var/mob/living/Nominee,datum/team/cult/team) //Cult Master Poll
if(world.time < CULT_POLL_WAIT)
to_chat(Nominee, "It would be premature to select a leader while everyone is still settling in, try again in [DisplayTimeText(CULT_POLL_WAIT-world.time)].")
return
team.cult_vote_called = TRUE //somebody's trying to be a master, make sure we don't let anyone else try
for(var/datum/mind/B in team.members)
if(B.current)
B.current.update_action_buttons_icon()
if(!B.current.incapacitated())
SEND_SOUND(B.current, 'sound/hallucinations/im_here1.ogg')
to_chat(B.current, "<span class='cultlarge'>Acolyte [Nominee] has asserted that they are worthy of leading the cult. A vote will be called shortly.</span>")
sleep(100)
var/list/asked_cultists = list()
for(var/datum/mind/B in team.members)
if(B.current && B.current != Nominee && !B.current.incapacitated())
SEND_SOUND(B.current, 'sound/magic/exit_blood.ogg')
asked_cultists += B.current
var/list/yes_voters = pollCandidates("[Nominee] seeks to lead your cult, do you support [Nominee.p_them()]?", poll_time = 300, group = asked_cultists)
if(QDELETED(Nominee) || Nominee.incapacitated())
team.cult_vote_called = FALSE
for(var/datum/mind/B in team.members)
if(B.current)
B.current.update_action_buttons_icon()
if(!B.current.incapacitated())
to_chat(B.current,"<span class='cultlarge'>[Nominee] has died in the process of attempting to win the cult's support!</span>")
return FALSE
if(!Nominee.mind)
team.cult_vote_called = FALSE
for(var/datum/mind/B in team.members)
if(B.current)
B.current.update_action_buttons_icon()
if(!B.current.incapacitated())
to_chat(B.current,"<span class='cultlarge'>[Nominee] has gone catatonic in the process of attempting to win the cult's support!</span>")
return FALSE
if(LAZYLEN(yes_voters) <= LAZYLEN(asked_cultists) * 0.5)
team.cult_vote_called = FALSE
for(var/datum/mind/B in team.members)
if(B.current)
B.current.update_action_buttons_icon()
if(!B.current.incapacitated())
to_chat(B.current, "<span class='cultlarge'>[Nominee] could not win the cult's support and shall continue to serve as an acolyte.</span>")
return FALSE
team.cult_mastered = TRUE
SSticker.mode.remove_cultist(Nominee.mind, TRUE)
Nominee.mind.add_antag_datum(/datum/antagonist/cult/master)
for(var/datum/mind/B in team.members)
if(B.current)
for(var/datum/action/innate/cult/mastervote/vote in B.current.actions)
vote.Remove(B.current)
if(!B.current.incapacitated())
to_chat(B.current,"<span class='cultlarge'>[Nominee] has won the cult's support and is now their master. Follow [Nominee.p_their()] orders to the best of your ability!</span>")
return TRUE
/datum/action/innate/cult/master/IsAvailable()
if(!owner.mind || !owner.mind.has_antag_datum(/datum/antagonist/cult/master) || GLOB.cult_narsie)
return 0
return ..()
/datum/action/innate/cult/master/finalreck
name = "Final Reckoning"
desc = "A single-use spell that brings the entire cult to the master's location."
button_icon_state = "sintouch"
/datum/action/innate/cult/master/finalreck/Activate()
var/datum/antagonist/cult/antag = owner.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
if(!antag)
return
for(var/i in 1 to 4)
chant(i)
var/list/destinations = list()
for(var/turf/T in orange(1, owner))
if(!is_blocked_turf(T, TRUE))
destinations += T
if(!LAZYLEN(destinations))
to_chat(owner, "<span class='warning'>You need more space to summon the cult!</span>")
return
if(do_after(owner, 30, target = owner))
for(var/datum/mind/B in antag.cult_team.members)
if(B.current && B.current.stat != DEAD)
var/turf/mobloc = get_turf(B.current)
switch(i)
if(1)
new /obj/effect/temp_visual/cult/sparks(mobloc, B.current.dir)
playsound(mobloc, "sparks", 50, 1)
if(2)
new /obj/effect/temp_visual/dir_setting/cult/phase/out(mobloc, B.current.dir)
playsound(mobloc, "sparks", 75, 1)
if(3)
new /obj/effect/temp_visual/dir_setting/cult/phase(mobloc, B.current.dir)
playsound(mobloc, "sparks", 100, 1)
if(4)
playsound(mobloc, 'sound/magic/exit_blood.ogg', 100, 1)
if(B.current != owner)
var/turf/final = pick(destinations)
if(istype(B.current.loc, /obj/item/device/soulstone))
var/obj/item/device/soulstone/S = B.current.loc
S.release_shades(owner)
B.current.setDir(SOUTH)
new /obj/effect/temp_visual/cult/blood(final)
addtimer(CALLBACK(B.current, /mob/.proc/reckon, final), 10)
else
return
antag.cult_team.reckoning_complete = TRUE
Remove(owner)
/mob/proc/reckon(turf/final)
new /obj/effect/temp_visual/cult/blood/out(get_turf(src))
forceMove(final)
/datum/action/innate/cult/master/finalreck/proc/chant(chant_number)
switch(chant_number)
if(1)
owner.say("C'arta forbici!", language = /datum/language/common)
if(2)
owner.say("Pleggh e'ntrath!", language = /datum/language/common)
playsound(get_turf(owner),'sound/magic/clockwork/narsie_attack.ogg', 50, 1)
if(3)
owner.say("Barhah hra zar'garis!", language = /datum/language/common)
playsound(get_turf(owner),'sound/magic/clockwork/narsie_attack.ogg', 75, 1)
if(4)
owner.say("N'ath reth sh'yro eth d'rekkathnor!!!", language = /datum/language/common)
playsound(get_turf(owner),'sound/magic/clockwork/narsie_attack.ogg', 100, 1)
/datum/action/innate/cult/master/cultmark
name = "Mark Target"
desc = "Marks a target for the cult."
button_icon_state = "cult_mark"
var/obj/effect/proc_holder/cultmark/CM
var/cooldown = 0
var/base_cooldown = 1200
/datum/action/innate/cult/master/cultmark/New()
CM = new()
CM.attached_action = src
..()
/datum/action/innate/cult/master/cultmark/IsAvailable()
if(!owner.mind || !owner.mind.has_antag_datum(/datum/antagonist/cult/master))
return FALSE
if(cooldown > world.time)
if(!CM.active)
to_chat(owner, "<span class='cultlarge'><b>You need to wait [DisplayTimeText(cooldown - world.time)] before you can mark another target!</b></span>")
return FALSE
return ..()
/datum/action/innate/cult/master/cultmark/Destroy()
QDEL_NULL(CM)
return ..()
/datum/action/innate/cult/master/cultmark/Activate()
CM.toggle(owner) //the important bit
return TRUE
/obj/effect/proc_holder/cultmark
active = FALSE
ranged_mousepointer = 'icons/effects/cult_target.dmi'
var/datum/action/innate/cult/master/cultmark/attached_action
/obj/effect/proc_holder/cultmark/Destroy()
attached_action = null
return ..()
/obj/effect/proc_holder/cultmark/proc/toggle(mob/user)
if(active)
remove_ranged_ability("<span class='cult'>You cease the marking ritual.</span>")
else
add_ranged_ability(user, "<span class='cult'>You prepare to mark a target for your cult...</span>")
/obj/effect/proc_holder/cultmark/InterceptClickOn(mob/living/caller, params, atom/target)
if(..())
return
if(ranged_ability_user.incapacitated())
remove_ranged_ability()
return
var/turf/T = get_turf(ranged_ability_user)
if(!isturf(T))
return FALSE
var/datum/antagonist/cult/C = caller.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
if(target in view(7, get_turf(ranged_ability_user)))
C.cult_team.blood_target = target
var/area/A = get_area(target)
attached_action.cooldown = world.time + attached_action.base_cooldown
addtimer(CALLBACK(attached_action.owner, /mob.proc/update_action_buttons_icon), attached_action.base_cooldown)
C.cult_team.blood_target_image = image('icons/effects/cult_target.dmi', target, "glow", ABOVE_MOB_LAYER)
C.cult_team.blood_target_image.appearance_flags = RESET_COLOR
C.cult_team.blood_target_image.pixel_x = -target.pixel_x
C.cult_team.blood_target_image.pixel_y = -target.pixel_y
for(var/datum/mind/B in SSticker.mode.cult)
if(B.current && B.current.stat != DEAD && B.current.client)
to_chat(B.current, "<span class='cultlarge'><b>Master [ranged_ability_user] has marked [C.cult_team.blood_target] in the [A.name] as the cult's top priority, get there immediately!</b></span>")
SEND_SOUND(B.current, sound(pick('sound/hallucinations/over_here2.ogg','sound/hallucinations/over_here3.ogg'),0,1,75))
B.current.client.images += C.cult_team.blood_target_image
attached_action.owner.update_action_buttons_icon()
remove_ranged_ability("<span class='cult'>The marking rite is complete! It will last for 90 seconds.</span>")
C.cult_team.blood_target_reset_timer = addtimer(CALLBACK(GLOBAL_PROC, .proc/reset_blood_target,C.cult_team), 900, TIMER_STOPPABLE)
return TRUE
return FALSE
/proc/reset_blood_target(datum/team/cult/team)
for(var/datum/mind/B in team.members)
if(B.current && B.current.stat != DEAD && B.current.client)
if(team.blood_target)
to_chat(B.current,"<span class='cultlarge'><b>The blood mark has expired!</b></span>")
B.current.client.images -= team.blood_target_image
QDEL_NULL(team.blood_target_image)
team.blood_target = null
//////// ELDRITCH PULSE /////////
/datum/action/innate/cult/master/pulse
name = "Eldritch Pulse"
desc = "Seize upon a fellow cultist or cult structure and teleport it to a nearby location."
icon_icon = 'icons/mob/actions/actions_spells.dmi'
button_icon_state = "arcane_barrage"
var/obj/effect/proc_holder/pulse/PM
var/cooldown = 0
var/base_cooldown = 150
var/throwing = FALSE
var/mob/living/throwee
/datum/action/innate/cult/master/pulse/New()
PM = new()
PM.attached_action = src
..()
/datum/action/innate/cult/master/pulse/IsAvailable()
if(!owner.mind || !owner.mind.has_antag_datum(/datum/antagonist/cult/master))
return FALSE
if(cooldown > world.time)
if(!PM.active)
to_chat(owner, "<span class='cultlarge'><b>You need to wait [DisplayTimeText(cooldown - world.time)] before you can pulse again!</b></span>")
return FALSE
return ..()
/datum/action/innate/cult/master/pulse/Destroy()
PM.attached_action = null //What the fuck is even going on here.
QDEL_NULL(PM)
return ..()
/datum/action/innate/cult/master/pulse/Activate()
PM.toggle(owner) //the important bit
return TRUE
/obj/effect/proc_holder/pulse
active = FALSE
ranged_mousepointer = 'icons/effects/throw_target.dmi'
var/datum/action/innate/cult/master/pulse/attached_action
/obj/effect/proc_holder/pulse/Destroy()
QDEL_NULL(attached_action)
return ..()
/obj/effect/proc_holder/pulse/proc/toggle(mob/user)
if(active)
remove_ranged_ability("<span class='cult'>You cease your preparations...</span>")
attached_action.throwing = FALSE
else
add_ranged_ability(user, "<span class='cult'>You prepare to tear through the fabric of reality...</span>")
/obj/effect/proc_holder/pulse/InterceptClickOn(mob/living/caller, params, atom/target)
if(..())
return
if(ranged_ability_user.incapacitated())
remove_ranged_ability()
return
var/turf/T = get_turf(ranged_ability_user)
if(!isturf(T))
return FALSE
if(target in view(7, get_turf(ranged_ability_user)))
if((!(iscultist(target) || istype(target, /obj/structure/destructible/cult)) || target == caller) && !(attached_action.throwing))
return
if(!attached_action.throwing)
attached_action.throwing = TRUE
attached_action.throwee = target
SEND_SOUND(ranged_ability_user, sound('sound/weapons/thudswoosh.ogg'))
to_chat(ranged_ability_user,"<span class='cult'><b>You reach through the veil with your mind's eye and seize [target]!</b></span>")
return
else
new /obj/effect/temp_visual/cult/sparks(get_turf(attached_action.throwee), ranged_ability_user.dir)
var/distance = get_dist(attached_action.throwee, target)
if(distance >= 16)
return
playsound(target,'sound/magic/exit_blood.ogg')
attached_action.throwee.Beam(target,icon_state="sendbeam",time=4)
attached_action.throwee.forceMove(get_turf(target))
new /obj/effect/temp_visual/cult/sparks(get_turf(target), ranged_ability_user.dir)
attached_action.throwing = FALSE
attached_action.cooldown = world.time + attached_action.base_cooldown
remove_mousepointer(ranged_ability_user.client)
remove_ranged_ability("<span class='cult'>A pulse of blood magic surges through you as you shift [attached_action.throwee] through time and space.</span>")
caller.update_action_buttons_icon()
addtimer(CALLBACK(caller, /mob.proc/update_action_buttons_icon), attached_action.base_cooldown)
-606
View File
@@ -1,606 +0,0 @@
/obj/item/melee/cultblade
name = "eldritch longsword"
desc = "A sword humming with unholy energy. It glows with a dim red light."
icon_state = "cultblade"
item_state = "cultblade"
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
inhand_x_dimension = 64
inhand_y_dimension = 64
flags_1 = CONDUCT_1
sharpness = IS_SHARP
w_class = WEIGHT_CLASS_BULKY
force = 30
throwforce = 10
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "rended")
/obj/item/melee/cultblade/attack(mob/living/target, mob/living/carbon/human/user)
if(!iscultist(user))
user.Knockdown(100)
user.dropItemToGround(src, TRUE)
user.visible_message("<span class='warning'>A powerful force shoves [user] away from [target]!</span>", \
"<span class='cultlarge'>\"You shouldn't play with sharp things. You'll poke someone's eye out.\"</span>")
if(ishuman(user))
var/mob/living/carbon/human/H = user
H.apply_damage(rand(force/2, force), BRUTE, pick("l_arm", "r_arm"))
else
user.adjustBruteLoss(rand(force/2,force))
return
..()
/obj/item/melee/cultblade/ghost
name = "eldritch sword"
force = 19 //can't break normal airlocks
flags_1 = NODROP_1|DROPDEL_1
/obj/item/melee/cultblade/pickup(mob/living/user)
..()
if(!iscultist(user))
if(!is_servant_of_ratvar(user))
to_chat(user, "<span class='cultlarge'>\"I wouldn't advise that.\"</span>")
to_chat(user, "<span class='warning'>An overwhelming sense of nausea overpowers you!</span>")
user.Dizzy(120)
else
to_chat(user, "<span class='cultlarge'>\"One of Ratvar's toys is trying to play with things [user.p_they()] shouldn't. Cute.\"</span>")
to_chat(user, "<span class='userdanger'>A horrible force yanks at your arm!</span>")
user.emote("scream")
user.apply_damage(30, BRUTE, pick("l_arm", "r_arm"))
user.dropItemToGround(src)
/obj/item/melee/cultblade/dagger
name = "sacrificial dagger"
desc = "A strange dagger said to be used by sinister groups for \"preparing\" a corpse before sacrificing it to their dark gods."
icon = 'icons/obj/wizard.dmi'
icon_state = "render"
item_state = "knife"
lefthand_file = 'icons/mob/inhands/equipment/kitchen_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/kitchen_righthand.dmi'
inhand_x_dimension = 32
inhand_y_dimension = 32
w_class = WEIGHT_CLASS_SMALL
force = 15
throwforce = 25
embed_chance = 75
/obj/item/melee/cultblade/dagger/attack(mob/living/target, mob/living/carbon/human/user)
..()
if(iscarbon(target))
var/mob/living/carbon/C = target
C.bleed(50)
if(is_servant_of_ratvar(C) && C.reagents)
C.reagents.add_reagent("heparin", 1)
/obj/item/twohanded/required/cult_bastard
name = "bloody bastard sword"
desc = "An enormous sword used by Nar-Sien cultists to rapidly harvest the souls of non-believers."
w_class = WEIGHT_CLASS_HUGE
block_chance = 50
throwforce = 20
force = 35
armour_penetration = 45
throw_speed = 1
throw_range = 3
sharpness = IS_SHARP
light_color = "#ff0000"
attack_verb = list("cleaved", "slashed", "torn", "hacked", "ripped", "diced", "carved")
icon_state = "cultbastard"
item_state = "cultbastard"
hitsound = 'sound/weapons/bladeslice.ogg'
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
inhand_x_dimension = 64
inhand_y_dimension = 64
actions_types = list()
flags_2 = SLOWS_WHILE_IN_HAND_2
var/datum/action/innate/dash/cult/jaunt
var/datum/action/innate/cult/spin2win/linked_action
var/spinning = FALSE
var/spin_cooldown = 250
var/dash_toggled = TRUE
/obj/item/twohanded/required/cult_bastard/Initialize()
. = ..()
set_light(4)
jaunt = new(src)
linked_action = new(src)
/obj/item/twohanded/required/cult_bastard/can_be_pulled(user)
return FALSE
/obj/item/twohanded/required/cult_bastard/attack_self(mob/user)
dash_toggled = !dash_toggled
if(dash_toggled)
to_chat(loc, "<span class='notice'>You raise [src] and prepare to jaunt with it.</span>")
else
to_chat(loc, "<span class='notice'>You lower [src] and prepare to swing it normally.</span>")
/obj/item/twohanded/required/cult_bastard/pickup(mob/living/user)
. = ..()
if(!iscultist(user))
if(!is_servant_of_ratvar(user))
to_chat(user, "<span class='cultlarge'>\"I wouldn't advise that.\"</span>")
to_chat(user, "<span class='warning'>An overwhelming sense of nausea overpowers you!</span>")
user.Dizzy(80)
user.dropItemToGround(src, TRUE)
user.Knockdown(30)
return
else
to_chat(user, "<span class='cultlarge'>\"One of Ratvar's toys is trying to play with things [user.p_they()] shouldn't. Cute.\"</span>")
to_chat(user, "<span class='userdanger'>A horrible force yanks at your arm!</span>")
user.emote("scream")
user.apply_damage(30, BRUTE, pick("l_arm", "r_arm"))
user.dropItemToGround(src, TRUE)
user.Knockdown(50)
return
jaunt.Grant(user, src)
linked_action.Grant(user, src)
user.update_icons()
/obj/item/twohanded/required/cult_bastard/dropped(mob/user)
. = ..()
linked_action.Remove(user)
jaunt.Remove(user)
user.update_icons()
/obj/item/twohanded/required/cult_bastard/IsReflect()
if(spinning)
playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, 1)
return TRUE
else
..()
/obj/item/twohanded/required/cult_bastard/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(prob(final_block_chance))
if(attack_type == PROJECTILE_ATTACK)
owner.visible_message("<span class='danger'>[owner] deflects [attack_text] with [src]!</span>")
playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 100, 1)
return TRUE
else
playsound(src, 'sound/weapons/parry.ogg', 75, 1)
owner.visible_message("<span class='danger'>[owner] parries [attack_text] with [src]!</span>")
return TRUE
return FALSE
/obj/item/twohanded/required/cult_bastard/afterattack(atom/target, mob/user, proximity, click_parameters)
. = ..()
if(dash_toggled)
jaunt.Teleport(user, target)
return
if(!proximity)
return
if(ishuman(target))
var/mob/living/carbon/human/H = target
if(H.stat != CONSCIOUS)
var/obj/item/device/soulstone/SS = new /obj/item/device/soulstone(src)
SS.attack(H, user)
if(!LAZYLEN(SS.contents))
qdel(SS)
if(istype(target, /obj/structure/constructshell) && contents.len)
var/obj/item/device/soulstone/SS = contents[1]
if(istype(SS))
SS.transfer_soul("CONSTRUCT",target,user)
qdel(SS)
/datum/action/innate/dash/cult
name = "Rend the Veil"
desc = "Use the sword to shear open the flimsy fabric of this reality and teleport to your target."
icon_icon = 'icons/mob/actions/actions_cult.dmi'
button_icon_state = "phaseshift"
dash_sound = 'sound/magic/enter_blood.ogg'
recharge_sound = 'sound/magic/exit_blood.ogg'
beam_effect = "sendbeam"
phasein = /obj/effect/temp_visual/dir_setting/cult/phase
phaseout = /obj/effect/temp_visual/dir_setting/cult/phase/out
/datum/action/innate/dash/cult/IsAvailable()
if(iscultist(holder) && current_charges)
return TRUE
else
return FALSE
/datum/action/innate/cult/spin2win
name = "Geometer's Fury"
desc = "You draw on the power of the sword's ancient runes, spinning it wildly around you as you become immune to most attacks."
background_icon_state = "bg_demon"
button_icon_state = "sintouch"
var/cooldown = 0
var/mob/living/carbon/human/holder
var/obj/item/twohanded/required/cult_bastard/sword
/datum/action/innate/cult/spin2win/Grant(mob/user, obj/bastard)
. = ..()
sword = bastard
holder = user
/datum/action/innate/cult/spin2win/IsAvailable()
if(iscultist(holder) && cooldown <= world.time)
return TRUE
else
return FALSE
/datum/action/innate/cult/spin2win/Activate()
cooldown = world.time + sword.spin_cooldown
holder.changeNext_move(50)
holder.apply_status_effect(/datum/status_effect/sword_spin)
sword.spinning = TRUE
sword.block_chance = 100
sword.slowdown += 1.5
addtimer(CALLBACK(src, .proc/stop_spinning), 50)
holder.update_action_buttons_icon()
/datum/action/innate/cult/spin2win/proc/stop_spinning()
sword.spinning = FALSE
sword.block_chance = 50
sword.slowdown -= 1.5
sleep(sword.spin_cooldown)
holder.update_action_buttons_icon()
/obj/item/restraints/legcuffs/bola/cult
name = "nar'sien bola"
desc = "A strong bola, bound with dark magic. Throw it to trip and slow your victim."
icon_state = "bola_cult"
breakouttime = 45
knockdown = 10
/obj/item/clothing/head/culthood
name = "ancient cultist hood"
icon_state = "culthood"
desc = "A torn, dust-caked hood. Strange letters line the inside."
flags_inv = HIDEFACE|HIDEHAIR|HIDEEARS
flags_cover = HEADCOVERSEYES
armor = list(melee = 30, bullet = 10, laser = 5,energy = 5, bomb = 0, bio = 0, rad = 0, fire = 10, acid = 10)
cold_protection = HEAD
min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT
heat_protection = HEAD
max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT
/obj/item/clothing/suit/cultrobes
name = "ancient cultist robes"
desc = "A ragged, dusty set of robes. Strange letters line the inside."
icon_state = "cultrobes"
item_state = "cultrobes"
body_parts_covered = CHEST|GROIN|LEGS|ARMS
allowed = list(/obj/item/tome, /obj/item/melee/cultblade)
armor = list(melee = 50, bullet = 30, laser = 50,energy = 20, bomb = 25, bio = 10, rad = 0, fire = 10, acid = 10)
flags_inv = HIDEJUMPSUIT
cold_protection = CHEST|GROIN|LEGS|ARMS
min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT
heat_protection = CHEST|GROIN|LEGS|ARMS
max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT
/obj/item/clothing/head/culthood/alt
name = "cultist hood"
desc = "An armored hood worn by the followers of Nar-Sie."
icon_state = "cult_hoodalt"
item_state = "cult_hoodalt"
/obj/item/clothing/head/culthood/alt/ghost
flags_1 = NODROP_1|DROPDEL_1
/obj/item/clothing/suit/cultrobes/alt
name = "cultist robes"
desc = "An armored set of robes worn by the followers of Nar-Sie."
icon_state = "cultrobesalt"
item_state = "cultrobesalt"
/obj/item/clothing/suit/cultrobes/alt/ghost
flags_1 = NODROP_1|DROPDEL_1
/obj/item/clothing/head/magus
name = "magus helm"
icon_state = "magus"
item_state = "magus"
desc = "A helm worn by the followers of Nar-Sie."
flags_inv = HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDEEARS|HIDEEYES
armor = list(melee = 30, bullet = 30, laser = 30,energy = 20, bomb = 0, bio = 0, rad = 0, fire = 10, acid = 10)
flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH
/obj/item/clothing/suit/magusred
name = "magus robes"
desc = "A set of armored robes worn by the followers of Nar-Sie."
icon_state = "magusred"
item_state = "magusred"
body_parts_covered = CHEST|GROIN|LEGS|ARMS
allowed = list(/obj/item/tome, /obj/item/melee/cultblade)
armor = list(melee = 50, bullet = 30, laser = 50,energy = 20, bomb = 25, bio = 10, rad = 0, fire = 10, acid = 10)
flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT
/obj/item/clothing/head/helmet/space/hardsuit/cult
name = "nar-sien hardened helmet"
desc = "A heavily-armored helmet worn by warriors of the Nar-Sien cult. It can withstand hard vacuum."
icon_state = "cult_helmet"
item_state = "cult_helmet"
armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30, fire = 40, acid = 75)
brightness_on = 0
actions_types = list()
/obj/item/clothing/suit/space/hardsuit/cult
name = "nar-sien hardened armor"
icon_state = "cult_armor"
item_state = "cult_armor"
desc = "A heavily-armored exosuit worn by warriors of the Nar-Sien cult. It can withstand hard vacuum."
w_class = WEIGHT_CLASS_BULKY
allowed = list(/obj/item/tome, /obj/item/melee/cultblade, /obj/item/tank/internals/)
armor = list(melee = 70, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30, fire = 40, acid = 75)
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/cult
/obj/item/sharpener/cult
name = "eldritch whetstone"
desc = "A block, empowered by dark magic. Sharp weapons will be enhanced when used on the stone."
icon_state = "cult_sharpener"
used = 0
increment = 5
max = 40
prefix = "darkened"
/obj/item/sharpener/cult/update_icon()
icon_state = "cult_sharpener[used ? "_used" : ""]"
/obj/item/clothing/suit/hooded/cultrobes/cult_shield
name = "empowered cultist armor"
desc = "Empowered garb which creates a powerful shield around the user."
icon_state = "cult_armor"
item_state = "cult_armor"
w_class = WEIGHT_CLASS_BULKY
armor = list(melee = 50, bullet = 40, laser = 50,energy = 30, bomb = 50, bio = 30, rad = 30, fire = 50, acid = 60)
body_parts_covered = CHEST|GROIN|LEGS|ARMS
allowed = list(/obj/item/tome, /obj/item/melee/cultblade)
var/current_charges = 3
hoodtype = /obj/item/clothing/head/hooded/cult_hoodie
/obj/item/clothing/head/hooded/cult_hoodie
name = "empowered cultist armor"
desc = "Empowered garb which creates a powerful shield around the user."
icon_state = "cult_hoodalt"
armor = list(melee = 50, bullet = 40, laser = 50,energy = 30, bomb = 50, bio = 30, rad = 30, fire = 50, acid = 50)
body_parts_covered = HEAD
flags_inv = HIDEHAIR|HIDEFACE|HIDEEARS
/obj/item/clothing/suit/hooded/cultrobes/cult_shield/equipped(mob/living/user, slot)
..()
if(!iscultist(user))
if(!is_servant_of_ratvar(user))
to_chat(user, "<span class='cultlarge'>\"I wouldn't advise that.\"</span>")
to_chat(user, "<span class='warning'>An overwhelming sense of nausea overpowers you!</span>")
user.dropItemToGround(src, TRUE)
user.Dizzy(30)
user.Knockdown(100)
else
to_chat(user, "<span class='cultlarge'>\"Trying to use things you don't own is bad, you know.\"</span>")
to_chat(user, "<span class='userdanger'>The armor squeezes at your body!</span>")
user.emote("scream")
user.adjustBruteLoss(25)
user.dropItemToGround(src, TRUE)
/obj/item/clothing/suit/hooded/cultrobes/cult_shield/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(current_charges)
owner.visible_message("<span class='danger'>\The [attack_text] is deflected in a burst of blood-red sparks!</span>")
current_charges--
new /obj/effect/temp_visual/cult/sparks(get_turf(owner))
if(!current_charges)
owner.visible_message("<span class='danger'>The runed shield around [owner] suddenly disappears!</span>")
owner.update_inv_wear_suit()
return 1
return 0
/obj/item/clothing/suit/hooded/cultrobes/cult_shield/worn_overlays(isinhands)
. = list()
if(!isinhands && current_charges)
. += mutable_appearance('icons/effects/cult_effects.dmi', "shield-cult", MOB_LAYER + 0.01)
/obj/item/clothing/suit/hooded/cultrobes/berserker
name = "flagellant's robes"
desc = "Blood-soaked robes infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage."
icon_state = "cultrobes"
item_state = "cultrobes"
flags_inv = HIDEJUMPSUIT
allowed = list(/obj/item/tome, /obj/item/melee/cultblade)
body_parts_covered = CHEST|GROIN|LEGS|ARMS
armor = list(melee = -50, bullet = -50, laser = -50,energy = -50, bomb = -50, bio = -50, rad = -50, fire = 0, acid = 0)
slowdown = -1
hoodtype = /obj/item/clothing/head/hooded/berserkerhood
/obj/item/clothing/head/hooded/berserkerhood
name = "flagellant's robes"
desc = "Blood-soaked garb infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage."
icon_state = "culthood"
body_parts_covered = HEAD
flags_inv = HIDEHAIR|HIDEFACE|HIDEEARS
armor = list(melee = -50, bullet = -50, laser = -50, energy = -50, bomb = -50, bio = -50, rad = -50, fire = 0, acid = 0)
/obj/item/clothing/suit/hooded/cultrobes/berserker/equipped(mob/living/user, slot)
..()
if(!iscultist(user))
if(!is_servant_of_ratvar(user))
to_chat(user, "<span class='cultlarge'>\"I wouldn't advise that.\"</span>")
to_chat(user, "<span class='warning'>An overwhelming sense of nausea overpowers you!</span>")
user.dropItemToGround(src, TRUE)
user.Dizzy(30)
user.Knockdown(100)
else
to_chat(user, "<span class='cultlarge'>\"Trying to use things you don't own is bad, you know.\"</span>")
to_chat(user, "<span class='userdanger'>The robes squeeze at your body!</span>")
user.emote("scream")
user.adjustBruteLoss(25)
user.dropItemToGround(src, TRUE)
/obj/item/clothing/glasses/night/cultblind
desc = "May nar-sie guide you through the darkness and shield you from the light."
name = "zealot's blindfold"
icon_state = "blindfold"
item_state = "blindfold"
darkness_view = 8
flash_protect = 1
/obj/item/clothing/glasses/night/cultblind/equipped(mob/living/user, slot)
..()
if(!iscultist(user))
to_chat(user, "<span class='cultlarge'>\"You want to be blind, do you?\"</span>")
user.dropItemToGround(src, TRUE)
user.Dizzy(30)
user.Knockdown(100)
user.blind_eyes(30)
/obj/item/reagent_containers/food/drinks/bottle/unholywater
name = "flask of unholy water"
desc = "Toxic to nonbelievers; reinvigorating to the faithful - this flask may be sipped or thrown."
icon_state = "holyflask"
color = "#333333"
list_reagents = list("unholywater" = 40)
/obj/item/device/shuttle_curse
name = "cursed orb"
desc = "You peer within this smokey orb and glimpse terrible fates befalling the escape shuttle."
icon = 'icons/obj/cult.dmi'
icon_state ="shuttlecurse"
var/global/curselimit = 0
/obj/item/device/shuttle_curse/attack_self(mob/living/user)
if(!iscultist(user))
user.dropItemToGround(src, TRUE)
user.Knockdown(100)
to_chat(user, "<span class='warning'>A powerful force shoves you away from [src]!</span>")
return
if(curselimit > 1)
to_chat(user, "<span class='notice'>We have exhausted our ability to curse the shuttle.</span>")
return
if(locate(/obj/singularity/narsie) in GLOB.poi_list)
to_chat(user, "<span class='warning'>Nar-Sie is already on this plane, there is no delaying the end of all things.</span>")
return
if(SSshuttle.emergency.mode == SHUTTLE_CALL)
var/cursetime = 1800
var/timer = SSshuttle.emergency.timeLeft(1) + cursetime
SSshuttle.emergency.setTimer(timer)
to_chat(user, "<span class='danger'>You shatter the orb! A dark essence spirals into the air, then disappears.</span>")
playsound(user.loc, 'sound/effects/glassbr1.ogg', 50, 1)
qdel(src)
sleep(20)
var/global/list/curses
if(!curses)
curses = list("A fuel technician just slit his own throat and begged for death. The shuttle will be delayed by three minutes.",
"The shuttle's navigation programming was replaced by a file containing two words, IT COMES. The shuttle will be delayed by three minutes.",
"The shuttle's custodian tore out his guts and began painting strange shapes on the floor. The shuttle will be delayed by three minutes.",
"A shuttle engineer began screaming 'DEATH IS NOT THE END' and ripped out wires until an arc flash seared off her flesh. The shuttle will be delayed by three minutes.",
"A shuttle inspector started laughing madly over the radio and then threw herself into an engine turbine. The shuttle will be delayed by three minutes.",
"The shuttle dispatcher was found dead with bloody symbols carved into their flesh. The shuttle will be delayed by three minutes.")
var/message = pick_n_take(curses)
priority_announce("[message]", "System Failure", 'sound/misc/notice1.ogg')
curselimit++
/obj/item/device/cult_shift
name = "veil shifter"
desc = "This relic teleports you forward a medium distance."
icon = 'icons/obj/cult.dmi'
icon_state ="shifter"
var/uses = 4
/obj/item/device/cult_shift/examine(mob/user)
..()
if(uses)
to_chat(user, "<span class='cult'>It has [uses] use\s remaining.</span>")
else
to_chat(user, "<span class='cult'>It seems drained.</span>")
/obj/item/device/cult_shift/proc/handle_teleport_grab(turf/T, mob/user)
var/mob/living/carbon/C = user
if(C.pulling)
var/atom/movable/pulled = C.pulling
pulled.forceMove(T)
. = pulled
/obj/item/device/cult_shift/attack_self(mob/user)
if(!uses || !iscarbon(user))
to_chat(user, "<span class='warning'>\The [src] is dull and unmoving in your hands.</span>")
return
if(!iscultist(user))
user.dropItemToGround(src, TRUE)
step(src, pick(GLOB.alldirs))
to_chat(user, "<span class='warning'>\The [src] flickers out of your hands, your connection to this dimension is too strong!</span>")
return
var/mob/living/carbon/C = user
var/turf/mobloc = get_turf(C)
var/turf/destination = get_teleport_loc(mobloc,C,9,1,3,1,0,1)
if(destination)
uses--
if(uses <= 0)
icon_state ="shifter_drained"
playsound(mobloc, "sparks", 50, 1)
new /obj/effect/temp_visual/dir_setting/cult/phase/out(mobloc, C.dir)
var/atom/movable/pulled = handle_teleport_grab(destination, C)
C.forceMove(destination)
if(pulled)
C.start_pulling(pulled) //forcemove resets pulls, so we need to re-pull
new /obj/effect/temp_visual/dir_setting/cult/phase(destination, C.dir)
playsound(destination, 'sound/effects/phasein.ogg', 25, 1)
playsound(destination, "sparks", 50, 1)
else
to_chat(C, "<span class='danger'>The veil cannot be torn here!</span>")
/obj/item/device/flashlight/flare/culttorch
name = "void torch"
desc = "Used by veteran cultists to instantly transport items to their needful bretheren."
w_class = WEIGHT_CLASS_SMALL
brightness_on = 1
icon_state = "torch"
item_state = "torch"
color = "#ff0000"
on_damage = 15
slot_flags = null
on = TRUE
var/charges = 5
/obj/item/device/flashlight/flare/culttorch/afterattack(atom/movable/A, mob/user, proximity)
if(!proximity)
return
if(!iscultist(user))
to_chat(user, "That doesn't seem to do anything useful.")
return
if(istype(A, /obj/item))
var/list/cultists = list()
for(var/datum/mind/M in SSticker.mode.cult)
if(M.current && M.current.stat != DEAD)
cultists |= M.current
var/mob/living/cultist_to_receive = input(user, "Who do you wish to call to [src]?", "Followers of the Geometer") as null|anything in (cultists - user)
if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated())
return
if(!cultist_to_receive)
to_chat(user, "<span class='cult italic'>You require a destination!</span>")
log_game("Void torch failed - no target")
return
if(cultist_to_receive.stat == DEAD)
to_chat(user, "<span class='cult italic'>[cultist_to_receive] has died!</span>")
log_game("Void torch failed - target died")
return
if(!iscultist(cultist_to_receive))
to_chat(user, "<span class='cult italic'>[cultist_to_receive] is not a follower of the Geometer!</span>")
log_game("Void torch failed - target was deconverted")
return
if(A in user.GetAllContents())
to_chat(user, "<span class='cult italic'>[A] must be on a surface in order to teleport it!</span>")
return
to_chat(user, "<span class='cult italic'>You ignite [A] with \the [src], turning it to ash, but through the torch's flames you see that [A] has reached [cultist_to_receive]!")
cultist_to_receive.put_in_hands(A)
charges--
to_chat(user, "\The [src] now has [charges] charge\s.")
if(charges == 0)
qdel(src)
else
..()
to_chat(user, "<span class='warning'>\The [src] can only transport items!</span>")
-244
View File
@@ -1,244 +0,0 @@
/obj/structure/destructible/cult
density = TRUE
anchored = TRUE
icon = 'icons/obj/cult.dmi'
var/cooldowntime = 0
break_sound = 'sound/hallucinations/veryfar_noise.ogg'
debris = list(/obj/item/stack/sheet/runed_metal = 1)
/obj/structure/destructible/cult/examine(mob/user)
..()
to_chat(user, "<span class='notice'>\The [src] is [anchored ? "":"not "]secured to the floor.</span>")
if((iscultist(user) || isobserver(user)) && cooldowntime > world.time)
to_chat(user, "<span class='cult italic'>The magic in [src] is too weak, [p_they()] will be ready to use again in [DisplayTimeText(cooldowntime - world.time)].</span>")
/obj/structure/destructible/cult/examine_status(mob/user)
if(iscultist(user) || isobserver(user))
var/t_It = p_they(TRUE)
var/t_is = p_are()
return "<span class='cult'>[t_It] [t_is] at <b>[round(obj_integrity * 100 / max_integrity)]%</b> stability.</span>"
return ..()
/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)
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>")
else
to_chat(M, "<span class='cult'>You cannot repair [src], as [p_they()] [p_are()] undamaged!</span>")
else
..()
/obj/structure/destructible/cult/attackby(obj/I, mob/user, params)
if(istype(I, /obj/item/tome) && iscultist(user))
anchored = !anchored
to_chat(user, "<span class='notice'>You [anchored ? "":"un"]secure \the [src] [anchored ? "to":"from"] the floor.</span>")
if(!anchored)
icon_state = "[initial(icon_state)]_off"
else
icon_state = initial(icon_state)
else
return ..()
/obj/structure/destructible/cult/ratvar_act()
if(take_damage(rand(25, 50), BURN) && src) //if we still exist
var/previouscolor = color
color = "#FAE48C"
animate(src, color = previouscolor, time = 8)
addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 8)
/obj/structure/destructible/cult/talisman
name = "altar"
desc = "A bloodstained altar dedicated to Nar-Sie."
icon_state = "talismanaltar"
break_message = "<span class='warning'>The altar shatters, leaving only the wailing of the damned!</span>"
/obj/structure/destructible/cult/talisman/attack_hand(mob/living/user)
if(!iscultist(user))
to_chat(user, "<span class='warning'>You're pretty sure you know exactly what this is used for and you can't seem to touch it.</span>")
return
if(!anchored)
to_chat(user, "<span class='cult italic'>You need to anchor [src] to the floor with a tome first.</span>")
return
if(cooldowntime > world.time)
to_chat(user, "<span class='cult italic'>The magic in [src] is weak, it will be ready to use again in [DisplayTimeText(cooldowntime - world.time)].</span>")
return
var/choice = alert(user,"You study the schematics etched into the forge...",,"Eldritch Whetstone","Zealot's Blindfold","Flask of Unholy Water")
var/pickedtype
switch(choice)
if("Eldritch Whetstone")
pickedtype = /obj/item/sharpener/cult
if("Zealot's Blindfold")
pickedtype = /obj/item/clothing/glasses/night/cultblind
if("Flask of Unholy Water")
pickedtype = /obj/item/reagent_containers/food/drinks/bottle/unholywater
if(src && !QDELETED(src) && anchored && pickedtype && Adjacent(user) && !user.incapacitated() && iscultist(user) && cooldowntime <= world.time)
cooldowntime = world.time + 2400
var/obj/item/N = new pickedtype(get_turf(src))
to_chat(user, "<span class='cult italic'>You kneel before the altar and your faith is rewarded with an [N]!</span>")
/obj/structure/destructible/cult/forge
name = "daemon forge"
desc = "A forge used in crafting the unholy weapons used by the armies of Nar-Sie."
icon_state = "forge"
light_range = 3
light_color = LIGHT_COLOR_LAVA
break_message = "<span class='warning'>The force breaks apart into shards with a howling scream!</span>"
/obj/structure/destructible/cult/forge/attack_hand(mob/living/user)
if(!iscultist(user))
to_chat(user, "<span class='warning'>The heat radiating from [src] pushes you back.</span>")
return
if(!anchored)
to_chat(user, "<span class='cult italic'>You need to anchor [src] to the floor with a tome first.</span>")
return
if(cooldowntime > world.time)
to_chat(user, "<span class='cult italic'>The magic in [src] is weak, it will be ready to use again in [DisplayTimeText(cooldowntime - world.time)].</span>")
return
var/choice = alert(user,"You study the schematics etched into the forge...",,"Shielded Robe","Flagellant's Robe","Bastard Sword")
var/pickedtype
switch(choice)
if("Shielded Robe")
pickedtype = /obj/item/clothing/suit/hooded/cultrobes/cult_shield
if("Flagellant's Robe")
pickedtype = /obj/item/clothing/suit/hooded/cultrobes/berserker
if("Bastard Sword")
if((world.time - SSticker.round_start_time) >= 12000)
pickedtype = /obj/item/twohanded/required/cult_bastard
else
cooldowntime = 12000 - (world.time - SSticker.round_start_time)
to_chat(user, "<span class='cult italic'>The forge fires are not yet hot enough for this weapon, give it another [DisplayTimeText(cooldowntime)].</span>")
cooldowntime = 0
return
if(src && !QDELETED(src) && anchored && pickedtype && Adjacent(user) && !user.incapacitated() && iscultist(user) && cooldowntime <= world.time)
cooldowntime = world.time + 2400
var/obj/item/N = new pickedtype(get_turf(src))
to_chat(user, "<span class='cult italic'>You work the forge as dark knowledge guides your hands, creating [N]!</span>")
/obj/structure/destructible/cult/pylon
name = "pylon"
desc = "A floating crystal that slowly heals those faithful to Nar'Sie."
icon_state = "pylon"
light_range = 5
light_color = LIGHT_COLOR_RED
break_sound = 'sound/effects/glassbr2.ogg'
break_message = "<span class='warning'>The blood-red crystal falls to the floor and shatters!</span>"
var/heal_delay = 25
var/last_heal = 0
var/corrupt_delay = 50
var/last_corrupt = 0
/obj/structure/destructible/cult/pylon/New()
START_PROCESSING(SSfastprocess, src)
..()
/obj/structure/destructible/cult/pylon/Destroy()
STOP_PROCESSING(SSfastprocess, src)
return ..()
/obj/structure/destructible/cult/pylon/process()
if(!anchored)
return
if(last_heal <= world.time)
last_heal = world.time + heal_delay
for(var/mob/living/L in range(5, src))
if(iscultist(L) || isshade(L) || isconstruct(L))
if(L.health != L.maxHealth)
new /obj/effect/temp_visual/heal(get_turf(src), "#960000")
if(ishuman(L))
L.adjustBruteLoss(-1, 0)
L.adjustFireLoss(-1, 0)
L.updatehealth()
if(isshade(L) || isconstruct(L))
var/mob/living/simple_animal/M = L
if(M.health < M.maxHealth)
M.adjustHealth(-1)
if(ishuman(L) && L.blood_volume < BLOOD_VOLUME_NORMAL)
L.blood_volume += 1.0
CHECK_TICK
if(last_corrupt <= world.time)
var/list/validturfs = list()
var/list/cultturfs = list()
for(var/T in circleviewturfs(src, 5))
if(istype(T, /turf/open/floor/engine/cult))
cultturfs |= T
continue
var/static/list/blacklisted_pylon_turfs = typecacheof(list(
/turf/closed,
/turf/open/floor/engine/cult,
/turf/open/space,
/turf/open/lava,
/turf/open/chasm))
if(is_type_in_typecache(T, blacklisted_pylon_turfs))
continue
else
validturfs |= T
last_corrupt = world.time + corrupt_delay
var/turf/T = safepick(validturfs)
if(T)
T.ChangeTurf(/turf/open/floor/engine/cult)
else
var/turf/open/floor/engine/cult/F = safepick(cultturfs)
if(F)
new /obj/effect/temp_visual/cult/turf/floor(F)
else
// Are we in space or something? No cult turfs or
// convertable turfs?
last_corrupt = world.time + corrupt_delay*2
/obj/structure/destructible/cult/tome
name = "archives"
desc = "A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl."
icon_state = "tomealtar"
light_range = 1.4
light_color = LIGHT_COLOR_FIRE
break_message = "<span class='warning'>The books and tomes of the archives burn into ash as the desk shatters!</span>"
/obj/structure/destructible/cult/tome/attack_hand(mob/living/user)
if(!iscultist(user))
to_chat(user, "<span class='warning'>All of these books seem to be gibberish.</span>")
return
if(!anchored)
to_chat(user, "<span class='cult italic'>You need to anchor [src] to the floor with a tome first.</span>")
return
if(cooldowntime > world.time)
to_chat(user, "<span class='cult italic'>The magic in [src] is weak, it will be ready to use again in [DisplayTimeText(cooldowntime - world.time)].</span>")
return
var/choice = alert(user,"You flip through the black pages of the archives...",,"Supply Talisman","Shuttle Curse","Veil Walker Set")
var/list/pickedtype = list()
switch(choice)
if("Supply Talisman")
pickedtype += /obj/item/paper/talisman/supply/weak
if("Shuttle Curse")
pickedtype += /obj/item/device/shuttle_curse
if("Veil Walker Set")
pickedtype += /obj/item/device/cult_shift
pickedtype += /obj/item/device/flashlight/flare/culttorch
if(src && !QDELETED(src) && anchored && pickedtype.len && Adjacent(user) && !user.incapacitated() && iscultist(user) && cooldowntime <= world.time)
cooldowntime = world.time + 2400
for(var/N in pickedtype)
var/obj/item/D = new N(get_turf(src))
to_chat(user, "<span class='cult italic'>You summon [D] from the archives!</span>")
/obj/effect/gateway
name = "gateway"
desc = "You're pretty sure that abyss is staring back."
icon = 'icons/obj/cult.dmi'
icon_state = "hole"
density = TRUE
anchored = TRUE
/obj/effect/gateway/singularity_act()
return
/obj/effect/gateway/singularity_pull()
return

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