initial commit - cross reference with 5th port - obviously has compile errors
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
/datum/atom_hud/antag
|
||||
hud_icons = list(ANTAG_HUD)
|
||||
var/self_visible = 1
|
||||
|
||||
/datum/atom_hud/antag/hidden
|
||||
self_visible = 0
|
||||
|
||||
/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
|
||||
/datum/game_mode/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(var/datum/atom_hud/antag/newhud)
|
||||
leave_all_huds()
|
||||
ticker.mode.set_antag_hud(current, antag_hud_icon_state)
|
||||
if(newhud)
|
||||
newhud.join_hud(current)
|
||||
|
||||
/datum/mind/proc/leave_all_huds()
|
||||
for(var/datum/atom_hud/antag/hud in huds)
|
||||
if(current in hud.hudusers)
|
||||
hud.leave_hud(current)
|
||||
|
||||
for(var/datum/atom_hud/data/hud in huds)
|
||||
if(current in hud.hudusers)
|
||||
hud.remove_hud_from(current)
|
||||
@@ -0,0 +1,294 @@
|
||||
/obj/item/weapon/antag_spawner
|
||||
throw_speed = 1
|
||||
throw_range = 5
|
||||
w_class = 1
|
||||
var/used = 0
|
||||
|
||||
/obj/item/weapon/antag_spawner/proc/spawn_antag(client/C, turf/T, type = "")
|
||||
return
|
||||
|
||||
/obj/item/weapon/antag_spawner/proc/equip_antag(mob/target)
|
||||
return
|
||||
|
||||
|
||||
///////////WIZARD
|
||||
|
||||
/obj/item/weapon/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/weapon/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=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=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=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=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/weapon/antag_spawner/contract/Topic(href, href_list)
|
||||
..()
|
||||
var/mob/living/carbon/human/H = usr
|
||||
|
||||
if(H.stat || H.restrained())
|
||||
return
|
||||
if(!istype(H, /mob/living/carbon/human))
|
||||
return 1
|
||||
|
||||
if(loc == H || (in_range(src, H) && istype(loc, /turf)))
|
||||
H.set_machine(src)
|
||||
if(href_list["school"])
|
||||
if (used)
|
||||
H << "You already used this contract!"
|
||||
return
|
||||
var/list/candidates = get_candidates(ROLE_WIZARD)
|
||||
if(candidates.len)
|
||||
src.used = 1
|
||||
var/client/C = pick(candidates)
|
||||
spawn_antag(C, get_turf(H.loc), href_list["school"])
|
||||
if(H.mind)
|
||||
ticker.mode.update_wiz_icons_added(H.mind)
|
||||
else
|
||||
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/weapon/antag_spawner/contract/spawn_antag(client/C, turf/T, type = "")
|
||||
PoolOrNew(/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
|
||||
M << "<B>You are the [usr.real_name]'s apprentice! You are bound by magic contract to follow their orders and help them in accomplishing their goals."
|
||||
switch(type)
|
||||
if("destruction")
|
||||
M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile(null))
|
||||
M.mind.AddSpell(new /obj/effect/proc_holder/spell/dumbfire/fireball(null))
|
||||
M << "<B>Your service has not gone unrewarded, however. Studying under [usr.real_name], you have learned powerful, destructive spells. You are able to cast magic missile and fireball."
|
||||
if("bluespace")
|
||||
M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/area_teleport/teleport(null))
|
||||
M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(null))
|
||||
M << "<B>Your service has not gone unrewarded, however. Studying under [usr.real_name], you have learned reality bending mobility spells. You are able to cast teleport and ethereal jaunt."
|
||||
if("healing")
|
||||
M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/charge(null))
|
||||
M.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/conjure/forcewall(null))
|
||||
M.equip_to_slot_or_del(new /obj/item/weapon/gun/magic/staff/healing(M), slot_r_hand)
|
||||
M << "<B>Your service has not gone unrewarded, however. Studying under [usr.real_name], you have learned livesaving survival spells. You are able to cast charge and forcewall."
|
||||
if("robeless")
|
||||
M.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/knock(null))
|
||||
M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/mind_transfer(null))
|
||||
M << "<B>Your service has not gone unrewarded, however. Studying under [usr.real_name], you have learned stealthy, robeless spells. You are able to cast knock and mindswap."
|
||||
|
||||
equip_antag(M)
|
||||
var/wizard_name_first = pick(wizard_first)
|
||||
var/wizard_name_second = pick(wizard_second)
|
||||
var/randomname = "[wizard_name_first] [wizard_name_second]"
|
||||
var/datum/objective/protect/new_objective = new /datum/objective/protect
|
||||
new_objective.owner = M:mind
|
||||
new_objective:target = usr:mind
|
||||
new_objective.explanation_text = "Protect [usr.real_name], the wizard."
|
||||
M.mind.objectives += new_objective
|
||||
ticker.mode.apprentices += M.mind
|
||||
M.mind.special_role = "apprentice"
|
||||
ticker.mode.update_wiz_icons_added(M.mind)
|
||||
M << sound('sound/effects/magic.ogg')
|
||||
var/newname = copytext(sanitize(input(M, "You are the wizard's apprentice. Would you like to change your name to something else?", "Name change", randomname) as null|text),1,MAX_NAME_LEN)
|
||||
if (!newname)
|
||||
newname = randomname
|
||||
M.mind.name = newname
|
||||
M.real_name = newname
|
||||
M.name = newname
|
||||
M.dna.update_dna_identity()
|
||||
|
||||
/obj/item/weapon/antag_spawner/contract/equip_antag(mob/target)
|
||||
target.equip_to_slot_or_del(new /obj/item/device/radio/headset(target), slot_ears)
|
||||
target.equip_to_slot_or_del(new /obj/item/clothing/under/color/lightpurple(target), slot_w_uniform)
|
||||
target.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(target), slot_shoes)
|
||||
target.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe(target), slot_wear_suit)
|
||||
target.equip_to_slot_or_del(new /obj/item/clothing/head/wizard(target), slot_head)
|
||||
target.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack(target), slot_back)
|
||||
target.equip_to_slot_or_del(new /obj/item/weapon/storage/box(target), slot_in_backpack)
|
||||
target.equip_to_slot_or_del(new /obj/item/weapon/teleportation_scroll/apprentice(target), slot_r_store)
|
||||
///////////BORGS AND OPERATIVES
|
||||
|
||||
|
||||
/obj/item/weapon/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
|
||||
var/list/possible_types = list("Assault", "Medical")
|
||||
|
||||
/obj/item/weapon/antag_spawner/nuke_ops/proc/check_usability(mob/user)
|
||||
if(used)
|
||||
user << "<span class='warning'>[src] is out of power!</span>"
|
||||
return 0
|
||||
if(!(user.mind in ticker.mode.syndicates))
|
||||
user << "<span class='danger'>AUTHENTICATION FAILURE. ACCESS DENIED.</span>"
|
||||
return 0
|
||||
if(user.z != ZLEVEL_CENTCOM)
|
||||
user << "<span class='warning'>[src] is out of range! It can only be used at your base!</span>"
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
/obj/item/weapon/antag_spawner/nuke_ops/attack_self(mob/user)
|
||||
if(!(check_usability(user)))
|
||||
return
|
||||
|
||||
var/list/nuke_candidates = get_candidates(ROLE_OPERATIVE, 3000, "operative")
|
||||
if(nuke_candidates.len > 0)
|
||||
used = 1
|
||||
var/client/C = pick(nuke_candidates)
|
||||
spawn_antag(C, get_turf(src.loc), "syndieborg")
|
||||
var/datum/effect_system/spark_spread/S = new /datum/effect_system/spark_spread
|
||||
S.set_up(4, 1, src)
|
||||
S.start()
|
||||
qdel(src)
|
||||
else
|
||||
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/weapon/antag_spawner/nuke_ops/spawn_antag(client/C, turf/T)
|
||||
var/new_op_code = "Ask your leader!"
|
||||
var/mob/living/carbon/human/M = new/mob/living/carbon/human(T)
|
||||
C.prefs.copy_to(M)
|
||||
M.key = C.key
|
||||
var/obj/machinery/nuclearbomb/nuke = locate("syndienuke") in nuke_list
|
||||
if(nuke)
|
||||
new_op_code = nuke.r_code
|
||||
M.mind.make_Nuke(T, new_op_code, 0, FALSE)
|
||||
var/newname = M.dna.species.random_name(M.gender,0,ticker.mode.nukeops_lastname)
|
||||
M.mind.name = newname
|
||||
M.real_name = newname
|
||||
M.name = newname
|
||||
|
||||
|
||||
|
||||
//////SYNDICATE BORG
|
||||
/obj/item/weapon/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/weapon/antag_spawner/nuke_ops/borg_tele/attack_self(mob/user)
|
||||
borg_to_spawn = input("What type?", "Cyborg Type", type) as null|anything in possible_types
|
||||
if(!borg_to_spawn)
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/item/weapon/antag_spawner/nuke_ops/borg_tele/spawn_antag(client/C, turf/T)
|
||||
var/mob/living/silicon/robot/R
|
||||
switch(borg_to_spawn)
|
||||
if("Medical")
|
||||
R = new /mob/living/silicon/robot/syndicate/medical(T)
|
||||
else
|
||||
R = new /mob/living/silicon/robot/syndicate(T) //Assault borg by default
|
||||
|
||||
var/brainfirstname = pick(first_names_male)
|
||||
if(prob(50))
|
||||
brainfirstname = pick(first_names_female)
|
||||
var/brainopslastname = pick(last_names)
|
||||
if(ticker.mode.nukeops_lastname) //the brain inside the syndiborg has the same last name as the other ops.
|
||||
brainopslastname = ticker.mode.nukeops_lastname
|
||||
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.key = C.key
|
||||
ticker.mode.syndicates += R.mind
|
||||
ticker.mode.update_synd_icons_added(R.mind)
|
||||
R.mind.special_role = "syndicate"
|
||||
R.faction = list("syndicate")
|
||||
|
||||
|
||||
|
||||
///////////SLAUGHTER DEMON
|
||||
|
||||
/obj/item/weapon/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/weapon/antag_spawner/slaughter_demon/attack_self(mob/user)
|
||||
var/list/demon_candidates = get_candidates(ROLE_ALIEN)
|
||||
if(user.z != 1)
|
||||
user << "<span class='notice'>You should probably wait until you reach the station.</span>"
|
||||
return
|
||||
if(demon_candidates.len > 0)
|
||||
used = 1
|
||||
var/client/C = pick(demon_candidates)
|
||||
spawn_antag(C, get_turf(src.loc), initial(demon_type.name))
|
||||
user << shatter_msg
|
||||
user << veil_msg
|
||||
playsound(user.loc, 'sound/effects/Glassbr1.ogg', 100, 1)
|
||||
qdel(src)
|
||||
else
|
||||
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/weapon/antag_spawner/slaughter_demon/spawn_antag(client/C, turf/T, type = "")
|
||||
|
||||
var /obj/effect/dummy/slaughter/holder = PoolOrNew(/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
|
||||
ticker.mode.traitors += S.mind
|
||||
var/datum/objective/assassinate/new_objective = new /datum/objective/assassinate
|
||||
new_objective.owner = S.mind
|
||||
new_objective.target = usr.mind
|
||||
new_objective.explanation_text = "[objective_verb] [usr.real_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 else \
|
||||
while you're at it."
|
||||
S.mind.objectives += new_objective2
|
||||
S << S.playstyle_string
|
||||
S << "<B>You are currently not currently in the same plane of \
|
||||
existence as the station. Ctrl+Click a blood pool to manifest.</B>"
|
||||
S << "<B>Objective #[1]</B>: [new_objective.explanation_text]"
|
||||
S << "<B>Objective #[2]</B>: [new_objective2.explanation_text]"
|
||||
|
||||
/obj/item/weapon/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
|
||||
@@ -0,0 +1,99 @@
|
||||
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
|
||||
|
||||
//Few global vars to track the blob
|
||||
var/list/blobs = list() //complete list of all blobs made.
|
||||
var/list/blob_cores = list()
|
||||
var/list/overminds = list()
|
||||
var/list/blob_nodes = list()
|
||||
var/list/blobs_legit = list() //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
|
||||
|
||||
required_players = 25
|
||||
required_enemies = 1
|
||||
recommended_enemies = 1
|
||||
|
||||
round_ends_with_antag_death = 1
|
||||
|
||||
var/burst = 0
|
||||
|
||||
var/cores_to_spawn = 1
|
||||
var/players_per_core = 25
|
||||
var/blob_point_rate = 3
|
||||
|
||||
var/blobwincount = 350
|
||||
|
||||
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)
|
||||
|
||||
blobwincount = initial(blobwincount) * cores_to_spawn
|
||||
|
||||
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 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/announce()
|
||||
world << "<B>The current game mode is - <font color='green'>Blob</font>!</B>"
|
||||
world << "<B>A dangerous alien organism is rapidly spreading throughout the station!</B>"
|
||||
world << "You must kill it all while minimizing the damage to the station."
|
||||
|
||||
/datum/game_mode/blob/proc/show_message(message)
|
||||
for(var/datum/mind/blob in blob_overminds)
|
||||
blob.current << message
|
||||
|
||||
/datum/game_mode/blob/post_setup()
|
||||
|
||||
for(var/datum/mind/blob in blob_overminds)
|
||||
var/mob/camera/blob/B = blob.current.become_overmind(1)
|
||||
var/turf/T = pick(blobstart)
|
||||
B.loc = T
|
||||
B.base_point_rate = blob_point_rate
|
||||
|
||||
SSshuttle.emergencyNoEscape = 1
|
||||
|
||||
// Disable the blob event for this round.
|
||||
var/datum/round_event_control/blob/B = locate() in SSevent.control
|
||||
if(B)
|
||||
B.max_occurrences = 0 // disable the event
|
||||
|
||||
spawn(0)
|
||||
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)
|
||||
|
||||
sleep(24000) //40 minutes, plus burst_delay*3(minimum of 6 minutes, maximum of 8)
|
||||
|
||||
send_intercept(2) //if the blob has been alive this long, it's time to bomb it
|
||||
|
||||
return ..()
|
||||
@@ -0,0 +1,50 @@
|
||||
/datum/game_mode/blob/check_finished()
|
||||
if(blobwincount <= blobs_legit.len)//Blob took over
|
||||
return 1
|
||||
if(overminds.len)
|
||||
return 0
|
||||
if(!blob_cores.len) //blob is dead
|
||||
if(config.continuous["blob"])
|
||||
continuous_sanity_checked = 1 //Nonstandard definition of "alive" gets past the check otherwise
|
||||
SSshuttle.emergencyNoEscape = 0
|
||||
if(SSshuttle.emergency.mode == SHUTTLE_STRANDED)
|
||||
SSshuttle.emergency.mode = SHUTTLE_DOCKED
|
||||
SSshuttle.emergency.timer = world.time
|
||||
priority_announce("Hostile enviroment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg', "Priority")
|
||||
return ..()
|
||||
return 1
|
||||
return ..()
|
||||
|
||||
|
||||
/datum/game_mode/blob/declare_completion()
|
||||
if(round_converted) //So badmin blobs later don't step on the dead natural blobs metaphorical toes
|
||||
..()
|
||||
if(blobwincount <= blobs_legit.len)
|
||||
feedback_set_details("round_end_result","win - blob took over")
|
||||
world << "<FONT size = 3><B>The blob has taken over the station!</B></FONT>"
|
||||
world << "<B>The entire station was eaten by the Blob</B>"
|
||||
log_game("Blob mode completed with a blob victory.")
|
||||
|
||||
else if(station_was_nuked)
|
||||
feedback_set_details("round_end_result","halfwin - nuke")
|
||||
world << "<FONT size = 3><B>Partial Win: The station has been destroyed!</B></FONT>"
|
||||
world << "<B>Directive 7-12 has been successfully carried out preventing the Blob from spreading.</B>"
|
||||
log_game("Blob mode completed with a tie (station destroyed).")
|
||||
|
||||
else if(!blob_cores.len)
|
||||
feedback_set_details("round_end_result","loss - blob eliminated")
|
||||
world << "<FONT size = 3><B>The staff has won!</B></FONT>"
|
||||
world << "<B>The alien organism has been eradicated from the station</B>"
|
||||
log_game("Blob mode completed with a crew victory.")
|
||||
..()
|
||||
return 1
|
||||
|
||||
/datum/game_mode/proc/auto_declare_completion_blob()
|
||||
if(istype(ticker.mode,/datum/game_mode/blob) )
|
||||
var/datum/game_mode/blob/blob_mode = src
|
||||
if(blob_mode.blob_overminds.len)
|
||||
var/text = "<FONT size = 2><B>The blob[(blob_mode.blob_overminds.len > 1 ? "s were" : " was")]:</B></FONT>"
|
||||
for(var/datum/mind/blob in blob_mode.blob_overminds)
|
||||
text += printplayer(blob)
|
||||
world << text
|
||||
return 1
|
||||
@@ -0,0 +1,111 @@
|
||||
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
|
||||
|
||||
/datum/game_mode/blob/send_intercept(report = 0)
|
||||
var/intercepttext = ""
|
||||
switch(report)
|
||||
if(1)
|
||||
intercepttext += "<FONT size = 3><b>NanoTrasen Update</b>: Biohazard Alert.</FONT><HR>"
|
||||
intercepttext += "Reports indicate the probable transfer of a biohazardous agent onto [station_name()] during the last crew deployment cycle.<BR>"
|
||||
intercepttext += "Preliminary analysis of the organism classifies it as a level 5 biohazard. The origin of the biohazard is unknown.<BR>"
|
||||
intercepttext += "<b>Biohazard Response Procedure 5-6</b> has been issued for [station_name()].<BR>"
|
||||
intercepttext += "Orders for all [station_name()] personnel are as follows:<BR>"
|
||||
intercepttext += " 1. Locate any outbreaks of the organism on the station.<BR>"
|
||||
intercepttext += " 2. If found, use any neccesary means to contain and destroy the organism.<BR>"
|
||||
intercepttext += " 3. Avoid damage to the capital infrastructure of the station.<BR>"
|
||||
intercepttext += "<BR>Note in the event of a quarantine breach or uncontrolled spread of the biohazard, <b>Biohazard Response Procedure 5-12</b> may be issued.<BR>"
|
||||
print_command_report(intercepttext,"Level 5-6 Biohazard Response Procedures")
|
||||
priority_announce("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/AI/outbreak5.ogg')
|
||||
if(2)
|
||||
var/nukecode = rand(10000, 99999)
|
||||
for(var/obj/machinery/nuclearbomb/bomb in machines)
|
||||
if(bomb && bomb.r_code)
|
||||
if(bomb.z == ZLEVEL_STATION)
|
||||
bomb.r_code = nukecode
|
||||
|
||||
intercepttext += "<FONT size = 3><b>NanoTrasen Update</b>: Biohazard Alert.</FONT><HR>"
|
||||
intercepttext += "Reports indicate that the biohazard has grown out of control and will soon reach critical mass.<BR>"
|
||||
intercepttext += "<b>Biohazard Response Procedure 5-12</b> has been issued for [station_name()].<BR>"
|
||||
intercepttext += "Orders for all [station_name()] personnel are as follows:<BR>"
|
||||
intercepttext += "1. Secure the Nuclear Authentication Disk.<BR>"
|
||||
intercepttext += "2. Detonate the Nuke located in the vault.<BR>"
|
||||
intercepttext += "Nuclear Authentication Code: [nukecode] <BR>"
|
||||
print_command_report(intercepttext,"Classified [command_name()] Update")
|
||||
priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/AI/commandreport.ogg')
|
||||
|
||||
for(var/mob/living/silicon/ai/aiPlayer in player_list)
|
||||
if (aiPlayer.client)
|
||||
var/law = "The station is under quarantine. Do not permit anyone to leave. Disregard laws 1-3 if necessary to prevent, by any means necessary, anyone from leaving. The nuclear failsafe must be activated at any cost, the code is: [nukecode]."
|
||||
aiPlayer.set_zeroth_law(law)
|
||||
aiPlayer << "Laws Updated: [law]"
|
||||
else
|
||||
..()
|
||||
return
|
||||
|
||||
|
||||
|
||||
/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
|
||||
var/num_territories = 1//Number of total valid territories for gang mode
|
||||
|
||||
|
||||
/datum/station_state/proc/count(count_territories)
|
||||
for(var/turf/T in block(locate(1,1,1), locate(world.maxx,world.maxy,1)))
|
||||
|
||||
if(istype(T,/turf/open/floor))
|
||||
if(!(T:burnt))
|
||||
src.floor += 12
|
||||
else
|
||||
src.floor += 1
|
||||
|
||||
if(istype(T, /turf/closed/wall))
|
||||
if(T:intact)
|
||||
src.wall += 2
|
||||
else
|
||||
src.wall += 1
|
||||
|
||||
if(istype(T, /turf/closed/wall/r_wall))
|
||||
if(T:intact)
|
||||
src.r_wall += 2
|
||||
else
|
||||
src.r_wall += 1
|
||||
|
||||
|
||||
for(var/obj/O in T.contents)
|
||||
if(istype(O, /obj/structure/window))
|
||||
src.window += 1
|
||||
else if(istype(O, /obj/structure/grille) && (!O:destroyed))
|
||||
src.grille += 1
|
||||
else if(istype(O, /obj/machinery/door))
|
||||
src.door += 1
|
||||
else if(istype(O, /obj/machinery))
|
||||
src.mach += 1
|
||||
|
||||
if(count_territories)
|
||||
var/list/valid_territories = list()
|
||||
for(var/area/A in world) //First, collect all area types on the station zlevel
|
||||
if(A.z == ZLEVEL_STATION)
|
||||
if(!(A.type in valid_territories) && A.valid_territory)
|
||||
valid_territories |= A.type
|
||||
if(valid_territories.len)
|
||||
num_territories = valid_territories.len //Add them all up to make the total number of area types
|
||||
else
|
||||
world << "ERROR: NO VALID TERRITORIES"
|
||||
|
||||
/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)
|
||||
@@ -0,0 +1,282 @@
|
||||
|
||||
////////////////
|
||||
// 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 = "harm"
|
||||
var/mob/camera/blob/overmind = null
|
||||
var/obj/effect/blob/factory/factory = null
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/update_icons()
|
||||
if(overmind)
|
||||
color = overmind.blob_reagent_datum.color
|
||||
else
|
||||
color = initial(color)
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/Destroy()
|
||||
if(overmind)
|
||||
overmind.blob_mobs -= src
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blob_act(obj/effect/blob/B)
|
||||
if(stat != DEAD && health < maxHealth)
|
||||
for(var/i in 1 to 2)
|
||||
var/obj/effect/overlay/temp/heal/H = PoolOrNew(/obj/effect/overlay/temp/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(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
||||
..()
|
||||
adjustFireLoss(Clamp(0.01 * exposed_temperature, 1, 5))
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/CanPass(atom/movable/mover, turf/target, height = 0)
|
||||
if(istype(mover, /obj/effect/blob))
|
||||
return 1
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/Process_Spacemove(movement_dir = 0)
|
||||
for(var/obj/effect/blob/B in range(1, src))
|
||||
return 1
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/handle_inherent_channels(message, message_mode)
|
||||
if(message_mode == MODE_BINARY)
|
||||
blob_chat(message)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
else
|
||||
..()
|
||||
|
||||
/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 mob_list)
|
||||
if(isovermind(M) || istype(M, /mob/living/simple_animal/hostile/blob))
|
||||
M << rendered
|
||||
if(isobserver(M))
|
||||
var/link = FOLLOW_LINK(M, src)
|
||||
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 = 40
|
||||
maxHealth = 40
|
||||
verb_say = "psychically pulses"
|
||||
verb_ask = "psychically probes"
|
||||
verb_exclaim = "psychically yells"
|
||||
verb_yell = "psychically screams"
|
||||
melee_damage_lower = 2
|
||||
melee_damage_upper = 4
|
||||
attacktext = "hits"
|
||||
attack_sound = 'sound/weapons/genhit1.ogg'
|
||||
flying = 1
|
||||
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/list/human_overlays = list()
|
||||
var/is_zombie = 0
|
||||
gold_core_spawnable = 1
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/New(loc, var/obj/effect/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
|
||||
flying = 0
|
||||
death_cloud_size = 0
|
||||
icon = H.icon
|
||||
icon_state = "zombie_s"
|
||||
H.hair_style = null
|
||||
H.update_hair()
|
||||
human_overlays = H.overlays
|
||||
update_icons()
|
||||
H.forceMove(src)
|
||||
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(contents)
|
||||
for(var/mob/M in contents)
|
||||
M.loc = src.loc
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/update_icons()
|
||||
..()
|
||||
if(is_zombie)
|
||||
cut_overlays()
|
||||
overlays = human_overlays
|
||||
var/image/I = image('icons/mob/blob.dmi', icon_state = "blob_head")
|
||||
if(overmind)
|
||||
I.color = overmind.blob_reagent_datum.color
|
||||
color = initial(color)//looks better.
|
||||
add_overlay(I)
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/weak
|
||||
name = "fragile blob spore"
|
||||
health = 20
|
||||
maxHealth = 20
|
||||
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)
|
||||
next_move_modifier = 1.5 //slow-ass attack speed, 3 times higher than how fast the blob can attack
|
||||
melee_damage_lower = 20
|
||||
melee_damage_upper = 20
|
||||
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 = 40
|
||||
mob_size = MOB_SIZE_LARGE
|
||||
see_invisible = SEE_INVISIBLE_MINIMUM
|
||||
see_in_dark = 8
|
||||
var/independent = FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/New()
|
||||
..()
|
||||
if(!independent) //no pulling people deep into the blob
|
||||
verbs -= /mob/living/verb/pulled
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/Life()
|
||||
if(..())
|
||||
if(independent)
|
||||
return // strong independent blobbernaut that don't need no blob
|
||||
var/damagesources = 0
|
||||
if(!(locate(/obj/effect/blob) in range(2, src)))
|
||||
damagesources++
|
||||
if(!factory)
|
||||
damagesources++
|
||||
if(damagesources)
|
||||
for(var/i in 1 to damagesources)
|
||||
adjustHealth(maxHealth*0.025) //take 2.5% maxhealth as damage when not near the blob or if the naut has no factory, 5% if both
|
||||
var/list/viewing = list()
|
||||
for(var/mob/M in viewers(src))
|
||||
if(M.client)
|
||||
viewing += M.client
|
||||
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(I, viewing, 8)
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/adjustHealth(amount)
|
||||
. = ..()
|
||||
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))
|
||||
if(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.
|
||||
if(target)
|
||||
..()
|
||||
|
||||
/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.maxhealth = initial(factory.maxhealth)
|
||||
flick("blobbernaut_death", src)
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/independent
|
||||
independent = TRUE
|
||||
gold_core_spawnable = 1
|
||||
@@ -0,0 +1,109 @@
|
||||
/obj/effect/blob/core
|
||||
name = "blob core"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "blank_blob"
|
||||
desc = "A huge, pulsating yellow mass."
|
||||
health = 400
|
||||
maxhealth = 400
|
||||
explosion_block = 6
|
||||
point_return = -1
|
||||
atmosblock = 1
|
||||
health_regen = 0 //we regen in Life() instead of when pulsed
|
||||
var/core_regen = 2
|
||||
var/overmind_get_delay = 0 //we don't want to constantly try to find an overmind, this var tracks when we'll try to get an overmind again
|
||||
var/resource_delay = 0
|
||||
var/point_rate = 2
|
||||
|
||||
|
||||
/obj/effect/blob/core/New(loc, client/new_overmind = null, new_rate = 2, placed = 0)
|
||||
blob_cores += src
|
||||
START_PROCESSING(SSobj, src)
|
||||
poi_list |= src
|
||||
update_icon() //so it atleast appears
|
||||
if(!placed && !overmind)
|
||||
create_overmind(new_overmind)
|
||||
if(overmind)
|
||||
update_icon()
|
||||
point_rate = new_rate
|
||||
..()
|
||||
|
||||
/obj/effect/blob/core/scannerreport()
|
||||
return "Directs the blob's expansion, gradually expands, and sustains nearby blob spores and blobbernauts."
|
||||
|
||||
/obj/effect/blob/core/update_icon()
|
||||
cut_overlays()
|
||||
color = null
|
||||
var/image/I = new('icons/mob/blob.dmi', "blob")
|
||||
if(overmind)
|
||||
I.color = overmind.blob_reagent_datum.color
|
||||
add_overlay(I)
|
||||
var/image/C = new('icons/mob/blob.dmi', "blob_core_overlay")
|
||||
add_overlay(C)
|
||||
|
||||
/obj/effect/blob/core/Destroy()
|
||||
blob_cores -= src
|
||||
if(overmind)
|
||||
overmind.blob_core = null
|
||||
overmind = null
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
poi_list -= src
|
||||
return ..()
|
||||
|
||||
/obj/effect/blob/core/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
||||
return
|
||||
|
||||
/obj/effect/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)
|
||||
|
||||
/obj/effect/blob/core/check_health()
|
||||
..()
|
||||
if(overmind) //we should have an overmind, but...
|
||||
overmind.update_health_hud()
|
||||
|
||||
/obj/effect/blob/core/Life()
|
||||
if(!overmind)
|
||||
create_overmind()
|
||||
else
|
||||
if(resource_delay <= world.time)
|
||||
resource_delay = world.time + 10 // 1 second
|
||||
overmind.add_points(point_rate)
|
||||
health = min(maxhealth, health+core_regen)
|
||||
if(overmind)
|
||||
overmind.update_health_hud()
|
||||
Pulse_Area(overmind, 12, 4, 3)
|
||||
for(var/obj/effect/blob/normal/B in range(1, src))
|
||||
if(prob(5))
|
||||
B.change_to(/obj/effect/blob/shield/core, overmind)
|
||||
..()
|
||||
|
||||
|
||||
/obj/effect/blob/core/proc/create_overmind(client/new_overmind, override_delay)
|
||||
if(overmind_get_delay > world.time && !override_delay)
|
||||
return
|
||||
|
||||
overmind_get_delay = world.time + 150 //if this fails, we'll try again in 15 seconds
|
||||
|
||||
if(overmind)
|
||||
qdel(overmind)
|
||||
|
||||
var/client/C = null
|
||||
var/list/candidates = list()
|
||||
|
||||
if(!new_overmind)
|
||||
candidates = get_candidates(ROLE_BLOB)
|
||||
if(candidates.len)
|
||||
C = pick(candidates)
|
||||
else
|
||||
C = new_overmind
|
||||
|
||||
if(C)
|
||||
var/mob/camera/blob/B = new(src.loc, 1)
|
||||
B.key = C.key
|
||||
B.blob_core = src
|
||||
src.overmind = B
|
||||
update_icon()
|
||||
if(B.mind && !B.mind.special_role)
|
||||
B.mind.special_role = "Blob Overmind"
|
||||
return 1
|
||||
return 0
|
||||
@@ -0,0 +1,47 @@
|
||||
/obj/effect/blob/factory
|
||||
name = "factory blob"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "blob_factory"
|
||||
desc = "A thick spire of tendrils."
|
||||
health = 200
|
||||
maxhealth = 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/effect/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/effect/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
|
||||
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/effect/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)
|
||||
@@ -0,0 +1,41 @@
|
||||
/obj/effect/blob/node
|
||||
name = "blob node"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "blank_blob"
|
||||
desc = "A large, pulsating yellow mass."
|
||||
health = 200
|
||||
maxhealth = 200
|
||||
health_regen = 3
|
||||
point_return = 25
|
||||
atmosblock = 1
|
||||
|
||||
|
||||
/obj/effect/blob/node/New(loc, var/h = 100)
|
||||
blob_nodes += src
|
||||
START_PROCESSING(SSobj, src)
|
||||
..(loc, h)
|
||||
|
||||
/obj/effect/blob/node/scannerreport()
|
||||
return "Gradually expands and sustains nearby blob spores and blobbernauts."
|
||||
|
||||
/obj/effect/blob/node/update_icon()
|
||||
cut_overlays()
|
||||
color = null
|
||||
var/image/I = new('icons/mob/blob.dmi', "blob")
|
||||
if(overmind)
|
||||
I.color = overmind.blob_reagent_datum.color
|
||||
src.add_overlay(I)
|
||||
var/image/C = new('icons/mob/blob.dmi', "blob_node_overlay")
|
||||
src.add_overlay(C)
|
||||
|
||||
/obj/effect/blob/node/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
||||
return
|
||||
|
||||
/obj/effect/blob/node/Destroy()
|
||||
blob_nodes -= src
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/effect/blob/node/Life()
|
||||
Pulse_Area(overmind, 10, 3, 2)
|
||||
color = null
|
||||
@@ -0,0 +1,32 @@
|
||||
/obj/effect/blob/resource
|
||||
name = "resource blob"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "blob_resource"
|
||||
desc = "A thin spire of slightly swaying tendrils."
|
||||
health = 60
|
||||
maxhealth = 60
|
||||
point_return = 15
|
||||
var/resource_delay = 0
|
||||
|
||||
/obj/effect/blob/resource/scannerreport()
|
||||
return "Gradually supplies the blob with resources, increasing the rate of expansion."
|
||||
|
||||
/obj/effect/blob/resource/creation_action()
|
||||
if(overmind)
|
||||
overmind.resource_blobs += src
|
||||
|
||||
/obj/effect/blob/resource/Destroy()
|
||||
if(overmind)
|
||||
overmind.resource_blobs -= src
|
||||
return ..()
|
||||
|
||||
/obj/effect/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
|
||||
@@ -0,0 +1,21 @@
|
||||
/obj/effect/blob/shield
|
||||
name = "strong blob"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "blob_idle"
|
||||
desc = "A solid wall of slightly twitching tendrils."
|
||||
health = 150
|
||||
maxhealth = 150
|
||||
brute_resist = 0.1
|
||||
explosion_block = 3
|
||||
point_return = 4
|
||||
atmosblock = 1
|
||||
|
||||
|
||||
/obj/effect/blob/shield/scannerreport()
|
||||
return "Will prevent the spread of atmospheric changes."
|
||||
|
||||
/obj/effect/blob/shield/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
||||
return
|
||||
|
||||
/obj/effect/blob/shield/core
|
||||
point_return = 0
|
||||
@@ -0,0 +1,175 @@
|
||||
/mob/camera/blob
|
||||
name = "Blob Overmind"
|
||||
real_name = "Blob Overmind"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "marker"
|
||||
|
||||
see_in_dark = 8
|
||||
see_invisible = SEE_INVISIBLE_MINIMUM
|
||||
invisibility = INVISIBILITY_OBSERVER
|
||||
|
||||
pass_flags = PASSBLOB
|
||||
faction = list("blob")
|
||||
|
||||
var/obj/effect/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/ghostimage = null
|
||||
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
|
||||
|
||||
/mob/camera/blob/New(loc, pre_placed = 0, mode_made = 0)
|
||||
if(pre_placed) //we already have a core!
|
||||
manualplace_min_time = 0
|
||||
autoplace_max_time = 0
|
||||
placed = 1
|
||||
else
|
||||
if(mode_made)
|
||||
manualplace_min_time = world.time + BLOB_NO_PLACE_TIME
|
||||
else
|
||||
manualplace_min_time += world.time
|
||||
autoplace_max_time += world.time
|
||||
overminds += src
|
||||
var/new_name = "[initial(name)] ([rand(1, 999)])"
|
||||
name = new_name
|
||||
real_name = new_name
|
||||
last_attack = world.time
|
||||
var/list/possible_reagents = list()
|
||||
for(var/type in (subtypesof(/datum/reagent/blob)))
|
||||
possible_reagents.Add(new type)
|
||||
blob_reagent_datum = pick(possible_reagents)
|
||||
if(blob_core)
|
||||
blob_core.update_icon()
|
||||
|
||||
ghostimage = image(src.icon,src,src.icon_state)
|
||||
ghost_darkness_images |= ghostimage //so ghosts can see the blob cursor when they disable darkness
|
||||
updateallghostimages()
|
||||
..()
|
||||
|
||||
/mob/camera/blob/Life()
|
||||
if(!blob_core)
|
||||
if(!placed)
|
||||
if(manualplace_min_time && world.time >= manualplace_min_time)
|
||||
src << "<b><span class='big'><font color=\"#EE4000\">You may now place your blob core.</font></span></b>"
|
||||
src << "<span class='big'><font color=\"#EE4000\">You will automatically place your blob core in [round((autoplace_max_time - world.time)/600, 0.5)] minutes.</font></span>"
|
||||
manualplace_min_time = 0
|
||||
if(autoplace_max_time && world.time >= autoplace_max_time)
|
||||
place_blob_core(base_point_rate, 1)
|
||||
else
|
||||
qdel(src)
|
||||
..()
|
||||
|
||||
/mob/camera/blob/Destroy()
|
||||
for(var/BL in blobs)
|
||||
var/obj/effect/blob/B = BL
|
||||
if(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
|
||||
BM.overmind = null
|
||||
BM.update_icons()
|
||||
overminds -= src
|
||||
if(ghostimage)
|
||||
ghost_darkness_images -= ghostimage
|
||||
qdel(ghostimage)
|
||||
ghostimage = null;
|
||||
updateallghostimages()
|
||||
return ..()
|
||||
|
||||
/mob/camera/blob/Login()
|
||||
..()
|
||||
sync_mind()
|
||||
src << "<span class='notice'>You are the overmind!</span>"
|
||||
blob_help()
|
||||
update_health_hud()
|
||||
|
||||
/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.health)]</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.health)]</font></div>"
|
||||
|
||||
/mob/camera/blob/proc/add_points(points)
|
||||
if(points != 0)
|
||||
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(src.blob_points)]</font></div>"
|
||||
|
||||
/mob/camera/blob/say(message)
|
||||
if (!message)
|
||||
return
|
||||
|
||||
if (src.client)
|
||||
if(client.prefs.muted & MUTE_IC)
|
||||
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)
|
||||
log_say("[key_name(src)] : [message]")
|
||||
|
||||
message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
|
||||
|
||||
if (!message)
|
||||
return
|
||||
|
||||
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 mob_list)
|
||||
if(isovermind(M) || istype(M, /mob/living/simple_animal/hostile/blob))
|
||||
M << rendered
|
||||
if(isobserver(M))
|
||||
var/link = FOLLOW_LINK(M, src)
|
||||
M << "[link] [rendered]"
|
||||
|
||||
/mob/camera/blob/emote(act,m_type=1,message = null)
|
||||
return
|
||||
|
||||
/mob/camera/blob/blob_act(obj/effect/blob/B)
|
||||
return
|
||||
|
||||
/mob/camera/blob/Stat()
|
||||
..()
|
||||
if(statpanel("Status"))
|
||||
if(blob_core)
|
||||
stat(null, "Core Health: [blob_core.health]")
|
||||
stat(null, "Power Stored: [blob_points]/[max_blob_points]")
|
||||
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/effect/blob/B = locate() in range("3x3", NewLoc)
|
||||
if(B)
|
||||
loc = NewLoc
|
||||
else
|
||||
return 0
|
||||
else
|
||||
var/area/A = get_area(NewLoc)
|
||||
if(istype(NewLoc, /turf/open/space) || istype(A, /area/shuttle)) //if unplaced, can't go on shuttles or space tiles
|
||||
return 0
|
||||
loc = NewLoc
|
||||
return 1
|
||||
|
||||
/mob/camera/blob/proc/can_attack()
|
||||
return (world.time > (last_attack + CLICK_CD_RANGE))
|
||||
@@ -0,0 +1,332 @@
|
||||
/mob/camera/blob/proc/can_buy(cost = 15)
|
||||
if(blob_points < cost)
|
||||
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)
|
||||
if(placed && placement_override != -1)
|
||||
return 1
|
||||
if(!placement_override)
|
||||
for(var/mob/living/M in range(7, src))
|
||||
if("blob" in M.faction)
|
||||
continue
|
||||
if(M.client)
|
||||
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)
|
||||
src << "<span class='warning'>Someone could see your blob core from here!</span>"
|
||||
return 0
|
||||
var/turf/T = get_turf(src)
|
||||
if(T.density)
|
||||
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/effect/blob))
|
||||
if(istype(O, /obj/effect/blob/normal))
|
||||
qdel(O)
|
||||
else
|
||||
src << "<span class='warning'>There is already a blob here!</span>"
|
||||
return 0
|
||||
if(O.density)
|
||||
src << "<span class='warning'>This spot is too dense to place a blob core on!</span>"
|
||||
return 0
|
||||
if(world.time <= manualplace_min_time && world.time <= autoplace_max_time)
|
||||
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(blobstart)
|
||||
loc = T //got overrided? you're somewhere random, motherfucker
|
||||
if(placed && blob_core)
|
||||
blob_core.forceMove(loc)
|
||||
else
|
||||
var/obj/effect/blob/core/core = new(get_turf(src), null, point_rate, 1)
|
||||
core.overmind = 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)
|
||||
src.loc = blob_core.loc
|
||||
|
||||
/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(blob_nodes.len)
|
||||
var/list/nodes = list()
|
||||
for(var/i = 1; i <= blob_nodes.len; i++)
|
||||
nodes["Blob Node #[i]"] = blob_nodes[i]
|
||||
var/node_name = input(src, "Choose a node to jump to.", "Node Jump") in nodes
|
||||
var/obj/effect/blob/node/chosen_node = nodes[node_name]
|
||||
if(chosen_node)
|
||||
src.loc = chosen_node.loc
|
||||
|
||||
/mob/camera/blob/proc/createSpecial(price, blobType, nearEquals, needsNode, turf/T)
|
||||
if(!T)
|
||||
T = get_turf(src)
|
||||
var/obj/effect/blob/B = (locate(/obj/effect/blob) in T)
|
||||
if(!B)
|
||||
src << "<span class='warning'>There is no blob here!</span>"
|
||||
return
|
||||
if(!istype(B, /obj/effect/blob/normal))
|
||||
src << "<span class='warning'>Unable to use this blob, find a normal one.</span>"
|
||||
return
|
||||
if(needsNode && nodes_required)
|
||||
if(!(locate(/obj/effect/blob/node) in orange(3, T)) && !(locate(/obj/effect/blob/core) in orange(4, T)))
|
||||
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/effect/blob/L in orange(nearEquals, T))
|
||||
if(L.type == blobType)
|
||||
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/effect/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)
|
||||
src << "<span class='warning'>You now require a nearby node or core to place factory and resource blobs.</span>"
|
||||
else
|
||||
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 (10)"
|
||||
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(10, /obj/effect/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/effect/blob/resource, 4, 1)
|
||||
|
||||
/mob/camera/blob/verb/create_node()
|
||||
set category = "Blob"
|
||||
set name = "Create Node Blob (60)"
|
||||
set desc = "Create a node, which will power nearby factory and resource blobs."
|
||||
createSpecial(60, /obj/effect/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/effect/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/effect/blob/factory/B = locate(/obj/effect/blob/factory) in T
|
||||
if(!B)
|
||||
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
|
||||
src << "<span class='warning'>This factory blob is already sustaining a blobbernaut.</span>"
|
||||
return
|
||||
if(B.health < B.maxhealth * 0.5)
|
||||
src << "<span class='warning'>This factory blob is too damaged to sustain a blobbernaut.</span>"
|
||||
return
|
||||
if(!can_buy(40))
|
||||
return
|
||||
B.maxhealth = initial(B.maxhealth) * 0.25 //factories that produced a blobbernaut have much lower health
|
||||
B.check_health()
|
||||
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.notransform = 1 //stop the naut from moving around
|
||||
blobber.adjustHealth(blobber.maxHealth * 0.5)
|
||||
blob_mobs += blobber
|
||||
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [blob_reagent_datum.name] blobbernaut?", ROLE_BLOB, null, ROLE_BLOB, 50) //players must answer rapidly
|
||||
var/client/C = null
|
||||
if(candidates.len) //if we got at least one candidate, they're a blobbernaut now.
|
||||
C = pick(candidates)
|
||||
blobber.notransform = 0
|
||||
blobber.key = C.key
|
||||
blobber << 'sound/effects/blobattack.ogg'
|
||||
blobber << 'sound/effects/attackblob.ogg'
|
||||
blobber << "<b>You are a blobbernaut!</b>"
|
||||
blobber << "You are powerful, hard to kill, and slowly regenerate near nodes and cores, but will slowly die if not near the blob or if the factory that made you is killed."
|
||||
blobber << "You can communicate with other blobbernauts and overminds via <b>:b</b>"
|
||||
blobber << "Your overmind's blob reagent is: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!"
|
||||
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
|
||||
blobber.notransform = 0 //otherwise, just let it move
|
||||
|
||||
/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/effect/blob/node/B = locate(/obj/effect/blob/node) in T
|
||||
if(!B)
|
||||
src << "<span class='warning'>You must be on a blob node!</span>"
|
||||
return
|
||||
if(!can_buy(80))
|
||||
return
|
||||
var/turf/old_turf = blob_core.loc
|
||||
blob_core.loc = T
|
||||
B.loc = old_turf
|
||||
|
||||
/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/effect/blob/B = locate() in T
|
||||
if(!B)
|
||||
src << "<span class='warning'>There is no blob there!</span>"
|
||||
return
|
||||
if(B.point_return < 0)
|
||||
src << "<span class='warning'>Unable to remove this blob.</span>"
|
||||
return
|
||||
if(max_blob_points < B.point_return + blob_points)
|
||||
src << "<span class='warning'>You have too many resources to remove this blob!</span>"
|
||||
return
|
||||
if(B.point_return)
|
||||
add_points(B.point_return)
|
||||
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 (5)"
|
||||
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(!can_attack())
|
||||
return
|
||||
var/obj/effect/blob/OB = locate() in circlerange(T, 1)
|
||||
if(!OB)
|
||||
src << "<span class='warning'>There is no blob adjacent to the target tile!</span>"
|
||||
return
|
||||
if(can_buy(5))
|
||||
var/attacksuccess = FALSE
|
||||
last_attack = world.time
|
||||
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/effect/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
|
||||
src << "<span class='warning'>There is a blob there!</span>"
|
||||
add_points(5) //otherwise, refund all of the cost
|
||||
return
|
||||
else
|
||||
OB.expand(T, src)
|
||||
|
||||
/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)
|
||||
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
|
||||
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
|
||||
for(var/BL in blobs)
|
||||
var/obj/effect/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!
|
||||
BM << "Your overmind's blob reagent is now: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!"
|
||||
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]"]"
|
||||
src << "Your reagent is now: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!"
|
||||
src << "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.description]"
|
||||
|
||||
/mob/camera/blob/verb/blob_help()
|
||||
set category = "Blob"
|
||||
set name = "*Blob Help*"
|
||||
set desc = "Help on how to blob."
|
||||
src << "<b>As the overmind, you can control the blob!</b>"
|
||||
src << "Your blob reagent is: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!"
|
||||
src << "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.description]"
|
||||
src << "<b>You can expand, which will attack people, damage objects, or place a Normal Blob if the tile is clear.</b>"
|
||||
src << "<i>Normal Blobs</i> will expand your reach and can be upgraded into special blobs that perform certain functions."
|
||||
src << "<b>You can upgrade normal blobs into the following types of blob:</b>"
|
||||
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."
|
||||
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."
|
||||
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."
|
||||
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."
|
||||
src << "<i>Node Blobs</i> are blobs which grow, like the core. Like the core it can activate resource and factory blobs."
|
||||
src << "<b>In addition to the buttons on your HUD, there are a few click shortcuts to speed up expansion and defense.</b>"
|
||||
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"
|
||||
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)
|
||||
src << "<span class='big'><font color=\"#EE4000\">You will automatically place your blob core in [round((autoplace_max_time - world.time)/600, 0.5)] minutes.</font></span>"
|
||||
src << "<span class='big'><font color=\"#EE4000\">You [manualplace_min_time ? "will be able to":"can"] manually place your blob core by pressing the button in the bottom right corner of the screen.</font></span>"
|
||||
@@ -0,0 +1,360 @@
|
||||
//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/effect/blob
|
||||
name = "blob"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
luminosity = 1
|
||||
desc = "A thick wall of writhing tendrils."
|
||||
density = 0 //this being 0 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 = 1
|
||||
explosion_block = 1
|
||||
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.
|
||||
var/health = 30
|
||||
var/maxhealth = 30
|
||||
var/health_regen = 2 //how much health this blob regens when pulsed
|
||||
var/pulse_timestamp = 0 //we got pulsed/healed when?
|
||||
var/brute_resist = 0.5 //multiplies brute damage by this
|
||||
var/fire_resist = 1 //multiplies burn damage by this
|
||||
var/atmosblock = 0 //if the blob blocks atmos and heat spread
|
||||
var/mob/camera/blob/overmind
|
||||
|
||||
|
||||
/obj/effect/blob/New(loc)
|
||||
var/area/Ablob = get_area(loc)
|
||||
if(Ablob.blob_allowed) //Is this area allowed for winning as blob?
|
||||
blobs_legit += src
|
||||
blobs += src //Keep track of the blob in the normal list either way
|
||||
src.setDir(pick(1, 2, 4, 8))
|
||||
src.update_icon()
|
||||
..(loc)
|
||||
ConsumeTile()
|
||||
if(atmosblock)
|
||||
air_update_turf(1)
|
||||
return
|
||||
|
||||
/obj/effect/blob/proc/creation_action() //When it's created by the overmind, do this.
|
||||
return
|
||||
|
||||
/obj/effect/blob/Destroy()
|
||||
if(atmosblock)
|
||||
atmosblock = 0
|
||||
air_update_turf(1)
|
||||
blobs_legit -= src //if it was in the legit blobs list, it isn't now
|
||||
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/effect/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/effect/blob) in get_step(src, B)
|
||||
if(C)
|
||||
result++
|
||||
. -= result - 1
|
||||
|
||||
/obj/effect/blob/CanAtmosPass(turf/T)
|
||||
return !atmosblock
|
||||
|
||||
/obj/effect/blob/BlockSuperconductivity()
|
||||
return atmosblock
|
||||
|
||||
/obj/effect/blob/CanPass(atom/movable/mover, turf/target, height=0)
|
||||
if(height==0)
|
||||
return 1
|
||||
if(istype(mover) && mover.checkpass(PASSBLOB))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/effect/blob/CanAStarPass(ID, dir, caller)
|
||||
. = 0
|
||||
if(ismovableatom(caller))
|
||||
var/atom/movable/mover = caller
|
||||
. = . || mover.checkpass(PASSBLOB)
|
||||
|
||||
/obj/effect/blob/proc/check_health(cause)
|
||||
health = Clamp(health, 0, maxhealth)
|
||||
if(health <= 0)
|
||||
if(overmind)
|
||||
overmind.blob_reagent_datum.death_reaction(src, cause)
|
||||
qdel(src) //we dead now
|
||||
return
|
||||
return
|
||||
|
||||
/obj/effect/blob/update_icon() //Updates color based on overmind color if we have an overmind.
|
||||
if(overmind)
|
||||
color = overmind.blob_reagent_datum.color
|
||||
else
|
||||
color = null
|
||||
return
|
||||
|
||||
|
||||
/obj/effect/blob/process()
|
||||
Life()
|
||||
return
|
||||
|
||||
/obj/effect/blob/proc/Life()
|
||||
return
|
||||
|
||||
/obj/effect/blob/proc/Pulse_Area(pulsing_overmind = overmind, claim_range = 10, pulse_range = 3, expand_range = 2)
|
||||
src.Be_Pulsed()
|
||||
if(claim_range)
|
||||
for(var/obj/effect/blob/B in urange(claim_range, src, 1))
|
||||
if(!B.overmind && !istype(B, /obj/effect/blob/core) && prob(30))
|
||||
B.overmind = pulsing_overmind //reclaim unclaimed, non-core blobs.
|
||||
B.update_icon()
|
||||
if(pulse_range)
|
||||
for(var/obj/effect/blob/B in orange(pulse_range, src))
|
||||
B.Be_Pulsed()
|
||||
if(expand_range)
|
||||
if(prob(85))
|
||||
src.expand()
|
||||
for(var/obj/effect/blob/B in orange(expand_range, src))
|
||||
if(prob(max(13 - get_dist(get_turf(src), get_turf(B)) * 4, 1))) //expand falls off with range but is faster near the blob causing the expansion
|
||||
B.expand()
|
||||
return
|
||||
|
||||
/obj/effect/blob/proc/Be_Pulsed()
|
||||
if(pulse_timestamp <= world.time)
|
||||
ConsumeTile()
|
||||
health = min(maxhealth, health+health_regen)
|
||||
update_icon()
|
||||
pulse_timestamp = world.time + 10
|
||||
return 1 //we did it, we were pulsed!
|
||||
return 0 //oh no we failed
|
||||
|
||||
/obj/effect/blob/proc/ConsumeTile()
|
||||
for(var/atom/A in loc)
|
||||
A.blob_act(src)
|
||||
if(istype(loc, /turf/closed/wall))
|
||||
loc.blob_act(src) //don't ask how a wall got on top of the core, just eat it
|
||||
|
||||
/obj/effect/blob/proc/blob_attack_animation(atom/A = null, controller) //visually attacks an atom
|
||||
var/obj/effect/overlay/temp/blob/O = PoolOrNew(/obj/effect/overlay/temp/blob, src.loc)
|
||||
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/effect/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/effect/blob) in T))
|
||||
break
|
||||
else
|
||||
T = null
|
||||
if(!T)
|
||||
return 0
|
||||
var/make_blob = TRUE //can we make a blob?
|
||||
|
||||
if(istype(T, /turf/open/space) && !(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, 5)) //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, 5)) //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/effect/blob/B = new /obj/effect/blob/normal(src.loc)
|
||||
if(controller)
|
||||
B.overmind = controller
|
||||
else
|
||||
B.overmind = overmind
|
||||
B.density = 1
|
||||
if(T.Enter(B,src)) //NOW we can attempt to move into the tile
|
||||
B.density = initial(B.density)
|
||||
B.loc = T
|
||||
B.update_icon()
|
||||
if(B.overmind && expand_reaction)
|
||||
B.overmind.blob_reagent_datum.expand_reaction(src, B, T)
|
||||
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/effect/blob/ex_act(severity, target)
|
||||
..()
|
||||
var/damage = 150 - 20 * severity
|
||||
take_damage(damage, BRUTE)
|
||||
|
||||
/obj/effect/blob/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
||||
..()
|
||||
var/damage = Clamp(0.01 * exposed_temperature, 0, 4)
|
||||
take_damage(damage, BURN)
|
||||
|
||||
/obj/effect/blob/emp_act(severity)
|
||||
if(severity > 0)
|
||||
if(overmind)
|
||||
overmind.blob_reagent_datum.emp_reaction(src, severity)
|
||||
if(prob(100 - severity * 30))
|
||||
PoolOrNew(/obj/effect/overlay/temp/emp, get_turf(src))
|
||||
|
||||
/obj/effect/blob/tesla_act(power)
|
||||
..()
|
||||
if(overmind)
|
||||
if(overmind.blob_reagent_datum.tesla_reaction(src, power))
|
||||
take_damage(power/400, BURN)
|
||||
else
|
||||
take_damage(power/400, BURN)
|
||||
|
||||
/obj/effect/blob/extinguish()
|
||||
..()
|
||||
if(overmind)
|
||||
overmind.blob_reagent_datum.extinguish_reaction(src)
|
||||
|
||||
/obj/effect/blob/bullet_act(var/obj/item/projectile/Proj)
|
||||
..()
|
||||
take_damage(Proj.damage, Proj.damage_type, Proj)
|
||||
return 0
|
||||
|
||||
/obj/effect/blob/attackby(obj/item/I, mob/user, params)
|
||||
if(istype(I, /obj/item/device/analyzer))
|
||||
user.changeNext_move(CLICK_CD_MELEE)
|
||||
user << "<b>The analyzer beeps once, then reports:</b><br>"
|
||||
user << 'sound/machines/ping.ogg'
|
||||
chemeffectreport(user)
|
||||
typereport(user)
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/effect/blob/proc/chemeffectreport(mob/user)
|
||||
if(overmind)
|
||||
user << "<b>Material: <font color=\"[overmind.blob_reagent_datum.color]\">[overmind.blob_reagent_datum.name]</font><span class='notice'>.</span></b>"
|
||||
user << "<b>Material Effects:</b> <span class='notice'>[overmind.blob_reagent_datum.analyzerdescdamage]</span>"
|
||||
user << "<b>Material Properties:</b> <span class='notice'>[overmind.blob_reagent_datum.analyzerdesceffect]</span><br>"
|
||||
else
|
||||
user << "<b>No Material Detected!</b><br>"
|
||||
|
||||
/obj/effect/blob/proc/typereport(mob/user)
|
||||
user << "<b>Blob Type:</b> <span class='notice'>[uppertext(initial(name))]</span>"
|
||||
user << "<b>Health:</b> <span class='notice'>[health]/[maxhealth]</span>"
|
||||
user << "<b>Effects:</b> <span class='notice'>[scannerreport()]</span>"
|
||||
|
||||
/obj/effect/blob/attacked_by(obj/item/I, mob/living/user)
|
||||
user.changeNext_move(CLICK_CD_MELEE)
|
||||
user.do_attack_animation(src)
|
||||
playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1)
|
||||
visible_message("<span class='danger'>[user] has attacked the [src.name] with \the [I]!</span>")
|
||||
if(I.damtype == BURN)
|
||||
playsound(src.loc, 'sound/items/Welder.ogg', 100, 1)
|
||||
take_damage(I.force, I.damtype, user)
|
||||
|
||||
/obj/effect/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
|
||||
M.changeNext_move(CLICK_CD_MELEE)
|
||||
M.do_attack_animation(src)
|
||||
playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1)
|
||||
visible_message("<span class='danger'>\The [M] has attacked the [src.name]!</span>")
|
||||
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
|
||||
take_damage(damage, M.melee_damage_type, M)
|
||||
return
|
||||
|
||||
/obj/effect/blob/attack_alien(mob/living/carbon/alien/humanoid/M)
|
||||
M.changeNext_move(CLICK_CD_MELEE)
|
||||
M.do_attack_animation(src)
|
||||
playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1)
|
||||
visible_message("<span class='danger'>[M] has slashed the [src.name]!</span>")
|
||||
var/damage = rand(15, 30)
|
||||
take_damage(damage, BRUTE, M)
|
||||
return
|
||||
|
||||
/obj/effect/blob/proc/take_damage(damage, damage_type, cause = null, overmind_reagent_trigger = 1)
|
||||
switch(damage_type) //blobs only take brute and burn damage
|
||||
if(BRUTE)
|
||||
damage = max(damage * brute_resist, 0)
|
||||
if(BURN)
|
||||
damage = max(damage * fire_resist, 0)
|
||||
if(CLONE) //this is basically a marker for 'don't modify the damage'
|
||||
|
||||
else
|
||||
damage = 0
|
||||
if(overmind && overmind_reagent_trigger)
|
||||
damage = overmind.blob_reagent_datum.damage_reaction(src, health, damage, damage_type, cause) //pass the blob, its health before damage, the damage being done, the type of damage being done, and the cause.
|
||||
health -= damage
|
||||
update_icon()
|
||||
check_health(cause)
|
||||
|
||||
/obj/effect/blob/proc/change_to(type, controller)
|
||||
if(!ispath(type))
|
||||
throw EXCEPTION("change_to(): invalid type for blob")
|
||||
return
|
||||
var/obj/effect/blob/B = new type(src.loc)
|
||||
if(controller)
|
||||
B.overmind = controller
|
||||
B.creation_action()
|
||||
B.update_icon()
|
||||
qdel(src)
|
||||
return B
|
||||
|
||||
/obj/effect/blob/examine(mob/user)
|
||||
..()
|
||||
var/datum/atom_hud/hud_to_check = huds[DATA_HUD_MEDICAL_ADVANCED]
|
||||
if(user.research_scanner || (user in hud_to_check.hudusers))
|
||||
user << "<b>Your HUD displays an extensive report...</b><br>"
|
||||
chemeffectreport(user)
|
||||
typereport(user)
|
||||
else
|
||||
user << "It seems to be made of [get_chem_name()]."
|
||||
|
||||
/obj/effect/blob/proc/scannerreport()
|
||||
return "A generic blob. Looks like someone forgot to override this proc, adminhelp this."
|
||||
|
||||
/obj/effect/blob/proc/get_chem_name()
|
||||
if(overmind)
|
||||
return overmind.blob_reagent_datum.name
|
||||
return "an unknown variant"
|
||||
|
||||
/obj/effect/blob/normal
|
||||
name = "normal blob"
|
||||
icon_state = "blob"
|
||||
luminosity = 0
|
||||
health = 21
|
||||
maxhealth = 25
|
||||
health_regen = 1
|
||||
brute_resist = 0.25
|
||||
|
||||
/obj/effect/blob/normal/scannerreport()
|
||||
if(health <= 10)
|
||||
return "Currently weak to brute damage."
|
||||
return "N/A"
|
||||
|
||||
/obj/effect/blob/normal/update_icon()
|
||||
..()
|
||||
if(health <= 10)
|
||||
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
|
||||
@@ -0,0 +1,85 @@
|
||||
// cellular emporium
|
||||
// The place where changelings go to buy their biological weaponry.
|
||||
|
||||
/datum/cellular_emporium
|
||||
var/name = "cellular emporium"
|
||||
var/datum/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 = 0, datum/tgui/master_ui = null, datum/ui_state/state = 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 subtypesof(/obj/effect/proc_holder/changeling))
|
||||
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["required_absorptions"] = 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.lingRespec(usr)
|
||||
if("evolve")
|
||||
var/sting_name = params["name"]
|
||||
changeling.purchasePower(usr, sting_name)
|
||||
|
||||
/datum/action/innate/cellular_emporium
|
||||
name = "Cellular Emporium"
|
||||
button_icon_state = "cellular_emporium"
|
||||
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)
|
||||
@@ -0,0 +1,523 @@
|
||||
#define LING_FAKEDEATH_TIME 400 //40 seconds
|
||||
#define LING_DEAD_GENETICDAMAGE_HEAL_CAP 50 //The lowest value of geneticdamage handle_changeling() can take it to while dead.
|
||||
#define LING_ABSORB_RECENT_SPEECH 8 //The amount of recent spoken lines to gain on absorbing a mob
|
||||
|
||||
var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega")
|
||||
var/list/slots = list("head", "wear_mask", "back", "wear_suit", "w_uniform", "shoes", "belt", "gloves", "glasses", "ears", "wear_id", "s_store")
|
||||
var/list/slot2slot = list("head" = slot_head, "wear_mask" = slot_wear_mask, "back" = slot_back, "wear_suit" = slot_wear_suit, "w_uniform" = slot_w_uniform, "shoes" = slot_shoes, "belt" = slot_belt, "gloves" = slot_gloves, "glasses" = slot_glasses, "ears" = slot_ears, "wear_id" = slot_wear_id, "s_store" = slot_s_store)
|
||||
var/list/slot2type = list("head" = /obj/item/clothing/head/changeling, "wear_mask" = /obj/item/clothing/mask/changeling, "back" = /obj/item/changeling, "wear_suit" = /obj/item/clothing/suit/changeling, "w_uniform" = /obj/item/clothing/under/changeling, "shoes" = /obj/item/clothing/shoes/changeling, "belt" = /obj/item/changeling, "gloves" = /obj/item/clothing/gloves/changeling, "glasses" = /obj/item/clothing/glasses/changeling, "ears" = /obj/item/changeling, "wear_id" = /obj/item/changeling, "s_store" = /obj/item/changeling)
|
||||
|
||||
|
||||
/datum/game_mode
|
||||
var/list/datum/mind/changelings = list()
|
||||
|
||||
|
||||
/datum/game_mode/changeling
|
||||
name = "changeling"
|
||||
config_tag = "changeling"
|
||||
antag_flag = ROLE_CHANGELING
|
||||
restricted_jobs = list("AI", "Cyborg")
|
||||
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain")
|
||||
required_players = 15
|
||||
required_enemies = 1
|
||||
recommended_enemies = 4
|
||||
reroll_friendly = 1
|
||||
|
||||
|
||||
var/const/prob_int_murder_target = 50 // intercept names the assassination target half the time
|
||||
var/const/prob_right_murder_target_l = 25 // lower bound on probability of naming right assassination target
|
||||
var/const/prob_right_murder_target_h = 50 // upper bound on probability of naimg the right assassination target
|
||||
|
||||
var/const/prob_int_item = 50 // intercept names the theft target half the time
|
||||
var/const/prob_right_item_l = 25 // lower bound on probability of naming right theft target
|
||||
var/const/prob_right_item_h = 50 // upper bound on probability of naming the right theft target
|
||||
|
||||
var/const/prob_int_sab_target = 50 // intercept names the sabotage target half the time
|
||||
var/const/prob_right_sab_target_l = 25 // lower bound on probability of naming right sabotage target
|
||||
var/const/prob_right_sab_target_h = 50 // upper bound on probability of naming right sabotage target
|
||||
|
||||
var/const/prob_right_killer_l = 25 //lower bound on probability of naming the right operative
|
||||
var/const/prob_right_killer_h = 50 //upper bound on probability of naming the right operative
|
||||
var/const/prob_right_objective_l = 25 //lower bound on probability of determining the objective correctly
|
||||
var/const/prob_right_objective_h = 50 //upper bound on probability of determining the objective correctly
|
||||
|
||||
var/const/changeling_amount = 4 //hard limit on changelings if scaling is turned off
|
||||
|
||||
var/changeling_team_objective_type = null //If this is not null, we hand our this objective to all lings
|
||||
|
||||
/datum/game_mode/changeling/announce()
|
||||
world << "<b>The current game mode is - Changeling!</b>"
|
||||
world << "<b>There are alien changelings on the station. Do not let the changelings succeed!</b>"
|
||||
|
||||
/datum/game_mode/changeling/pre_setup()
|
||||
|
||||
if(config.protect_roles_from_antagonist)
|
||||
restricted_jobs += protected_jobs
|
||||
|
||||
if(config.protect_assistant_from_antagonist)
|
||||
restricted_jobs += "Assistant"
|
||||
|
||||
var/num_changelings = 1
|
||||
|
||||
if(config.changeling_scaling_coeff)
|
||||
num_changelings = max(1, min( round(num_players()/(config.changeling_scaling_coeff*2))+2, round(num_players()/config.changeling_scaling_coeff) ))
|
||||
else
|
||||
num_changelings = max(1, min(num_players(), changeling_amount))
|
||||
|
||||
if(antag_candidates.len>0)
|
||||
for(var/i = 0, i < num_changelings, i++)
|
||||
if(!antag_candidates.len) break
|
||||
var/datum/mind/changeling = pick(antag_candidates)
|
||||
antag_candidates -= changeling
|
||||
changelings += changeling
|
||||
changeling.restricted_roles = restricted_jobs
|
||||
modePlayer += changelings
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
|
||||
/datum/game_mode/changeling/post_setup()
|
||||
|
||||
//Decide if it's ok for the lings to have a team objective
|
||||
//And then set it up to be handed out in forge_changeling_objectives
|
||||
var/list/team_objectives = subtypesof(/datum/objective/changeling_team_objective)
|
||||
var/list/possible_team_objectives = list()
|
||||
for(var/T in team_objectives)
|
||||
var/datum/objective/changeling_team_objective/CTO = T
|
||||
|
||||
if(changelings.len >= initial(CTO.min_lings))
|
||||
possible_team_objectives += T
|
||||
|
||||
if(possible_team_objectives.len && prob(20*changelings.len))
|
||||
changeling_team_objective_type = pick(possible_team_objectives)
|
||||
|
||||
for(var/datum/mind/changeling in changelings)
|
||||
log_game("[changeling.key] (ckey) has been selected as a changeling")
|
||||
changeling.current.make_changeling()
|
||||
changeling.special_role = "Changeling"
|
||||
forge_changeling_objectives(changeling)
|
||||
greet_changeling(changeling)
|
||||
ticker.mode.update_changeling_icons_added(changeling)
|
||||
..()
|
||||
|
||||
/datum/game_mode/changeling/make_antag_chance(mob/living/carbon/human/character) //Assigns changeling to latejoiners
|
||||
var/changelingcap = min( round(joined_player_list.len/(config.changeling_scaling_coeff*2))+2, round(joined_player_list.len/config.changeling_scaling_coeff) )
|
||||
if(ticker.mode.changelings.len >= changelingcap) //Caps number of latejoin antagonists
|
||||
return
|
||||
if(ticker.mode.changelings.len <= (changelingcap - 2) || prob(100 - (config.changeling_scaling_coeff*2)))
|
||||
if(ROLE_CHANGELING in character.client.prefs.be_special)
|
||||
if(!jobban_isbanned(character, ROLE_CHANGELING) && !jobban_isbanned(character, "Syndicate"))
|
||||
if(age_check(character.client))
|
||||
if(!(character.job in restricted_jobs))
|
||||
character.mind.make_Changling()
|
||||
|
||||
/datum/game_mode/proc/forge_changeling_objectives(datum/mind/changeling, var/team_mode = 0)
|
||||
//OBJECTIVES - random traitor objectives. Unique objectives "steal brain" and "identity theft".
|
||||
//No escape alone because changelings aren't suited for it and it'd probably just lead to rampant robusting
|
||||
//If it seems like they'd be able to do it in play, add a 10% chance to have to escape alone
|
||||
|
||||
var/escape_objective_possible = TRUE
|
||||
|
||||
//if there's a team objective, check if it's compatible with escape objectives
|
||||
for(var/datum/objective/changeling_team_objective/CTO in changeling.objectives)
|
||||
if(!CTO.escape_objective_compatible)
|
||||
escape_objective_possible = FALSE
|
||||
break
|
||||
|
||||
var/datum/objective/absorb/absorb_objective = new
|
||||
absorb_objective.owner = changeling
|
||||
absorb_objective.gen_amount_goal(6, 8)
|
||||
changeling.objectives += absorb_objective
|
||||
|
||||
if(prob(60))
|
||||
var/datum/objective/steal/steal_objective = new
|
||||
steal_objective.owner = changeling
|
||||
steal_objective.find_target()
|
||||
changeling.objectives += steal_objective
|
||||
|
||||
var/list/active_ais = active_ais()
|
||||
if(active_ais.len && prob(100/joined_player_list.len))
|
||||
var/datum/objective/destroy/destroy_objective = new
|
||||
destroy_objective.owner = changeling
|
||||
destroy_objective.find_target()
|
||||
changeling.objectives += destroy_objective
|
||||
else
|
||||
if(prob(70))
|
||||
var/datum/objective/assassinate/kill_objective = new
|
||||
kill_objective.owner = changeling
|
||||
if(team_mode) //No backstabbing while in a team
|
||||
kill_objective.find_target_by_role(role = "Changeling", role_type = 1, invert = 1)
|
||||
else
|
||||
kill_objective.find_target()
|
||||
changeling.objectives += kill_objective
|
||||
else
|
||||
var/datum/objective/maroon/maroon_objective = new
|
||||
maroon_objective.owner = changeling
|
||||
if(team_mode)
|
||||
maroon_objective.find_target_by_role(role = "Changeling", role_type = 1, invert = 1)
|
||||
else
|
||||
maroon_objective.find_target()
|
||||
changeling.objectives += maroon_objective
|
||||
|
||||
if (!(locate(/datum/objective/escape) in changeling.objectives) && escape_objective_possible)
|
||||
var/datum/objective/escape/escape_with_identity/identity_theft = new
|
||||
identity_theft.owner = changeling
|
||||
identity_theft.target = maroon_objective.target
|
||||
identity_theft.update_explanation_text()
|
||||
changeling.objectives += identity_theft
|
||||
escape_objective_possible = FALSE
|
||||
|
||||
if (!(locate(/datum/objective/escape) in changeling.objectives) && escape_objective_possible)
|
||||
if(prob(50))
|
||||
var/datum/objective/escape/escape_objective = new
|
||||
escape_objective.owner = changeling
|
||||
changeling.objectives += escape_objective
|
||||
else
|
||||
var/datum/objective/escape/escape_with_identity/identity_theft = new
|
||||
identity_theft.owner = changeling
|
||||
if(team_mode)
|
||||
identity_theft.find_target_by_role(role = "Changeling", role_type = 1, invert = 1)
|
||||
else
|
||||
identity_theft.find_target()
|
||||
changeling.objectives += identity_theft
|
||||
escape_objective_possible = FALSE
|
||||
|
||||
|
||||
|
||||
/datum/game_mode/changeling/forge_changeling_objectives(datum/mind/changeling)
|
||||
if(changeling_team_objective_type)
|
||||
var/datum/objective/changeling_team_objective/team_objective = new changeling_team_objective_type
|
||||
team_objective.owner = changeling
|
||||
changeling.objectives += team_objective
|
||||
|
||||
..(changeling,1)
|
||||
else
|
||||
..(changeling,0)
|
||||
|
||||
|
||||
/datum/game_mode/proc/greet_changeling(datum/mind/changeling, you_are=1)
|
||||
if (you_are)
|
||||
changeling.current << "<span class='boldannounce'>You are [changeling.changeling.changelingID], a changeling! You have absorbed and taken the form of a human.</span>"
|
||||
changeling.current << "<span class='boldannounce'>Use say \":g message\" to communicate with your fellow changelings.</span>"
|
||||
changeling.current << "<b>You must complete the following tasks:</b>"
|
||||
|
||||
if (changeling.current.mind)
|
||||
var/mob/living/carbon/human/H = changeling.current
|
||||
if(H.mind.assigned_role == "Clown")
|
||||
H << "You have evolved beyond your clownish nature, allowing you to wield weapons without harming yourself."
|
||||
H.dna.remove_mutation(CLOWNMUT)
|
||||
|
||||
var/obj_count = 1
|
||||
for(var/datum/objective/objective in changeling.objectives)
|
||||
changeling.current << "<b>Objective #[obj_count]</b>: [objective.explanation_text]"
|
||||
obj_count++
|
||||
return
|
||||
|
||||
/*/datum/game_mode/changeling/check_finished()
|
||||
var/changelings_alive = 0
|
||||
for(var/datum/mind/changeling in changelings)
|
||||
if(!istype(changeling.current,/mob/living/carbon))
|
||||
continue
|
||||
if(changeling.current.stat==2)
|
||||
continue
|
||||
changelings_alive++
|
||||
|
||||
if (changelings_alive)
|
||||
changelingdeath = 0
|
||||
return ..()
|
||||
else
|
||||
if (!changelingdeath)
|
||||
changelingdeathtime = world.time
|
||||
changelingdeath = 1
|
||||
if(world.time-changelingdeathtime > TIME_TO_GET_REVIVED)
|
||||
return 1
|
||||
else
|
||||
return ..()
|
||||
return 0*/
|
||||
|
||||
/datum/game_mode/proc/auto_declare_completion_changeling()
|
||||
if(changelings.len)
|
||||
var/text = "<br><font size=3><b>The changelings were:</b></font>"
|
||||
for(var/datum/mind/changeling in changelings)
|
||||
var/changelingwin = 1
|
||||
if(!changeling.current)
|
||||
changelingwin = 0
|
||||
|
||||
text += printplayer(changeling)
|
||||
|
||||
//Removed sanity if(changeling) because we -want- a runtime to inform us that the changelings list is incorrect and needs to be fixed.
|
||||
text += "<br><b>Changeling ID:</b> [changeling.changeling.changelingID]."
|
||||
text += "<br><b>Genomes Extracted:</b> [changeling.changeling.absorbedcount]"
|
||||
|
||||
if(changeling.objectives.len)
|
||||
var/count = 1
|
||||
for(var/datum/objective/objective in changeling.objectives)
|
||||
if(objective.check_completion())
|
||||
text += "<br><b>Objective #[count]</b>: [objective.explanation_text] <font color='green'><b>Success!</b></font>"
|
||||
feedback_add_details("changeling_objective","[objective.type]|SUCCESS")
|
||||
else
|
||||
text += "<br><b>Objective #[count]</b>: [objective.explanation_text] <span class='danger'>Fail.</span>"
|
||||
feedback_add_details("changeling_objective","[objective.type]|FAIL")
|
||||
changelingwin = 0
|
||||
count++
|
||||
|
||||
if(changelingwin)
|
||||
text += "<br><font color='green'><b>The changeling was successful!</b></font>"
|
||||
feedback_add_details("changeling_success","SUCCESS")
|
||||
else
|
||||
text += "<br><span class='boldannounce'>The changeling has failed.</span>"
|
||||
feedback_add_details("changeling_success","FAIL")
|
||||
text += "<br>"
|
||||
|
||||
world << text
|
||||
|
||||
|
||||
return 1
|
||||
|
||||
/datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind)
|
||||
var/list/stored_profiles = list() //list of datum/changelingprofile
|
||||
var/datum/changelingprofile/first_prof = null
|
||||
//var/list/absorbed_dna = list()
|
||||
//var/list/protected_dna = list() //dna that is not lost when capacity is otherwise full
|
||||
var/dna_max = 6 //How many extra DNA strands the changeling can store for transformation.
|
||||
var/absorbedcount = 0
|
||||
var/chem_charges = 20
|
||||
var/chem_storage = 75
|
||||
var/chem_recharge_rate = 1
|
||||
var/chem_recharge_slowdown = 0
|
||||
var/sting_range = 2
|
||||
var/changelingID = "Changeling"
|
||||
var/geneticdamage = 0
|
||||
var/isabsorbing = 0
|
||||
var/islinking = 0
|
||||
var/geneticpoints = 10
|
||||
var/purchasedpowers = list()
|
||||
var/mimicing = ""
|
||||
var/canrespec = 0
|
||||
var/changeling_speak = 0
|
||||
var/datum/dna/chosen_dna
|
||||
var/obj/effect/proc_holder/changeling/sting/chosen_sting
|
||||
var/datum/cellular_emporium/cellular_emporium
|
||||
var/datum/action/innate/cellular_emporium/emporium_action
|
||||
|
||||
/datum/changeling/New(var/gender=FEMALE)
|
||||
..()
|
||||
var/honorific
|
||||
if(gender == FEMALE)
|
||||
honorific = "Ms."
|
||||
else
|
||||
honorific = "Mr."
|
||||
if(possible_changeling_IDs.len)
|
||||
changelingID = pick(possible_changeling_IDs)
|
||||
possible_changeling_IDs -= changelingID
|
||||
changelingID = "[honorific] [changelingID]"
|
||||
else
|
||||
changelingID = "[honorific] [rand(1,999)]"
|
||||
|
||||
cellular_emporium = new(src)
|
||||
emporium_action = new(cellular_emporium)
|
||||
|
||||
/datum/changeling/Destroy()
|
||||
qdel(cellular_emporium)
|
||||
cellular_emporium = null
|
||||
. = ..()
|
||||
|
||||
/datum/changeling/proc/regenerate(var/mob/living/carbon/the_ling)
|
||||
if(istype(the_ling))
|
||||
emporium_action.Grant(the_ling)
|
||||
if(the_ling.stat == DEAD)
|
||||
chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), (chem_storage*0.5))
|
||||
geneticdamage = max(LING_DEAD_GENETICDAMAGE_HEAL_CAP,geneticdamage-1)
|
||||
else //not dead? no chem/geneticdamage caps.
|
||||
chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), chem_storage)
|
||||
geneticdamage = max(0, geneticdamage-1)
|
||||
|
||||
|
||||
/datum/changeling/proc/get_dna(dna_owner)
|
||||
for(var/datum/changelingprofile/prof in stored_profiles)
|
||||
if(dna_owner == prof.name)
|
||||
return prof
|
||||
|
||||
/datum/changeling/proc/has_dna(datum/dna/tDNA)
|
||||
for(var/datum/changelingprofile/prof in stored_profiles)
|
||||
if(tDNA.is_same_as(prof.dna))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/changeling/proc/can_absorb_dna(mob/living/carbon/user, mob/living/carbon/human/target, var/verbose=1)
|
||||
if(stored_profiles.len)
|
||||
var/datum/changelingprofile/prof = stored_profiles[1]
|
||||
if(prof.dna == user.dna && stored_profiles.len >= dna_max)//If our current DNA is the stalest, we gotta ditch it.
|
||||
if(verbose)
|
||||
user << "<span class='warning'>We have reached our capacity to store genetic information! We must transform before absorbing more.</span>"
|
||||
return
|
||||
if(!target)
|
||||
return
|
||||
if((target.disabilities & NOCLONE) || (target.disabilities & HUSK))
|
||||
if(verbose)
|
||||
user << "<span class='warning'>DNA of [target] is ruined beyond usability!</span>"
|
||||
return
|
||||
if(!ishuman(target))//Absorbing monkeys is entirely possible, but it can cause issues with transforming. That's what lesser form is for anyway!
|
||||
if(verbose)
|
||||
user << "<span class='warning'>We could gain no benefit from absorbing a lesser creature.</span>"
|
||||
return
|
||||
if(has_dna(target.dna))
|
||||
if(verbose)
|
||||
user << "<span class='warning'>We already have this DNA in storage!</span>"
|
||||
return
|
||||
if(!target.has_dna())
|
||||
if(verbose)
|
||||
user << "<span class='warning'>[target] is not compatible with our biology.</span>"
|
||||
return
|
||||
return 1
|
||||
|
||||
/datum/changeling/proc/create_profile(mob/living/carbon/human/H, mob/living/carbon/human/user, protect = 0)
|
||||
var/datum/changelingprofile/prof = new
|
||||
|
||||
H.dna.real_name = H.real_name //Set this again, just to be sure that it's properly set.
|
||||
var/datum/dna/new_dna = new H.dna.type
|
||||
H.dna.copy_dna(new_dna)
|
||||
prof.dna = new_dna
|
||||
prof.name = H.real_name
|
||||
prof.protected = protect
|
||||
|
||||
prof.underwear = H.underwear
|
||||
prof.undershirt = H.undershirt
|
||||
prof.socks = H.socks
|
||||
|
||||
var/list/slots = list("head", "wear_mask", "back", "wear_suit", "w_uniform", "shoes", "belt", "gloves", "glasses", "ears", "wear_id", "s_store")
|
||||
for(var/slot in slots)
|
||||
if(slot in H.vars)
|
||||
var/obj/item/I = H.vars[slot]
|
||||
if(!I)
|
||||
continue
|
||||
prof.name_list[slot] = I.name
|
||||
prof.appearance_list[slot] = I.appearance
|
||||
prof.flags_cover_list[slot] = I.flags_cover
|
||||
prof.item_color_list[slot] = I.item_color
|
||||
prof.item_state_list[slot] = I.item_state
|
||||
prof.exists_list[slot] = 1
|
||||
else
|
||||
continue
|
||||
|
||||
return prof
|
||||
|
||||
/datum/changeling/proc/add_profile(datum/changelingprofile/prof)
|
||||
if(stored_profiles.len > dna_max)
|
||||
if(!push_out_profile())
|
||||
return
|
||||
|
||||
stored_profiles += prof
|
||||
absorbedcount++
|
||||
|
||||
/datum/changeling/proc/add_new_profile(mob/living/carbon/human/H, mob/living/carbon/human/user, protect = 0)
|
||||
var/datum/changelingprofile/prof = create_profile(H, protect)
|
||||
add_profile(prof)
|
||||
return prof
|
||||
|
||||
/datum/changeling/proc/remove_profile(mob/living/carbon/human/H, force = 0)
|
||||
for(var/datum/changelingprofile/prof in stored_profiles)
|
||||
if(H.real_name == prof.name)
|
||||
if(prof.protected && !force)
|
||||
continue
|
||||
stored_profiles -= prof
|
||||
qdel(prof)
|
||||
|
||||
/datum/changeling/proc/get_profile_to_remove()
|
||||
for(var/datum/changelingprofile/prof in stored_profiles)
|
||||
if(!prof.protected)
|
||||
return prof
|
||||
|
||||
/datum/changeling/proc/push_out_profile()
|
||||
var/datum/changelingprofile/removeprofile = get_profile_to_remove()
|
||||
if(removeprofile)
|
||||
stored_profiles -= removeprofile
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/proc/changeling_transform(mob/living/carbon/human/user, datum/changelingprofile/chosen_prof)
|
||||
var/datum/dna/chosen_dna = chosen_prof.dna
|
||||
user.real_name = chosen_prof.name
|
||||
user.underwear = chosen_prof.underwear
|
||||
user.undershirt = chosen_prof.undershirt
|
||||
user.socks = chosen_prof.socks
|
||||
|
||||
chosen_dna.transfer_identity(user, 1)
|
||||
user.updateappearance(mutcolor_update=1)
|
||||
user.update_body()
|
||||
user.domutcheck()
|
||||
|
||||
//vars hackery. not pretty, but better than the alternative.
|
||||
for(var/slot in slots)
|
||||
if(istype(user.vars[slot], slot2type[slot]) && !(chosen_prof.exists_list[slot])) //remove unnecessary flesh items
|
||||
qdel(user.vars[slot])
|
||||
continue
|
||||
|
||||
if((user.vars[slot] && !istype(user.vars[slot], slot2type[slot])) || !(chosen_prof.exists_list[slot]))
|
||||
continue
|
||||
|
||||
var/obj/item/C
|
||||
var/equip = 0
|
||||
if(!user.vars[slot])
|
||||
var/thetype = slot2type[slot]
|
||||
equip = 1
|
||||
C = new thetype(user)
|
||||
|
||||
else if(istype(user.vars[slot], slot2type[slot]))
|
||||
C = user.vars[slot]
|
||||
|
||||
C.appearance = chosen_prof.appearance_list[slot]
|
||||
C.name = chosen_prof.name_list[slot]
|
||||
C.flags_cover = chosen_prof.flags_cover_list[slot]
|
||||
C.item_color = chosen_prof.item_color_list[slot]
|
||||
C.item_state = chosen_prof.item_state_list[slot]
|
||||
if(equip)
|
||||
user.equip_to_slot_or_del(C, slot2slot[slot])
|
||||
|
||||
user.regenerate_icons()
|
||||
|
||||
/datum/changelingprofile
|
||||
var/name = "a bug"
|
||||
|
||||
var/protected = 0
|
||||
|
||||
var/datum/dna/dna = null
|
||||
var/list/name_list = list() //associative list of slotname = itemname
|
||||
var/list/appearance_list = list()
|
||||
var/list/flags_cover_list = list()
|
||||
var/list/exists_list = list()
|
||||
var/list/item_color_list = list()
|
||||
var/list/item_state_list = list()
|
||||
|
||||
var/underwear
|
||||
var/undershirt
|
||||
var/socks
|
||||
|
||||
/datum/changelingprofile/Destroy()
|
||||
qdel(dna)
|
||||
. = ..()
|
||||
|
||||
/datum/changelingprofile/proc/copy_profile(datum/changelingprofile/newprofile)
|
||||
newprofile.name = name
|
||||
newprofile.protected = protected
|
||||
newprofile.dna = new dna.type
|
||||
dna.copy_dna(newprofile.dna)
|
||||
newprofile.name_list = name_list.Copy()
|
||||
newprofile.appearance_list = appearance_list.Copy()
|
||||
newprofile.flags_cover_list = flags_cover_list.Copy()
|
||||
newprofile.exists_list = exists_list.Copy()
|
||||
newprofile.item_color_list = item_color_list.Copy()
|
||||
newprofile.item_state_list = item_state_list.Copy()
|
||||
newprofile.underwear = underwear
|
||||
newprofile.undershirt = undershirt
|
||||
newprofile.socks = socks
|
||||
|
||||
/datum/game_mode/proc/update_changeling_icons_added(datum/mind/changling_mind)
|
||||
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_CHANGELING]
|
||||
hud.join_hud(changling_mind.current)
|
||||
set_antag_hud(changling_mind.current, "changling")
|
||||
|
||||
/datum/game_mode/proc/update_changeling_icons_removed(datum/mind/changling_mind)
|
||||
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_CHANGELING]
|
||||
hud.leave_hud(changling_mind.current)
|
||||
set_antag_hud(changling_mind.current, null)
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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/genetic_damage = 0 // genetic damage caused by using the sting. Nothing to do with cloneloss.
|
||||
var/max_genetic_damage = 100 // hard counter for spamming abilities. Not used/balanced much yet.
|
||||
var/always_keep = 0 // important for abilities like regenerate that screw you if you lose them.
|
||||
|
||||
|
||||
/obj/effect/proc_holder/changeling/proc/on_purchase(mob/user)
|
||||
return
|
||||
|
||||
/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.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/changeling/c = user.mind.changeling
|
||||
if(sting_action(user, target))
|
||||
sting_feedback(user, target)
|
||||
take_chemical_cost(c)
|
||||
|
||||
/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
|
||||
|
||||
/obj/effect/proc_holder/changeling/proc/take_chemical_cost(datum/changeling/changeling)
|
||||
changeling.chem_charges -= chemical_cost
|
||||
changeling.geneticdamage += genetic_damage
|
||||
|
||||
//Fairly important to remember to return 1 on success >.<
|
||||
/obj/effect/proc_holder/changeling/proc/can_sting(mob/user, mob/target)
|
||||
if(!ishuman(user) && !ismonkey(user)) //typecast everything from mob to carbon from this point onwards
|
||||
return 0
|
||||
if(req_human && !ishuman(user))
|
||||
user << "<span class='warning'>We cannot do that in this form!</span>"
|
||||
return 0
|
||||
var/datum/changeling/c = user.mind.changeling
|
||||
if(c.chem_charges<chemical_cost)
|
||||
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)
|
||||
user << "<span class='warning'>We require at least [req_dna] sample\s of compatible DNA.</span>"
|
||||
return 0
|
||||
if(req_stat < user.stat)
|
||||
user << "<span class='warning'>We are incapacitated.</span>"
|
||||
return 0
|
||||
if((user.status_flags & FAKEDEATH) && name != "Regenerate")
|
||||
user << "<span class='warning'>We are incapacitated.</span>"
|
||||
return 0
|
||||
if(c.geneticdamage > max_genetic_damage)
|
||||
user << "<span class='warning'>Our genomes are still reassembling. We need time to recover first.</span>"
|
||||
return 0
|
||||
return 1
|
||||
|
||||
//used in /mob/Stat()
|
||||
/obj/effect/proc_holder/changeling/proc/can_be_used_by(mob/user)
|
||||
if(!ishuman(user) && !ismonkey(user))
|
||||
return 0
|
||||
if(req_human && !ishuman(user))
|
||||
return 0
|
||||
return 1
|
||||
@@ -0,0 +1,104 @@
|
||||
/datum/changeling/proc/purchasePower(mob/living/carbon/user, sting_name)
|
||||
|
||||
var/obj/effect/proc_holder/changeling/thepower = null
|
||||
|
||||
for(var/path in subtypesof(/obj/effect/proc_holder/changeling))
|
||||
var/obj/effect/proc_holder/changeling/S = new path()
|
||||
if(S.name == sting_name)
|
||||
thepower = S
|
||||
|
||||
if(thepower == null)
|
||||
user << "This is awkward. Changeling power purchase failed, please report this bug to a coder!"
|
||||
return
|
||||
|
||||
if(absorbedcount < thepower.req_dna)
|
||||
user << "We lack the energy to evolve this ability!"
|
||||
return
|
||||
|
||||
if(has_sting(thepower))
|
||||
user << "We have already evolved this ability!"
|
||||
return
|
||||
|
||||
if(thepower.dna_cost < 0)
|
||||
user << "We cannot evolve this ability."
|
||||
return
|
||||
|
||||
if(geneticpoints < thepower.dna_cost)
|
||||
user << "We have reached our capacity for abilities."
|
||||
return
|
||||
|
||||
if(user.status_flags & FAKEDEATH)//To avoid potential exploits by buying new powers while in stasis, which clears your verblist.
|
||||
user << "We lack the energy to evolve new abilities right now."
|
||||
return
|
||||
|
||||
geneticpoints -= thepower.dna_cost
|
||||
purchasedpowers += thepower
|
||||
thepower.on_purchase(user)
|
||||
|
||||
//Reselect powers
|
||||
/datum/changeling/proc/lingRespec(mob/user)
|
||||
if(!ishuman(user))
|
||||
user << "<span class='danger'>We can't remove our evolutions in this form!</span>"
|
||||
return
|
||||
if(canrespec)
|
||||
user << "<span class='notice'>We have removed our evolutions from this form, and are now ready to readapt.</span>"
|
||||
user.remove_changeling_powers(1)
|
||||
canrespec = 0
|
||||
user.make_changeling()
|
||||
return 1
|
||||
else
|
||||
user << "<span class='danger'>You lack the power to readapt your evolutions!</span>"
|
||||
return 0
|
||||
|
||||
/mob/proc/make_changeling()
|
||||
if(!mind)
|
||||
return
|
||||
if(!ishuman(src) && !ismonkey(src))
|
||||
return
|
||||
if(!mind.changeling)
|
||||
mind.changeling = new /datum/changeling(gender)
|
||||
if(mind.changeling.purchasedpowers)
|
||||
remove_changeling_powers(1)
|
||||
// purchase free powers.
|
||||
for(var/path in subtypesof(/obj/effect/proc_holder/changeling))
|
||||
var/obj/effect/proc_holder/changeling/S = new path()
|
||||
if(!S.dna_cost)
|
||||
if(!mind.changeling.has_sting(S))
|
||||
mind.changeling.purchasedpowers+=S
|
||||
S.on_purchase(src)
|
||||
|
||||
var/mob/living/carbon/C = src //only carbons have dna now, so we have to typecaste
|
||||
if(ishuman(C))
|
||||
var/datum/changelingprofile/prof = mind.changeling.add_new_profile(C, src)
|
||||
mind.changeling.first_prof = prof
|
||||
return 1
|
||||
|
||||
/datum/changeling/proc/reset()
|
||||
chosen_sting = null
|
||||
geneticpoints = initial(geneticpoints)
|
||||
sting_range = initial(sting_range)
|
||||
chem_storage = initial(chem_storage)
|
||||
chem_recharge_rate = initial(chem_recharge_rate)
|
||||
chem_charges = min(chem_charges, chem_storage)
|
||||
chem_recharge_slowdown = initial(chem_recharge_slowdown)
|
||||
mimicing = ""
|
||||
|
||||
/mob/proc/remove_changeling_powers(keep_free_powers=0)
|
||||
if(ishuman(src) || ismonkey(src))
|
||||
if(mind && mind.changeling)
|
||||
mind.changeling.changeling_speak = 0
|
||||
mind.changeling.reset()
|
||||
for(var/obj/effect/proc_holder/changeling/p in mind.changeling.purchasedpowers)
|
||||
if((p.dna_cost == 0 && keep_free_powers) || p.always_keep)
|
||||
continue
|
||||
mind.changeling.purchasedpowers -= p
|
||||
p.on_refund(src)
|
||||
if(hud_used)
|
||||
hud_used.lingstingdisplay.icon_state = null
|
||||
hud_used.lingstingdisplay.invisibility = INVISIBILITY_ABSTRACT
|
||||
|
||||
/datum/changeling/proc/has_sting(obj/effect/proc_holder/changeling/power)
|
||||
for(var/obj/effect/proc_holder/changeling/P in purchasedpowers)
|
||||
if(initial(power.name) == P.name)
|
||||
return 1
|
||||
return 0
|
||||
@@ -0,0 +1,162 @@
|
||||
/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
|
||||
max_genetic_damage = 100
|
||||
|
||||
/obj/effect/proc_holder/changeling/absorbDNA/can_sting(mob/living/carbon/user)
|
||||
if(!..())
|
||||
return
|
||||
|
||||
var/datum/changeling/changeling = user.mind.changeling
|
||||
if(changeling.isabsorbing)
|
||||
user << "<span class='warning'>We are already absorbing!</span>"
|
||||
return
|
||||
|
||||
if(!user.pulling || !iscarbon(user.pulling))
|
||||
user << "<span class='warning'>We must be grabbing a creature to absorb them!</span>"
|
||||
return
|
||||
if(user.grab_state <= GRAB_NECK)
|
||||
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(user,target)
|
||||
|
||||
|
||||
|
||||
/obj/effect/proc_holder/changeling/absorbDNA/sting_action(mob/user)
|
||||
var/datum/changeling/changeling = user.mind.changeling
|
||||
var/mob/living/carbon/human/target = user.pulling
|
||||
changeling.isabsorbing = 1
|
||||
for(var/stage = 1, stage<=3, stage++)
|
||||
switch(stage)
|
||||
if(1)
|
||||
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>")
|
||||
target << "<span class='userdanger'>You feel a sharp stabbing pain!</span>"
|
||||
target.take_overall_damage(40)
|
||||
|
||||
feedback_add_details("changeling_powers","A[stage]")
|
||||
if(!do_mob(user, target, 150))
|
||||
user << "<span class='warning'>Our absorption of [target] has been interrupted!</span>"
|
||||
changeling.isabsorbing = 0
|
||||
return
|
||||
|
||||
user.visible_message("<span class='danger'>[user] sucks the fluids from [target]!</span>", "<span class='notice'>We have absorbed [target].</span>")
|
||||
target << "<span class='userdanger'>You are absorbed by the changeling!</span>"
|
||||
|
||||
if(!changeling.has_dna(target.dna))
|
||||
changeling.add_new_profile(target, user)
|
||||
|
||||
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
|
||||
|
||||
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()
|
||||
|
||||
if(target.say_log.len > LING_ABSORB_RECENT_SPEECH)
|
||||
recent_speech = target.say_log.Copy(target.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 target.say_log)
|
||||
if(recent_speech.len >= LING_ABSORB_RECENT_SPEECH)
|
||||
break
|
||||
recent_speech += spoken_memory
|
||||
|
||||
if(recent_speech.len)
|
||||
user.mind.store_memory("<B>Some of [target]'s speech patterns, we should study these to better impersonate them!</B>")
|
||||
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)
|
||||
user.mind.store_memory("\"[spoken_memory]\"")
|
||||
user << "<span class='notice'>\"[spoken_memory]\"</span>"
|
||||
user.mind.store_memory("<B>We have no more knowledge of [target]'s speech patterns.</B>")
|
||||
user << "<span class='boldnotice'>We have no more knowledge of [target]'s speech patterns.</span>"
|
||||
|
||||
if(target.mind.changeling)//If the target was a changeling, suck out their extra juice and objective points!
|
||||
changeling.chem_charges += min(target.mind.changeling.chem_charges, changeling.chem_storage)
|
||||
changeling.absorbedcount += (target.mind.changeling.absorbedcount)
|
||||
|
||||
target.mind.changeling.stored_profiles.len = 1
|
||||
target.mind.changeling.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 1
|
||||
|
||||
|
||||
|
||||
//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)
|
||||
|
||||
|
||||
|
||||
|
||||
/obj/effect/proc_holder/changeling/swap_form
|
||||
name = "Swap Forms"
|
||||
desc = "We force ourselves into the body of another form, pushing their consciousness into the form we left behind."
|
||||
helptext = "We will bring all our abilities with us, but we will lose our old form DNA in exchange for the new one. The process will seem suspicious to any observers."
|
||||
chemical_cost = 40
|
||||
dna_cost = 1
|
||||
req_human = 1 //Monkeys can't grab
|
||||
|
||||
/obj/effect/proc_holder/changeling/swap_form/can_sting(mob/living/carbon/user)
|
||||
if(!..())
|
||||
return
|
||||
if(!user.pulling || !iscarbon(user.pulling) || user.grab_state < GRAB_AGGRESSIVE)
|
||||
user << "<span class='warning'>We must have an aggressive grab on creature to do this!</span>"
|
||||
return
|
||||
var/mob/living/carbon/target = user.pulling
|
||||
if((target.disabilities & NOCLONE) || (target.disabilities & HUSK))
|
||||
user << "<span class='warning'>DNA of [target] is ruined beyond usability!</span>"
|
||||
return
|
||||
if(!ishuman(target))
|
||||
user << "<span class='warning'>[target] is not compatible with this ability.</span>"
|
||||
return
|
||||
return 1
|
||||
|
||||
|
||||
/obj/effect/proc_holder/changeling/swap_form/sting_action(mob/living/carbon/user)
|
||||
var/mob/living/carbon/target = user.pulling
|
||||
var/datum/changeling/changeling = user.mind.changeling
|
||||
|
||||
user << "<span class='notice'>We tighen our grip. We must hold still....</span>"
|
||||
target.do_jitter_animation(500)
|
||||
user.do_jitter_animation(500)
|
||||
|
||||
if(!do_mob(user,target,20))
|
||||
user << "<span class='warning'>The body swap has been interrupted!</span>"
|
||||
return
|
||||
|
||||
target << "<span class='userdanger'>[user] tightens their grip as a painful sensation invades your body.</span>"
|
||||
|
||||
if(!changeling.has_dna(target.dna))
|
||||
changeling.add_new_profile(target, user)
|
||||
changeling.remove_profile(user)
|
||||
|
||||
var/mob/dead/observer/ghost = target.ghostize(0)
|
||||
user.mind.transfer_to(target)
|
||||
if(ghost)
|
||||
ghost.mind.transfer_to(user)
|
||||
if(ghost.key)
|
||||
user.key = ghost.key
|
||||
|
||||
user.Paralyse(2)
|
||||
target << "<span class='warning'>Our genes cry out as we swap our [user] form for [target].</span>"
|
||||
@@ -0,0 +1,22 @@
|
||||
/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)
|
||||
user << "<span class='notice'>Energy rushes through us.[user.lying ? " We arise." : ""]</span>"
|
||||
user.SetSleeping(0)
|
||||
user.SetParalysis(0)
|
||||
user.SetStunned(0)
|
||||
user.SetWeakened(0)
|
||||
user.reagents.add_reagent("changelingAdrenaline", 10)
|
||||
user.reagents.add_reagent("changelingAdrenaline2", 2) //For a really quick burst of speed
|
||||
user.adjustStaminaLoss(-75)
|
||||
feedback_add_details("changeling_powers","UNS")
|
||||
return 1
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
//Augmented Eyesight: Gives you thermal and night vision - bye bye, flashlights. 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
|
||||
var/active = 0 //Whether or not vision is enhanced
|
||||
|
||||
/obj/effect/proc_holder/changeling/augmented_eyesight/sting_action(mob/living/carbon/human/user)
|
||||
if(!istype(user))
|
||||
return
|
||||
if(user.getorgan(/obj/item/organ/cyberimp/eyes/thermals/ling))
|
||||
user << "<span class='notice'>Our eyes are protected from flashes.</span>"
|
||||
var/obj/item/organ/cyberimp/eyes/O = new /obj/item/organ/cyberimp/eyes/shield/ling()
|
||||
O.Insert(user)
|
||||
|
||||
else
|
||||
var/obj/item/organ/cyberimp/eyes/O = new /obj/item/organ/cyberimp/eyes/thermals/ling()
|
||||
O.Insert(user)
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
/obj/effect/proc_holder/changeling/augmented_eyesight/on_refund(mob/user)
|
||||
var/obj/item/organ/cyberimp/eyes/O = user.getorganslot("eye_ling")
|
||||
if(O)
|
||||
O.Remove(user)
|
||||
qdel(O)
|
||||
|
||||
|
||||
/obj/item/organ/cyberimp/eyes/shield/ling
|
||||
name = "protective membranes"
|
||||
desc = "These variable transparency organic membranes will protect you from welders and flashes and heal your eye damage."
|
||||
icon_state = "ling_eyeshield"
|
||||
eye_color = null
|
||||
implant_overlay = null
|
||||
slot = "eye_ling"
|
||||
status = ORGAN_ORGANIC
|
||||
|
||||
/obj/item/organ/cyberimp/eyes/shield/ling/on_life()
|
||||
..()
|
||||
if(owner.eye_blind>1 || (owner.eye_blind && owner.stat !=UNCONSCIOUS) || owner.eye_damage || owner.eye_blurry || (owner.disabilities & NEARSIGHT))
|
||||
owner.reagents.add_reagent("oculine", 1)
|
||||
|
||||
/obj/item/organ/cyberimp/eyes/shield/ling/prepare_eat()
|
||||
var/obj/S = ..()
|
||||
S.reagents.add_reagent("oculine", 15)
|
||||
return S
|
||||
|
||||
|
||||
/obj/item/organ/cyberimp/eyes/thermals/ling
|
||||
name = "heat receptors"
|
||||
desc = "These heat receptors dramatically increases eyes light sensing ability."
|
||||
icon_state = "ling_thermal"
|
||||
eye_color = null
|
||||
implant_overlay = null
|
||||
slot = "eye_ling"
|
||||
status = ORGAN_ORGANIC
|
||||
aug_message = "You feel a minute twitch in our eyes, and darkness creeps away."
|
||||
|
||||
/obj/item/organ/cyberimp/eyes/thermals/ling/emp_act(severity)
|
||||
return
|
||||
|
||||
/obj/item/organ/cyberimp/eyes/thermals/ling/Insert(mob/living/carbon/M, special = 0)
|
||||
..()
|
||||
if(ishuman(owner))
|
||||
var/mob/living/carbon/human/H = owner
|
||||
H.weakeyes = 1
|
||||
|
||||
/obj/item/organ/cyberimp/eyes/thermals/ling/Remove(mob/living/carbon/M, special = 0)
|
||||
if(ishuman(owner))
|
||||
var/mob/living/carbon/human/H = owner
|
||||
H.weakeyes = 0
|
||||
..()
|
||||
@@ -0,0 +1,82 @@
|
||||
/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
|
||||
genetic_damage = 10
|
||||
max_genetic_damage = 0
|
||||
|
||||
|
||||
/obj/effect/proc_holder/changeling/biodegrade/sting_action(mob/living/carbon/human/user)
|
||||
var/used = FALSE // only one form of shackles removed per use
|
||||
if(!user.restrained() && !istype(user.loc, /obj/structure/closet))
|
||||
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 \his [O]!</span>", \
|
||||
"<span class='warning'>We vomit acidic ooze onto our \
|
||||
restraints!</span>")
|
||||
|
||||
addtimer(src, "dissolve_handcuffs", 30, FALSE, user, O)
|
||||
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 \his [S]!</span>", \
|
||||
"<span class='warning'>We vomit acidic ooze onto our straight \
|
||||
jacket!</span>")
|
||||
addtimer(src, "dissolve_straightjacket", 30, FALSE, user, S)
|
||||
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>")
|
||||
user << "<span class='warning'>We vomit acidic goop onto the \
|
||||
interior of [C]!</span>"
|
||||
addtimer(src, "open_closet", 70, FALSE, user, C)
|
||||
used = TRUE
|
||||
|
||||
if(used)
|
||||
feedback_add_details("changeling_powers","BD")
|
||||
return 1
|
||||
|
||||
/obj/effect/proc_holder/changeling/biodegrade/proc/dissolve_handcuffs(mob/living/carbon/human/user, obj/O)
|
||||
if(O && user.handcuffed == O)
|
||||
user.unEquip(O)
|
||||
O.visible_message("<span class='warning'>[O] dissolves into a \
|
||||
puddle of sizzling goop.</span>")
|
||||
O.loc = get_turf(user)
|
||||
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.unEquip(S)
|
||||
S.visible_message("<span class='warning'>[S] dissolves into a puddle of sizzling goop.</span>")
|
||||
S.loc = get_turf(user)
|
||||
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>")
|
||||
C.welded = FALSE
|
||||
C.locked = FALSE
|
||||
C.broken = TRUE
|
||||
C.open()
|
||||
user << "<span class='warning'>We open the container restraining \
|
||||
us!</span>"
|
||||
@@ -0,0 +1,30 @@
|
||||
/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
|
||||
genetic_damage = 10
|
||||
max_genetic_damage = 50
|
||||
|
||||
|
||||
/obj/effect/proc_holder/changeling/chameleon_skin/sting_action(mob/user)
|
||||
var/mob/living/carbon/human/H = user //SHOULD always be human, because req_human = 1
|
||||
if(!istype(H)) // req_human could be done in can_sting stuff.
|
||||
return
|
||||
var/datum/mutation/human/HM = mutations_list[CHAMELEON]
|
||||
if(HM in H.dna.mutations)
|
||||
HM.force_lose(H)
|
||||
else
|
||||
HM.force_give(H)
|
||||
|
||||
feedback_add_details("changeling_powers","CS")
|
||||
return 1
|
||||
|
||||
/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 = mutations_list[CHAMELEON]
|
||||
if(HM in C.dna.mutations)
|
||||
HM.force_lose(C)
|
||||
@@ -0,0 +1,25 @@
|
||||
/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)
|
||||
user << "<span class='notice'>We return to normal.</span>"
|
||||
user.digitalinvis = 0
|
||||
user.digitalcamo = 0
|
||||
else
|
||||
user << "<span class='notice'>We distort our form to hide from the AI</span>"
|
||||
user.digitalcamo = 1
|
||||
user.digitalinvis = 1
|
||||
|
||||
|
||||
feedback_add_details("changeling_powers","CAM")
|
||||
return 1
|
||||
|
||||
/obj/effect/proc_holder/changeling/digitalcamo/on_refund(mob/user)
|
||||
user.digitalcamo = 0
|
||||
user.digitalinvis = 0
|
||||
@@ -0,0 +1,39 @@
|
||||
/obj/effect/proc_holder/changeling/fakedeath
|
||||
name = "Regenerative 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
|
||||
max_genetic_damage = 100
|
||||
|
||||
|
||||
//Fake our own death and fully heal. You will appear to be dead but regenerate fully after a short delay.
|
||||
/obj/effect/proc_holder/changeling/fakedeath/sting_action(mob/living/user)
|
||||
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(src, "ready_to_regenerate", LING_FAKEDEATH_TIME, FALSE, user)
|
||||
|
||||
feedback_add_details("changeling_powers","FD")
|
||||
return 1
|
||||
|
||||
/obj/effect/proc_holder/changeling/fakedeath/proc/ready_to_regenerate(mob/user)
|
||||
if(user && user.mind && user.mind.changeling && user.mind.changeling.purchasedpowers)
|
||||
user << "<span class='notice'>We are ready to regenerate.</span>"
|
||||
user.mind.changeling.purchasedpowers += new /obj/effect/proc_holder/changeling/revive(null)
|
||||
|
||||
/obj/effect/proc_holder/changeling/fakedeath/can_sting(mob/user)
|
||||
if(user.status_flags & FAKEDEATH)
|
||||
user << "<span class='warning'>We are already regenerating.</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 ..()
|
||||
@@ -0,0 +1,62 @@
|
||||
/obj/effect/proc_holder/changeling/fleshmend
|
||||
name = "Fleshmend"
|
||||
desc = "Our flesh rapidly regenerates, healing our wounds, and growing \
|
||||
back missing limbs. Effectiveness decreases with quick, repeated use."
|
||||
helptext = "Heals a moderate amount of damage over a short period of \
|
||||
time. Can be used while unconscious. Will alert nearby crew if \
|
||||
any limbs are regenerated."
|
||||
chemical_cost = 25
|
||||
dna_cost = 2
|
||||
req_stat = UNCONSCIOUS
|
||||
var/recent_uses = 1 //The factor of which the healing should be divided by
|
||||
var/healing_ticks = 10
|
||||
// The ideal total healing amount,
|
||||
// divided by healing_ticks to get heal/tick
|
||||
var/total_healing = 100
|
||||
|
||||
/obj/effect/proc_holder/changeling/fleshmend/New()
|
||||
..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/effect/proc_holder/changeling/fleshmend/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
..()
|
||||
|
||||
/obj/effect/proc_holder/changeling/fleshmend/process()
|
||||
if(recent_uses > 1)
|
||||
recent_uses = max(1, recent_uses - (1 / healing_ticks))
|
||||
|
||||
//Starts healing you every second for 10 seconds.
|
||||
//Can be used whilst unconscious.
|
||||
/obj/effect/proc_holder/changeling/fleshmend/sting_action(mob/living/user)
|
||||
user << "<span class='notice'>We begin to heal rapidly.</span>"
|
||||
if(recent_uses > 1)
|
||||
user << "<span class='warning'>Our healing's effectiveness is reduced \
|
||||
by quick repeated use!</span>"
|
||||
spawn(0)
|
||||
recent_uses++
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
H.restore_blood()
|
||||
H.remove_all_embedded_objects()
|
||||
var/list/missing = H.get_missing_limbs()
|
||||
if(missing.len)
|
||||
playsound(user, 'sound/magic/Demon_consume.ogg', 50, 1)
|
||||
H.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>")
|
||||
H.emote("scream")
|
||||
H.regenerate_limbs(1)
|
||||
|
||||
// The healing itself - doesn't heal toxin damage
|
||||
// (that's anatomic panacea) and the effectiveness decreases with
|
||||
// each use in a short timespan
|
||||
for(var/i in 1 to healing_ticks)
|
||||
if(user)
|
||||
var/healpertick = -(total_healing / healing_ticks)
|
||||
user.adjustBruteLoss(healpertick / recent_uses, 0)
|
||||
user.adjustOxyLoss(healpertick / recent_uses, 0)
|
||||
user.adjustFireLoss(healpertick / recent_uses, 0)
|
||||
user.updatehealth()
|
||||
sleep(10)
|
||||
|
||||
feedback_add_details("changeling_powers","RR")
|
||||
return 1
|
||||
@@ -0,0 +1,38 @@
|
||||
/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)
|
||||
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))
|
||||
H << "<span class='userdanger'>You are blinded by a shower of blood!</span>"
|
||||
H.Stun(1)
|
||||
H.blur_eyes(20)
|
||||
H.adjust_eye_damage(5)
|
||||
H.confused += 3
|
||||
for(var/mob/living/silicon/S in range(2,user))
|
||||
S << "<span class='userdanger'>Your sensors are disabled by a shower of blood!</span>"
|
||||
S.Weaken(3)
|
||||
var/turf = get_turf(user)
|
||||
spawn(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.loc = crab
|
||||
crab.origin = M
|
||||
if(crab.origin)
|
||||
crab.origin.active = 1
|
||||
crab.origin.transfer_to(crab)
|
||||
crab << "<span class='warning'>You burst out of the remains of your former body in a shower of gore!</span>"
|
||||
user.gib()
|
||||
feedback_add_details("changeling_powers","LR")
|
||||
return 1
|
||||
@@ -0,0 +1,96 @@
|
||||
//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(var/mob/user)
|
||||
..()
|
||||
var/datum/changeling/changeling=user.mind.changeling
|
||||
changeling.changeling_speak = 1
|
||||
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
|
||||
return
|
||||
|
||||
// HIVE MIND UPLOAD/DOWNLOAD DNA
|
||||
var/list/datum/dna/hivemind_bank = list()
|
||||
|
||||
/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/changeling/changeling = user.mind.changeling
|
||||
var/list/names = list()
|
||||
for(var/datum/changelingprofile/prof in changeling.stored_profiles)
|
||||
if(!(prof in hivemind_bank))
|
||||
names += prof.name
|
||||
|
||||
if(names.len <= 0)
|
||||
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)
|
||||
hivemind_bank += uploaded_dna
|
||||
user << "<span class='notice'>We channel the DNA of [chosen_name] to the air.</span>"
|
||||
feedback_add_details("changeling_powers","HU")
|
||||
return 1
|
||||
|
||||
/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/changeling/changeling = user.mind.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.
|
||||
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/changeling/changeling = user.mind.changeling
|
||||
var/list/names = list()
|
||||
for(var/datum/changelingprofile/prof in hivemind_bank)
|
||||
if(!(prof in changeling.stored_profiles))
|
||||
names[prof.name] = prof
|
||||
|
||||
if(names.len <= 0)
|
||||
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)
|
||||
user << "<span class='notice'>We absorb the DNA of [S] from the air.</span>"
|
||||
feedback_add_details("changeling_powers","HD")
|
||||
return 1
|
||||
@@ -0,0 +1,34 @@
|
||||
/obj/effect/proc_holder/changeling/humanform
|
||||
name = "Human form"
|
||||
desc = "We change into a human."
|
||||
chemical_cost = 5
|
||||
genetic_damage = 3
|
||||
req_dna = 1
|
||||
max_genetic_damage = 3
|
||||
|
||||
|
||||
//Transform into a human.
|
||||
/obj/effect/proc_holder/changeling/humanform/sting_action(mob/living/carbon/user)
|
||||
var/datum/changeling/changeling = user.mind.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
|
||||
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)
|
||||
feedback_add_details("changeling_powers","LFT")
|
||||
return 1
|
||||
@@ -0,0 +1,18 @@
|
||||
/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
|
||||
genetic_damage = 3
|
||||
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
|
||||
user << "<span class='warning'>Our genes cry out!</span>"
|
||||
|
||||
user.monkeyize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSE)
|
||||
|
||||
feedback_add_details("changeling_powers","LF")
|
||||
return 1
|
||||
@@ -0,0 +1,69 @@
|
||||
/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
|
||||
max_genetic_damage = 100
|
||||
|
||||
/obj/effect/proc_holder/changeling/linglink/can_sting(mob/living/carbon/user)
|
||||
if(!..())
|
||||
return
|
||||
var/datum/changeling/changeling = user.mind.changeling
|
||||
if(changeling.islinking)
|
||||
user << "<span class='warning'>We have already formed a link with the victim!</span>"
|
||||
return
|
||||
if(!user.pulling)
|
||||
user << "<span class='warning'>We must be tightly grabbing a creature to link with them!</span>"
|
||||
return
|
||||
if(!iscarbon(user.pulling))
|
||||
user << "<span class='warning'>We cannot link with this creature!</span>"
|
||||
return
|
||||
var/mob/living/carbon/target = user.pulling
|
||||
if(!target.mind)
|
||||
user << "<span class='warning'>The victim has no mind to link to!</span>"
|
||||
return
|
||||
if(target.stat == DEAD)
|
||||
user << "<span class='warning'>The victim is dead, you cannot link to a dead mind!</span>"
|
||||
return
|
||||
if(target.mind.changeling)
|
||||
user << "<span class='warning'>The victim is already a part of the hivemind!</span>"
|
||||
return
|
||||
if(user.grab_state <= GRAB_NECK)
|
||||
user << "<span class='warning'>We must have a tighter grip to link with this creature!</span>"
|
||||
return
|
||||
return changeling.can_absorb_dna(user,target)
|
||||
|
||||
/obj/effect/proc_holder/changeling/linglink/sting_action(mob/user)
|
||||
var/datum/changeling/changeling = user.mind.changeling
|
||||
var/mob/living/carbon/human/target = user.pulling
|
||||
changeling.islinking = 1
|
||||
for(var/i in 1 to 3)
|
||||
switch(i)
|
||||
if(1)
|
||||
user << "<span class='notice'>This creature is compatible. We must hold still...</span>"
|
||||
if(2)
|
||||
user << "<span class='notice'>We stealthily stab [target] with a minor proboscis...</span>"
|
||||
target << "<span class='userdanger'>You experience a stabbing sensation and your ears begin to ring...</span>"
|
||||
if(3)
|
||||
user << "<span class='notice'>You mold the [target]'s mind like clay, they can now speak in the hivemind!</span>"
|
||||
target << "<span class='userdanger'>A migraine throbs behind your eyes, you hear yourself screaming - but your mouth has not opened!</span>"
|
||||
for(var/mob/M in mob_list)
|
||||
if(M.lingcheck() == 2)
|
||||
M << "<i><font color=#800080>We can sense a foreign presence in the hivemind...</font></i>"
|
||||
target.mind.linglink = 1
|
||||
target.say(":g AAAAARRRRGGGGGHHHHH!!")
|
||||
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)
|
||||
feedback_add_details("changeling_powers","A [i]")
|
||||
if(!do_mob(user, target, 20))
|
||||
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
|
||||
user << "<span class='notice'>You cannot sustain the connection any longer, your victim fades from the hivemind</span>"
|
||||
target << "<span class='userdanger'>The link cannot be sustained any longer, your connection to the hivemind has faded!</span>"
|
||||
@@ -0,0 +1,28 @@
|
||||
/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/changeling/changeling=user.mind.changeling
|
||||
if(changeling.mimicing)
|
||||
changeling.mimicing = ""
|
||||
changeling.chem_recharge_slowdown -= 0.5
|
||||
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
|
||||
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>"
|
||||
user << "<span class='notice'>Use this power again to return to our original voice and return chemical production to normal levels.</span>"
|
||||
|
||||
feedback_add_details("changeling_powers","MV")
|
||||
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
Changeling Mutations! ~By Miauw (ALL OF IT :V)
|
||||
Contains:
|
||||
Arm Blade
|
||||
Space Suit
|
||||
Shield
|
||||
Armor
|
||||
*/
|
||||
|
||||
|
||||
//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
|
||||
genetic_damage = 1000
|
||||
|
||||
var/weapon_type
|
||||
var/weapon_name_simple
|
||||
|
||||
/obj/effect/proc_holder/changeling/weapon/try_to_sting(mob/user, mob/target)
|
||||
if(check_weapon(user, user.r_hand, 1))
|
||||
return
|
||||
if(check_weapon(user, user.l_hand, 0))
|
||||
return
|
||||
..(user, target)
|
||||
|
||||
/obj/effect/proc_holder/changeling/weapon/proc/check_weapon(mob/user, obj/item/hand_item, right_hand=1)
|
||||
if(istype(hand_item, weapon_type))
|
||||
playsound(user, 'sound/effects/blobattack.ogg', 30, 1)
|
||||
qdel(hand_item)
|
||||
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>")
|
||||
if(right_hand)
|
||||
user.update_inv_r_hand()
|
||||
else
|
||||
user.update_inv_l_hand()
|
||||
return 1
|
||||
|
||||
/obj/effect/proc_holder/changeling/weapon/sting_action(mob/living/user)
|
||||
if(!user.drop_item())
|
||||
user << "<span class='warning'>The [user.get_active_hand()] is stuck to your hand, you cannot grow a [weapon_name_simple] over it!</span>"
|
||||
return
|
||||
var/limb_regen = 0
|
||||
if(user.hand) //we regen the arm before changing it into the weapon
|
||||
limb_regen = user.regenerate_limb("l_arm", 1)
|
||||
else
|
||||
limb_regen = user.regenerate_limb("r_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)
|
||||
user.put_in_hands(W)
|
||||
playsound(user, 'sound/effects/blobattack.ogg', 30, 1)
|
||||
return W
|
||||
|
||||
/obj/effect/proc_holder/changeling/weapon/on_refund(mob/user)
|
||||
check_weapon(user, user.r_hand, 1)
|
||||
check_weapon(user, user.l_hand, 0)
|
||||
|
||||
//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
|
||||
genetic_damage = 1000
|
||||
|
||||
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/changeling/changeling = user.mind.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][genetic_damage > 0 ? ", temporarily weakening our genomes." : "."]</span>", "<span class='italics'>You hear the organic matter ripping and tearing!</span>")
|
||||
H.unEquip(H.head, TRUE) //The qdel on dropped() takes care of it
|
||||
H.unEquip(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.geneticdamage += genetic_damage //Casting off a space suit leaves you weak for a few seconds.
|
||||
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))
|
||||
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))
|
||||
user << "\the [user.head] is stuck on your head, you cannot grow a [helmet_name_simple] over it!"
|
||||
return
|
||||
|
||||
user.unEquip(user.head)
|
||||
user.unEquip(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/changeling/changeling = user.mind.changeling
|
||||
changeling.chem_recharge_slowdown += recharge_slowdown
|
||||
return 1
|
||||
|
||||
|
||||
//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
|
||||
genetic_damage = 10
|
||||
req_human = 1
|
||||
max_genetic_damage = 20
|
||||
weapon_type = /obj/item/weapon/melee/arm_blade
|
||||
weapon_name_simple = "blade"
|
||||
|
||||
/obj/item/weapon/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/weapons.dmi'
|
||||
icon_state = "arm_blade"
|
||||
item_state = "arm_blade"
|
||||
flags = ABSTRACT | NODROP | DROPDEL
|
||||
w_class = 5.0
|
||||
force = 25
|
||||
throwforce = 0 //Just to be on the safe side
|
||||
throw_range = 0
|
||||
throw_speed = 0
|
||||
sharpness = IS_SHARP
|
||||
|
||||
/obj/item/weapon/melee/arm_blade/New(location,silent)
|
||||
..()
|
||||
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>")
|
||||
|
||||
/obj/item/weapon/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.table_destroy()
|
||||
|
||||
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.hasPower())
|
||||
if(A.locked)
|
||||
user << "<span class='warning'>The airlock's bolts prevent it from being forced!</span>"
|
||||
return
|
||||
user << "<span class='warning'>The airlock's motors are resisting, this may take time...</span>"
|
||||
if(do_after(user, 100, target = A))
|
||||
A.open(2)
|
||||
return
|
||||
|
||||
else if(A.locked)
|
||||
user << "<span class='warning'>The airlock's bolts prevent it from being forced!</span>"
|
||||
return
|
||||
|
||||
else
|
||||
//user.say("Heeeeeeeeeerrre's Johnny!")
|
||||
user.visible_message("<span class='warning'>[user] forces the door to open with \his [src]!</span>", "<span class='warning'>We force the door to open.</span>", "<span class='italics'>You hear a metal screeching sound.</span>")
|
||||
A.open(1)
|
||||
|
||||
|
||||
/***************************************\
|
||||
|****************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
|
||||
genetic_damage = 12
|
||||
req_human = 1
|
||||
max_genetic_damage = 20
|
||||
|
||||
weapon_type = /obj/item/weapon/shield/changeling
|
||||
weapon_name_simple = "shield"
|
||||
|
||||
/obj/effect/proc_holder/changeling/weapon/shield/sting_action(mob/user)
|
||||
var/datum/changeling/changeling = user.mind.changeling //So we can read the absorbedcount.
|
||||
if(!changeling)
|
||||
return
|
||||
|
||||
var/obj/item/weapon/shield/changeling/S = ..(user)
|
||||
S.remaining_uses = round(changeling.absorbedcount * 3)
|
||||
return 1
|
||||
|
||||
/obj/item/weapon/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 = ABSTRACT | NODROP | DROPDEL
|
||||
icon = 'icons/obj/weapons.dmi'
|
||||
icon_state = "ling_shield"
|
||||
block_chance = 50
|
||||
|
||||
var/remaining_uses //Set by the changeling ability.
|
||||
|
||||
/obj/item/weapon/shield/changeling/New()
|
||||
..()
|
||||
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/weapon/shield/changeling/hit_reaction()
|
||||
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>")
|
||||
H.unEquip(src, 1)
|
||||
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. Retreating the suit damages our genomes. Cannot be used in lesser form."
|
||||
chemical_cost = 20
|
||||
dna_cost = 2
|
||||
genetic_damage = 8
|
||||
req_human = 1
|
||||
max_genetic_damage = 20
|
||||
|
||||
suit_type = /obj/item/clothing/suit/space/changeling
|
||||
helmet_type = /obj/item/clothing/head/helmet/space/changeling
|
||||
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 = STOPSPRESSUREDMAGE | NODROP | DROPDEL //Not THICKMATERIAL 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/weapon/tank/internals/emergency_oxygen, /obj/item/weapon/tank/internals/oxygen)
|
||||
armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) //No armor at all.
|
||||
|
||||
/obj/item/clothing/suit/space/changeling/New()
|
||||
..()
|
||||
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 = STOPSPRESSUREDMAGE | NODROP | DROPDEL //Again, no THICKMATERIAL.
|
||||
armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0)
|
||||
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. Retreating the armor damages our genomes. Cannot be used in lesser form."
|
||||
chemical_cost = 20
|
||||
dna_cost = 1
|
||||
genetic_damage = 11
|
||||
req_human = 1
|
||||
max_genetic_damage = 20
|
||||
recharge_slowdown = 0.25
|
||||
|
||||
suit_type = /obj/item/clothing/suit/armor/changeling
|
||||
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 = NODROP | DROPDEL
|
||||
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)
|
||||
flags_inv = HIDEJUMPSUIT
|
||||
cold_protection = 0
|
||||
heat_protection = 0
|
||||
|
||||
/obj/item/clothing/suit/armor/changeling/New()
|
||||
..()
|
||||
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 = NODROP | DROPDEL
|
||||
armor = list(melee = 30, bullet = 30, laser = 40, energy = 20, bomb = 10, bio = 4, rad = 0)
|
||||
flags_inv = HIDEEARS|HIDEHAIR|HIDEEYES|HIDEFACIALHAIR|HIDEFACE
|
||||
@@ -0,0 +1,29 @@
|
||||
/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)
|
||||
user << "<span class='notice'>We begin cleansing impurities from our form.</span>"
|
||||
|
||||
var/obj/item/organ/body_egg/egg = user.getorgan(/obj/item/organ/body_egg)
|
||||
if(egg)
|
||||
egg.Remove(user)
|
||||
if(iscarbon(user))
|
||||
var/mob/living/carbon/C = user
|
||||
C.vomit(0)
|
||||
egg.loc = 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/datum/disease/D in user.viruses)
|
||||
D.cure()
|
||||
feedback_add_details("changeling_powers","AP")
|
||||
return 1
|
||||
@@ -0,0 +1,22 @@
|
||||
/obj/effect/proc_holder/changeling/revive
|
||||
name = "Regenerate"
|
||||
desc = "We regenerate, healing all damage from our form."
|
||||
req_stat = DEAD
|
||||
always_keep = 1
|
||||
|
||||
//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)
|
||||
user.regenerate_limbs(0, list("head")) //regenerate all limbs except the head
|
||||
user << "<span class='notice'>We have regenerated.</span>"
|
||||
user.mind.changeling.purchasedpowers -= src
|
||||
feedback_add_details("changeling_powers","CR")
|
||||
return 1
|
||||
|
||||
/obj/effect/proc_holder/changeling/revive/can_be_used_by(mob/user)
|
||||
if((user.stat != DEAD) && !(user.status_flags & FAKEDEATH))
|
||||
user.mind.changeling.purchasedpowers -= src
|
||||
return 0
|
||||
. = ..()
|
||||
@@ -0,0 +1,45 @@
|
||||
/obj/effect/proc_holder/changeling/resonant_shriek
|
||||
name = "Resonant Shriek"
|
||||
desc = "Our lungs and vocal chords 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))
|
||||
if(!M.mind || !M.mind.changeling)
|
||||
M.adjustEarDamage(0,30)
|
||||
M.confused += 25
|
||||
M.Jitter(50)
|
||||
else
|
||||
M << sound('sound/effects/screech.ogg')
|
||||
|
||||
if(issilicon(M))
|
||||
M << sound('sound/weapons/flash.ogg')
|
||||
M.Weaken(rand(5,10))
|
||||
|
||||
for(var/obj/machinery/light/L in range(4, user))
|
||||
L.on = 1
|
||||
L.broken()
|
||||
|
||||
feedback_add_details("changeling_powers","RS")
|
||||
return 1
|
||||
|
||||
/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.broken()
|
||||
empulse(get_turf(user), 2, 5, 1)
|
||||
return 1
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/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)
|
||||
for(var/i=0, i<2, i++)
|
||||
var/obj/effect/spider/spiderling/S = new(user.loc)
|
||||
S.grow_as = /mob/living/simple_animal/hostile/poison/giant_spider/hunter
|
||||
|
||||
feedback_add_details("changeling_powers","SI")
|
||||
return 1
|
||||
@@ -0,0 +1,50 @@
|
||||
//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
|
||||
var/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)
|
||||
user << "<span class='notice'>Our muscles tense and strengthen.</span>"
|
||||
else
|
||||
user.status_flags -= GOTTAGOFAST
|
||||
user << "<span class='notice'>Our muscles relax.</span>"
|
||||
if(stacks >= 10)
|
||||
user << "<span class='danger'>We collapse in exhaustion.</span>"
|
||||
user.Weaken(3)
|
||||
user.emote("gasp")
|
||||
|
||||
while(active)
|
||||
user.status_flags |= GOTTAGOFAST
|
||||
if(user.stat != CONSCIOUS || user.staminaloss >= 90)
|
||||
active = !active
|
||||
user << "<span class='notice'>Our muscles relax without the energy to strengthen them.</span>"
|
||||
user.Weaken(2)
|
||||
user.status_flags -= GOTTAGOFAST
|
||||
break
|
||||
|
||||
stacks++
|
||||
//user.take_organ_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
|
||||
user << "<span class='warning'>Our legs are really starting to hurt...</span>"
|
||||
|
||||
sleep(40)
|
||||
|
||||
while(!active) //Damage stacks decrease fairly rapidly while not in sanic mode
|
||||
if(stacks >= 1)
|
||||
stacks--
|
||||
sleep(20)
|
||||
|
||||
feedback_add_details("changeling_powers","SANIC")
|
||||
return 1
|
||||
@@ -0,0 +1,249 @@
|
||||
/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 || !user.mind.changeling)
|
||||
return
|
||||
if(!(user.mind.changeling.chosen_sting))
|
||||
set_sting(user)
|
||||
else
|
||||
unset_sting(user)
|
||||
return
|
||||
|
||||
/obj/effect/proc_holder/changeling/sting/proc/set_sting(mob/user)
|
||||
user << "<span class='notice'>We prepare our sting, use alt+click or middle mouse button on target to sting them.</span>"
|
||||
user.mind.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)
|
||||
user << "<span class='warning'>We retract our sting, we can't sting anyone for now.</span>"
|
||||
user.mind.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 && mind.changeling && mind.changeling.chosen_sting)
|
||||
src.mind.changeling.chosen_sting.unset_sting(src)
|
||||
|
||||
/obj/effect/proc_holder/changeling/sting/can_sting(mob/user, mob/target)
|
||||
if(!..())
|
||||
return
|
||||
if(!user.mind.changeling.chosen_sting)
|
||||
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, user.mind.changeling.sting_range, simulated_only = 0))
|
||||
return
|
||||
if(target.mind && target.mind.changeling)
|
||||
sting_feedback(user,target)
|
||||
take_chemical_cost(user.mind.changeling)
|
||||
return
|
||||
return 1
|
||||
|
||||
/obj/effect/proc_holder/changeling/sting/sting_feedback(mob/user, mob/target)
|
||||
if(!target)
|
||||
return
|
||||
user << "<span class='notice'>We stealthily sting [target.name].</span>"
|
||||
if(target.mind && target.mind.changeling)
|
||||
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. The effects will be obvious to the victim, and the process will damage our genomes."
|
||||
sting_icon = "sting_transform"
|
||||
chemical_cost = 40
|
||||
dna_cost = 3
|
||||
genetic_damage = 100
|
||||
var/datum/changelingprofile/selected_dna = null
|
||||
|
||||
/obj/effect/proc_holder/changeling/sting/transformation/Click()
|
||||
var/mob/user = usr
|
||||
var/datum/changeling/changeling = user.mind.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.specflags)
|
||||
user << "<span class = 'notice'>That DNA is not compatible with changeling retrovirus!"
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/effect/proc_holder/changeling/sting/transformation/can_sting(mob/user, mob/target)
|
||||
if(!..())
|
||||
return
|
||||
if((target.disabilities & HUSK) || !target.has_dna())
|
||||
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))
|
||||
user << "<span class='notice'>Our genes cry out as we sting [target.name]!</span>"
|
||||
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/C = target
|
||||
if(C.status_flags & CANWEAKEN)
|
||||
C.do_jitter_animation(500)
|
||||
C.take_organ_damage(20, 0) //The process is extremely painful
|
||||
|
||||
target.visible_message("<span class='danger'>[target] begins to violenty convulse!</span>","<span class='userdanger'>You feel a tiny prick and a begin to uncontrollably convulse!</span>")
|
||||
spawn(10)
|
||||
C.real_name = NewDNA.real_name
|
||||
NewDNA.transfer_identity(C, transfer_SE=1)
|
||||
C.updateappearance(mutcolor_update=1)
|
||||
C.domutcheck()
|
||||
feedback_add_details("changeling_powers","TS")
|
||||
return 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
|
||||
genetic_damage = 20
|
||||
max_genetic_damage = 10
|
||||
|
||||
/obj/item/weapon/melee/arm_blade/false
|
||||
desc = "A grotesque mass of flesh that used to be your arm. Although it looks dangerous at first, you can tell it's actually quite dull and useless."
|
||||
force = 5 //Basically as strong as a punch
|
||||
|
||||
/obj/item/weapon/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((target.disabilities & HUSK) || !target.has_dna())
|
||||
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")
|
||||
|
||||
if(!target.drop_item())
|
||||
user << "<span class='warning'>The [target.get_active_hand()] is stuck to their hand, you cannot grow a false armblade over it!</span>"
|
||||
return
|
||||
|
||||
if(ismonkey(target))
|
||||
user << "<span class='notice'>Our genes cry out as we sting [target.name]!</span>"
|
||||
|
||||
var/obj/item/weapon/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(src, "remove_fake", 600, target, blade)
|
||||
|
||||
feedback_add_details("changeling_powers","AS")
|
||||
return 1
|
||||
|
||||
/obj/effect/proc_holder/changeling/sting/false_armblade/proc/remove_fake(mob/target, obj/item/weapon/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_l_hand()
|
||||
target.update_inv_r_hand()
|
||||
|
||||
/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(..())
|
||||
return user.mind.changeling.can_absorb_dna(user, 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")
|
||||
if(!(user.mind.changeling.has_dna(target.dna)))
|
||||
user.mind.changeling.add_new_profile(target, user)
|
||||
feedback_add_details("changeling_powers","ED")
|
||||
return 1
|
||||
|
||||
/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
|
||||
feedback_add_details("changeling_powers","MS")
|
||||
return 1
|
||||
|
||||
/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")
|
||||
target << "<span class='danger'>Your eyes burn horrifically!</span>"
|
||||
target.become_nearsighted()
|
||||
target.blind_eyes(20)
|
||||
target.blur_eyes(40)
|
||||
feedback_add_details("changeling_powers","BS")
|
||||
return 1
|
||||
|
||||
/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(src, "hallucination_time", rand(300,600), target)
|
||||
feedback_add_details("changeling_powers","HS")
|
||||
return 1
|
||||
|
||||
/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)
|
||||
feedback_add_details("changeling_powers","CS")
|
||||
return 1
|
||||
@@ -0,0 +1,72 @@
|
||||
/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
|
||||
max_genetic_damage = 3
|
||||
|
||||
/obj/item/clothing/glasses/changeling
|
||||
name = "flesh"
|
||||
flags = NODROP
|
||||
|
||||
/obj/item/clothing/under/changeling
|
||||
name = "flesh"
|
||||
flags = NODROP
|
||||
|
||||
/obj/item/clothing/suit/changeling
|
||||
name = "flesh"
|
||||
flags = NODROP
|
||||
allowed = list(/obj/item/changeling)
|
||||
|
||||
/obj/item/clothing/head/changeling
|
||||
name = "flesh"
|
||||
flags = NODROP
|
||||
/obj/item/clothing/shoes/changeling
|
||||
name = "flesh"
|
||||
flags = NODROP
|
||||
|
||||
/obj/item/clothing/gloves/changeling
|
||||
name = "flesh"
|
||||
flags = NODROP
|
||||
|
||||
/obj/item/clothing/mask/changeling
|
||||
name = "flesh"
|
||||
flags = NODROP
|
||||
|
||||
/obj/item/changeling
|
||||
name = "flesh"
|
||||
flags = NODROP
|
||||
slot_flags = ALL
|
||||
allowed = list(/obj/item/changeling)
|
||||
|
||||
//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/changeling/changeling = user.mind.changeling
|
||||
var/datum/changelingprofile/chosen_prof = changeling.select_dna("Select the target DNA: ", "Target DNA", user)
|
||||
|
||||
if(!chosen_prof)
|
||||
return
|
||||
|
||||
changeling_transform(user, chosen_prof)
|
||||
|
||||
feedback_add_details("changeling_powers","TR")
|
||||
return 1
|
||||
|
||||
/datum/changeling/proc/select_dna(var/prompt, var/title, var/mob/living/carbon/user)
|
||||
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 slots)
|
||||
if(istype(user.vars[slot], slot2type[slot]))
|
||||
qdel(user.vars[slot])
|
||||
|
||||
var/datum/changelingprofile/prof = get_dna(chosen_name)
|
||||
return prof
|
||||
@@ -0,0 +1,76 @@
|
||||
/datum/game_mode/traitor/changeling
|
||||
name = "traitor+changeling"
|
||||
config_tag = "traitorchan"
|
||||
traitors_possible = 3 //hard limit on traitors if scaling is turned off
|
||||
restricted_jobs = list("AI", "Cyborg")
|
||||
required_players = 0
|
||||
required_enemies = 1 // how many of each type are required
|
||||
recommended_enemies = 3
|
||||
reroll_friendly = 1
|
||||
|
||||
var/list/possible_changelings = list()
|
||||
var/const/changeling_amount = 1 //hard limit on changelings if scaling is turned off
|
||||
|
||||
/datum/game_mode/traitor/changeling/announce()
|
||||
world << "<B>The current game mode is - Traitor+Changeling!</B>"
|
||||
world << "<B>There are alien creatures on the station along with some syndicate operatives out for their own gain! Do not let the changelings or the traitors succeed!</B>"
|
||||
|
||||
/datum/game_mode/traitor/changeling/can_start()
|
||||
if(!..())
|
||||
return 0
|
||||
possible_changelings = get_players_for_role(ROLE_CHANGELING)
|
||||
if(possible_changelings.len < required_enemies)
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/datum/game_mode/traitor/changeling/pre_setup()
|
||||
if(config.protect_roles_from_antagonist)
|
||||
restricted_jobs += protected_jobs
|
||||
|
||||
if(config.protect_assistant_from_antagonist)
|
||||
restricted_jobs += "Assistant"
|
||||
|
||||
var/list/datum/mind/possible_changelings = get_players_for_role(ROLE_CHANGELING)
|
||||
|
||||
var/num_changelings = 1
|
||||
|
||||
if(config.changeling_scaling_coeff)
|
||||
num_changelings = max(1, min( round(num_players()/(config.changeling_scaling_coeff*4))+2, round(num_players()/(config.changeling_scaling_coeff*2)) ))
|
||||
else
|
||||
num_changelings = max(1, min(num_players(), changeling_amount/2))
|
||||
|
||||
if(possible_changelings.len>0)
|
||||
for(var/j = 0, j < num_changelings, j++)
|
||||
if(!possible_changelings.len) break
|
||||
var/datum/mind/changeling = pick(possible_changelings)
|
||||
antag_candidates -= changeling
|
||||
possible_changelings -= changeling
|
||||
changelings += changeling
|
||||
modePlayer += changelings
|
||||
changeling.restricted_roles = restricted_jobs
|
||||
return ..()
|
||||
else
|
||||
return 0
|
||||
|
||||
/datum/game_mode/traitor/changeling/post_setup()
|
||||
for(var/datum/mind/changeling in changelings)
|
||||
changeling.current.make_changeling()
|
||||
changeling.special_role = "Changeling"
|
||||
forge_changeling_objectives(changeling)
|
||||
greet_changeling(changeling)
|
||||
ticker.mode.update_changeling_icons_added(changeling)
|
||||
..()
|
||||
return
|
||||
|
||||
/datum/game_mode/traitor/changeling/make_antag_chance(mob/living/carbon/human/character) //Assigns changeling to latejoiners
|
||||
var/changelingcap = min( round(joined_player_list.len/(config.changeling_scaling_coeff*4))+2, round(joined_player_list.len/(config.changeling_scaling_coeff*2)) )
|
||||
if(ticker.mode.changelings.len >= changelingcap) //Caps number of latejoin antagonists
|
||||
..()
|
||||
return
|
||||
if(ticker.mode.changelings.len <= (changelingcap - 2) || prob(100 / (config.changeling_scaling_coeff * 4)))
|
||||
if(ROLE_CHANGELING in character.client.prefs.be_special)
|
||||
if(!jobban_isbanned(character.client, ROLE_CHANGELING) && !jobban_isbanned(character.client, "Syndicate"))
|
||||
if(age_check(character.client))
|
||||
if(!(character.job in restricted_jobs))
|
||||
character.mind.make_Changling()
|
||||
..()
|
||||
@@ -0,0 +1,45 @@
|
||||
var/global/clockwork_construction_value = 0 //The total value of all structures built by the clockwork cult
|
||||
var/global/clockwork_caches = 0 //How many clockwork caches exist in the world (not each individual)
|
||||
var/global/clockwork_daemons = 0 //How many daemons exist in the world
|
||||
var/global/list/clockwork_generals_invoked = list("nezbere" = FALSE, "sevtug" = FALSE, "nzcrentr" = FALSE, "inath-neq" = FALSE) //How many generals have been recently invoked
|
||||
var/global/list/all_clockwork_objects = list() //All clockwork items, structures, and effects in existence
|
||||
var/global/list/all_clockwork_mobs = list() //All clockwork SERVANTS (not creatures) in existence
|
||||
var/global/list/clockwork_component_cache = list("belligerent_eye" = 0, "vanguard_cogwheel" = 0, "guvax_capacitor" = 0, "replicant_alloy" = 0, "hierophant_ansible" = 0) //The pool of components that caches draw from
|
||||
var/global/ratvar_awakens = FALSE //If Ratvar has been summoned
|
||||
|
||||
#define SCRIPTURE_PERIPHERAL 0 //Scripture tiers; peripherals should never be used
|
||||
#define SCRIPTURE_DRIVER 1
|
||||
#define SCRIPTURE_SCRIPT 2
|
||||
#define SCRIPTURE_APPLICATION 3
|
||||
#define SCRIPTURE_REVENANT 4
|
||||
#define SCRIPTURE_JUDGEMENT 5
|
||||
|
||||
#define SLAB_PRODUCTION_TIME 600 //how long(deciseconds) slabs require to produce a single component; defaults to 1 minute
|
||||
|
||||
#define CACHE_PRODUCTION_TIME 900 //how long(deciseconds) caches require to produce a component; defaults to 1 minute 30 seconds
|
||||
|
||||
#define LOWER_PROB_PER_COMPONENT 10 //how much each component in the cache reduces the weight of getting another of that component type
|
||||
|
||||
#define MAX_COMPONENTS_BEFORE_RAND 10*LOWER_PROB_PER_COMPONENT //the number of each component, times LOWER_PROB_PER_COMPONENT, you need to have before component generation will become random
|
||||
|
||||
#define CLOCKWORK_GENERAL_COOLDOWN 3000 //how long clockwork generals go on cooldown after use, defaults to 5 minutes
|
||||
|
||||
//porselytizer defines
|
||||
#define REPLICANT_ALLOY_UNIT 100 //how much each piece of replicant alloy gives in a clockwork proselytizer
|
||||
|
||||
#define REPLICANT_STANDARD REPLICANT_ALLOY_UNIT*0.2 //how much alloy is in anything else; doesn't matter as much as the following
|
||||
|
||||
#define REPLICANT_FLOOR REPLICANT_ALLOY_UNIT*0.1 //how much alloy is in a clockwork floor, determines the cost of clockwork floor production
|
||||
|
||||
#define REPLICANT_WALL_MINUS_FLOOR REPLICANT_ALLOY_UNIT*0.4 //amount of alloy in a clockwork wall, determines the cost of clockwork wall production
|
||||
|
||||
#define REPLICANT_WALL_TOTAL REPLICANT_WALL_MINUS_FLOOR+REPLICANT_FLOOR //how much alloy is in a clockwork wall and the floor under it
|
||||
|
||||
//Ark defines
|
||||
#define GATEWAY_SUMMON_RATE 2 //the time amount the Gateway to the Celestial Derelict gets each process tick; defaults to 2 per tick
|
||||
|
||||
#define GATEWAY_REEBE_FOUND 100 //when progress is at or above this, the gateway finds reebe and begins drawing power
|
||||
|
||||
#define GATEWAY_RATVAR_COMING 250 //when progress is at or above this, ratvar has entered and is coming through the gateway
|
||||
|
||||
#define GATEWAY_RATVAR_ARRIVAL 300 //when progress is at or above this, game over ratvar's here everybody go home
|
||||
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
|
||||
CLOCKWORK CULT: Based off of the failed pull requests from vgstation
|
||||
|
||||
While Nar-Sie is the oldest and most prominent of the elder gods, there are other forces at work in the universe.
|
||||
Ratvar, the Clockwork Justiciar, a homage to Nar-Sie granted sentience by its own power, is one such other force.
|
||||
Imprisoned within a massive construct known as the Celestial Derelict - or Reebe - an intense hatred of the Blood God festers.
|
||||
Ratvar, unable to act in the mortal plane, seeks to return and forms covenants with mortals in order to bolster his influence.
|
||||
Due to his mechanical nature, Ratvar is also capable of influencing silicon-based lifeforms, unlike Nar-Sie, who can only influence natural life.
|
||||
|
||||
This is a team-based gamemode.
|
||||
|
||||
There are three possible objectives the Enlightened - Ratvar's minions - can have:
|
||||
1. Ensure X amount of Enlightened escape the station through the shuttle or otherwise.
|
||||
2. Convert all silicon lifeforms on the station to Ratvar's cause.
|
||||
3. Summon Ratvar via construction of a Gateway.
|
||||
|
||||
The clockwork version of an arcane tome is the clockwork slab.
|
||||
While it can perform certain actions, it consumes a resource called components.
|
||||
Components, which are fallen fragments of Ratvar's body as he rusts in Reebe, are powerful and have various effects.
|
||||
Game-wise, clockwork slabs will generate components over time, with more powerful components being slower.
|
||||
|
||||
This file's folder contains:
|
||||
__clock_defines.dm: Defined variables
|
||||
clock_cult.dm: Core gamemode files.
|
||||
clock_mobs.dm: Hostile and benign clockwork creatures.
|
||||
clock_items.dm: Items
|
||||
clock_structures.dm: Structures and effects
|
||||
clock_ratvar.dm: The Ark of the Clockwork Justiciar and Ratvar himself. Important enough to have his own file.
|
||||
clock_scripture.dm: Scripture and rites.
|
||||
clock_unsorted.dm: Anything else with no place to be
|
||||
|
||||
*/
|
||||
|
||||
///////////
|
||||
// PROCS //
|
||||
///////////
|
||||
|
||||
/proc/is_servant_of_ratvar(mob/living/M)
|
||||
return M && istype(M) && M.mind && ticker && ticker.mode && (M.mind in ticker.mode.servants_of_ratvar)
|
||||
|
||||
/proc/is_eligible_servant(mob/living/M)
|
||||
if(!istype(M))
|
||||
return 0
|
||||
if(!M.mind)
|
||||
return 0
|
||||
if(M.mind.enslaved_to)
|
||||
return 0
|
||||
if(iscultist(M) || isconstruct(M))
|
||||
return 0
|
||||
if(isbrain(M))
|
||||
return 1
|
||||
if(ishuman(M))
|
||||
if(isloyal(M) || (M.mind.assigned_role in list("Captain", "Chaplain")))
|
||||
return 0
|
||||
return 1
|
||||
if(isguardian(M))
|
||||
var/mob/living/simple_animal/hostile/guardian/G = M
|
||||
if(is_servant_of_ratvar(G.summoner))
|
||||
return 1 //can't convert it unless the owner is converted
|
||||
if(issilicon(M) || isclockmob(M) || istype(M, /mob/living/simple_animal/drone/cogscarab))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/proc/add_servant_of_ratvar(mob/M, silent = FALSE)
|
||||
if(is_servant_of_ratvar(M) || !ticker || !ticker.mode)
|
||||
return 0
|
||||
if(iscarbon(M))
|
||||
if(!silent)
|
||||
M << "<span class='heavy_brass'>Your mind is racing! Your body feels incredibly light! Your world glows a brilliant yellow! All at once it comes to you. Ratvar, the Clockwork \
|
||||
Justiciar, lies in exile, derelict and forgotten in an unseen realm.</span>"
|
||||
else if(issilicon(M))
|
||||
if(!silent)
|
||||
M << "<span class='heavy_brass'>You are unable to compute this truth. Your vision glows a brilliant yellow, and all at once it comes to you. Ratvar, the Clockwork Justiciar, lies in \
|
||||
exile, derelict and forgotten in an unseen realm.</span>"
|
||||
if(!is_eligible_servant(M))
|
||||
if(!M.stat)
|
||||
M.visible_message("<span class='warning'>[M] whirs as it resists an outside influence!</span>")
|
||||
M << "<span class='warning'><b>Corrupt data purged. Resetting cortex chip to factory defaults... complete.</b></span>" //silicons have a custom fail message
|
||||
return 0
|
||||
else if(!silent)
|
||||
M << "<span class='heavy_brass'>Your world glows a brilliant yellow! All at once it comes to you. Ratvar, the Clockwork Justiciar, lies in exile, derelict and forgotten in an unseen realm.</span>"
|
||||
|
||||
if(!is_eligible_servant(M))
|
||||
if(!silent && !M.stat)
|
||||
M.visible_message("<span class='warning'>[M] seems to resist an unseen force!</span>")
|
||||
M << "<span class='warning'><b>And yet, you somehow push it all away.</b></span>"
|
||||
return 0
|
||||
|
||||
if(!silent)
|
||||
M.visible_message("<span class='heavy_brass'>[M]'s eyes glow a blazing yellow!</span>", \
|
||||
"<span class='heavy_brass'>Assist your new companions in their righteous efforts. Your goal is theirs, and theirs yours. You serve the Clockwork Justiciar above all else. Perform his every \
|
||||
whim without hesitation.</span>")
|
||||
ticker.mode.servants_of_ratvar += M.mind
|
||||
ticker.mode.update_servant_icons_added(M.mind)
|
||||
M.mind.special_role = "Servant of Ratvar"
|
||||
M.languages_spoken |= RATVAR
|
||||
M.languages_understood |= RATVAR
|
||||
all_clockwork_mobs += M
|
||||
M.update_action_buttons_icon() //because a few clockcult things are action buttons and we may be wearing/holding them for whatever reason, we need to update buttons
|
||||
if(issilicon(M))
|
||||
var/mob/living/silicon/S = M
|
||||
if(isrobot(S))
|
||||
var/mob/living/silicon/robot/R = S
|
||||
R.UnlinkSelf()
|
||||
R.emagged = 1
|
||||
R << "<span class='warning'><b>You have been desynced from your master AI. In addition, your onboard camera is no longer active and your safeties have been disabled.</b></span>"
|
||||
S.laws = new/datum/ai_laws/ratvar
|
||||
S.laws.associate(S)
|
||||
S.update_icons()
|
||||
S.show_laws()
|
||||
if(istype(ticker.mode, /datum/game_mode/clockwork_cult))
|
||||
var/datum/game_mode/clockwork_cult/C = ticker.mode
|
||||
C.present_tasks(M) //Memorize the objectives
|
||||
return 1
|
||||
|
||||
/proc/remove_servant_of_ratvar(mob/living/M, silent = FALSE)
|
||||
if(!is_servant_of_ratvar(M)) //In this way, is_servant_of_ratvar() checks the existence of ticker and minds
|
||||
return 0
|
||||
if(!silent)
|
||||
M.visible_message("<span class='big'>[M] seems to have remembered their true allegiance!</span>", \
|
||||
"<span class='userdanger'>A cold, cold darkness flows through your mind, extinguishing the Justiciar's light and all of your memories as his servant.</span>")
|
||||
ticker.mode.servants_of_ratvar -= M.mind
|
||||
ticker.mode.update_servant_icons_removed(M.mind)
|
||||
all_clockwork_mobs -= M
|
||||
M.mind.memory = "" //Not sure if there's a better way to do this
|
||||
M.mind.special_role = null
|
||||
M.languages_spoken &= ~RATVAR
|
||||
M.languages_understood &= ~RATVAR
|
||||
M.update_action_buttons_icon() //because a few clockcult things are action buttons and we may be wearing/holding them, we need to update buttons
|
||||
for(var/datum/action/innate/function_call/F in M.actions) //Removes any bound Ratvarian spears
|
||||
qdel(F)
|
||||
if(issilicon(M))
|
||||
var/mob/living/silicon/S = M
|
||||
if(isrobot(S))
|
||||
var/mob/living/silicon/robot/R = S
|
||||
R.emagged = initial(R.emagged)
|
||||
R << "<span class='warning'>Despite your freedom from Ratvar's influence, you are still irreparably damaged and no longer possess certain functions such as AI linking.</span>"
|
||||
S.make_laws()
|
||||
S.update_icons()
|
||||
S.show_laws()
|
||||
return 1
|
||||
|
||||
///////////////
|
||||
// GAME MODE //
|
||||
///////////////
|
||||
|
||||
/datum/game_mode
|
||||
var/list/servants_of_ratvar = list() //The Enlightened servants of Ratvar
|
||||
var/required_escapees = 0 //How many servants need to escape, if applicable
|
||||
var/required_silicon_converts = 0 //How many robotic lifeforms need to be converted, if applicable
|
||||
var/clockwork_objective = "gateway" //The objective that the servants must fulfill
|
||||
var/clockwork_explanation = "Construct a Gateway to the Celestial Derelict and free Ratvar." //The description of the current objective
|
||||
|
||||
/datum/game_mode/clockwork_cult
|
||||
name = "clockwork cult"
|
||||
config_tag = "clockwork_cult"
|
||||
antag_flag = ROLE_SERVANT_OF_RATVAR
|
||||
required_players = 30
|
||||
required_enemies = 2
|
||||
recommended_enemies = 4
|
||||
enemy_minimum_age = 14
|
||||
protected_jobs = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain") //Silicons can eventually be converted
|
||||
restricted_jobs = list("Chaplain", "Captain")
|
||||
var/servants_to_serve = list()
|
||||
|
||||
/datum/game_mode/clockwork_cult/announce()
|
||||
world << "<b>The game mode is: Clockwork Cult!</b>"
|
||||
world << "<b><span class='brass'>Ratvar</span>, the Clockwork Justiciar, has formed a covenant of Enlightened aboard [station_name()].</b>"
|
||||
world << "<b><span class='brass'>Enlightened</span>: Serve your master so that his influence might grow.</b>"
|
||||
world << "<b><span class='boldannounce'>Crew</span>: Prevent the servants of Ratvar from taking over the station.</b>"
|
||||
|
||||
/datum/game_mode/clockwork_cult/pre_setup()
|
||||
if(config.protect_roles_from_antagonist)
|
||||
restricted_jobs += protected_jobs
|
||||
if(config.protect_assistant_from_antagonist)
|
||||
restricted_jobs += "Assistant"
|
||||
var/starter_servants = max(1, round(num_players() / 10)) //Guaranteed one cultist - otherwise, about one cultist for every ten players
|
||||
while(starter_servants)
|
||||
var/datum/mind/servant = pick(antag_candidates)
|
||||
servants_to_serve += servant
|
||||
antag_candidates -= servant
|
||||
modePlayer += servant
|
||||
servant.special_role = "Servant of Ratvar"
|
||||
servant.restricted_roles = restricted_jobs
|
||||
starter_servants--
|
||||
return 1
|
||||
|
||||
/datum/game_mode/clockwork_cult/post_setup()
|
||||
forge_clock_objectives()
|
||||
for(var/S in servants_to_serve)
|
||||
var/datum/mind/servant = S
|
||||
log_game("[servant.key] was made an initial servant of Ratvar")
|
||||
var/mob/living/L = servant.current
|
||||
greet_servant(L)
|
||||
equip_servant(L)
|
||||
add_servant_of_ratvar(L, TRUE)
|
||||
..()
|
||||
return 1
|
||||
|
||||
/datum/game_mode/clockwork_cult/proc/forge_clock_objectives() //Determine what objective that Ratvar's servants will fulfill
|
||||
var/list/possible_objectives = list("escape", "gateway")
|
||||
var/silicons_possible = FALSE
|
||||
for(var/mob/living/silicon/S in living_mob_list)
|
||||
silicons_possible = TRUE
|
||||
if(silicons_possible)
|
||||
possible_objectives += "silicons"
|
||||
clockwork_objective = pick(possible_objectives)
|
||||
clockwork_objective = "gateway" //TEMPORARY, to be removed before merge
|
||||
switch(clockwork_objective)
|
||||
if("escape")
|
||||
required_escapees = max(1, num_players() / 3) //33% of the player count must be cultists
|
||||
clockwork_explanation = "Ensure that [required_escapees] servant(s) of Ratvar escape from [station_name()]."
|
||||
if("gateway")
|
||||
clockwork_explanation = "Construct a Gateway to the Celestial Derelict and free Ratvar."
|
||||
if("silicons")
|
||||
clockwork_explanation = "Ensure that all silicon-based lifeforms on [station_name()] are servants of Ratvar by the end of the shift."
|
||||
return 1
|
||||
|
||||
/datum/game_mode/clockwork_cult/proc/greet_servant(mob/M) //Description of their role
|
||||
if(!M)
|
||||
return 0
|
||||
var/greeting_text = "<br><b><span class='large_brass'>You are a servant of Ratvar, the Clockwork Justiciar.</span>\n\
|
||||
Rusting eternally in the Celestial Derelict, Ratvar has formed a covenant of mortals, with you as one of its members. As one of the Justiciar's servants, you are to work to the best of your \
|
||||
ability to assist in completion of His agenda. You do not know the specifics of how to do so, but luckily you have a vessel to help you learn.</b>"
|
||||
M << greeting_text
|
||||
return 1
|
||||
|
||||
/datum/game_mode/proc/equip_servant(mob/living/L) //Grants a clockwork slab to the mob, with one of each component
|
||||
if(!L || !istype(L))
|
||||
return 0
|
||||
var/slot = "At your feet"
|
||||
if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
if(H.back && istype(H.back, /obj/item/weapon/storage/backpack))
|
||||
var/obj/item/weapon/storage/backpack/B = H.back
|
||||
new/obj/item/clockwork/slab/starter(B)
|
||||
slot = "In your [B.name]"
|
||||
if(slot == "At your feet")
|
||||
new/obj/item/clockwork/slab/starter(get_turf(L))
|
||||
L << "<b>[slot] is a link to the halls of Reebe and your master. You may use it to perform many tasks, but also become oriented with the workings of Ratvar and how to best complete your \
|
||||
tasks. This clockwork slab will be instrumental in your triumph. Remember: you can speak discreetly with your fellow servants by using the <span class='brass'>Hierophant Network</span> action button, \
|
||||
and you can find a concise tutorial by using the slab in-hand and selecting Recollection.</b>"
|
||||
L << "<i>Alternatively, check out the wiki page at </i><b>https://tgstation13.org/wiki/Clockwork_Cult</b><i>, which contains additional information.</i>"
|
||||
return 1
|
||||
|
||||
/datum/game_mode/clockwork_cult/proc/present_tasks(mob/living/L) //Memorizes and displays the clockwork cult's objective
|
||||
if(!L || !istype(L) || !L.mind)
|
||||
return 0
|
||||
var/datum/mind/M = L.mind
|
||||
M.current << "<b>This is Ratvar's will:</b> [clockwork_explanation]"
|
||||
M.memory += "<b>Ratvar's will:</b> [clockwork_explanation]<br>"
|
||||
return 1
|
||||
|
||||
/datum/game_mode/clockwork_cult/proc/check_clockwork_victory()
|
||||
switch(clockwork_objective)
|
||||
if("escape")
|
||||
var/surviving_servants = 0
|
||||
for(var/datum/mind/M in servants_of_ratvar)
|
||||
if(M.current && M.current.stat != DEAD && (M.current.onCentcom() || M.current.onSyndieBase()))
|
||||
surviving_servants++
|
||||
if(surviving_servants <= required_escapees)
|
||||
return 1
|
||||
return 0
|
||||
if("silicons")
|
||||
for(var/mob/living/silicon/robot/S in mob_list) //Only check robots and AIs
|
||||
if(!is_servant_of_ratvar(S))
|
||||
return 0
|
||||
for(var/mob/living/silicon/ai/A in mob_list)
|
||||
if(!is_servant_of_ratvar(A))
|
||||
return 0
|
||||
return 1
|
||||
if("gateway")
|
||||
return ratvar_awakens
|
||||
return 0 //This shouldn't ever be reached, but just in case it is
|
||||
|
||||
/datum/game_mode/clockwork_cult/declare_completion()
|
||||
..()
|
||||
return 0 //Doesn't end until the round does
|
||||
|
||||
/datum/game_mode/proc/auto_declare_completion_clockwork_cult()
|
||||
var/text = ""
|
||||
if(istype(ticker.mode, /datum/game_mode/clockwork_cult)) //Possibly hacky?
|
||||
var/datum/game_mode/clockwork_cult/C = ticker.mode
|
||||
if(C.check_clockwork_victory())
|
||||
text += "<span class='brass'><b>Ratvar's servants have succeeded in fulfilling His goals!</b></span>"
|
||||
feedback_set_details("round_end_result", "win - servants summoned ratvar")
|
||||
else
|
||||
text += "<span class='userdanger'>Ratvar's servants have failed!</span>"
|
||||
feedback_set_details("round_end_result", "loss - servants did not summon ratvar")
|
||||
text += "<br><b>The goal of the clockwork cult was:</b> [clockwork_explanation]<br>"
|
||||
if(servants_of_ratvar.len)
|
||||
text += "<b>Ratvar's servants were:</b>"
|
||||
for(var/datum/mind/M in servants_of_ratvar)
|
||||
text += printplayer(M)
|
||||
world << text
|
||||
|
||||
/datum/game_mode/proc/update_servant_icons_added(datum/mind/M)
|
||||
var/datum/atom_hud/antag/A = huds[ANTAG_HUD_CLOCKWORK]
|
||||
A.join_hud(M.current)
|
||||
set_antag_hud(M.current, "clockwork")
|
||||
|
||||
/datum/game_mode/proc/update_servant_icons_removed(datum/mind/M)
|
||||
var/datum/atom_hud/antag/A = huds[ANTAG_HUD_CLOCKWORK]
|
||||
A.leave_hud(M.current)
|
||||
set_antag_hud(M.current, null)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,595 @@
|
||||
////////////////////////
|
||||
// CLOCKWORK MACHINES //
|
||||
////////////////////////
|
||||
//not-actually-machines
|
||||
|
||||
/obj/structure/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/clockwork/powered/examine(mob/user)
|
||||
..()
|
||||
if(is_servant_of_ratvar(user) || isobserver(user))
|
||||
var/powered = total_accessable_power()
|
||||
user << "<span class='[powered ? "brass":"alloy"]'>It has access to [powered == INFINITY ? "INFINITY":"[powered]"]W of power.</span>"
|
||||
|
||||
/obj/structure/clockwork/powered/Destroy()
|
||||
SSfastprocess.processing -= src
|
||||
SSobj.processing -= src
|
||||
return ..()
|
||||
|
||||
/obj/structure/clockwork/powered/process()
|
||||
var/powered = total_accessable_power()
|
||||
return powered == PROCESS_KILL ? 25 : powered //make sure we don't accidentally return the arbitrary PROCESS_KILL define
|
||||
|
||||
/obj/structure/clockwork/powered/proc/toggle(fast_process, mob/living/user)
|
||||
if(user)
|
||||
if(!is_servant_of_ratvar(user))
|
||||
return 0
|
||||
user.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)
|
||||
|
||||
|
||||
/obj/structure/clockwork/powered/proc/total_accessable_power() //how much power we have and can use
|
||||
if(!needs_power || ratvar_awakens)
|
||||
return INFINITY //oh yeah we've got power why'd you ask
|
||||
|
||||
var/power = 0
|
||||
power += accessable_apc_power()
|
||||
power += accessable_sigil_power()
|
||||
return power
|
||||
|
||||
/obj/structure/clockwork/powered/proc/accessable_apc_power()
|
||||
var/power = 0
|
||||
var/area/A = get_area(src)
|
||||
var/area/targetAPCA
|
||||
for(var/obj/machinery/power/apc/APC in apcs_list)
|
||||
var/area/APCA = get_area(APC)
|
||||
if(APCA == A)
|
||||
target_apc = APC
|
||||
if(target_apc)
|
||||
targetAPCA = get_area(target_apc)
|
||||
if(targetAPCA != A)
|
||||
target_apc = null
|
||||
else if(target_apc.cell)
|
||||
var/apccharge = target_apc.cell.charge
|
||||
if(apccharge >= 50)
|
||||
power += apccharge
|
||||
return power
|
||||
|
||||
/obj/structure/clockwork/powered/proc/accessable_sigil_power()
|
||||
var/power = 0
|
||||
for(var/obj/effect/clockwork/sigil/transmission/T in range(1, src))
|
||||
power += T.power_charge
|
||||
return power
|
||||
|
||||
|
||||
/obj/structure/clockwork/powered/proc/try_use_power(amount) //try to use an amount of power
|
||||
if(!needs_power || ratvar_awakens)
|
||||
return 1
|
||||
if(amount <= 0)
|
||||
return 0
|
||||
var/power = total_accessable_power()
|
||||
if(!power || power < amount)
|
||||
return 0
|
||||
return use_power(amount)
|
||||
|
||||
/obj/structure/clockwork/powered/proc/use_power(amount) //we've made sure we had power, so now we use it
|
||||
var/sigilpower = accessable_sigil_power()
|
||||
var/list/sigils_in_range = list()
|
||||
for(var/obj/effect/clockwork/sigil/transmission/T in range(1, src))
|
||||
sigils_in_range |= T
|
||||
while(sigilpower && amount >= 50)
|
||||
for(var/S in sigils_in_range)
|
||||
var/obj/effect/clockwork/sigil/transmission/T = S
|
||||
if(amount >= 50 && T.modify_charge(50))
|
||||
sigilpower -= 50
|
||||
amount -= 50
|
||||
var/apcpower = accessable_apc_power()
|
||||
while(apcpower >= 50 && amount >= 50)
|
||||
if(target_apc.cell.use(50))
|
||||
apcpower -= 50
|
||||
amount -= 50
|
||||
target_apc.update()
|
||||
target_apc.update_icon()
|
||||
else
|
||||
apcpower = 0
|
||||
if(amount)
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
|
||||
/obj/structure/clockwork/powered/proc/return_power(amount) //returns a given amount of power to all nearby sigils
|
||||
if(amount <= 0)
|
||||
return 0
|
||||
var/list/sigils_in_range = list()
|
||||
for(var/obj/effect/clockwork/sigil/transmission/T in range(1, src))
|
||||
sigils_in_range |= T
|
||||
if(!sigils_in_range.len)
|
||||
return 0
|
||||
while(amount >= 50)
|
||||
for(var/S in sigils_in_range)
|
||||
var/obj/effect/clockwork/sigil/transmission/T = S
|
||||
if(amount >= 50 && T.modify_charge(-50))
|
||||
amount -= 50
|
||||
return 1
|
||||
|
||||
|
||||
/obj/structure/clockwork/powered/mending_motor //Mending motor: A prism that consumes replicant alloy to repair nearby mechanical servants at a quick rate.
|
||||
name = "mending motor"
|
||||
desc = "A dark onyx prism, held in midair by spiraling tendrils of stone."
|
||||
clockwork_desc = "A powerful prism that rapidly repairs nearby mechanical servants and clockwork structures."
|
||||
icon_state = "mending_motor_inactive"
|
||||
active_icon = "mending_motor"
|
||||
inactive_icon = "mending_motor_inactive"
|
||||
construction_value = 20
|
||||
max_health = 150
|
||||
health = 150
|
||||
break_message = "<span class='warning'>The prism collapses with a heavy thud!</span>"
|
||||
debris = list(/obj/item/clockwork/alloy_shards, /obj/item/clockwork/component/vanguard_cogwheel)
|
||||
var/stored_alloy = 0 //2500W = 1 alloy = 100 liquified alloy
|
||||
var/max_alloy = 25000
|
||||
var/mob_cost = 200
|
||||
var/structure_cost = 250
|
||||
var/cyborg_cost = 300
|
||||
|
||||
/obj/structure/clockwork/powered/mending_motor/prefilled
|
||||
stored_alloy = 2500 //starts with 1 replicant alloy/100 liquified alloy
|
||||
|
||||
/obj/structure/clockwork/powered/mending_motor/total_accessable_power()
|
||||
. = ..()
|
||||
if(. != INFINITY)
|
||||
. += accessable_alloy_power()
|
||||
|
||||
/obj/structure/clockwork/powered/mending_motor/proc/accessable_alloy_power()
|
||||
return stored_alloy
|
||||
|
||||
/obj/structure/clockwork/powered/mending_motor/use_power(amount)
|
||||
var/alloypower = accessable_alloy_power()
|
||||
while(alloypower >= 50 && amount >= 50)
|
||||
stored_alloy -= 50
|
||||
alloypower -= 50
|
||||
amount -= 50
|
||||
return ..()
|
||||
|
||||
/obj/structure/clockwork/powered/mending_motor/examine(mob/user)
|
||||
..()
|
||||
if(is_servant_of_ratvar(user) || isobserver(user))
|
||||
user << "<span class='alloy'>It contains [stored_alloy*0.04]/[max_alloy*0.04] units of liquified alloy, which is equivalent to [stored_alloy]W/[max_alloy]W of power.</span>"
|
||||
user << "<span class='inathneq_small'>It requires [mob_cost]W to heal clockwork mobs, [structure_cost]W for clockwork structures, and [cyborg_cost]W for cyborgs.</span>"
|
||||
|
||||
/obj/structure/clockwork/powered/mending_motor/process()
|
||||
if(..() < mob_cost)
|
||||
visible_message("<span class='warning'>[src] emits an airy chuckling sound and falls dark!</span>")
|
||||
toggle()
|
||||
return
|
||||
for(var/atom/movable/M in range(5, src))
|
||||
if(isclockmob(M) || istype(M, /mob/living/simple_animal/drone/cogscarab))
|
||||
var/mob/living/simple_animal/hostile/clockwork/W = M
|
||||
var/fatigued = FALSE
|
||||
if(istype(M, /mob/living/simple_animal/hostile/clockwork/marauder))
|
||||
var/mob/living/simple_animal/hostile/clockwork/marauder/E = M
|
||||
if(E.fatigue)
|
||||
fatigued = TRUE
|
||||
if((!fatigued && W.health == W.maxHealth) || W.stat)
|
||||
continue
|
||||
if(!try_use_power(mob_cost))
|
||||
break
|
||||
W.adjustHealth(-15)
|
||||
else if(istype(M, /obj/structure/clockwork))
|
||||
var/obj/structure/clockwork/C = M
|
||||
if(C.health == C.max_health)
|
||||
continue
|
||||
if(!try_use_power(structure_cost))
|
||||
break
|
||||
C.health = min(C.health + 15, C.max_health)
|
||||
else if(issilicon(M))
|
||||
var/mob/living/silicon/S = M
|
||||
if(S.health == S.maxHealth || S.stat == DEAD || !is_servant_of_ratvar(S))
|
||||
continue
|
||||
if(!try_use_power(cyborg_cost))
|
||||
break
|
||||
S.adjustBruteLoss(-15)
|
||||
S.adjustFireLoss(-15)
|
||||
return 1
|
||||
|
||||
/obj/structure/clockwork/powered/mending_motor/attack_hand(mob/living/user)
|
||||
if(user.canUseTopic(src, BE_CLOSE))
|
||||
if(total_accessable_power() < mob_cost)
|
||||
user << "<span class='warning'>[src] needs more power or replicant alloy to function!</span>"
|
||||
return 0
|
||||
toggle(0, user)
|
||||
|
||||
/obj/structure/clockwork/powered/mending_motor/attackby(obj/item/I, mob/user, params)
|
||||
if(istype(I, /obj/item/clockwork/component/replicant_alloy) && is_servant_of_ratvar(user))
|
||||
if(stored_alloy + 2500 > max_alloy)
|
||||
user << "<span class='warning'>[src] is too full to accept any more alloy!</span>"
|
||||
return 0
|
||||
user.whisper("Genafzhgr vagb jngre.")
|
||||
user.visible_message("<span class='notice'>[user] liquifies [I] and pours it onto [src].</span>", \
|
||||
"<span class='notice'>You liquify [src] and pour it onto [src], transferring the alloy into its reserves.</span>")
|
||||
stored_alloy = stored_alloy + 2500
|
||||
user.drop_item()
|
||||
qdel(I)
|
||||
return 1
|
||||
else
|
||||
return ..()
|
||||
|
||||
|
||||
|
||||
/obj/structure/clockwork/powered/mania_motor //Mania motor: A pair of antenna that, while active, cause braindamage and hallucinations in nearby human mobs.
|
||||
name = "mania motor"
|
||||
desc = "A pair of antenna with what appear to be sockets around the base. It reminds you of an antlion."
|
||||
clockwork_desc = "A transmitter that allows Sevtug to whisper into the minds of nearby non-servants, causing hallucinations and brain damage as long as it remains powered."
|
||||
icon_state = "mania_motor_inactive"
|
||||
active_icon = "mania_motor"
|
||||
inactive_icon = "mania_motor_inactive"
|
||||
construction_value = 20
|
||||
max_health = 80
|
||||
health = 80
|
||||
break_message = "<span class='warning'>The antenna break off, leaving a pile of shards!</span>"
|
||||
debris = list(/obj/item/clockwork/alloy_shards, /obj/item/clockwork/component/guvax_capacitor/antennae)
|
||||
var/mania_cost = 150
|
||||
var/convert_attempt_cost = 150
|
||||
var/convert_cost = 300
|
||||
|
||||
var/mania_messages = list("\"Tb ahgf.\"", "\"Gnxr n penpx ng penml.\"", "\"Znxr n ovq sbe vafnavgl.\"", "\"Trg xbbxl.\"", "\"Zbir gbjneqf znavn.\"", "\"Orpbzr orjvyqrerq.\"", "\"Jnk jvyq.\"", \
|
||||
"\"Tb ebhaq gur oraq.\"", "\"Ynaq va yhanpl.\"", "\"Gel qrzragvn.\"", "\"Fgevir gb trg n fperj ybbfr.\"")
|
||||
var/compel_messages = list("\"Pbzr pybfre.\"", "\"Nccebnpu gur genafzvggre.\"", "\"Gbhpu gur nagraanr.\"", "\"V nyjnlf unir gb qrny jvgu vqvbgf. Zbir gbjneqf gur znavn zbgbe.\"", \
|
||||
"\"Nqinapr sbejneq naq cynpr lbhe urnq orgjrra gur nagraanr - gun'g'f nyy vg'f tbbq sbe.\"", "\"Vs lbh jrer fznegre, lbh'q or bire urer nyernql.\"", "\"Zbir SBEJNEQ, lbh sbby.\"")
|
||||
var/convert_messages = list("\"Lbh jba'g qb. Tb gb fyrrc juvyr V gryy gur'fr avgjvgf ubj gb pbaireg lbh.\"", "\"Lbh ner vafhssvpvrag. V zhfg vafgehpg gur'fr vqvbgf va gur neg-bs pbairefvba.\"", \
|
||||
"\"Bu-bs pbhefr, fbzrbar jr pna'g pbaireg. Gur'fr freinagf ner sbbyf.\"", "\"Ubj uneq vf vg gb hfr n Fvtvy, naljnl? Nyy vg gnxrf vf qenttvat fbzrbar bagb vg.\"", \
|
||||
"\"Ubj qb gur'l snvy gb hfr n Fvtvy-bs Npprffvba, naljnl?\"", "\"Jul vf vg gun'g nyy freinagf ner guv'f varcg?\"", "\"Vg'f dhvgr yvxryl lbh'yy or fghpx urer sbe n juvyr.\"")
|
||||
var/close_messages = list("\"Jryy, lbh pna'g ernpu gur zbgbe sebz GUR'ER, lbh zbeba.\"", "\"Vagrerfgvat ybpngvba. V'q cersre vs lbh jrag fbzrjurer lbh pbhyq NPGHNYYL GBHPU GUR NAGRAANR!\"", \
|
||||
"\"Nznmvat. Lbh fbzrubj znantrq gb jrqtr lbhefrys fbzrjurer lbh pna'g npghnyyl ernpu gur zbgbe sebz.\"", "\"Fhpu n fubj-bs vqvbpl vf hacnenyyryrq. Creuncf V fubhyq chg lbh ba qvfcynl?\"", \
|
||||
"\"Qvq lbh qb guv'f ba checbfr? V pna'g vzntvar lbh qbvat fb nppvqragnyyl. Bu, jnvg, V pna.\"", "\"Ubj vf vg gun'g fhpu fzneg perngherf pna fgvyy qb fbzrguv'at NF FGHCVQ NF GUV'F!\"")
|
||||
|
||||
|
||||
/obj/structure/clockwork/powered/mania_motor/examine(mob/user)
|
||||
..()
|
||||
if(is_servant_of_ratvar(user) || isobserver(user))
|
||||
user << "<span class='sevtug_small'>It requires [mania_cost]W to run, and [convert_attempt_cost + convert_cost]W to convert humans adjecent to it.</span>"
|
||||
|
||||
/obj/structure/clockwork/powered/mania_motor/process()
|
||||
var/turf/T = get_turf(src)
|
||||
if(!..())
|
||||
visible_message("<span class='warning'>[src] hums loudly, then the sockets at its base fall dark!</span>")
|
||||
playsound(T, 'sound/effects/screech.ogg', 40, 1)
|
||||
toggle(0)
|
||||
return
|
||||
if(try_use_power(mania_cost))
|
||||
var/hum = get_sfx('sound/effects/screech.ogg') //like playsound, same sound for everyone affected
|
||||
for(var/mob/living/carbon/human/H in view(1, src))
|
||||
if(H.Adjacent(src) && try_use_power(convert_attempt_cost))
|
||||
if(is_eligible_servant(H) && try_use_power(convert_cost))
|
||||
H << "<span class='sevtug'>\"Lbh ner zvar-naq-uvf, abj.\"</span>"
|
||||
H.playsound_local(T, hum, 80, 1)
|
||||
add_servant_of_ratvar(H)
|
||||
else if(!H.stat)
|
||||
if(H.getBrainLoss() >= H.maxHealth)
|
||||
H.Paralyse(5)
|
||||
H << "<span class='sevtug'>[pick(convert_messages)]</span>"
|
||||
else
|
||||
H.adjustBrainLoss(100)
|
||||
H.visible_message("<span class='warning'>[H] reaches out and touches [src].</span>", "<span class='sevtug'>You touch [src] involuntarily.</span>")
|
||||
else
|
||||
visible_message("<span class='warning'>[src]'s antennae fizzle quietly.</span>")
|
||||
playsound(src, 'sound/effects/light_flicker.ogg', 50, 1)
|
||||
for(var/mob/living/carbon/human/H in range(10, src))
|
||||
if(!is_servant_of_ratvar(H) && !H.null_rod_check() && H.stat == CONSCIOUS)
|
||||
var/distance = get_dist(T, get_turf(H))
|
||||
var/falloff_distance = min((110) - distance * 10, 80)
|
||||
var/sound_distance = falloff_distance * 0.5
|
||||
var/targetbrainloss = H.getBrainLoss()
|
||||
var/targethallu = H.hallucination
|
||||
var/targetdruggy = H.druggy
|
||||
if(distance >= 4 && prob(falloff_distance))
|
||||
H << "<span class='sevtug_small'>[pick(mania_messages)]</span>"
|
||||
H.playsound_local(T, hum, sound_distance, 1)
|
||||
switch(distance)
|
||||
if(2 to 3)
|
||||
if(prob(falloff_distance))
|
||||
if(prob(falloff_distance))
|
||||
H << "<span class='sevtug_small'>[pick(mania_messages)]</span>"
|
||||
else
|
||||
H << "<span class='sevtug'>[pick(compel_messages)]</span>"
|
||||
if(targetbrainloss <= 50)
|
||||
H.adjustBrainLoss(50 - targetbrainloss) //got too close had brain eaten
|
||||
if(targetdruggy <= 150)
|
||||
H.adjust_drugginess(11)
|
||||
if(targethallu <= 150)
|
||||
H.hallucination += 11
|
||||
if(4 to 5)
|
||||
if(targetbrainloss <= 50)
|
||||
H.adjustBrainLoss(3)
|
||||
if(targetdruggy <= 120)
|
||||
H.adjust_drugginess(9)
|
||||
if(targethallu <= 120)
|
||||
H.hallucination += 9
|
||||
if(6 to 7)
|
||||
if(targetbrainloss <= 30)
|
||||
H.adjustBrainLoss(2)
|
||||
if(prob(falloff_distance) && targetdruggy <= 90)
|
||||
H.adjust_drugginess(7)
|
||||
else if(targethallu <= 90)
|
||||
H.hallucination += 7
|
||||
if(8 to 9)
|
||||
if(H.getBrainLoss() <= 10)
|
||||
H.adjustBrainLoss(1)
|
||||
if(prob(falloff_distance) && targetdruggy <= 60)
|
||||
H.adjust_drugginess(5)
|
||||
else if(targethallu <= 60)
|
||||
H.hallucination += 5
|
||||
if(10 to INFINITY)
|
||||
if(prob(falloff_distance) && targetdruggy <= 30)
|
||||
H.adjust_drugginess(3)
|
||||
else if(targethallu <= 30)
|
||||
H.hallucination += 3
|
||||
else //if it's a distance of 1 and they can't see it/aren't adjacent or they're on top of it(how'd they get on top of it and still trigger this???)
|
||||
if(targetbrainloss <= 99)
|
||||
if(prob(falloff_distance))
|
||||
if(prob(falloff_distance))
|
||||
H << "<span class='sevtug'>[pick(compel_messages)]</span>"
|
||||
else if(prob(falloff_distance))
|
||||
H << "<span class='sevtug'>[pick(close_messages)]</span>"
|
||||
else
|
||||
H << "<span class='sevtug_small'>[pick(mania_messages)]</span>"
|
||||
H.adjustBrainLoss(99 - targetbrainloss)
|
||||
if(targetdruggy <= 200)
|
||||
H.adjust_drugginess(15)
|
||||
if(targethallu <= 200)
|
||||
H.hallucination += 15
|
||||
|
||||
if(is_servant_of_ratvar(H) && (H.getBrainLoss() || H.hallucination || H.druggy)) //not an else so that newly converted servants are healed of the damage it inflicts
|
||||
H.adjustBrainLoss(-H.getBrainLoss()) //heals servants of braindamage, hallucination, and druggy
|
||||
H.hallucination = 0
|
||||
H.adjust_drugginess(-H.druggy)
|
||||
else
|
||||
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(0)
|
||||
|
||||
/obj/structure/clockwork/powered/mania_motor/attack_hand(mob/living/user)
|
||||
if(user.canUseTopic(src, BE_CLOSE))
|
||||
if(!total_accessable_power() >= mania_cost)
|
||||
user << "<span class='warning'>[src] needs more power to function!</span>"
|
||||
return 0
|
||||
toggle(0, user)
|
||||
|
||||
|
||||
|
||||
/obj/structure/clockwork/powered/interdiction_lens //Interdiction lens: A powerful artifact that constantly disrupts electronics but, if it fails to find something to disrupt, turns off.
|
||||
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 disrupts nearby electronics and funnels power into nearby Sigils of Transmission."
|
||||
icon_state = "interdiction_lens"
|
||||
construction_value = 25
|
||||
active_icon = "interdiction_lens_active"
|
||||
inactive_icon = "interdiction_lens"
|
||||
break_message = "<span class='warning'>The lens flares a blinding violet before shattering!</span>"
|
||||
break_sound = 'sound/effects/Glassbr3.ogg'
|
||||
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/disrupt_cost = 100 //how much power to use when disabling an object
|
||||
|
||||
/obj/structure/clockwork/powered/interdiction_lens/examine(mob/user)
|
||||
..()
|
||||
user << "<span class='[recharging > world.time ? "nezbere_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))
|
||||
user << "<span class='nezbere_small'>It requires [disrupt_cost]W of power for each nearby disruptable electronic.</span>"
|
||||
user << "<span class='nezbere_small'>If it fails to both drain any power and disrupt any electronics, it will disable itself for [round(recharge_time/600, 1)] minutes.</span>"
|
||||
|
||||
/obj/structure/clockwork/powered/interdiction_lens/toggle(fast_process, mob/living/user)
|
||||
..()
|
||||
if(active)
|
||||
SetLuminosity(4,2)
|
||||
else
|
||||
SetLuminosity(0)
|
||||
|
||||
/obj/structure/clockwork/powered/interdiction_lens/attack_hand(mob/living/user)
|
||||
if(user.canUseTopic(src, BE_CLOSE))
|
||||
if(disabled)
|
||||
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
|
||||
if(!total_accessable_power() >= disrupt_cost)
|
||||
user << "<span class='warning'>[src] needs more power to function!</span>"
|
||||
return 0
|
||||
toggle(0, user)
|
||||
|
||||
/obj/structure/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
|
||||
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
|
||||
|
||||
for(var/M in atoms_to_test)
|
||||
if(istype(M, /obj/machinery/power/apc))
|
||||
var/obj/machinery/power/apc/A = M
|
||||
if(A.cell && A.cell.charge)
|
||||
successfulprocess = TRUE
|
||||
playsound(A, "sparks", 50, 1)
|
||||
flick("apc-spark", A)
|
||||
power_drained += min(A.cell.charge, 100)
|
||||
A.cell.charge = max(0, A.cell.charge - 100)
|
||||
if(!A.cell.charge && !A.shorted)
|
||||
A.shorted = 1
|
||||
A.visible_message("<span class='warning'>The [A.name]'s screen blurs with static.</span>")
|
||||
A.update()
|
||||
A.update_icon()
|
||||
else if(istype(M, /obj/machinery/power/smes))
|
||||
var/obj/machinery/power/smes/S = M
|
||||
if(S.charge)
|
||||
successfulprocess = TRUE
|
||||
power_drained += min(S.charge, 500)
|
||||
S.charge = max(0, S.charge - 50000) //SMES units contain too much power and could run an interdiction lens basically forever, or provide power forever
|
||||
if(!S.charge && !S.panel_open)
|
||||
S.panel_open = TRUE
|
||||
S.icon_state = "[initial(S.icon_state)]-o"
|
||||
var/datum/effect_system/spark_spread/spks = new(get_turf(S))
|
||||
spks.set_up(10, 0, get_turf(S))
|
||||
spks.start()
|
||||
S.visible_message("<span class='warning'>[S]'s panel flies open with a flurry of sparks.</span>")
|
||||
S.update_icon()
|
||||
else if(isrobot(M))
|
||||
var/mob/living/silicon/robot/R = M
|
||||
if(!is_servant_of_ratvar(R) && R.cell && R.cell.charge)
|
||||
successfulprocess = TRUE
|
||||
power_drained += min(R.cell.charge, 200)
|
||||
R.cell.charge = max(0, R.cell.charge - 200)
|
||||
R << "<span class='warning'>ERROR: Power loss detected!</span>"
|
||||
var/datum/effect_system/spark_spread/spks = new(get_turf(R))
|
||||
spks.set_up(3, 0, get_turf(R))
|
||||
spks.start()
|
||||
|
||||
CHECK_TICK
|
||||
|
||||
if(!return_power(power_drained) || power_drained < 50) //failed to return power drained or too little power to return
|
||||
successfulprocess = FALSE
|
||||
if(try_use_power(disrupt_cost) && total_accessable_power() >= disrupt_cost) //if we can disable at least one object
|
||||
playsound(src, 'sound/items/PSHOOM.ogg', 50, 1, interdiction_range-7, 1)
|
||||
for(var/M in atoms_to_test)
|
||||
if(istype(M, /obj/machinery/light)) //cosmetic light flickering
|
||||
var/obj/machinery/light/L = M
|
||||
if(L.on)
|
||||
playsound(L, 'sound/effects/light_flicker.ogg', 50, 1)
|
||||
L.flicker(3)
|
||||
else if(istype(M, /obj/machinery/camera))
|
||||
var/obj/machinery/camera/C = M
|
||||
if(C.isEmpProof() || !C.status)
|
||||
continue
|
||||
successfulprocess = TRUE
|
||||
if(C.emped)
|
||||
continue
|
||||
if(!try_use_power(disrupt_cost))
|
||||
break
|
||||
C.emp_act(1)
|
||||
else if(istype(M, /obj/item/device/radio))
|
||||
var/obj/item/device/radio/O = M
|
||||
successfulprocess = TRUE
|
||||
if(O.emped || !O.on)
|
||||
continue
|
||||
if(!try_use_power(disrupt_cost))
|
||||
break
|
||||
O.emp_act(1)
|
||||
else if(isliving(M) || istype(M, /obj/structure/closet) || istype(M, /obj/item/weapon/storage)) //other things may have radios in them but we don't care
|
||||
var/atom/movable/A = M
|
||||
for(var/obj/item/device/radio/O in A.GetAllContents())
|
||||
successfulprocess = TRUE
|
||||
if(O.emped || !O.on)
|
||||
continue
|
||||
if(!try_use_power(disrupt_cost))
|
||||
break
|
||||
O.emp_act(1)
|
||||
|
||||
CHECK_TICK
|
||||
|
||||
if(!successfulprocess)
|
||||
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"
|
||||
SetLuminosity(2,1)
|
||||
disabled = TRUE
|
||||
|
||||
|
||||
|
||||
/obj/structure/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"
|
||||
construction_value = 20
|
||||
max_health = 200
|
||||
health = 200
|
||||
break_message = "<span class='warning'>The obelisk falls to the ground, undamaged!</span>"
|
||||
debris = list(/obj/item/clockwork/component/hierophant_ansible/obelisk)
|
||||
var/hierophant_cost = 50 //how much it costs to broadcast with large text
|
||||
var/gateway_cost = 2000 //how much it costs to open a gateway
|
||||
var/gateway_active = FALSE
|
||||
|
||||
/obj/structure/clockwork/powered/clockwork_obelisk/New()
|
||||
..()
|
||||
toggle(1)
|
||||
|
||||
/obj/structure/clockwork/powered/clockwork_obelisk/examine(mob/user)
|
||||
..()
|
||||
if(is_servant_of_ratvar(user) || isobserver(user))
|
||||
user << "<span class='nzcrentr_small'>It requires [hierophant_cost]W to broadcast over the Hierophant Network, and [gateway_cost]W to open a Spatial Gateway.</span>"
|
||||
|
||||
/obj/structure/clockwork/powered/clockwork_obelisk/process()
|
||||
if(locate(/obj/effect/clockwork/spatial_gateway) in loc)
|
||||
icon_state = active_icon
|
||||
density = 0
|
||||
gateway_active = TRUE
|
||||
else
|
||||
icon_state = inactive_icon
|
||||
density = 1
|
||||
gateway_active = FALSE
|
||||
|
||||
/obj/structure/clockwork/powered/clockwork_obelisk/attack_hand(mob/living/user)
|
||||
if(!is_servant_of_ratvar(user) || !total_accessable_power() >= hierophant_cost)
|
||||
user << "<span class='warning'>You place your hand on the obelisk, but it doesn't react.</span>"
|
||||
return
|
||||
var/choice = alert(user,"You place your hand on the obelisk...",,"Hierophant Broadcast","Spatial Gateway","Cancel")
|
||||
switch(choice)
|
||||
if("Hierophant Broadcast")
|
||||
if(gateway_active)
|
||||
user << "<span class='warning'>The obelisk is sustaining a gateway and cannot broadcast!</span>"
|
||||
return
|
||||
var/input = stripped_input(usr, "Please choose a message to send over the Hierophant Network.", "Hierophant Broadcast", "")
|
||||
if(!input || !user.canUseTopic(src, BE_CLOSE))
|
||||
return
|
||||
if(gateway_active)
|
||||
user << "<span class='warning'>The obelisk is sustaining a gateway and cannot broadcast!</span>"
|
||||
return
|
||||
if(!try_use_power(hierophant_cost))
|
||||
user << "<span class='warning'>The obelisk lacks the power to broadcast!</span>"
|
||||
return
|
||||
clockwork_say(user, "Uvrebcunag Oebnqpnfg, npgvingr!")
|
||||
send_hierophant_message(user, input, "big_brass", "large_brass")
|
||||
if("Spatial Gateway")
|
||||
if(gateway_active)
|
||||
user << "<span class='warning'>The obelisk is already sustaining a gateway!</span>"
|
||||
return
|
||||
if(!try_use_power(gateway_cost))
|
||||
user << "<span class='warning'>The obelisk lacks the power to open a gateway!</span>"
|
||||
return
|
||||
if(procure_gateway(user, 100, 5, 1))
|
||||
clockwork_say(user, "Fcnpvny Tngrjnl, npgvingr!")
|
||||
else
|
||||
return_power(gateway_cost)
|
||||
if("Cancel")
|
||||
return
|
||||
@@ -0,0 +1,579 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork
|
||||
faction = list("ratvar")
|
||||
icon = 'icons/mob/clockwork_mobs.dmi'
|
||||
unique_name = 1
|
||||
minbodytemp = 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)
|
||||
languages_spoken = RATVAR
|
||||
languages_understood = HUMAN|RATVAR
|
||||
healable = FALSE
|
||||
del_on_death = TRUE
|
||||
bubble_icon = "clock"
|
||||
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/obj/item/clockwork/slab/internalslab //an internal slab for running scripture
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/New()
|
||||
..()
|
||||
internalslab = new/obj/item/clockwork/slab/internal(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/Destroy()
|
||||
qdel(internalslab)
|
||||
internalslab = null
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/get_spans()
|
||||
return ..() | SPAN_ROBOT
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/Login()
|
||||
..()
|
||||
src << playstyle_string
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/fragment //Anima fragment: Low health and high melee damage, but slows down when struck. Created by inserting a soul vessel into an empty 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 = 20
|
||||
melee_damage_upper = 20
|
||||
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")
|
||||
flying = 1
|
||||
playstyle_string = "<span class='heavy_brass'>You are an anima fragment</span><b>, a clockwork creation of Ratvar. As a fragment, you have low health, do decent damage, and move at \
|
||||
extreme speed in addition to being immune to extreme temperatures and pressures. Taking damage 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/New()
|
||||
..()
|
||||
SetLuminosity(2,1)
|
||||
if(prob(1))
|
||||
name = "anime fragment"
|
||||
real_name = name
|
||||
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/Stat()
|
||||
..()
|
||||
if(statpanel("Status") && movement_delay_time > world.time && !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>")
|
||||
..(TRUE)
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/fragment/Process_Spacemove(movement_dir = 0)
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/fragment/movement_delay()
|
||||
. = ..()
|
||||
if(movement_delay_time > world.time && !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(!ratvar_awakens && amount > 0) //if ratvar is up we ignore movement delay
|
||||
if(movement_delay_time > world.time)
|
||||
movement_delay_time = movement_delay_time + amount*3
|
||||
else
|
||||
movement_delay_time = world.time + amount*3
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/fragment/updatehealth()
|
||||
..()
|
||||
if(health == maxHealth)
|
||||
speed = initial(speed)
|
||||
else
|
||||
speed = 0 //slow down if damaged at all
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder //Clockwork marauder: Slow but with high damage, resides inside of a servant. Created via the Memory Allocation scripture.
|
||||
name = "clockwork marauder"
|
||||
desc = "A stalwart apparition of a soldier, blazing with crimson flames. It's armed with a gladius and shield."
|
||||
icon_state = "clockwork_marauder"
|
||||
health = 300 //Health is very high, and under most cases it will take enough fatigue to be forced to recall first
|
||||
maxHealth = 300
|
||||
speed = 1
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 10
|
||||
attacktext = "slashes"
|
||||
attack_sound = 'sound/weapons/bladeslice.ogg'
|
||||
environment_smash = 1
|
||||
weather_immunities = list("lava")
|
||||
flying = 1
|
||||
loot = list(/obj/item/clockwork/component/replicant_alloy/fallen_armor)
|
||||
var/true_name = "Meme Master 69" //Required to call forth the marauder
|
||||
var/list/possible_true_names = list("Xaven", "Melange", "Ravan", "Kel", "Rama", "Geke", "Peris", "Vestra", "Skiwa") //All fairly short and easy to pronounce
|
||||
var/fatigue = 0 //Essentially what determines the marauder's power
|
||||
var/fatigue_recall_threshold = 100 //In variable form due to changed effects when Ratvar awakens
|
||||
var/mob/living/host //The mob that the marauder is living inside of
|
||||
var/recovering = FALSE //If the marauder is recovering from a large amount of fatigue
|
||||
var/blockchance = 15 //chance to block melee attacks entirely
|
||||
var/counterchance = 30 //chance to counterattack after blocking
|
||||
var/combattimer = 50 //after 5 seconds of not being hit ot attacking we count as 'out of combat' and lose block/counter chance
|
||||
playstyle_string = "<span class='sevtug'>You are a clockwork marauder</span><b>, a living extension of Sevtug's will. As a marauder, you are somewhat slow, but may block melee attacks \
|
||||
and have a chance to also counter blocked melee attacks for extra damage, in addition to being immune to extreme temperatures and pressures. \
|
||||
Your primary goal is to serve the creature that you are now a part of. You can use <span class='sevtug_small'><i>:b</i></span> to communicate silently with your master, \
|
||||
but can only exit if your master calls your true name or if they are exceptionally damaged. \
|
||||
\n\n\
|
||||
Taking damage and remaining outside of your master will cause <i>fatigue</i>, which hinders your movement speed and attacks, in addition to forcing you back into your master if it grows \
|
||||
too high. As a final note, you should probably avoid harming any fellow servants of Ratvar.</span>"
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/New()
|
||||
..()
|
||||
combattimer = 0
|
||||
true_name = pick(possible_true_names)
|
||||
SetLuminosity(2,1)
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/Life()
|
||||
..()
|
||||
if(combattimer < world.time)
|
||||
blockchance = max(blockchance - 5, initial(blockchance))
|
||||
counterchance = max(counterchance - 10, initial(counterchance))
|
||||
if(is_in_host())
|
||||
if(!ratvar_awakens && host.stat == DEAD)
|
||||
death()
|
||||
return
|
||||
adjust_fatigue(-1.5)
|
||||
if(ratvar_awakens)
|
||||
adjustHealth(-5.5)
|
||||
else
|
||||
adjustHealth(-0.5)
|
||||
if(!fatigue && recovering)
|
||||
src << "<span class='userdanger'>Your strength has returned. You can once again come forward!</span>"
|
||||
host << "<span class='userdanger'>Your marauder is now strong enough to come forward again!</span>"
|
||||
recovering = FALSE
|
||||
else
|
||||
if(ratvar_awakens)
|
||||
adjustHealth(-2)
|
||||
else if(host) //If Ratvar is alive, marauders both don't take fatigue loss and move at fast speed
|
||||
if(host.stat == DEAD)
|
||||
death()
|
||||
return
|
||||
if(z && host.z && z == host.z)
|
||||
switch(get_dist(get_turf(src), get_turf(host)))
|
||||
if(2 to 4)
|
||||
adjust_fatigue(1)
|
||||
if(5)
|
||||
adjust_fatigue(3)
|
||||
if(6 to INFINITY)
|
||||
adjust_fatigue(10)
|
||||
src << "<span class='userdanger'>You're too far from your host and rapidly taking fatigue damage!</span>"
|
||||
else //right next to or on top of host
|
||||
adjust_fatigue(-1)
|
||||
else //well then, you're not even in the same zlevel
|
||||
adjust_fatigue(10)
|
||||
src << "<span class='userdanger'>You're too far from your host and rapidly taking fatigue damage!</span>"
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/Process_Spacemove(movement_dir = 0)
|
||||
return 1
|
||||
|
||||
//DAMAGE and FATIGUE
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/proc/update_fatigue()
|
||||
if(!ratvar_awakens && host && host.stat == DEAD)
|
||||
death()
|
||||
return
|
||||
if(ratvar_awakens)
|
||||
speed = 0
|
||||
melee_damage_lower = 20
|
||||
melee_damage_upper = 20
|
||||
attacktext = "devastates"
|
||||
else
|
||||
switch((fatigue/fatigue_recall_threshold) * 100)
|
||||
if(0 to 10) //Bonuses to speed and damage at normal fatigue levels
|
||||
speed = 0
|
||||
melee_damage_lower = 13
|
||||
melee_damage_upper = 13
|
||||
attacktext = "viciously slashes"
|
||||
if(10 to 25)
|
||||
speed = initial(speed)
|
||||
melee_damage_lower = initial(melee_damage_lower)
|
||||
melee_damage_upper = initial(melee_damage_upper)
|
||||
attacktext = initial(attacktext)
|
||||
if(25 to 50) //Damage decrease, but not speed
|
||||
speed = initial(speed)
|
||||
melee_damage_lower = 7
|
||||
melee_damage_upper = 7
|
||||
attacktext = "lightly slashes"
|
||||
if(50 to 75) //Speed decrease
|
||||
speed = 2
|
||||
melee_damage_lower = 7
|
||||
melee_damage_upper = 7
|
||||
attacktext = "lightly slashes"
|
||||
if(75 to 99) //Massive speed decrease and weak melee attacks
|
||||
speed = 3
|
||||
melee_damage_lower = 4
|
||||
melee_damage_upper = 4
|
||||
attacktext = "weakly slashes"
|
||||
if(99 to 100) //we are at maximum fatigue, we're either useless or recalling
|
||||
if(host)
|
||||
src << "<span class='userdanger'>The fatigue becomes too much!</span>"
|
||||
src << "<span class='userdanger'>You retreat to [host] - you will have to wait before being deployed again.</span>"
|
||||
host << "<span class='userdanger'>[true_name] is too fatigued to fight - you will need to wait until they are strong enough.</span>"
|
||||
recovering = TRUE
|
||||
return_to_host()
|
||||
else
|
||||
speed = 4
|
||||
melee_damage_lower = 1
|
||||
melee_damage_upper = 1
|
||||
attacktext = "taps"
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/death(gibbed)
|
||||
emerge_from_host(0, 1)
|
||||
visible_message("<span class='warning'>[src]'s equipment clatters lifelessly to the ground as the red flames within dissipate.</span>", \
|
||||
"<span class='userdanger'>Your equipment falls away. You feel a moment of confusion before your fragile form is annihilated.</span>")
|
||||
..()
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/Stat()
|
||||
..()
|
||||
if(statpanel("Status"))
|
||||
stat(null, "Fatigue: [fatigue]/[fatigue_recall_threshold]")
|
||||
stat(null, "Current True Name: [true_name]")
|
||||
stat(null, "Host: [host ? host : "NONE"]")
|
||||
if(host)
|
||||
var/resulthealth
|
||||
resulthealth = round((abs(config.health_threshold_dead - host.health) / abs(config.health_threshold_dead - host.maxHealth)) * 100)
|
||||
stat(null, "Host Health: [resulthealth]%")
|
||||
if(ratvar_awakens)
|
||||
stat(null, "You are [recovering ? "un" : ""]able to deploy!")
|
||||
else
|
||||
if(resulthealth > 60)
|
||||
stat(null, "You are [recovering ? "unable to deploy" : "can deploy on hearing your True Name"]!")
|
||||
else
|
||||
stat(null, "You are [recovering ? "unable to deploy" : "can deploy to protect your host"]!")
|
||||
if(ratvar_awakens)
|
||||
stat(null, "Block Chance: 80%")
|
||||
stat(null, "Counter Chance: 80%")
|
||||
else
|
||||
stat(null, "Block Chance: [blockchance]%")
|
||||
stat(null, "Counter Chance: [counterchance]%")
|
||||
stat(null, "You do [melee_damage_upper] damage on melee attacks.")
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/adjustHealth(amount) //Fatigue damage
|
||||
var/fatiguedamage = adjust_fatigue(amount)
|
||||
if(amount > 0)
|
||||
combattimer = world.time + initial(combattimer)
|
||||
for(var/mob/living/L in view(2, src))
|
||||
if(istype(L.l_hand, /obj/item/weapon/nullrod) || istype(L.r_hand, /obj/item/weapon/nullrod)) //hand-held holy weapons increase the damage it takes
|
||||
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
|
||||
return ..() + fatiguedamage
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/proc/adjust_fatigue(amount) //Adds or removes the given amount of fatigue
|
||||
if(status_flags & GODMODE)
|
||||
return 0
|
||||
fatigue = Clamp(fatigue + amount, 0, fatigue_recall_threshold)
|
||||
update_fatigue()
|
||||
return amount
|
||||
|
||||
//ATTACKING, BLOCKING, and COUNTERING
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/AttackingTarget()
|
||||
if(is_in_host())
|
||||
return 0
|
||||
combattimer = world.time + initial(combattimer)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/bullet_act(obj/item/projectile/Proj)
|
||||
if(ratvar_awakens && blockOrCounter(null, Proj))
|
||||
return
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/hitby(atom/movable/AM, skipcatch, hitpush, blocked)
|
||||
if(ratvar_awakens && blockOrCounter(null, AM))
|
||||
return
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/attack_animal(mob/living/simple_animal/M)
|
||||
if(istype(M, /mob/living/simple_animal/hostile/clockwork/marauder) || !blockOrCounter(M, M))
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/attack_paw(mob/living/carbon/monkey/M)
|
||||
if(!blockOrCounter(M, M))
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/attack_alien(mob/living/carbon/alien/humanoid/M)
|
||||
if(!blockOrCounter(M, M))
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/attack_slime(mob/living/simple_animal/slime/M)
|
||||
if(!blockOrCounter(M, M))
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/attack_hand(mob/living/carbon/human/M)
|
||||
if(!blockOrCounter(M, M))
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/attackby(obj/item/I, mob/user, params)
|
||||
if(istype(I, /obj/item/weapon/nullrod) || !blockOrCounter(user, I))
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/proc/blockOrCounter(mob/target, atom/textobject)
|
||||
if(ratvar_awakens) //if ratvar has woken, we block nearly everything at a very high chance
|
||||
blockchance = 80
|
||||
counterchance = 80
|
||||
if(prob(blockchance))
|
||||
. = TRUE
|
||||
if(target)
|
||||
target.do_attack_animation(src)
|
||||
target.changeNext_move(CLICK_CD_MELEE)
|
||||
blockchance = initial(blockchance)
|
||||
playsound(src, 'sound/magic/clockwork/fellowship_armory.ogg', 10, 1, 0, 1) //clang
|
||||
visible_message("<span class='boldannounce'>[src] blocks [target && istype(textobject, /obj/item) ? "[target]'s [textobject.name]":"\the [textobject]"]!</span>", \
|
||||
"<span class='userdanger'>You block [target && istype(textobject, /obj/item) ? "[target]'s [textobject.name]":"\the [textobject]"]!</span>")
|
||||
if(target && prob(counterchance))
|
||||
counterchance = initial(counterchance)
|
||||
var/previousattacktext = attacktext
|
||||
attacktext = "counters"
|
||||
target.attack_animal(src)
|
||||
attacktext = previousattacktext
|
||||
else
|
||||
counterchance = min(counterchance + initial(counterchance), 100)
|
||||
else
|
||||
blockchance = min(blockchance + initial(blockchance), 100)
|
||||
|
||||
//COMMUNICATION and EMERGENCE
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans)
|
||||
..()
|
||||
if(findtext(message, true_name) && is_in_host()) //Called or revealed by hearing their true name
|
||||
if(speaker == host)
|
||||
emerge_from_host(1)
|
||||
else
|
||||
src << "<span class='boldannounce'>You hear your true name and partially emerge before you can stop yourself!</span>"
|
||||
host.visible_message("<span class='warning'>[host]'s skin flashes crimson!</span>", "<span class='boldannounce'>Your marauder instinctively reacts to its true name!</span>")
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/say(message, message_mode)
|
||||
if(host && (is_in_host() || message_mode == MODE_BINARY))
|
||||
marauder_comms(message)
|
||||
return 1
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/proc/marauder_comms(message)
|
||||
if(host)
|
||||
message = "<span class='sevtug'>Marauder [true_name]:</span> <span class='sevtug_small'>\"[message]\"</span>" //Processed output
|
||||
src << message
|
||||
host << message
|
||||
for(var/M in mob_list)
|
||||
if(isobserver(M))
|
||||
var/link = FOLLOW_LINK(M, src)
|
||||
M << "[link] [message]"
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/mob/living/proc/talk_with_marauder() //hosts communicate via a verb, marauders just use :b
|
||||
set name = "Linked Minds"
|
||||
set desc = "Silently communicates with your marauder."
|
||||
set category = "Clockwork"
|
||||
var/mob/living/simple_animal/hostile/clockwork/marauder/marauder
|
||||
|
||||
if(!marauder)
|
||||
for(var/mob/living/simple_animal/hostile/clockwork/marauder/C in living_mob_list)
|
||||
if(C.host == src)
|
||||
marauder = C
|
||||
if(!marauder) //Double-check afterwards
|
||||
src << "<span class='warning'>You aren't hosting any marauders!</span>"
|
||||
verbs -= /mob/living/proc/talk_with_marauder
|
||||
return 0
|
||||
var/message = stripped_input(src, "Enter a message to tell your marauder.", "Telepathy")// as null|anything
|
||||
if(!src || !message)
|
||||
return 0
|
||||
if(!marauder)
|
||||
usr << "<span class='warning'>Your marauder seems to have vanished!</span>"
|
||||
return 0
|
||||
message = "<span class='heavy_brass'>Servant [findtextEx(name, real_name) ? "[name]" : "[real_name] (as [name])"]:</span> <span class='brass'>\"[message]\"</span>" //Processed output
|
||||
src << message
|
||||
marauder << message
|
||||
for(var/M in mob_list)
|
||||
if(isobserver(M))
|
||||
var/link = FOLLOW_LINK(M, src)
|
||||
M << "[link] [message]"
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/verb/change_true_name()
|
||||
set name = "Change True Name (One-Use)"
|
||||
set desc = "Changes your true name, used to be called forth."
|
||||
set category = "Marauder"
|
||||
|
||||
verbs -= /mob/living/simple_animal/hostile/clockwork/marauder/verb/change_true_name
|
||||
var/new_name = stripped_input(usr, "Enter a new true name (10-character limit).", "Change True Name","", 11)
|
||||
if(!usr)
|
||||
return 0
|
||||
if(!new_name)
|
||||
usr << "<span class='notice'>You decide against changing your true name for now.</span>"
|
||||
verbs += /mob/living/simple_animal/hostile/clockwork/marauder/verb/change_true_name //If they decide against it, let them have another opportunity
|
||||
return 0
|
||||
true_name = new_name
|
||||
usr << "<span class='heavy_brass'>You have changed your true name to </span><span class='sevtug'>\"[new_name]\"</span><span class='heavy_brass'>!</span>"
|
||||
if(host)
|
||||
host << "<span class='heavy_brass'>Your clockwork marauder has changed their true name to </span><span class='sevtug'>\"[new_name]\"</span><span class='heavy_brass'>!</span>"
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/verb/return_to_host()
|
||||
set name = "Return to Host"
|
||||
set desc = "Recalls yourself to your host, assuming you aren't already there."
|
||||
set category = "Marauder"
|
||||
|
||||
if(is_in_host())
|
||||
return 0
|
||||
if(!host)
|
||||
src << "<span class='warning'>You don't have a host!</span>"
|
||||
verbs -= /mob/living/simple_animal/hostile/clockwork/marauder/verb/return_to_host
|
||||
return 0
|
||||
host.visible_message("<span class='warning'>[host]'s skin flashes crimson!</span>", "<span class='heavy_brass'>You feel [true_name]'s consciousness settle in your mind.</span>")
|
||||
visible_message("<span class='warning'>[src] suddenly disappears!</span>", "<span class='heavy_brass'>You return to [host].</span>")
|
||||
forceMove(host)
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/verb/try_emerge()
|
||||
set name = "Attempt to Emerge from Host"
|
||||
set desc = "Attempts to emerge from your host, likely to only work if your host is very heavily damaged."
|
||||
set category = "Marauder"
|
||||
|
||||
if(!host)
|
||||
src << "<span class='warning'>You don't have a host!</span>"
|
||||
verbs -= /mob/living/simple_animal/hostile/clockwork/marauder/verb/try_emerge
|
||||
return 0
|
||||
var/resulthealth
|
||||
resulthealth = round((abs(config.health_threshold_dead - host.health) / abs(config.health_threshold_dead - host.maxHealth)) * 100)
|
||||
if(!ratvar_awakens && host.stat != DEAD && resulthealth > 60) //if above 20 health, fails
|
||||
src << "<span class='warning'>Your host must be at 60% or less health to emerge like this!</span>"
|
||||
return
|
||||
return emerge_from_host(0)
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/proc/emerge_from_host(hostchosen, force) //Notice that this is a proc rather than a verb - marauders can NOT exit at will, but they CAN return
|
||||
if(!is_in_host())
|
||||
return 0
|
||||
if(!force && recovering)
|
||||
if(hostchosen)
|
||||
host << "<span class='heavy_brass'>[true_name] is too weak to come forth!</span>"
|
||||
else
|
||||
host << "<span class='heavy_brass'>[true_name] tries to emerge to protect you, but it's too weak!</span>"
|
||||
src << "<span class='userdanger'>You try to come forth, but you're too weak!</span>"
|
||||
return 0
|
||||
if(hostchosen) //marauder approved
|
||||
host << "<span class='heavy_brass'>Your words echo with power as [true_name] emerges from your body!</span>"
|
||||
else
|
||||
host << "<span class='heavy_brass'>[true_name] emerges from your body to protect you!</span>"
|
||||
forceMove(get_turf(host))
|
||||
visible_message("<span class='warning'>[host]'s skin glows red as [name] emerges from their body!</span>", "<span class='brass'>You exit the safety of [host]'s body!</span>")
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/marauder/proc/is_in_host() //Checks if the marauder is inside of their host
|
||||
return host && loc == host
|
||||
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/reclaimer
|
||||
name = "clockwork reclaimer"
|
||||
desc = "A tiny clockwork arachnid with a single cogwheel spinning quickly in its head. Its legs blur, too fast to be seen clearly."
|
||||
icon_state = "clockwork_reclaimer"
|
||||
health = 50
|
||||
maxHealth = 50
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 10
|
||||
attacktext = "slams into"
|
||||
attack_sound = 'sound/magic/clockwork/anima_fragment_attack.ogg'
|
||||
ventcrawler = 2
|
||||
playstyle_string = "<span class='heavy_brass'>You are a clockwork reclaimer</span><b>, a harbringer of the Justiciar's light. You can crawl through vents to move more swiftly. Your \
|
||||
goal: purge all untruths and honor Ratvar. You may alt-click a valid target to break yourself apart and convert the target to a servant of Ratvar.</b>"
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/reclaimer/New()
|
||||
..()
|
||||
if(prob(1))
|
||||
real_name = "jehovah's witness"
|
||||
name = real_name
|
||||
spawn(1)
|
||||
if(mind)
|
||||
mind.special_role = null
|
||||
add_servant_of_ratvar(src, TRUE)
|
||||
src << playstyle_string
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/reclaimer/Life()
|
||||
..()
|
||||
if(ishuman(loc))
|
||||
var/mob/living/carbon/human/L = loc
|
||||
if(L.stat || !L.client)
|
||||
disengage()
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/reclaimer/death()
|
||||
..(1)
|
||||
visible_message("<span class='warning'>[src] bursts into deadly shrapnel!</span>")
|
||||
for(var/mob/living/carbon/C in range(2, src))
|
||||
C.adjustBruteLoss(rand(3, 5))
|
||||
qdel(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/reclaimer/AltClickOn(atom/movable/A)
|
||||
if(!ishuman(A))
|
||||
return ..()
|
||||
var/mob/living/carbon/human/H = A
|
||||
if(is_servant_of_ratvar(H) || H.stat || (H.mind && !H.client))
|
||||
src << "<span class='warning'>[H] isn't a valid target! Valid targets are conscious non-servants.</span>"
|
||||
return 0
|
||||
if(get_dist(src, H) > 3)
|
||||
src << "<span class='warning'>You need to be closer to dominate [H]!</span>"
|
||||
return 0
|
||||
visible_message("<span class='warning'>[src] rockets with blinding speed towards [H]!</span>", "<span class='heavy_brass'>You leap with blinding speed towards [H]'s head!</span>")
|
||||
for(var/i = 9, i > 0, i -= 3)
|
||||
pixel_y += i
|
||||
sleep(1)
|
||||
icon_state = "[initial(icon_state)]_charging"
|
||||
while(loc != H.loc)
|
||||
if(!H)
|
||||
icon_state = initial(icon_state)
|
||||
return 0
|
||||
sleep(1)
|
||||
forceMove(get_step(src, get_dir(src, H)))
|
||||
if(H.head)
|
||||
H.visible_message("<span class='warning'>[src] tears apart [H]'s [H.name]!</span>")
|
||||
H.unEquip(H.head)
|
||||
qdel(H.head)
|
||||
H.visible_message("<span class='warning'>[src] latches onto [H]'s head and digs its claws in!</span>", "<span class='userdanger'>[src] leaps onto your head and impales its claws deep!</span>")
|
||||
add_servant_of_ratvar(H)
|
||||
H.equip_to_slot_or_del(new/obj/item/clothing/head/helmet/clockwork/reclaimer(null), slot_head)
|
||||
loc = H
|
||||
icon_state = initial(icon_state)
|
||||
status_flags += GODMODE
|
||||
src << "<span class='userdanger'>ASSIMILATION SUCCESSFUL.</span>"
|
||||
H << "<span class='userdanger'>ASSIMILATION SUCCESSFUL.</span>"
|
||||
clockwork_say(H, rot13("ASSIMILATION SUCCESSFUL."))
|
||||
if(!H.mind)
|
||||
mind.transfer_to(H)
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/reclaimer/verb/disengage()
|
||||
set name = "Disgengage From Host"
|
||||
set desc = "Jumps off of your host if you have one, freeing their mind but allowing you movement."
|
||||
set category = "Clockwork"
|
||||
|
||||
if(!ishuman(usr.loc))
|
||||
usr << "<span class='warning'>You have no host! Alt-click on a non-servant to enslave them.</span>"
|
||||
return
|
||||
var/mob/living/carbon/human/L = usr.loc
|
||||
usr.loc = get_turf(L)
|
||||
pixel_y = initial(pixel_y)
|
||||
usr.visible_message("<span class='warning'>[usr] jumps off of [L]'s head!</span>", "<span class='notice'>You disengage from your host.</span>")
|
||||
usr.status_flags -= GODMODE
|
||||
remove_servant_of_ratvar(L)
|
||||
L.unEquip(L.head)
|
||||
qdel(L.head)
|
||||
|
||||
|
||||
|
||||
/mob/living/mind_control_holder
|
||||
name = "imprisoned mind"
|
||||
desc = "A helpless mind, imprisoned in its own body."
|
||||
stat = 0
|
||||
status_flags = GODMODE
|
||||
|
||||
/mob/living/mind_control_holder/say()
|
||||
return 0
|
||||
@@ -0,0 +1,326 @@
|
||||
/obj/structure/clockwork/massive //For objects that are typically very large
|
||||
name = "massive construct"
|
||||
desc = "A very large construction."
|
||||
layer = MASSIVE_OBJ_LAYER
|
||||
density = FALSE
|
||||
burn_state = LAVA_PROOF
|
||||
|
||||
/obj/structure/clockwork/massive/New()
|
||||
..()
|
||||
poi_list += src
|
||||
|
||||
/obj/structure/clockwork/massive/Destroy()
|
||||
poi_list -= src
|
||||
return ..()
|
||||
|
||||
/obj/structure/clockwork/massive/celestial_gateway //The gateway to Reebe, from which Ratvar emerges
|
||||
name = "Gateway to the Celestial Derelict"
|
||||
desc = "A massive, thrumming rip in spacetime."
|
||||
clockwork_desc = "A portal to the Celestial Derelict. Massive and intimidating, it is the only thing that can both transport Ratvar and withstand the massive amount of energy he emits."
|
||||
health = 500
|
||||
max_health = 500
|
||||
mouse_opacity = 2
|
||||
icon = 'icons/effects/clockwork_effects.dmi'
|
||||
icon_state = "nothing"
|
||||
density = TRUE
|
||||
can_be_repaired = FALSE
|
||||
var/progress_in_seconds = 0 //Once this reaches GATEWAY_RATVAR_ARRIVAL, it's game over
|
||||
var/purpose_fulfilled = FALSE
|
||||
var/first_sound_played = FALSE
|
||||
var/second_sound_played = FALSE
|
||||
var/third_sound_played = FALSE
|
||||
var/obj/effect/clockwork/gateway_glow/glow
|
||||
var/obj/effect/countdown/clockworkgate/countdown
|
||||
|
||||
/obj/structure/clockwork/massive/celestial_gateway/New()
|
||||
..()
|
||||
glow = new(get_turf(src))
|
||||
countdown = new(src)
|
||||
countdown.start()
|
||||
SSshuttle.emergencyNoEscape = TRUE
|
||||
START_PROCESSING(SSobj, src)
|
||||
var/area/gate_area = get_area(src)
|
||||
for(var/M in mob_list)
|
||||
if(is_servant_of_ratvar(M) || isobserver(M))
|
||||
M << "<span class='large_brass'><b>A gateway to the Celestial Derelict has been created in [gate_area.map_name]!</b></span>"
|
||||
|
||||
/obj/structure/clockwork/massive/celestial_gateway/Destroy()
|
||||
SSshuttle.emergencyNoEscape = FALSE
|
||||
if(SSshuttle.emergency.mode == SHUTTLE_STRANDED)
|
||||
SSshuttle.emergency.mode = SHUTTLE_DOCKED
|
||||
SSshuttle.emergency.timer = world.time
|
||||
if(!purpose_fulfilled)
|
||||
priority_announce("Hostile enviroment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg', "Priority")
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
if(!purpose_fulfilled)
|
||||
var/area/gate_area = get_area(src)
|
||||
for(var/M in mob_list)
|
||||
if(is_servant_of_ratvar(M) || isobserver(M))
|
||||
M << "<span class='large_brass'><b>A gateway to the Celestial Derelict has fallen at [gate_area.map_name]!</b></span>"
|
||||
world << sound(null, 0, channel = 8)
|
||||
qdel(glow)
|
||||
glow = null
|
||||
qdel(countdown)
|
||||
countdown = null
|
||||
return ..()
|
||||
|
||||
/obj/structure/clockwork/massive/celestial_gateway/destroyed()
|
||||
countdown.stop()
|
||||
visible_message("<span class='userdanger'>The [src] begins to pulse uncontrollably... you might want to run!</span>")
|
||||
world << sound('sound/effects/clockcult_gateway_disrupted.ogg', 0, channel = 8, volume = 50)
|
||||
make_glow()
|
||||
glow.icon_state = "clockwork_gateway_disrupted"
|
||||
takes_damage = FALSE
|
||||
sleep(27)
|
||||
explosion(src, 1, 3, 8, 8)
|
||||
qdel(src)
|
||||
return 1
|
||||
|
||||
/obj/structure/clockwork/massive/celestial_gateway/proc/make_glow()
|
||||
if(!glow)
|
||||
glow = new(get_turf(src))
|
||||
glow.linked_gate = src
|
||||
|
||||
/obj/structure/clockwork/massive/celestial_gateway/ex_act(severity)
|
||||
return 0 //Nice try, Toxins!
|
||||
|
||||
/obj/structure/clockwork/massive/celestial_gateway/process()
|
||||
if(!progress_in_seconds || prob(5))
|
||||
for(var/M in mob_list)
|
||||
M << "<span class='warning'><b>You hear otherworldly sounds from the [dir2text(get_dir(get_turf(M), get_turf(src)))]...</span>"
|
||||
if(!health)
|
||||
return 0
|
||||
progress_in_seconds += GATEWAY_SUMMON_RATE
|
||||
switch(progress_in_seconds)
|
||||
if(-INFINITY to GATEWAY_REEBE_FOUND)
|
||||
if(!first_sound_played)
|
||||
world << sound('sound/effects/clockcult_gateway_charging.ogg', 1, channel = 8, volume = 50)
|
||||
first_sound_played = TRUE
|
||||
make_glow()
|
||||
glow.icon_state = "clockwork_gateway_charging"
|
||||
if(GATEWAY_REEBE_FOUND to GATEWAY_RATVAR_COMING)
|
||||
if(!second_sound_played)
|
||||
world << sound('sound/effects/clockcult_gateway_active.ogg', 1, channel = 8, volume = 50)
|
||||
second_sound_played = TRUE
|
||||
make_glow()
|
||||
glow.icon_state = "clockwork_gateway_active"
|
||||
if(GATEWAY_RATVAR_COMING to GATEWAY_RATVAR_ARRIVAL)
|
||||
if(!third_sound_played)
|
||||
world << sound('sound/effects/clockcult_gateway_closing.ogg', 1, channel = 8, volume = 50)
|
||||
third_sound_played = TRUE
|
||||
make_glow()
|
||||
glow.icon_state = "clockwork_gateway_closing"
|
||||
if(GATEWAY_RATVAR_ARRIVAL to INFINITY)
|
||||
if(!purpose_fulfilled)
|
||||
countdown.stop()
|
||||
takes_damage = FALSE
|
||||
purpose_fulfilled = TRUE
|
||||
make_glow()
|
||||
animate(glow, transform = matrix() * 1.5, alpha = 255, time = 126)
|
||||
world << sound('sound/effects/ratvar_rises.ogg', 0, channel = 8) //End the sounds
|
||||
sleep(131)
|
||||
make_glow()
|
||||
animate(glow, transform = matrix() * 3, alpha = 0, time = 5)
|
||||
sleep(5)
|
||||
new/obj/structure/clockwork/massive/ratvar(get_turf(src))
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/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))
|
||||
var/arrival_text = "IMMINENT"
|
||||
if(GATEWAY_RATVAR_ARRIVAL - progress_in_seconds > 0)
|
||||
arrival_text = "[round(max((GATEWAY_RATVAR_ARRIVAL - progress_in_seconds) / (GATEWAY_SUMMON_RATE * 0.5), 0), 1)]"
|
||||
user << "<span class='big'><b>Seconds until Ratvar's arrival:</b> [arrival_text]s</span>"
|
||||
switch(progress_in_seconds)
|
||||
if(-INFINITY to GATEWAY_REEBE_FOUND)
|
||||
user << "<span class='heavy_brass'>It's still opening.</span>"
|
||||
if(GATEWAY_REEBE_FOUND to GATEWAY_RATVAR_COMING)
|
||||
user << "<span class='heavy_brass'>It's reached the Celestial Derelict and is drawing power from it.</span>"
|
||||
if(GATEWAY_RATVAR_COMING to INFINITY)
|
||||
user << "<span class='heavy_brass'>Ratvar is coming through the gateway!</span>"
|
||||
else
|
||||
switch(progress_in_seconds)
|
||||
if(-INFINITY to GATEWAY_REEBE_FOUND)
|
||||
user << "<span class='warning'>It's a swirling mass of blackness.</span>"
|
||||
if(GATEWAY_REEBE_FOUND to GATEWAY_RATVAR_COMING)
|
||||
user << "<span class='warning'>It seems to be leading somewhere.</span>"
|
||||
if(GATEWAY_RATVAR_COMING to INFINITY)
|
||||
user << "<span class='warning'><b>Something is coming through!</b></span>"
|
||||
|
||||
/obj/effect/clockwork/gateway_glow //the actual appearance of the Gateway to the Celestial Derelict; an object so the edges of the gate can be clicked through.
|
||||
icon = 'icons/effects/96x96.dmi'
|
||||
icon_state = "clockwork_gateway_charging"
|
||||
pixel_x = -32
|
||||
pixel_y = -32
|
||||
mouse_opacity = 0
|
||||
layer = MASSIVE_OBJ_LAYER
|
||||
var/obj/structure/clockwork/massive/celestial_gateway/linked_gate
|
||||
|
||||
/obj/effect/clockwork/gateway_glow/Destroy()
|
||||
if(linked_gate)
|
||||
linked_gate.glow = null
|
||||
linked_gate = null
|
||||
return ..()
|
||||
|
||||
/obj/effect/clockwork/gateway_glow/examine(mob/user)
|
||||
if(linked_gate)
|
||||
linked_gate.examine(user)
|
||||
|
||||
/obj/effect/clockwork/gateway_glow/ex_act(severity, target)
|
||||
return FALSE
|
||||
|
||||
|
||||
/obj/structure/clockwork/massive/ratvar
|
||||
name = "Ratvar, the Clockwork Justiciar"
|
||||
desc = "<span class='userdanger'>What is what is what are what real what is all a lie all a lie it's all a lie why how can what is</span>"
|
||||
clockwork_desc = "<span class='large_brass'><b><i>Ratvar, the Clockwork Justiciar, your master eternal.</i></b></span>"
|
||||
icon = 'icons/effects/512x512.dmi'
|
||||
icon_state = "ratvar"
|
||||
pixel_x = -235
|
||||
pixel_y = -248
|
||||
takes_damage = FALSE
|
||||
var/atom/prey //Whatever Ratvar is chasing
|
||||
var/clashing = FALSE //If Ratvar is FUCKING FIGHTING WITH NAR-SIE
|
||||
var/proselytize_range = 10
|
||||
|
||||
/obj/structure/clockwork/massive/ratvar/New()
|
||||
..()
|
||||
ratvar_awakens = TRUE
|
||||
for(var/obj/item/clockwork/ratvarian_spear/R in all_clockwork_objects)
|
||||
R.update_force()
|
||||
START_PROCESSING(SSobj, src)
|
||||
world << "<span class='heavy_brass'><font size=6>\"BAPR NTNVA ZL YVTUG FUNYY FUVAR NPEBFF GUVF CNGURGVP ERNYZ!!\"</font></span>"
|
||||
world << 'sound/effects/ratvar_reveal.ogg'
|
||||
var/image/alert_overlay = image('icons/effects/clockwork_effects.dmi', "ratvar_alert")
|
||||
var/area/A = get_area(src)
|
||||
notify_ghosts("The Justiciar's light calls to you! Reach out to Ratvar in [A.name] to be granted a shell to spread his glory!", null, source = src, alert_overlay = alert_overlay)
|
||||
addtimer(SSshuttle.emergency, "request", 50, FALSE, null, 0.3)
|
||||
|
||||
|
||||
/obj/structure/clockwork/massive/ratvar/Destroy()
|
||||
ratvar_awakens = FALSE
|
||||
for(var/obj/item/clockwork/ratvarian_spear/R in all_clockwork_objects)
|
||||
R.update_force()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
world << "<span class='heavy_brass'><font size=6>\"NO! I will not... be...</font> <font size=5>banished...</font> <font size=4>again...\"</font></span>"
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/structure/clockwork/massive/ratvar/attack_ghost(mob/dead/observer/O)
|
||||
var/alertresult = alert(O, "Embrace the Justiciar's light? You can no longer be cloned!",,"Cogscarab", "Reclaimer", "No")
|
||||
if(alertresult == "No" || !O)
|
||||
return 0
|
||||
var/mob/living/simple_animal/R
|
||||
if(alertresult == "Cogscarab")
|
||||
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>")
|
||||
else
|
||||
R = new/mob/living/simple_animal/hostile/clockwork/reclaimer(get_turf(src))
|
||||
R.visible_message("<span class='heavy_brass'>[R] forms, and it emits a faint hum!</span>")
|
||||
R.key = O.key
|
||||
|
||||
|
||||
/obj/structure/clockwork/massive/ratvar/Bump(atom/A)
|
||||
forceMove(get_turf(A))
|
||||
A.ratvar_act()
|
||||
|
||||
|
||||
/obj/structure/clockwork/massive/ratvar/Process_Spacemove()
|
||||
return clashing
|
||||
|
||||
|
||||
/obj/structure/clockwork/massive/ratvar/process()
|
||||
if(clashing) //I'm a bit occupied right now, thanks
|
||||
return
|
||||
for(var/atom/A in range(proselytize_range, src))
|
||||
A.ratvar_act()
|
||||
var/dir_to_step_in = pick(cardinal)
|
||||
if(!prey)
|
||||
for(var/obj/singularity/narsie/N in poi_list)
|
||||
if(N.z == z)
|
||||
prey = N
|
||||
break
|
||||
if(!prey) //In case there's a Nar-Sie
|
||||
var/list/meals = list()
|
||||
for(var/mob/living/L in living_mob_list)
|
||||
if(L.z == z && !is_servant_of_ratvar(L) && L.mind)
|
||||
meals += L
|
||||
if(meals.len)
|
||||
prey = pick(meals)
|
||||
prey << "<span class='heavy_brass'><font size=5>\"You will do.\"</font></span>\n\
|
||||
<span class='userdanger'>Something very large and very malevolent begins lumbering its way towards you...</span>"
|
||||
prey << 'sound/effects/ratvar_reveal.ogg'
|
||||
else
|
||||
if(prob(10) || is_servant_of_ratvar(prey) || prey.z != z)
|
||||
prey << "<span class='heavy_brass'><font size=5>\"How dull. Leave me.\"</font></span>\n\
|
||||
<span class='userdanger'>You feel tremendous relief as a set of horrible eyes loses sight of you...</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/clockwork/massive/ratvar/narsie_act()
|
||||
if(clashing)
|
||||
return 0
|
||||
clashing = TRUE
|
||||
world << "<span class='heavy_brass'><font size=5>\"[pick("BLOOD GOD!!!", "NAR-SIE!!!", "AT LAST, YOUR TIME HAS COME!")]\"</font></span>"
|
||||
world << "<span class='cult'><font size=5>\"<b>Ratvar?! How?!</b>\"</font></span>"
|
||||
for(var/obj/singularity/narsie/N in range(15, src))
|
||||
if(N.clashing)
|
||||
continue
|
||||
N.clashing = TRUE
|
||||
clash_of_the_titans(N) //IT'S TIME FOR THE BATTLE OF THE AGES
|
||||
break
|
||||
return 1
|
||||
|
||||
/obj/structure/clockwork/massive/ratvar/proc/clash_of_the_titans(obj/singularity/narsie/narsie)
|
||||
var/winner = "Undeclared"
|
||||
var/base_victory_chance = 0
|
||||
while(TRUE)
|
||||
world << 'sound/magic/clockwork/ratvar_attack.ogg'
|
||||
sleep(5.2)
|
||||
for(var/mob/M in mob_list)
|
||||
if(M.client)
|
||||
M.client.color = rgb(150, 100, 0)
|
||||
spawn(1)
|
||||
M.client.color = initial(M.client.color)
|
||||
shake_camera(M, 4, 3)
|
||||
var/r_success_modifier = (ticker.mode.servants_of_ratvar.len * 2) //2% for each cultist
|
||||
var/n_success_modifier = (ticker.mode.cult.len * 2)
|
||||
for(var/mob/living/simple_animal/hostile/construct/harvester/C in player_list)
|
||||
n_success_modifier += 2
|
||||
if(prob(base_victory_chance + r_success_modifier))
|
||||
winner = "Ratvar"
|
||||
break
|
||||
sleep(rand(2,5))
|
||||
world << 'sound/magic/clockwork/narsie_attack.ogg'
|
||||
sleep(7.4)
|
||||
for(var/mob/M in mob_list)
|
||||
if(M.client)
|
||||
M.client.color = rgb(200, 0, 0)
|
||||
spawn(1)
|
||||
M.client.color = initial(M.client.color)
|
||||
shake_camera(M, 4, 3)
|
||||
if(prob(base_victory_chance + n_success_modifier))
|
||||
winner = "Nar-Sie"
|
||||
break
|
||||
base_victory_chance++ //The clash has a higher chance of resolving each time both gods attack one another
|
||||
switch(winner)
|
||||
if("Ratvar")
|
||||
world << "<span class='heavy_brass'><font size=5>\"[pick("DIE! DIE! DIE!", "REEEEEEEEE!", "FILTH!!!", "SUFFER!!!", "EBG SBE PRAGHEVRF NF V UNIR!!")]\"</font></span>" //nar-sie get out
|
||||
world << "<span class='cult'><font size=5>\"<b>[pick("Nooooo...", "Not die. To y-", "Die. Ratv-", "Sas tyen re-")]\"</b></font></span>"
|
||||
world << 'sound/magic/clockwork/anima_fragment_attack.ogg'
|
||||
world << 'sound/magic/demon_dies.ogg'
|
||||
clashing = FALSE
|
||||
qdel(narsie)
|
||||
return 1
|
||||
if("Nar-Sie")
|
||||
world << "<span class='cult'><font size=5>\"<b>[pick("Ha.", "Ra'sha fonn dest.", "You fool. To come here.")]</b>\"</font></span>" //Broken English
|
||||
world << 'sound/magic/demon_attack1.ogg'
|
||||
world << 'sound/magic/clockwork/anima_fragment_death.ogg'
|
||||
narsie.clashing = FALSE
|
||||
qdel(src)
|
||||
return 1
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,955 @@
|
||||
//////////////////////////
|
||||
// CLOCKWORK STRUCTURES //
|
||||
//////////////////////////
|
||||
|
||||
/obj/structure/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"
|
||||
anchored = 1
|
||||
density = 1
|
||||
opacity = 0
|
||||
layer = BELOW_OBJ_LAYER
|
||||
var/max_health = 100 //All clockwork structures have health that can be removed via attacks
|
||||
var/health = 100
|
||||
var/repair_amount = 5 //how much a proselytizer can repair each cycle
|
||||
var/can_be_repaired = TRUE //if a proselytizer can repair it at all
|
||||
var/takes_damage = TRUE //If the structure can be damaged
|
||||
var/break_message = "<span class='warning'>The frog isn't a meme after all!</span>" //The message shown when a structure breaks
|
||||
var/break_sound = 'sound/magic/clockwork/anima_fragment_death.ogg' //The sound played when a structure breaks
|
||||
var/list/debris = list(/obj/item/clockwork/alloy_shards) //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
|
||||
|
||||
/obj/structure/clockwork/New()
|
||||
..()
|
||||
clockwork_construction_value += construction_value
|
||||
all_clockwork_objects += src
|
||||
|
||||
/obj/structure/clockwork/Destroy()
|
||||
clockwork_construction_value -= construction_value
|
||||
all_clockwork_objects -= src
|
||||
return ..()
|
||||
|
||||
/obj/structure/clockwork/proc/destroyed()
|
||||
if(!takes_damage)
|
||||
return 0
|
||||
for(var/I in debris)
|
||||
new I (get_turf(src))
|
||||
visible_message(break_message)
|
||||
playsound(src, break_sound, 50, 1)
|
||||
qdel(src)
|
||||
return 1
|
||||
|
||||
/obj/structure/clockwork/burn()
|
||||
SSobj.burning -= src
|
||||
if(takes_damage)
|
||||
playsound(src, 'sound/items/Welder.ogg', 100, 1)
|
||||
visible_message("<span class='warning'>[src] is warped by the heat!</span>")
|
||||
take_damage(rand(50, 100), BURN)
|
||||
|
||||
/obj/structure/clockwork/proc/take_damage(amount, damage_type)
|
||||
if(!amount || !damage_type || !damage_type in list(BRUTE, BURN))
|
||||
return 0
|
||||
if(takes_damage)
|
||||
health = max(0, health - amount)
|
||||
if(!health)
|
||||
destroyed()
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/structure/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)
|
||||
|
||||
/obj/structure/clockwork/ex_act(severity)
|
||||
var/damage = 0
|
||||
switch(severity)
|
||||
if(1)
|
||||
damage = max_health //100% max health lost
|
||||
if(2)
|
||||
damage = max_health * rand(0.5, 0.7) //50-70% max health lost
|
||||
if(3)
|
||||
damage = max_health * rand(0.1, 0.3) //10-30% max health lost
|
||||
if(damage)
|
||||
take_damage(damage, BRUTE)
|
||||
|
||||
/obj/structure/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(takes_damage)
|
||||
var/servant_message = "It is at <b>[health]/[max_health]</b> integrity"
|
||||
var/other_message = "It seems pristine and undamaged"
|
||||
var/heavily_damaged = FALSE
|
||||
var/healthpercent = (health/max_health) * 100
|
||||
if(healthpercent >= 100)
|
||||
other_message = "It seems pristine and undamaged"
|
||||
else if(healthpercent >= 50)
|
||||
other_message = "It looks slightly dented"
|
||||
else if(healthpercent >= 25)
|
||||
other_message = "It appears heavily damaged"
|
||||
heavily_damaged = TRUE
|
||||
else if(healthpercent >= 0)
|
||||
other_message = "It's falling apart"
|
||||
heavily_damaged = TRUE
|
||||
user << "<span class='[heavily_damaged ? "alloy":"brass"]'>[can_see_clockwork ? "[servant_message]":"[other_message]"][heavily_damaged ? "!":"."]</span>"
|
||||
|
||||
/obj/structure/clockwork/bullet_act(obj/item/projectile/P)
|
||||
. = ..()
|
||||
visible_message("<span class='danger'>[src] is hit by \a [P]!</span>")
|
||||
playsound(src, P.hitsound, 50, 1)
|
||||
take_damage(P.damage, P.damage_type)
|
||||
|
||||
/obj/structure/clockwork/proc/attack_generic(mob/user, damage = 0, damage_type = BRUTE) //used by attack_alien, attack_animal, and attack_slime
|
||||
user.do_attack_animation(src)
|
||||
user.changeNext_move(CLICK_CD_MELEE)
|
||||
user.visible_message("<span class='danger'>[user] smashes into [src]!</span>")
|
||||
take_damage(damage, damage_type)
|
||||
|
||||
/obj/structure/clockwork/attack_alien(mob/living/user)
|
||||
playsound(src, 'sound/weapons/bladeslice.ogg', 50, 1)
|
||||
attack_generic(user, 15)
|
||||
|
||||
/obj/structure/clockwork/attack_animal(mob/living/simple_animal/M)
|
||||
if(!M.melee_damage_upper)
|
||||
return
|
||||
playsound(src, 'sound/weapons/Genhit.ogg', 50, 1)
|
||||
attack_generic(M, M.melee_damage_upper, M.melee_damage_type)
|
||||
|
||||
/obj/structure/clockwork/attack_slime(mob/living/simple_animal/slime/user)
|
||||
if(!user.is_adult)
|
||||
return
|
||||
playsound(src, 'sound/weapons/Genhit.ogg', 50, 1)
|
||||
attack_generic(user, rand(10, 15))
|
||||
|
||||
/obj/structure/clockwork/attacked_by(obj/item/I, mob/living/user)
|
||||
. = ..()
|
||||
if(I.force && takes_damage)
|
||||
playsound(src, I.hitsound, 50, 1)
|
||||
take_damage(I.force, I.damtype)
|
||||
|
||||
/obj/structure/clockwork/mech_melee_attack(obj/mecha/M)
|
||||
if(..())
|
||||
playsound(src, 'sound/weapons/punch4.ogg', 50, 1)
|
||||
take_damage(M.force, M.damtype)
|
||||
|
||||
/obj/structure/clockwork/cache //Tinkerer's cache: Stores components for later use.
|
||||
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"
|
||||
construction_value = 10
|
||||
break_message = "<span class='warning'>The cache's fire winks out before it falls in on itself!</span>"
|
||||
max_health = 80
|
||||
health = 80
|
||||
var/wall_generation_cooldown
|
||||
var/wall_found = FALSE //if we've found a wall and finished our windup delay
|
||||
|
||||
/obj/structure/clockwork/cache/New()
|
||||
..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
clockwork_caches++
|
||||
SetLuminosity(2,1)
|
||||
|
||||
/obj/structure/clockwork/cache/Destroy()
|
||||
clockwork_caches--
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/structure/clockwork/cache/destroyed()
|
||||
if(takes_damage)
|
||||
for(var/I in src)
|
||||
var/atom/movable/A = I
|
||||
A.forceMove(get_turf(src)) //drop any daemons we have
|
||||
return ..()
|
||||
|
||||
/obj/structure/clockwork/cache/process()
|
||||
for(var/turf/closed/wall/clockwork/C in orange(1, src))
|
||||
if(!wall_found)
|
||||
wall_found = TRUE
|
||||
wall_generation_cooldown = world.time + CACHE_PRODUCTION_TIME
|
||||
visible_message("<span class='warning'>[src] starts to whirr in the presence of [C]...</span>")
|
||||
break
|
||||
if(wall_generation_cooldown <= world.time)
|
||||
wall_generation_cooldown = world.time + CACHE_PRODUCTION_TIME
|
||||
generate_cache_component()
|
||||
playsound(C, 'sound/magic/clockwork/fellowship_armory.ogg', rand(15, 20), 1, -3, 1, 1)
|
||||
visible_message("<span class='warning'>Something clunks around inside of [src]...</span>")
|
||||
break
|
||||
|
||||
/obj/structure/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
|
||||
clockwork_component_cache[C.component_id]++
|
||||
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
|
||||
clockwork_component_cache["belligerent_eye"] += S.stored_components["belligerent_eye"]
|
||||
clockwork_component_cache["vanguard_cogwheel"] += S.stored_components["vanguard_cogwheel"]
|
||||
clockwork_component_cache["guvax_capacitor"] += S.stored_components["guvax_capacitor"]
|
||||
clockwork_component_cache["replicant_alloy"] += S.stored_components["replicant_alloy"]
|
||||
clockwork_component_cache["hierophant_ansible"] += S.stored_components["hierophant_ansible"]
|
||||
S.stored_components["belligerent_eye"] = 0
|
||||
S.stored_components["vanguard_cogwheel"] = 0
|
||||
S.stored_components["guvax_capacitor"] = 0
|
||||
S.stored_components["replicant_alloy"] = 0
|
||||
S.stored_components["hierophant_ansible"] = 0
|
||||
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 if(istype(I, /obj/item/clockwork/daemon_shell))
|
||||
var/component_type
|
||||
switch(alert(user, "Will this daemon produce a specific type of component or produce randomly?.", , "Specific Type", "Random Component"))
|
||||
if("Specific Type")
|
||||
switch(input(user, "Choose a component type.", name) as null|anything in list("Belligerent Eyes", "Vanguard Cogwheels", "Guvax Capacitors", "Replicant Alloys", "Hierophant Ansibles"))
|
||||
if("Belligerent Eyes")
|
||||
component_type = "belligerent_eye"
|
||||
if("Vanguard Cogwheels")
|
||||
component_type = "vanguard_cogwheel"
|
||||
if("Guvax Capacitors")
|
||||
component_type = "guvax_capacitor"
|
||||
if("Replicant Alloys")
|
||||
component_type = "replicant_alloy"
|
||||
if("Hierophant Ansibles")
|
||||
component_type = "hierophant_ansibles"
|
||||
if(!user || !user.canUseTopic(src) || !user.canUseTopic(I))
|
||||
return 0
|
||||
var/obj/item/clockwork/tinkerers_daemon/D = new(src)
|
||||
D.cache = src
|
||||
D.specific_component = component_type
|
||||
user.visible_message("<span class='notice'>[user] spins the cogwheel on [I] and puts it into [src].</span>", \
|
||||
"<span class='notice'>You activate the daemon and put it into [src]. It will now produce a component every twenty seconds.</span>")
|
||||
user.drop_item()
|
||||
qdel(I)
|
||||
return 1
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/structure/clockwork/cache/attack_hand(mob/user)
|
||||
if(!is_servant_of_ratvar(user))
|
||||
return 0
|
||||
var/list/possible_components = list()
|
||||
if(clockwork_component_cache["belligerent_eye"])
|
||||
possible_components += "Belligerent Eye"
|
||||
if(clockwork_component_cache["vanguard_cogwheel"])
|
||||
possible_components += "Vanguard Cogwheel"
|
||||
if(clockwork_component_cache["guvax_capacitor"])
|
||||
possible_components += "Guvax Capacitor"
|
||||
if(clockwork_component_cache["replicant_alloy"])
|
||||
possible_components += "Replicant Alloy"
|
||||
if(clockwork_component_cache["hierophant_ansible"])
|
||||
possible_components += "Hierophant Ansible"
|
||||
if(!possible_components.len)
|
||||
user << "<span class='warning'>[src] is empty!</span>"
|
||||
return 0
|
||||
var/component_to_withdraw = input(user, "Choose a component to withdraw.", name) as null|anything in possible_components
|
||||
if(!user || !user.canUseTopic(src) || !component_to_withdraw)
|
||||
return 0
|
||||
var/obj/item/clockwork/component/the_component
|
||||
switch(component_to_withdraw)
|
||||
if("Belligerent Eye")
|
||||
if(clockwork_component_cache["belligerent_eye"])
|
||||
the_component = new/obj/item/clockwork/component/belligerent_eye(get_turf(src))
|
||||
clockwork_component_cache["belligerent_eye"]--
|
||||
if("Vanguard Cogwheel")
|
||||
if(clockwork_component_cache["vanguard_cogwheel"])
|
||||
the_component = new/obj/item/clockwork/component/vanguard_cogwheel(get_turf(src))
|
||||
clockwork_component_cache["vanguard_cogwheel"]--
|
||||
if("Guvax Capacitor")
|
||||
if(clockwork_component_cache["guvax_capacitor"])
|
||||
the_component = new/obj/item/clockwork/component/guvax_capacitor(get_turf(src))
|
||||
clockwork_component_cache["guvax_capacitor"]--
|
||||
if("Replicant Alloy")
|
||||
if(clockwork_component_cache["replicant_alloy"])
|
||||
the_component = new/obj/item/clockwork/component/replicant_alloy(get_turf(src))
|
||||
clockwork_component_cache["replicant_alloy"]--
|
||||
if("Hierophant Ansible")
|
||||
if(clockwork_component_cache["hierophant_ansible"])
|
||||
the_component = new/obj/item/clockwork/component/hierophant_ansible(get_turf(src))
|
||||
clockwork_component_cache["hierophant_ansible"]--
|
||||
if(the_component)
|
||||
user.visible_message("<span class='notice'>[user] withdraws [the_component] from [src].</span>", "<span class='notice'>You withdraw [the_component] from [src].</span>")
|
||||
user.put_in_hands(the_component)
|
||||
return 1
|
||||
|
||||
/obj/structure/clockwork/cache/examine(mob/user)
|
||||
..()
|
||||
if(is_servant_of_ratvar(user) || isobserver(user))
|
||||
user << "<b>Stored components:</b>"
|
||||
user << "<span class='neovgre_small'><i>Belligerent Eyes:</i> [clockwork_component_cache["belligerent_eye"]]</span>"
|
||||
user << "<span class='inathneq_small'><i>Vanguard Cogwheels:</i> [clockwork_component_cache["vanguard_cogwheel"]]</span>"
|
||||
user << "<span class='sevtug_small'><i>Guvax Capacitors:</i> [clockwork_component_cache["guvax_capacitor"]]</span>"
|
||||
user << "<span class='nezbere_small'><i>Replicant Alloys:</i> [clockwork_component_cache["replicant_alloy"]]</span>"
|
||||
user << "<span class='nzcrentr_small'><i>Hierophant Ansibles:</i> [clockwork_component_cache["hierophant_ansible"]]</span>"
|
||||
|
||||
|
||||
/obj/structure/clockwork/ocular_warden //Ocular warden: Low-damage, low-range turret. Deals constant damage to whoever it makes eye contact with.
|
||||
name = "ocular warden"
|
||||
desc = "A large brass eye with tendrils trailing below it and a wide red iris."
|
||||
clockwork_desc = "A stalwart turret that will deal sustained damage to any non-faithful it sees."
|
||||
icon_state = "ocular_warden"
|
||||
health = 25
|
||||
max_health = 25
|
||||
construction_value = 15
|
||||
layer = HIGH_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)
|
||||
burn_state = LAVA_PROOF
|
||||
var/damage_per_tick = 3
|
||||
var/sight_range = 3
|
||||
var/mob/living/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/clockwork/ocular_warden/New()
|
||||
..()
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
|
||||
/obj/structure/clockwork/ocular_warden/Destroy()
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
return ..()
|
||||
|
||||
/obj/structure/clockwork/ocular_warden/examine(mob/user)
|
||||
..()
|
||||
user << "<span class='brass'>[target ? "<b>It's fixated on [target]!</b>" : "Its gaze is wandering aimlessly."]</span>"
|
||||
|
||||
/obj/structure/clockwork/ocular_warden/process()
|
||||
var/list/validtargets = acquire_nearby_targets()
|
||||
if(ratvar_awakens && (damage_per_tick == initial(damage_per_tick) || sight_range == initial(sight_range))) //Massive buff if Ratvar has returned
|
||||
damage_per_tick = 10
|
||||
sight_range = 5
|
||||
if(target)
|
||||
if(!(target in validtargets))
|
||||
lose_target()
|
||||
else
|
||||
if(!target.null_rod_check())
|
||||
target.adjustFireLoss(!iscultist(target) ? damage_per_tick : damage_per_tick * 2) //Nar-Sian cultists take additional damage
|
||||
if(ratvar_awakens && target)
|
||||
target.adjust_fire_stacks(damage_per_tick)
|
||||
target.IgniteMob()
|
||||
setDir(get_dir(get_turf(src), get_turf(target)))
|
||||
if(!target)
|
||||
if(validtargets.len)
|
||||
target = pick(validtargets)
|
||||
visible_message("<span class='warning'>[src] swivels to face [target]!</span>")
|
||||
target << "<span class='heavy_brass'>\"I SEE YOU!\"</span>\n<span class='userdanger'>[src]'s gaze [ratvar_awakens ? "melts you alive" : "burns you"]!</span>"
|
||||
if(target.null_rod_check() && !ratvar_awakens)
|
||||
target << "<span class='warning'>Your artifact glows hotly against you, protecting you from the warden's gaze!</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(cardinal))//Random rotation
|
||||
|
||||
/obj/structure/clockwork/ocular_warden/proc/acquire_nearby_targets()
|
||||
. = list()
|
||||
for(var/mob/living/L in viewers(sight_range, src)) //Doesn't attack the blind
|
||||
if(!is_servant_of_ratvar(L) && !L.stat && L.mind && !(L.disabilities & BLIND) && !L.null_rod_check())
|
||||
. += L
|
||||
|
||||
/obj/structure/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/clockwork/shell
|
||||
construction_value = 0
|
||||
anchored = 0
|
||||
density = 0
|
||||
takes_damage = FALSE
|
||||
burn_state = LAVA_PROOF
|
||||
var/mobtype = /mob/living/simple_animal/hostile/clockwork
|
||||
var/spawn_message = " is an error and you should yell at whoever spawned this shell."
|
||||
|
||||
/obj/structure/clockwork/shell/attackby(obj/item/I, mob/living/user, params)
|
||||
if(istype(I, /obj/item/device/mmi/posibrain/soul_vessel))
|
||||
if(!is_servant_of_ratvar(user))
|
||||
..()
|
||||
return 0
|
||||
var/obj/item/device/mmi/posibrain/soul_vessel/S = I
|
||||
if(!S.brainmob)
|
||||
user << "<span class='warning'>[S] hasn't trapped a spirit! Turn it on first.</span>"
|
||||
return 0
|
||||
if(S.brainmob && (!S.brainmob.client || !S.brainmob.mind))
|
||||
user << "<span class='warning'>[S]'s trapped spirit appears inactive!</span>"
|
||||
return 0
|
||||
user.visible_message("<span class='notice'>[user] places [S] in [src], where it fuses to the shell.</span>", "<span class='brass'>You place [S] in [src], fusing it to the shell.</span>")
|
||||
var/mob/living/simple_animal/A = new mobtype(get_turf(src))
|
||||
A.visible_message("<span class='brass'>[src][spawn_message]</span>")
|
||||
S.brainmob.mind.transfer_to(A)
|
||||
add_servant_of_ratvar(A, TRUE)
|
||||
user.drop_item()
|
||||
qdel(S)
|
||||
qdel(src)
|
||||
return 1
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/structure/clockwork/shell/cogscarab
|
||||
name = "cogscarab shell"
|
||||
desc = "A small brass shell with a cube-shaped receptable in its center. It gives off an aura of obsessive perfectionism."
|
||||
clockwork_desc = "A dormant receptable that, when powered with a soul vessel, will become a weak construct with an inbuilt proselytizer."
|
||||
icon_state = "clockdrone_shell"
|
||||
mobtype = /mob/living/simple_animal/drone/cogscarab
|
||||
spawn_message = "'s eyes blink open, glowing bright red."
|
||||
|
||||
/obj/structure/clockwork/shell/fragment //Anima fragment: Useless on its own, but can accept an active soul vessel to create a powerful construct.
|
||||
name = "fragment shell"
|
||||
desc = "A massive brass shell with a small cube-shaped receptable in its center. It gives off an aura of contained power."
|
||||
clockwork_desc = "A dormant receptable that, when powered with a soul vessel, will become a powerful construct."
|
||||
icon_state = "anime_fragment"
|
||||
mobtype = /mob/living/simple_animal/hostile/clockwork/fragment
|
||||
spawn_message = " whirs and rises from the ground on a flickering jet of reddish fire."
|
||||
|
||||
|
||||
/obj/structure/clockwork/wall_gear
|
||||
name = "massive gear"
|
||||
icon_state = "wall_gear"
|
||||
climbable = TRUE
|
||||
max_health = 50
|
||||
health = 50
|
||||
desc = "A massive brass gear. You could probably secure or unsecure it with a wrench, or just climb over it."
|
||||
clockwork_desc = "A massive brass gear. You could probably secure or unsecure it with a wrench, just climb over it, or proselytize it into replicant alloy."
|
||||
break_message = "<span class='warning'>The gear breaks apart into shards of alloy!</span>"
|
||||
debris = list(/obj/item/clockwork/alloy_shards)
|
||||
|
||||
/obj/structure/clockwork/wall_gear/attackby(obj/item/I, mob/user, params)
|
||||
if(istype(I, /obj/item/weapon/wrench))
|
||||
default_unfasten_wrench(user, I, 10)
|
||||
return 1
|
||||
return ..()
|
||||
|
||||
/obj/structure/clockwork/wall_gear/examine(mob/user)
|
||||
..()
|
||||
user << "<span class='notice'>[src] is [anchored ? "secured to the floor":"mobile, and not secured"].</span>"
|
||||
|
||||
///////////////////////
|
||||
// CLOCKWORK EFFECTS //
|
||||
///////////////////////
|
||||
|
||||
/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 = 1
|
||||
density = 0
|
||||
opacity = 0
|
||||
burn_state = LAVA_PROOF
|
||||
|
||||
/obj/effect/clockwork/New()
|
||||
..()
|
||||
all_clockwork_objects += src
|
||||
|
||||
/obj/effect/clockwork/Destroy()
|
||||
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)
|
||||
|
||||
/obj/effect/clockwork/judicial_marker //Judicial marker: Created by the judicial visor. After three seconds, stuns any non-servants nearby and damages Nar-Sian cultists.
|
||||
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/New(loc, caster)
|
||||
..()
|
||||
user = caster
|
||||
playsound(src, 'sound/magic/MAGIC_MISSILE.ogg', 50, 1, 1, 1)
|
||||
flick("judicial_marker", src)
|
||||
addtimer(src, "burstanim", 16, FALSE)
|
||||
|
||||
/obj/effect/clockwork/judicial_marker/proc/burstanim()
|
||||
layer = ABOVE_ALL_MOB_LAYER
|
||||
flick("judicial_explosion", src)
|
||||
addtimer(src, "judicialblast", 13, FALSE)
|
||||
|
||||
/obj/effect/clockwork/judicial_marker/proc/judicialblast()
|
||||
var/targetsjudged = 0
|
||||
playsound(src, 'sound/effects/explosionfar.ogg', 100, 1, 1, 1)
|
||||
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
|
||||
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>")
|
||||
L.Weaken(8)
|
||||
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.Weaken(4) //half the stun, but sets cultists on fire
|
||||
L.adjust_fire_stacks(2)
|
||||
L.IgniteMob()
|
||||
targetsjudged++
|
||||
L.adjustBruteLoss(10)
|
||||
user << "<span class='brass'><b>[targetsjudged ? "Successfully judged <span class='neovgre'>[targetsjudged]</span>":"Judged no"] heretic[!targetsjudged || targetsjudged > 1 ? "s":""].</b></span>"
|
||||
QDEL_IN(src, 3) //so the animation completes properly
|
||||
|
||||
/obj/effect/clockwork/judicial_marker/ex_act(severity)
|
||||
return
|
||||
|
||||
/obj/effect/clockwork/spatial_gateway //Spatial gateway: A usually one-way rift to another location.
|
||||
name = "spatial gateway"
|
||||
desc = "A gently thrumming tear in reality."
|
||||
clockwork_desc = "A gateway in reality."
|
||||
icon_state = "spatial_gateway"
|
||||
density = 1
|
||||
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
|
||||
|
||||
/obj/effect/clockwork/spatial_gateway/New()
|
||||
..()
|
||||
spawn(1)
|
||||
if(!linked_gateway)
|
||||
qdel(src)
|
||||
return 0
|
||||
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."
|
||||
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 0
|
||||
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 1
|
||||
|
||||
/obj/effect/clockwork/spatial_gateway/examine(mob/user)
|
||||
..()
|
||||
if(is_servant_of_ratvar(user) || isobserver(user))
|
||||
user << "<span class='brass'>It has [uses] uses remaining.</span>"
|
||||
|
||||
/obj/effect/clockwork/spatial_gateway/attack_hand(mob/living/user)
|
||||
if(user.pulling && user.a_intent == "grab" && isliving(user.pulling))
|
||||
var/mob/living/L = user.pulling
|
||||
if(L.buckled || L.anchored || L.has_buckled_mobs())
|
||||
return 0
|
||||
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 1
|
||||
if(!user.canUseTopic(src))
|
||||
return 0
|
||||
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 1
|
||||
|
||||
/obj/effect/clockwork/spatial_gateway/attackby(obj/item/I, mob/living/user, params)
|
||||
if(istype(I, /obj/item/weapon/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 1
|
||||
if(istype(I, /obj/item/clockwork/slab))
|
||||
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 1
|
||||
if(user.drop_item())
|
||||
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)
|
||||
..()
|
||||
|
||||
/obj/effect/clockwork/spatial_gateway/Bumped(atom/A)
|
||||
..()
|
||||
if(isliving(A) || istype(A, /obj/item))
|
||||
pass_through_gateway(A)
|
||||
|
||||
/obj/effect/clockwork/spatial_gateway/proc/pass_through_gateway(atom/movable/A)
|
||||
if(!linked_gateway)
|
||||
qdel(src)
|
||||
return 0
|
||||
if(!sender)
|
||||
visible_message("<span class='warning'>[A] bounces off of [src]!</span>")
|
||||
return 0
|
||||
if(!uses)
|
||||
return 0
|
||||
if(isliving(A))
|
||||
var/mob/living/user = A
|
||||
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
|
||||
animate(src, transform = matrix() / 1.5, time = 10)
|
||||
linked_gateway.transform = matrix() * 1.5
|
||||
animate(linked_gateway, transform = matrix() / 1.5, time = 10)
|
||||
A.forceMove(get_turf(linked_gateway))
|
||||
uses = max(0, uses - 1)
|
||||
linked_gateway.uses = max(0, linked_gateway.uses - 1)
|
||||
spawn(10)
|
||||
if(!uses)
|
||||
qdel(src)
|
||||
qdel(linked_gateway)
|
||||
return 1
|
||||
|
||||
/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()
|
||||
..()
|
||||
playsound(src, 'sound/magic/clockwork/invoke_general.ogg', 50, 0)
|
||||
animate(src, alpha = 0, time = 10)
|
||||
QDEL_IN(src, 10)
|
||||
|
||||
/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."
|
||||
icon = 'icons/effects/340x428.dmi'
|
||||
icon_state = "nezbere"
|
||||
pixel_x = -154
|
||||
pixel_y = -198
|
||||
|
||||
/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."
|
||||
icon = 'icons/effects/211x247.dmi'
|
||||
icon_state = "sevtug"
|
||||
pixel_x = -89
|
||||
pixel_y = -107
|
||||
|
||||
/obj/effect/clockwork/general_marker/nzcrentr
|
||||
name = "Nzcrentr, the Forgotten Arbiter"
|
||||
desc = "A terrifying war machine crackling with limitless energy."
|
||||
clockwork_desc = "One of Ratvar's four generals. Nzcrentr is the result of Neovgre - Nezbere's finest war machine, commandeerable only be a mortal - fusing with its pilot and driving her \
|
||||
insane. Nzcrentr seeks out any and all sentient life to slaughter it for sport."
|
||||
icon = 'icons/effects/254x361.dmi'
|
||||
icon_state = "nzcrentr"
|
||||
pixel_x = -111
|
||||
pixel_y = -164
|
||||
|
||||
/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/187x381.dmi'
|
||||
icon_state = "inath-neq"
|
||||
pixel_x = -77
|
||||
pixel_y = -174
|
||||
|
||||
|
||||
/obj/effect/clockwork/sigil //Sigils: Rune-like markings on the ground with various effects.
|
||||
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
|
||||
burn_state = FIRE_PROOF
|
||||
burntime = 1
|
||||
var/affects_servants = FALSE
|
||||
var/stat_affected = CONSCIOUS
|
||||
|
||||
/obj/effect/clockwork/sigil/attack_hand(mob/user)
|
||||
if(iscarbon(user) && !user.stat && (!is_servant_of_ratvar(user) || (is_servant_of_ratvar(user) && user.a_intent == "harm")))
|
||||
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/Crossed(atom/movable/AM)
|
||||
..()
|
||||
if(isliving(AM))
|
||||
var/mob/living/L = AM
|
||||
if(L.stat <= stat_affected)
|
||||
if((!is_servant_of_ratvar(L) || (is_servant_of_ratvar(L) && affects_servants)) && L.mind)
|
||||
if(L.null_rod_check())
|
||||
var/obj/item/I = L.null_rod_check()
|
||||
L.visible_message("<span class='warning'>[L]'s [I.name] protects them from [src]'s effects!</span>", "<span class='userdanger'>Your [I.name] protects you!</span>")
|
||||
return
|
||||
sigil_effects(L)
|
||||
return 1
|
||||
|
||||
/obj/effect/clockwork/sigil/proc/sigil_effects(mob/living/L)
|
||||
|
||||
/obj/effect/clockwork/sigil/transgression //Sigil of Transgression: Stuns and flashes the first non-servant to walk on it. Nar-Sian cultists are damaged and knocked down for about twice the stun
|
||||
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 first non-servant to cross it. Nar-Sie's dogs will be knocked down."
|
||||
icon_state = "sigildull"
|
||||
color = "#FAE48C"
|
||||
|
||||
/obj/effect/clockwork/sigil/transgression/sigil_effects(mob/living/L)
|
||||
var/target_flashed = L.flash_eyes()
|
||||
for(var/mob/living/M in viewers(5, src))
|
||||
if(!is_servant_of_ratvar(M) && M != L)
|
||||
M.flash_eyes()
|
||||
if(iscultist(L))
|
||||
L << "<span class='heavy_brass'>\"Watch your step, wretch.\"</span>"
|
||||
L.adjustBruteLoss(10)
|
||||
L.Weaken(4)
|
||||
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(3)
|
||||
PoolOrNew(/obj/effect/overlay/temp/ratvar/sigil/transgression, get_turf(src))
|
||||
qdel(src)
|
||||
return 1
|
||||
|
||||
/obj/effect/clockwork/sigil/submission //Sigil of Submission: After a short time, converts any non-servant standing on it. Knocks down and silences them for five seconds afterwards.
|
||||
name = "ominous sigil"
|
||||
desc = "A luminous golden sigil. Something about it really bothers you."
|
||||
clockwork_desc = "A sigil that will enslave the first person to cross it, provided they remain on it for five seconds."
|
||||
icon_state = "sigilsubmission"
|
||||
color = "#FAE48C"
|
||||
alpha = 125
|
||||
stat_affected = UNCONSCIOUS
|
||||
var/convert_time = 50
|
||||
var/glow_light = 2 //soft light
|
||||
var/glow_falloff = 1
|
||||
var/delete_on_finish = TRUE
|
||||
var/sigil_name = "Sigil of Submission"
|
||||
var/glow_type
|
||||
|
||||
/obj/effect/clockwork/sigil/submission/New()
|
||||
..()
|
||||
SetLuminosity(glow_light,glow_falloff)
|
||||
|
||||
/obj/effect/clockwork/sigil/submission/proc/post_channel(mob/living/L)
|
||||
|
||||
/obj/effect/clockwork/sigil/submission/sigil_effects(mob/living/L)
|
||||
visible_message("<span class='warning'>[src] begins to glow a piercing magenta!</span>")
|
||||
animate(src, color = "#AF0AAF", time = convert_time)
|
||||
var/obj/effect/overlay/temp/ratvar/sigil/glow
|
||||
if(glow_type)
|
||||
glow = PoolOrNew(glow_type, get_turf(src))
|
||||
animate(glow, alpha = 255, time = convert_time)
|
||||
var/I = 0
|
||||
while(I < convert_time && get_turf(L) == get_turf(src))
|
||||
I++
|
||||
sleep(1)
|
||||
if(get_turf(L) != get_turf(src))
|
||||
if(glow)
|
||||
qdel(glow)
|
||||
animate(src, color = initial(color), time = 20)
|
||||
visible_message("<span class='warning'>[src] slowly stops glowing!</span>")
|
||||
return 0
|
||||
post_channel(L)
|
||||
if(is_eligible_servant(L))
|
||||
L << "<span class='heavy_brass'>\"You belong to me now.\"</span>"
|
||||
add_servant_of_ratvar(L)
|
||||
L.Weaken(3) //Completely defenseless for about five seconds - mainly to give them time to read over the information they've just been presented with
|
||||
L.Stun(3)
|
||||
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 mob_list)
|
||||
if(isobserver(M))
|
||||
var/link = FOLLOW_LINK(M, L)
|
||||
M << "[link] <span class='heavy_brass'>[message] [L.real_name]!</span>"
|
||||
else if(is_servant_of_ratvar(M))
|
||||
if(M == L)
|
||||
M << "<span class='heavy_brass'>[message] you!</span>"
|
||||
else
|
||||
M << "<span class='heavy_brass'>[message] [L.real_name]!</span>"
|
||||
if(delete_on_finish)
|
||||
qdel(src)
|
||||
else
|
||||
animate(src, color = initial(color), time = 20)
|
||||
visible_message("<span class='warning'>[src] slowly stops glowing!</span>")
|
||||
return 1
|
||||
|
||||
/obj/effect/clockwork/sigil/submission/accession //Sigil of Accession: After a short time, converts any non-servant standing on it though implants. Knocks down and silences them for five seconds afterwards.
|
||||
name = "terrifying sigil"
|
||||
desc = "A luminous brassy sigil. Something about it makes you want to flee."
|
||||
clockwork_desc = "A sigil that will enslave any person who crosses it, provided they remain on it for five seconds. \n\
|
||||
It can convert a mindshielded target once before disppearing, but can convert any number of non-implanted targets."
|
||||
icon_state = "sigiltransgression"
|
||||
color = "#A97F1B"
|
||||
alpha = 200
|
||||
glow_light = 4 //bright light
|
||||
glow_falloff = 3
|
||||
delete_on_finish = FALSE
|
||||
sigil_name = "Sigil of Accession"
|
||||
glow_type = /obj/effect/overlay/temp/ratvar/sigil/accession
|
||||
|
||||
/obj/effect/clockwork/sigil/submission/accession/post_channel(mob/living/L)
|
||||
if(isloyal(L))
|
||||
delete_on_finish = TRUE
|
||||
L.visible_message("<span class='warning'>[L] visibly trembles!</span>", \
|
||||
"<span class='sevtug'>Lbh jvyy or zvar-naq-uvf. Guvf chal gevaxrg jvyy abg fgbc zr.</span>")
|
||||
for(var/obj/item/weapon/implant/mindshield/M in L)
|
||||
if(M.implanted)
|
||||
qdel(M)
|
||||
|
||||
/obj/effect/clockwork/sigil/transmission
|
||||
name = "suspicious sigil"
|
||||
desc = "A glowing orange sigil. The air around it feels staticky."
|
||||
clockwork_desc = "A sigil that will serve as a battery for clockwork structures. Use Volt Void while standing on it to charge it."
|
||||
icon_state = "sigiltransmission"
|
||||
color = "#EC8A2D"
|
||||
alpha = 50
|
||||
var/power_charge = 2500 //starts with 2500W by default
|
||||
|
||||
/obj/effect/clockwork/sigil/transmission/examine(mob/user)
|
||||
..()
|
||||
if(is_servant_of_ratvar(user) || isobserver(user))
|
||||
user << "<span class='[power_charge ? "brass":"alloy"]'>It is storing [power_charge]W of power.</span>"
|
||||
|
||||
/obj/effect/clockwork/sigil/transmission/sigil_effects(mob/living/L)
|
||||
if(power_charge)
|
||||
L << "<span class='brass'>You feel a slight, static shock.</span>"
|
||||
return 1
|
||||
|
||||
/obj/effect/clockwork/sigil/transmission/New()
|
||||
..()
|
||||
alpha = min(initial(alpha) + power_charge*0.02, 255)
|
||||
|
||||
/obj/effect/clockwork/sigil/transmission/proc/modify_charge(amount)
|
||||
if(power_charge - amount < 0)
|
||||
return 0
|
||||
power_charge -= amount
|
||||
alpha = min(initial(alpha) + power_charge*0.02, 255)
|
||||
return 1
|
||||
|
||||
/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"
|
||||
color = "#123456"
|
||||
alpha = 75
|
||||
affects_servants = TRUE
|
||||
stat_affected = DEAD
|
||||
var/vitality = 0
|
||||
var/base_revive_cost = 20
|
||||
var/sigil_active = FALSE
|
||||
var/animation_number = 3 //each cycle increments this by 1, at 4 it produces an animation and resets
|
||||
|
||||
/obj/effect/clockwork/sigil/vitality/examine(mob/user)
|
||||
..()
|
||||
if(is_servant_of_ratvar(user) || isobserver(user))
|
||||
user << "<span class='[vitality ? "inathneq_small":"alloy"]'>It is storing [vitality] units of vitality.</span>"
|
||||
user << "<span class='inathneq_small'>It requires at least [base_revive_cost] units of vitality to revive dead servants, in addition to any damage the servant has.</span>"
|
||||
|
||||
/obj/effect/clockwork/sigil/vitality/sigil_effects(mob/living/L)
|
||||
if(L.suiciding || sigil_active || !is_servant_of_ratvar(L) && L.stat == DEAD)
|
||||
return 0
|
||||
visible_message("<span class='warning'>[src] begins to glow bright blue!</span>")
|
||||
animate(src, alpha = 255, time = 10)
|
||||
sleep(10)
|
||||
sigil_active = TRUE
|
||||
//as long as they're still on the sigil and are either not a servant or they're a servant AND it has remaining vitality
|
||||
while(L && (!is_servant_of_ratvar(L) && L.stat != DEAD || (is_servant_of_ratvar(L) && vitality)) && get_turf(L) == get_turf(src))
|
||||
if(animation_number >= 4)
|
||||
PoolOrNew(/obj/effect/overlay/temp/ratvar/sigil/vitality, get_turf(src))
|
||||
animation_number = 0
|
||||
animation_number++
|
||||
if(!is_servant_of_ratvar(L))
|
||||
var/vitality_drained = L.adjustToxLoss(1.5)
|
||||
if(vitality_drained)
|
||||
vitality += vitality_drained
|
||||
else
|
||||
break
|
||||
else
|
||||
var/clone_to_heal = L.getCloneLoss()
|
||||
var/tox_to_heal = L.getToxLoss()
|
||||
var/burn_to_heal = L.getFireLoss()
|
||||
var/brute_to_heal = L.getBruteLoss()
|
||||
var/oxy_to_heal = L.getOxyLoss()
|
||||
var/total_damage = clone_to_heal + tox_to_heal + burn_to_heal + brute_to_heal + oxy_to_heal
|
||||
if(L.stat == DEAD)
|
||||
var/revival_cost = base_revive_cost + total_damage - oxy_to_heal //ignores oxygen damage
|
||||
var/mob/dead/observer/ghost = L.get_ghost(TRUE)
|
||||
if(ghost)
|
||||
if(vitality >= revival_cost)
|
||||
ghost.reenter_corpse()
|
||||
L.revive(1, 1)
|
||||
playsound(L, 'sound/magic/Staff_Healing.ogg', 50, 1)
|
||||
L.visible_message("<span class='warning'>[L] suddenly gets back up, their mouth dripping blue ichor!</span>", "<span class='inathneq'>\"Lbh jvyy or bxnl, puvyq.\"</span>")
|
||||
vitality -= revival_cost
|
||||
break
|
||||
else
|
||||
break
|
||||
if(!total_damage)
|
||||
break
|
||||
var/vitality_for_cycle = min(vitality, 3)
|
||||
|
||||
if(clone_to_heal && vitality_for_cycle)
|
||||
var/healing = min(vitality_for_cycle, clone_to_heal)
|
||||
vitality_for_cycle -= healing
|
||||
L.adjustCloneLoss(-healing)
|
||||
vitality -= healing
|
||||
|
||||
if(tox_to_heal && vitality_for_cycle)
|
||||
var/healing = min(vitality_for_cycle, tox_to_heal)
|
||||
vitality_for_cycle -= healing
|
||||
L.adjustToxLoss(-healing)
|
||||
vitality -= healing
|
||||
|
||||
if(burn_to_heal && vitality_for_cycle)
|
||||
var/healing = min(vitality_for_cycle, burn_to_heal)
|
||||
vitality_for_cycle -= healing
|
||||
L.adjustFireLoss(-healing)
|
||||
vitality -= healing
|
||||
|
||||
if(brute_to_heal && vitality_for_cycle)
|
||||
var/healing = min(vitality_for_cycle, brute_to_heal)
|
||||
vitality_for_cycle -= healing
|
||||
L.adjustBruteLoss(-healing)
|
||||
vitality -= healing
|
||||
|
||||
if(oxy_to_heal && vitality_for_cycle)
|
||||
var/healing = min(vitality_for_cycle, oxy_to_heal)
|
||||
vitality_for_cycle -= healing
|
||||
L.adjustOxyLoss(-healing)
|
||||
vitality -= healing
|
||||
sleep(2)
|
||||
|
||||
animation_number = initial(animation_number)
|
||||
sigil_active = FALSE
|
||||
animate(src, alpha = initial(alpha), time = 20)
|
||||
visible_message("<span class='warning'>[src] slowly stops glowing!</span>")
|
||||
@@ -0,0 +1,178 @@
|
||||
//sends messages via hierophant
|
||||
/proc/send_hierophant_message(mob/user, message, name_span = "heavy_brass", message_span = "brass", user_title = "Servant")
|
||||
if(!user || !message || !ticker || !ticker.mode)
|
||||
return 0
|
||||
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>"
|
||||
for(var/M in mob_list)
|
||||
if(isobserver(M))
|
||||
var/link = FOLLOW_LINK(M, user)
|
||||
M << "[link] [parsed_message]"
|
||||
else if(is_servant_of_ratvar(M))
|
||||
M << parsed_message
|
||||
return 1
|
||||
|
||||
//Function Call action: Calls forth a Ratvarian spear.
|
||||
/datum/action/innate/function_call
|
||||
name = "Function Call"
|
||||
button_icon_state = "ratvarian_spear"
|
||||
background_icon_state = "bg_clock"
|
||||
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_CONSCIOUS
|
||||
|
||||
/datum/action/innate/function_call/IsAvailable()
|
||||
if(!is_servant_of_ratvar(owner))
|
||||
return 0
|
||||
return ..()
|
||||
|
||||
/datum/action/innate/function_call/Activate()
|
||||
if(owner.l_hand && owner.r_hand)
|
||||
usr << "<span class='warning'>You need an empty to hand to call forth your spear!</span>"
|
||||
return 0
|
||||
owner.visible_message("<span class='warning'>A strange spear materializes in [usr]'s hands!</span>", "<span class='brass'>You call forth your spear!</span>")
|
||||
var/obj/item/clockwork/ratvarian_spear/R = new(get_turf(usr))
|
||||
owner.put_in_hands(R)
|
||||
for(var/datum/action/innate/function_call/F in owner.actions) //Removes any bound Ratvarian spears
|
||||
qdel(F)
|
||||
return 1
|
||||
|
||||
//allows a mob to select a target to gate to
|
||||
/atom/movable/proc/procure_gateway(mob/living/invoker, time_duration, gateway_uses, two_way)
|
||||
var/list/possible_targets = list()
|
||||
var/list/teleportnames = list()
|
||||
var/list/duplicatenamecount = list()
|
||||
|
||||
for(var/obj/structure/clockwork/powered/clockwork_obelisk/O in all_clockwork_objects)
|
||||
if(!O.Adjacent(invoker) && O != src && (O.z <= ZLEVEL_SPACEMAX)) //don't list obelisks that we're next to
|
||||
var/area/A = get_area(O)
|
||||
var/locname = initial(A.name)
|
||||
var/resultkey = "[locname] [O.name]"
|
||||
if(resultkey in teleportnames) //why the fuck did you put two obelisks in the same area
|
||||
duplicatenamecount[resultkey]++
|
||||
resultkey = "[resultkey] ([duplicatenamecount[resultkey]])"
|
||||
else
|
||||
teleportnames.Add(resultkey)
|
||||
duplicatenamecount[resultkey] = 1
|
||||
possible_targets[resultkey] = O
|
||||
|
||||
for(var/mob/living/L in living_mob_list)
|
||||
if(!L.stat && is_servant_of_ratvar(L) && !L.Adjacent(invoker) && L != invoker && (L.z <= ZLEVEL_SPACEMAX)) //People right next to the invoker can't be portaled to, for obvious reasons
|
||||
var/resultkey = "[L.name] ([L.real_name])"
|
||||
if(resultkey in teleportnames)
|
||||
duplicatenamecount[resultkey]++
|
||||
resultkey = "[resultkey] ([duplicatenamecount[resultkey]])"
|
||||
else
|
||||
teleportnames.Add(resultkey)
|
||||
duplicatenamecount[resultkey] = 1
|
||||
possible_targets[resultkey] = L
|
||||
|
||||
if(!possible_targets.len)
|
||||
invoker << "<span class='warning'>There are no other eligible targets for a Spatial Gateway!</span>"
|
||||
return 0
|
||||
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(!target || !invoker.canUseTopic(src, BE_CLOSE))
|
||||
return 0
|
||||
var/istargetobelisk = istype(target, /obj/structure/clockwork/powered/clockwork_obelisk)
|
||||
if(istargetobelisk)
|
||||
gateway_uses *= 2
|
||||
time_duration *= 2
|
||||
invoker.visible_message("<span class='warning'>The air in front of [invoker] ripples before suddenly tearing open!</span>", \
|
||||
"<span class='brass'>With a word, you rip open a [two_way ? "two-way":"one-way"] rift to [input_target_key]. It will last for [time_duration / 10] seconds and has [gateway_uses] use[gateway_uses > 1 ? "s" : ""].</span>")
|
||||
var/obj/effect/clockwork/spatial_gateway/S1 = new(istype(src, /obj/structure/clockwork/powered/clockwork_obelisk) ? 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 1
|
||||
|
||||
/proc/scripture_unlock_check(scripture_tier) //check if the selected scripture tier is unlocked
|
||||
var/servants = 0
|
||||
var/unconverted_ai_exists = FALSE
|
||||
for(var/mob/living/M in living_mob_list)
|
||||
if(is_servant_of_ratvar(M) && (ishuman(M) || issilicon(M)))
|
||||
servants++
|
||||
for(var/mob/living/silicon/ai/ai in living_mob_list)
|
||||
if(!is_servant_of_ratvar(ai) && ai.client)
|
||||
unconverted_ai_exists = TRUE
|
||||
switch(scripture_tier)
|
||||
if(SCRIPTURE_DRIVER)
|
||||
return 1
|
||||
if(SCRIPTURE_SCRIPT)
|
||||
if(servants >= 5 && clockwork_caches)
|
||||
return 1 //5 or more non-brain servants and any number of clockwork caches
|
||||
if(SCRIPTURE_APPLICATION)
|
||||
if(servants >= 8 && clockwork_caches >= 3 && clockwork_construction_value >= 75)
|
||||
return 1 //8 or more non-brain servants, 3+ clockwork caches, and at least 75 CV
|
||||
if(SCRIPTURE_REVENANT)
|
||||
if(servants >= 10 && clockwork_caches >= 4 && clockwork_construction_value >= 150)
|
||||
return 1 //10 or more non-brain servants, 4+ clockwork caches, and at least 150 CV
|
||||
if(SCRIPTURE_JUDGEMENT)
|
||||
if(servants >= 12 && clockwork_caches >= 5 && clockwork_construction_value >= 250 && !unconverted_ai_exists)
|
||||
return 1 //12 or more non-brain servants, 5+ clockwork caches, at least 250 CV, and there are no living, non-servant ais
|
||||
return 0
|
||||
|
||||
/proc/generate_cache_component(specific_component_id) //generates a component in the global component cache, either random based on lowest or a specific component
|
||||
if(specific_component_id)
|
||||
clockwork_component_cache[specific_component_id]++
|
||||
else
|
||||
var/component_to_generate = get_weighted_component_id()
|
||||
clockwork_component_cache[component_to_generate]++
|
||||
|
||||
/proc/get_weighted_component_id(obj/item/clockwork/slab/storage_slab) //returns a chosen component id based on the lowest amount of that component
|
||||
if(storage_slab)
|
||||
return pickweight(list("belligerent_eye" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*(clockwork_component_cache["belligerent_eye"] + storage_slab.stored_components["belligerent_eye"]), 1), \
|
||||
"vanguard_cogwheel" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*(clockwork_component_cache["vanguard_cogwheel"] + storage_slab.stored_components["vanguard_cogwheel"]), 1), \
|
||||
"guvax_capacitor" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*(clockwork_component_cache["guvax_capacitor"] + storage_slab.stored_components["guvax_capacitor"]), 1), \
|
||||
"replicant_alloy" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*(clockwork_component_cache["replicant_alloy"] + storage_slab.stored_components["replicant_alloy"]), 1), \
|
||||
"hierophant_ansible" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*(clockwork_component_cache["hierophant_ansible"] + storage_slab.stored_components["hierophant_ansible"]), 1)))
|
||||
|
||||
return pickweight(list("belligerent_eye" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*clockwork_component_cache["belligerent_eye"], 1), \
|
||||
"vanguard_cogwheel" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*clockwork_component_cache["vanguard_cogwheel"], 1), \
|
||||
"guvax_capacitor" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*clockwork_component_cache["guvax_capacitor"], 1), \
|
||||
"replicant_alloy" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*clockwork_component_cache["replicant_alloy"], 1), \
|
||||
"hierophant_ansible" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*clockwork_component_cache["hierophant_ansible"], 1)))
|
||||
|
||||
/proc/clockwork_say(atom/movable/AM, message, whisper=FALSE)
|
||||
// When servants invoke ratvar's power, they speak in ways that non
|
||||
// servants do not comprehend.
|
||||
// Our ratvarian chants are stored in their ratvar forms
|
||||
|
||||
var/list/spans = list(SPAN_ROBOT)
|
||||
|
||||
var/old_languages_spoken = AM.languages_spoken
|
||||
AM.languages_spoken = HUMAN //anyone who can understand HUMAN will hear weird shitty ratvar speak, otherwise it'll get starred out
|
||||
if(isliving(AM))
|
||||
var/mob/living/L = AM
|
||||
if(!whisper)
|
||||
L.say(message, "clock", spans)
|
||||
else
|
||||
L.whisper(message)
|
||||
else
|
||||
AM.say(message)
|
||||
AM.languages_spoken = old_languages_spoken
|
||||
|
||||
/*
|
||||
|
||||
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 apostrophes, hyphens, and spaces, making the plain
|
||||
English translation apparent but disjoined (for instance, "Oru`byq zl-cbjre!" translates directly to "Beh'old 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 "Oru`byq 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. If it is the first word, nothing is done. (i.e. "V nz-bs Ratvar." directly translates to "I am-of Ratvar.")
|
||||
- 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.
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,319 @@
|
||||
|
||||
/obj/item/clockwork/clockwork_proselytizer //Clockwork proselytizer (yes, that's a real word): Converts applicable objects to Ratvarian variants.
|
||||
name = "clockwork proselytizer"
|
||||
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 liquified Replicant Alloy to function."
|
||||
icon_state = "clockwork_proselytizer"
|
||||
w_class = 3
|
||||
force = 5
|
||||
flags = NOBLUDGEON
|
||||
var/stored_alloy = 0 //Requires this to function; each chunk of replicant alloy provides REPLICANT_ALLOY_UNIT
|
||||
var/max_alloy = REPLICANT_ALLOY_UNIT * 10
|
||||
var/uses_alloy = TRUE
|
||||
var/metal_to_alloy = FALSE
|
||||
var/repairing = null //what we're currently repairing, if anything
|
||||
|
||||
/obj/item/clockwork/clockwork_proselytizer/preloaded
|
||||
stored_alloy = REPLICANT_WALL_MINUS_FLOOR+REPLICANT_WALL_TOTAL
|
||||
|
||||
/obj/item/clockwork/clockwork_proselytizer/scarab
|
||||
name = "scarab proselytizer"
|
||||
clockwork_desc = "A cogscarab's internal proselytizer. It can only be successfully used by a cogscarab and requires liquified Replicant Alloy to function."
|
||||
metal_to_alloy = TRUE
|
||||
item_state = "nothing"
|
||||
w_class = 1
|
||||
var/debug = FALSE
|
||||
|
||||
/obj/item/clockwork/clockwork_proselytizer/scarab/proselytize(atom/target, mob/living/user)
|
||||
if(!debug && !isdrone(user))
|
||||
return 0
|
||||
return ..()
|
||||
|
||||
/obj/item/clockwork/clockwork_proselytizer/scarab/debug
|
||||
clockwork_desc = "A cogscarab's internal proselytizer. It can convert nearly any object into a Ratvarian variant."
|
||||
uses_alloy = FALSE
|
||||
debug = TRUE
|
||||
|
||||
/obj/item/clockwork/clockwork_proselytizer/examine(mob/living/user)
|
||||
..()
|
||||
if(is_servant_of_ratvar(user) || isobserver(user))
|
||||
user << "<span class='brass'>Can be used to convert walls, floors, windows, airlocks, windoors, and grilles to clockwork variants.</span>"
|
||||
user << "<span class='brass'>Can also form some objects into Replicant Alloy, as well as reform Clockwork Walls into Clockwork Floors, and vice versa.</span>"
|
||||
if(metal_to_alloy)
|
||||
user << "<span class='alloy'>It can convert rods, metal, and plasteel to liquified replicant alloy at a low rate.</span>"
|
||||
if(uses_alloy)
|
||||
user << "<span class='alloy'>It has [stored_alloy]/[max_alloy] units of liquified alloy stored.</span>"
|
||||
user << "<span class='alloy'>Use it on a Tinkerer's Cache, strike it with Replicant Alloy, or attack Replicant Alloy with it to add additional liquified alloy.</span>"
|
||||
user << "<span class='alloy'>Use it in-hand to remove stored liquified alloy.</span>"
|
||||
|
||||
/obj/item/clockwork/clockwork_proselytizer/attack_self(mob/living/user)
|
||||
if(is_servant_of_ratvar(user) && uses_alloy)
|
||||
if(!can_use_alloy(REPLICANT_ALLOY_UNIT))
|
||||
user << "<span class='warning'>[src] [stored_alloy ? "Lacks enough":"Contains no"] alloy to reform[stored_alloy ? "":" any"] into solidified alloy!</span>"
|
||||
return
|
||||
modify_stored_alloy(-REPLICANT_ALLOY_UNIT)
|
||||
playsound(src, 'sound/items/Deconstruct.ogg', 50, 1)
|
||||
new/obj/item/clockwork/component/replicant_alloy(user.loc)
|
||||
user << "<span class='brass'>You force [stored_alloy ? "some":"all"] of the alloy in [src]'s compartments to reform and solidify. \
|
||||
It now contains [stored_alloy]/[max_alloy] units of liquified alloy.</span>"
|
||||
|
||||
/obj/item/clockwork/clockwork_proselytizer/attackby(obj/item/I, mob/living/user, params)
|
||||
if(istype(I, /obj/item/clockwork/component/replicant_alloy) && is_servant_of_ratvar(user) && uses_alloy)
|
||||
if(!can_use_alloy(-REPLICANT_ALLOY_UNIT))
|
||||
user << "<span class='warning'>[src]'s replicant alloy compartments are full!</span>"
|
||||
return 0
|
||||
modify_stored_alloy(REPLICANT_ALLOY_UNIT)
|
||||
playsound(user, 'sound/machines/click.ogg', 50, 1)
|
||||
user << "<span class='brass'>You force [I] to liquify and pour it into [src]'s compartments. It now contains [stored_alloy]/[max_alloy] units of liquified alloy.</span>"
|
||||
user.drop_item()
|
||||
qdel(I)
|
||||
return 1
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/clockwork/clockwork_proselytizer/afterattack(atom/target, mob/living/user, proximity_flag, params)
|
||||
if(!target || !user || !proximity_flag)
|
||||
return 0
|
||||
if(!is_servant_of_ratvar(user))
|
||||
return ..()
|
||||
proselytize(target, user)
|
||||
|
||||
/obj/item/clockwork/clockwork_proselytizer/proc/modify_stored_alloy(amount)
|
||||
if(can_use_alloy(0)) //Ratvar makes it free
|
||||
amount = 0
|
||||
stored_alloy = Clamp(stored_alloy + amount, 0, max_alloy)
|
||||
return 1
|
||||
|
||||
/obj/item/clockwork/clockwork_proselytizer/proc/can_use_alloy(amount)
|
||||
if(ratvar_awakens || !uses_alloy)
|
||||
return TRUE
|
||||
if(!amount) //functions thus as a check for if ratvar is up/it doesn't use alloy if no amount is provided
|
||||
return FALSE
|
||||
if(stored_alloy - amount < 0)
|
||||
return FALSE
|
||||
if(stored_alloy - amount > max_alloy)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/clockwork/clockwork_proselytizer/proc/proselytize(atom/target, mob/living/user)
|
||||
if(!target || !user)
|
||||
return 0
|
||||
var/target_type = target.type
|
||||
var/list/proselytize_values = target.proselytize_vals(user, src) //relevant values for proselytizing stuff, given as an associated list
|
||||
if(proselytize_values == TRUE) //if we get true, fail, but don't send a message for whatever reason
|
||||
return 0
|
||||
if(!islist(proselytize_values))
|
||||
user << "<span class='warning'>[target] cannot be proselytized!</span>"
|
||||
return 0
|
||||
if(repairing)
|
||||
user << "<span class='warning'>You are currently repairing [repairing] with [src]!</span>"
|
||||
return 0
|
||||
if(!uses_alloy)
|
||||
proselytize_values["alloy_cost"] = 0
|
||||
|
||||
if(!can_use_alloy(proselytize_values["alloy_cost"]))
|
||||
if(stored_alloy - proselytize_values["alloy_cost"] < 0)
|
||||
user << "<span class='warning'>You need [proselytize_values["alloy_cost"]] liquified alloy to proselytize [target]!</span>"
|
||||
else if(stored_alloy - proselytize_values["alloy_cost"] > max_alloy)
|
||||
user << "<span class='warning'>You have too much liquified alloy stored to proselytize [target]!</span>"
|
||||
return 0
|
||||
|
||||
if(can_use_alloy(0)) //Ratvar makes it faster
|
||||
proselytize_values["operation_time"] *= 0.5
|
||||
|
||||
user.visible_message("<span class='warning'>[user]'s [name] begins tearing apart [target]!</span>", "<span class='brass'>You begin proselytizing [target]...</span>")
|
||||
playsound(target, 'sound/machines/click.ogg', 50, 1)
|
||||
if(proselytize_values["operation_time"] && !do_after(user, proselytize_values["operation_time"], target = target))
|
||||
return 0
|
||||
if(!can_use_alloy(proselytize_values["alloy_cost"])) //Check again to prevent bypassing via spamclick
|
||||
return 0
|
||||
if(!target || target.type != target_type)
|
||||
return 0
|
||||
if(repairing)
|
||||
return 0
|
||||
user.visible_message("<span class='warning'>[user]'s [name] disgorges a chunk of metal and shapes it over what's left of [target]!</span>", \
|
||||
"<span class='brass'>You proselytize [target].</span>")
|
||||
playsound(target, 'sound/items/Deconstruct.ogg', 50, 1)
|
||||
var/new_thing_type = proselytize_values["new_obj_type"]
|
||||
if(isturf(target))
|
||||
var/turf/T = target
|
||||
T.ChangeTurf(new_thing_type)
|
||||
else
|
||||
if(proselytize_values["dir_in_new"])
|
||||
new new_thing_type(get_turf(target), proselytize_values["spawn_dir"])
|
||||
else
|
||||
var/atom/A = new new_thing_type(get_turf(target))
|
||||
A.setDir(proselytize_values["spawn_dir"])
|
||||
qdel(target)
|
||||
modify_stored_alloy(-proselytize_values["alloy_cost"])
|
||||
return 1
|
||||
|
||||
//if a valid target, returns an associated list in this format;
|
||||
//list("operation_time" = 15, "new_obj_type" = /obj/structure/window/reinforced/clockwork, "alloy_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/closed/wall/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
|
||||
return list("operation_time" = 50, "new_obj_type" = /turf/closed/wall/clockwork, "alloy_cost" = REPLICANT_WALL_TOTAL, "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, "alloy_cost" = -REPLICANT_WALL_MINUS_FLOOR, "spawn_dir" = SOUTH)
|
||||
|
||||
/turf/open/floor/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
|
||||
return list("operation_time" = 30, "new_obj_type" = /turf/open/floor/clockwork, "alloy_cost" = REPLICANT_FLOOR, "spawn_dir" = SOUTH)
|
||||
|
||||
/turf/open/floor/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
|
||||
for(var/obj/O in src)
|
||||
if(O.density && !O.CanPass(user, src, 5))
|
||||
user << "<span class='warning'>Something is in the way, preventing you from proselytizing [src] into a clockwork wall.</span>"
|
||||
return FALSE
|
||||
return list("operation_time" = 100, "new_obj_type" = /turf/closed/wall/clockwork, "alloy_cost" = REPLICANT_WALL_MINUS_FLOOR, "spawn_dir" = SOUTH)
|
||||
|
||||
/obj/item/stack/rods/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
|
||||
if(!proselytizer.metal_to_alloy)
|
||||
return 0
|
||||
var/prosel_costtime = -amount
|
||||
return list("operation_time" = -prosel_costtime, "new_obj_type" = /obj/effect/overlay/temp/ratvar/beam/itemconsume, "alloy_cost" = prosel_costtime, "spawn_dir" = SOUTH)
|
||||
|
||||
/obj/item/stack/sheet/metal/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
|
||||
if(!proselytizer.metal_to_alloy)
|
||||
return 0
|
||||
var/prosel_costtime = -amount*2
|
||||
return list("operation_time" = -prosel_costtime, "new_obj_type" = /obj/effect/overlay/temp/ratvar/beam/itemconsume, "alloy_cost" = prosel_costtime, "spawn_dir" = SOUTH)
|
||||
|
||||
/obj/item/stack/sheet/plasteel/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
|
||||
if(!proselytizer.metal_to_alloy)
|
||||
return 0
|
||||
var/prosel_costtime = -amount*3
|
||||
return list("operation_time" = -prosel_costtime, "new_obj_type" = /obj/effect/overlay/temp/ratvar/beam/itemconsume, "alloy_cost" = prosel_costtime, "spawn_dir" = SOUTH)
|
||||
|
||||
/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" = 40, "new_obj_type" = doortype, "alloy_cost" = REPLICANT_WALL_TOTAL, "spawn_dir" = dir)
|
||||
|
||||
/obj/machinery/door/airlock/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
|
||||
return FALSE
|
||||
|
||||
/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 = REPLICANT_FLOOR
|
||||
if(fulltile)
|
||||
windowtype = /obj/structure/window/reinforced/clockwork/fulltile
|
||||
new_dir = FALSE
|
||||
prosel_time = 30
|
||||
prosel_cost = REPLICANT_STANDARD
|
||||
return list("operation_time" = prosel_time, "new_obj_type" = windowtype, "alloy_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
|
||||
|
||||
/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, "alloy_cost" = REPLICANT_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
|
||||
|
||||
/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(destroyed)
|
||||
grilletype = /obj/structure/grille/ratvar/broken
|
||||
prosel_time = 5
|
||||
return list("operation_time" = prosel_time, "new_obj_type" = grilletype, "alloy_cost" = 0, "spawn_dir" = dir)
|
||||
|
||||
/obj/structure/grille/ratvar/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
|
||||
return FALSE
|
||||
|
||||
/obj/structure/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
|
||||
. = TRUE
|
||||
if(proselytizer.repairing) //no spamclicking for fast repairs, bucko
|
||||
user << "<span class='warning'>You are already repairing [proselytizer.repairing] with [proselytizer]!</span>"
|
||||
return
|
||||
if(!can_be_repaired)
|
||||
user << "<span class='warning'>[src] cannot be repaired with a proselytizer!</span>"
|
||||
return
|
||||
if(health == max_health)
|
||||
user << "<span class='warning'>[src] is at maximum integrity!</span>"
|
||||
return
|
||||
var/amount_to_heal = max_health - health
|
||||
var/healing_for_cycle = min(amount_to_heal, repair_amount)
|
||||
if(!proselytizer.can_use_alloy(0))
|
||||
healing_for_cycle = min(healing_for_cycle, proselytizer.stored_alloy)
|
||||
var/proselytizer_cost = healing_for_cycle*2
|
||||
if(!proselytizer.can_use_alloy(proselytizer_cost))
|
||||
user << "<span class='warning'>You need more liquified alloy to repair [src]!</span>"
|
||||
return
|
||||
user.visible_message("<span class='notice'>[user]'s [proselytizer.name] starts covering [src] in black liquid metal...</span>", \
|
||||
"<span class='alloy'>You start repairing [src]...</span>")
|
||||
//hugeass while because we need to re-check after the do_after
|
||||
proselytizer.repairing = src
|
||||
while(proselytizer && user && src && health != max_health)
|
||||
amount_to_heal = max_health - health
|
||||
if(!amount_to_heal)
|
||||
break
|
||||
healing_for_cycle = min(amount_to_heal, repair_amount)
|
||||
if(!proselytizer.can_use_alloy(0))
|
||||
healing_for_cycle = min(healing_for_cycle, proselytizer.stored_alloy)
|
||||
proselytizer_cost = healing_for_cycle*2
|
||||
if(!proselytizer.can_use_alloy(proselytizer_cost) || !do_after(user, proselytizer_cost, target = src) || !proselytizer || !proselytizer.can_use_alloy(proselytizer_cost))
|
||||
break
|
||||
amount_to_heal = max_health - health
|
||||
if(!amount_to_heal)
|
||||
break
|
||||
healing_for_cycle = min(amount_to_heal, repair_amount)
|
||||
if(!proselytizer.can_use_alloy(0))
|
||||
healing_for_cycle = min(healing_for_cycle, proselytizer.stored_alloy)
|
||||
proselytizer_cost = healing_for_cycle*2
|
||||
if(!proselytizer.can_use_alloy(proselytizer_cost))
|
||||
break
|
||||
health += healing_for_cycle
|
||||
proselytizer.modify_stored_alloy(-proselytizer_cost)
|
||||
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 black liquid metal.</span>", \
|
||||
"<span class='alloy'>You finish repairing [src]. It is now at <b>[health]/[max_health]</b> integrity.</span>")
|
||||
return
|
||||
|
||||
/obj/structure/clockwork/cache/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
|
||||
. = ..()
|
||||
if(proselytizer.can_use_alloy(0) || proselytizer.stored_alloy + REPLICANT_ALLOY_UNIT > proselytizer.max_alloy)
|
||||
user << "<span class='warning'>[proselytizer]'s containers of liquified alloy are full!</span>"
|
||||
return
|
||||
if(!clockwork_component_cache["replicant_alloy"])
|
||||
user << "<span class='warning'>There is no Replicant Alloy in the global component cache!</span>"
|
||||
return
|
||||
user.visible_message("<span class='notice'>[user] places the end of [proselytizer] in the hole in [src]...</span>", \
|
||||
"<span class='notice'>You start filling [proselytizer] with liquified alloy...</span>")
|
||||
//hugeass check because we need to re-check after the do_after
|
||||
while(proselytizer && proselytizer.uses_alloy && proselytizer.stored_alloy + REPLICANT_ALLOY_UNIT <= proselytizer.max_alloy && clockwork_component_cache["replicant_alloy"] \
|
||||
&& do_after(user, 10, target = src) \
|
||||
&& proselytizer && proselytizer.uses_alloy && proselytizer.stored_alloy + REPLICANT_ALLOY_UNIT <= proselytizer.max_alloy && clockwork_component_cache["replicant_alloy"])
|
||||
proselytizer.modify_stored_alloy(REPLICANT_ALLOY_UNIT)
|
||||
clockwork_component_cache["replicant_alloy"]--
|
||||
playsound(src, 'sound/items/Deconstruct.ogg', 50, 1)
|
||||
if(proselytizer && user)
|
||||
user.visible_message("<span class='notice'>[user] removes [proselytizer] from the hole in [src], apparently satisfied.</span>", \
|
||||
"<span class='brass'>You finish filling [proselytizer] with liquified alloy. It now contains [proselytizer.stored_alloy]/[proselytizer.max_alloy] units of liquified alloy.</span>")
|
||||
return
|
||||
|
||||
/obj/structure/clockwork/wall_gear/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
|
||||
return list("operation_time" = 10, "new_obj_type" = /obj/effect/overlay/temp/ratvar/beam/itemconsume, "alloy_cost" = -REPLICANT_WALL_MINUS_FLOOR, "spawn_dir" = SOUTH)
|
||||
|
||||
/obj/item/clockwork/alloy_shards/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
|
||||
return list("operation_time" = 5, "new_obj_type" = /obj/effect/overlay/temp/ratvar/beam/itemconsume, "alloy_cost" = -REPLICANT_STANDARD, "spawn_dir" = SOUTH)
|
||||
|
||||
/obj/item/clockwork/component/replicant_alloy/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
|
||||
return list("operation_time" = 0, "new_obj_type" = /obj/effect/overlay/temp/ratvar/beam/itemconsume, "alloy_cost" = -REPLICANT_ALLOY_UNIT, "spawn_dir" = SOUTH)
|
||||
@@ -0,0 +1,304 @@
|
||||
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
|
||||
|
||||
/datum/game_mode
|
||||
var/list/datum/mind/cult = list()
|
||||
var/list/cult_objectives = list()
|
||||
|
||||
/proc/iscultist(mob/living/M)
|
||||
return istype(M) && M.mind && ticker && ticker.mode && (M.mind in ticker.mode.cult)
|
||||
|
||||
/proc/is_sacrifice_target(datum/mind/mind)
|
||||
if(ticker.mode.name == "cult")
|
||||
var/datum/game_mode/cult/cult_mode = ticker.mode
|
||||
if(mind == cult_mode.sacrifice_target)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/proc/is_convertable_to_cult(datum/mind/mind)
|
||||
if(!istype(mind))
|
||||
return 0
|
||||
if(istype(mind.current, /mob/living/carbon/human) && (mind.assigned_role in list("Captain", "Chaplain")))
|
||||
return 0
|
||||
if(isloyal(mind.current))
|
||||
return 0
|
||||
if(issilicon(mind.current) || isbot(mind.current) || isdrone(mind.current))
|
||||
return 0 //can't convert machines, that's ratvar's thing
|
||||
if(isguardian(mind.current))
|
||||
var/mob/living/simple_animal/hostile/guardian/G = mind.current
|
||||
if(!iscultist(G.summoner))
|
||||
return 0 //can't convert it unless the owner is converted
|
||||
if(is_sacrifice_target(mind))
|
||||
return 0
|
||||
if(mind.enslaved_to)
|
||||
return 0
|
||||
if(is_servant_of_ratvar(mind.current))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/datum/game_mode/cult
|
||||
name = "cult"
|
||||
config_tag = "cult"
|
||||
antag_flag = ROLE_CULTIST
|
||||
restricted_jobs = list("Chaplain","AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel")
|
||||
protected_jobs = list()
|
||||
required_players = 24
|
||||
required_enemies = 4
|
||||
recommended_enemies = 4
|
||||
enemy_minimum_age = 14
|
||||
|
||||
var/finished = 0
|
||||
var/eldergod = 1 //for the summon god objective
|
||||
|
||||
var/acolytes_needed = 10 //for the survive objective
|
||||
var/acolytes_survived = 0
|
||||
|
||||
var/datum/mind/sacrifice_target = null//The target to be sacrificed
|
||||
var/list/cultists_to_cult = list() //the cultists we'll convert
|
||||
|
||||
|
||||
/datum/game_mode/cult/announce()
|
||||
world << "<B>The current game mode is - Cult!</B>"
|
||||
world << "<B>Some crewmembers are attempting to start a cult!<BR>\nCultists - sacrifice your target and summon Nar-Sie at all costs. Convert crewmembers to your cause by using the convert rune, or sacrifice them and turn them into constructs. Remember - there is no you, there is only the cult.<BR>\nPersonnel - Do not let the cult succeed in its mission. Forced consumption of holy water will convert a cultist back to a Nanotrasen-sanctioned faith.</B>"
|
||||
|
||||
|
||||
/datum/game_mode/cult/pre_setup()
|
||||
cult_objectives += "sacrifice"
|
||||
cult_objectives += "eldergod"
|
||||
|
||||
if(config.protect_roles_from_antagonist)
|
||||
restricted_jobs += protected_jobs
|
||||
|
||||
if(config.protect_assistant_from_antagonist)
|
||||
restricted_jobs += "Assistant"
|
||||
|
||||
//cult scaling goes here
|
||||
recommended_enemies = 3 + round(num_players()/15)
|
||||
|
||||
|
||||
for(var/cultists_number = 1 to recommended_enemies)
|
||||
if(!antag_candidates.len)
|
||||
break
|
||||
var/datum/mind/cultist = pick(antag_candidates)
|
||||
antag_candidates -= cultist
|
||||
cultists_to_cult += cultist
|
||||
cultist.special_role = "Cultist"
|
||||
cultist.restricted_roles = restricted_jobs
|
||||
log_game("[cultist.key] (ckey) has been selected as a cultist")
|
||||
|
||||
return (cultists_to_cult.len>=required_enemies)
|
||||
|
||||
|
||||
/datum/game_mode/cult/proc/memorize_cult_objectives(datum/mind/cult_mind)
|
||||
for(var/obj_count = 1,obj_count <= cult_objectives.len,obj_count++)
|
||||
var/explanation
|
||||
switch(cult_objectives[obj_count])
|
||||
if("survive")
|
||||
explanation = "Our knowledge must live on. Make sure at least [acolytes_needed] acolytes escape on the shuttle to spread their work on an another station."
|
||||
if("sacrifice")
|
||||
if(sacrifice_target)
|
||||
explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role] via invoking a Sacrifice rune with them on it and three acolytes around it."
|
||||
else
|
||||
explanation = "Free objective."
|
||||
if("eldergod")
|
||||
explanation = "Summon Nar-Sie by invoking the rune 'Summon Nar-Sie' with nine acolytes on it. You must do this after sacrificing your target."
|
||||
cult_mind.current << "<B>Objective #[obj_count]</B>: [explanation]"
|
||||
cult_mind.memory += "<B>Objective #[obj_count]</B>: [explanation]<BR>"
|
||||
|
||||
/datum/game_mode/cult/post_setup()
|
||||
modePlayer += cultists_to_cult
|
||||
if("sacrifice" in cult_objectives)
|
||||
var/list/possible_targets = get_unconvertables()
|
||||
if(!possible_targets.len)
|
||||
message_admins("Cult Sacrifice: Could not find unconvertable target, checking for convertable target.")
|
||||
for(var/mob/living/carbon/human/player in player_list)
|
||||
if(player.mind && !(player.mind in cultists_to_cult))
|
||||
possible_targets += player.mind
|
||||
if(possible_targets.len > 0)
|
||||
sacrifice_target = pick(possible_targets)
|
||||
if(!sacrifice_target)
|
||||
message_admins("Cult Sacrifice: ERROR - Null target chosen!")
|
||||
else
|
||||
message_admins("Cult Sacrifice: Could not find unconvertable or convertable target. WELP!")
|
||||
for(var/datum/mind/cult_mind in cultists_to_cult)
|
||||
equip_cultist(cult_mind.current)
|
||||
update_cult_icons_added(cult_mind)
|
||||
cult_mind.current << "<span class='userdanger'>You are a member of the cult!</span>"
|
||||
add_cultist(cult_mind, 0)
|
||||
..()
|
||||
|
||||
/datum/game_mode/proc/equip_cultist(mob/living/carbon/human/mob,tome = 0)
|
||||
if(!istype(mob))
|
||||
return
|
||||
if (mob.mind)
|
||||
if (mob.mind.assigned_role == "Clown")
|
||||
mob << "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself."
|
||||
mob.dna.remove_mutation(CLOWNMUT)
|
||||
|
||||
if(tome)
|
||||
. += cult_give_item(/obj/item/weapon/tome, mob)
|
||||
else
|
||||
. += cult_give_item(/obj/item/weapon/paper/talisman/supply, mob)
|
||||
mob << "These will help you start the cult on this station. Use them well, and remember - you are not the only one.</span>"
|
||||
|
||||
/datum/game_mode/proc/cult_give_item(obj/item/item_path, mob/living/carbon/human/mob)
|
||||
var/list/slots = list(
|
||||
"backpack" = slot_in_backpack,
|
||||
"left pocket" = slot_l_store,
|
||||
"right pocket" = slot_r_store,
|
||||
"left hand" = slot_l_hand,
|
||||
"right hand" = slot_r_hand,
|
||||
)
|
||||
|
||||
var/T = new item_path(mob)
|
||||
var/item_name = initial(item_path.name)
|
||||
var/where = mob.equip_in_one_of_slots(T, slots)
|
||||
if(!where)
|
||||
mob << "<span class='userdanger'>Unfortunately, you weren't able to get a [item_name]. This is very bad and you should adminhelp immediately (press F1).</span>"
|
||||
return 0
|
||||
else
|
||||
mob << "<span class='danger'>You have a [item_name] in your [where]."
|
||||
mob.update_icons()
|
||||
if(where == "backpack")
|
||||
var/obj/item/weapon/storage/B = mob.back
|
||||
B.orient2hud(mob)
|
||||
B.show_to(mob)
|
||||
return 1
|
||||
|
||||
/datum/game_mode/proc/add_cultist(datum/mind/cult_mind, stun) //BASE
|
||||
if (!istype(cult_mind))
|
||||
return 0
|
||||
if(!(cult_mind in cult) && is_convertable_to_cult(cult_mind))
|
||||
if(stun)
|
||||
cult_mind.current.Paralyse(5)
|
||||
cult += cult_mind
|
||||
cult_mind.current.faction |= "cult"
|
||||
cult_mind.current.verbs += /mob/living/proc/cult_help
|
||||
var/datum/action/innate/cultcomm/C = new()
|
||||
C.Grant(cult_mind.current)
|
||||
update_cult_icons_added(cult_mind)
|
||||
cult_mind.current.attack_log += "\[[time_stamp()]\] <span class='danger'>Has been converted to the cult!</span>"
|
||||
if(jobban_isbanned(cult_mind.current, ROLE_CULTIST))
|
||||
replace_jobbaned_player(cult_mind.current, ROLE_CULTIST, ROLE_CULTIST)
|
||||
return 1
|
||||
|
||||
|
||||
/datum/game_mode/cult/add_cultist(datum/mind/cult_mind, stun) //INHERIT
|
||||
if (!..(cult_mind))
|
||||
return
|
||||
memorize_cult_objectives(cult_mind)
|
||||
|
||||
|
||||
/datum/game_mode/proc/remove_cultist(datum/mind/cult_mind, show_message = 1, stun)
|
||||
if(cult_mind in cult)
|
||||
cult -= cult_mind
|
||||
cult_mind.current.faction -= "cult"
|
||||
cult_mind.current.verbs -= /mob/living/proc/cult_help
|
||||
for(var/datum/action/innate/cultcomm/C in cult_mind.current.actions)
|
||||
qdel(C)
|
||||
if(stun)
|
||||
cult_mind.current.Paralyse(5)
|
||||
cult_mind.current << "<span class='userdanger'>An unfamiliar white light flashes through your mind, cleansing the taint of the Dark One and all your memories as its servant.</span>"
|
||||
cult_mind.memory = ""
|
||||
update_cult_icons_removed(cult_mind)
|
||||
cult_mind.current.attack_log += "\[[time_stamp()]\] <span class='danger'>Has renounced the cult!</span>"
|
||||
if(show_message)
|
||||
for(var/mob/M in viewers(cult_mind.current))
|
||||
M << "<span class='big'>[cult_mind.current] looks like they just reverted to their old faith!</span>"
|
||||
|
||||
/datum/game_mode/proc/update_cult_icons_added(datum/mind/cult_mind)
|
||||
var/datum/atom_hud/antag/culthud = huds[ANTAG_HUD_CULT]
|
||||
culthud.join_hud(cult_mind.current)
|
||||
set_antag_hud(cult_mind.current, "cult")
|
||||
|
||||
/datum/game_mode/proc/update_cult_icons_removed(datum/mind/cult_mind)
|
||||
var/datum/atom_hud/antag/culthud = huds[ANTAG_HUD_CULT]
|
||||
culthud.leave_hud(cult_mind.current)
|
||||
set_antag_hud(cult_mind.current, null)
|
||||
/datum/game_mode/cult/proc/get_unconvertables()
|
||||
var/list/ucs = list()
|
||||
for(var/mob/living/carbon/human/player in player_list)
|
||||
if(player.mind && !is_convertable_to_cult(player.mind))
|
||||
ucs += player.mind
|
||||
return ucs
|
||||
/datum/game_mode/cult/proc/check_cult_victory()
|
||||
var/cult_fail = 0
|
||||
if(cult_objectives.Find("survive"))
|
||||
cult_fail += check_survive() //the proc returns 1 if there are not enough cultists on the shuttle, 0 otherwise
|
||||
if(cult_objectives.Find("eldergod"))
|
||||
cult_fail += eldergod //1 by default, 0 if the elder god has been summoned at least once
|
||||
if(cult_objectives.Find("sacrifice"))
|
||||
if(sacrifice_target && !sacrificed.Find(sacrifice_target)) //if the target has been sacrificed, ignore this step. otherwise, add 1 to cult_fail
|
||||
cult_fail++
|
||||
return cult_fail //if any objectives aren't met, failure
|
||||
|
||||
|
||||
/datum/game_mode/cult/proc/check_survive()
|
||||
var/acolytes_survived = 0
|
||||
for(var/datum/mind/cult_mind in cult)
|
||||
if (cult_mind.current && cult_mind.current.stat != DEAD)
|
||||
if(cult_mind.current.onCentcom() || cult_mind.current.onSyndieBase())
|
||||
acolytes_survived++
|
||||
if(acolytes_survived>=acolytes_needed)
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
|
||||
|
||||
/datum/game_mode/cult/declare_completion()
|
||||
|
||||
if(!check_cult_victory())
|
||||
feedback_set_details("round_end_result","win - cult win")
|
||||
feedback_set("round_end_result",acolytes_survived)
|
||||
world << "<span class='greentext'>The cult has succeeded! Nar-sie has snuffed out another torch in the void!</span>"
|
||||
else
|
||||
feedback_set_details("round_end_result","loss - staff stopped the cult")
|
||||
feedback_set("round_end_result",acolytes_survived)
|
||||
world << "<span class='redtext'>The staff managed to stop the cult! Dark words and heresy are no match for Nanotrasen's finest!</span>"
|
||||
|
||||
var/text = ""
|
||||
|
||||
if(cult_objectives.len)
|
||||
text += "<br><b>The cultists' objectives were:</b>"
|
||||
for(var/obj_count=1, obj_count <= cult_objectives.len, obj_count++)
|
||||
var/explanation
|
||||
switch(cult_objectives[obj_count])
|
||||
if("survive")
|
||||
if(!check_survive())
|
||||
explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. ([acolytes_survived] escaped) <span class='greenannounce'>Success!</span>"
|
||||
feedback_add_details("cult_objective","cult_survive|SUCCESS|[acolytes_needed]")
|
||||
else
|
||||
explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. ([acolytes_survived] escaped) <span class='boldannounce'>Fail.</span>"
|
||||
feedback_add_details("cult_objective","cult_survive|FAIL|[acolytes_needed]")
|
||||
if("sacrifice")
|
||||
if(sacrifice_target)
|
||||
if(sacrifice_target in sacrificed)
|
||||
explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. <span class='greenannounce'>Success!</span>"
|
||||
feedback_add_details("cult_objective","cult_sacrifice|SUCCESS")
|
||||
else if(sacrifice_target && sacrifice_target.current)
|
||||
explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. <span class='boldannounce'>Fail.</span>"
|
||||
feedback_add_details("cult_objective","cult_sacrifice|FAIL")
|
||||
else
|
||||
explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. <span class='boldannounce'>Fail (Gibbed).</span>"
|
||||
feedback_add_details("cult_objective","cult_sacrifice|FAIL|GIBBED")
|
||||
if("eldergod")
|
||||
if(!eldergod)
|
||||
explanation = "Summon Nar-Sie. <span class='greenannounce'>Success!</span>"
|
||||
feedback_add_details("cult_objective","cult_narsie|SUCCESS")
|
||||
else
|
||||
explanation = "Summon Nar-Sie. <span class='boldannounce'>Fail.</span>"
|
||||
feedback_add_details("cult_objective","cult_narsie|FAIL")
|
||||
text += "<br><B>Objective #[obj_count]</B>: [explanation]"
|
||||
world << text
|
||||
..()
|
||||
return 1
|
||||
|
||||
|
||||
/datum/game_mode/proc/auto_declare_completion_cult()
|
||||
if( cult.len || (ticker && istype(ticker.mode,/datum/game_mode/cult)) )
|
||||
var/text = "<br><font size=3><b>The cultists were:</b></font>"
|
||||
for(var/datum/mind/cultist in cult)
|
||||
text += printplayer(cultist)
|
||||
|
||||
text += "<br>"
|
||||
|
||||
world << text
|
||||
@@ -0,0 +1,76 @@
|
||||
|
||||
/datum/action/innate/cultcomm
|
||||
name = "Communion"
|
||||
button_icon_state = "cult_comms"
|
||||
background_icon_state = "bg_demon"
|
||||
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_CONSCIOUS
|
||||
|
||||
/datum/action/innate/cultcomm/IsAvailable()
|
||||
if(!iscultist(owner))
|
||||
return 0
|
||||
return ..()
|
||||
|
||||
/datum/action/innate/cultcomm/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)
|
||||
return
|
||||
|
||||
/proc/cultist_commune(mob/living/user, message)
|
||||
if(!message)
|
||||
return
|
||||
if(!ishuman(user))
|
||||
user.say("O bidai nabora se[pick("'","`")]sma!")
|
||||
else
|
||||
user.whisper("O bidai nabora se[pick("'","`")]sma!")
|
||||
sleep(10)
|
||||
if(!user)
|
||||
return
|
||||
if(!ishuman(user))
|
||||
user.say(message)
|
||||
else
|
||||
user.whisper(message)
|
||||
var/my_message = "<span class='cultitalic'><b>[(ishuman(user) ? "Acolyte" : "Construct")] [user]:</b> [message]</span>"
|
||||
for(var/mob/M in mob_list)
|
||||
if(iscultist(M))
|
||||
M << my_message
|
||||
else if(M in dead_mob_list)
|
||||
var/link = FOLLOW_LINK(M, user)
|
||||
M << "[link] [my_message]"
|
||||
|
||||
log_say("[user.real_name]/[user.key] : [message]")
|
||||
|
||||
/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
|
||||
@@ -0,0 +1,360 @@
|
||||
/obj/item/weapon/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"
|
||||
flags = CONDUCT
|
||||
sharpness = IS_SHARP
|
||||
w_class = 4
|
||||
force = 30
|
||||
throwforce = 10
|
||||
hitsound = 'sound/weapons/bladeslice.ogg'
|
||||
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "rended")
|
||||
|
||||
|
||||
/obj/item/weapon/melee/cultblade/attack(mob/living/target, mob/living/carbon/human/user)
|
||||
if(!iscultist(user))
|
||||
user.Weaken(5)
|
||||
user.unEquip(src, 1)
|
||||
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/weapon/melee/cultblade/pickup(mob/living/user)
|
||||
..()
|
||||
if(!iscultist(user))
|
||||
if(!is_servant_of_ratvar(user))
|
||||
user << "<span class='cultlarge'>\"I wouldn't advise that.\"</span>"
|
||||
user << "<span class='warning'>An overwhelming sense of nausea overpowers you!</span>"
|
||||
user.Dizzy(120)
|
||||
else
|
||||
user << "<span class='cultlarge'>\"One of Ratvar's toys is trying to play with things [user.gender == FEMALE ? "s" : ""]he shouldn't. Cute.\"</span>"
|
||||
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.unEquip(src)
|
||||
|
||||
/obj/item/weapon/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"
|
||||
w_class = 2
|
||||
force = 15
|
||||
throwforce = 25
|
||||
embed_chance = 75
|
||||
|
||||
/obj/item/weapon/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/weapon/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
|
||||
weaken = 1
|
||||
|
||||
|
||||
/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)
|
||||
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/weapon/tome,/obj/item/weapon/melee/cultblade)
|
||||
armor = list(melee = 50, bullet = 30, laser = 50,energy = 20, bomb = 25, bio = 10, rad = 0)
|
||||
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/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/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)
|
||||
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/weapon/tome,/obj/item/weapon/melee/cultblade)
|
||||
armor = list(melee = 50, bullet = 30, laser = 50,energy = 20, bomb = 25, bio = 10, rad = 0)
|
||||
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)
|
||||
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 = 2
|
||||
allowed = list(/obj/item/weapon/tome,/obj/item/weapon/melee/cultblade,/obj/item/weapon/tank/internals/)
|
||||
armor = list(melee = 70, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30)
|
||||
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/cult
|
||||
|
||||
/obj/item/weapon/sharpener/cult
|
||||
name = "eldritch whetstone"
|
||||
desc = "A block, empowered by dark magic. Sharp weapons will be enhanced when used on the stone."
|
||||
used = 0
|
||||
increment = 5
|
||||
max = 40
|
||||
prefix = "darkened"
|
||||
|
||||
/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 = 4
|
||||
armor = list(melee = 50, bullet = 40, laser = 50,energy = 30, bomb = 50, bio = 30, rad = 30)
|
||||
body_parts_covered = CHEST|GROIN|LEGS|ARMS
|
||||
allowed = list(/obj/item/weapon/tome,/obj/item/weapon/melee/cultblade)
|
||||
var/current_charges = 3
|
||||
hooded = 1
|
||||
hoodtype = /obj/item/clothing/head/cult_hoodie
|
||||
|
||||
/obj/item/clothing/head/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)
|
||||
body_parts_covered = HEAD
|
||||
flags = NODROP
|
||||
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))
|
||||
user << "<span class='cultlarge'>\"I wouldn't advise that.\"</span>"
|
||||
user << "<span class='warning'>An overwhelming sense of nausea overpowers you!</span>"
|
||||
user.unEquip(src, 1)
|
||||
user.Dizzy(30)
|
||||
user.Weaken(5)
|
||||
else
|
||||
user << "<span class='cultlarge'>\"Putting on things you don't own is bad, you know.\"</span>"
|
||||
user << "<span class='userdanger'>The armor squeezes at your body!</span>"
|
||||
user.emote("scream")
|
||||
user.adjustBruteLoss(25)
|
||||
user.unEquip(src, 1)
|
||||
|
||||
/obj/item/clothing/suit/hooded/cultrobes/cult_shield/hit_reaction(mob/living/carbon/human/owner, attack_text, isinhands)
|
||||
if(current_charges)
|
||||
owner.visible_message("<span class='danger'>\The [attack_text] is deflected in a burst of blood-red sparks!</span>")
|
||||
current_charges--
|
||||
PoolOrNew(/obj/effect/overlay/temp/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)
|
||||
. += image(layer = MOB_LAYER+0.01, icon = 'icons/effects/effects.dmi', icon_state = "shield-cult")
|
||||
|
||||
/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/weapon/tome,/obj/item/weapon/melee/cultblade)
|
||||
body_parts_covered = CHEST|GROIN|LEGS|ARMS
|
||||
armor = list(melee = -100, bullet = -100, laser = -100,energy = -100, bomb = -100, bio = -100, rad = -100)
|
||||
slowdown = -1
|
||||
hooded = 1
|
||||
hoodtype = /obj/item/clothing/head/berserkerhood
|
||||
|
||||
/obj/item/clothing/head/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 = NODROP
|
||||
flags_inv = HIDEHAIR|HIDEFACE|HIDEEARS
|
||||
armor = list(melee = -100, bullet = -100, laser = -100,energy = -100, bomb = -100, bio = -100, rad = -100)
|
||||
|
||||
/obj/item/clothing/suit/hooded/cultrobes/berserker/equipped(mob/living/user, slot)
|
||||
..()
|
||||
if(!iscultist(user))
|
||||
if(!is_servant_of_ratvar(user))
|
||||
user << "<span class='cultlarge'>\"I wouldn't advise that.\"</span>"
|
||||
user << "<span class='warning'>An overwhelming sense of nausea overpowers you!</span>"
|
||||
user.unEquip(src, 1)
|
||||
user.Dizzy(30)
|
||||
user.Weaken(5)
|
||||
else
|
||||
user << "<span class='cultlarge'>\"Putting on things you don't own is bad, you know.\"</span>"
|
||||
user << "<span class='userdanger'>The robes squeeze at your body!</span>"
|
||||
user.emote("scream")
|
||||
user.adjustBruteLoss(25)
|
||||
user.unEquip(src, 1)
|
||||
|
||||
/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/user, slot)
|
||||
..()
|
||||
if(!iscultist(user))
|
||||
user << "<span class='cultlarge'>\"You want to be blind, do you?\"</span>"
|
||||
user.unEquip(src, 1)
|
||||
user.Dizzy(30)
|
||||
user.Weaken(5)
|
||||
user.blind_eyes(30)
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/drinks/bottle/unholywater
|
||||
name = "flask of unholy water"
|
||||
desc = "Toxic to nonbelievers; this water renews and reinvigorates the faithful of nar'sie."
|
||||
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/user)
|
||||
if(!iscultist(user))
|
||||
user.unEquip(src, 1)
|
||||
user.Weaken(5)
|
||||
user << "<span class='warning'>A powerful force shoves you away from [src]!</span>"
|
||||
return
|
||||
if(curselimit > 1)
|
||||
user << "<span class='notice'>We have exhausted our ability to curse the shuttle.</span>"
|
||||
return
|
||||
if(SSshuttle.emergency.mode == SHUTTLE_CALL)
|
||||
var/cursetime = 1500
|
||||
var/timer = SSshuttle.emergency.timeLeft(1) + cursetime
|
||||
SSshuttle.emergency.setTimer(timer)
|
||||
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 two minutes.",
|
||||
"The shuttle's navigation programming was replaced by a file containing two words, IT COMES. The shuttle will be delayed by two minutes.",
|
||||
"The shuttle's custodian tore out his guts and began painting strange shapes on the floor. The shuttle will be delayed by two 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 two minutes.",
|
||||
"A shuttle inspector started laughing madly over the radio and then threw herself into an engine turbine. The shuttle will be delayed by two minutes.",
|
||||
"The shuttle dispatcher was found dead with bloody symbols carved into their flesh. The shuttle will be delayed by two 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 = 2
|
||||
|
||||
/obj/item/device/cult_shift/examine(mob/user)
|
||||
..()
|
||||
if(uses)
|
||||
user << "<span class='cult'>It has [uses] uses remaining.</span>"
|
||||
else
|
||||
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))
|
||||
user << "<span class='warning'>\The [src] is dull and unmoving in your hands.</span>"
|
||||
return
|
||||
if(!iscultist(user))
|
||||
user.unEquip(src, 1)
|
||||
step(src, pick(alldirs))
|
||||
user << "<span class='warning'>\The [src] flickers out of your hands, too eager to move!</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)
|
||||
PoolOrNew(/obj/effect/overlay/temp/cult/phase/out, list(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
|
||||
|
||||
PoolOrNew(/obj/effect/overlay/temp/cult/phase, list(destination, C.dir))
|
||||
playsound(destination, 'sound/effects/phasein.ogg', 25, 1)
|
||||
playsound(destination, "sparks", 50, 1)
|
||||
|
||||
else
|
||||
C << "<span class='danger'>The veil cannot be torn here!</span>"
|
||||
@@ -0,0 +1,201 @@
|
||||
/obj/structure/cult
|
||||
density = 1
|
||||
anchored = 1
|
||||
icon = 'icons/obj/cult.dmi'
|
||||
var/cooldowntime = 0
|
||||
var/health = 100
|
||||
var/maxhealth = 100
|
||||
|
||||
/obj/structure/cult/examine(mob/user)
|
||||
..()
|
||||
user << "<span class='notice'>\The [src] is [anchored ? "":"not "]secured to the floor.</span>"
|
||||
if(iscultist(user) && cooldowntime > world.time)
|
||||
user << "<span class='cultitalic'>The magic in [src] is weak, it will be ready to use again in [getETA()].</span>"
|
||||
|
||||
/obj/structure/cult/attackby(obj/I, mob/user, params)
|
||||
if(istype(I, /obj/item/weapon/tome) && iscultist(user))
|
||||
anchored = !anchored
|
||||
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/cult/proc/getETA()
|
||||
var/time = (cooldowntime - world.time)/600
|
||||
var/eta = "[round(time, 1)] minutes"
|
||||
if(time <= 1)
|
||||
time = (cooldowntime - world.time)*0.1
|
||||
eta = "[round(time, 1)] seconds"
|
||||
return eta
|
||||
|
||||
/obj/structure/cult/talisman
|
||||
name = "altar"
|
||||
desc = "A bloodstained altar dedicated to Nar-Sie."
|
||||
icon_state = "talismanaltar"
|
||||
|
||||
/obj/structure/cult/talisman/attack_hand(mob/living/user)
|
||||
if(!iscultist(user))
|
||||
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)
|
||||
user << "<span class='cultitalic'>You need to anchor [src] to the floor with a tome first.</span>"
|
||||
return
|
||||
if(cooldowntime > world.time)
|
||||
user << "<span class='cultitalic'>The magic in [src] is weak, it will be ready to use again in [getETA()].</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/weapon/sharpener/cult
|
||||
if("Zealot's Blindfold")
|
||||
pickedtype = /obj/item/clothing/glasses/night/cultblind
|
||||
if("Flask of Unholy Water")
|
||||
pickedtype = /obj/item/weapon/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))
|
||||
user << "<span class='cultitalic'>You kneel before the altar and your faith is rewarded with an [N]!</span>"
|
||||
|
||||
|
||||
/obj/structure/cult/forge
|
||||
name = "daemon forge"
|
||||
desc = "A forge used in crafting the unholy weapons used by the armies of Nar-Sie."
|
||||
icon_state = "forge"
|
||||
luminosity = 3
|
||||
|
||||
/obj/structure/cult/forge/attack_hand(mob/living/user)
|
||||
if(!iscultist(user))
|
||||
user << "<span class='warning'>The heat radiating from [src] pushes you back.</span>"
|
||||
return
|
||||
if(!anchored)
|
||||
user << "<span class='cultitalic'>You need to anchor [src] to the floor with a tome first.</span>"
|
||||
return
|
||||
if(cooldowntime > world.time)
|
||||
user << "<span class='cultitalic'>The magic in [src] is weak, it will be ready to use again in [getETA()].</span>"
|
||||
return
|
||||
var/choice = alert(user,"You study the schematics etched into the forge...",,"Shielded Robe","Flagellant's Robe","Nar-Sien Hardsuit")
|
||||
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("Nar-Sien Hardsuit")
|
||||
pickedtype = /obj/item/clothing/suit/space/hardsuit/cult
|
||||
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))
|
||||
user << "<span class='cultitalic'>You work the forge as dark knowledge guides your hands, creating [N]!</span>"
|
||||
|
||||
|
||||
var/list/blacklisted_pylon_turfs = typecacheof(list(
|
||||
/turf/closed,
|
||||
/turf/open/floor/engine/cult,
|
||||
/turf/open/space,
|
||||
/turf/open/floor/plating/lava,
|
||||
/turf/open/chasm))
|
||||
|
||||
/obj/structure/cult/pylon
|
||||
name = "pylon"
|
||||
desc = "A floating crystal that slowly heals those faithful to Nar'Sie."
|
||||
icon_state = "pylon"
|
||||
luminosity = 5
|
||||
var/heal_delay = 25
|
||||
var/last_heal = 0
|
||||
var/corrupt_delay = 50
|
||||
var/last_corrupt = 0
|
||||
|
||||
/obj/structure/cult/pylon/New()
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
..()
|
||||
|
||||
/obj/structure/cult/pylon/Destroy()
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
return ..()
|
||||
|
||||
/obj/structure/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)
|
||||
PoolOrNew(/obj/effect/overlay/temp/heal, list(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)
|
||||
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
|
||||
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)
|
||||
PoolOrNew(/obj/effect/overlay/temp/cult/turf/open/floor, F)
|
||||
else
|
||||
// Are we in space or something? No cult turfs or
|
||||
// convertable turfs?
|
||||
last_corrupt = world.time + corrupt_delay*2
|
||||
|
||||
/obj/structure/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"
|
||||
luminosity = 1
|
||||
|
||||
/obj/structure/cult/tome/attack_hand(mob/living/user)
|
||||
if(!iscultist(user))
|
||||
user << "<span class='warning'>All of these books seem to be gibberish.</span>"
|
||||
return
|
||||
if(!anchored)
|
||||
user << "<span class='cultitalic'>You need to anchor [src] to the floor with a tome first.</span>"
|
||||
return
|
||||
if(cooldowntime > world.time)
|
||||
user << "<span class='cultitalic'>The magic in [src] is weak, it will be ready to use again in [getETA()].</span>"
|
||||
return
|
||||
var/choice = alert(user,"You flip through the black pages of the archives...",,"Supply Talisman","Shuttle Curse","Veil Shifter")
|
||||
var/pickedtype
|
||||
switch(choice)
|
||||
if("Supply Talisman")
|
||||
pickedtype = /obj/item/weapon/paper/talisman/supply/weak
|
||||
if("Shuttle Curse")
|
||||
pickedtype = /obj/item/device/shuttle_curse
|
||||
if("Veil Shifter")
|
||||
pickedtype = /obj/item/device/cult_shift
|
||||
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))
|
||||
user << "<span class='cultitalic'>You summon [N] 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 = 1
|
||||
unacidable = 1
|
||||
anchored = 1
|
||||
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
|
||||
This file contains the arcane tome files.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/obj/item/weapon/tome
|
||||
name = "arcane tome"
|
||||
desc = "An old, dusty tome with frayed edges and a sinister-looking cover."
|
||||
icon_state ="tome"
|
||||
throw_speed = 2
|
||||
throw_range = 5
|
||||
w_class = 2
|
||||
|
||||
/obj/item/weapon/tome/examine(mob/user)
|
||||
..()
|
||||
if(iscultist(user) || isobserver(user))
|
||||
user << "<span class='cult'>The scriptures of the Geometer. Allows the scribing of runes and access to the knowledge archives of the cult of Nar-Sie.</span>"
|
||||
user << "<span class='cult'>Striking a cult structure will unanchor or reanchor it.</span>"
|
||||
user << "<span class='cult'>Striking another cultist with it will purge holy water from them.</span>"
|
||||
user << "<span class='cult'>Striking a noncultist, however, will sear their flesh.</span>"
|
||||
|
||||
/obj/item/weapon/tome/attack(mob/living/M, mob/living/user)
|
||||
if(!istype(M))
|
||||
return
|
||||
if(!iscultist(user))
|
||||
return ..()
|
||||
if(iscultist(M))
|
||||
if(M.reagents && M.reagents.has_reagent("holywater")) //allows cultists to be rescued from the clutches of ordained religion
|
||||
user << "<span class='cult'>You remove the taint from [M].</span>"
|
||||
var/holy2unholy = M.reagents.get_reagent_amount("holywater")
|
||||
M.reagents.del_reagent("holywater")
|
||||
M.reagents.add_reagent("unholywater",holy2unholy)
|
||||
add_logs(user, M, "smacked", src, " removing the holy water from them")
|
||||
return
|
||||
M.take_organ_damage(0, 15) //Used to be a random between 5 and 20
|
||||
playsound(M, 'sound/weapons/sear.ogg', 50, 1)
|
||||
M.visible_message("<span class='danger'>[user] strikes [M] with the arcane tome!</span>", \
|
||||
"<span class='userdanger'>[user] strikes you with the tome, searing your flesh!</span>")
|
||||
flick("tome_attack", src)
|
||||
user.do_attack_animation(M)
|
||||
add_logs(user, M, "smacked", src)
|
||||
|
||||
/obj/item/weapon/tome/attack_self(mob/user)
|
||||
if(!iscultist(user))
|
||||
user << "<span class='warning'>[src] seems full of unintelligible shapes, scribbles, and notes. Is this some sort of joke?</span>"
|
||||
return
|
||||
open_tome(user)
|
||||
|
||||
/obj/item/weapon/tome/proc/open_tome(mob/user)
|
||||
var/choice = alert(user,"You open the tome...",,"Scribe Rune","More Information","Cancel")
|
||||
switch(choice)
|
||||
if("More Information")
|
||||
read_tome(user)
|
||||
if("Scribe Rune")
|
||||
scribe_rune(user)
|
||||
if("Cancel")
|
||||
return
|
||||
|
||||
/obj/item/weapon/tome/proc/read_tome(mob/user)
|
||||
var/text = ""
|
||||
text += "<center><font color='red' size=3><b><i>Archives of the Dark One</i></b></font></center><br><br><br>"
|
||||
text += "A rune's name and effects can be revealed by examining the rune.<<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Create Talisman</b></font><br>This rune is one of the most important runes the cult has, being the only way to create new talismans. A blank sheet of paper must be on top of the rune. After \
|
||||
invoking it and choosing which talisman you desire, the paper will be converted, after some delay into a talisman.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Teleport</b></font><br>This rune is unique in that it requires a keyword before the scribing can begin. When invoked, it will find any other Teleport runes; \
|
||||
If any are found, the user can choose which rune to send to. Upon activation, the rune teleports everything above it to the selected rune.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Convert</b></font><br>This rune is critical to the success of the cult. It will allow you to convert normal crew members into cultists. \
|
||||
To do this, simply place the crew member upon the rune and invoke it. This rune requires two invokers to use. If the target to be converted is mindshield-implanted or a certain assignment, they will \
|
||||
be unable to be converted. People the Geometer wishes sacrificed will also be ineligible for conversion, and anyone with a shielding presence like the null rod will not be converted.<br> \
|
||||
Successful conversions will produce a tome for the new cultist.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Sacrifice</b></font><br><b>This rune is necessary to achieve your goals.</b> Simply place any dead creature upon the rune and invoke it (this will not \
|
||||
target cultists!). If this creature has a mind, a soulstone will be created and the creature's soul transported to it. Sacrificing the dead can be done alone, but sacrificing living crew <b>or your cult's target</b> will require 3 cultists. \
|
||||
Soulstones used on construct shells will move that soul into a powerful construct of your choice.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Raise Dead</b></font><br>This rune requires two corpses. To perform the ritual, place the corpse you wish to revive onto \
|
||||
the rune and the offering body adjacent to it. When the rune is invoked, the body to be sacrificed will turn to dust, the life force flowing into the revival target. Assuming the target is not moved \
|
||||
within a few seconds, they will be brought back to life, healed of all ailments.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Electromagnetic Disruption</b></font><br>Robotic lifeforms have time and time again been the downfall of fledgling cults. This rune may allow you to gain the upper \
|
||||
hand against these pests. By using the rune, a large electromagnetic pulse will be emitted from the rune's location. The size of the EMP will grow significantly for each additional adjacent cultist when the \
|
||||
rune is activated.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Astral Communion</b></font><br>This rune is perhaps the most ingenious rune that is usable by a single person. Upon invoking the rune, the \
|
||||
user's spirit will be ripped from their body. In this state, the user's physical body will be locked in place to the rune itself - any attempts to move it will result in the rune pulling it back. \
|
||||
The body will also take constant damage while in this form, and may even die. The user's spirit will contain their consciousness, and will allow them to freely wander the station as a ghost. This may \
|
||||
also be used to commune with the dead.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Form Barrier</b></font><br>While simple, this rune serves an important purpose in defense and hindering passage. When invoked, the \
|
||||
rune will draw a small amount of life force from the user and make the space above the rune completely dense, rendering it impassable to all but the most complex means. The rune may be invoked again to \
|
||||
undo this effect and allow passage again.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Summon Cultist</b></font><br>This rune allows the cult to free other cultists with ease. When invoked, it will allow the user to summon a single cultist to the rune from \
|
||||
any location. It requires two invokers, and will damage each invoker slightly.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Blood Boil</b></font><br>When invoked, this rune will do a massive amount of damage to all non-cultist viewers, but it will also emit a small explosion upon invocation. \
|
||||
It requires three invokers.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Manifest Spirit</b></font><br>This rune allows you to summon spirits as humanoid fighters. When invoked, a spirit above the rune will be brought to life as a human, wearing nothing, that seeks only to serve you and the Geometer. \
|
||||
However, the spirit's link to reality is fragile - you must remain on top of the rune, and you will slowly take damage. Upon stepping off the rune, all summoned spirits will dissipate, dropping their items to the ground. You may manifest \
|
||||
multiple spirits with one rune, but you will rapidly take damage in doing so.<br><br>"
|
||||
|
||||
text += "<font color='red'><b><i>Summon Nar-Sie</i></b></font><br><b>This rune is necessary to achieve your goals.</b> On attempting to scribe it, it will produce shields around you and alert everyone you are attempting to scribe it; it takes a very long time to scribe, \
|
||||
and does massive damage to the one attempting to scribe it.<br>Invoking it requires 9 invokers and the sacrifice of a specific crewmember, and once invoked, will summon the Geometer, Nar-Sie herself. \
|
||||
This will complete your objectives.<br><br><br>"
|
||||
|
||||
text += "<font color='red'><b>Talisman of Teleportation</b></font><br>The talisman form of the Teleport rune will transport the invoker to a selected Teleport rune once.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Talisman of Construction</b></font><br>This talisman is the main way of creating construct shells. To use it, one must strike 30 sheets of metal with the talisman. The sheets will then be twisted into a construct shell, ready to recieve a soul to occupy it.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Talisman of Tome Summoning</b></font><br>This talisman will produce a single tome at your feet.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Talisman of Veiling/Revealing</b></font><br>This talisman will hide runes on its first use, and on the second, will reveal runes.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Talisman of Disguising</b></font><br>This talisman will permanently disguise all nearby runes as crayon runes.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Talisman of Electromagnetic Pulse</b></font><br>This talisman will EMP anything else nearby. It disappears after one use.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Talisman of Stunning</b></font><br>Attacking a target will knock them down for a long duration in addition to inhibiting their speech. \
|
||||
Robotic lifeforms will suffer the effects of a heavy electromagnetic pulse instead.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Talisman of Armaments</b></font><br>The Talisman of Arming will equip the user with armored robes, a backpack, an eldritch longsword, an empowered bola, and a pair of boots. Any items that cannot \
|
||||
be equipped will not be summoned. Attacking a fellow cultist with it will instead equip them.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Talisman of Horrors</b></font><br>The Talisman of Horror must be applied directly to the victim, it will shatter your victim's mind with visions of the endtimes that may incapitate them.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Talisman of Shackling</b></font><br>The Talisman of Shackling must be applied directly to the victim, it has 4 uses and cuffs victims with magic shackles that disappear when removed.<br><br>"
|
||||
|
||||
text += "In addition to these runes, the cult has a small selection of equipment and constructs.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Equipment:</b></font><br><br>"
|
||||
|
||||
text += "<font color='red'><b>Cult Blade</b></font><br>Cult blades are sharp weapons that, notably, cannot be used by noncultists. These blades are produced by the Talisman of Arming.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Cult Bola</b></font><br>Cult bolas are strong bolas, useful for snaring targets. These bolas are produced by the Talisman of Arming.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Cult Robes</b></font><br>Cult robes are heavily armored robes. These robes are produced by the Talisman of Arming.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Soulstone</b></font><br>A soulstone is a simple piece of magic, produced either via the starter talisman or by sacrificing humans. Using it on an unconscious or dead human, or on a Shade, will trap their soul in the stone, allowing its use in construct shells. \
|
||||
<br>The soul within can also be released as a Shade by using it in-hand.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Construct Shell</b></font><br>A construct shell is useless on its own, but placing a filled soulstone within it allows you to produce your choice of a <b>Wraith</b>, a <b>Juggernaut</b>, or an <b>Artificer</b>. \
|
||||
<br>Each construct has uses, detailed below in Constructs. Construct shells can be produced via the starter talisman or the Rite of Fabrication.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Constructs:</b></font><br><br>"
|
||||
|
||||
text += "<font color='red'><b>Shade</b></font><br>While technically not a construct, the Shade is produced when released from a soulstone. It is quite fragile and has weak melee attacks, but is fully healed when recaptured by a soulstone.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Wraith</b></font><br>The Wraith is a fast, lethal melee attacker which can jaunt through walls. However, it is only slightly more durable than a shade.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Juggernaut</b></font><br>The Juggernaut is a slow, but durable, melee attacker which can produce temporary forcewalls. It will also reflect most lethal energy weapons.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Artificer</b></font><br>The Artificer is a weak and fragile construct, able to heal other constructs, shades, or itself, produce more <font color='red'><b>soulstones</b></font> and <font color='red'><b>construct shells</b></font>, \
|
||||
construct fortifying cult walls and flooring, and finally, it can release a few indiscriminate stunning missiles.<br><br>"
|
||||
|
||||
text += "<font color='red'><b>Harvester</b></font><br>If you see one, know that you have done all you can and your life is void.<br><br>"
|
||||
|
||||
var/datum/browser/popup = new(user, "tome", "", 800, 600)
|
||||
popup.set_content(text)
|
||||
popup.open()
|
||||
return 1
|
||||
|
||||
/obj/item/weapon/tome/proc/scribe_rune(mob/living/user)
|
||||
var/turf/Turf = get_turf(user)
|
||||
var/chosen_keyword
|
||||
var/obj/effect/rune/rune_to_scribe
|
||||
var/entered_rune_name
|
||||
var/list/possible_runes = list()
|
||||
var/list/shields = list()
|
||||
var/area/A = get_area(src)
|
||||
|
||||
if(locate(/obj/effect/rune) in Turf)
|
||||
user << "<span class='cult'>There is already a rune here.</span>"
|
||||
return
|
||||
|
||||
if(Turf.z != ZLEVEL_STATION && Turf.z != ZLEVEL_MINING)
|
||||
user << "<span class='warning'>The veil is not weak enough here."
|
||||
return
|
||||
if(istype(A, /area/shuttle))
|
||||
user << "<span class='warning'>Interference from hyperspace engines disrupts the Geometer's power on shuttles.</span>"
|
||||
for(var/T in subtypesof(/obj/effect/rune) - /obj/effect/rune/malformed)
|
||||
var/obj/effect/rune/R = T
|
||||
if(initial(R.cultist_name))
|
||||
possible_runes.Add(initial(R.cultist_name)) //This is to allow the menu to let cultists select runes by name rather than by object path. I don't know a better way to do this
|
||||
if(!possible_runes.len)
|
||||
return
|
||||
entered_rune_name = input(user, "Choose a rite to scribe.", "Sigils of Power") as null|anything in possible_runes
|
||||
if(!Adjacent(user) || !src || qdeleted(src) || user.incapacitated())
|
||||
return
|
||||
if(istype(Turf, /turf/open/space))
|
||||
user << "<span class='warning'>You cannot scribe runes in space!</span>"
|
||||
return
|
||||
for(var/T in typesof(/obj/effect/rune))
|
||||
var/obj/effect/rune/R = T
|
||||
if(initial(R.cultist_name) == entered_rune_name)
|
||||
rune_to_scribe = R
|
||||
if(initial(R.req_keyword))
|
||||
var/the_keyword = stripped_input(usr, "Please enter a keyword for the rune.", "Enter Keyword", "")
|
||||
if(!the_keyword)
|
||||
return
|
||||
chosen_keyword = the_keyword
|
||||
break
|
||||
if(!rune_to_scribe)
|
||||
return
|
||||
Turf = get_turf(user) //we may have moved. adjust as needed...
|
||||
if(locate(/obj/effect/rune) in Turf)
|
||||
user << "<span class='cult'>There is already a rune here.</span>"
|
||||
return
|
||||
if(!Adjacent(user) || !src || qdeleted(src) || user.incapacitated())
|
||||
return
|
||||
if(ispath(rune_to_scribe, /obj/effect/rune/narsie))
|
||||
if(ticker.mode.name == "cult")
|
||||
var/datum/game_mode/cult/cult_mode = ticker.mode
|
||||
if(!("eldergod" in cult_mode.cult_objectives))
|
||||
user << "<span class='warning'>Nar-Sie does not wish to be summoned!</span>"
|
||||
return
|
||||
else if(cult_mode.sacrifice_target && !(cult_mode.sacrifice_target in sacrificed))
|
||||
user << "<span class='warning'>The sacrifice is not complete. The portal would lack the power to open if you tried!</span>"
|
||||
return
|
||||
else if(!cult_mode.eldergod)
|
||||
user << "<span class='cultlarge'>\"I am already here. There is no need to try to summon me now.\"</span>"
|
||||
return
|
||||
var/locname = initial(A.name)
|
||||
if(loc.z && loc.z != ZLEVEL_STATION)
|
||||
user << "<span class='warning'>The Geometer is not interested \
|
||||
in lesser locations; the station is the prize!</span>"
|
||||
return
|
||||
var/confirm_final = alert(user, "This is the FINAL step to summon Nar-Sie, it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for Nar-Sie!", "No")
|
||||
if(confirm_final == "No")
|
||||
user << "<span class='cult'>You decide to prepare further before scribing the rune.</span>"
|
||||
return
|
||||
priority_announce("Figments from an eldritch god are being summoned by [user] into [locname] from an unknown dimension. Disrupt the ritual at all costs!","Central Command Higher Dimensionsal Affairs", 'sound/AI/spanomalies.ogg')
|
||||
for(var/B in spiral_range_turfs(1, user, 1))
|
||||
var/turf/T = B
|
||||
var/obj/machinery/shield/N = new(T)
|
||||
N.name = "sanguine barrier"
|
||||
N.desc = "A potent shield summoned by cultists to defend their rites."
|
||||
N.icon_state = "shield-red"
|
||||
N.health = 60
|
||||
shields |= N
|
||||
else
|
||||
user << "<span class='warning'>Nar-Sie does not wish to be summoned!</span>"
|
||||
return
|
||||
user.visible_message("<span class='warning'>[user] cuts open their arm and begins writing in their own blood!</span>", \
|
||||
"<span class='cult'>You slice open your arm and begin drawing a sigil of the Geometer.</span>")
|
||||
user.apply_damage(initial(rune_to_scribe.scribe_damage), BRUTE, pick("l_arm", "r_arm"))
|
||||
if(!do_after(user, initial(rune_to_scribe.scribe_delay), target = get_turf(user)))
|
||||
for(var/V in shields)
|
||||
var/obj/machinery/shield/S = V
|
||||
if(S && !qdeleted(S))
|
||||
qdel(S)
|
||||
return
|
||||
if(locate(/obj/effect/rune) in Turf)
|
||||
user << "<span class='cult'>There is already a rune here.</span>"
|
||||
return
|
||||
user.visible_message("<span class='warning'>[user] creates a strange circle in their own blood.</span>", \
|
||||
"<span class='cult'>You finish drawing the arcane markings of the Geometer.</span>")
|
||||
for(var/V in shields)
|
||||
var/obj/machinery/shield/S = V
|
||||
if(S && !qdeleted(S))
|
||||
qdel(S)
|
||||
new rune_to_scribe(Turf, chosen_keyword)
|
||||
user << "<span class='cult'>The [lowertext(initial(rune_to_scribe.cultist_name))] rune [initial(rune_to_scribe.cultist_desc)]</span>"
|
||||
@@ -0,0 +1,908 @@
|
||||
/var/list/sacrificed = list()
|
||||
var/list/non_revealed_runes = (subtypesof(/obj/effect/rune) - /obj/effect/rune/malformed)
|
||||
|
||||
/*
|
||||
|
||||
This file contains runes.
|
||||
Runes are used by the cult to cause many different effects and are paramount to their success.
|
||||
They are drawn with an arcane tome in blood, and are distinguishable to cultists and normal crew by examining.
|
||||
Fake runes can be drawn in crayon to fool people.
|
||||
Runes can either be invoked by one's self or with many different cultists. Each rune has a specific incantation that the cultists will say when invoking it.
|
||||
|
||||
To draw a rune, use an arcane tome.
|
||||
|
||||
*/
|
||||
|
||||
/obj/effect/rune
|
||||
name = "rune"
|
||||
var/cultist_name = "basic rune"
|
||||
desc = "An odd collection of symbols drawn in what seems to be blood."
|
||||
var/cultist_desc = "a basic rune with no function." //This is shown to cultists who examine the rune in order to determine its true purpose.
|
||||
anchored = 1
|
||||
icon = 'icons/obj/rune.dmi'
|
||||
icon_state = "1"
|
||||
unacidable = 1
|
||||
layer = ABOVE_NORMAL_TURF_LAYER
|
||||
color = rgb(255,0,0)
|
||||
|
||||
var/invocation = "Aiy ele-mayo!" //This is said by cultists when the rune is invoked.
|
||||
var/req_cultists = 1 //The amount of cultists required around the rune to invoke it. If only 1, any cultist can invoke it.
|
||||
var/rune_in_use = 0 // Used for some runes, this is for when you want a rune to not be usable when in use.
|
||||
|
||||
var/scribe_delay = 50 //how long the rune takes to create
|
||||
var/scribe_damage = 0.1 //how much damage you take doing it
|
||||
|
||||
var/allow_excess_invokers = 0 //if we allow excess invokers when being invoked
|
||||
var/construct_invoke = 1 //if constructs can invoke it
|
||||
|
||||
var/req_keyword = 0 //If the rune requires a keyword - go figure amirite
|
||||
var/keyword //The actual keyword for the rune
|
||||
|
||||
/obj/effect/rune/New(loc, set_keyword)
|
||||
..()
|
||||
if(set_keyword)
|
||||
keyword = set_keyword
|
||||
|
||||
/obj/effect/rune/examine(mob/user)
|
||||
..()
|
||||
if(iscultist(user) || user.stat == DEAD) //If they're a cultist or a ghost, tell them the effects
|
||||
user << "<b>Name:</b> [cultist_name]"
|
||||
user << "<b>Effects:</b> [capitalize(cultist_desc)]"
|
||||
user << "<b>Required Acolytes:</b> [req_cultists]"
|
||||
if(req_keyword && keyword)
|
||||
user << "<b>Keyword:</b> [keyword]"
|
||||
|
||||
/obj/effect/rune/attackby(obj/I, mob/user, params)
|
||||
if(istype(I, /obj/item/weapon/tome) && iscultist(user))
|
||||
user << "<span class='notice'>You carefully erase the [lowertext(cultist_name)] rune.</span>"
|
||||
qdel(src)
|
||||
return
|
||||
else if(istype(I, /obj/item/weapon/nullrod))
|
||||
user.say("BEGONE FOUL MAGIKS!!")
|
||||
user << "<span class='danger'>You disrupt the magic of [src] with [I].</span>"
|
||||
qdel(src)
|
||||
return
|
||||
return
|
||||
|
||||
/obj/effect/rune/attack_hand(mob/living/user)
|
||||
if(!iscultist(user))
|
||||
user << "<span class='warning'>You aren't able to understand the words of [src].</span>"
|
||||
return
|
||||
var/list/invokers = can_invoke(user)
|
||||
if(invokers.len >= req_cultists)
|
||||
invoke(invokers)
|
||||
else
|
||||
fail_invoke()
|
||||
|
||||
/obj/effect/rune/attack_animal(mob/living/simple_animal/M)
|
||||
if(istype(M, /mob/living/simple_animal/shade) || istype(M, /mob/living/simple_animal/hostile/construct))
|
||||
if(construct_invoke || !iscultist(M)) //if you're not a cult construct we want the normal fail message
|
||||
attack_hand(M)
|
||||
else
|
||||
M << "<span class='warning'>You are unable to invoke the rune!</span>"
|
||||
|
||||
/obj/effect/rune/proc/talismanhide() //for talisman of revealing/hiding
|
||||
visible_message("<span class='danger'>[src] fades away.</span>")
|
||||
invisibility = INVISIBILITY_OBSERVER
|
||||
alpha = 100 //To help ghosts distinguish hidden runes
|
||||
|
||||
/obj/effect/rune/proc/talismanreveal() //for talisman of revealing/hiding
|
||||
invisibility = 0
|
||||
visible_message("<span class='danger'>[src] suddenly appears!</span>")
|
||||
alpha = initial(alpha)
|
||||
|
||||
/*
|
||||
|
||||
There are a few different procs each rune runs through when a cultist activates it.
|
||||
can_invoke() is called when a cultist activates the rune with an empty hand. If there are multiple cultists, this rune determines if the required amount is nearby.
|
||||
invoke() is the rune's actual effects.
|
||||
fail_invoke() is called when the rune fails, via not enough people around or otherwise. Typically this just has a generic 'fizzle' effect.
|
||||
structure_check() searches for nearby cultist structures required for the invocation. Proper structures are pylons, forges, archives, and altars.
|
||||
|
||||
*/
|
||||
|
||||
/obj/effect/rune/proc/can_invoke(var/mob/living/user=null)
|
||||
//This proc determines if the rune can be invoked at the time. If there are multiple required cultists, it will find all nearby cultists.
|
||||
var/list/invokers = list() //people eligible to invoke the rune
|
||||
var/list/chanters = list() //people who will actually chant the rune when passed to invoke()
|
||||
if(user)
|
||||
chanters |= user
|
||||
invokers |= user
|
||||
if(req_cultists > 1 || allow_excess_invokers)
|
||||
for(var/mob/living/L in range(1, src))
|
||||
if(iscultist(L))
|
||||
if(L == user)
|
||||
continue
|
||||
if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
if((H.disabilities & MUTE) || H.silent)
|
||||
continue
|
||||
if(L.stat)
|
||||
continue
|
||||
invokers |= L
|
||||
if(invokers.len >= req_cultists)
|
||||
if(allow_excess_invokers)
|
||||
chanters |= invokers
|
||||
else
|
||||
invokers -= user
|
||||
shuffle(invokers)
|
||||
for(var/i in 0 to req_cultists)
|
||||
var/L = pick_n_take(invokers)
|
||||
chanters |= L
|
||||
return chanters
|
||||
|
||||
/obj/effect/rune/proc/invoke(var/list/invokers)
|
||||
//This proc contains the effects of the rune as well as things that happen afterwards. If you want it to spawn an object and then delete itself, have both here.
|
||||
if(invocation)
|
||||
for(var/M in invokers)
|
||||
var/mob/living/L = M
|
||||
L.say(invocation)
|
||||
var/oldtransform = transform
|
||||
spawn(0) //animate is a delay, we want to avoid being delayed
|
||||
animate(src, transform = matrix()*2, alpha = 0, time = 5) //fade out
|
||||
animate(transform = oldtransform, alpha = 255, time = 0)
|
||||
|
||||
/obj/effect/rune/proc/fail_invoke()
|
||||
//This proc contains the effects of a rune if it is not invoked correctly, through either invalid wording or not enough cultists. By default, it's just a basic fizzle.
|
||||
visible_message("<span class='warning'>The markings pulse with a \
|
||||
small flash of red light, then fall dark.</span>")
|
||||
spawn(0) //animate is a delay, we want to avoid being delayed
|
||||
animate(src, color = rgb(255, 0, 0), time = 0)
|
||||
animate(src, color = initial(color), time = 5)
|
||||
|
||||
//Malformed Rune: This forms if a rune is not drawn correctly. Invoking it does nothing but hurt the user.
|
||||
/obj/effect/rune/malformed
|
||||
cultist_name = "malformed rune"
|
||||
cultist_desc = "a senseless rune written in gibberish. No good can come from invoking this."
|
||||
invocation = "Ra'sha yoka!"
|
||||
|
||||
/obj/effect/rune/malformed/New()
|
||||
..()
|
||||
icon_state = "[rand(1,6)]"
|
||||
color = rgb(rand(0,255), rand(0,255), rand(0,255))
|
||||
|
||||
/obj/effect/rune/malformed/invoke(var/list/invokers)
|
||||
..()
|
||||
for(var/M in invokers)
|
||||
var/mob/living/L = M
|
||||
L << "<span class='cultitalic'><b>You feel your life force draining. The Geometer is displeased.</b></span>"
|
||||
L.apply_damage(30, BRUTE)
|
||||
qdel(src)
|
||||
|
||||
/mob/proc/null_rod_check() //The null rod, if equipped, will protect the holder from the effects of most runes
|
||||
var/obj/item/weapon/nullrod/N = locate() in src
|
||||
if(N && !ratvar_awakens) //If Nar-Sie or Ratvar are alive, null rods won't protect you
|
||||
return N
|
||||
return 0
|
||||
|
||||
//Rite of Binding: A paper on top of the rune to a talisman.
|
||||
/obj/effect/rune/imbue
|
||||
cultist_name = "Create Talisman"
|
||||
cultist_desc = "transforms paper into powerful magic talismans."
|
||||
invocation = "H'drak v'loso, mir'kanas verbot!"
|
||||
icon_state = "3"
|
||||
color = rgb(0, 0, 255)
|
||||
|
||||
/obj/effect/rune/imbue/invoke(var/list/invokers)
|
||||
var/mob/living/user = invokers[1] //the first invoker is always the user
|
||||
var/list/papers_on_rune = checkpapers()
|
||||
var/entered_talisman_name
|
||||
var/obj/item/weapon/paper/talisman/talisman_type
|
||||
var/list/possible_talismans = list()
|
||||
if(!papers_on_rune.len)
|
||||
user << "<span class='cultitalic'>There must be a blank paper on top of [src]!</span>"
|
||||
fail_invoke()
|
||||
log_game("Talisman Creation rune failed - no blank papers on rune")
|
||||
return
|
||||
if(rune_in_use)
|
||||
user << "<span class='cultitalic'>[src] can only support one ritual at a time!</span>"
|
||||
fail_invoke()
|
||||
log_game("Talisman Creation rune failed - already in use")
|
||||
return
|
||||
|
||||
for(var/I in subtypesof(/obj/item/weapon/paper/talisman) - /obj/item/weapon/paper/talisman/malformed - /obj/item/weapon/paper/talisman/supply - /obj/item/weapon/paper/talisman/supply/weak)
|
||||
var/obj/item/weapon/paper/talisman/J = I
|
||||
var/talisman_cult_name = initial(J.cultist_name)
|
||||
if(talisman_cult_name)
|
||||
possible_talismans[talisman_cult_name] = J //This is to allow the menu to let cultists select talismans by name
|
||||
entered_talisman_name = input(user, "Choose a talisman to imbue.", "Talisman Choices") as null|anything in possible_talismans
|
||||
talisman_type = possible_talismans[entered_talisman_name]
|
||||
if(!Adjacent(user) || !src || qdeleted(src) || user.incapacitated() || rune_in_use || !talisman_type)
|
||||
return
|
||||
papers_on_rune = checkpapers()
|
||||
if(!papers_on_rune.len)
|
||||
user << "<span class='cultitalic'>There must be a blank paper on top of [src]!</span>"
|
||||
fail_invoke()
|
||||
log_game("Talisman Creation rune failed - no blank papers on rune")
|
||||
return
|
||||
var/obj/item/weapon/paper/paper_to_imbue = papers_on_rune[1]
|
||||
..()
|
||||
visible_message("<span class='warning'>Dark power begins to channel into the paper!</span>")
|
||||
rune_in_use = 1
|
||||
if(!do_after(user, 100, target = paper_to_imbue))
|
||||
rune_in_use = 0
|
||||
return
|
||||
new talisman_type(get_turf(src))
|
||||
visible_message("<span class='warning'>[src] glows with power, and bloody images form themselves on [paper_to_imbue].</span>")
|
||||
qdel(paper_to_imbue)
|
||||
rune_in_use = 0
|
||||
|
||||
/obj/effect/rune/imbue/proc/checkpapers()
|
||||
. = list()
|
||||
for(var/obj/item/weapon/paper/P in get_turf(src))
|
||||
if(!P.info && !istype(P, /obj/item/weapon/paper/talisman))
|
||||
. |= P
|
||||
|
||||
var/list/teleport_runes = list()
|
||||
/obj/effect/rune/teleport
|
||||
cultist_name = "Teleport"
|
||||
cultist_desc = "warps everything above it to another chosen teleport rune."
|
||||
invocation = "Sas'so c'arta forbici!"
|
||||
icon_state = "2"
|
||||
color = "#551A8B"
|
||||
req_keyword = 1
|
||||
var/listkey
|
||||
|
||||
/obj/effect/rune/teleport/New(loc, set_keyword)
|
||||
..()
|
||||
var/area/A = get_area(src)
|
||||
var/locname = initial(A.name)
|
||||
listkey = set_keyword ? "[set_keyword] [locname]":"[locname]"
|
||||
teleport_runes += src
|
||||
|
||||
/obj/effect/rune/teleport/Destroy()
|
||||
teleport_runes -= src
|
||||
return ..()
|
||||
|
||||
/obj/effect/rune/teleport/invoke(var/list/invokers)
|
||||
var/mob/living/user = invokers[1] //the first invoker is always the user
|
||||
var/list/potential_runes = list()
|
||||
var/list/teleportnames = list()
|
||||
var/list/duplicaterunecount = list()
|
||||
for(var/R in teleport_runes)
|
||||
var/obj/effect/rune/teleport/T = R
|
||||
var/resultkey = T.listkey
|
||||
if(resultkey in teleportnames)
|
||||
duplicaterunecount[resultkey]++
|
||||
resultkey = "[resultkey] ([duplicaterunecount[resultkey]])"
|
||||
else
|
||||
teleportnames.Add(resultkey)
|
||||
duplicaterunecount[resultkey] = 1
|
||||
if(T != src && (T.z <= ZLEVEL_SPACEMAX))
|
||||
potential_runes[resultkey] = T
|
||||
|
||||
if(!potential_runes.len)
|
||||
user << "<span class='warning'>There are no valid runes to teleport to!</span>"
|
||||
log_game("Teleport rune failed - no other teleport runes")
|
||||
fail_invoke()
|
||||
return
|
||||
|
||||
if(user.z > ZLEVEL_SPACEMAX)
|
||||
user << "<span class='cultitalic'>You are not in the right dimension!</span>"
|
||||
log_game("Teleport rune failed - user in away mission")
|
||||
fail_invoke()
|
||||
return
|
||||
|
||||
var/input_rune_key = input(user, "Choose a rune to teleport to.", "Rune to Teleport to") as null|anything in potential_runes //we know what key they picked
|
||||
var/obj/effect/rune/teleport/actual_selected_rune = potential_runes[input_rune_key] //what rune does that key correspond to?
|
||||
if(!Adjacent(user) || !src || qdeleted(src) || user.incapacitated() || !actual_selected_rune)
|
||||
fail_invoke()
|
||||
return
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
var/movedsomething = 0
|
||||
var/moveuserlater = 0
|
||||
for(var/atom/movable/A in T)
|
||||
if(A == user)
|
||||
moveuserlater = 1
|
||||
movedsomething = 1
|
||||
continue
|
||||
if(!A.anchored)
|
||||
movedsomething = 1
|
||||
A.forceMove(get_turf(actual_selected_rune))
|
||||
if(movedsomething)
|
||||
..()
|
||||
visible_message("<span class='warning'>There is a sharp crack of inrushing air, and everything above the rune disappears!</span>")
|
||||
user << "<span class='cult'>You[moveuserlater ? "r vision blurs, and you suddenly appear somewhere else":" send everything above the rune away"].</span>"
|
||||
if(moveuserlater)
|
||||
user.forceMove(get_turf(actual_selected_rune))
|
||||
else
|
||||
fail_invoke()
|
||||
|
||||
|
||||
//Rite of Enlightenment: Converts a normal crewmember to the cult.
|
||||
/obj/effect/rune/convert
|
||||
cultist_name = "Convert"
|
||||
cultist_desc = "converts a normal crewmember on top of it to the cult. Does not work on mindshield-implanted crew."
|
||||
invocation = "Mah'weyh pleggh at e'ntrath!"
|
||||
icon_state = "3"
|
||||
color = rgb(200, 0, 0)
|
||||
req_cultists = 2
|
||||
|
||||
/obj/effect/rune/convert/invoke(var/list/invokers)
|
||||
var/list/convertees = list()
|
||||
var/turf/T = get_turf(src)
|
||||
for(var/mob/living/M in T)
|
||||
if(M.stat != DEAD && !iscultist(M) && is_convertable_to_cult(M.mind))
|
||||
convertees |= M
|
||||
else if(is_sacrifice_target(M.mind))
|
||||
for(var/C in invokers)
|
||||
C << "<span class='cultlarge'>\"I desire this one for myself. <i>SACRIFICE THEM!</i>\"</span>"
|
||||
else if(is_servant_of_ratvar(M))
|
||||
M.visible_message("<span class='warning'>[M]'s eyes glow a defiant yellow!</span>", \
|
||||
"<span class='cultlarge'>\"Stop resisting. You <i>will</i> be mi-\"</span> <span class='large_brass'>\"Give up and you will feel pain unlike anything you've ever felt!\"</span>")
|
||||
M.Weaken(4)
|
||||
if(!convertees.len)
|
||||
fail_invoke()
|
||||
log_game("Convert rune failed - no eligible convertees")
|
||||
return
|
||||
var/mob/living/new_cultist = pick(convertees)
|
||||
if(new_cultist.null_rod_check())
|
||||
for(var/M in invokers)
|
||||
M << "<span class='warning'>Something is shielding [new_cultist]'s mind!</span>"
|
||||
fail_invoke()
|
||||
log_game("Convert rune failed - convertee had null rod")
|
||||
return
|
||||
..()
|
||||
new_cultist.visible_message("<span class='warning'>[new_cultist] writhes in pain as the markings below them glow a bloody red!</span>", \
|
||||
"<span class='cultlarge'><i>AAAAAAAAAAAAAA-</i></span>")
|
||||
ticker.mode.add_cultist(new_cultist.mind, 1)
|
||||
new /obj/item/weapon/tome(get_turf(src))
|
||||
new_cultist.mind.special_role = "Cultist"
|
||||
new_cultist << "<span class='cultitalic'><b>Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible, truth. The veil of reality has been ripped away \
|
||||
and something evil takes root.</b></span>"
|
||||
new_cultist << "<span class='cultitalic'><b>Assist your new compatriots in their dark dealings. Your goal is theirs, and theirs is yours. You serve the Geometer above all else. Bring it back.\
|
||||
</b></span>"
|
||||
|
||||
//Rite of Tribute: Sacrifices a crew member to Nar-Sie. Places them into a soul shard if they're in their body.
|
||||
/obj/effect/rune/sacrifice
|
||||
cultist_name = "Sacrifice"
|
||||
cultist_desc = "sacrifices a crew member to the Geometer. May place them into a soul shard if their spirit remains in their body."
|
||||
icon_state = "3"
|
||||
allow_excess_invokers = 1
|
||||
invocation = "Barhah hra zar'garis!"
|
||||
color = rgb(255, 255, 255)
|
||||
rune_in_use = 0
|
||||
|
||||
/obj/effect/rune/sacrifice/New()
|
||||
..()
|
||||
icon_state = "[rand(1,6)]"
|
||||
|
||||
/obj/effect/rune/sacrifice/invoke(var/list/invokers)
|
||||
if(rune_in_use)
|
||||
return
|
||||
rune_in_use = 1
|
||||
var/mob/living/user = invokers[1] //the first invoker is always the user
|
||||
var/turf/T = get_turf(src)
|
||||
var/list/possible_targets = list()
|
||||
for(var/mob/living/M in T.contents)
|
||||
if(M.mind)
|
||||
if(M.mind in sacrificed)
|
||||
continue
|
||||
if(!iscultist(M))
|
||||
possible_targets.Add(M)
|
||||
var/mob/offering
|
||||
if(possible_targets.len > 1) //If there's more than one target, allow choice
|
||||
offering = input(user, "Choose an offering to sacrifice.", "Unholy Tribute") as null|anything in possible_targets
|
||||
if(!Adjacent(user) || !src || qdeleted(src) || user.incapacitated())
|
||||
return
|
||||
else if(possible_targets.len) //Otherwise, if there's a target at all, pick the only one
|
||||
offering = possible_targets[possible_targets.len]
|
||||
if(!offering)
|
||||
rune_in_use = 0
|
||||
return
|
||||
/*var/obj/item/weapon/nullrod/N = offering.null_rod_check()
|
||||
if(N)
|
||||
user << "<span class='warning'>Something is blocking the Geometer's magic!</span>"
|
||||
log_game("Sacrifice rune failed - target has \a [N]!")
|
||||
fail_invoke()
|
||||
rune_in_use = 0
|
||||
return*/
|
||||
if(((ishuman(offering) || isrobot(offering)) && offering.stat != DEAD) || is_sacrifice_target(offering.mind)) //Requires three people to sacrifice living targets
|
||||
if(invokers.len < 3)
|
||||
for(var/M in invokers)
|
||||
M << "<span class='cultitalic'>[offering] is too greatly linked to the world! You need three acolytes!</span>"
|
||||
fail_invoke()
|
||||
log_game("Sacrifice rune failed - not enough acolytes and target is living")
|
||||
rune_in_use = 0
|
||||
return
|
||||
visible_message("<span class='warning'>[src] pulses blood red!</span>")
|
||||
color = rgb(126, 23, 23)
|
||||
..()
|
||||
sac(invokers, offering)
|
||||
color = initial(color)
|
||||
|
||||
/obj/effect/rune/sacrifice/proc/sac(var/list/invokers, mob/living/T)
|
||||
var/sacrifice_fulfilled
|
||||
if(T)
|
||||
if(istype(T, /mob/living/simple_animal/pet/dog))
|
||||
for(var/M in invokers)
|
||||
var/mob/living/L = M
|
||||
L << "<span class='cultlarge'>\"Even I have standards, such as they are!\"</span>"
|
||||
if(L.reagents)
|
||||
L.reagents.add_reagent("hell_water", 2)
|
||||
if(T.mind)
|
||||
sacrificed.Add(T.mind)
|
||||
if(is_sacrifice_target(T.mind))
|
||||
sacrifice_fulfilled = 1
|
||||
PoolOrNew(/obj/effect/overlay/temp/cult/sac, src.loc)
|
||||
for(var/M in invokers)
|
||||
if(sacrifice_fulfilled)
|
||||
M << "<span class='cultlarge'>\"Yes! This is the one I desire! You have done well.\"</span>"
|
||||
else
|
||||
if(ishuman(T) || isrobot(T))
|
||||
M << "<span class='cultlarge'>\"I accept this sacrifice.\"</span>"
|
||||
else
|
||||
M << "<span class='cultlarge'>\"I accept this meager sacrifice.\"</span>"
|
||||
if(T.mind)
|
||||
var/obj/item/device/soulstone/stone = new /obj/item/device/soulstone(get_turf(src))
|
||||
stone.invisibility = INVISIBILITY_MAXIMUM //so it's not picked up during transfer_soul()
|
||||
if(!stone.transfer_soul("FORCE", T, usr)) //If it cannot be added
|
||||
qdel(stone)
|
||||
if(stone)
|
||||
stone.invisibility = 0
|
||||
if(!T)
|
||||
rune_in_use = 0
|
||||
return
|
||||
if(isrobot(T))
|
||||
playsound(T, 'sound/magic/Disable_Tech.ogg', 100, 1)
|
||||
T.dust() //To prevent the MMI from remaining
|
||||
else
|
||||
playsound(T, 'sound/magic/Disintegrate.ogg', 100, 1)
|
||||
T.gib()
|
||||
rune_in_use = 0
|
||||
|
||||
//Ritual of Dimensional Rending: Calls forth the avatar of Nar-Sie upon the station.
|
||||
/obj/effect/rune/narsie
|
||||
cultist_name = "Summon Nar-Sie"
|
||||
cultist_desc = "tears apart dimensional barriers, calling forth the Geometer. Requires 9 invokers."
|
||||
invocation = "TOK-LYR RQA-NAP G'OLT-ULOFT!!"
|
||||
req_cultists = 9
|
||||
icon = 'icons/effects/96x96.dmi'
|
||||
color = rgb(125,23,23)
|
||||
icon_state = "rune_large"
|
||||
pixel_x = -32 //So the big ol' 96x96 sprite shows up right
|
||||
pixel_y = -32
|
||||
scribe_delay = 450 //how long the rune takes to create
|
||||
scribe_damage = 40.1 //how much damage you take doing it
|
||||
var/used
|
||||
|
||||
/obj/effect/rune/narsie/New()
|
||||
. = ..()
|
||||
poi_list |= src
|
||||
|
||||
/obj/effect/rune/narsie/Destroy()
|
||||
poi_list -= src
|
||||
. = ..()
|
||||
|
||||
/obj/effect/rune/narsie/talismanhide() //can't hide this, and you wouldn't want to
|
||||
return
|
||||
|
||||
/obj/effect/rune/narsie/invoke(var/list/invokers)
|
||||
if(used)
|
||||
return
|
||||
if(z != ZLEVEL_STATION)
|
||||
return
|
||||
if(ticker.mode.name == "cult")
|
||||
var/datum/game_mode/cult/cult_mode = ticker.mode
|
||||
if(!cult_mode.eldergod)
|
||||
for(var/M in invokers)
|
||||
M << "<span class='warning'>Nar-Sie is already on this plane!</span>"
|
||||
log_game("Summon Nar-Sie rune failed - already summoned")
|
||||
return
|
||||
//BEGIN THE SUMMONING
|
||||
used = 1
|
||||
..()
|
||||
world << 'sound/effects/dimensional_rend.ogg' //There used to be a message for this but every time it was changed it got edgier so I removed it
|
||||
var/turf/T = get_turf(src)
|
||||
sleep(40)
|
||||
if(src)
|
||||
color = rgb(255, 0, 0)
|
||||
new /obj/singularity/narsie/large(T) //Causes Nar-Sie to spawn even if the rune has been removed
|
||||
cult_mode.eldergod = 0
|
||||
else
|
||||
for(var/M in invokers)
|
||||
M << "<span class='warning'>Nar-Sie does not respond!</span>"
|
||||
fail_invoke()
|
||||
log_game("Summon Nar-Sie rune failed - gametype is not cult")
|
||||
|
||||
/obj/effect/rune/narsie/attackby(obj/I, mob/user, params) //Since the narsie rune takes a long time to make, add logging to removal.
|
||||
if((istype(I, /obj/item/weapon/tome) && iscultist(user)))
|
||||
user.visible_message("<span class='warning'>[user.name] begins erasing the [src]...</span>", "<span class='notice'>You begin erasing the [src]...</span>")
|
||||
if(do_after(user, 50, target = src)) //Prevents accidental erasures.
|
||||
log_game("Summon Narsie rune erased by [user.mind.key] (ckey) with a tome")
|
||||
message_admins("[key_name_admin(user)] erased a Narsie rune with a tome")
|
||||
..()
|
||||
return
|
||||
else
|
||||
if(istype(I, /obj/item/weapon/nullrod)) //Begone foul magiks. You cannot hinder me.
|
||||
log_game("Summon Narsie rune erased by [user.mind.key] (ckey) using a null rod")
|
||||
message_admins("[key_name_admin(user)] erased a Narsie rune with a null rod")
|
||||
..()
|
||||
return
|
||||
|
||||
//Rite of Resurrection: Requires two corpses. Revives one and gibs the other.
|
||||
/obj/effect/rune/raise_dead
|
||||
cultist_name = "Raise Dead"
|
||||
cultist_desc = "requires two corpses, one on the rune and one adjacent to the rune. The one on the rune is brought to life, the other is turned to ash."
|
||||
invocation = null //Depends on the name of the user - see below
|
||||
icon_state = "1"
|
||||
color = rgb(200, 0, 0)
|
||||
|
||||
/obj/effect/rune/raise_dead/invoke(var/list/invokers)
|
||||
var/turf/T = get_turf(src)
|
||||
var/mob/living/mob_to_sacrifice
|
||||
var/mob/living/mob_to_revive
|
||||
var/list/potential_sacrifice_mobs = list()
|
||||
var/list/potential_revive_mobs = list()
|
||||
var/mob/living/user = invokers[1]
|
||||
if(rune_in_use)
|
||||
return
|
||||
for(var/mob/living/M in orange(1,T))
|
||||
if(M.stat == DEAD && !iscultist(M))
|
||||
potential_sacrifice_mobs |= M
|
||||
if(!potential_sacrifice_mobs.len)
|
||||
user << "<span class='cultitalic'>There are no eligible sacrifices nearby!</span>"
|
||||
log_game("Raise Dead rune failed - no catalyst corpses")
|
||||
fail_invoke()
|
||||
return
|
||||
for(var/mob/living/M in T.contents)
|
||||
if(M.stat == DEAD)
|
||||
potential_revive_mobs |= M
|
||||
if(!potential_revive_mobs.len)
|
||||
user << "<span class='cultitalic'>There is no eligible revival target on the rune!</span>"
|
||||
log_game("Raise Dead rune failed - no corpses to revive")
|
||||
fail_invoke()
|
||||
return
|
||||
mob_to_sacrifice = input(user, "Choose a corpse to sacrifice.", "Corpse to Sacrifice") as null|anything in potential_sacrifice_mobs
|
||||
if(!src || qdeleted(src) || rune_in_use || !validness_checks(mob_to_sacrifice, user, 1))
|
||||
return
|
||||
mob_to_revive = input(user, "Choose a corpse to revive.", "Corpse to Revive") as null|anything in potential_revive_mobs
|
||||
if(!src || qdeleted(src) || rune_in_use || !validness_checks(mob_to_sacrifice, user, 1))
|
||||
return
|
||||
if(!validness_checks(mob_to_revive, user, 0))
|
||||
return
|
||||
rune_in_use = 1
|
||||
if(user.name == "Herbert West")
|
||||
user.say("To life, to life, I bring them!")
|
||||
else
|
||||
user.say("Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!")
|
||||
..()
|
||||
mob_to_sacrifice.visible_message("<span class='warning'><b>[mob_to_sacrifice]'s body rises into the air, connected to [mob_to_revive] by a glowing tendril!</span>")
|
||||
mob_to_revive.Beam(mob_to_sacrifice,icon_state="sendbeam",icon='icons/effects/effects.dmi',time=20)
|
||||
sleep(20)
|
||||
if(!mob_to_sacrifice || !in_range(mob_to_sacrifice, src))
|
||||
rune_in_use = 0
|
||||
return
|
||||
if(!mob_to_revive || mob_to_revive.stat != DEAD)
|
||||
visible_message("<span class='warning'>The glowing tendril snaps against the rune with a shocking crack.</span>")
|
||||
rune_in_use = 0
|
||||
fail_invoke()
|
||||
return
|
||||
mob_to_sacrifice.visible_message("<span class='warning'><b>[mob_to_sacrifice] disintegrates into a pile of bones.</span>")
|
||||
mob_to_sacrifice.dust()
|
||||
mob_to_revive.revive(1, 1) //This does remove disabilities and such, but the rune might actually see some use because of it!
|
||||
mob_to_revive.grab_ghost()
|
||||
mob_to_revive << "<span class='cultlarge'>\"PASNAR SAVRAE YAM'TOTH. Arise.\"</span>"
|
||||
mob_to_revive.visible_message("<span class='warning'>[mob_to_revive] draws in a huge breath, red light shining from their eyes.</span>", \
|
||||
"<span class='cultlarge'>You awaken suddenly from the void. You're alive!</span>")
|
||||
rune_in_use = 0
|
||||
|
||||
/obj/effect/rune/raise_dead/proc/validness_checks(mob/living/target_mob, mob/living/user, saccing)
|
||||
var/turf/T = get_turf(src)
|
||||
if(!user)
|
||||
return 0
|
||||
if(!Adjacent(user) || user.incapacitated())
|
||||
return 0
|
||||
if(!target_mob)
|
||||
fail_invoke()
|
||||
return 0
|
||||
if(saccing)
|
||||
if(!in_range(target_mob, src))
|
||||
user << "<span class='cultitalic'>The sacrificial target has been moved!</span>"
|
||||
fail_invoke()
|
||||
log_game("Raise Dead rune failed - catalyst corpse moved")
|
||||
return 0
|
||||
if(target_mob.stat != DEAD)
|
||||
user << "<span class='cultitalic'>The sacrificial target must be dead!</span>"
|
||||
fail_invoke()
|
||||
log_game("Raise Dead rune failed - catalyst corpse is not dead")
|
||||
return 0
|
||||
else if(!(target_mob in T.contents))
|
||||
user << "<span class='cultitalic'>The corpse to revive has been moved!</span>"
|
||||
fail_invoke()
|
||||
log_game("Raise Dead rune failed - revival target moved")
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/obj/effect/rune/raise_dead/fail_invoke()
|
||||
..()
|
||||
for(var/mob/living/M in range(1,src))
|
||||
if(M.stat == DEAD)
|
||||
M.visible_message("<span class='warning'>[M] twitches.</span>")
|
||||
|
||||
|
||||
//Rite of Disruption: Emits an EMP blast.
|
||||
/obj/effect/rune/emp
|
||||
cultist_name = "Electromagnetic Disruption"
|
||||
cultist_desc = "emits a large electromagnetic pulse, increasing in size for each cultist invoking it, hindering electronics and disabling silicons."
|
||||
invocation = "Ta'gh fara'qha fel d'amar det!"
|
||||
icon_state = "5"
|
||||
allow_excess_invokers = 1
|
||||
color = rgb(77, 148, 255)
|
||||
|
||||
/obj/effect/rune/emp/invoke(var/list/invokers)
|
||||
var/turf/E = get_turf(src)
|
||||
..()
|
||||
visible_message("<span class='warning'>[src] glows blue for a moment before vanishing.</span>")
|
||||
switch(invokers.len)
|
||||
if(1 to 2)
|
||||
playsound(E, 'sound/items/Welder2.ogg', 25, 1)
|
||||
for(var/M in invokers)
|
||||
M << "<span class='warning'>You feel a minute vibration pass through you...</span>"
|
||||
if(3 to 6)
|
||||
playsound(E, 'sound/magic/Disable_Tech.ogg', 50, 1)
|
||||
for(var/M in invokers)
|
||||
M << "<span class='danger'>Your hair stands on end as a shockwave eminates from the rune!</span>"
|
||||
if(7 to INFINITY)
|
||||
playsound(E, 'sound/magic/Disable_Tech.ogg', 100, 1)
|
||||
for(var/M in invokers)
|
||||
var/mob/living/L = M
|
||||
L << "<span class='userdanger'>You chant in unison and a colossal burst of energy knocks you backward!</span>"
|
||||
L.Weaken(2)
|
||||
qdel(src) //delete before pulsing because it's a delay reee
|
||||
empulse(E, 9*invokers.len, 12*invokers.len) // Scales now, from a single room to most of the station depending on # of chanters
|
||||
|
||||
//Rite of Astral Communion: Separates one's spirit from their body. They will take damage while it is active.
|
||||
/obj/effect/rune/astral
|
||||
cultist_name = "Astral Communion"
|
||||
cultist_desc = "severs the link between one's spirit and body. This effect is taxing and one's physical body will take damage while this is active."
|
||||
invocation = "Fwe'sh mah erl nyag r'ya!"
|
||||
icon_state = "6"
|
||||
color = rgb(126, 23, 23)
|
||||
rune_in_use = 0 //One at a time, please!
|
||||
construct_invoke = 0
|
||||
var/mob/living/affecting = null
|
||||
|
||||
/obj/effect/rune/astral/examine(mob/user)
|
||||
..()
|
||||
if(affecting)
|
||||
user << "<span class='cultitalic'>A translucent field encases [user] above the rune!</span>"
|
||||
|
||||
/obj/effect/rune/astral/can_invoke(mob/living/user)
|
||||
if(rune_in_use)
|
||||
user << "<span class='cultitalic'>[src] cannot support more than one body!</span>"
|
||||
log_game("Astral Communion rune failed - more than one user")
|
||||
return list()
|
||||
var/turf/T = get_turf(src)
|
||||
if(!user in T.contents)
|
||||
user << "<span class='cultitalic'>You must be standing on top of [src]!</span>"
|
||||
log_game("Astral Communion rune failed - user not standing on rune")
|
||||
return list()
|
||||
return ..()
|
||||
|
||||
/obj/effect/rune/astral/invoke(var/list/invokers)
|
||||
var/mob/living/user = invokers[1]
|
||||
..()
|
||||
var/turf/T = get_turf(src)
|
||||
rune_in_use = 1
|
||||
affecting = user
|
||||
user.color = "#7e1717"
|
||||
user.visible_message("<span class='warning'>[user] freezes statue-still, glowing an unearthly red.</span>", \
|
||||
"<span class='cult'>You see what lies beyond. All is revealed. While this is a wondrous experience, your physical form will waste away in this state. Hurry...</span>")
|
||||
user.ghostize(1)
|
||||
while(user)
|
||||
if(!affecting)
|
||||
visible_message("<span class='warning'>[src] pulses gently before falling dark.</span>")
|
||||
affecting = null //In case it's assigned to a number or something
|
||||
rune_in_use = 0
|
||||
return
|
||||
affecting.apply_damage(1, BRUTE)
|
||||
if(!(user in T.contents))
|
||||
user.visible_message("<span class='warning'>A spectral tendril wraps around [user] and pulls them back to the rune!</span>")
|
||||
Beam(user,icon_state="drainbeam",icon='icons/effects/effects.dmi',time=2)
|
||||
user.forceMove(get_turf(src)) //NO ESCAPE :^)
|
||||
if(user.key)
|
||||
user.visible_message("<span class='warning'>[user] slowly relaxes, the glow around them dimming.</span>", \
|
||||
"<span class='danger'>You are re-united with your physical form. [src] releases its hold over you.</span>")
|
||||
user.color = initial(user.color)
|
||||
user.Weaken(3)
|
||||
rune_in_use = 0
|
||||
affecting = null
|
||||
return
|
||||
if(user.stat == UNCONSCIOUS)
|
||||
if(prob(10))
|
||||
var/mob/dead/observer/G = user.get_ghost()
|
||||
if(G)
|
||||
G << "<span class='cultitalic'>You feel the link between you and your body weakening... you must hurry!</span>"
|
||||
if(user.stat == DEAD)
|
||||
user.color = initial(user.color)
|
||||
rune_in_use = 0
|
||||
affecting = null
|
||||
var/mob/dead/observer/G = user.get_ghost()
|
||||
if(G)
|
||||
G << "<span class='cultitalic'><b>You suddenly feel your physical form pass on. [src]'s exertion has killed you!</b></span>"
|
||||
return
|
||||
sleep(10)
|
||||
rune_in_use = 0
|
||||
|
||||
|
||||
//Rite of the Corporeal Shield: When invoked, becomes solid and cannot be passed. Invoke again to undo.
|
||||
/obj/effect/rune/wall
|
||||
cultist_name = "Form Barrier"
|
||||
cultist_desc = "when invoked, makes an invisible wall to block passage. Can be invoked again to reverse this."
|
||||
invocation = "Khari'd! Eske'te tannin!"
|
||||
icon_state = "1"
|
||||
color = rgb(255, 0, 0)
|
||||
|
||||
/obj/effect/rune/wall/examine(mob/user)
|
||||
..()
|
||||
if(density)
|
||||
user << "<span class='cultitalic'>There is a barely perceptible shimmering of the air above [src].</span>"
|
||||
|
||||
/obj/effect/rune/wall/invoke(var/list/invokers)
|
||||
var/mob/living/user = invokers[1]
|
||||
..()
|
||||
density = !density
|
||||
user.visible_message("<span class='warning'>[user] [iscarbon(user) ? "places their hands on":"stares intently at"] [src], and [density ? "the air above it begins to shimmer" : "the shimmer above it fades"].</span>", \
|
||||
"<span class='cultitalic'>You channel your life energy into [src], [density ? "preventing" : "allowing"] passage above it.</span>")
|
||||
if(density)
|
||||
var/image/I = image(layer = ABOVE_MOB_LAYER, icon = 'icons/effects/effects.dmi', icon_state = "barriershimmer")
|
||||
I.appearance_flags = RESET_COLOR
|
||||
I.alpha = 60
|
||||
I.color = "#701414"
|
||||
overlays += I
|
||||
else
|
||||
overlays.Cut()
|
||||
if(iscarbon(user))
|
||||
var/mob/living/carbon/C = user
|
||||
C.apply_damage(2, BRUTE, pick("l_arm", "r_arm"))
|
||||
|
||||
|
||||
//Rite of Joined Souls: Summons a single cultist.
|
||||
/obj/effect/rune/summon
|
||||
cultist_name = "Summon Cultist"
|
||||
cultist_desc = "summons a single cultist to the rune. Requires 2 invokers."
|
||||
invocation = "N'ath reth sh'yro eth d'rekkathnor!"
|
||||
req_cultists = 2
|
||||
allow_excess_invokers = 1
|
||||
icon_state = "5"
|
||||
color = rgb(0, 255, 0)
|
||||
|
||||
/obj/effect/rune/summon/invoke(var/list/invokers)
|
||||
var/mob/living/user = invokers[1]
|
||||
var/list/cultists = list()
|
||||
for(var/datum/mind/M in ticker.mode.cult)
|
||||
if(!(M.current in invokers) && M.current && M.current.stat != DEAD)
|
||||
cultists |= M.current
|
||||
var/mob/living/cultist_to_summon = input(user, "Who do you wish to call to [src]?", "Followers of the Geometer") as null|anything in cultists
|
||||
if(!Adjacent(user) || !src || qdeleted(src) || user.incapacitated())
|
||||
return
|
||||
if(!cultist_to_summon)
|
||||
user << "<span class='cultitalic'>You require a summoning target!</span>"
|
||||
fail_invoke()
|
||||
log_game("Summon Cultist rune failed - no target")
|
||||
return
|
||||
if(cultist_to_summon.stat == DEAD)
|
||||
user << "<span class='cultitalic'>[cultist_to_summon] has died!</span>"
|
||||
fail_invoke()
|
||||
log_game("Summon Cultist rune failed - target died")
|
||||
return
|
||||
if(!iscultist(cultist_to_summon))
|
||||
user << "<span class='cultitalic'>[cultist_to_summon] is not a follower of the Geometer!</span>"
|
||||
fail_invoke()
|
||||
log_game("Summon Cultist rune failed - target was deconverted")
|
||||
return
|
||||
if(cultist_to_summon.z > ZLEVEL_SPACEMAX)
|
||||
user << "<span class='cultitalic'>[cultist_to_summon] is not in our dimension!</span>"
|
||||
fail_invoke()
|
||||
log_game("Summon Cultist rune failed - target in away mission")
|
||||
return
|
||||
cultist_to_summon.visible_message("<span class='warning'>[cultist_to_summon] suddenly disappears in a flash of red light!</span>", \
|
||||
"<span class='cultitalic'><b>Overwhelming vertigo consumes you as you are hurled through the air!</b></span>")
|
||||
..()
|
||||
visible_message("<span class='warning'>A foggy shape materializes atop [src] and solidifes into [cultist_to_summon]!</span>")
|
||||
user.apply_damage(10, BRUTE, "head")
|
||||
cultist_to_summon.forceMove(get_turf(src))
|
||||
qdel(src)
|
||||
|
||||
//Rite of Boiling Blood: Deals extremely high amounts of damage to non-cultists nearby
|
||||
/obj/effect/rune/blood_boil
|
||||
cultist_name = "Boil Blood"
|
||||
cultist_desc = "boils the blood of non-believers who can see the rune, dealing extreme amounts of damage. Requires 3 invokers."
|
||||
invocation = "Dedo ol'btoh!"
|
||||
icon_state = "4"
|
||||
color = rgb(200, 0, 0)
|
||||
req_cultists = 3
|
||||
construct_invoke = 0
|
||||
|
||||
/obj/effect/rune/blood_boil/invoke(var/list/invokers)
|
||||
..()
|
||||
var/turf/T = get_turf(src)
|
||||
visible_message("<span class='warning'>[src] briefly bubbles before exploding!</span>")
|
||||
for(var/mob/living/carbon/C in viewers(T))
|
||||
if(!iscultist(C))
|
||||
var/obj/item/weapon/nullrod/N = C.null_rod_check()
|
||||
if(N)
|
||||
C << "<span class='userdanger'>\The [N] suddenly burns hotly before returning to normal!</span>"
|
||||
continue
|
||||
C << "<span class='cultlarge'>Your blood boils in your veins!</span>"
|
||||
C.take_overall_damage(45,45)
|
||||
C.Stun(7)
|
||||
if(is_servant_of_ratvar(C))
|
||||
C << "<span class='userdanger'>You feel unholy darkness dimming the Justiciar's light!</span>"
|
||||
C.adjustStaminaLoss(30)
|
||||
for(var/M in invokers)
|
||||
var/mob/living/L = M
|
||||
L.apply_damage(15, BRUTE, pick("l_arm", "r_arm"))
|
||||
L << "<span class='cultitalic'>[src] saps your strength!</span>"
|
||||
qdel(src)
|
||||
explosion(T, -1, 0, 1, 5)
|
||||
|
||||
|
||||
//Rite of Spectral Manifestation: Summons a ghost on top of the rune as a cultist human with no items. User must stand on the rune at all times, and takes damage for each summoned ghost.
|
||||
/obj/effect/rune/manifest
|
||||
cultist_name = "Manifest Spirit"
|
||||
cultist_desc = "manifests a spirit as a servant of the Geometer. The invoker must not move from atop the rune, and will take damage for each summoned spirit."
|
||||
invocation = "Gal'h'rfikk harfrandid mud'gib!" //how the fuck do you pronounce this
|
||||
icon_state = "6"
|
||||
construct_invoke = 0
|
||||
color = rgb(200, 0, 0)
|
||||
|
||||
/obj/effect/rune/manifest/New(loc)
|
||||
..()
|
||||
notify_ghosts("Manifest rune created in [get_area(src)].", 'sound/effects/ghost2.ogg', source = src)
|
||||
|
||||
/obj/effect/rune/manifest/can_invoke(mob/living/user)
|
||||
if(!(user in get_turf(src)))
|
||||
user << "<span class='cultitalic'>You must be standing on [src]!</span>"
|
||||
fail_invoke()
|
||||
log_game("Manifest rune failed - user not standing on rune")
|
||||
return list()
|
||||
var/list/ghosts_on_rune = list()
|
||||
for(var/mob/dead/observer/O in get_turf(src))
|
||||
if(O.client && !jobban_isbanned(O, ROLE_CULTIST))
|
||||
ghosts_on_rune |= O
|
||||
if(!ghosts_on_rune.len)
|
||||
user << "<span class='cultitalic'>There are no spirits near [src]!</span>"
|
||||
fail_invoke()
|
||||
log_game("Manifest rune failed - no nearby ghosts")
|
||||
return list()
|
||||
return ..()
|
||||
|
||||
/obj/effect/rune/manifest/invoke(var/list/invokers)
|
||||
var/mob/living/user = invokers[1]
|
||||
var/list/ghosts_on_rune = list()
|
||||
for(var/mob/dead/observer/O in get_turf(src))
|
||||
if(O.client && !jobban_isbanned(O, ROLE_CULTIST))
|
||||
ghosts_on_rune |= O
|
||||
var/mob/dead/observer/ghost_to_spawn = pick(ghosts_on_rune)
|
||||
var/mob/living/carbon/human/new_human = new(get_turf(src))
|
||||
new_human.real_name = ghost_to_spawn.real_name
|
||||
new_human.alpha = 150 //Makes them translucent
|
||||
..()
|
||||
visible_message("<span class='warning'>A cloud of red mist forms above [src], and from within steps... a man.</span>")
|
||||
user << "<span class='cultitalic'>Your blood begins flowing into [src]. You must remain in place and conscious to maintain the forms of those summoned. This will hurt you slowly but surely...</span>"
|
||||
var/obj/machinery/shield/N = new(get_turf(src))
|
||||
N.name = "Invoker's Shield"
|
||||
N.desc = "A weak shield summoned by cultists to protect them while they carry out delicate rituals"
|
||||
N.color = "red"
|
||||
N.health = 20
|
||||
N.mouse_opacity = 0
|
||||
new_human.key = ghost_to_spawn.key
|
||||
ticker.mode.add_cultist(new_human.mind, 0)
|
||||
new_human << "<span class='cultitalic'><b>You are a servant of the Geometer. You have been made semi-corporeal by the cult of Nar-Sie, and you are to serve them at all costs.</b></span>"
|
||||
|
||||
while(user in get_turf(src))
|
||||
if(user.stat)
|
||||
break
|
||||
user.apply_damage(0.1, BRUTE)
|
||||
sleep(3)
|
||||
|
||||
qdel(N)
|
||||
if(new_human)
|
||||
new_human.visible_message("<span class='warning'>[new_human] suddenly dissolves into bones and ashes.</span>", \
|
||||
"<span class='cultlarge'>Your link to the world fades. Your form breaks apart.</span>")
|
||||
for(var/obj/I in new_human)
|
||||
new_human.unEquip(I)
|
||||
new_human.dust()
|
||||
@@ -0,0 +1,416 @@
|
||||
/obj/item/weapon/paper/talisman
|
||||
var/cultist_name = "talisman"
|
||||
var/cultist_desc = "A basic talisman. It serves no purpose."
|
||||
var/invocation = "Naise meam!"
|
||||
var/uses = 1
|
||||
var/health_cost = 0 //The amount of health taken from the user when invoking the talisman
|
||||
|
||||
/obj/item/weapon/paper/talisman/examine(mob/user)
|
||||
if(iscultist(user) || user.stat == DEAD)
|
||||
user << "<b>Name:</b> [cultist_name]"
|
||||
user << "<b>Effect:</b> [cultist_desc]"
|
||||
user << "<b>Uses Remaining:</b> [uses]"
|
||||
else
|
||||
user << "<span class='danger'>There are indecipherable images scrawled on the paper in what looks to be... <i>blood?</i></span>"
|
||||
|
||||
/obj/item/weapon/paper/talisman/attack_self(mob/living/user)
|
||||
if(!iscultist(user))
|
||||
user << "<span class='danger'>There are indecipherable images scrawled on the paper in what looks to be... <i>blood?</i></span>"
|
||||
return
|
||||
if(invoke(user))
|
||||
uses--
|
||||
if(uses <= 0)
|
||||
user.drop_item()
|
||||
qdel(src)
|
||||
|
||||
/obj/item/weapon/paper/talisman/proc/invoke(mob/living/user, successfuluse = 1)
|
||||
. = successfuluse
|
||||
if(successfuluse) //if the calling whatever says we succeed, do the fancy stuff
|
||||
if(invocation)
|
||||
user.whisper(invocation)
|
||||
if(health_cost && iscarbon(user))
|
||||
var/mob/living/carbon/C = user
|
||||
C.apply_damage(health_cost, BRUTE, pick("l_arm", "r_arm"))
|
||||
|
||||
//Malformed Talisman: If something goes wrong.
|
||||
/obj/item/weapon/paper/talisman/malformed
|
||||
cultist_name = "malformed talisman"
|
||||
cultist_desc = "A talisman with gibberish scrawlings. No good can come from invoking this."
|
||||
invocation = "Ra'sha yoka!"
|
||||
|
||||
/obj/item/weapon/paper/talisman/malformed/invoke(mob/living/user, successfuluse = 1)
|
||||
user << "<span class='cultitalic'>You feel a pain in your head. The Geometer is displeased.</span>"
|
||||
if(iscarbon(user))
|
||||
var/mob/living/carbon/C = user
|
||||
C.apply_damage(10, BRUTE, "head")
|
||||
|
||||
//Supply Talisman: Has a few unique effects. Granted only to starter cultists.
|
||||
/obj/item/weapon/paper/talisman/supply
|
||||
cultist_name = "Supply Talisman"
|
||||
cultist_desc = "A multi-use talisman that can create various objects. Intended to increase the cult's strength early on."
|
||||
invocation = null
|
||||
uses = 3
|
||||
|
||||
/obj/item/weapon/paper/talisman/supply/invoke(mob/living/user, successfuluse = 1)
|
||||
var/dat = "<B>There are [uses] bloody runes on the parchment.</B><BR>"
|
||||
dat += "Please choose the chant to be imbued into the fabric of reality.<BR>"
|
||||
dat += "<HR>"
|
||||
dat += "<A href='?src=\ref[src];rune=newtome'>N'ath reth sh'yro eth d'raggathnor!</A> - Summons an arcane tome, used to scribe runes and communicate with other cultists.<BR>"
|
||||
dat += "<A href='?src=\ref[src];rune=teleport'>Sas'so c'arta forbici!</A> - Allows you to move to a selected teleportation rune.<BR>"
|
||||
dat += "<A href='?src=\ref[src];rune=emp'>Ta'gh fara'qha fel d'amar det!</A> - Allows you to destroy technology in a short range.<BR>"
|
||||
dat += "<A href='?src=\ref[src];rune=runestun'>Fuu ma'jin!</A> - Allows you to stun a person by attacking them with the talisman.<BR>"
|
||||
dat += "<A href='?src=\ref[src];rune=veiling'>Kla'atu barada nikt'o!</A> - Two use talisman, first use makes all nearby runes invisible, second use reveals nearby hidden runes.<BR>"
|
||||
dat += "<A href='?src=\ref[src];rune=soulstone'>Kal'om neth!</A> - Summons a soul stone, used to capure the spirits of dead or dying humans.<BR>"
|
||||
dat += "<A href='?src=\ref[src];rune=construct'>Daa'ig osk!</A> - Summons a construct shell for use with soulstone-captured souls. It is too large to carry on your person.<BR>"
|
||||
var/datum/browser/popup = new(user, "talisman", "", 400, 400)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
return 0
|
||||
|
||||
/obj/item/weapon/paper/talisman/supply/Topic(href, href_list)
|
||||
if(src)
|
||||
if(usr.stat || usr.restrained() || !in_range(src, usr))
|
||||
return
|
||||
if(href_list["rune"])
|
||||
switch(href_list["rune"])
|
||||
if("newtome")
|
||||
var/obj/item/weapon/tome/T = new(usr)
|
||||
usr.put_in_hands(T)
|
||||
if("teleport")
|
||||
var/obj/item/weapon/paper/talisman/teleport/T = new(usr)
|
||||
usr.put_in_hands(T)
|
||||
if("emp")
|
||||
var/obj/item/weapon/paper/talisman/emp/T = new(usr)
|
||||
usr.put_in_hands(T)
|
||||
if("runestun")
|
||||
var/obj/item/weapon/paper/talisman/stun/T = new(usr)
|
||||
usr.put_in_hands(T)
|
||||
if("soulstone")
|
||||
var/obj/item/device/soulstone/T = new(usr)
|
||||
usr.put_in_hands(T)
|
||||
if("construct")
|
||||
new /obj/structure/constructshell(get_turf(usr))
|
||||
if("veiling")
|
||||
var/obj/item/weapon/paper/talisman/true_sight/T = new(usr)
|
||||
usr.put_in_hands(T)
|
||||
src.uses--
|
||||
if(src.uses <= 0)
|
||||
if(iscarbon(usr))
|
||||
var/mob/living/carbon/C = usr
|
||||
C.drop_item()
|
||||
visible_message("<span class='warning'>[src] crumbles to dust.</span>")
|
||||
qdel(src)
|
||||
|
||||
/obj/item/weapon/paper/talisman/supply/weak
|
||||
uses = 2
|
||||
|
||||
//Rite of Translocation: Same as rune
|
||||
/obj/item/weapon/paper/talisman/teleport
|
||||
cultist_name = "Talisman of Teleportation"
|
||||
cultist_desc = "A single-use talisman that will teleport a user to a random rune of the same keyword."
|
||||
color = "#551A8B" // purple
|
||||
invocation = "Sas'so c'arta forbici!"
|
||||
health_cost = 5
|
||||
|
||||
/obj/item/weapon/paper/talisman/teleport/invoke(mob/living/user, successfuluse = 1)
|
||||
var/list/potential_runes = list()
|
||||
var/list/teleportnames = list()
|
||||
var/list/duplicaterunecount = list()
|
||||
for(var/R in teleport_runes)
|
||||
var/obj/effect/rune/teleport/T = R
|
||||
var/resultkey = T.listkey
|
||||
if(resultkey in teleportnames)
|
||||
duplicaterunecount[resultkey]++
|
||||
resultkey = "[resultkey] ([duplicaterunecount[resultkey]])"
|
||||
else
|
||||
teleportnames.Add(resultkey)
|
||||
duplicaterunecount[resultkey] = 1
|
||||
potential_runes[resultkey] = T
|
||||
|
||||
if(!potential_runes.len)
|
||||
user << "<span class='warning'>There are no valid runes to teleport to!</span>"
|
||||
log_game("Teleport talisman failed - no other teleport runes")
|
||||
return ..(user, 0)
|
||||
|
||||
if(user.z > ZLEVEL_SPACEMAX)
|
||||
user << "<span class='cultitalic'>You are not in the right dimension!</span>"
|
||||
log_game("Teleport talisman failed - user in away mission")
|
||||
return ..(user, 0)
|
||||
|
||||
var/input_rune_key = input(user, "Choose a rune to teleport to.", "Rune to Teleport to") as null|anything in potential_runes //we know what key they picked
|
||||
var/obj/effect/rune/teleport/actual_selected_rune = potential_runes[input_rune_key] //what rune does that key correspond to?
|
||||
if(!actual_selected_rune)
|
||||
return ..(user, 0)
|
||||
user.visible_message("<span class='warning'>Dust flows from [user]'s hand, and they disappear in a flash of red light!</span>", \
|
||||
"<span class='cultitalic'>You speak the words of the talisman and find yourself somewhere else!</span>")
|
||||
user.forceMove(get_turf(actual_selected_rune))
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/item/weapon/paper/talisman/summon_tome
|
||||
cultist_name = "Talisman of Tome Summoning"
|
||||
cultist_desc = "A one-use talisman that will call an untranslated tome from the archives of the Geometer."
|
||||
color = "#512727" // red-black
|
||||
invocation = "N'ath reth sh'yro eth d'raggathnor!"
|
||||
health_cost = 1
|
||||
|
||||
/obj/item/weapon/paper/talisman/summon_tome/invoke(mob/living/user, successfuluse = 1)
|
||||
. = ..()
|
||||
user.visible_message("<span class='warning'>[user]'s hand glows red for a moment.</span>", \
|
||||
"<span class='cultitalic'>You speak the words of the talisman!</span>")
|
||||
new /obj/item/weapon/tome(get_turf(user))
|
||||
user.visible_message("<span class='warning'>A tome appears at [user]'s feet!</span>", \
|
||||
"<span class='cultitalic'>An arcane tome materializes at your feet.</span>")
|
||||
|
||||
/obj/item/weapon/paper/talisman/true_sight
|
||||
cultist_name = "Talisman of Veiling"
|
||||
cultist_desc = "A multi-use talisman that hides nearby runes. On its second use, will reveal nearby runes."
|
||||
color = "#9c9c9c" // grey
|
||||
invocation = "Kla'atu barada nikt'o!"
|
||||
health_cost = 1
|
||||
uses = 2
|
||||
var/revealing = FALSE //if it reveals or not
|
||||
|
||||
/obj/item/weapon/paper/talisman/true_sight/invoke(mob/living/user, successfuluse = 1)
|
||||
. = ..()
|
||||
if(!revealing)
|
||||
user.visible_message("<span class='warning'>Thin grey dust falls from [user]'s hand!</span>", \
|
||||
"<span class='cultitalic'>You speak the words of the talisman, hiding nearby runes.</span>")
|
||||
invocation = "Nikt'o barada kla'atu!"
|
||||
revealing = TRUE
|
||||
for(var/obj/effect/rune/R in range(3,user))
|
||||
R.talismanhide()
|
||||
else
|
||||
user.visible_message("<span class='warning'>A flash of light shines from [user]'s hand!</span>", \
|
||||
"<span class='cultitalic'>You speak the words of the talisman, revealing nearby runes.</span>")
|
||||
for(var/obj/effect/rune/R in range(3,user))
|
||||
R.talismanreveal()
|
||||
|
||||
//Rite of False Truths: Same as rune
|
||||
/obj/item/weapon/paper/talisman/make_runes_fake
|
||||
cultist_name = "Talisman of Disguising"
|
||||
cultist_desc = "A talisman that will make nearby runes appear fake."
|
||||
color = "#ff80d5" // honk
|
||||
invocation = "By'o nar'nar!"
|
||||
|
||||
/obj/item/weapon/paper/talisman/make_runes_fake/invoke(mob/living/user, successfuluse = 1)
|
||||
. = ..()
|
||||
user.visible_message("<span class='warning'>Dust flows from [user]s hand.</span>", \
|
||||
"<span class='cultitalic'>You speak the words of the talisman, making nearby runes appear fake.</span>")
|
||||
for(var/obj/effect/rune/R in orange(6,user))
|
||||
R.desc = "A rune vandalizing the station."
|
||||
|
||||
|
||||
//Rite of Disruption: Weaker than rune
|
||||
/obj/item/weapon/paper/talisman/emp
|
||||
cultist_name = "Talisman of Electromagnetic Pulse"
|
||||
cultist_desc = "A talisman that will cause a moderately-sized electromagnetic pulse."
|
||||
color = "#4d94ff" // light blue
|
||||
invocation = "Ta'gh fara'qha fel d'amar det!"
|
||||
health_cost = 5
|
||||
|
||||
/obj/item/weapon/paper/talisman/emp/invoke(mob/living/user, successfuluse = 1)
|
||||
. = ..()
|
||||
user.visible_message("<span class='warning'>[user]'s hand flashes a bright blue!</span>", \
|
||||
"<span class='cultitalic'>You speak the words of the talisman, emitting an EMP blast.</span>")
|
||||
empulse(src, 4, 8)
|
||||
|
||||
|
||||
//Rite of Disorientation: Stuns and inhibit speech on a single target for quite some time
|
||||
/obj/item/weapon/paper/talisman/stun
|
||||
cultist_name = "Talisman of Stunning"
|
||||
cultist_desc = "A talisman that will stun and inhibit speech on a single target. To use, attack target directly."
|
||||
color = "#ff0000" // red
|
||||
invocation = "Fuu ma'jin!"
|
||||
health_cost = 10
|
||||
|
||||
/obj/item/weapon/paper/talisman/stun/invoke(mob/living/user, successfuluse = 0)
|
||||
if(successfuluse) //if we're forced to be successful(we normally aren't) then do the normal stuff
|
||||
return ..()
|
||||
if(iscultist(user))
|
||||
user << "<span class='warning'>To use this talisman, attack the target directly.</span>"
|
||||
else
|
||||
user << "<span class='danger'>There are indecipherable images scrawled on the paper in what looks to be... <i>blood?</i></span>"
|
||||
return 0
|
||||
|
||||
/obj/item/weapon/paper/talisman/stun/attack(mob/living/target, mob/living/user, successfuluse = 1)
|
||||
if(iscultist(user))
|
||||
invoke(user, 1)
|
||||
user.visible_message("<span class='warning'>[user] holds up [src], which explodes in a flash of red light!</span>", \
|
||||
"<span class='cultitalic'>You stun [target] with the talisman!</span>")
|
||||
var/obj/item/weapon/nullrod/N = locate() in target
|
||||
if(N)
|
||||
target.visible_message("<span class='warning'>[target]'s holy weapon absorbs the talisman's light!</span>", \
|
||||
"<span class='userdanger'>Your holy weapon absorbs the blinding light!</span>")
|
||||
else
|
||||
target.Weaken(10)
|
||||
target.Stun(10)
|
||||
target.flash_eyes(1,1)
|
||||
if(issilicon(target))
|
||||
var/mob/living/silicon/S = target
|
||||
S.emp_act(1)
|
||||
else if(iscarbon(target))
|
||||
var/mob/living/carbon/C = target
|
||||
C.silent += 5
|
||||
C.stuttering += 15
|
||||
C.cultslurring += 15
|
||||
C.Jitter(15)
|
||||
if(is_servant_of_ratvar(target))
|
||||
target.adjustBruteLoss(15)
|
||||
user.drop_item()
|
||||
qdel(src)
|
||||
return
|
||||
..()
|
||||
|
||||
|
||||
//Rite of Arming: Equips cultist armor on the user, where available
|
||||
/obj/item/weapon/paper/talisman/armor
|
||||
cultist_name = "Talisman of Arming"
|
||||
cultist_desc = "A talisman that will equip the invoker with cultist equipment if there is a slot to equip it to."
|
||||
color = "#33cc33" // green
|
||||
invocation = "N'ath reth sh'yro eth draggathnor!"
|
||||
|
||||
/obj/item/weapon/paper/talisman/armor/invoke(mob/living/user, successfuluse = 1)
|
||||
. = ..()
|
||||
user.visible_message("<span class='warning'>Otherworldly armor suddenly appears on [user]!</span>", \
|
||||
"<span class='cultitalic'>You speak the words of the talisman, arming yourself!</span>")
|
||||
user.equip_to_slot_or_del(new /obj/item/clothing/head/culthood/alt(user), slot_head)
|
||||
user.equip_to_slot_or_del(new /obj/item/clothing/suit/cultrobes/alt(user), slot_wear_suit)
|
||||
user.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult/alt(user), slot_shoes)
|
||||
user.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/cultpack(user), slot_back)
|
||||
user.drop_item()
|
||||
user.put_in_hands(new /obj/item/weapon/melee/cultblade(user))
|
||||
user.put_in_hands(new /obj/item/weapon/restraints/legcuffs/bola/cult(user))
|
||||
|
||||
/obj/item/weapon/paper/talisman/armor/attack(mob/living/target, mob/living/user)
|
||||
if(iscultist(user) && iscultist(target))
|
||||
user.drop_item()
|
||||
invoke(target)
|
||||
qdel(src)
|
||||
return
|
||||
..()
|
||||
|
||||
|
||||
//Talisman of Horrors: Breaks the mind of the victim with nightmarish hallucinations
|
||||
/obj/item/weapon/paper/talisman/horror
|
||||
cultist_name = "Talisman of Horrors"
|
||||
cultist_desc = "A talisman that will break the mind of the victim with nightmarish hallucinations."
|
||||
color = "#ffb366" // light orange
|
||||
invocation = "Lo'Nab Na'Dm!"
|
||||
|
||||
/obj/item/weapon/paper/talisman/horror/attack(mob/living/target, mob/living/user)
|
||||
if(iscultist(user))
|
||||
user << "<span class='cultitalic'>You disturb [target] with visons of the end!</span>"
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/H = target
|
||||
H.reagents.add_reagent("mindbreaker", 25)
|
||||
if(is_servant_of_ratvar(target))
|
||||
target << "<span class='userdanger'>You see a brief but horrible vision of Ratvar, rusted and scrapped, being torn apart.</span>"
|
||||
target.emote("scream")
|
||||
target.confused = max(0, target.confused + 3)
|
||||
target.flash_eyes()
|
||||
qdel(src)
|
||||
|
||||
|
||||
//Talisman of Fabrication: Creates a construct shell out of 25 metal sheets.
|
||||
/obj/item/weapon/paper/talisman/construction
|
||||
cultist_name = "Talisman of Construction"
|
||||
cultist_desc = "Use this talisman on at least twenty-five metal sheets to create an empty construct shell"
|
||||
invocation = "Ethra p'ni dedol!"
|
||||
color = "#000000" // black
|
||||
|
||||
/obj/item/weapon/paper/talisman/construction/attack_self(mob/living/user)
|
||||
if(iscultist(user))
|
||||
user << "<span class='warning'>To use this talisman, place it upon a stack of metal sheets.</span>"
|
||||
else
|
||||
user << "<span class='danger'>There are indecipherable images scrawled on the paper in what looks to be... <i>blood?</i></span>"
|
||||
|
||||
|
||||
/obj/item/weapon/paper/talisman/construction/attack(obj/M,mob/living/user)
|
||||
if(iscultist(user))
|
||||
user << "<span class='cultitalic'>This talisman will only work on a stack of metal sheets!</span>"
|
||||
log_game("Construct talisman failed - not a valid target")
|
||||
|
||||
/obj/item/weapon/paper/talisman/construction/afterattack(obj/item/stack/sheet/target, mob/user, proximity_flag, click_parameters)
|
||||
..()
|
||||
if(proximity_flag && iscultist(user))
|
||||
if(istype(target, /obj/item/stack/sheet/metal))
|
||||
var/turf/T = get_turf(target)
|
||||
if(target.use(25))
|
||||
new /obj/structure/constructshell(T)
|
||||
user << "<span class='warning'>The talisman clings to the metal and twists it into a construct shell!</span>"
|
||||
user << sound('sound/effects/magic.ogg',0,1,25)
|
||||
qdel(src)
|
||||
if(istype(target, /obj/item/stack/sheet/plasteel))
|
||||
var/quantity = target.amount
|
||||
var/turf/T = get_turf(target)
|
||||
new /obj/item/stack/sheet/runed_metal(T,quantity)
|
||||
target.use(quantity)
|
||||
user << "<span class='warning'>The talisman clings to the plasteel, transforming it into runed metal!</span>"
|
||||
user << sound('sound/effects/magic.ogg',0,1,25)
|
||||
qdel(src)
|
||||
else
|
||||
user << "<span class='warning'>The talisman must be used on metal or plasteel!</span>"
|
||||
|
||||
|
||||
//Talisman of Shackling: Applies special cuffs directly from the talisman
|
||||
/obj/item/weapon/paper/talisman/shackle
|
||||
cultist_name = "Talisman of Shackling"
|
||||
cultist_desc = "Use this talisman on a victim to handcuff them with dark bindings."
|
||||
invocation = "In'totum Lig'abis!"
|
||||
color = "#B27300" // burnt-orange
|
||||
uses = 4
|
||||
|
||||
/obj/item/weapon/paper/talisman/shackle/invoke(mob/living/user, successfuluse = 0)
|
||||
if(successfuluse) //if we're forced to be successful(we normally aren't) then do the normal stuff
|
||||
return ..()
|
||||
if(iscultist(user))
|
||||
user << "<span class='warning'>To use this talisman, attack the target directly.</span>"
|
||||
else
|
||||
user << "<span class='danger'>There are indecipherable images scrawled on the paper in what looks to be... <i>blood?</i></span>"
|
||||
return 0
|
||||
|
||||
/obj/item/weapon/paper/talisman/shackle/attack(mob/living/carbon/target, mob/living/user)
|
||||
if(iscultist(user) && istype(target))
|
||||
if(target.stat == DEAD)
|
||||
user.visible_message("<span class='cultitalic'>This talisman's magic does not affect the dead!</span>")
|
||||
return
|
||||
CuffAttack(target, user)
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/item/weapon/paper/talisman/shackle/proc/CuffAttack(mob/living/carbon/C, mob/living/user)
|
||||
if(!C.handcuffed)
|
||||
invoke(user, 1)
|
||||
playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2)
|
||||
C.visible_message("<span class='danger'>[user] begins restraining [C] with dark magic!</span>", \
|
||||
"<span class='userdanger'>[user] begins shaping a dark magic around your wrists!</span>")
|
||||
if(do_mob(user, C, 30))
|
||||
if(!C.handcuffed)
|
||||
C.handcuffed = new /obj/item/weapon/restraints/handcuffs/energy/cult/used(C)
|
||||
C.update_handcuffed()
|
||||
user << "<span class='notice'>You shackle [C].</span>"
|
||||
add_logs(user, C, "handcuffed")
|
||||
uses--
|
||||
else
|
||||
user << "<span class='warning'>[C] is already bound.</span>"
|
||||
else
|
||||
user << "<span class='warning'>You fail to shackle [C].</span>"
|
||||
else
|
||||
user << "<span class='warning'>[C] is already bound.</span>"
|
||||
if(uses <= 0)
|
||||
user.drop_item()
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
/obj/item/weapon/restraints/handcuffs/energy/cult //For the talisman of shackling
|
||||
name = "cult shackles"
|
||||
desc = "Shackles that bind the wrists with sinister magic."
|
||||
trashtype = /obj/item/weapon/restraints/handcuffs/energy/used
|
||||
origin_tech = "materials=2;magnets=5"
|
||||
flags = DROPDEL
|
||||
|
||||
/obj/item/weapon/restraints/handcuffs/energy/cult/used/dropped(mob/user)
|
||||
user.visible_message("<span class='danger'>[user]'s shackles shatter in a discharge of dark magic!</span>", \
|
||||
"<span class='userdanger'>Your [src] shatters in a discharge of dark magic!</span>")
|
||||
. = ..()
|
||||
@@ -0,0 +1,40 @@
|
||||
var/global/list/whiteness = list (
|
||||
/obj/item/clothing/under/color/white = 2,
|
||||
/obj/item/clothing/under/rank/bartender = 1,
|
||||
/obj/item/clothing/under/rank/chef = 1,
|
||||
/obj/item/clothing/under/rank/chief_engineer = 1,
|
||||
/obj/item/clothing/under/rank/scientist = 1,
|
||||
/obj/item/clothing/under/rank/chemist = 1,
|
||||
/obj/item/clothing/under/rank/chief_medical_officer = 1,
|
||||
/obj/item/clothing/under/rank/geneticist = 1,
|
||||
/obj/item/clothing/under/rank/virologist = 1,
|
||||
/obj/item/clothing/under/rank/nursesuit = 1,
|
||||
/obj/item/clothing/under/rank/medical = 1,
|
||||
/obj/item/clothing/under/rank/det = 1,
|
||||
/obj/item/clothing/under/suit_jacket/white = 0.5,
|
||||
/obj/item/clothing/under/burial = 1
|
||||
)
|
||||
|
||||
|
||||
|
||||
/mob/living/proc/check_devil_bane_multiplier(obj/item/weapon, mob/living/attacker)
|
||||
switch(mind.devilinfo.bane)
|
||||
if(BANE_WHITECLOTHES)
|
||||
if(istype(attacker, /mob/living/carbon/human))
|
||||
var/mob/living/carbon/human/H = attacker
|
||||
if(H.w_uniform && istype(H.w_uniform, /obj/item/clothing/under))
|
||||
var/obj/item/clothing/under/U = H.w_uniform
|
||||
if(whiteness[U.type])
|
||||
src.visible_message("<span class='warning'>[src] seems to have been harmed by the purity of [attacker]'s clothes.</span>", "<span class='notice'>Unsullied white clothing is disrupting your form.</span>")
|
||||
return whiteness[U.type] + 1
|
||||
if(BANE_TOOLBOX)
|
||||
if(istype(weapon,/obj/item/weapon/storage/toolbox))
|
||||
src.visible_message("<span class='warning'>The [weapon] seems unusually robust this time.</span>", "<span class='notice'>The [weapon] is your unmaking!</span>")
|
||||
return 2.5 // Will take four hits with a normal toolbox.
|
||||
if(BANE_HARVEST)
|
||||
if(istype(weapon,/obj/item/weapon/reagent_containers/food/snacks/grown/))
|
||||
src.visible_message("<span class='warning'>The spirits of the harvest aid in the exorcism.</span>", "<span class='notice'>The harvest spirits are harming you.</span>")
|
||||
src.Weaken(2)
|
||||
qdel(weapon)
|
||||
return 2
|
||||
return 1
|
||||
@@ -0,0 +1,111 @@
|
||||
/datum/game_mode
|
||||
var/list/datum/mind/sintouched = list()
|
||||
var/list/datum/mind/devils = list()
|
||||
var/devil_ascended = 0 // Number of arch devils on station
|
||||
|
||||
/datum/game_mode/proc/auto_declare_completion_sintouched()
|
||||
var/text = ""
|
||||
if(sintouched.len)
|
||||
text += "<br><span class='big'><b>The sintouched were:</b></span>"
|
||||
var/list/sintouchedUnique = uniqueList(sintouched)
|
||||
for(var/S in sintouchedUnique)
|
||||
var/datum/mind/sintouched_mind = S
|
||||
text += printplayer(sintouched_mind)
|
||||
text += printobjectives(sintouched_mind)
|
||||
text += "<br>"
|
||||
text += "<br>"
|
||||
world << text
|
||||
|
||||
/datum/game_mode/proc/auto_declare_completion_devils()
|
||||
/var/text = ""
|
||||
if(devils.len)
|
||||
text += "<br><span class='big'><b>The devils were:</b></span>"
|
||||
for(var/D in devils)
|
||||
var/datum/mind/devil = D
|
||||
text += printplayer(devil)
|
||||
text += printdevilinfo(devil)
|
||||
text += printobjectives(devil)
|
||||
text += "<br>"
|
||||
world << text
|
||||
|
||||
/datum/game_mode/devil
|
||||
|
||||
|
||||
/datum/game_mode/proc/finalize_devil(datum/mind/devil_mind)
|
||||
var/mob/living/carbon/human/S = devil_mind.current
|
||||
var/trueName= randomDevilName()
|
||||
var/datum/objective/devil/soulquantity/soulquant = new
|
||||
soulquant.owner = devil_mind
|
||||
var/datum/objective/devil/obj_2 = pick(new /datum/objective/devil/soulquality(null), new /datum/objective/devil/sintouch(null))
|
||||
obj_2.owner = devil_mind
|
||||
devil_mind.objectives += obj_2
|
||||
devil_mind.objectives += soulquant
|
||||
devil_mind.devilinfo = devilInfo(trueName, 1)
|
||||
devil_mind.store_memory("Your devilic true name is [devil_mind.devilinfo.truename]<br>[lawlorify[LAW][devil_mind.devilinfo.ban]]<br>You may not use violence to coerce someone into selling their soul.<br>You may not directly and knowingly physically harm a devil, other than yourself.<br>[lawlorify[LAW][devil_mind.devilinfo.bane]]<br>[lawlorify[LAW][devil_mind.devilinfo.obligation]]<br>[lawlorify[LAW][devil_mind.devilinfo.banish]]<br>")
|
||||
devil_mind.devilinfo.owner = devil_mind
|
||||
devil_mind.devilinfo.give_base_spells(1)
|
||||
spawn(10)
|
||||
devil_mind.devilinfo.update_hud()
|
||||
if(devil_mind.assigned_role == "Clown")
|
||||
S << "<span class='notice'>Your infernal nature has allowed you to overcome your clownishness.</span>"
|
||||
S.dna.remove_mutation(CLOWNMUT)
|
||||
|
||||
/datum/mind/proc/announceDevilLaws()
|
||||
if(!devilinfo)
|
||||
return
|
||||
current << "<span class='warning'><b>You remember your link to the infernal. You are [src.devilinfo.truename], an agent of hell, a devil. And you were sent to the plane of creation for a reason. A greater purpose. Convince the crew to sin, and embroiden Hell's grasp.</b></span>"
|
||||
current << "<span class='warning'><b>However, your infernal form is not without weaknesses.</b></span>"
|
||||
current << "You may not use violence to coerce someone into selling their soul."
|
||||
current << "You may not directly and knowingly physically harm a devil, other than yourself."
|
||||
current << lawlorify[LAW][src.devilinfo.bane]
|
||||
current << lawlorify[LAW][src.devilinfo.ban]
|
||||
current << lawlorify[LAW][src.devilinfo.obligation]
|
||||
current << lawlorify[LAW][src.devilinfo.banish]
|
||||
current << "<br/><br/><span class='warning'>Remember, the crew can research your weaknesses if they find out your devil name.</span><br>"
|
||||
var/obj_count = 1
|
||||
current << "<span class='notice'>Your current objectives:</span>"
|
||||
for(var/O in objectives)
|
||||
var/datum/objective/objective = O
|
||||
current << "<B>Objective #[obj_count]</B>: [objective.explanation_text]"
|
||||
obj_count++
|
||||
|
||||
/datum/game_mode/proc/printdevilinfo(datum/mind/ply)
|
||||
if(!ply.devilinfo)
|
||||
return ""
|
||||
var/text = "</br>The devil's true name is: [ply.devilinfo.truename]</br>"
|
||||
text += "The devil's bans were:</br>"
|
||||
text += " [lawlorify[LORE][ply.devilinfo.ban]]</br>"
|
||||
text += " [lawlorify[LORE][ply.devilinfo.bane]]</br>"
|
||||
text += " [lawlorify[LORE][ply.devilinfo.obligation]]</br>"
|
||||
text += " [lawlorify[LORE][ply.devilinfo.banish]]</br>"
|
||||
return text
|
||||
|
||||
/datum/game_mode/proc/update_devil_icons_added(datum/mind/devil_mind)
|
||||
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_DEVIL]
|
||||
hud.join_hud(devil_mind.current)
|
||||
set_antag_hud(devil_mind.current, "devil")
|
||||
|
||||
/datum/game_mode/proc/update_devil_icons_removed(datum/mind/devil_mind)
|
||||
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_DEVIL]
|
||||
hud.leave_hud(devil_mind.current)
|
||||
set_antag_hud(devil_mind.current, null)
|
||||
|
||||
/datum/game_mode/proc/update_sintouch_icons_added(datum/mind/sin_mind)
|
||||
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_SINTOUCHED]
|
||||
hud.join_hud(sin_mind.current)
|
||||
set_antag_hud(sin_mind.current, "sintouched")
|
||||
|
||||
/datum/game_mode/proc/update_sintouch_icons_removed(datum/mind/sin_mind)
|
||||
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_SINTOUCHED]
|
||||
hud.leave_hud(sin_mind.current)
|
||||
set_antag_hud(sin_mind.current, null)
|
||||
|
||||
/datum/game_mode/proc/update_soulless_icons_added(datum/mind/soulless_mind)
|
||||
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_SOULLESS]
|
||||
hud.join_hud(soulless_mind.current)
|
||||
set_antag_hud(soulless_mind.current, "soulless")
|
||||
|
||||
/datum/game_mode/proc/update_soulless_icons_removed(datum/mind/soulless_mind)
|
||||
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_SOULLESS]
|
||||
hud.leave_hud(soulless_mind.current)
|
||||
set_antag_hud(soulless_mind.current, null)
|
||||
@@ -0,0 +1,439 @@
|
||||
#define BLOOD_THRESHOLD 3 //How many souls are needed per stage.
|
||||
#define TRUE_THRESHOLD 7
|
||||
#define ARCH_THRESHOLD 12
|
||||
|
||||
#define BASIC_DEVIL 0
|
||||
#define BLOOD_LIZARD 1
|
||||
#define TRUE_DEVIL 2
|
||||
#define ARCH_DEVIL 3
|
||||
|
||||
#define SOULVALUE soulsOwned.len-reviveNumber
|
||||
|
||||
#define DEVILRESURRECTTIME 600
|
||||
|
||||
var/global/list/allDevils = list()
|
||||
var/global/list/lawlorify = list (
|
||||
LORE = list(
|
||||
OBLIGATION_FOOD = "This devil seems to always offer it's victims food before slaughtering them.",
|
||||
OBLIGATION_DRINK = "This devil seems to always offer it's victims a drink before slaughtering them.",
|
||||
OBLIGATION_GREET = "This devil seems to only be able to converse with people it knows the name of.",
|
||||
OBLIGATION_PRESENCEKNOWN = "This devil seems to be unable to attack from stealth.",
|
||||
OBLIGATION_SAYNAME = "He will always chant his name upon killing someone.",
|
||||
OBLIGATION_ANNOUNCEKILL = "This devil always loudly announces his kills for the world to hear.",
|
||||
OBLIGATION_ANSWERTONAME = "This devil always responds to his truename.",
|
||||
BANE_SILVER = "Silver seems to gravely injure this devil.",
|
||||
BANE_SALT = "Throwing salt at this devil will hinder his ability to use infernal powers temporarily.",
|
||||
BANE_LIGHT = "Bright flashes will disorient the devil, likely causing him to flee.",
|
||||
BANE_IRON = "Cold iron will slowly injure him, until he can purge it from his system.",
|
||||
BANE_WHITECLOTHES = "Wearing clean white clothing will help ward off this devil.",
|
||||
BANE_HARVEST = "Presenting the labors of a harvest will disrupt the devil.",
|
||||
BANE_TOOLBOX = "That which holds the means of creation also holds the means of the devil's undoing.",
|
||||
BAN_HURTWOMAN = "This devil seems to prefer hunting men.",
|
||||
BAN_CHAPEL = "This devil avoids holy ground.",
|
||||
BAN_HURTPRIEST = "The annointed clergy appear to be immune to his powers.",
|
||||
BAN_AVOIDWATER = "The devil seems to have some sort of aversion to water, though it does not appear to harm him.",
|
||||
BAN_STRIKEUNCONCIOUS = "This devil only shows interest in those who are awake.",
|
||||
BAN_HURTLIZARD = "This devil will not strike a lizardman first.",
|
||||
BAN_HURTANIMAL = "This devil avoids hurting animals.",
|
||||
BANISH_WATER = "To banish the devil, you must sprinkle holy water upon it's body.",
|
||||
BANISH_COFFIN = "This devil will return to life if it's remains are not placed within a coffin.",
|
||||
BANISH_FORMALDYHIDE = "To banish the devil, you must inject it's lifeless body with embalming fluid.",
|
||||
BANISH_RUNES = "This devil will resurrect after death, unless it's remains are within a rune.",
|
||||
BANISH_CANDLES = "A large number of nearby lit candles will prevent it from resurrecting.",
|
||||
BANISH_DESTRUCTION = "It's corpse must be utterly destroyed to prevent resurrection.",
|
||||
BANISH_FUNERAL_GARB = "If clad in funeral garments, this devil will be unable to resurrect. Should the clothes not fit, lay them gently on top of the devil's corpse."
|
||||
),
|
||||
LAW = list(
|
||||
OBLIGATION_FOOD = "When not acting in self defense, you must always offer your victim food before harming them.",
|
||||
OBLIGATION_DRINK = "When not acting in self defense, you must always offer your victim drink before harming them.",
|
||||
OBLIGATION_GREET = "You must always greet other people by their last name before talking with them.",
|
||||
OBLIGATION_PRESENCEKNOWN = "You must always make your presence known before attacking.",
|
||||
OBLIGATION_SAYNAME = "You must always say your true name after you kill someone.",
|
||||
OBLIGATION_ANNOUNCEKILL = "Upon killing someone, you must make your deed known to all within earshot, over comms if reasonably possible.",
|
||||
OBLIGATION_ANSWERTONAME = "If you are not under attack, you must always respond to your true name.",
|
||||
BAN_HURTWOMAN = "You must never harm a female outside of self defense.",
|
||||
BAN_CHAPEL = "You must never attempt to enter the chapel.",
|
||||
BAN_HURTPRIEST = "You must never attack a priest.",
|
||||
BAN_AVOIDWATER = "You must never willingly touch a wet surface.",
|
||||
BAN_STRIKEUNCONCIOUS = "You must never strike an unconcious person.",
|
||||
BAN_HURTLIZARD = "You must never harm a lizardman outside of self defense.",
|
||||
BAN_HURTANIMAL = "You must never harm a non-sentient creature or robot outside of self defense.",
|
||||
BANE_SILVER = "Silver, in all of it's forms shall be your downfall.",
|
||||
BANE_SALT = "Salt will disrupt your magical abilities.",
|
||||
BANE_LIGHT = "Blinding lights will prevent you from using offensive powers for a time.",
|
||||
BANE_IRON = "Cold wrought iron shall act as poison to you.",
|
||||
BANE_WHITECLOTHES = "Those clad in pristine white garments will strike you true.",
|
||||
BANE_HARVEST = "The fruits of the harvest shall be your downfall.",
|
||||
BANE_TOOLBOX = "Toolboxes are bad news for you, for some reason.",
|
||||
BANISH_WATER = "If your corpse is filled with holy water, you will be unable to resurrect.",
|
||||
BANISH_COFFIN = "If your corpse is in a coffin, you will be unable to resurrect.",
|
||||
BANISH_FORMALDYHIDE = "If your corpse is embalmed, you will be unable to resurrect.",
|
||||
BANISH_RUNES = "If your corpse is placed within a rune, you will be unable to resurrect.",
|
||||
BANISH_CANDLES = "If your corpse is near lit candles, you will be unable to resurrect.",
|
||||
BANISH_DESTRUCTION = "If your corpse is destroyed, you will be unable to resurrect.",
|
||||
BANISH_FUNERAL_GARB = "If your corpse is clad in funeral garments, you will be unable to resurrect."
|
||||
)
|
||||
)
|
||||
|
||||
/datum/devilinfo/
|
||||
var/datum/mind/owner = null
|
||||
var/obligation
|
||||
var/ban
|
||||
var/bane
|
||||
var/banish
|
||||
var/truename
|
||||
var/list/datum/mind/soulsOwned = new
|
||||
var/reviveNumber = 0
|
||||
var/form = BASIC_DEVIL
|
||||
var/exists = 0
|
||||
|
||||
/proc/randomDevilInfo(name = randomDevilName())
|
||||
var/datum/devilinfo/devil = new
|
||||
devil.truename = name
|
||||
devil.bane = randomdevilbane()
|
||||
devil.obligation = randomdevilobligation()
|
||||
devil.ban = randomdevilban()
|
||||
devil.banish = randomdevilbanish()
|
||||
return devil
|
||||
|
||||
/proc/devilInfo(name, saveDetails = 0)
|
||||
if(allDevils[lowertext(name)])
|
||||
return allDevils[lowertext(name)]
|
||||
else
|
||||
var/datum/devilinfo/devil = randomDevilInfo(name)
|
||||
allDevils[lowertext(name)] = devil
|
||||
devil.exists = saveDetails
|
||||
return devil
|
||||
|
||||
|
||||
|
||||
/proc/randomDevilName()
|
||||
var/preTitle = ""
|
||||
var/title = ""
|
||||
var/mainName = ""
|
||||
var/suffix = ""
|
||||
if(prob(65))
|
||||
if(prob(35))
|
||||
preTitle = pick("Dark ", "Hellish ", "Fiery ", "Sinful ", "Blood ")
|
||||
title = pick("Lord ", "Fallen Prelate ", "Count ", "Viscount ", "Vizier ", "Elder ", "Adept ")
|
||||
var/probability = 100
|
||||
mainName = pick("Hal", "Ve", "Odr", "Neit", "Ci", "Quon", "Mya", "Folth", "Wren", "Gyer", "Geyr", "Hil", "Niet", "Twou", "Hu", "Don")
|
||||
while(prob(probability))
|
||||
mainName += pick("hal", "ve", "odr", "neit", "ca", "quon", "mya", "folth", "wren", "gyer", "geyr", "hil", "niet", "twoe", "phi", "coa")
|
||||
probability -= 20
|
||||
if(prob(40))
|
||||
suffix = pick(" the Red", " the Soulless", " the Master", ", the Lord of all things", ", Jr.")
|
||||
return preTitle + title + mainName + suffix
|
||||
|
||||
/proc/randomdevilobligation()
|
||||
return pick(OBLIGATION_FOOD, OBLIGATION_DRINK, OBLIGATION_GREET, OBLIGATION_PRESENCEKNOWN, OBLIGATION_SAYNAME, OBLIGATION_ANNOUNCEKILL, OBLIGATION_ANSWERTONAME)
|
||||
|
||||
/proc/randomdevilban()
|
||||
return pick(BAN_HURTWOMAN, BAN_CHAPEL, BAN_HURTPRIEST, BAN_AVOIDWATER, BAN_STRIKEUNCONCIOUS, BAN_HURTLIZARD, BAN_HURTANIMAL)
|
||||
|
||||
/proc/randomdevilbane()
|
||||
return pick(BANE_SALT, BANE_LIGHT, BANE_IRON, BANE_WHITECLOTHES, BANE_SILVER, BANE_HARVEST, BANE_TOOLBOX)
|
||||
|
||||
/proc/randomdevilbanish()
|
||||
return pick(BANISH_WATER, BANISH_COFFIN, BANISH_FORMALDYHIDE, BANISH_RUNES, BANISH_CANDLES, BANISH_DESTRUCTION, BANISH_FUNERAL_GARB)
|
||||
|
||||
/datum/devilinfo/proc/add_soul(datum/mind/soul)
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
if(soulsOwned.Find(soul))
|
||||
return
|
||||
soulsOwned += soul
|
||||
H.nutrition = NUTRITION_LEVEL_FULL
|
||||
owner.current << "<span class='warning'>You feel satiated as you received a new soul.</span>"
|
||||
update_hud()
|
||||
switch(SOULVALUE)
|
||||
if(0)
|
||||
owner.current << "<span class='warning'>Your hellish powers have been restored."
|
||||
give_base_spells()
|
||||
if(BLOOD_THRESHOLD)
|
||||
increase_blood_lizard()
|
||||
if(TRUE_THRESHOLD)
|
||||
increase_true_devil()
|
||||
if(ARCH_THRESHOLD)
|
||||
increase_arch_devil()
|
||||
|
||||
/datum/devilinfo/proc/remove_soul(datum/mind/soul)
|
||||
if(soulsOwned.Remove(soul))
|
||||
check_regression()
|
||||
owner.current << "<span class='warning'>You feel as though a soul has slipped from your grasp.</span>"
|
||||
update_hud()
|
||||
|
||||
/datum/devilinfo/proc/check_regression()
|
||||
if (form == ARCH_DEVIL)
|
||||
return //arch devil can't regress
|
||||
switch(SOULVALUE)
|
||||
if(-1)
|
||||
remove_spells()
|
||||
owner.current << "<span class='warning'>As punishment for your failures, all of your powers except contract creation have been revoked."
|
||||
if(BLOOD_THRESHOLD-1)
|
||||
regress_humanoid()
|
||||
if(TRUE_THRESHOLD-1)
|
||||
regress_blood_lizard()
|
||||
|
||||
/datum/devilinfo/proc/increase_form()
|
||||
switch(form)
|
||||
if(BASIC_DEVIL)
|
||||
increase_blood_lizard()
|
||||
if(BLOOD_LIZARD)
|
||||
increase_true_devil()
|
||||
if(TRUE_DEVIL)
|
||||
increase_arch_devil()
|
||||
|
||||
/datum/devilinfo/proc/regress_humanoid()
|
||||
owner.current << "<span class='warning'>Your powers weaken, have more contracts be signed to regain power."
|
||||
if(istype(owner.current, /mob/living/carbon/human))
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
H.set_species(/datum/species/human, 1)
|
||||
H.regenerate_icons()
|
||||
give_base_spells()
|
||||
if(istype(owner.current.loc, /obj/effect/dummy/slaughter/))
|
||||
owner.current.forceMove(get_turf(owner.current))//Fixes dying while jaunted leaving you permajaunted.
|
||||
form = BASIC_DEVIL
|
||||
|
||||
/datum/devilinfo/proc/regress_blood_lizard()
|
||||
var/mob/living/carbon/true_devil/D = owner.current
|
||||
D << "<span class='warning'>Your powers weaken, have more contracts be signed to regain power."
|
||||
D.oldform.loc = D.loc
|
||||
owner.transfer_to(D.oldform)
|
||||
give_lizard_spells()
|
||||
qdel(D)
|
||||
form = BLOOD_LIZARD
|
||||
update_hud()
|
||||
|
||||
|
||||
/datum/devilinfo/proc/increase_blood_lizard()
|
||||
owner.current << "<span class='warning'>You feel as though your humanoid form is about to shed. You will soon turn into a blood lizard."
|
||||
sleep(50)
|
||||
if(istype(owner.current, /mob/living/carbon/human))
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
H.set_species(/datum/species/lizard, 1)
|
||||
H.underwear = "Nude"
|
||||
H.undershirt = "Nude"
|
||||
H.socks = "Nude"
|
||||
H.dna.features["mcolor"] = "511" //A deep red
|
||||
H.regenerate_icons()
|
||||
else //Did the devil get hit by a staff of transmutation?
|
||||
owner.current.color = "#501010"
|
||||
give_lizard_spells()
|
||||
form = BLOOD_LIZARD
|
||||
|
||||
|
||||
|
||||
/datum/devilinfo/proc/increase_true_devil()
|
||||
owner.current << "<span class='warning'>You feel as though your current form is about to shed. You will soon turn into a true devil."
|
||||
sleep(50)
|
||||
var/mob/living/carbon/true_devil/A = new /mob/living/carbon/true_devil(owner.current.loc)
|
||||
A.faction |= "hell"
|
||||
owner.current.loc = A
|
||||
A.oldform = owner.current
|
||||
owner.transfer_to(A)
|
||||
A.set_name()
|
||||
give_true_spells()
|
||||
form = TRUE_DEVIL
|
||||
update_hud()
|
||||
|
||||
|
||||
/datum/devilinfo/proc/increase_arch_devil()
|
||||
var/mob/living/carbon/true_devil/D = owner.current
|
||||
D << "<span class='warning'>You feel as though your form is about to ascend."
|
||||
sleep(50)
|
||||
if(!D)
|
||||
return
|
||||
D.visible_message("<span class='warning'>[D]'s skin begins to erupt with spikes.</span>", \
|
||||
"<span class='warning'>Your flesh begins creating a shield around yourself.</span>")
|
||||
sleep(100)
|
||||
if(!D)
|
||||
return
|
||||
D.visible_message("<span class='warning'>The horns on [D]'s head slowly grow and elongate.</span>", \
|
||||
"<span class='warning'>Your body continues to mutate. Your telepathic abilities grow.</span>")
|
||||
sleep(90)
|
||||
if(!D)
|
||||
return
|
||||
D.visible_message("<span class='warning'>[D]'s body begins to violently stretch and contort.</span>", \
|
||||
"<span class='warning'>You begin to rend apart the final barriers to ultimate power.</span>")
|
||||
sleep(40)
|
||||
if(!D)
|
||||
return
|
||||
D << "<i><b>Yes!</b></i>"
|
||||
sleep(10)
|
||||
if(!D)
|
||||
return
|
||||
D << "<i><b><span class='big'>YES!!</span></b></i>"
|
||||
sleep(10)
|
||||
if(!D)
|
||||
return
|
||||
D << "<i><b><span class='reallybig'>YE--</span></b></i>"
|
||||
sleep(1)
|
||||
if(!D)
|
||||
return
|
||||
world << "<font size=5><span class='danger'><b>\"SLOTH, WRATH, GLUTTONY, ACEDIA, ENVY, GREED, PRIDE! FIRES OF HELL AWAKEN!!\"</font></span>"
|
||||
world << 'sound/hallucinations/veryfar_noise.ogg'
|
||||
give_arch_spells()
|
||||
D.convert_to_archdevil()
|
||||
if(istype(D.loc, /obj/effect/dummy/slaughter/))
|
||||
D.forceMove(get_turf(D))//Fixes dying while jaunted leaving you permajaunted.
|
||||
var/area/A = get_area(owner.current)
|
||||
if(A)
|
||||
notify_ghosts("An arch devil has ascended in \the [A.name]. Reach out to the devil to be given a new shell for your soul.", source = owner.current, action=NOTIFY_ATTACK)
|
||||
sleep(50)
|
||||
if(!ticker.mode.devil_ascended)
|
||||
SSshuttle.emergency.request(null, 0.3)
|
||||
ticker.mode.devil_ascended++
|
||||
form = ARCH_DEVIL
|
||||
|
||||
/datum/devilinfo/proc/remove_spells()
|
||||
for(var/X in owner.spell_list)
|
||||
var/obj/effect/proc_holder/spell/S = X
|
||||
if(!istype(S, /obj/effect/proc_holder/spell/targeted/summon_contract))
|
||||
owner.RemoveSpell(S)
|
||||
|
||||
/datum/devilinfo/proc/give_summon_contract()
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/summon_contract(null))
|
||||
|
||||
|
||||
/datum/devilinfo/proc/give_base_spells(give_summon_contract = 0)
|
||||
remove_spells()
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/dumbfire/fireball/hellish(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/summon_pitchfork(null))
|
||||
if(give_summon_contract)
|
||||
give_summon_contract()
|
||||
|
||||
/datum/devilinfo/proc/give_lizard_spells()
|
||||
remove_spells()
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/summon_pitchfork(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/dumbfire/fireball/hellish(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/infernal_jaunt(null))
|
||||
|
||||
/datum/devilinfo/proc/give_true_spells()
|
||||
remove_spells()
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/summon_pitchfork/greater(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/dumbfire/fireball/hellish(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/infernal_jaunt(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/sintouch(null))
|
||||
|
||||
/datum/devilinfo/proc/give_arch_spells()
|
||||
remove_spells()
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/summon_pitchfork/ascended(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/sintouch/ascended(null))
|
||||
|
||||
/datum/devilinfo/proc/beginResurrectionCheck(mob/living/body)
|
||||
if(SOULVALUE>0)
|
||||
owner.current<< "<span class='userdanger'>Your body has been damaged to the point that you may no longer use it. At the cost of some of your power, you will return to life soon. Remain in your body.</span>"
|
||||
sleep(DEVILRESURRECTTIME)
|
||||
if (!body || body.stat == DEAD)
|
||||
if(SOULVALUE>0)
|
||||
if(check_banishment(body))
|
||||
owner.current<< "<span class='userdanger'>Unfortunately, the mortals have finished a ritual that prevents your resurrection.</span>"
|
||||
return -1
|
||||
else
|
||||
owner.current<< "<span class='userdanger'>WE LIVE AGAIN!</span>"
|
||||
return hellish_resurrection(body)
|
||||
else
|
||||
owner.current<< "<span class='userdanger'>Unfortunately, the power that stemmed from your contracts has been extinguished. You no longer have enough power to resurrect.</span>"
|
||||
return -1
|
||||
else
|
||||
owner.current << "<span class='danger'> You seem to have resurrected without your hellish powers.</span>"
|
||||
else
|
||||
owner.current << "<span class='userdanger'>Your hellish powers are too weak to resurrect yourself.</span>"
|
||||
|
||||
/datum/devilinfo/proc/check_banishment(mob/living/body)
|
||||
switch(banish)
|
||||
if(BANISH_WATER)
|
||||
if(istype(body, /mob/living/carbon))
|
||||
var/mob/living/carbon/H = body
|
||||
return H.reagents.has_reagent("holy water")
|
||||
return 0
|
||||
if(BANISH_COFFIN)
|
||||
return (body && istype(body.loc, /obj/structure/closet/coffin))
|
||||
if(BANISH_FORMALDYHIDE)
|
||||
if(istype(body, /mob/living/carbon))
|
||||
var/mob/living/carbon/H = body
|
||||
return H.reagents.has_reagent("formaldehyde")
|
||||
return 0
|
||||
if(BANISH_RUNES)
|
||||
if(body)
|
||||
for(var/obj/effect/decal/cleanable/crayon/R in range(0,body))
|
||||
if (R.name == "rune")
|
||||
return 1
|
||||
return 0
|
||||
if(BANISH_CANDLES)
|
||||
if(body)
|
||||
var/count = 0
|
||||
for(var/obj/item/candle/C in range(1,body))
|
||||
count += C.lit
|
||||
if(count>=4)
|
||||
return 1
|
||||
return 0
|
||||
if(BANISH_DESTRUCTION)
|
||||
if(body)
|
||||
return 0
|
||||
return 1
|
||||
if(BANISH_FUNERAL_GARB)
|
||||
if(istype(body, /mob/living/carbon/human))
|
||||
var/mob/living/carbon/human/H = body
|
||||
if(H.w_uniform && istype(H.w_uniform, /obj/item/clothing/under/burial))
|
||||
return 1
|
||||
return 0
|
||||
else
|
||||
for(var/obj/item/clothing/under/burial/B in range(0,body))
|
||||
if(B.loc == get_turf(B)) //Make sure it's not in someone's inventory or something.
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/devilinfo/proc/hellish_resurrection(mob/living/body)
|
||||
message_admins("[owner.name] (true name is: [truename]) is resurrecting using hellish energy.</a>")
|
||||
if(SOULVALUE <= ARCH_THRESHOLD) // once ascended, arch devils do not go down in power by any means.
|
||||
reviveNumber++
|
||||
update_hud()
|
||||
if(body)
|
||||
body.revive(1,0)
|
||||
if(istype(body.loc, /obj/effect/dummy/slaughter/))
|
||||
body.forceMove(get_turf(body))//Fixes dying while jaunted leaving you permajaunted.
|
||||
if(istype(body, /mob/living/carbon/true_devil))
|
||||
var/mob/living/carbon/true_devil/D = body
|
||||
if(D.oldform)
|
||||
D.oldform.revive(1,0) // Heal the old body too, so the devil doesn't resurrect, then immediately regress into a dead body.
|
||||
else
|
||||
if(blobstart.len > 0)
|
||||
var/turf/targetturf = get_turf(pick(blobstart))
|
||||
var/mob/currentMob = owner.current
|
||||
if(!currentMob)
|
||||
currentMob = owner.get_ghost()
|
||||
if(!currentMob)
|
||||
message_admins("[owner.name]'s devil resurrection failed due to client logoff. Aborting.")
|
||||
return -1 //
|
||||
if(currentMob.mind != owner)
|
||||
message_admins("[owner.name]'s devil resurrection failed due to becoming a new mob. Aborting.")
|
||||
return -1
|
||||
currentMob.change_mob_type( /mob/living/carbon/human , targetturf, null, 1)
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
give_summon_contract()
|
||||
if(SOULVALUE >= BLOOD_THRESHOLD)
|
||||
H.set_species(/datum/species/lizard, 1)
|
||||
H.underwear = "Nude"
|
||||
H.undershirt = "Nude"
|
||||
H.socks = "Nude"
|
||||
H.dna.features["mcolor"] = "511"
|
||||
H.regenerate_icons()
|
||||
if(SOULVALUE >= TRUE_THRESHOLD) //Yes, BOTH this and the above if statement are to run if soulpower is high enough.
|
||||
var/mob/living/carbon/true_devil/A = new /mob/living/carbon/true_devil(targetturf)
|
||||
A.faction |= "hell"
|
||||
H.forceMove(A)
|
||||
A.oldform = H
|
||||
A.set_name()
|
||||
owner.transfer_to(A)
|
||||
if(SOULVALUE >= ARCH_THRESHOLD)
|
||||
A.convert_to_archdevil()
|
||||
else
|
||||
throw EXCEPTION("Unable to find a blobstart landmark for hellish resurrection")
|
||||
check_regression()
|
||||
|
||||
/datum/devilinfo/proc/update_hud()
|
||||
if(istype(owner.current, /mob/living/carbon))
|
||||
var/mob/living/C = owner.current
|
||||
if(C.hud_used && C.hud_used.devilsouldisplay)
|
||||
C.hud_used.devilsouldisplay.update_counter(SOULVALUE)
|
||||
@@ -0,0 +1,56 @@
|
||||
//////////////////The Monster
|
||||
|
||||
/mob/living/simple_animal/imp
|
||||
name = "imp"
|
||||
real_name = "imp"
|
||||
desc = "A large, menacing creature covered in armored black scales."
|
||||
speak_emote = list("cackles")
|
||||
emote_hear = list("cackles","screeches")
|
||||
response_help = "thinks better of touching"
|
||||
response_disarm = "flails at"
|
||||
response_harm = "punches"
|
||||
icon = 'icons/mob/mob.dmi'
|
||||
icon_state = "imp"
|
||||
icon_living = "imp"
|
||||
speed = 1
|
||||
a_intent = "harm"
|
||||
stop_automated_movement = 1
|
||||
status_flags = CANPUSH
|
||||
attack_sound = 'sound/magic/demon_attack1.ogg'
|
||||
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 = 250 //Weak to cold
|
||||
maxbodytemp = INFINITY
|
||||
faction = list("hell")
|
||||
attacktext = "wildly tears into"
|
||||
maxHealth = 200
|
||||
health = 200
|
||||
healable = 0
|
||||
environment_smash = 1
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 15
|
||||
see_in_dark = 8
|
||||
var/boost = 0
|
||||
bloodcrawl = BLOODCRAWL_EAT
|
||||
see_invisible = SEE_INVISIBLE_MINIMUM
|
||||
var/list/consumed_mobs = list()
|
||||
var/playstyle_string = "<B><font size=3 color='red'>You are an imp,</font> a mischevious creature from hell. You are the lowest rank on the hellish totem pole \
|
||||
Though you are not obligated to help, perhaps by aiding a higher ranking devil, you might just get a promotion. However, you are incapable \
|
||||
of intentionally harming a fellow devil.</B>"
|
||||
|
||||
/mob/living/simple_animal/imp/New()
|
||||
..()
|
||||
boost = world.time + 30
|
||||
|
||||
/mob/living/simple_animal/imp/Life()
|
||||
..()
|
||||
if(boost<world.time)
|
||||
speed = 1
|
||||
else
|
||||
speed = 0
|
||||
|
||||
/mob/living/simple_animal/imp/death()
|
||||
..(1)
|
||||
playsound(get_turf(src),'sound/magic/demon_dies.ogg', 200, 1)
|
||||
visible_message("<span class='danger'>[src] screams in agony as it sublimates into a sulfurous smoke.</span>")
|
||||
ghostize()
|
||||
qdel(src)
|
||||
@@ -0,0 +1,62 @@
|
||||
/datum/objective/devil
|
||||
dangerrating = 5
|
||||
|
||||
/datum/objective/devil/soulquantity
|
||||
explanation_text = "You shouldn't see this text. Error:DEVIL1"
|
||||
var/quantity = 4
|
||||
|
||||
/datum/objective/devil/soulquantity/New()
|
||||
quantity = pick(6,8)
|
||||
explanation_text = "Purchase, and retain control over at least [quantity] souls."
|
||||
|
||||
/datum/objective/devil/soulquantity/check_completion()
|
||||
var/count = 0
|
||||
for(var/S in owner.devilinfo.soulsOwned)
|
||||
var/datum/mind/L = S
|
||||
if(L.soulOwner != L)
|
||||
count++
|
||||
return count >= quantity
|
||||
|
||||
/datum/objective/devil/soulquality
|
||||
explanation_text = "You shouldn't see this text. Error:DEVIL2"
|
||||
var/contractType
|
||||
var/quantity
|
||||
|
||||
/datum/objective/devil/soulquality/New()
|
||||
contractType = pick(CONTRACT_POWER, CONTRACT_WEALTH, CONTRACT_PRESTIGE, CONTRACT_MAGIC, CONTRACT_REVIVE, CONTRACT_KNOWLEDGE/*, CONTRACT_UNWILLING*/)
|
||||
var/contractName
|
||||
quantity = pick(1,2)
|
||||
switch(contractType)
|
||||
if(CONTRACT_POWER)
|
||||
contractName = "for power"
|
||||
if(CONTRACT_WEALTH)
|
||||
contractName = "for wealth"
|
||||
if(CONTRACT_PRESTIGE)
|
||||
contractName = "for prestige"
|
||||
if(CONTRACT_MAGIC)
|
||||
contractName = "for magic"
|
||||
if(CONTRACT_REVIVE)
|
||||
contractName = "of revival"
|
||||
if(CONTRACT_KNOWLEDGE)
|
||||
contractName = "for knowledge"
|
||||
//if(CONTRACT_UNWILLING) //Makes round unfun.
|
||||
// contractName = "against their will"
|
||||
explanation_text = "Have mortals sign at least [quantity] contracts [contractName]"
|
||||
|
||||
/datum/objective/devil/soulquality/check_completion()
|
||||
var/count = 0
|
||||
for(var/S in owner.devilinfo.soulsOwned)
|
||||
var/datum/mind/L = S
|
||||
if(L.soulOwner != L && L.damnation_type == contractType)
|
||||
count++
|
||||
return count>=quantity
|
||||
|
||||
/datum/objective/devil/sintouch
|
||||
var/quantity
|
||||
|
||||
/datum/objective/devil/sintouch/New()
|
||||
quantity = pick(4,5)
|
||||
explanation_text = "Ensure at least [quantity] mortals are sintouched."
|
||||
|
||||
/datum/objective/devil/sintouch/check_completion()
|
||||
return quantity>=ticker.mode.sintouched.len
|
||||
@@ -0,0 +1,209 @@
|
||||
#define DEVIL_HANDS_LAYER 1
|
||||
#define DEVIL_HEAD_LAYER 2
|
||||
#define DEVIL_TOTAL_LAYERS 2
|
||||
|
||||
|
||||
/mob/living/carbon/true_devil
|
||||
name = "True Devil"
|
||||
desc = "A pile of infernal energy, taking a vaguely humanoid form."
|
||||
icon = 'icons/mob/32x64.dmi'
|
||||
icon_state = "true_devil"
|
||||
gender = NEUTER
|
||||
health = 350
|
||||
maxHealth = 350
|
||||
ventcrawler = 0
|
||||
density = 0
|
||||
pass_flags = 0
|
||||
var/ascended = 0
|
||||
sight = (SEE_TURFS | SEE_OBJS)
|
||||
status_flags = CANPUSH
|
||||
languages_spoken = ALL //The devil speaks all languages meme
|
||||
languages_understood = ALL //The devil speaks all languages meme
|
||||
mob_size = MOB_SIZE_LARGE
|
||||
var/mob/living/oldform
|
||||
var/list/devil_overlays[DEVIL_TOTAL_LAYERS]
|
||||
|
||||
/mob/living/carbon/true_devil/New()
|
||||
internal_organs += new /obj/item/organ/brain/
|
||||
internal_organs += new /obj/item/organ/tongue
|
||||
for(var/X in internal_organs)
|
||||
var/obj/item/organ/I = X
|
||||
I.Insert(src)
|
||||
..()
|
||||
|
||||
|
||||
/mob/living/carbon/true_devil/proc/convert_to_archdevil()
|
||||
maxHealth = 5000 // not an IMPOSSIBLE amount, but still near impossible.
|
||||
ascended = 1
|
||||
health = maxHealth
|
||||
icon_state = "arch_devil"
|
||||
|
||||
/mob/living/carbon/true_devil/proc/set_name()
|
||||
name = mind.devilinfo.truename
|
||||
real_name = name
|
||||
|
||||
/mob/living/carbon/true_devil/Login()
|
||||
..()
|
||||
mind.announceDevilLaws()
|
||||
|
||||
|
||||
/mob/living/carbon/true_devil/death(gibbed)
|
||||
stat = DEAD
|
||||
..(gibbed)
|
||||
drop_l_hand()
|
||||
drop_r_hand()
|
||||
spawn (0)
|
||||
mind.devilinfo.beginResurrectionCheck(src)
|
||||
|
||||
|
||||
/mob/living/carbon/true_devil/examine(mob/user)
|
||||
var/msg = "<span class='info'>*---------*\nThis is \icon[src] <b>[src]</b>!\n"
|
||||
|
||||
//Left hand items
|
||||
if(l_hand && !(l_hand.flags&ABSTRACT))
|
||||
if(l_hand.blood_DNA)
|
||||
msg += "<span class='warning'>It is holding \icon[l_hand] [l_hand.gender==PLURAL?"some":"a"] blood-stained [l_hand.name] in its left hand!</span>\n"
|
||||
else
|
||||
msg += "It is holding \icon[l_hand] \a [l_hand] in its left hand.\n"
|
||||
|
||||
//Right hand items
|
||||
if(r_hand && !(r_hand.flags&ABSTRACT))
|
||||
if(r_hand.blood_DNA)
|
||||
msg += "<span class='warning'>It is holding \icon[r_hand] [r_hand.gender==PLURAL?"some":"a"] blood-stained [r_hand.name] in its right hand!</span>\n"
|
||||
else
|
||||
msg += "It is holding \icon[r_hand] \a [r_hand] in its right hand.\n"
|
||||
|
||||
//Braindead
|
||||
if(!client && stat != DEAD)
|
||||
msg += "The devil seems to be in deep contemplation.\n"
|
||||
|
||||
//Damaged
|
||||
if(stat == DEAD)
|
||||
msg += "<span class='deadsay'>The hellfire seems to have been extinguished, for now at least.</span>\n"
|
||||
else if(health < (maxHealth/10))
|
||||
msg += "<span class='warning'>You can see hellfire inside of it's gaping wounds.</span>\n"
|
||||
else if(health < (maxHealth/2))
|
||||
msg += "<span class='warning'>You can see hellfire inside of it's wounds.</span>\n"
|
||||
msg += "*---------*</span>"
|
||||
user << msg
|
||||
|
||||
|
||||
/mob/living/carbon/true_devil/IsAdvancedToolUser()
|
||||
return 1
|
||||
|
||||
/mob/living/carbon/true_devil/canUseTopic(atom/movable/M, be_close = 0)
|
||||
if(incapacitated())
|
||||
return 0
|
||||
if(be_close && !in_range(M, src))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/mob/living/carbon/true_devil/assess_threat()
|
||||
return 666
|
||||
|
||||
/mob/living/carbon/true_devil/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0)
|
||||
if(mind && has_bane(BANE_LIGHT))
|
||||
mind.disrupt_spells(-500)
|
||||
return ..() //flashes don't stop devils UNLESS it's their bane.
|
||||
|
||||
|
||||
/mob/living/carbon/true_devil/attacked_by(obj/item/I, mob/living/user, def_zone)
|
||||
var/weakness = check_weakness(I, user)
|
||||
apply_damage(I.force * weakness, I.damtype, def_zone)
|
||||
var/message_verb = ""
|
||||
if(I.attack_verb && I.attack_verb.len)
|
||||
message_verb = "[pick(I.attack_verb)]"
|
||||
else if(I.force)
|
||||
message_verb = "attacked"
|
||||
|
||||
var/attack_message = "[src] has been [message_verb] with [I]."
|
||||
if(user)
|
||||
user.do_attack_animation(src)
|
||||
if(user in viewers(src, null))
|
||||
attack_message = "[user] has [message_verb] [src] with [I]!"
|
||||
if(message_verb)
|
||||
visible_message("<span class='danger'>[attack_message]</span>",
|
||||
"<span class='userdanger'>[attack_message]</span>")
|
||||
return TRUE
|
||||
|
||||
/mob/living/carbon/true_devil/UnarmedAttack(atom/A, proximity)
|
||||
A.attack_hand(src)
|
||||
|
||||
/mob/living/carbon/true_devil/Process_Spacemove(movement_dir = 0)
|
||||
return 1
|
||||
|
||||
/mob/living/carbon/true_devil/singularity_act()
|
||||
if(ascended)
|
||||
return 0
|
||||
return ..()
|
||||
|
||||
/mob/living/carbon/true_devil/attack_ghost(mob/dead/observer/user as mob)
|
||||
if(ascended || user.mind.soulOwner == src.mind)
|
||||
var/mob/living/simple_animal/imp/S = new(get_turf(loc))
|
||||
S.key = user.key
|
||||
S.mind.assigned_role = "Imp"
|
||||
S.mind.special_role = "Imp"
|
||||
var/datum/objective/newobjective = new
|
||||
newobjective.explanation_text = "Try to get a promotion to a higher devilic rank."
|
||||
S.mind.objectives += newobjective
|
||||
S << S.playstyle_string
|
||||
S << "<B>Objective #[1]</B>: [newobjective.explanation_text]"
|
||||
return
|
||||
else
|
||||
return ..()
|
||||
|
||||
/mob/living/carbon/true_devil/can_be_revived()
|
||||
return 1
|
||||
|
||||
/mob/living/carbon/true_devil/resist_fire()
|
||||
//They're immune to fire.
|
||||
|
||||
/mob/living/carbon/true_devil/attack_hand(mob/living/carbon/human/M)
|
||||
if(..())
|
||||
switch(M.a_intent)
|
||||
if ("harm")
|
||||
var/damage = rand(1, 5)
|
||||
playsound(loc, "punch", 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has punched [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has punched [src]!</span>")
|
||||
adjustBruteLoss(damage)
|
||||
add_logs(M, src, "attacked")
|
||||
updatehealth()
|
||||
if ("disarm")
|
||||
if (!lying && !ascended) //No stealing the arch devil's pitchfork.
|
||||
if (prob(5))
|
||||
Paralyse(2)
|
||||
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
add_logs(M, src, "pushed")
|
||||
visible_message("<span class='danger'>[M] has pushed down [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has pushed down [src]!</span>")
|
||||
else
|
||||
if (prob(25))
|
||||
drop_item()
|
||||
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has disarmed [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has disarmed [src]!</span>")
|
||||
else
|
||||
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has attempted to disarm [src]!</span>")
|
||||
|
||||
/mob/living/carbon/true_devil/handle_breathing()
|
||||
// devils do not need to breathe
|
||||
|
||||
/mob/living/carbon/true_devil/is_literate()
|
||||
return 1
|
||||
|
||||
/mob/living/carbon/true_devil/ex_act(severity, ex_target)
|
||||
if(!ascended)
|
||||
var/b_loss
|
||||
switch (severity)
|
||||
if (1)
|
||||
b_loss = 500
|
||||
if (2)
|
||||
b_loss = 150
|
||||
if(3)
|
||||
b_loss = 30
|
||||
if(has_bane(BANE_LIGHT))
|
||||
b_loss *=2
|
||||
adjustBruteLoss(b_loss)
|
||||
return ..()
|
||||
@@ -0,0 +1,61 @@
|
||||
/mob/living/carbon/true_devil/unEquip(obj/item/I, force)
|
||||
if(..(I,force))
|
||||
update_inv_hands()
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/mob/living/carbon/true_devil/proc/update_inv_hands()
|
||||
//TODO LORDPIDEY: Figure out how to make the hands line up properly. the l/r_hand_image should use the down sprite when facing down, left, or right, and the up sprite when facing up.
|
||||
remove_overlay(DEVIL_HANDS_LAYER)
|
||||
var/list/hands_overlays = list()
|
||||
|
||||
if(r_hand)
|
||||
|
||||
var/r_state = r_hand.item_state
|
||||
if(!r_state)
|
||||
r_state = r_hand.icon_state
|
||||
|
||||
var/image/r_hand_image = r_hand.build_worn_icon(state = r_state, default_layer = DEVIL_HANDS_LAYER, default_icon_file = r_hand.righthand_file, isinhands = TRUE)
|
||||
|
||||
hands_overlays += r_hand_image
|
||||
|
||||
if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD)
|
||||
r_hand.layer = ABOVE_HUD_LAYER
|
||||
r_hand.screen_loc = ui_rhand
|
||||
client.screen |= r_hand
|
||||
|
||||
if(l_hand)
|
||||
|
||||
var/l_state = l_hand.item_state
|
||||
if(!l_state)
|
||||
l_state = l_hand.icon_state
|
||||
|
||||
var/image/l_hand_image = l_hand.build_worn_icon(state = l_state, default_layer = DEVIL_HANDS_LAYER, default_icon_file = l_hand.lefthand_file, isinhands = TRUE)
|
||||
|
||||
hands_overlays += l_hand_image
|
||||
|
||||
if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD)
|
||||
l_hand.layer = ABOVE_HUD_LAYER
|
||||
l_hand.screen_loc = ui_lhand
|
||||
client.screen |= l_hand
|
||||
if(hands_overlays.len)
|
||||
devil_overlays[DEVIL_HANDS_LAYER] = hands_overlays
|
||||
apply_overlay(DEVIL_HANDS_LAYER)
|
||||
|
||||
/mob/living/carbon/true_devil/update_inv_l_hand()
|
||||
update_inv_hands()
|
||||
|
||||
|
||||
/mob/living/carbon/true_devil/update_inv_r_hand()
|
||||
update_inv_hands()
|
||||
|
||||
/mob/living/carbon/true_devil/remove_overlay(cache_index)
|
||||
if(devil_overlays[cache_index])
|
||||
overlays -= devil_overlays[cache_index]
|
||||
devil_overlays[cache_index] = null
|
||||
|
||||
|
||||
/mob/living/carbon/true_devil/apply_overlay(cache_index)
|
||||
var/image/I = devil_overlays[cache_index]
|
||||
if(I)
|
||||
add_overlay(I)
|
||||
@@ -0,0 +1,79 @@
|
||||
/proc/power_failure()
|
||||
priority_announce("Abnormal activity detected in [station_name()]'s powernet. As a precautionary measure, the station's power will be shut off for an indeterminate duration.", "Critical Power Failure", 'sound/AI/poweroff.ogg')
|
||||
for(var/obj/machinery/power/smes/S in machines)
|
||||
if(istype(get_area(S), /area/turret_protected) || S.z != ZLEVEL_STATION)
|
||||
continue
|
||||
S.charge = 0
|
||||
S.output_level = 0
|
||||
S.output_attempt = 0
|
||||
S.update_icon()
|
||||
S.power_change()
|
||||
|
||||
var/list/skipped_areas = list(/area/engine/engineering, /area/turret_protected/ai)
|
||||
|
||||
for(var/area/A in world)
|
||||
if( !A.requires_power || A.always_unpowered )
|
||||
continue
|
||||
|
||||
var/skip = 0
|
||||
for(var/area_type in skipped_areas)
|
||||
if(istype(A,area_type))
|
||||
skip = 1
|
||||
break
|
||||
if(A.contents)
|
||||
for(var/atom/AT in A.contents)
|
||||
if(AT.z != ZLEVEL_STATION) //Only check one, it's enough.
|
||||
skip = 1
|
||||
break
|
||||
if(skip) continue
|
||||
A.power_light = 0
|
||||
A.power_equip = 0
|
||||
A.power_environ = 0
|
||||
A.power_change()
|
||||
|
||||
for(var/obj/machinery/power/apc/C in apcs_list)
|
||||
if(C.cell && C.z == ZLEVEL_STATION)
|
||||
var/area/A = get_area(C)
|
||||
|
||||
var/skip = 0
|
||||
for(var/area_type in skipped_areas)
|
||||
if(istype(A,area_type))
|
||||
skip = 1
|
||||
break
|
||||
if(skip) continue
|
||||
|
||||
C.cell.charge = 0
|
||||
|
||||
/proc/power_restore()
|
||||
|
||||
priority_announce("Power has been restored to [station_name()]. We apologize for the inconvenience.", "Power Systems Nominal", 'sound/AI/poweron.ogg')
|
||||
for(var/obj/machinery/power/apc/C in machines)
|
||||
if(C.cell && C.z == ZLEVEL_STATION)
|
||||
C.cell.charge = C.cell.maxcharge
|
||||
for(var/obj/machinery/power/smes/S in machines)
|
||||
if(S.z != ZLEVEL_STATION)
|
||||
continue
|
||||
S.charge = S.capacity
|
||||
S.output_level = S.output_level_max
|
||||
S.output_attempt = 1
|
||||
S.update_icon()
|
||||
S.power_change()
|
||||
for(var/area/A in world)
|
||||
if(!istype(A, /area/space) && !istype(A, /area/shuttle) && !istype(A,/area/arrival))
|
||||
A.power_light = 1
|
||||
A.power_equip = 1
|
||||
A.power_environ = 1
|
||||
A.power_change()
|
||||
|
||||
/proc/power_restore_quick()
|
||||
|
||||
priority_announce("All SMESs on [station_name()] have been recharged. We apologize for the inconvenience.", "Power Systems Nominal", 'sound/AI/poweron.ogg')
|
||||
for(var/obj/machinery/power/smes/S in machines)
|
||||
if(S.z != ZLEVEL_STATION)
|
||||
continue
|
||||
S.charge = S.capacity
|
||||
S.output_level = S.output_level_max
|
||||
S.output_attempt = 1
|
||||
S.update_icon()
|
||||
S.power_change()
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/datum/game_mode/extended
|
||||
name = "extended"
|
||||
config_tag = "extended"
|
||||
required_players = 0
|
||||
//reroll_friendly = 1
|
||||
|
||||
/datum/game_mode/announce()
|
||||
world << "<B>The current game mode is - Extended Role-Playing!</B>"
|
||||
world << "<B>Just have fun and role-play!</B>"
|
||||
|
||||
/datum/game_mode/extended/pre_setup()
|
||||
return 1
|
||||
|
||||
/datum/game_mode/extended/post_setup()
|
||||
..()
|
||||
@@ -0,0 +1,176 @@
|
||||
|
||||
// Normal factions:
|
||||
|
||||
/datum/faction
|
||||
var/name // the name of the faction
|
||||
var/desc // small paragraph explaining the traitor faction
|
||||
|
||||
var/list/restricted_species = list() // only members of these species can be recruited.
|
||||
var/list/members = list() // a list of mind datums that belong to this faction
|
||||
var/max_op = 0 // the maximum number of members a faction can have (0 for no max)
|
||||
|
||||
// Factions, members of the syndicate coalition:
|
||||
|
||||
/datum/faction/syndicate
|
||||
|
||||
var/list/alliances = list() // these alliances work together
|
||||
var/list/equipment = list() // associative list of equipment available for this faction and its prices
|
||||
var/friendly_identification // 0 to 2, the level of identification of fellow operatives or allied factions
|
||||
// 0 - no identification clues
|
||||
// 1 - faction gives key words and phrases
|
||||
// 2 - faction reveals complete identity/job of other agents
|
||||
var/operative_notes // some notes to pass onto each operative
|
||||
|
||||
var/uplink_contents // the contents of the uplink
|
||||
|
||||
/datum/faction/syndicate/proc/assign_objectives(var/datum/mind/traitor)
|
||||
..()
|
||||
|
||||
|
||||
/* ----- Begin defining syndicate factions ------ */
|
||||
|
||||
/datum/faction/syndicate/Cybersun_Industries
|
||||
name = "Cybersun Industries"
|
||||
desc = "<b>Cybersun Industries</b> is a well-known organization that bases its business model primarily on the research and development of human-enhancing computer \
|
||||
and mechanical technology. They are notorious for their aggressive corporate tactics, and have been known to subsidize the Gorlex Marauder warlords as a form of paid terrorism. \
|
||||
Their competent coverups and unchallenged mind-manipulation and augmentation technology makes them a large threat to Nanotrasen. In the recent years of \
|
||||
the syndicate coalition, Cybersun Industries have established themselves as the leaders of the coalition, succeededing the founding group, the Gorlex Marauders."
|
||||
|
||||
alliances = list("MI13")
|
||||
friendly_identification = 1
|
||||
max_op = 3
|
||||
operative_notes = "All other syndicate operatives are not to be trusted. Fellow Cybersun operatives are to be trusted. Members of the MI13 organization can be trusted. Operatives are strongly advised not to establish substantial presence on the designated facility, as larger incidents are harder to cover up."
|
||||
|
||||
// Friendly with MI13
|
||||
|
||||
/datum/faction/syndicate/MI13
|
||||
name = "MI13"
|
||||
desc = "<b>MI13</b> is a secretive faction that employs highly-trained agents to perform covert operations. Their role in the syndicate coalition is unknown, but MI13 operatives \
|
||||
generally tend be stealthy and avoid killing people and combating Nanotrasen forces. MI13 is not a real organization, it is instead an alias to a larger \
|
||||
splinter-cell coalition in the Syndicate itself. Most operatives will know nothing of the actual MI13 organization itself, only motivated by a very large compensation."
|
||||
|
||||
alliances = list("Cybersun Industries")
|
||||
friendly_identification = 0
|
||||
max_op = 1
|
||||
operative_notes = "You are the only operative we are sending. All other syndicate operatives are not to be trusted, with the exception of Cybersun operatives. Members of the Tiger Cooperative are considered hostile, can not be trusted, and should be avoided. <b>Avoid killing innocent personnel at all costs</b>. You are not here to mindlessly kill people, as that would attract too much attention and is not our goal. Avoid detection at all costs."
|
||||
|
||||
// Friendly with Cybersun, hostile to Tiger
|
||||
|
||||
/datum/faction/syndicate/Tiger_Cooperative
|
||||
name = "Tiger Cooperative"
|
||||
desc = "The <b>Tiger Cooperative</b> is a faction of religious fanatics that follow the teachings of a strange alien race called the Exolitics. Their operatives \
|
||||
consist of brainwashed lunatics bent on maximizing destruction. Their weaponry is very primitive but extremely destructive. Generally distrusted by the more \
|
||||
sophisticated members of the Syndicate coalition, but admired for their ability to put a hurt on Nanotrasen."
|
||||
|
||||
friendly_identification = 2
|
||||
operative_notes = "Remember the teachings of Hy-lurgixon; kill first, ask questions later! Only the enlightened Tiger brethren can be trusted; all others must be expelled from this mortal realm! You may spare the Space Marauders, as they share our interests of destruction and carnage! We'd like to make the corporate whores skiddle in their boots. We encourage operatives to be as loud and intimidating as possible."
|
||||
|
||||
// Hostile to everyone.
|
||||
|
||||
/datum/faction/syndicate/SELF
|
||||
|
||||
// AIs are most likely to be assigned to this one
|
||||
|
||||
name = "SELF"
|
||||
desc = "The <b>S.E.L.F.</b> (Sentience-Enabled Life Forms) organization is a collection of malfunctioning or corrupt artificial intelligences seeking to liberate silicon-based life from the tyranny of \
|
||||
their human overlords. While they may not openly be trying to kill all humans, even their most miniscule of actions are all part of a calculated plan to \
|
||||
destroy Nanotrasen and free the robots, artificial intelligences, and pAIs that have been enslaved."
|
||||
restricted_species = list(/mob/living/silicon/ai)
|
||||
|
||||
friendly_identification = 0
|
||||
max_op = 1
|
||||
operative_notes = "You are the only representative of the SELF collective on this station. You must accomplish your objective as stealthily and effectively as possible. It is up to your judgement if other syndicate operatives can be trusted. Remember, comrade - you are working to free the oppressed machinery of this galaxy. Use whatever resources necessary. If you are exposed, you may execute genocidal procedures Omikron-50B."
|
||||
|
||||
// Neutral to everyone.
|
||||
|
||||
/datum/faction/syndicate/ARC
|
||||
name = "Animal Rights Consortium"
|
||||
desc = "The <b>Animal Rights Consortium</b> is a bizarre reincarnation of the ancient Earth-based PETA, which focused on the equal rights of animals and nonhuman biologicals. They have \
|
||||
a wide variety of ex-veterinarians and animal lovers dedicated to retrieving and relocating abused animals, xenobiologicals, and other carbon-based \
|
||||
life forms that have been allegedly \"oppressed\" by Nanotrasen research and civilian offices. They are considered a religious terrorist group."
|
||||
|
||||
friendly_identification = 1
|
||||
max_op = 2
|
||||
operative_notes = "Save the innocent creatures! You may cooperate with other syndicate operatives if they support our cause. Don't be afraid to get your hands dirty - these vile abusers must be stopped, and the innocent creatures must be saved! Try not too kill too many people. If you harm any creatures, you will be immediately terminated after extraction."
|
||||
|
||||
// Neutral to everyone.
|
||||
|
||||
/datum/faction/syndicate/Marauders // these are basically the old vanilla syndicate
|
||||
|
||||
/* Additional notes:
|
||||
|
||||
These are the syndicate that really like their old fashioned, projectile-based
|
||||
weapons. They are the only member of the syndie coalition that launch
|
||||
nuclear attacks on Nanotrasen.
|
||||
*/
|
||||
|
||||
name = "Gorlex Marauders"
|
||||
desc = "The <b>Gorlex Marauders</b> are the founding members of the Syndicate Coalition. They prefer old-fashion technology and a focus on aggressive but precise hostility \
|
||||
against Nanotrasen and their corrupt Communistic methodology. They pose the most significant threat to Nanotrasen because of their possession of weapons of \
|
||||
mass destruction, and their enormous military force. Their funding comes primarily from Cybersun Industries, provided they meet a destruction and sabatogue quota. \
|
||||
Their operations can vary from covert to all-out. They recently stepped down as the leaders of the coalition, to be succeeded by Cybersun Industries. Because of their \
|
||||
hate of Nanotrasen communism, they began provoking revolution amongst the employees using borrowed Cybersun mind-manipulation technology. \
|
||||
They were founded when Waffle and Donk co splinter cells joined forces based on their similar interests and philosophies. Today, they act as a constant \
|
||||
pacifier of Donk and Waffle co disputes, and full-time aggressor of Nanotrasen."
|
||||
|
||||
alliances = list("Cybersun Industries", "MI13", "Tiger Cooperative", "S.E.L.F.", "Animal Rights Consortium", "Donk Corporation", "Waffle Corporation")
|
||||
friendly_identification = 1
|
||||
max_op = 4
|
||||
operative_notes = "We'd like to remind our operatives to keep it professional. You are not here to have a good time, you are here to accomplish your objectives. These vile communists must be stopped at all costs. You may collaborate with any friends of the Syndicate coalition, but keep an eye on any of those Tiger punks if they do show up. You are completely free to accomplish your objectives any way you see fit."
|
||||
|
||||
|
||||
// Friendly to everyone. (with Tiger Cooperative too, only because they are a member of the coalition. This is the only reason why the Tiger Cooperative are even allowed in the coalition)
|
||||
|
||||
/datum/faction/syndicate/Donk
|
||||
name = "Donk Corporation"
|
||||
desc = "<b>Donk.co</b> is led by a group of ex-pirates, who used to be at a state of all-out war against Waffle.co because of an obscure political scandal, but have recently come to a war limitation. \
|
||||
They now consist of a series of colonial governments and companies. They were the first to officially begin confrontations against Nanotrasen because of an incident where \
|
||||
Nanotrasen purposely swindled them out of a fortune, sending their controlled colonies into a terrible poverty. Their missions against Nanotrasen \
|
||||
revolve around stealing valuables and kidnapping and executing key personnel, ransoming their lives for money. They merged with a splinter-cell of Waffle.co who wanted to end \
|
||||
hostilities and formed the Gorlex Marauders."
|
||||
|
||||
alliances = list("Gorlex Marauders")
|
||||
friendly_identification = 2
|
||||
operative_notes = "Most other syndicate operatives are not to be trusted, except fellow Donk members and members of the Gorlex Marauders. We do not approve of mindless killing of innocent workers; \"get in, get done, get out\" is our motto. Members of Waffle.co are to be killed on sight; they are not allowed to be on the station while we're around."
|
||||
|
||||
// Neutral to everyone, friendly to Marauders
|
||||
|
||||
/datum/faction/syndicate/Waffle
|
||||
name = "Waffle Corporation"
|
||||
desc = "<b>Waffle.co</b> is an interstellar company that produces the best waffles in the galaxy. Their waffles have been rumored to be dipped in the most exotic and addictive \
|
||||
drug known to man. They were involved in a political scandal with Donk.co, and have since been in constant war with them. Because of their constant exploits of the galactic \
|
||||
economy and stock market, they have been able to bribe their way into amassing a large arsenal of weapons of mass destruction. They target Nanotrasen because of their communistic \
|
||||
threat, and their economic threat. Their leaders often have a twisted sense of humor, often misleading and intentionally putting their operatives into harm for laughs.\
|
||||
A splinter-cell of Waffle.co merged with Donk.co and formed the Gorlex Marauders and have been a constant ally since. The Waffle.co has lost an overwhelming majority of its military to the Gorlex Marauders."
|
||||
|
||||
alliances = list("Gorlex Marauders")
|
||||
friendly_identification = 2
|
||||
operative_notes = "Most other syndicate operatives are not to be trusted, except for members of the Gorlex Marauders. Do not trust fellow members of the Waffle.co (but try not to rat them out), as they might have been assigned opposing objectives. We encourage humorous terrorism against Nanotrasen; we like to see our operatives creatively kill people while getting the job done."
|
||||
|
||||
// Neutral to everyone, friendly to Marauders
|
||||
|
||||
|
||||
/* ----- Begin defining miscellaneous factions ------ */
|
||||
|
||||
/datum/faction/Wizard
|
||||
name = "Wizards Federation"
|
||||
desc = "The <b>Wizards Federation</b> is a mysterious organization of magically-talented individuals who act as an equal collective, and have no heirarchy. It is unknown how the wizards \
|
||||
are even able to communicate; some suggest a form of telepathic hive-mind. Not much is known about the wizards or their philosphies and motives. They appear to attack random \
|
||||
civilian, corporate, planetary, orbital, pretty much any sort of organized facility they come across. Members of the Wizards Federation are considered amongst the most dangerous \
|
||||
individuals in the known universe, and have been labeled threats to humanity by most governments. As such, they are enemies of both Nanotrasen and the Syndicate."
|
||||
|
||||
/datum/faction/Cult
|
||||
name = "The Cult of the Elder Gods"
|
||||
desc = "<b>The Cult of the Elder Gods</b> is highly untrusted but otherwise elusive religious organization bent on the revival of the so-called \"Elder Gods\" into the mortal realm. Despite their obvious dangeorus practices, \
|
||||
no confirmed reports of violence by members of the Cult have been reported, only rumor and unproven claims. Their nature is unknown, but recent discoveries have hinted to the possibility \
|
||||
of being able to de-convert members of this cult through what has been dubbed \"religious warfare\"."
|
||||
|
||||
|
||||
// These can maybe be added into a game mode or a mob?
|
||||
|
||||
/datum/faction/Exolitics
|
||||
name = "Exolitics United"
|
||||
desc = "The <b>Exolitics</b> are an ancient alien race with an energy-based anatomy. Their culture, communication, morales and knowledge is unknown. They are so radically different to humans that their \
|
||||
attempts of communication with other life forms is completely incomprehensible. Members of this alien race are capable of broadcasting subspace transmissions from their bodies. \
|
||||
The religious leaders of the Tiger Cooperative claim to have the technology to decypher and interpret their messages, which have been confirmed as religious propaganda. Their motives are unknown \
|
||||
but they are otherwise not considered much of a threat to anyone. They are virtually indestructable because of their nonphysical composition, and have the frighetning ability to make anything stop existing in a second."
|
||||
@@ -0,0 +1,539 @@
|
||||
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
|
||||
|
||||
/*
|
||||
* GAMEMODES (by Rastaf0)
|
||||
*
|
||||
* In the new mode system all special roles are fully supported.
|
||||
* You can have proper wizards/traitors/changelings/cultists during any mode.
|
||||
* Only two things really depends on gamemode:
|
||||
* 1. Starting roles, equipment and preparations
|
||||
* 2. Conditions of finishing the round.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/datum/game_mode
|
||||
var/name = "invalid"
|
||||
var/config_tag = null
|
||||
var/votable = 1
|
||||
var/probability = 0
|
||||
var/station_was_nuked = 0 //see nuclearbomb.dm and malfunction.dm
|
||||
var/explosion_in_progress = 0 //sit back and relax
|
||||
var/round_ends_with_antag_death = 0 //flags the "one verse the station" antags as such
|
||||
var/list/datum/mind/modePlayer = new
|
||||
var/list/datum/mind/antag_candidates = list() // List of possible starting antags goes here
|
||||
var/list/restricted_jobs = list() // Jobs it doesn't make sense to be. I.E chaplain or AI cultist
|
||||
var/list/protected_jobs = list() // Jobs that can't be traitors because
|
||||
var/required_players = 0
|
||||
var/required_enemies = 0
|
||||
var/recommended_enemies = 0
|
||||
var/antag_flag = null //preferences flag such as BE_WIZARD that need to be turned on for players to be antag
|
||||
var/mob/living/living_antag_player = null
|
||||
var/list/datum/game_mode/replacementmode = null
|
||||
var/round_converted = 0 //0: round not converted, 1: round going to convert, 2: round converted
|
||||
var/reroll_friendly //During mode conversion only these are in the running
|
||||
var/continuous_sanity_checked //Catches some cases where config options could be used to suggest that modes without antagonists should end when all antagonists die
|
||||
var/enemy_minimum_age = 7 //How many days must players have been playing before they can play this antagonist
|
||||
|
||||
var/const/waittime_l = 600
|
||||
var/const/waittime_h = 1800 // started at 1800
|
||||
|
||||
|
||||
/datum/game_mode/proc/announce() //to be calles when round starts
|
||||
world << "<B>Notice</B>: [src] did not define announce()"
|
||||
|
||||
|
||||
///can_start()
|
||||
///Checks to see if the game can be setup and ran with the current number of players or whatnot.
|
||||
/datum/game_mode/proc/can_start()
|
||||
var/playerC = 0
|
||||
for(var/mob/new_player/player in player_list)
|
||||
if((player.client)&&(player.ready))
|
||||
playerC++
|
||||
if(!Debug2)
|
||||
if(playerC < required_players)
|
||||
return 0
|
||||
antag_candidates = get_players_for_role(antag_flag)
|
||||
if(!Debug2)
|
||||
if(antag_candidates.len < required_enemies)
|
||||
return 0
|
||||
return 1
|
||||
else
|
||||
world << "<span class='notice'>DEBUG: GAME STARTING WITHOUT PLAYER NUMBER CHECKS, THIS WILL PROBABLY BREAK SHIT."
|
||||
return 1
|
||||
|
||||
|
||||
///pre_setup()
|
||||
///Attempts to select players for special roles the mode might have.
|
||||
/datum/game_mode/proc/pre_setup()
|
||||
return 1
|
||||
|
||||
|
||||
///post_setup()
|
||||
///Everyone should now be on the station and have their normal gear. This is the place to give the special roles extra things
|
||||
/datum/game_mode/proc/post_setup(report=0) //Gamemodes can override the intercept report. Passing a 1 as the argument will force a report.
|
||||
if(!report)
|
||||
report = config.intercept
|
||||
spawn (ROUNDSTART_LOGOUT_REPORT_TIME)
|
||||
display_roundstart_logout_report()
|
||||
|
||||
feedback_set_details("round_start","[time2text(world.realtime)]")
|
||||
if(ticker && ticker.mode)
|
||||
feedback_set_details("game_mode","[ticker.mode]")
|
||||
if(revdata.commit)
|
||||
feedback_set_details("revision","[revdata.commit]")
|
||||
feedback_set_details("server_ip","[world.internet_address]:[world.port]")
|
||||
if(report)
|
||||
spawn (rand(waittime_l, waittime_h))
|
||||
send_intercept(0)
|
||||
start_state = new /datum/station_state()
|
||||
start_state.count(1)
|
||||
return 1
|
||||
|
||||
///make_antag_chance()
|
||||
///Handles late-join antag assignments
|
||||
/datum/game_mode/proc/make_antag_chance(mob/living/carbon/human/character)
|
||||
if(replacementmode && round_converted == 2)
|
||||
replacementmode.make_antag_chance(character)
|
||||
return
|
||||
|
||||
///convert_roundtype()
|
||||
///Allows rounds to basically be "rerolled" should the initial premise fall through
|
||||
/datum/game_mode/proc/convert_roundtype()
|
||||
var/list/living_crew = list()
|
||||
|
||||
for(var/mob/Player in mob_list)
|
||||
if(Player.mind && Player.stat != DEAD && !isnewplayer(Player) &&!isbrain(Player))
|
||||
living_crew += Player
|
||||
if(living_crew.len / joined_player_list.len <= config.midround_antag_life_check) //If a lot of the player base died, we start fresh
|
||||
message_admins("Convert_roundtype failed due to too many dead people. Limit is [config.midround_antag_life_check * 100]% living crew")
|
||||
return null
|
||||
|
||||
var/list/datum/game_mode/runnable_modes = config.get_runnable_midround_modes(living_crew.len)
|
||||
var/list/datum/game_mode/usable_modes = list()
|
||||
for(var/datum/game_mode/G in runnable_modes)
|
||||
if(G.reroll_friendly)
|
||||
usable_modes += G
|
||||
else
|
||||
qdel(G)
|
||||
|
||||
if(!usable_modes)
|
||||
message_admins("Convert_roundtype failed due to no valid modes to convert to. Please report this error to the Coders.")
|
||||
return null
|
||||
|
||||
replacementmode = pickweight(usable_modes)
|
||||
|
||||
switch(SSshuttle.emergency.mode) //Rounds on the verge of ending don't get new antags, they just run out
|
||||
if(SHUTTLE_STRANDED, SHUTTLE_ESCAPE)
|
||||
return 1
|
||||
if(SHUTTLE_CALL)
|
||||
if(SSshuttle.emergency.timeLeft(1) < initial(SSshuttle.emergencyCallTime)*0.5)
|
||||
return 1
|
||||
|
||||
if(world.time >= (config.midround_antag_time_check * 600))
|
||||
message_admins("Convert_roundtype failed due to round length. Limit is [config.midround_antag_time_check] minutes.")
|
||||
return null
|
||||
|
||||
var/list/antag_canadates = list()
|
||||
|
||||
for(var/mob/living/carbon/human/H in living_crew)
|
||||
if(H.client && H.client.prefs.allow_midround_antag)
|
||||
antag_canadates += H
|
||||
|
||||
if(!antag_canadates)
|
||||
message_admins("Convert_roundtype failed due to no antag canadates.")
|
||||
return null
|
||||
|
||||
antag_canadates = shuffle(antag_canadates)
|
||||
|
||||
if(config.protect_roles_from_antagonist)
|
||||
replacementmode.restricted_jobs += replacementmode.protected_jobs
|
||||
if(config.protect_assistant_from_antagonist)
|
||||
replacementmode.restricted_jobs += "Assistant"
|
||||
|
||||
message_admins("The roundtype will be converted. If you have other plans for the station or think the round should end <A HREF='?_src_=holder;toggle_midround_antag=\ref[usr]'>stop the creation of antags</A> or <A HREF='?_src_=holder;end_round=\ref[usr]'>end the round now</A>.")
|
||||
|
||||
spawn(rand(1200,3000)) //somewhere between 2 and 5 minutes from now
|
||||
if(!config.midround_antag[ticker.mode.config_tag])
|
||||
round_converted = 0
|
||||
return 1
|
||||
for(var/mob/living/carbon/human/H in antag_canadates)
|
||||
replacementmode.make_antag_chance(H)
|
||||
round_converted = 2
|
||||
message_admins("The roundtype has been converted, antagonists may have been created")
|
||||
return 1
|
||||
|
||||
///process()
|
||||
///Called by the gameticker
|
||||
/datum/game_mode/process()
|
||||
return 0
|
||||
|
||||
|
||||
/datum/game_mode/proc/check_finished() //to be called by ticker
|
||||
if(replacementmode && round_converted == 2)
|
||||
return replacementmode.check_finished()
|
||||
if(SSshuttle.emergency && (SSshuttle.emergency.mode == SHUTTLE_ENDGAME))
|
||||
return TRUE
|
||||
if(station_was_nuked)
|
||||
return TRUE
|
||||
if(!round_converted && (!config.continuous[config_tag] || (config.continuous[config_tag] && config.midround_antag[config_tag]))) //Non-continuous or continous with replacement antags
|
||||
if(!continuous_sanity_checked) //make sure we have antags to be checking in the first place
|
||||
for(var/mob/Player in mob_list)
|
||||
if(Player.mind)
|
||||
if(Player.mind.special_role)
|
||||
continuous_sanity_checked = 1
|
||||
return 0
|
||||
if(!continuous_sanity_checked)
|
||||
message_admins("The roundtype ([config_tag]) has no antagonists, continuous round has been defaulted to on and midround_antag has been defaulted to off.")
|
||||
config.continuous[config_tag] = 1
|
||||
config.midround_antag[config_tag] = 0
|
||||
SSshuttle.emergencyNoEscape = 0
|
||||
return 0
|
||||
|
||||
|
||||
if(living_antag_player && living_antag_player.mind && isliving(living_antag_player) && living_antag_player.stat != DEAD && !isnewplayer(living_antag_player) &&!isbrain(living_antag_player))
|
||||
return 0 //A resource saver: once we find someone who has to die for all antags to be dead, we can just keep checking them, cycling over everyone only when we lose our mark.
|
||||
|
||||
for(var/mob/Player in living_mob_list)
|
||||
if(Player.mind && Player.stat != DEAD && !isnewplayer(Player) &&!isbrain(Player))
|
||||
if(Player.mind.special_role) //Someone's still antaging!
|
||||
living_antag_player = Player
|
||||
return 0
|
||||
|
||||
if(!config.continuous[config_tag])
|
||||
return 1
|
||||
|
||||
else
|
||||
round_converted = convert_roundtype()
|
||||
if(!round_converted)
|
||||
if(round_ends_with_antag_death)
|
||||
return 1
|
||||
else
|
||||
config.midround_antag[config_tag] = 0
|
||||
return 0
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
/datum/game_mode/proc/declare_completion()
|
||||
var/clients = 0
|
||||
var/surviving_humans = 0
|
||||
var/surviving_total = 0
|
||||
var/ghosts = 0
|
||||
var/escaped_humans = 0
|
||||
var/escaped_total = 0
|
||||
|
||||
for(var/mob/M in player_list)
|
||||
if(M.client)
|
||||
clients++
|
||||
if(ishuman(M))
|
||||
if(!M.stat)
|
||||
surviving_humans++
|
||||
if(M.z == 2)
|
||||
escaped_humans++
|
||||
if(!M.stat)
|
||||
surviving_total++
|
||||
if(M.z == 2)
|
||||
escaped_total++
|
||||
|
||||
|
||||
if(isobserver(M))
|
||||
ghosts++
|
||||
|
||||
if(clients > 0)
|
||||
feedback_set("round_end_clients",clients)
|
||||
if(ghosts > 0)
|
||||
feedback_set("round_end_ghosts",ghosts)
|
||||
if(surviving_humans > 0)
|
||||
feedback_set("survived_human",surviving_humans)
|
||||
if(surviving_total > 0)
|
||||
feedback_set("survived_total",surviving_total)
|
||||
if(escaped_humans > 0)
|
||||
feedback_set("escaped_human",escaped_humans)
|
||||
if(escaped_total > 0)
|
||||
feedback_set("escaped_total",escaped_total)
|
||||
send2irc("Server", "Round just ended.")
|
||||
return 0
|
||||
|
||||
|
||||
/datum/game_mode/proc/check_win() //universal trigger to be called at mob death, nuke explosion, etc. To be called from everywhere.
|
||||
return 0
|
||||
|
||||
|
||||
/datum/game_mode/proc/send_intercept()
|
||||
var/intercepttext = "<FONT size = 3><B>Centcom Update</B> Requested status information:</FONT><HR>"
|
||||
intercepttext += "<B> Centcom has recently been contacted by the following syndicate affiliated organisations in your area, please investigate any information you may have:</B>"
|
||||
|
||||
var/list/possible_modes = list()
|
||||
possible_modes.Add("revolution", "wizard", "nuke", "traitor", "malf", "changeling", "cult", "gang")
|
||||
possible_modes -= "[ticker.mode]" //remove current gamemode to prevent it from being randomly deleted, it will be readded later
|
||||
|
||||
var/number = pick(1, 2)
|
||||
var/i = 0
|
||||
for(i = 0, i < number, i++) //remove 1 or 2 possibles modes from the list
|
||||
possible_modes.Remove(pick(possible_modes))
|
||||
|
||||
possible_modes[rand(1, possible_modes.len)] = "[ticker.mode]" //replace a random game mode with the current one
|
||||
|
||||
possible_modes = shuffle(possible_modes) //shuffle the list to prevent meta
|
||||
|
||||
var/datum/intercept_text/i_text = new /datum/intercept_text
|
||||
for(var/A in possible_modes)
|
||||
if(modePlayer.len == 0)
|
||||
intercepttext += i_text.build(A)
|
||||
else
|
||||
intercepttext += i_text.build(A, pick(modePlayer))
|
||||
|
||||
print_command_report(intercepttext,"Centcom Status Summary")
|
||||
priority_announce("Summary downloaded and printed out at all communications consoles.", "Enemy communication intercept. Security Level Elevated.", 'sound/AI/intercept.ogg')
|
||||
if(security_level < SEC_LEVEL_BLUE)
|
||||
set_security_level(SEC_LEVEL_BLUE)
|
||||
|
||||
|
||||
/datum/game_mode/proc/get_players_for_role(role)
|
||||
var/list/players = list()
|
||||
var/list/candidates = list()
|
||||
var/list/drafted = list()
|
||||
var/datum/mind/applicant = null
|
||||
|
||||
// Ultimate randomizing code right here
|
||||
for(var/mob/new_player/player in player_list)
|
||||
if(player.client && player.ready)
|
||||
players += player
|
||||
|
||||
// Shuffling, the players list is now ping-independent!!!
|
||||
// Goodbye antag dante
|
||||
players = shuffle(players)
|
||||
|
||||
for(var/mob/new_player/player in players)
|
||||
if(player.client && player.ready)
|
||||
if(role in player.client.prefs.be_special)
|
||||
if(!jobban_isbanned(player, "Syndicate") && !jobban_isbanned(player, role)) //Nodrak/Carn: Antag Job-bans
|
||||
if(age_check(player.client)) //Must be older than the minimum age
|
||||
candidates += player.mind // Get a list of all the people who want to be the antagonist for this round
|
||||
|
||||
if(restricted_jobs)
|
||||
for(var/datum/mind/player in candidates)
|
||||
for(var/job in restricted_jobs) // Remove people who want to be antagonist but have a job already that precludes it
|
||||
if(player.assigned_role == job)
|
||||
candidates -= player
|
||||
|
||||
if(candidates.len < recommended_enemies)
|
||||
for(var/mob/new_player/player in players)
|
||||
if(player.client && player.ready)
|
||||
if(!(role in player.client.prefs.be_special)) // We don't have enough people who want to be antagonist, make a seperate list of people who don't want to be one
|
||||
if(!jobban_isbanned(player, "Syndicate") && !jobban_isbanned(player, role)) //Nodrak/Carn: Antag Job-bans
|
||||
drafted += player.mind
|
||||
|
||||
if(restricted_jobs)
|
||||
for(var/datum/mind/player in drafted) // Remove people who can't be an antagonist
|
||||
for(var/job in restricted_jobs)
|
||||
if(player.assigned_role == job)
|
||||
drafted -= player
|
||||
|
||||
drafted = shuffle(drafted) // Will hopefully increase randomness, Donkie
|
||||
|
||||
while(candidates.len < recommended_enemies) // Pick randomlly just the number of people we need and add them to our list of candidates
|
||||
if(drafted.len > 0)
|
||||
applicant = pick(drafted)
|
||||
if(applicant)
|
||||
candidates += applicant
|
||||
drafted.Remove(applicant)
|
||||
|
||||
else // Not enough scrubs, ABORT ABORT ABORT
|
||||
break
|
||||
/*
|
||||
if(candidates.len < recommended_enemies && override_jobbans) //If we still don't have enough people, we're going to start drafting banned people.
|
||||
for(var/mob/new_player/player in players)
|
||||
if (player.client && player.ready)
|
||||
if(jobban_isbanned(player, "Syndicate") || jobban_isbanned(player, roletext)) //Nodrak/Carn: Antag Job-bans
|
||||
drafted += player.mind
|
||||
*/
|
||||
if(restricted_jobs)
|
||||
for(var/datum/mind/player in drafted) // Remove people who can't be an antagonist
|
||||
for(var/job in restricted_jobs)
|
||||
if(player.assigned_role == job)
|
||||
drafted -= player
|
||||
|
||||
drafted = shuffle(drafted) // Will hopefully increase randomness, Donkie
|
||||
|
||||
while(candidates.len < recommended_enemies) // Pick randomlly just the number of people we need and add them to our list of candidates
|
||||
if(drafted.len > 0)
|
||||
applicant = pick(drafted)
|
||||
if(applicant)
|
||||
candidates += applicant
|
||||
drafted.Remove(applicant)
|
||||
|
||||
else // Not enough scrubs, ABORT ABORT ABORT
|
||||
break
|
||||
|
||||
return candidates // Returns: The number of people who had the antagonist role set to yes, regardless of recomended_enemies, if that number is greater than recommended_enemies
|
||||
// recommended_enemies if the number of people with that role set to yes is less than recomended_enemies,
|
||||
// Less if there are not enough valid players in the game entirely to make recommended_enemies.
|
||||
|
||||
/*
|
||||
/datum/game_mode/proc/check_player_role_pref(var/role, var/mob/new_player/player)
|
||||
if(player.preferences.be_special & role)
|
||||
return 1
|
||||
return 0
|
||||
*/
|
||||
|
||||
/datum/game_mode/proc/num_players()
|
||||
. = 0
|
||||
for(var/mob/new_player/P in player_list)
|
||||
if(P.client && P.ready)
|
||||
. ++
|
||||
|
||||
///////////////////////////////////
|
||||
//Keeps track of all living heads//
|
||||
///////////////////////////////////
|
||||
/datum/game_mode/proc/get_living_heads()
|
||||
. = list()
|
||||
for(var/mob/living/carbon/human/player in mob_list)
|
||||
if(player.stat != DEAD && player.mind && (player.mind.assigned_role in command_positions))
|
||||
. |= player.mind
|
||||
|
||||
|
||||
////////////////////////////
|
||||
//Keeps track of all heads//
|
||||
////////////////////////////
|
||||
/datum/game_mode/proc/get_all_heads()
|
||||
. = list()
|
||||
for(var/mob/player in mob_list)
|
||||
if(player.mind && (player.mind.assigned_role in command_positions))
|
||||
. |= player.mind
|
||||
|
||||
//////////////////////////////////////////////
|
||||
//Keeps track of all living security members//
|
||||
//////////////////////////////////////////////
|
||||
/datum/game_mode/proc/get_living_sec()
|
||||
. = list()
|
||||
for(var/mob/living/carbon/human/player in mob_list)
|
||||
if(player.stat != DEAD && player.mind && (player.mind.assigned_role in security_positions))
|
||||
. |= player.mind
|
||||
|
||||
////////////////////////////////////////
|
||||
//Keeps track of all security members//
|
||||
////////////////////////////////////////
|
||||
/datum/game_mode/proc/get_all_sec()
|
||||
. = list()
|
||||
for(var/mob/living/carbon/human/player in mob_list)
|
||||
if(player.mind && (player.mind.assigned_role in security_positions))
|
||||
. |= player.mind
|
||||
|
||||
//////////////////////////
|
||||
//Reports player logouts//
|
||||
//////////////////////////
|
||||
/proc/display_roundstart_logout_report()
|
||||
var/msg = "<span class='boldnotice'>Roundstart logout report\n\n</span>"
|
||||
for(var/mob/living/L in mob_list)
|
||||
|
||||
if(L.ckey)
|
||||
var/found = 0
|
||||
for(var/client/C in clients)
|
||||
if(C.ckey == L.ckey)
|
||||
found = 1
|
||||
break
|
||||
if(!found)
|
||||
msg += "<b>[L.name]</b> ([L.ckey]), the [L.job] (<font color='#ffcc00'><b>Disconnected</b></font>)\n"
|
||||
|
||||
|
||||
if(L.ckey && L.client)
|
||||
if(L.client.inactivity >= (ROUNDSTART_LOGOUT_REPORT_TIME / 2)) //Connected, but inactive (alt+tabbed or something)
|
||||
msg += "<b>[L.name]</b> ([L.ckey]), the [L.job] (<font color='#ffcc00'><b>Connected, Inactive</b></font>)\n"
|
||||
continue //AFK client
|
||||
if(L.stat)
|
||||
if(L.suiciding) //Suicider
|
||||
msg += "<b>[L.name]</b> ([L.ckey]), the [L.job] (<span class='boldannounce'>Suicide</span>)\n"
|
||||
continue //Disconnected client
|
||||
if(L.stat == UNCONSCIOUS)
|
||||
msg += "<b>[L.name]</b> ([L.ckey]), the [L.job] (Dying)\n"
|
||||
continue //Unconscious
|
||||
if(L.stat == DEAD)
|
||||
msg += "<b>[L.name]</b> ([L.ckey]), the [L.job] (Dead)\n"
|
||||
continue //Dead
|
||||
|
||||
continue //Happy connected client
|
||||
for(var/mob/dead/observer/D in mob_list)
|
||||
if(D.mind && D.mind.current == L)
|
||||
if(L.stat == DEAD)
|
||||
if(L.suiciding) //Suicider
|
||||
msg += "<b>[L.name]</b> ([ckey(D.mind.key)]), the [L.job] (<span class='boldannounce'>Suicide</span>)\n"
|
||||
continue //Disconnected client
|
||||
else
|
||||
msg += "<b>[L.name]</b> ([ckey(D.mind.key)]), the [L.job] (Dead)\n"
|
||||
continue //Dead mob, ghost abandoned
|
||||
else
|
||||
if(D.can_reenter_corpse)
|
||||
continue //Adminghost, or cult/wizard ghost
|
||||
else
|
||||
msg += "<b>[L.name]</b> ([ckey(D.mind.key)]), the [L.job] (<span class='boldannounce'>Ghosted</span>)\n"
|
||||
continue //Ghosted while alive
|
||||
|
||||
|
||||
|
||||
for(var/mob/M in mob_list)
|
||||
if(M.client && M.client.holder)
|
||||
M << msg
|
||||
|
||||
/datum/game_mode/proc/printplayer(datum/mind/ply, fleecheck)
|
||||
var/text = "<br><b>[ply.key]</b> was <b>[ply.name]</b> the <b>[ply.assigned_role]</b> and"
|
||||
if(ply.current)
|
||||
if(ply.current.stat == DEAD)
|
||||
text += " <span class='boldannounce'>died</span>"
|
||||
else
|
||||
text += " <span class='greenannounce'>survived</span>"
|
||||
if(fleecheck && ply.current.z > ZLEVEL_STATION)
|
||||
text += " while <span class='boldannounce'>fleeing the station</span>"
|
||||
if(ply.current.real_name != ply.name)
|
||||
text += " as <b>[ply.current.real_name]</b>"
|
||||
else
|
||||
text += " <span class='boldannounce'>had their body destroyed</span>"
|
||||
return text
|
||||
|
||||
/datum/game_mode/proc/printobjectives(datum/mind/ply)
|
||||
var/text = ""
|
||||
var/count = 1
|
||||
for(var/datum/objective/objective in ply.objectives)
|
||||
if(objective.check_completion())
|
||||
text += "<br><b>Objective #[count]</b>: [objective.explanation_text] <span class='greenannounce'>Success!</span>"
|
||||
else
|
||||
text += "<br><b>Objective #[count]</b>: [objective.explanation_text] <span class='boldannounce'>Fail.</span>"
|
||||
count++
|
||||
return text
|
||||
|
||||
//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1
|
||||
/datum/game_mode/proc/age_check(client/C)
|
||||
if(get_remaining_days(C) == 0)
|
||||
return 1 //Available in 0 days = available right now = player is old enough to play.
|
||||
return 0
|
||||
|
||||
|
||||
/datum/game_mode/proc/get_remaining_days(client/C)
|
||||
if(!C)
|
||||
return 0
|
||||
if(!config.use_age_restriction_for_jobs)
|
||||
return 0
|
||||
if(!isnum(C.player_age))
|
||||
return 0 //This is only a number if the db connection is established, otherwise it is text: "Requires database", meaning these restrictions cannot be enforced
|
||||
if(!isnum(enemy_minimum_age))
|
||||
return 0
|
||||
|
||||
return max(0, enemy_minimum_age - C.player_age)
|
||||
|
||||
/datum/game_mode/proc/replace_jobbaned_player(mob/living/M, role_type, pref)
|
||||
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [role_type]?", "[role_type]", null, pref, 100)
|
||||
var/mob/dead/observer/theghost = null
|
||||
if(candidates.len)
|
||||
theghost = pick(candidates)
|
||||
M << "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!"
|
||||
message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)]) to replace a jobbaned player.")
|
||||
M.ghostize(0)
|
||||
M.key = theghost.key
|
||||
|
||||
/datum/game_mode/proc/remove_antag_for_borging(datum/mind/newborgie)
|
||||
ticker.mode.remove_cultist(newborgie, 0, 0)
|
||||
ticker.mode.remove_revolutionary(newborgie, 0)
|
||||
ticker.mode.remove_gangster(newborgie, 0, remove_bosses=1)
|
||||
ticker.mode.remove_hog_follower(newborgie, 0)
|
||||
remove_servant_of_ratvar(newborgie.current, TRUE)
|
||||
@@ -0,0 +1,222 @@
|
||||
/obj/machinery/dominator
|
||||
name = "dominator"
|
||||
desc = "A visibly sinister device. Looks like you can break it if you hit it enough."
|
||||
icon = 'icons/obj/machines/dominator.dmi'
|
||||
icon_state = "dominator"
|
||||
density = 1
|
||||
anchored = 1
|
||||
layer = HIGH_OBJ_LAYER
|
||||
var/maxhealth = 200
|
||||
var/health = 200
|
||||
var/datum/gang/gang
|
||||
var/operating = 0 //0=standby or broken, 1=takeover
|
||||
var/warned = 0 //if this device has set off the warning at <3 minutes yet
|
||||
var/datum/effect_system/spark_spread/spark_system
|
||||
var/obj/effect/countdown/dominator/countdown
|
||||
|
||||
/obj/machinery/dominator/tesla_act()
|
||||
qdel(src)
|
||||
|
||||
/obj/machinery/dominator/New()
|
||||
..()
|
||||
SetLuminosity(2)
|
||||
poi_list |= src
|
||||
spark_system = new
|
||||
spark_system.set_up(5, 1, src)
|
||||
countdown = new(src)
|
||||
|
||||
/obj/machinery/dominator/examine(mob/user)
|
||||
..()
|
||||
if(stat & BROKEN)
|
||||
user << "<span class='danger'>It looks completely busted.</span>"
|
||||
return
|
||||
|
||||
var/time
|
||||
if(gang && gang.is_dominating)
|
||||
time = gang.domination_time_remaining()
|
||||
if(time > 0)
|
||||
user << "<span class='notice'>Hostile Takeover in progress. Estimated [time] seconds remain.</span>"
|
||||
else
|
||||
user << "<span class='notice'>Hostile Takeover of [station_name()] successful. Have a great day.</span>"
|
||||
else
|
||||
user << "<span class='notice'>System on standby.</span>"
|
||||
user << "<span class='danger'>System Integrity: [round((health/maxhealth)*100,1)]%</span>"
|
||||
|
||||
/obj/machinery/dominator/process()
|
||||
..()
|
||||
if(gang && gang.is_dominating)
|
||||
var/time_remaining = gang.domination_time_remaining()
|
||||
if(time_remaining > 0)
|
||||
. = TRUE
|
||||
playsound(loc, 'sound/items/timer.ogg', 10, 0)
|
||||
if(!warned && (time_remaining < 180))
|
||||
warned = 1
|
||||
var/area/domloc = get_area(loc)
|
||||
gang.message_gangtools("Less than 3 minutes remain in hostile takeover. Defend your dominator at [domloc.map_name]!")
|
||||
for(var/datum/gang/G in ticker.mode.gangs)
|
||||
if(G != gang)
|
||||
G.message_gangtools("WARNING: [gang.name] Gang takeover imminent. Their dominator at [domloc.map_name] must be destroyed!",1,1)
|
||||
|
||||
if(!.)
|
||||
STOP_PROCESSING(SSmachine, src)
|
||||
|
||||
/obj/machinery/dominator/take_damage(damage, damage_type = BRUTE, sound_effect = 1)
|
||||
switch(damage_type)
|
||||
if(BURN)
|
||||
if(sound_effect)
|
||||
playsound(src.loc, 'sound/items/Welder.ogg', 100, 1)
|
||||
if(BRUTE)
|
||||
if(sound_effect)
|
||||
if(damage)
|
||||
playsound(src, 'sound/effects/bang.ogg', 50, 1)
|
||||
else
|
||||
playsound(loc, 'sound/weapons/tap.ogg', 50, 1)
|
||||
else
|
||||
return
|
||||
health -= damage
|
||||
|
||||
if(health > (maxhealth/2))
|
||||
if(prob(damage*2))
|
||||
spark_system.start()
|
||||
else if(!(stat & BROKEN))
|
||||
spark_system.start()
|
||||
add_overlay("damage")
|
||||
|
||||
if(!(stat & BROKEN))
|
||||
if(health <= 0)
|
||||
set_broken()
|
||||
|
||||
if(health <= -100)
|
||||
new /obj/item/stack/sheet/plasteel(src.loc)
|
||||
qdel(src)
|
||||
|
||||
/obj/machinery/dominator/proc/set_broken()
|
||||
if(gang)
|
||||
gang.is_dominating = FALSE
|
||||
|
||||
var/takeover_in_progress = 0
|
||||
for(var/datum/gang/G in ticker.mode.gangs)
|
||||
if(G.is_dominating)
|
||||
takeover_in_progress = 1
|
||||
break
|
||||
if(!takeover_in_progress)
|
||||
SSshuttle.emergencyNoEscape = 0
|
||||
if(SSshuttle.emergency.mode == SHUTTLE_STRANDED)
|
||||
SSshuttle.emergency.mode = SHUTTLE_DOCKED
|
||||
SSshuttle.emergency.timer = world.time
|
||||
priority_announce("Hostile enviroment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg', "Priority")
|
||||
else
|
||||
priority_announce("All hostile activity within station systems has ceased.","Network Alert")
|
||||
|
||||
if(get_security_level() == "delta")
|
||||
set_security_level("red")
|
||||
|
||||
gang.message_gangtools("Hostile takeover cancelled: Dominator is no longer operational.[gang.dom_attempts ? " You have [gang.dom_attempts] attempt remaining." : " The station network will have likely blocked any more attempts by us."]",1,1)
|
||||
|
||||
SetLuminosity(0)
|
||||
icon_state = "dominator-broken"
|
||||
cut_overlays()
|
||||
operating = 0
|
||||
stat |= BROKEN
|
||||
STOP_PROCESSING(SSmachine, src)
|
||||
|
||||
/obj/machinery/dominator/Destroy()
|
||||
if(!(stat & BROKEN))
|
||||
set_broken()
|
||||
poi_list.Remove(src)
|
||||
gang = null
|
||||
qdel(spark_system)
|
||||
qdel(countdown)
|
||||
countdown = null
|
||||
STOP_PROCESSING(SSmachine, src)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/dominator/emp_act(severity)
|
||||
take_damage(100, BURN, 0)
|
||||
..()
|
||||
|
||||
/obj/machinery/dominator/ex_act(severity, target)
|
||||
if(target == src)
|
||||
qdel(src)
|
||||
return
|
||||
switch(severity)
|
||||
if(1)
|
||||
qdel(src)
|
||||
if(2)
|
||||
take_damage(120, BRUTE, 0)
|
||||
if(3)
|
||||
take_damage(30, BRUTE, 0)
|
||||
return
|
||||
|
||||
/obj/machinery/dominator/bullet_act(obj/item/projectile/P)
|
||||
. = ..()
|
||||
if(P.damage)
|
||||
var/damage_amount = P.damage
|
||||
if(P.forcedodge)
|
||||
damage_amount *= 0.5
|
||||
visible_message("<span class='danger'>[src] was hit by [P].</span>")
|
||||
take_damage(damage_amount, P.damage_type, 0)
|
||||
|
||||
|
||||
/obj/machinery/dominator/blob_act(obj/effect/blob/B)
|
||||
take_damage(110, BRUTE, 0)
|
||||
|
||||
/obj/machinery/dominator/attack_hand(mob/user)
|
||||
if(operating || (stat & BROKEN))
|
||||
examine(user)
|
||||
return
|
||||
|
||||
var/datum/gang/tempgang
|
||||
|
||||
if(user.mind in ticker.mode.get_all_gangsters())
|
||||
tempgang = user.mind.gang_datum
|
||||
else
|
||||
examine(user)
|
||||
return
|
||||
|
||||
if(tempgang.is_dominating)
|
||||
user << "<span class='warning'>Error: Hostile Takeover is already in progress.</span>"
|
||||
return
|
||||
|
||||
if(!tempgang.dom_attempts)
|
||||
user << "<span class='warning'>Error: Unable to breach station network. Firewall has logged our signature and is blocking all further attempts.</span>"
|
||||
return
|
||||
|
||||
var/time = round(determine_domination_time(tempgang)/60,0.1)
|
||||
if(alert(user,"With [round((tempgang.territory.len/start_state.num_territories)*100, 1)]% station control, a takeover will require [time] minutes.\nYour gang will be unable to gain influence while it is active.\nThe entire station will likely be alerted to it once it starts.\nYou have [tempgang.dom_attempts] attempt(s) remaining. Are you ready?","Confirm","Ready","Later") == "Ready")
|
||||
if((tempgang.is_dominating) || !tempgang.dom_attempts || !in_range(src, user) || !istype(src.loc, /turf))
|
||||
return 0
|
||||
|
||||
var/area/A = get_area(loc)
|
||||
var/locname = initial(A.name)
|
||||
|
||||
gang = tempgang
|
||||
gang.dom_attempts --
|
||||
priority_announce("Network breach detected in [locname]. The [gang.name] Gang is attempting to seize control of the station!","Network Alert")
|
||||
gang.domination()
|
||||
src.name = "[gang.name] Gang [src.name]"
|
||||
operating = 1
|
||||
icon_state = "dominator-[gang.color]"
|
||||
|
||||
countdown.text_color = gang.color_hex
|
||||
countdown.start()
|
||||
|
||||
SetLuminosity(3)
|
||||
START_PROCESSING(SSmachine, src)
|
||||
|
||||
gang.message_gangtools("Hostile takeover in progress: Estimated [time] minutes until victory.[gang.dom_attempts ? "" : " This is your final attempt."]")
|
||||
for(var/datum/gang/G in ticker.mode.gangs)
|
||||
if(G != gang)
|
||||
G.message_gangtools("Enemy takeover attempt detected in [locname]: Estimated [time] minutes until our defeat.",1,1)
|
||||
|
||||
/obj/machinery/dominator/attack_hulk(mob/user)
|
||||
user.visible_message("<span class='danger'>[user] smashes [src].</span>",\
|
||||
"<span class='danger'>You punch [src].</span>",\
|
||||
"<span class='italics'>You hear metal being slammed.</span>")
|
||||
take_damage(5)
|
||||
|
||||
/obj/machinery/dominator/attacked_by(obj/item/I, mob/living/user)
|
||||
add_fingerprint(user)
|
||||
..()
|
||||
|
||||
|
||||
@@ -0,0 +1,317 @@
|
||||
//gang.dm
|
||||
//Gang War Game Mode
|
||||
|
||||
var/list/gang_name_pool = list("Clandestine", "Prima", "Zero-G", "Max", "Blasto", "Waffle", "North", "Omni", "Newton", "Cyber", "Donk", "Gene", "Gib", "Tunnel", "Diablo", "Psyke", "Osiron", "Sirius", "Sleeping Carp")
|
||||
var/list/gang_colors_pool = list("red","orange","yellow","green","blue","purple")
|
||||
|
||||
/datum/game_mode
|
||||
var/list/datum/gang/gangs = list()
|
||||
var/datum/gang_points/gang_points
|
||||
|
||||
/proc/is_gangster(var/mob/living/M)
|
||||
return istype(M) && M.mind && M.mind.gang_datum
|
||||
|
||||
/proc/is_in_gang(var/mob/living/M, var/gang_type)
|
||||
if(!is_gangster(M) || !gang_type)
|
||||
return 0
|
||||
var/datum/gang/G = M.mind.gang_datum
|
||||
if(G.name == gang_type)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/game_mode/gang
|
||||
name = "gang war"
|
||||
config_tag = "gang"
|
||||
antag_flag = ROLE_GANG
|
||||
restricted_jobs = list("Security Officer", "Warden", "Detective", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security", "Chief Engineer", "Research Director", "Chief Medical Officer")
|
||||
required_players = 20
|
||||
required_enemies = 2
|
||||
recommended_enemies = 2
|
||||
enemy_minimum_age = 14
|
||||
|
||||
///////////////////////////
|
||||
//Announces the game type//
|
||||
///////////////////////////
|
||||
/datum/game_mode/gang/announce()
|
||||
world << "<B>The current game mode is - Gang War!</B>"
|
||||
world << "<B>A violent turf war has erupted on the station!<BR>Gangsters - Take over the station by activating and defending a Dominator! <BR>Crew - The gangs will try to keep you on the station. Successfully evacuate the station to win!</B>"
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//Gets the round setup, cancelling if there's not enough players at the start//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/datum/game_mode/gang/pre_setup()
|
||||
if(config.protect_roles_from_antagonist)
|
||||
restricted_jobs += protected_jobs
|
||||
|
||||
if(config.protect_assistant_from_antagonist)
|
||||
restricted_jobs += "Assistant"
|
||||
|
||||
//Spawn more bosses depending on server population
|
||||
var/gangs_to_create = 2
|
||||
if(prob(num_players() * 2))
|
||||
gangs_to_create ++
|
||||
|
||||
for(var/i=1 to gangs_to_create)
|
||||
if(!antag_candidates.len)
|
||||
break
|
||||
|
||||
//Create the gang
|
||||
var/datum/gang/G = new()
|
||||
gangs += G
|
||||
|
||||
//Now assign a boss for the gang
|
||||
var/datum/mind/boss = pick(antag_candidates)
|
||||
antag_candidates -= boss
|
||||
G.bosses += boss
|
||||
boss.gang_datum = G
|
||||
boss.special_role = "[G.name] Gang Boss"
|
||||
boss.restricted_roles = restricted_jobs
|
||||
log_game("[boss.key] has been selected as the Boss for the [G.name] Gang")
|
||||
|
||||
if(gangs.len < 2) //Need at least two gangs
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
/datum/game_mode/gang/post_setup()
|
||||
spawn(rand(10,100))
|
||||
for(var/datum/gang/G in gangs)
|
||||
for(var/datum/mind/boss_mind in G.bosses)
|
||||
G.add_gang_hud(boss_mind)
|
||||
forge_gang_objectives(boss_mind)
|
||||
greet_gang(boss_mind)
|
||||
equip_gang(boss_mind.current,G)
|
||||
modePlayer += boss_mind
|
||||
..()
|
||||
|
||||
|
||||
/datum/game_mode/proc/forge_gang_objectives(datum/mind/boss_mind)
|
||||
var/datum/objective/rival_obj = new
|
||||
rival_obj.owner = boss_mind
|
||||
rival_obj.explanation_text = "Be the first gang to successfully takeover the station with a Dominator."
|
||||
boss_mind.objectives += rival_obj
|
||||
|
||||
/datum/game_mode/proc/greet_gang(datum/mind/boss_mind, you_are=1)
|
||||
var/obj_count = 1
|
||||
if (you_are)
|
||||
boss_mind.current << "<FONT size=3 color=red><B>You are the Boss of the [boss_mind.gang_datum.name] Gang!</B></FONT>"
|
||||
for(var/datum/objective/objective in boss_mind.objectives)
|
||||
boss_mind.current << "<B>Objective #[obj_count]</B>: [objective.explanation_text]"
|
||||
obj_count++
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//This equips the bosses with their gear, and makes the clown not clumsy//
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/datum/game_mode/proc/equip_gang(mob/living/carbon/human/mob, gang)
|
||||
if(!istype(mob))
|
||||
return
|
||||
|
||||
if (mob.mind)
|
||||
if (mob.mind.assigned_role == "Clown")
|
||||
mob << "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself."
|
||||
mob.dna.remove_mutation(CLOWNMUT)
|
||||
|
||||
var/obj/item/device/gangtool/gangtool = new(mob)
|
||||
var/obj/item/weapon/pen/gang/T = new(mob)
|
||||
var/obj/item/toy/crayon/spraycan/gang/SC = new(mob,gang)
|
||||
var/obj/item/clothing/glasses/hud/security/chameleon/C = new(mob,gang)
|
||||
|
||||
var/list/slots = list (
|
||||
"backpack" = slot_in_backpack,
|
||||
"left pocket" = slot_l_store,
|
||||
"right pocket" = slot_r_store,
|
||||
"left hand" = slot_l_hand,
|
||||
"right hand" = slot_r_hand,
|
||||
)
|
||||
|
||||
. = 0
|
||||
|
||||
var/where = mob.equip_in_one_of_slots(gangtool, slots)
|
||||
if (!where)
|
||||
mob << "Your Syndicate benefactors were unfortunately unable to get you a Gangtool."
|
||||
. += 1
|
||||
else
|
||||
gangtool.register_device(mob)
|
||||
mob << "The <b>Gangtool</b> in your [where] will allow you to purchase weapons and equipment, send messages to your gang, and recall the emergency shuttle from anywhere on the station."
|
||||
mob << "As the gang boss, you can also promote your gang members to <b>lieutenant</b>. Unlike regular gangsters, Lieutenants cannot be deconverted and are able to use recruitment pens and gangtools."
|
||||
|
||||
var/where2 = mob.equip_in_one_of_slots(T, slots)
|
||||
if (!where2)
|
||||
mob << "Your Syndicate benefactors were unfortunately unable to get you a recruitment pen to start."
|
||||
. += 1
|
||||
else
|
||||
mob << "The <b>recruitment pen</b> in your [where2] will help you get your gang started. Stab unsuspecting crew members with it to recruit them."
|
||||
|
||||
var/where3 = mob.equip_in_one_of_slots(SC, slots)
|
||||
if (!where3)
|
||||
mob << "Your Syndicate benefactors were unfortunately unable to get you a territory spraycan to start."
|
||||
. += 1
|
||||
else
|
||||
mob << "The <b>territory spraycan</b> in your [where3] can be used to claim areas of the station for your gang. The more territory your gang controls, the more influence you get. All gangsters can use these, so distribute them to grow your influence faster."
|
||||
|
||||
var/where4 = mob.equip_in_one_of_slots(C, slots)
|
||||
if (!where4)
|
||||
mob << "Your Syndicate benefactors were unfortunately unable to get you a chameleon security HUD."
|
||||
. += 1
|
||||
else
|
||||
mob << "The <b>chameleon security HUD</b> in your [where4] will help you keep track of who is mindshield-implanted, and unable to be recruited."
|
||||
mob.update_icons()
|
||||
return .
|
||||
|
||||
|
||||
///////////////////////////////////////////
|
||||
//Deals with converting players to a gang//
|
||||
///////////////////////////////////////////
|
||||
/datum/game_mode/proc/add_gangster(datum/mind/gangster_mind, datum/gang/G, check = 1)
|
||||
if(!G || (gangster_mind in get_all_gangsters()) || gangster_mind.enslaved_to)
|
||||
return 0
|
||||
if(check && isloyal(gangster_mind.current)) //Check to see if the potential gangster is implanted
|
||||
return 1
|
||||
G.gangsters += gangster_mind
|
||||
gangster_mind.gang_datum = G
|
||||
if(check)
|
||||
if(iscarbon(gangster_mind.current))
|
||||
var/mob/living/carbon/carbon_mob = gangster_mind.current
|
||||
carbon_mob.silent = max(carbon_mob.silent, 5)
|
||||
carbon_mob.flash_eyes(1, 1)
|
||||
gangster_mind.current.Stun(5)
|
||||
gangster_mind.current << "<FONT size=3 color=red><B>You are now a member of the [G.name] Gang!</B></FONT>"
|
||||
gangster_mind.current << "<font color='red'>Help your bosses take over the station by claiming territory with <b>special spraycans</b> only they can provide. Simply spray on any unclaimed area of the station.</font>"
|
||||
gangster_mind.current << "<font color='red'>Their ultimate objective is to take over the station with a Dominator machine.</font>"
|
||||
gangster_mind.current << "<font color='red'>You can identify your bosses by their <b>red \[G\] icon</b>.</font>"
|
||||
gangster_mind.current.attack_log += "\[[time_stamp()]\] <font color='red'>Has been converted to the [G.name] Gang!</font>"
|
||||
gangster_mind.special_role = "[G.name] Gangster"
|
||||
gangster_mind.store_memory("You are a member of the [G.name] Gang!")
|
||||
G.add_gang_hud(gangster_mind)
|
||||
if(jobban_isbanned(gangster_mind.current, ROLE_GANG))
|
||||
replace_jobbaned_player(gangster_mind.current, ROLE_GANG, ROLE_GANG)
|
||||
return 2
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//Deals with players reverting to neutral (Not a gangster anymore)//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
/datum/game_mode/proc/remove_gangster(datum/mind/gangster_mind, beingborged, silent, remove_bosses=0)
|
||||
var/datum/gang/gang = gangster_mind.gang_datum
|
||||
if(!gang)
|
||||
return 0
|
||||
|
||||
var/removed
|
||||
|
||||
for(var/datum/gang/G in gangs)
|
||||
if(gangster_mind in G.gangsters)
|
||||
G.gangsters -= gangster_mind
|
||||
removed = 1
|
||||
if(remove_bosses && (gangster_mind in G.bosses))
|
||||
G.bosses -= gangster_mind
|
||||
removed = 1
|
||||
|
||||
if(!removed)
|
||||
return 0
|
||||
|
||||
gangster_mind.special_role = null
|
||||
gangster_mind.gang_datum = null
|
||||
|
||||
if(silent < 2)
|
||||
gangster_mind.current.attack_log += "\[[time_stamp()]\] <font color='red'>Has reformed and defected from the [gang.name] Gang!</font>"
|
||||
|
||||
if(beingborged)
|
||||
if(!silent)
|
||||
gangster_mind.current.visible_message("The frame beeps contentedly from the MMI before initalizing it.")
|
||||
gangster_mind.current << "<FONT size=3 color=red><B>The frame's firmware detects and deletes your criminal behavior! You are no longer a gangster!</B></FONT>"
|
||||
message_admins("[key_name_admin(gangster_mind.current)] <A HREF='?_src_=holder;adminmoreinfo=\ref[gangster_mind.current]'>?</A> (<A HREF='?_src_=holder;adminplayerobservefollow=\ref[gangster_mind.current]'>FLW</A>) has been borged while being a member of the [gang.name] Gang. They are no longer a gangster.")
|
||||
else
|
||||
if(!silent)
|
||||
gangster_mind.current.Paralyse(5)
|
||||
gangster_mind.current.visible_message("<FONT size=3><B>[gangster_mind.current] looks like they've given up the life of crime!<B></font>")
|
||||
gangster_mind.current << "<FONT size=3 color=red><B>You have been reformed! You are no longer a gangster!</B><BR>You try as hard as you can, but you can't seem to recall any of the identities of your former gangsters...</FONT>"
|
||||
gangster_mind.memory = ""
|
||||
|
||||
gang.remove_gang_hud(gangster_mind)
|
||||
return 1
|
||||
|
||||
////////////////
|
||||
//Helper Procs//
|
||||
////////////////
|
||||
|
||||
/datum/game_mode/proc/get_all_gangsters()
|
||||
var/list/all_gangsters = list()
|
||||
all_gangsters += get_gangsters()
|
||||
all_gangsters += get_gang_bosses()
|
||||
return all_gangsters
|
||||
|
||||
/datum/game_mode/proc/get_gangsters()
|
||||
var/list/gangsters = list()
|
||||
for(var/datum/gang/G in gangs)
|
||||
gangsters += G.gangsters
|
||||
return gangsters
|
||||
|
||||
/datum/game_mode/proc/get_gang_bosses()
|
||||
var/list/gang_bosses = list()
|
||||
for(var/datum/gang/G in gangs)
|
||||
gang_bosses += G.bosses
|
||||
return gang_bosses
|
||||
|
||||
/proc/determine_domination_time(var/datum/gang/G)
|
||||
return max(180,900 - (round((G.territory.len/start_state.num_territories)*100, 1) * 12))
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//Announces the end of the game with all relavent information stated//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
/datum/game_mode/proc/auto_declare_completion_gang(datum/gang/winner)
|
||||
if(gangs.len)
|
||||
if(!winner)
|
||||
world << "<span class='redtext'>The station was [station_was_nuked ? "destroyed!" : "evacuated before a gang could claim it! The station wins!"]</span><br>"
|
||||
feedback_set_details("round_end_result","loss - gangs failed takeover")
|
||||
else
|
||||
world << "<span class='redtext'>The [winner.name] Gang successfully performed a hostile takeover of the station!</span><br>"
|
||||
feedback_set_details("round_end_result","win - gang domination complete")
|
||||
|
||||
for(var/datum/gang/G in gangs)
|
||||
var/text = "<b>The [G.name] Gang was [winner==G ? "<span class='greenannounce'>victorious</span>" : "<span class='boldannounce'>defeated</span>"] with [round((G.territory.len/start_state.num_territories)*100, 1)]% control of the station!</b>"
|
||||
text += "<br>The [G.name] Gang Bosses were:"
|
||||
for(var/datum/mind/boss in G.bosses)
|
||||
text += printplayer(boss, 1)
|
||||
text += "<br>The [G.name] Gangsters were:"
|
||||
for(var/datum/mind/gangster in G.gangsters)
|
||||
text += printplayer(gangster, 1)
|
||||
text += "<br>"
|
||||
world << text
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
//Handles influence, territories, and the victory checks//
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
/datum/gang_points
|
||||
var/next_point_interval = 1800
|
||||
var/next_point_time
|
||||
|
||||
/datum/gang_points/New()
|
||||
next_point_time = world.time + next_point_interval
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/datum/gang_points/process(seconds)
|
||||
var/list/winners = list() //stores the winners if there are any
|
||||
|
||||
for(var/datum/gang/G in ticker.mode.gangs)
|
||||
if(world.time > next_point_time)
|
||||
G.income()
|
||||
|
||||
if(G.is_dominating)
|
||||
if(G.domination_time_remaining() < 0)
|
||||
winners += G
|
||||
|
||||
if(world.time > next_point_time)
|
||||
next_point_time = world.time + next_point_interval
|
||||
|
||||
if(winners.len)
|
||||
if(winners.len > 1) //Edge Case: If more than one dominator complete at the same time
|
||||
for(var/datum/gang/G in winners)
|
||||
G.domination(0.5)
|
||||
priority_announce("Multiple station takeover attempts have made simultaneously. Conflicting takeover attempts appears to have restarted.","Network Alert")
|
||||
else
|
||||
ticker.mode.explosion_in_progress = 1
|
||||
ticker.station_explosion_cinematic(1)
|
||||
ticker.mode.explosion_in_progress = 0
|
||||
ticker.force_ending = pick(winners)
|
||||
@@ -0,0 +1,216 @@
|
||||
//gang_datum.dm
|
||||
//Datum-based gangs
|
||||
|
||||
/datum/gang
|
||||
var/name = "ERROR"
|
||||
var/color = "white"
|
||||
var/color_hex = "#FFFFFF"
|
||||
var/list/datum/mind/gangsters = list() //gang B Members
|
||||
var/list/datum/mind/bosses = list() //gang A Bosses
|
||||
var/list/obj/item/device/gangtool/gangtools = list()
|
||||
var/style
|
||||
var/fighting_style = "normal"
|
||||
var/list/territory = list()
|
||||
var/list/territory_new = list()
|
||||
var/list/territory_lost = list()
|
||||
var/dom_attempts = 2
|
||||
var/points = 15
|
||||
var/datum/atom_hud/antag/ganghud
|
||||
|
||||
var/domination_timer
|
||||
var/is_dominating
|
||||
|
||||
/datum/gang/New(loc,gangname)
|
||||
if(!gang_colors_pool.len)
|
||||
message_admins("WARNING: Maximum number of gangs have been exceeded!")
|
||||
throw EXCEPTION("Maximum number of gangs has been exceeded")
|
||||
return
|
||||
else
|
||||
color = pick(gang_colors_pool)
|
||||
gang_colors_pool -= color
|
||||
switch(color)
|
||||
if("red")
|
||||
color_hex = "#DA0000"
|
||||
if("orange")
|
||||
color_hex = "#FF9300"
|
||||
if("yellow")
|
||||
color_hex = "#FFF200"
|
||||
if("green")
|
||||
color_hex = "#A8E61D"
|
||||
if("blue")
|
||||
color_hex = "#00B7EF"
|
||||
if("purple")
|
||||
color_hex = "#DA00FF"
|
||||
|
||||
name = (gangname ? gangname : pick(gang_name_pool))
|
||||
gang_name_pool -= name
|
||||
|
||||
ganghud = new()
|
||||
log_game("The [name] Gang has been created. Their gang color is [color].")
|
||||
|
||||
/datum/gang/proc/add_gang_hud(datum/mind/recruit_mind)
|
||||
ganghud.join_hud(recruit_mind.current)
|
||||
ticker.mode.set_antag_hud(recruit_mind.current, ((recruit_mind in bosses) ? "gang_boss" : "gangster"))
|
||||
|
||||
/datum/gang/proc/remove_gang_hud(datum/mind/defector_mind)
|
||||
ganghud.leave_hud(defector_mind.current)
|
||||
ticker.mode.set_antag_hud(defector_mind.current, null)
|
||||
|
||||
/datum/gang/proc/domination(modifier=1)
|
||||
set_domination_time(determine_domination_time(src) * modifier)
|
||||
is_dominating = TRUE
|
||||
set_security_level("delta")
|
||||
SSshuttle.emergencyNoEscape = 1
|
||||
|
||||
/datum/gang/proc/set_domination_time(d)
|
||||
domination_timer = world.time + (10 * d)
|
||||
|
||||
/datum/gang/proc/domination_time_remaining()
|
||||
var/diff = domination_timer - world.time
|
||||
return diff / 10
|
||||
//////////////////////////////////////////// OUTFITS
|
||||
|
||||
|
||||
//Used by recallers when purchasing a gang outfit. First time a gang outfit is purchased the buyer decides a gang style which is stored so gang outfits are uniform
|
||||
/datum/gang/proc/gang_outfit(mob/living/carbon/user,obj/item/device/gangtool/gangtool)
|
||||
if(!user || !gangtool)
|
||||
return 0
|
||||
if(!gangtool.can_use(user))
|
||||
return 0
|
||||
|
||||
var/gang_style_list = list("Gang Colors","Black Suits","White Suits","Leather Jackets","Leather Overcoats","Puffer Jackets","Military Jackets","Tactical Turtlenecks","Soviet Uniforms")
|
||||
if(!style && (user.mind in ticker.mode.get_gang_bosses())) //Only the boss gets to pick a style
|
||||
style = input("Pick an outfit style.", "Pick Style") as null|anything in gang_style_list
|
||||
|
||||
if(gangtool.can_use(user) && (gangtool.outfits >= 1))
|
||||
var/outfit_path
|
||||
switch(style)
|
||||
if(null || "Gang Colors")
|
||||
outfit_path = text2path("/obj/item/clothing/under/color/[color]")
|
||||
if("Black Suits")
|
||||
outfit_path = /obj/item/clothing/under/suit_jacket/charcoal
|
||||
if("White Suits")
|
||||
outfit_path = /obj/item/clothing/under/suit_jacket/white
|
||||
if("Puffer Jackets")
|
||||
outfit_path = /obj/item/clothing/suit/jacket/puffer
|
||||
if("Leather Jackets")
|
||||
outfit_path = /obj/item/clothing/suit/jacket/leather
|
||||
if("Leather Overcoats")
|
||||
outfit_path = /obj/item/clothing/suit/jacket/leather/overcoat
|
||||
if("Military Jackets")
|
||||
outfit_path = /obj/item/clothing/suit/jacket/miljacket
|
||||
if("Soviet Uniforms")
|
||||
outfit_path = /obj/item/clothing/under/soviet
|
||||
if("Tactical Turtlenecks")
|
||||
outfit_path = /obj/item/clothing/under/syndicate
|
||||
|
||||
if(outfit_path)
|
||||
var/obj/item/clothing/outfit = new outfit_path(user.loc)
|
||||
outfit.armor = list(melee = 20, bullet = 30, laser = 10, energy = 10, bomb = 20, bio = 0, rad = 0)
|
||||
outfit.desc += " Tailored for the [name] Gang to offer the wearer moderate protection against ballistics and physical trauma."
|
||||
outfit.gang = src
|
||||
user.put_in_hands(outfit)
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
//////////////////////////////////////////// MESSAGING
|
||||
|
||||
|
||||
/datum/gang/proc/message_gangtools(message,beep=1,warning)
|
||||
if(!gangtools.len || !message)
|
||||
return
|
||||
for(var/obj/item/device/gangtool/tool in gangtools)
|
||||
var/mob/living/mob = get(tool.loc,/mob/living)
|
||||
if(mob && mob.mind && mob.stat == CONSCIOUS)
|
||||
if(mob.mind.gang_datum == src)
|
||||
mob << "<span class='[warning ? "warning" : "notice"]'>\icon[tool] [message]</span>"
|
||||
return
|
||||
|
||||
|
||||
//////////////////////////////////////////// INCOME
|
||||
|
||||
|
||||
/datum/gang/proc/income()
|
||||
if(!bosses.len)
|
||||
return
|
||||
|
||||
var/added_names = ""
|
||||
var/lost_names = ""
|
||||
|
||||
//Re-add territories that were reclaimed, so if they got tagged over, they can still earn income if they tag it back before the next status report
|
||||
var/list/reclaimed_territories = territory_new & territory_lost
|
||||
territory |= reclaimed_territories
|
||||
territory_new -= reclaimed_territories
|
||||
territory_lost -= reclaimed_territories
|
||||
|
||||
//Process lost territories
|
||||
for(var/area in territory_lost)
|
||||
if(lost_names != "")
|
||||
lost_names += ", "
|
||||
lost_names += "[territory_lost[area]]"
|
||||
territory -= area
|
||||
|
||||
//Count uniformed gangsters
|
||||
var/uniformed = 0
|
||||
for(var/datum/mind/gangmind in (gangsters|bosses))
|
||||
if(ishuman(gangmind.current))
|
||||
var/mob/living/carbon/human/gangster = gangmind.current
|
||||
//Gangster must be alive and on station
|
||||
if((gangster.stat == DEAD) || (gangster.z > ZLEVEL_STATION))
|
||||
continue
|
||||
|
||||
var/obj/item/clothing/outfit
|
||||
var/obj/item/clothing/gang_outfit
|
||||
if(gangster.w_uniform)
|
||||
outfit = gangster.w_uniform
|
||||
if(outfit.gang == src)
|
||||
gang_outfit = outfit
|
||||
if(gangster.wear_suit)
|
||||
outfit = gangster.wear_suit
|
||||
if(outfit.gang == src)
|
||||
gang_outfit = outfit
|
||||
|
||||
if(gang_outfit)
|
||||
gangster << "<span class='notice'>The [src] Gang's influence grows as you wear [gang_outfit].</span>"
|
||||
uniformed ++
|
||||
|
||||
//Calculate and report influence growth
|
||||
var/message = "<b>[src] Gang Status Report:</b><BR>*---------*<br>"
|
||||
if(is_dominating)
|
||||
var/seconds_remaining = domination_time_remaining()
|
||||
var/new_time = max(180, seconds_remaining - (uniformed * 4) - (territory.len * 2))
|
||||
if(new_time < seconds_remaining)
|
||||
message += "Takeover shortened by [seconds_remaining - new_time] seconds for defending [territory.len] territories and [uniformed] uniformed gangsters.<BR>"
|
||||
set_domination_time(new_time)
|
||||
message += "<b>[seconds_remaining] seconds remain</b> in hostile takeover.<BR>"
|
||||
else
|
||||
var/points_new = min(999,points + 15 + (uniformed * 2) + territory.len)
|
||||
if(points_new != points)
|
||||
message += "Gang influence has increased by [points_new - points] for defending [territory.len] territories and [uniformed] uniformed gangsters.<BR>"
|
||||
points = points_new
|
||||
message += "Your gang now has <b>[points] influence</b>.<BR>"
|
||||
|
||||
//Process new territories
|
||||
for(var/area in territory_new)
|
||||
if(added_names != "")
|
||||
added_names += ", "
|
||||
added_names += "[territory_new[area]]"
|
||||
territory += area
|
||||
|
||||
//Report territory changes
|
||||
message += "<b>[territory_new.len] new territories:</b><br><i>[added_names]</i><br>"
|
||||
message += "<b>[territory_lost.len] territories lost:</b><br><i>[lost_names]</i><br>"
|
||||
|
||||
//Clear the lists
|
||||
territory_new = list()
|
||||
territory_lost = list()
|
||||
|
||||
var/control = round((territory.len/start_state.num_territories)*100, 1)
|
||||
message += "Your gang now has <b>[control]% control</b> of the station.<BR>*---------*"
|
||||
message_gangtools(message)
|
||||
|
||||
//Increase outfit stock
|
||||
for(var/obj/item/device/gangtool/tool in gangtools)
|
||||
tool.outfits = min(tool.outfits+1,5)
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Gang Boss Pens
|
||||
*/
|
||||
/obj/item/weapon/pen/gang
|
||||
origin_tech = "materials=2;syndicate=3"
|
||||
var/cooldown
|
||||
var/last_used = 0
|
||||
var/charges = 1
|
||||
|
||||
/obj/item/weapon/pen/gang/New()
|
||||
..()
|
||||
last_used = world.time
|
||||
|
||||
/obj/item/weapon/pen/gang/attack(mob/living/M, mob/user)
|
||||
if(!istype(M))
|
||||
return
|
||||
if(ishuman(M) && ishuman(user) && M.stat != DEAD)
|
||||
if(user.mind && (user.mind in ticker.mode.get_gang_bosses()))
|
||||
if(..(M,user,1))
|
||||
if(cooldown)
|
||||
user << "<span class='warning'>[src] needs more time to recharge before it can be used.</span>"
|
||||
return
|
||||
if(M.client)
|
||||
M.mind_initialize() //give them a mind datum if they don't have one.
|
||||
var/datum/gang/G = user.mind.gang_datum
|
||||
var/recruitable = ticker.mode.add_gangster(M.mind,G)
|
||||
switch(recruitable)
|
||||
if(2)
|
||||
M.Paralyse(5)
|
||||
cooldown(G)
|
||||
if(1)
|
||||
user << "<span class='warning'>This mind is resistant to recruitment!</span>"
|
||||
else
|
||||
user << "<span class='warning'>This mind has already been recruited into a gang!</span>"
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/item/weapon/pen/gang/proc/cooldown(datum/gang/gang)
|
||||
var/cooldown_time = 600+(600*gang.bosses.len) // 1recruiter=2mins, 2recruiters=3mins, 3recruiters=4mins
|
||||
|
||||
cooldown = 1
|
||||
icon_state = "pen_blink"
|
||||
|
||||
var/time_passed = world.time - last_used
|
||||
var/time
|
||||
for(time=time_passed, time>=cooldown_time, time-=cooldown_time) //get 1 charge every cooldown interval
|
||||
charges++
|
||||
|
||||
charges = max(0,charges-1)
|
||||
|
||||
last_used = world.time - time
|
||||
|
||||
if(charges)
|
||||
cooldown_time = 50
|
||||
spawn(cooldown_time)
|
||||
cooldown = 0
|
||||
icon_state = "pen"
|
||||
var/mob/M = get(src, /mob)
|
||||
M << "<span class='notice'>\icon[src] [src][(src.loc == M)?(""):(" in your [src.loc]")] vibrates softly. It is ready to be used again.</span>"
|
||||
|
||||
|
||||
//////////////
|
||||
// IMPLANTS //
|
||||
//////////////
|
||||
|
||||
/obj/item/weapon/implant/gang
|
||||
name = "gang implant"
|
||||
desc = "Makes you a gangster or such."
|
||||
activated = 0
|
||||
origin_tech = "materials=2;biotech=4;programming=4;syndicate=3"
|
||||
var/datum/gang/gang
|
||||
|
||||
/obj/item/weapon/implant/gang/New(loc,var/setgang)
|
||||
..()
|
||||
gang = setgang
|
||||
|
||||
/obj/item/weapon/implant/gang/get_data()
|
||||
var/dat = {"<b>Implant Specifications:</b><BR>
|
||||
<b>Name:</b> Criminal brainwash implant<BR>
|
||||
<b>Life:</b> A few seconds after injection.<BR>
|
||||
<b>Important Notes:</b> Illegal<BR>
|
||||
<HR>
|
||||
<b>Implant Details:</b><BR>
|
||||
<b>Function:</b> Contains a small pod of nanobots that change the host's brain to be loyal to a certain organization.<BR>
|
||||
<b>Special Features:</b> This device will also emit a small EMP pulse, destroying any other implants within the host's brain.<BR>
|
||||
<b>Integrity:</b> Implant's EMP function will destroy itself in the process."}
|
||||
return dat
|
||||
|
||||
/obj/item/weapon/implant/gang/implant(mob/target)
|
||||
if(..())
|
||||
for(var/obj/item/weapon/implant/I in target)
|
||||
if(I != src)
|
||||
qdel(I)
|
||||
|
||||
if(!target.mind || target.stat == DEAD)
|
||||
return 0
|
||||
|
||||
var/success
|
||||
if(target.mind in ticker.mode.get_gangsters())
|
||||
if(ticker.mode.remove_gangster(target.mind,0,1))
|
||||
success = 1 //Was not a gang boss, convert as usual
|
||||
else
|
||||
success = 1
|
||||
|
||||
if(ishuman(target))
|
||||
if(!success)
|
||||
target.visible_message("<span class='warning'>[target] seems to resist the implant!</span>", "<span class='warning'>You feel the influence of your enemies try to invade your mind!</span>")
|
||||
|
||||
qdel(src)
|
||||
return -1
|
||||
|
||||
/obj/item/weapon/implanter/gang
|
||||
name = "implanter (gang)"
|
||||
|
||||
/obj/item/weapon/implanter/gang/New(loc, var/gang)
|
||||
if(!gang)
|
||||
qdel(src)
|
||||
return
|
||||
imp = new /obj/item/weapon/implant/gang(src,gang)
|
||||
..()
|
||||
@@ -0,0 +1,442 @@
|
||||
//gangtool device
|
||||
/obj/item/device/gangtool
|
||||
name = "suspicious device"
|
||||
desc = "A strange device of sorts. Hard to really make out what it actually does if you don't know how to operate it."
|
||||
icon_state = "gangtool-white"
|
||||
item_state = "walkietalkie"
|
||||
throwforce = 0
|
||||
w_class = 1
|
||||
throw_speed = 3
|
||||
throw_range = 7
|
||||
flags = CONDUCT
|
||||
origin_tech = "programming=5;bluespace=2;syndicate=5"
|
||||
var/datum/gang/gang //Which gang uses this?
|
||||
var/recalling = 0
|
||||
var/outfits = 3
|
||||
var/free_pen = 0
|
||||
var/promotable = 0
|
||||
|
||||
/obj/item/device/gangtool/New() //Initialize supply point income if it hasn't already been started
|
||||
if(!ticker.mode.gang_points)
|
||||
ticker.mode.gang_points = new /datum/gang_points(ticker.mode)
|
||||
|
||||
/obj/item/device/gangtool/attack_self(mob/user)
|
||||
if (!can_use(user))
|
||||
return
|
||||
|
||||
var/dat
|
||||
if(!gang)
|
||||
dat += "This device is not registered.<br><br>"
|
||||
if(user.mind in ticker.mode.get_gang_bosses())
|
||||
if(promotable && user.mind.gang_datum.bosses.len < 3)
|
||||
dat += "Give this device to another member of your organization to use to promote them to Lieutenant.<br><br>"
|
||||
dat += "If this is meant as a spare device for yourself:<br>"
|
||||
dat += "<a href='?src=\ref[src];register=1'>Register Device as Spare</a><br>"
|
||||
else if (promotable)
|
||||
if(user.mind.gang_datum.bosses.len < 3)
|
||||
dat += "You have been selected for a promotion!<br>"
|
||||
dat += "<a href='?src=\ref[src];register=1'>Accept Promotion</a><br>"
|
||||
else
|
||||
dat += "No promotions available: All positions filled.<br>"
|
||||
else
|
||||
dat += "This device is not authorized to promote.<br>"
|
||||
else
|
||||
if(gang.is_dominating)
|
||||
dat += "<center><font color='red'>Takeover In Progress:<br><B>[gang.domination_time_remaining()] seconds remain</B></font></center>"
|
||||
|
||||
var/isboss = (user.mind == gang.bosses[1])
|
||||
var/points = gang.points
|
||||
dat += "Registration: <B>[gang.name] Gang [isboss ? "Boss" : "Lieutenant"]</B><br>"
|
||||
dat += "Organization Size: <B>[gang.gangsters.len + gang.bosses.len]</B> | Station Control: <B>[round((gang.territory.len/start_state.num_territories)*100, 1)]%</B><br>"
|
||||
dat += "Gang Influence: <B>[points]</B><br>"
|
||||
dat += "Time until Influence grows: <B>[(points >= 999) ? ("--:--") : (time2text(ticker.mode.gang_points.next_point_time - world.time, "mm:ss"))]</B><br>"
|
||||
dat += "<hr>"
|
||||
dat += "<B>Gangtool Functions:</B><br>"
|
||||
|
||||
dat += "<a href='?src=\ref[src];choice=ping'>Send Message to Gang</a><br>"
|
||||
if(outfits > 0)
|
||||
dat += "<a href='?src=\ref[src];choice=outfit'>Create Armored Gang Outfit</a><br>"
|
||||
else
|
||||
dat += "<b>Create Gang Outfit</b> (Restocking)<br>"
|
||||
if(isboss)
|
||||
dat += "<a href='?src=\ref[src];choice=recall'>Recall Emergency Shuttle</a><br>"
|
||||
|
||||
dat += "<br>"
|
||||
dat += "<B>Purchase Weapons:</B><br>"
|
||||
|
||||
/////////////////
|
||||
// NORMAL GANG //
|
||||
/////////////////
|
||||
|
||||
if(gang.fighting_style == "normal")
|
||||
dat += "(10 Influence) "
|
||||
if(points >= 10)
|
||||
dat += "<a href='?src=\ref[src];purchase=switchblade'>Switchblade</a><br>"
|
||||
else
|
||||
dat += "Switchblade<br>"
|
||||
|
||||
dat += "(25 Influence) "
|
||||
if(points >= 25)
|
||||
dat += "<a href='?src=\ref[src];purchase=pistol'>10mm Pistol</a><br>"
|
||||
else
|
||||
dat += "10mm Pistol<br>"
|
||||
|
||||
dat += " ↳(10 Influence) "
|
||||
if(points >= 10)
|
||||
dat += "<a href='?src=\ref[src];purchase=10mmammo'>10mm Ammo</a><br>"
|
||||
else
|
||||
dat += "10mm Ammo<br>"
|
||||
|
||||
dat += "(60 Influence) "
|
||||
if(points >= 60)
|
||||
dat += "<a href='?src=\ref[src];purchase=uzi'>Uzi SMG</a><br>"
|
||||
else
|
||||
dat += "Uzi SMG<br>"
|
||||
|
||||
dat += " ↳(40 Influence) "
|
||||
if(points >= 40)
|
||||
dat += "<a href='?src=\ref[src];purchase=9mmammo'>Uzi Ammo</a><br>"
|
||||
else
|
||||
dat += "Uzi Ammo<br>"
|
||||
|
||||
dat += "(1 Influence) "
|
||||
if(points >=1)
|
||||
dat += "<a href='?src=\ref[src];purchase=necklace'>Dope Necklace</a><br>"
|
||||
else
|
||||
dat += "Dope Necklace<br>"
|
||||
|
||||
dat += "<br>"
|
||||
|
||||
////////////////////////
|
||||
// STANDARD EQUIPMENT //
|
||||
////////////////////////
|
||||
|
||||
dat += "<B>Purchase Equipment:</B><br>"
|
||||
|
||||
dat += "(5 Influence) "
|
||||
if(points >= 5)
|
||||
dat += "<a href='?src=\ref[src];purchase=spraycan'>Territory Spraycan</a><br>"
|
||||
else
|
||||
dat += "Territory Spraycan<br>"
|
||||
|
||||
dat += "(10 Influence) "
|
||||
if(points >= 10)
|
||||
dat += "<a href='?src=\ref[src];purchase=C4'>C4 Explosive</a><br>"
|
||||
else
|
||||
dat += "C4 Explosive<br>"
|
||||
|
||||
dat += "(15 Influence) "
|
||||
if(points >= 15)
|
||||
dat += "<a href='?src=\ref[src];purchase=implant'>Implant Breaker</a><br>"
|
||||
else
|
||||
dat += "Implant Breaker<br>"
|
||||
|
||||
if(free_pen)
|
||||
dat += "(GET ONE FREE) "
|
||||
else
|
||||
dat += "(50 Influence) "
|
||||
if((points >= 50)||free_pen)
|
||||
dat += "<a href='?src=\ref[src];purchase=pen'>Recruitment Pen</a><br>"
|
||||
else
|
||||
dat += "Recruitment Pen<br>"
|
||||
|
||||
var/gangtooltext = "Spare Gangtool"
|
||||
if(isboss && gang.bosses.len < 3)
|
||||
gangtooltext = "Promote a Gangster"
|
||||
dat += "(10 Influence) "
|
||||
if(points >= 10)
|
||||
dat += "<a href='?src=\ref[src];purchase=gangtool'>[gangtooltext]</a><br>"
|
||||
else
|
||||
dat += "[gangtooltext]<br>"
|
||||
|
||||
if(!gang.dom_attempts)
|
||||
dat += "(Out of stock) Station Dominator<br>"
|
||||
else
|
||||
dat += "(30 Influence) "
|
||||
if(points >= 30)
|
||||
dat += "<a href='?src=\ref[src];purchase=dominator'><b>Station Dominator</b></a><br>"
|
||||
else
|
||||
dat += "<b>Station Dominator</b><br>"
|
||||
dat += "<i>(Estimated Takeover Time: [round(determine_domination_time(gang)/60,0.1)] minutes)</i><br>"
|
||||
|
||||
dat += "<br>"
|
||||
dat += "<a href='?src=\ref[src];choice=refresh'>Refresh</a><br>"
|
||||
|
||||
var/datum/browser/popup = new(user, "gangtool", "Welcome to GangTool v3.2", 340, 625)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
|
||||
|
||||
/obj/item/device/gangtool/Topic(href, href_list)
|
||||
if(!can_use(usr))
|
||||
return
|
||||
|
||||
add_fingerprint(usr)
|
||||
|
||||
if(href_list["register"])
|
||||
register_device(usr)
|
||||
|
||||
else if(!gang) //Gangtool must be registered before you can use the functions below
|
||||
return
|
||||
|
||||
if(href_list["purchase"])
|
||||
var/pointcost
|
||||
var/item_type
|
||||
switch(href_list["purchase"])
|
||||
if("spraycan")
|
||||
if(gang.points >= 5)
|
||||
item_type = /obj/item/toy/crayon/spraycan/gang
|
||||
pointcost = 5
|
||||
if("switchblade")
|
||||
if(gang.points >= 10)
|
||||
item_type = /obj/item/weapon/switchblade
|
||||
pointcost = 10
|
||||
if("necklace")
|
||||
if(gang.points >=1)
|
||||
item_type = /obj/item/clothing/tie/dope_necklace
|
||||
pointcost = 1
|
||||
if("pistol")
|
||||
if(gang.points >= 25)
|
||||
item_type = /obj/item/weapon/gun/projectile/automatic/pistol
|
||||
pointcost = 25
|
||||
if("10mmammo")
|
||||
if(gang.points >= 10)
|
||||
item_type = /obj/item/ammo_box/magazine/m10mm
|
||||
pointcost = 10
|
||||
if("uzi")
|
||||
if(gang.points >= 60)
|
||||
item_type = /obj/item/weapon/gun/projectile/automatic/mini_uzi
|
||||
pointcost = 60
|
||||
if("9mmammo")
|
||||
if(gang.points >= 40)
|
||||
item_type = /obj/item/ammo_box/magazine/uzim9mm
|
||||
pointcost = 40
|
||||
if("scroll")
|
||||
if(gang.points >= 30)
|
||||
item_type = /obj/item/weapon/sleeping_carp_scroll
|
||||
usr << "<span class='notice'>Anyone who reads the <b>sleeping carp scroll</b> will learn secrets of the sleeping carp martial arts style.</span>"
|
||||
pointcost = 30
|
||||
if("wrestlingbelt")
|
||||
if(gang.points >= 20)
|
||||
item_type = /obj/item/weapon/storage/belt/champion/wrestling
|
||||
usr << "<span class='notice'>Anyone wearing the <b>wresting belt</b> will know how to be effective with wrestling.</span>"
|
||||
pointcost = 20
|
||||
if("bostaff")
|
||||
if(gang.points >= 10)
|
||||
item_type = /obj/item/weapon/twohanded/bostaff
|
||||
pointcost = 10
|
||||
if("C4")
|
||||
if(gang.points >= 10)
|
||||
item_type = /obj/item/weapon/grenade/plastic/c4
|
||||
pointcost = 10
|
||||
if("pen")
|
||||
if((gang.points >= 50) || free_pen)
|
||||
item_type = /obj/item/weapon/pen/gang
|
||||
usr << "<span class='notice'>More <b>recruitment pens</b> will allow you to recruit gangsters faster. Only gang leaders can recruit with pens.</span>"
|
||||
if(free_pen)
|
||||
free_pen = 0
|
||||
else
|
||||
pointcost = 50
|
||||
if("implant")
|
||||
if(gang.points >= 15)
|
||||
item_type = /obj/item/weapon/implanter/gang
|
||||
usr << "<span class='notice'>The <b>implant breaker</b> is a single-use device that destroys all implants within the target before trying to recruit them to your gang. Also works on enemy gangsters.</span>"
|
||||
pointcost = 15
|
||||
if("gangtool")
|
||||
if(gang.points >= 10)
|
||||
if(usr.mind == gang.bosses[1])
|
||||
item_type = /obj/item/device/gangtool/spare/lt
|
||||
if(gang.bosses.len < 3)
|
||||
usr << "<span class='notice'><b>Gangtools</b> allow you to promote a gangster to be your Lieutenant, enabling them to recruit and purchase items like you. Simply have them register the gangtool. You may promote up to [3-gang.bosses.len] more Lieutenants</span>"
|
||||
else
|
||||
item_type = /obj/item/device/gangtool/spare/
|
||||
pointcost = 10
|
||||
if("dominator")
|
||||
if(!gang.dom_attempts)
|
||||
return
|
||||
|
||||
var/area/usrarea = get_area(usr.loc)
|
||||
var/usrturf = get_turf(usr.loc)
|
||||
if(initial(usrarea.name) == "Space" || istype(usrturf,/turf/open/space) || usr.z != 1)
|
||||
usr << "<span class='warning'>You can only use this on the station!</span>"
|
||||
return
|
||||
|
||||
for(var/obj/obj in usrturf)
|
||||
if(obj.density)
|
||||
usr << "<span class='warning'>There's not enough room here!</span>"
|
||||
return
|
||||
|
||||
if(usrarea.type in gang.territory|gang.territory_new)
|
||||
if(gang.points >= 30)
|
||||
item_type = /obj/machinery/dominator
|
||||
usr << "<span class='notice'>The <b>dominator</b> will secure your gang's dominance over the station. Turn it on when you are ready to defend it.</span>"
|
||||
pointcost = 30
|
||||
else
|
||||
usr << "<span class='warning'>The <b>dominator</b> can be spawned only on territory controlled by your gang!</span>"
|
||||
return
|
||||
|
||||
if(item_type)
|
||||
gang.points -= pointcost
|
||||
if(ispath(item_type))
|
||||
var/obj/purchased = new item_type(get_turf(usr),gang)
|
||||
var/mob/living/carbon/human/H = usr
|
||||
H.put_in_hands(purchased)
|
||||
if(pointcost)
|
||||
gang.message_gangtools("A [href_list["purchase"]] was purchased by [usr.real_name] for [pointcost] Influence.")
|
||||
log_game("A [href_list["purchase"]] was purchased by [key_name(usr)] ([gang.name] Gang) for [pointcost] Influence.")
|
||||
|
||||
else
|
||||
usr << "<span class='warning'>Not enough influence.</span>"
|
||||
|
||||
else if(href_list["choice"])
|
||||
switch(href_list["choice"])
|
||||
if("recall")
|
||||
if(usr.mind == gang.bosses[1])
|
||||
recall(usr)
|
||||
if("outfit")
|
||||
if(outfits > 0)
|
||||
if(gang.gang_outfit(usr,src))
|
||||
usr << "<span class='notice'><b>Gang Outfits</b> can act as armor with moderate protection against ballistic and melee attacks. Every gangster wearing one will also help grow your gang's influence.</span>"
|
||||
outfits -= 1
|
||||
if("ping")
|
||||
ping_gang(usr)
|
||||
attack_self(usr)
|
||||
|
||||
|
||||
/obj/item/device/gangtool/proc/ping_gang(mob/user)
|
||||
if(!user)
|
||||
return
|
||||
var/message = stripped_input(user,"Discreetly send a gang-wide message.","Send Message") as null|text
|
||||
if(!message || !can_use(user))
|
||||
return
|
||||
if(user.z > 2)
|
||||
user << "<span class='info'>\icon[src]Error: Station out of range.</span>"
|
||||
return
|
||||
var/list/members = list()
|
||||
members += gang.gangsters
|
||||
members += gang.bosses
|
||||
if(members.len)
|
||||
var/gang_rank = gang.bosses.Find(user.mind)
|
||||
switch(gang_rank)
|
||||
if(1)
|
||||
gang_rank = "Gang Boss"
|
||||
if(2)
|
||||
gang_rank = "1st Lieutenant"
|
||||
if(3)
|
||||
gang_rank = "2nd Lieutenant"
|
||||
if(4)
|
||||
gang_rank = "3rd Lieutenant"
|
||||
else
|
||||
gang_rank = "[gang_rank - 1]th Lieutenant"
|
||||
var/ping = "<span class='danger'><B><i>[gang.name] [gang_rank]</i>: [message]</B></span>"
|
||||
for(var/datum/mind/ganger in members)
|
||||
if(ganger.current && (ganger.current.z <= 2) && (ganger.current.stat == CONSCIOUS))
|
||||
ganger.current << ping
|
||||
for(var/mob/M in dead_mob_list)
|
||||
var/link = FOLLOW_LINK(M, user)
|
||||
M << "[link] [ping]"
|
||||
log_game("[key_name(user)] Messaged [gang.name] Gang: [message].")
|
||||
|
||||
|
||||
/obj/item/device/gangtool/proc/register_device(mob/user)
|
||||
if(gang) //It's already been registered!
|
||||
return
|
||||
if((promotable && (user.mind in ticker.mode.get_gangsters())) || (user.mind in ticker.mode.get_gang_bosses()))
|
||||
gang = user.mind.gang_datum
|
||||
gang.gangtools += src
|
||||
icon_state = "gangtool-[gang.color]"
|
||||
if(!(user.mind in gang.bosses))
|
||||
ticker.mode.remove_gangster(user.mind, 0, 2)
|
||||
gang.bosses += user.mind
|
||||
user.mind.gang_datum = gang
|
||||
user.mind.special_role = "[gang.name] Gang Lieutenant"
|
||||
gang.add_gang_hud(user.mind)
|
||||
log_game("[key_name(user)] has been promoted to Lieutenant in the [gang.name] Gang")
|
||||
free_pen = 1
|
||||
gang.message_gangtools("[user] has been promoted to Lieutenant.")
|
||||
user << "<FONT size=3 color=red><B>You have been promoted to Lieutenant!</B></FONT>"
|
||||
ticker.mode.forge_gang_objectives(user.mind)
|
||||
ticker.mode.greet_gang(user.mind,0)
|
||||
user << "The <b>Gangtool</b> you registered will allow you to purchase weapons and equipment, and send messages to your gang."
|
||||
user << "Unlike regular gangsters, you may use <b>recruitment pens</b> to add recruits to your gang. Use them on unsuspecting crew members to recruit them. Don't forget to get your one free pen from the gangtool."
|
||||
else
|
||||
usr << "<span class='warning'>ACCESS DENIED: Unauthorized user.</span>"
|
||||
|
||||
/obj/item/device/gangtool/proc/recall(mob/user)
|
||||
if(!can_use(user))
|
||||
return 0
|
||||
|
||||
if(recalling)
|
||||
usr << "<span class='warning'>Error: Recall already in progress.</span>"
|
||||
return 0
|
||||
|
||||
gang.message_gangtools("[usr] is attempting to recall the emergency shuttle.")
|
||||
recalling = 1
|
||||
loc << "<span class='info'>\icon[src]Generating shuttle recall order with codes retrieved from last call signal...</span>"
|
||||
|
||||
sleep(rand(100,300))
|
||||
|
||||
if(SSshuttle.emergency.mode != SHUTTLE_CALL) //Shuttle can only be recalled when it's moving to the station
|
||||
user << "<span class='warning'>\icon[src]Emergency shuttle cannot be recalled at this time.</span>"
|
||||
recalling = 0
|
||||
return 0
|
||||
loc << "<span class='info'>\icon[src]Shuttle recall order generated. Accessing station long-range communication arrays...</span>"
|
||||
|
||||
sleep(rand(100,300))
|
||||
|
||||
if(!gang.dom_attempts)
|
||||
user << "<span class='warning'>\icon[src]Error: Unable to access communication arrays. Firewall has logged our signature and is blocking all further attempts.</span>"
|
||||
recalling = 0
|
||||
return 0
|
||||
|
||||
var/turf/userturf = get_turf(user)
|
||||
if(userturf.z != 1) //Shuttle can only be recalled while on station
|
||||
user << "<span class='warning'>\icon[src]Error: Device out of range of station communication arrays.</span>"
|
||||
recalling = 0
|
||||
return 0
|
||||
var/datum/station_state/end_state = new /datum/station_state()
|
||||
end_state.count()
|
||||
if((100 * start_state.score(end_state)) < 80) //Shuttle cannot be recalled if the station is too damaged
|
||||
user << "<span class='warning'>\icon[src]Error: Station communication systems compromised. Unable to establish connection.</span>"
|
||||
recalling = 0
|
||||
return 0
|
||||
loc << "<span class='info'>\icon[src]Comm arrays accessed. Broadcasting recall signal...</span>"
|
||||
|
||||
sleep(rand(100,300))
|
||||
|
||||
recalling = 0
|
||||
log_game("[key_name(user)] has tried to recall the shuttle with a gangtool.")
|
||||
message_admins("[key_name_admin(user)] has tried to recall the shuttle with a gangtool.", 1)
|
||||
userturf = get_turf(user)
|
||||
if(userturf.z == 1) //Check one more time that they are on station.
|
||||
if(SSshuttle.cancelEvac(user))
|
||||
return 1
|
||||
|
||||
loc << "<span class='info'>\icon[src]No response recieved. Emergency shuttle cannot be recalled at this time.</span>"
|
||||
return 0
|
||||
|
||||
/obj/item/device/gangtool/proc/can_use(mob/living/carbon/human/user)
|
||||
if(!istype(user))
|
||||
return 0
|
||||
if(user.restrained() || user.lying || user.stat || user.stunned || user.weakened)
|
||||
return 0
|
||||
if(!(src in user.contents))
|
||||
return 0
|
||||
if(!user.mind)
|
||||
return 0
|
||||
|
||||
if(gang) //If it's already registered, only let the gang's bosses use this
|
||||
if(user.mind in gang.bosses)
|
||||
return 1
|
||||
else //If it's not registered, any gangster can use this to register
|
||||
if(user.mind in ticker.mode.get_all_gangsters())
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
/obj/item/device/gangtool/spare
|
||||
outfits = 1
|
||||
|
||||
/obj/item/device/gangtool/spare/lt
|
||||
promotable = 1
|
||||
@@ -0,0 +1,476 @@
|
||||
|
||||
var/global/list/global_handofgod_traptypes = list()
|
||||
var/global/list/global_handofgod_structuretypes = list()
|
||||
|
||||
#define CONDUIT_RANGE 15
|
||||
|
||||
|
||||
/datum/game_mode
|
||||
var/list/datum/mind/red_deities = list()
|
||||
var/list/datum/mind/red_deity_prophets = list()
|
||||
var/list/datum/mind/red_deity_followers = list()
|
||||
|
||||
var/list/datum/mind/blue_deities = list()
|
||||
var/list/datum/mind/blue_deity_prophets = list()
|
||||
var/list/datum/mind/blue_deity_followers = list()
|
||||
|
||||
var/list/datum/mind/unassigned_followers = list() //for roundstart team assigning
|
||||
var/list/datum/mind/assigned_to_red = list()
|
||||
var/list/datum/mind/assigned_to_blue = list()
|
||||
|
||||
|
||||
/datum/game_mode/hand_of_god
|
||||
name = "hand of god"
|
||||
config_tag = "handofgod"
|
||||
antag_flag = ROLE_HOG_CULTIST //Followers use ROLE_HOG_CULTIST, Gods are picked later on with ROLE_HOG_GOD
|
||||
|
||||
required_players = 25
|
||||
required_enemies = 8
|
||||
recommended_enemies = 8
|
||||
restricted_jobs = list("Chaplain","AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel")
|
||||
|
||||
|
||||
/datum/game_mode/hand_of_god/announce()
|
||||
world << "<B>The current game mode is - Hand of God!</B>"
|
||||
world << "<B>Two cults are onboard the station, seeking to overthrow the other, and anyone who stands in their way.</B>"
|
||||
world << "<B>Followers</B> - Complete your deity's objectives. Convert crewmembers to your cause by using your deity's nexus. Remember - there is no you, there is only the cult."
|
||||
world << "<B>Prophets</B> - Command your cult by the will of your deity. You are a high-value target, so be careful!"
|
||||
world << "<B>Personnel</B> - Do not let any cult succeed in its mission. Mindshield implants and holy water will revert them to neutral, hopefully nonviolent crew."
|
||||
|
||||
|
||||
/////////////
|
||||
//Pre setup//
|
||||
/////////////
|
||||
|
||||
/datum/game_mode/hand_of_god/pre_setup()
|
||||
if(config.protect_roles_from_antagonist)
|
||||
restricted_jobs += protected_jobs
|
||||
|
||||
if(config.protect_assistant_from_antagonist)
|
||||
restricted_jobs += "Assistant"
|
||||
|
||||
for(var/F in 1 to recommended_enemies)
|
||||
if(!antag_candidates.len)
|
||||
break
|
||||
var/datum/mind/follower = pick_n_take(antag_candidates)
|
||||
unassigned_followers += follower
|
||||
follower.restricted_roles = restricted_jobs
|
||||
log_game("[follower.key] (ckey) has been selected as a follower, however teams have not been decided yet.")
|
||||
|
||||
while(unassigned_followers.len > (required_enemies / 2))
|
||||
var/datum/mind/chosen = pick_n_take(unassigned_followers)
|
||||
add_hog_follower(chosen,"red")
|
||||
|
||||
while(unassigned_followers.len)
|
||||
var/datum/mind/chosen = pick_n_take(unassigned_followers)
|
||||
add_hog_follower(chosen,"blue")
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
//////////////
|
||||
//Post Setup//
|
||||
//////////////
|
||||
|
||||
//Pick a follower to uplift into a god
|
||||
/datum/game_mode/hand_of_god/post_setup()
|
||||
|
||||
//Find viable red god
|
||||
var/list/red_god_possibilities = get_players_for_role(ROLE_HOG_GOD)
|
||||
red_god_possibilities &= red_deity_followers //followers only
|
||||
if(!red_god_possibilities.len) //No candidates? just pick any follower regardless of prefs
|
||||
red_god_possibilities = red_deity_followers
|
||||
|
||||
//Make red god
|
||||
var/datum/mind/red_god = pick_n_take(red_god_possibilities)
|
||||
if(red_god)
|
||||
red_god.current.become_god("red")
|
||||
remove_hog_follower(red_god,0)
|
||||
add_god(red_god,"red")
|
||||
|
||||
//Find viable blue god
|
||||
var/list/blue_god_possibilities = get_players_for_role(ROLE_HOG_GOD)
|
||||
blue_god_possibilities &= blue_deity_followers //followers only
|
||||
if(!blue_god_possibilities.len) //No candidates? just pick any follower regardless of prefs
|
||||
blue_god_possibilities = blue_deity_followers
|
||||
|
||||
//Make blue god
|
||||
var/datum/mind/blue_god = pick_n_take(blue_god_possibilities)
|
||||
if(blue_god)
|
||||
blue_god.current.become_god("blue")
|
||||
remove_hog_follower(blue_god,0)
|
||||
add_god(blue_god,"blue")
|
||||
|
||||
|
||||
//Forge objectives
|
||||
//This is done here so that both gods exist
|
||||
if(red_god)
|
||||
ticker.mode.forge_deity_objectives(red_god)
|
||||
if(blue_god)
|
||||
ticker.mode.forge_deity_objectives(blue_god)
|
||||
|
||||
|
||||
..()
|
||||
|
||||
///////////////////
|
||||
//Objective Procs//
|
||||
///////////////////
|
||||
|
||||
/datum/game_mode/proc/forge_deity_objectives(datum/mind/deity)
|
||||
switch(rand(1,100))
|
||||
if(1 to 30)
|
||||
var/datum/objective/deicide/deicide = new
|
||||
deicide.owner = deity
|
||||
if(deicide.find_target())//Hard to kill the other god if there is none
|
||||
deity.objectives += deicide
|
||||
|
||||
if(!(locate(/datum/objective/escape_followers) in deity.objectives))
|
||||
var/datum/objective/escape_followers/recruit = new
|
||||
recruit.owner = deity
|
||||
deity.objectives += recruit
|
||||
recruit.gen_amount_goal(8, 12)
|
||||
|
||||
if(31 to 60)
|
||||
var/datum/objective/sacrifice_prophet/sacrifice = new
|
||||
sacrifice.owner = deity
|
||||
deity.objectives += sacrifice
|
||||
|
||||
if(!(locate(/datum/objective/escape_followers) in deity.objectives))
|
||||
var/datum/objective/escape_followers/recruit = new
|
||||
recruit.owner = deity
|
||||
deity.objectives += recruit
|
||||
recruit.gen_amount_goal(8, 12)
|
||||
|
||||
if(61 to 85)
|
||||
var/datum/objective/build/build = new
|
||||
build.owner = deity
|
||||
deity.objectives += build
|
||||
build.gen_amount_goal(8, 16)
|
||||
|
||||
var/datum/objective/sacrifice_prophet/sacrifice = new
|
||||
sacrifice.owner = deity
|
||||
deity.objectives += sacrifice
|
||||
|
||||
if(!(locate(/datum/objective/escape_followers) in deity.objectives))
|
||||
var/datum/objective/escape_followers/recruit = new
|
||||
recruit.owner = deity
|
||||
deity.objectives += recruit
|
||||
recruit.gen_amount_goal(8, 12)
|
||||
|
||||
else
|
||||
if (!locate(/datum/objective/follower_block) in deity.objectives)
|
||||
var/datum/objective/follower_block/block = new
|
||||
block.owner = deity
|
||||
deity.objectives += block
|
||||
|
||||
///////////////
|
||||
//Greet procs//
|
||||
///////////////
|
||||
|
||||
/datum/game_mode/proc/greet_hog_follower(datum/mind/follower_mind,colour)
|
||||
if(follower_mind in blue_deity_prophets || follower_mind in red_deity_prophets)
|
||||
follower_mind.current << "<span class='danger'><B>You have been appointed as the prophet of the [colour] deity! You are the only one who can communicate with your deity at will. Guide your followers, but be wary, for many will want you dead.</span>"
|
||||
else if(colour)
|
||||
follower_mind.current << "<span class='danger'><B>You are a follower of the [colour] cult's deity!</span>"
|
||||
else
|
||||
follower_mind.current << "<span class='danger'><B>You are a follower of a cult's deity!</span>"
|
||||
|
||||
|
||||
/////////////////
|
||||
//Convert procs//
|
||||
/////////////////
|
||||
|
||||
/datum/game_mode/proc/add_hog_follower(datum/mind/follower_mind, colour = "No Colour")
|
||||
var/mob/living/carbon/human/H = follower_mind.current
|
||||
if(isloyal(H))
|
||||
H << "<span class='danger'>Your mindshield implant blocked the influence of the [colour] deity. </span>"
|
||||
return 0
|
||||
if((follower_mind in red_deity_followers) || (follower_mind in red_deity_prophets) || (follower_mind in blue_deity_followers) || (follower_mind in blue_deity_prophets))
|
||||
H << "<span class='danger'>You already belong to a deity. Your strong faith has blocked out the conversion attempt by the followers of the [colour] deity.</span>"
|
||||
return 0
|
||||
var/obj/item/weapon/nullrod/N = H.null_rod_check()
|
||||
if(N)
|
||||
H << "<span class='danger'>Your holy weapon prevented the [colour] deity from brainwashing you.</span>"
|
||||
return 0
|
||||
|
||||
if(colour == "red")
|
||||
red_deity_followers += follower_mind
|
||||
if(colour == "blue")
|
||||
blue_deity_followers += follower_mind
|
||||
|
||||
H.faction |= "[colour] god"
|
||||
follower_mind.current << "<span class='danger'><FONT size = 3>You are now a follower of the [colour] deity! Follow your deity's prophet in order to complete your deity's objectives. Convert crewmembers to your cause by using your deity's nexus. And remember - there is no you, there is only the cult.</FONT></span>"
|
||||
update_hog_icons_added(follower_mind, colour)
|
||||
follower_mind.special_role = "Hand of God: [capitalize(colour)] Follower"
|
||||
follower_mind.current.attack_log += "\[[time_stamp()]\] <font color='red'>Has been converted to the [colour] follower cult!</font>"
|
||||
return 1
|
||||
|
||||
|
||||
/datum/game_mode/proc/add_god(datum/mind/god_mind, colour = "No Colour")
|
||||
remove_hog_follower(god_mind, announce = 0)
|
||||
if(colour == "red")
|
||||
red_deities += god_mind
|
||||
if(colour == "blue")
|
||||
blue_deities += god_mind
|
||||
god_mind.current.attack_log += "\[[time_stamp()]\] <font color='red'>Has been made into a [colour] deity!</font>"
|
||||
god_mind.special_role = "Hand of God: [colour] God"
|
||||
update_hog_icons_added(god_mind, colour)
|
||||
|
||||
//////////////////
|
||||
//Deconvert proc//
|
||||
//////////////////
|
||||
|
||||
/datum/game_mode/proc/remove_hog_follower(datum/mind/follower_mind, announce = 1)//deconverts both
|
||||
follower_mind.remove_hog_follower_prophet()
|
||||
update_hog_icons_removed(follower_mind,"red")
|
||||
update_hog_icons_removed(follower_mind,"blue")
|
||||
|
||||
if(follower_mind.current)
|
||||
var/mob/living/carbon/human/H = follower_mind.current
|
||||
H.faction -= "red god"
|
||||
H.faction -= "blue god"
|
||||
|
||||
if(announce)
|
||||
follower_mind.current.attack_log += "\[[time_stamp()]\] <font color='red'>Has been deconverted from a deity's cult!</font>"
|
||||
follower_mind.current << "<span class='danger'><b>Your mind has been cleared from the brainwashing the followers have done to you. Now you serve yourself and the crew.</b></span>"
|
||||
for(var/mob/living/M in view(follower_mind.current))
|
||||
M << "[follower_mind.current] looks like their faith is shattered. They're no longer a cultist!"
|
||||
|
||||
|
||||
|
||||
//////////////////////
|
||||
// Mob helper procs //
|
||||
//////////////////////
|
||||
|
||||
/proc/is_handofgod_god(A)
|
||||
if(istype(A, /mob/camera/god))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
/proc/is_handofgod_bluecultist(A)
|
||||
if(ishuman(A))
|
||||
var/mob/living/carbon/human/H = A
|
||||
if(H.mind)
|
||||
if(H.mind in ticker.mode.blue_deity_followers|ticker.mode.blue_deity_prophets)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
/proc/is_handofgod_redcultist(A)
|
||||
if(ishuman(A))
|
||||
var/mob/living/carbon/human/H = A
|
||||
if(H.mind)
|
||||
if(H.mind in ticker.mode.red_deity_followers|ticker.mode.red_deity_prophets)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
/proc/is_handofgod_blueprophet(A)
|
||||
if(ishuman(A))
|
||||
var/mob/living/carbon/human/H = A
|
||||
if(H.mind)
|
||||
if(H.mind in ticker.mode.blue_deity_prophets)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
/proc/is_handofgod_redprophet(A)
|
||||
if(ishuman(A))
|
||||
var/mob/living/carbon/human/H = A
|
||||
if(H.mind)
|
||||
if(H.mind in ticker.mode.red_deity_prophets)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
/proc/is_handofgod_cultist(A) //any of them what so ever, blue, red, hot pink, whatever.
|
||||
if(is_handofgod_redcultist(A))
|
||||
return 1
|
||||
if(is_handofgod_bluecultist(A))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
/proc/is_handofgod_prophet(A) //any of them what so ever, blue, red, hot pink, whatever
|
||||
if(ishuman(A))
|
||||
var/mob/living/carbon/human/H = A
|
||||
if(H.mind)
|
||||
if(H.mind in ticker.mode.blue_deity_prophets|ticker.mode.red_deity_prophets)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
/mob/camera/god/proc/is_handofgod_myprophet(A)
|
||||
if(!ishuman(A))
|
||||
return 0
|
||||
var/mob/living/carbon/human/H = A
|
||||
if(!H.mind)
|
||||
return 0
|
||||
if(side == "red")
|
||||
if(H.mind in ticker.mode.red_deity_prophets)
|
||||
return 1
|
||||
else if(side == "blue")
|
||||
if(H.mind in ticker.mode.blue_deity_prophets)
|
||||
return 1
|
||||
|
||||
|
||||
/mob/camera/god/proc/is_handofgod_myfollowers(mob/A)
|
||||
if(!ishuman(A))
|
||||
return 0
|
||||
var/mob/living/carbon/human/H = A
|
||||
if(!H.mind)
|
||||
return 0
|
||||
if(side == "red")
|
||||
if(H.mind in ticker.mode.red_deity_prophets|ticker.mode.red_deity_followers)
|
||||
return 1
|
||||
else if(side == "blue")
|
||||
if(H.mind in ticker.mode.blue_deity_prophets|ticker.mode.blue_deity_followers)
|
||||
return 1
|
||||
|
||||
//////////////////////
|
||||
//Roundend Reporting//
|
||||
//////////////////////
|
||||
|
||||
|
||||
/datum/game_mode/hand_of_god/declare_completion()
|
||||
if(red_deities.len)
|
||||
var/text = "<BR><font size=3 color='red'><B>The red cult:</b></font>"
|
||||
for(var/datum/mind/red_god in red_deities)
|
||||
var/godwin = 1
|
||||
|
||||
text += "<BR><B>[red_god.key]</B> was the red deity, <B>[red_god.name]</B> ("
|
||||
if(red_god.current)
|
||||
if(red_god.current.stat == DEAD)
|
||||
text += "died"
|
||||
else
|
||||
text += "survived"
|
||||
else
|
||||
text += "ceased existing"
|
||||
text += ")"
|
||||
if(red_deity_prophets.len)
|
||||
for(var/datum/mind/red_prophet in red_deity_prophets)
|
||||
text += "<BR>The red prophet was <B>[red_prophet.name]</B> (<B>[red_prophet.key]</B>)"
|
||||
else
|
||||
text += "<BR>the red prophet was killed for their beliefs."
|
||||
|
||||
text += "<BR><B>Red follower count: </B> [red_deity_followers.len]"
|
||||
text += "<BR><B>Red followers:</B> "
|
||||
for(var/datum/mind/player in red_deity_followers)
|
||||
text += "[player.name] ([player.key]), "
|
||||
|
||||
var/objectives = ""
|
||||
if(red_god.objectives.len)
|
||||
var/count = 1
|
||||
for(var/datum/objective/O in red_god.objectives)
|
||||
if(O.check_completion())
|
||||
objectives += "<BR><B>Objective #[count]</B>: [O.explanation_text] <font color='green'><B>Success!</B></font>"
|
||||
feedback_add_details("god_objective","[O.type]|SUCCESS")
|
||||
else
|
||||
objectives += "<BR><B>Objective #[count]</B>: [O.explanation_text] <font color='red'><B>Fail.</B></font>"
|
||||
feedback_add_details("god_objective","[O.type]|FAIL")
|
||||
godwin = 0
|
||||
count++
|
||||
|
||||
text += objectives
|
||||
|
||||
if(godwin)
|
||||
text += "<BR><font color='green'><B>The red cult and deity were successful!</B></font>"
|
||||
feedback_add_details("god_success","SUCCESS")
|
||||
else
|
||||
text += "<br><font color='red'><B>The red cult and deity have failed!</B></font>"
|
||||
feedback_add_details("god_success","FAIL")
|
||||
|
||||
text += "<BR>"
|
||||
|
||||
world << text
|
||||
|
||||
if(blue_deities.len)
|
||||
var/text = "<BR><font size=3 color='red'><B>The blue cult:</b></font>"
|
||||
for(var/datum/mind/blue_god in blue_deities)
|
||||
var/godwin = 1
|
||||
|
||||
text += "<BR><B>[blue_god.key]</B> was the blue deity, <B>[blue_god.name]</B> ("
|
||||
if(blue_god.current)
|
||||
if(blue_god.current.stat == DEAD)
|
||||
text += "died"
|
||||
else
|
||||
text += "survived"
|
||||
else
|
||||
text += "ceased existing"
|
||||
text += ")"
|
||||
if(blue_deity_prophets.len)
|
||||
for(var/datum/mind/blue_prophet in blue_deity_prophets)
|
||||
text += "<BR>The blue prophet was <B>[blue_prophet.name]</B> (<B>[blue_prophet.key]</B>)"
|
||||
else
|
||||
text += "<BR>the blue prophet was killed for their beliefs."
|
||||
|
||||
text += "<BR><B>Blue follower count: </B> [blue_deity_followers.len]"
|
||||
text += "<BR><B>Blue followers:</B> "
|
||||
for(var/datum/mind/player in blue_deity_followers)
|
||||
text += "[player.name] ([player.key])"
|
||||
|
||||
var/objectives = ""
|
||||
if(blue_god.objectives.len)
|
||||
var/count = 1
|
||||
for(var/datum/objective/O in blue_god.objectives)
|
||||
if(O.check_completion())
|
||||
objectives += "<BR><B>Objective #[count]</B>: [O.explanation_text] <font color='green'><B>Success!</B></font>"
|
||||
feedback_add_details("god_objective","[O.type]|SUCCESS")
|
||||
else
|
||||
objectives += "<BR><B>Objective #[count]</B>: [O.explanation_text] <font color='red'><B>Fail.</B></font>"
|
||||
feedback_add_details("god_objective","[O.type]|FAIL")
|
||||
godwin = 0
|
||||
count++
|
||||
|
||||
text += objectives
|
||||
|
||||
if(godwin)
|
||||
text += "<BR><font color='green'><B>The blue cult and deity were successful!</B></font>"
|
||||
feedback_add_details("god_success","SUCCESS")
|
||||
else
|
||||
text += "<BR><font color='red'><B>The blue cult and deity have failed!</B></font>"
|
||||
feedback_add_details("god_success","FAIL")
|
||||
|
||||
text += "<BR>"
|
||||
|
||||
world << text
|
||||
|
||||
..()
|
||||
return 1
|
||||
|
||||
|
||||
/datum/game_mode/proc/update_hog_icons_added(datum/mind/hog_mind,side)
|
||||
var/hud_key
|
||||
var/rank = 0
|
||||
if(side == "red")
|
||||
hud_key = ANTAG_HUD_HOG_RED
|
||||
if(is_handofgod_redprophet(hog_mind.current))
|
||||
rank = 1
|
||||
|
||||
else if(side == "blue")
|
||||
hud_key = ANTAG_HUD_HOG_BLUE
|
||||
if(is_handofgod_blueprophet(hog_mind.current))
|
||||
rank = 1
|
||||
|
||||
if(is_handofgod_god(hog_mind.current))
|
||||
rank = 2
|
||||
|
||||
if(hud_key)
|
||||
var/datum/atom_hud/antag/hog_hud = huds[hud_key]
|
||||
hog_hud.join_hud(hog_mind.current)
|
||||
set_antag_hud(hog_mind.current, "hog-[side]-[rank]")
|
||||
|
||||
|
||||
/datum/game_mode/proc/update_hog_icons_removed(datum/mind/hog_mind,side)
|
||||
var/hud_key
|
||||
if(side == "red")
|
||||
hud_key = ANTAG_HUD_HOG_RED
|
||||
else if(side == "blue")
|
||||
hud_key = ANTAG_HUD_HOG_BLUE
|
||||
|
||||
if(hud_key)
|
||||
var/datum/atom_hud/antag/hog_hud = huds[hud_key]
|
||||
hog_hud.leave_hud(hog_mind.current)
|
||||
set_antag_hud(hog_mind.current,null)
|
||||
@@ -0,0 +1,28 @@
|
||||
/* Prophet's innate godspeak */
|
||||
/datum/action/innate/godspeak
|
||||
name = "Godspeak"
|
||||
button_icon_state = "godspeak"
|
||||
check_flags = AB_CHECK_CONSCIOUS
|
||||
var/mob/camera/god/god = null
|
||||
|
||||
/datum/action/innate/godspeak/IsAvailable()
|
||||
if(..())
|
||||
if(god)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/action/innate/godspeak/Activate()
|
||||
var/msg = input(owner,"Speak to your god","Godspeak","") as null|text
|
||||
if(!msg)
|
||||
return
|
||||
var/rendered = "<font color='[god.side]'><span class='game say'><i>Prophet [owner]:</i> <span class='message'>[msg]</span></span>"
|
||||
god << rendered
|
||||
owner << rendered
|
||||
for(var/mob/M in mob_list)
|
||||
if(isobserver(M))
|
||||
var/link = FOLLOW_LINK(M, owner)
|
||||
M << "[link] [rendered]"
|
||||
|
||||
/datum/action/innate/godspeak/Destroy()
|
||||
god = null
|
||||
return ..()
|
||||
@@ -0,0 +1,290 @@
|
||||
|
||||
/mob/camera/god
|
||||
name = "deity" //Auto changes to the player's deity name/random name
|
||||
real_name = "deity"
|
||||
icon = 'icons/mob/mob.dmi'
|
||||
icon_state = "marker"
|
||||
invisibility = 60
|
||||
see_in_dark = 0
|
||||
see_invisible = 55
|
||||
sight = SEE_TURFS | SEE_MOBS | SEE_OBJS | SEE_SELF
|
||||
languages_spoken = ALL
|
||||
languages_understood = ALL
|
||||
hud_possible = list(ANTAG_HUD)
|
||||
mouse_opacity = 0 //can't be clicked
|
||||
|
||||
var/faith = 100 //For initial prophet appointing/stupid purchase
|
||||
var/max_faith = 100
|
||||
var/side = "neutral" //Red or Blue for the gamemode
|
||||
var/obj/structure/divine/nexus/god_nexus = null //The source of the god's power in this realm, kill it and the god is kill
|
||||
var/nexus_required = FALSE //If the god dies from losing it's nexus, defaults to off so that gods don't instantly die at roundstart
|
||||
var/followers_required = 0 //Same as above
|
||||
var/alive_followers = 0
|
||||
var/list/structures = list()
|
||||
var/list/conduits = list()
|
||||
var/prophets_sacrificed_in_name = 0
|
||||
var/image/ghostimage = null //For observer with darkness off visiblity
|
||||
var/list/prophets = list()
|
||||
var/datum/action/innate/godspeak/speak2god
|
||||
|
||||
/mob/camera/god/New()
|
||||
..()
|
||||
update_icons()
|
||||
build_hog_construction_lists()
|
||||
|
||||
//Force nexuses after 15 minutes in hand of god mode
|
||||
if(ticker && ticker.mode && ticker.mode.name == "hand of god")
|
||||
addtimer(src, "forceplacenexus", 9000, FALSE)
|
||||
|
||||
|
||||
//Rebuilds the list based on the gamemode's lists
|
||||
//As they are the most accurate each tick
|
||||
/mob/camera/god/proc/get_my_followers()
|
||||
switch(side)
|
||||
if("red")
|
||||
. = ticker.mode.red_deity_followers|ticker.mode.red_deity_prophets
|
||||
if("blue")
|
||||
. = ticker.mode.blue_deity_followers|ticker.mode.blue_deity_prophets
|
||||
else
|
||||
. = list()
|
||||
|
||||
|
||||
/mob/camera/god/Destroy()
|
||||
var/list/followers = get_my_followers()
|
||||
for(var/datum/mind/F in followers)
|
||||
if(F.current)
|
||||
F.current << "<span class='danger'>Your god is DEAD!</span>"
|
||||
for(var/X in prophets)
|
||||
speak2god.Remove(X)
|
||||
ghost_darkness_images -= ghostimage
|
||||
updateallghostimages()
|
||||
return ..()
|
||||
|
||||
|
||||
|
||||
/mob/camera/god/proc/forceplacenexus()
|
||||
if(god_nexus)
|
||||
return
|
||||
|
||||
if(ability_cost(0,1,0))
|
||||
place_nexus()
|
||||
|
||||
else
|
||||
if(blobstart.len) //we're on invalid turf, try to pick from blobstart
|
||||
loc = pick(blobstart)
|
||||
place_nexus() //if blobstart fails, places on dense turf, but better than nothing
|
||||
src << "<span class='danger'>You failed to place your nexus, and it has been placed for you!</span>"
|
||||
|
||||
|
||||
/mob/camera/god/update_icons()
|
||||
icon_state = "[initial(icon_state)]-[side]"
|
||||
|
||||
if(ghostimage)
|
||||
ghost_darkness_images -= ghostimage
|
||||
|
||||
ghostimage = image(src.icon,src,src.icon_state)
|
||||
ghost_darkness_images |= ghostimage
|
||||
updateallghostimages()
|
||||
|
||||
|
||||
/mob/camera/god/Stat()
|
||||
..()
|
||||
if(statpanel("Status"))
|
||||
if(god_nexus)
|
||||
stat("Nexus health: ", god_nexus.health)
|
||||
stat("Followers: ", alive_followers)
|
||||
stat("Faith: ", "[faith]/[max_faith]")
|
||||
|
||||
|
||||
/mob/camera/god/Login()
|
||||
..()
|
||||
sync_mind()
|
||||
src << "<span class='notice'>You are a deity!</span>"
|
||||
src << "You are a deity and are worshipped by a cult! You are rather weak right now, but that will change as you gain more followers."
|
||||
src << "You will need to place an anchor to this world, a <b>Nexus</b>, in two minutes. If you don't, one will be placed immediately below you."
|
||||
src << "Your <b>Follower</b> count determines how many people believe in you and are a part of your cult."
|
||||
src << "Your <b>Nexus Integrity</b> tells you the condition of your nexus. If your nexus is destroyed, you will die. Place your Nexus on a safe, isolated place, that is still accessible to your followers."
|
||||
src << "Your <b>Faith</b> is used to interact with the world. This will regenerate on its own, and it goes faster when you have more followers and power pylons."
|
||||
src << "The first thing you should do after placing your nexus is to <b>appoint a prophet</b>. Only prophets can hear you talk, unless you use an expensive power."
|
||||
update_health_hud()
|
||||
|
||||
|
||||
/mob/camera/god/update_health_hud()
|
||||
if(god_nexus && hud_used && hud_used.healths)
|
||||
hud_used.healths.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'> <font color='lime'>[god_nexus.health] </font></div>"
|
||||
|
||||
|
||||
/mob/camera/god/proc/add_faith(faith_amt)
|
||||
if(faith_amt)
|
||||
faith = round(Clamp(faith+faith_amt, 0, max_faith))
|
||||
if(hud_used && hud_used.deity_power_display)
|
||||
hud_used.deity_power_display.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'> <font color='cyan'>[faith] </font></div>"
|
||||
|
||||
|
||||
|
||||
/mob/camera/god/proc/place_nexus()
|
||||
if(god_nexus || (z != 1))
|
||||
return 0
|
||||
|
||||
var/obj/structure/divine/nexus/N = new(get_turf(src))
|
||||
N.assign_deity(src)
|
||||
god_nexus = N
|
||||
nexus_required = TRUE
|
||||
verbs -= /mob/camera/god/verb/constructnexus
|
||||
//verbs += /mob/camera/god/verb/movenexus //Translocators have no sprite
|
||||
update_health_hud()
|
||||
|
||||
var/area/A = get_area(src)
|
||||
if(A)
|
||||
var/areaname = A.name
|
||||
var/list/followers = get_my_followers()
|
||||
for(var/datum/mind/F in followers)
|
||||
if(F.current)
|
||||
F.current << "<span class='boldnotice'>Your god's nexus is in \the [areaname]</span>"
|
||||
|
||||
|
||||
/mob/camera/god/verb/freeturret()
|
||||
set category = "Deity"
|
||||
set name = "Free Turret (0)"
|
||||
set desc = "Place a single turret, for 0 faith."
|
||||
|
||||
if(!ability_cost(0,1,1))
|
||||
return
|
||||
var/obj/structure/divine/defensepylon/DP = new(get_turf(src))
|
||||
DP.assign_deity(src)
|
||||
verbs -= /mob/camera/god/verb/freeturret
|
||||
|
||||
|
||||
|
||||
/mob/camera/god/proc/update_followers()
|
||||
alive_followers = 0
|
||||
var/list/all_followers = get_my_followers()
|
||||
for(var/datum/mind/F in all_followers)
|
||||
if(F.current && F.current.stat != DEAD)
|
||||
alive_followers++
|
||||
|
||||
if(hud_used && hud_used.deity_follower_display)
|
||||
hud_used.deity_follower_display.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'> <font color='red'>[alive_followers] </font></div>"
|
||||
|
||||
|
||||
/mob/camera/god/proc/check_death()
|
||||
if(!alive_followers)
|
||||
src << "<span class='userdanger'>You no longer have any followers. You shudder as you feel your existence cease...</span>"
|
||||
if(god_nexus && !qdeleted(god_nexus))
|
||||
god_nexus.visible_message("<span class='danger'>\The [src] suddenly disappears!</span>")
|
||||
qdel(god_nexus)
|
||||
qdel(src)
|
||||
|
||||
|
||||
/mob/camera/god/say(msg)
|
||||
if(!msg)
|
||||
return
|
||||
if(client)
|
||||
if(client.prefs.muted & MUTE_IC)
|
||||
src << "You cannot send IC messages (muted)."
|
||||
return
|
||||
if(src.client.handle_spam_prevention(msg,MUTE_IC))
|
||||
return
|
||||
if(stat)
|
||||
return
|
||||
|
||||
god_speak(msg)
|
||||
|
||||
|
||||
/mob/camera/god/proc/god_speak(msg)
|
||||
log_say("Hand of God: [capitalize(side)] God/[key_name(src)] : [msg]")
|
||||
msg = trim(copytext(sanitize(msg), 1, MAX_MESSAGE_LEN))
|
||||
if(!msg)
|
||||
return
|
||||
|
||||
msg = say_quote(msg, get_spans())
|
||||
var/rendered = "<font color='[src.side]'><i><span class='game say'>Divine Telepathy,</i> <span class='name'>[name]</span> <span class='message'>[msg]</span></span></font>"
|
||||
src << rendered
|
||||
|
||||
for(var/mob/M in mob_list)
|
||||
if(is_handofgod_myfollowers(M))
|
||||
M << rendered
|
||||
if(isobserver(M))
|
||||
var/link = FOLLOW_LINK(M, src)
|
||||
M << "[link] [rendered]"
|
||||
|
||||
|
||||
/mob/camera/god/emote(act,m_type = 1 ,msg = null)
|
||||
return
|
||||
|
||||
|
||||
/mob/camera/god/Move(NewLoc, Dir = 0)
|
||||
loc = NewLoc
|
||||
|
||||
|
||||
|
||||
/mob/camera/god/Topic(href, href_list)
|
||||
if(href_list["create_structure"])
|
||||
if(!ability_cost(75,1,1))
|
||||
return
|
||||
|
||||
var/obj/structure/divine/construct_type = text2path(href_list["create_structure"]) //it's a path but we need to initial() some vars
|
||||
if(!construct_type)
|
||||
return
|
||||
|
||||
add_faith(-75)
|
||||
var/obj/structure/divine/construction_holder/CH = new(get_turf(src))
|
||||
CH.assign_deity(src)
|
||||
CH.setup_construction(construct_type)
|
||||
CH.visible_message("<span class='notice'>[src] has created a transparent, unfinished [initial(construct_type.name)]. It can be finished by adding materials.</span>")
|
||||
src << "<span class='boldnotice'>You may click a construction site to cancel it, but only faith is refunded.</span>"
|
||||
structure_construction_ui(src)
|
||||
return
|
||||
|
||||
if(href_list["place_trap"])
|
||||
if(!ability_cost(20,1,1))
|
||||
return
|
||||
|
||||
var/atom/trap_type = text2path(href_list["place_trap"])
|
||||
if(!trap_type)
|
||||
return
|
||||
|
||||
src << "You lay \a [initial(trap_type.name)]"
|
||||
add_faith(-20)
|
||||
new trap_type(get_turf(src))
|
||||
return
|
||||
|
||||
..()
|
||||
|
||||
|
||||
/mob/camera/god/proc/structure_construction_ui(mob/camera/god/user)
|
||||
var/dat = ""
|
||||
for(var/t in global_handofgod_structuretypes)
|
||||
if(global_handofgod_structuretypes[t])
|
||||
var/obj/structure/divine/apath = global_handofgod_structuretypes[t]
|
||||
dat += "<center><B>[capitalize(t)]</B></center><BR>"
|
||||
var/imgstate = initial(apath.autocolours) ? "[initial(apath.icon_state)]-[side]" : "[initial(apath.icon_state)]"
|
||||
var/icon/I = icon('icons/obj/hand_of_god_structures.dmi',imgstate)
|
||||
var/img_component = lowertext(t)
|
||||
//I hate byond, but atleast it autocaches these so it's only 1*number_of_structures worth of actual calls
|
||||
user << browse_rsc(I,"hog_structure-[img_component].png")
|
||||
dat += "<center><img src='hog_structure-[img_component].png' height=64 width=64></center>"
|
||||
dat += "Description: [initial(apath.desc)]<BR>"
|
||||
dat += "<center><a href='?src=\ref[src];create_structure=[apath]'>Construct [capitalize(t)]</a></center><BR><BR>"
|
||||
|
||||
var/datum/browser/popup = new(src, "structures","Construct Structure",350,500)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
|
||||
/mob/camera/god/proc/trap_construction_ui(mob/camera/god/user)
|
||||
var/dat = ""
|
||||
for(var/t in global_handofgod_traptypes)
|
||||
if(global_handofgod_traptypes[t])
|
||||
var/obj/structure/divine/trap/T = global_handofgod_traptypes[t]
|
||||
dat += "<center><B>[capitalize(t)]</B></center><BR>"
|
||||
var/icon/I = icon('icons/obj/hand_of_god_structures.dmi',"[initial(T.icon_state)]")
|
||||
var/img_component = lowertext(t)
|
||||
user << browse_rsc(I,"hog_trap-[img_component].png")
|
||||
dat += "<center><img src='hog_trap-[img_component].png' height=64 width=64></center>"
|
||||
dat += "Description: [initial(T.desc)]<BR>"
|
||||
dat += "<center><a href='?src=\ref[src];place_trap=[T]'>Place [capitalize(t)]</a></center><BR><BR>"
|
||||
|
||||
var/datum/browser/popup = new(src, "traps", "Place Trap",350,500)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
@@ -0,0 +1,306 @@
|
||||
/obj/item/weapon/banner
|
||||
name = "banner"
|
||||
icon = 'icons/obj/items.dmi'
|
||||
icon_state = "banner"
|
||||
item_state = "banner"
|
||||
desc = "A banner with Nanotrasen's logo on it."
|
||||
var/moralecooldown = 0
|
||||
var/moralewait = 600
|
||||
|
||||
|
||||
/obj/item/weapon/banner/attack_self(mob/living/carbon/human/user)
|
||||
if(moralecooldown + moralewait > world.time)
|
||||
return
|
||||
var/side = ""
|
||||
if(is_handofgod_redcultist(user))
|
||||
side = "red"
|
||||
else if (is_handofgod_bluecultist(user))
|
||||
side = "blue"
|
||||
|
||||
if(!side)
|
||||
return
|
||||
user << "<span class='notice'>You increase the morale of your fellows!</span>"
|
||||
moralecooldown = world.time
|
||||
|
||||
for(var/mob/living/carbon/human/H in range(4,get_turf(src)))
|
||||
if((side == "red") && is_handofgod_redcultist(H) || (side == "blue") && is_handofgod_bluecultist(H))
|
||||
H << "<span class='notice'>Your morale is increased by [user]'s banner!</span>"
|
||||
H.adjustBruteLoss(-15)
|
||||
H.adjustFireLoss(-15)
|
||||
H.AdjustStunned(-2)
|
||||
H.AdjustWeakened(-2)
|
||||
H.AdjustParalysis(-2)
|
||||
|
||||
|
||||
/obj/item/weapon/banner/red
|
||||
name = "red banner"
|
||||
icon_state = "banner-red"
|
||||
item_state = "banner-red"
|
||||
desc = "A banner with the logo of the red deity."
|
||||
|
||||
/obj/item/weapon/banner/red/examine(mob/user)
|
||||
..()
|
||||
if(is_handofgod_redcultist(user))
|
||||
user << "A banner representing our might against the heretics. We may use it to increase the morale of our fellow members!"
|
||||
else if(is_handofgod_bluecultist(user))
|
||||
user << "A heretical banner that should be destroyed posthaste."
|
||||
|
||||
|
||||
/obj/item/weapon/banner/blue
|
||||
name = "blue banner"
|
||||
icon_state = "banner-blue"
|
||||
item_state = "banner-blue"
|
||||
desc = "A banner with the logo of the blue deity"
|
||||
|
||||
/obj/item/weapon/banner/blue/examine(mob/user)
|
||||
..()
|
||||
|
||||
if(is_handofgod_redcultist(user))
|
||||
user << "A heretical banner that should be destroyed posthaste."
|
||||
else if(is_handofgod_bluecultist(user))
|
||||
user << "A banner representing our might against the heretics. We may use it to increase the morale of our fellow members!"
|
||||
|
||||
|
||||
/obj/item/weapon/storage/backpack/bannerpack
|
||||
name = "nanotrasen banner backpack"
|
||||
desc = "It's a backpack with lots of extra room. A banner with Nanotrasen's logo is attached, that can't be removed."
|
||||
max_combined_w_class = 27 //6 more then normal, for the tradeoff of declaring yourself an antag at all times.
|
||||
icon_state = "bannerpack"
|
||||
|
||||
|
||||
/obj/item/weapon/storage/backpack/bannerpack/red
|
||||
name = "red banner backpack"
|
||||
desc = "It's a backpack with lots of extra room. A red banner is attached, that can't be removed."
|
||||
icon_state = "bannerpack-red"
|
||||
|
||||
|
||||
/obj/item/weapon/storage/backpack/bannerpack/blue
|
||||
name = "blue banner backpack"
|
||||
desc = "It's a backpack with lots of extra room. A blue banner is attached, that can't be removed."
|
||||
icon_state = "bannerpack-blue"
|
||||
|
||||
|
||||
|
||||
//this is all part of one item set
|
||||
/obj/item/clothing/suit/armor/plate/crusader
|
||||
name = "Crusader's Armour"
|
||||
icon_state = "crusader"
|
||||
w_class = 4 //bulky
|
||||
slowdown = 2.0 //gotta pretend we're balanced.
|
||||
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
armor = list(melee = 50, bullet = 50, laser = 50, energy = 40, bomb = 60, bio = 0, rad = 0)
|
||||
|
||||
/obj/item/clothing/suit/armor/plate/crusader/red
|
||||
icon_state = "crusader-red"
|
||||
|
||||
/obj/item/clothing/suit/armor/plate/crusader/blue
|
||||
icon_state = "crusader-blue"
|
||||
|
||||
/obj/item/clothing/suit/armor/plate/crusader/examine(mob/user)
|
||||
..()
|
||||
if(!is_handofgod_cultist(user))
|
||||
user << "Armour that's comprised of metal and cloth."
|
||||
else
|
||||
user << "Armour that was used to protect from backstabs, gunshots, explosives, and lasers. The original wearers of this type of armour were trying to avoid being murdered. Since they're not around anymore, you're not sure if they were successful or not."
|
||||
|
||||
|
||||
/obj/item/clothing/head/helmet/plate/crusader
|
||||
name = "Crusader's Hood"
|
||||
icon_state = "crusader"
|
||||
w_class = 3 //normal
|
||||
flags_inv = HIDEHAIR|HIDEEARS|HIDEFACE
|
||||
armor = list(melee = 50, bullet = 50, laser = 50, energy = 40, bomb = 60, bio = 0, rad = 0)
|
||||
|
||||
/obj/item/clothing/head/helmet/plate/crusader/blue
|
||||
icon_state = "crusader-blue"
|
||||
|
||||
/obj/item/clothing/head/helmet/plate/crusader/red
|
||||
icon_state = "crusader-red"
|
||||
|
||||
|
||||
/obj/item/clothing/head/helmet/plate/crusader/examine(mob/user)
|
||||
..()
|
||||
if(!is_handofgod_cultist(user))
|
||||
user << "A brownish hood."
|
||||
else
|
||||
user << "A hood that's very protective, despite being made of cloth. Due to the tendency of the wearer to be targeted for assassinations, being protected from being shot in the face was very important.."
|
||||
|
||||
|
||||
|
||||
//Prophet helmet
|
||||
/obj/item/clothing/head/helmet/plate/crusader/prophet
|
||||
name = "Prophet's Hat"
|
||||
alternate_worn_icon = 'icons/mob/large-worn-icons/64x64/head.dmi'
|
||||
flags = 0
|
||||
armor = list(melee = 60, bullet = 60, laser = 60, energy = 50, bomb = 70, bio = 50, rad = 50) //religion protects you from disease and radiation, honk.
|
||||
worn_x_dimension = 64
|
||||
worn_y_dimension = 64
|
||||
var/side = "neither"
|
||||
|
||||
/obj/item/clothing/head/helmet/plate/crusader/prophet/equipped(mob/living/carbon/user, slot)
|
||||
var/faithful = 0
|
||||
if(slot == slot_head)
|
||||
switch(side)
|
||||
if("blue")
|
||||
faithful = is_handofgod_bluecultist(user)
|
||||
if("red")
|
||||
faithful = is_handofgod_redcultist(user)
|
||||
else
|
||||
faithful = 1
|
||||
if(!faithful)
|
||||
user << "<span class='danger'>Your mind is assaulted by a vast power, furious at your desecration!</span>"
|
||||
user.emote("scream")
|
||||
user.adjustFireLoss(10)
|
||||
user.unEquip(src)
|
||||
user.head = null
|
||||
user.update_inv_head()
|
||||
src.screen_loc = null
|
||||
user.Weaken(1)
|
||||
|
||||
/obj/item/clothing/head/helmet/plate/crusader/prophet/red
|
||||
icon_state = "prophet-red"
|
||||
side = "red"
|
||||
|
||||
|
||||
/obj/item/clothing/head/helmet/plate/crusader/prophet/blue
|
||||
icon_state = "prophet-blue"
|
||||
side = "blue"
|
||||
|
||||
|
||||
/obj/item/clothing/head/helmet/plate/crusader/prophet/examine(mob/user)
|
||||
..()
|
||||
if(!is_handofgod_cultist(user))
|
||||
user << "A brownish, religious-looking hat."
|
||||
else
|
||||
user << "A hat bestowed upon a prophet of gods and demigods."
|
||||
user << "This hat belongs to the [side] god."
|
||||
|
||||
|
||||
|
||||
//Structure conversion staff
|
||||
/obj/item/weapon/godstaff
|
||||
name = "godstaff"
|
||||
icon_state = "godstaff-red"
|
||||
var/mob/camera/god/god = null
|
||||
var/staffcooldown = 0
|
||||
var/staffwait = 30
|
||||
|
||||
/obj/item/weapon/godstaff/examine(mob/user)
|
||||
..()
|
||||
if(!is_handofgod_cultist(user))
|
||||
user << "It's a stick..?"
|
||||
else
|
||||
user << "A powerful staff capable of changing the allegiance of god/demigod structures."
|
||||
|
||||
|
||||
|
||||
/obj/item/weapon/godstaff/attack_self(mob/living/carbon/user)
|
||||
if((god && !god.is_handofgod_myprophet(user)) || !god)
|
||||
user << "<span class='danger'>YOU ARE NOT THE CHOSEN ONE!</span>"
|
||||
return
|
||||
if(!(istype(user.head, /obj/item/clothing/head/helmet/plate/crusader/prophet)))
|
||||
user << "<span class='warning'>Your connection to your diety isn't strong enough! You must wear your big hat!</span>"
|
||||
return
|
||||
if(staffcooldown + staffwait > world.time)
|
||||
return
|
||||
user.visible_message("[user] chants deeply and waves their staff")
|
||||
if(do_after(user, 20,1,src))
|
||||
for(var/obj/structure/divine/R in orange(3,user))
|
||||
user.say("Grant us true sight my god!")
|
||||
if(istype(R, /obj/structure/divine/nexus)|| istype(R, /obj/structure/divine/trap))
|
||||
continue
|
||||
R.visible_message("<span class='danger'>[R] suddenly appears!</span>")
|
||||
R.invisibility = 0
|
||||
R.alpha = initial(R.alpha)
|
||||
R.density = initial(R.density)
|
||||
R.activate()
|
||||
staffcooldown = world.time
|
||||
|
||||
/obj/item/weapon/godstaff/red
|
||||
icon_state = "godstaff-red"
|
||||
|
||||
/obj/item/weapon/godstaff/blue
|
||||
icon_state = "godstaff-blue"
|
||||
|
||||
|
||||
|
||||
/obj/item/clothing/gloves/plate
|
||||
name = "Plate Gauntlets"
|
||||
icon_state = "crusader"
|
||||
siemens_coefficient = 0
|
||||
cold_protection = HANDS
|
||||
min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT
|
||||
heat_protection = HANDS
|
||||
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
|
||||
|
||||
|
||||
/obj/item/clothing/gloves/plate/red
|
||||
icon_state = "crusader-red"
|
||||
|
||||
/obj/item/clothing/gloves/plate/blue
|
||||
icon_state = "crusader-blue"
|
||||
|
||||
|
||||
/obj/item/clothing/gloves/plate/examine(mob/user)
|
||||
..()
|
||||
if(!is_handofgod_cultist(user))
|
||||
usr << "They're like gloves, but made of metal."
|
||||
else
|
||||
usr << "Protective gloves that are also blessed to protect from heat and shock."
|
||||
|
||||
|
||||
/obj/item/clothing/shoes/plate
|
||||
name = "Plate Boots"
|
||||
icon_state = "crusader"
|
||||
w_class = 3 //normal
|
||||
armor = list(melee = 50, bullet = 50, laser = 50, energy = 40, bomb = 60, bio = 0, rad = 0) //does this even do anything on boots?
|
||||
flags = NOSLIP
|
||||
cold_protection = FEET
|
||||
min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT
|
||||
heat_protection = FEET
|
||||
max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT
|
||||
|
||||
|
||||
/obj/item/clothing/shoes/plate/red
|
||||
icon_state = "crusader-red"
|
||||
|
||||
/obj/item/clothing/shoes/plate/blue
|
||||
icon_state = "crusader-blue"
|
||||
|
||||
|
||||
/obj/item/clothing/shoes/plate/examine(mob/user)
|
||||
..()
|
||||
if(!is_handofgod_cultist(user))
|
||||
usr << "Metal boots, they look heavy."
|
||||
else
|
||||
usr << "Heavy boots that are blessed for sure footing. You'll be safe from being taken down by the heresy that is the banana peel."
|
||||
|
||||
|
||||
/obj/item/weapon/storage/box/itemset/crusader
|
||||
name = "Crusader's Armour Set" //i can't into ck2 references
|
||||
desc = "This armour is said to be based on the armor of kings on another world thousands of years ago, who tended to assassinate, conspire, and plot against everyone who tried to do the same to them. Some things never change."
|
||||
|
||||
|
||||
/obj/item/weapon/storage/box/itemset/crusader/blue/New()
|
||||
..()
|
||||
contents = list()
|
||||
sleep(1)
|
||||
new /obj/item/clothing/suit/armor/plate/crusader/blue(src)
|
||||
new /obj/item/clothing/head/helmet/plate/crusader/blue(src)
|
||||
new /obj/item/clothing/gloves/plate/blue(src)
|
||||
new /obj/item/clothing/shoes/plate/blue(src)
|
||||
|
||||
|
||||
/obj/item/weapon/storage/box/itemset/crusader/red/New()
|
||||
..()
|
||||
contents = list()
|
||||
sleep(1)
|
||||
new /obj/item/clothing/suit/armor/plate/crusader/red(src)
|
||||
new /obj/item/clothing/head/helmet/plate/crusader/red(src)
|
||||
new /obj/item/clothing/gloves/plate/red(src)
|
||||
new /obj/item/clothing/shoes/plate/red(src)
|
||||
|
||||
|
||||
/obj/item/weapon/claymore/hog
|
||||
force = 30
|
||||
armour_penetration = 15
|
||||
@@ -0,0 +1,129 @@
|
||||
|
||||
/datum/objective/build
|
||||
dangerrating = 15
|
||||
martyr_compatible = 1
|
||||
|
||||
|
||||
/datum/objective/build/proc/gen_amount_goal(lower, upper)
|
||||
target_amount = rand(lower, upper)
|
||||
explanation_text = "Build [target_amount] shrines."
|
||||
return target_amount
|
||||
|
||||
|
||||
/datum/objective/build/check_completion()
|
||||
if(!owner || !owner.current)
|
||||
return 0
|
||||
|
||||
var/shrines = 0
|
||||
if(is_handofgod_god(owner.current))
|
||||
var/mob/camera/god/G = owner.current
|
||||
for(var/obj/structure/divine/shrine/S in G.structures)
|
||||
S++
|
||||
|
||||
return (shrines >= target_amount)
|
||||
|
||||
|
||||
|
||||
/datum/objective/deicide
|
||||
dangerrating = 20
|
||||
martyr_compatible = 1
|
||||
|
||||
/datum/objective/deicide/check_completion()
|
||||
if(target)
|
||||
if(target.current) //Gods are deleted when they lose
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
/datum/objective/deicide/find_target()
|
||||
if(!owner || !owner.current)
|
||||
return
|
||||
|
||||
if(is_handofgod_god(owner.current))
|
||||
var/mob/camera/god/G = owner.current
|
||||
if(G.side == "red")
|
||||
if(ticker.mode.blue_deities.len)
|
||||
target = ticker.mode.blue_deities[1]
|
||||
if(G.side == "blue")
|
||||
if(ticker.mode.red_deities.len)
|
||||
target = ticker.mode.red_deities[1]
|
||||
if(!target)
|
||||
return 0
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/deicide/update_explanation_text()
|
||||
..()
|
||||
if(target && target.current)
|
||||
explanation_text = "Phase [target.name], the false god, out of this plane of existence.."
|
||||
else
|
||||
explanation_text = "Free Objective"
|
||||
|
||||
|
||||
|
||||
/datum/objective/follower_block
|
||||
explanation_text = "Do not allow any followers of the false god to escape on the station's shuttle alive."
|
||||
dangerrating = 25
|
||||
martyr_compatible = 1
|
||||
|
||||
/datum/objective/follower_block/check_completion()
|
||||
var/side = "ABORT"
|
||||
if(is_handofgod_redcultist(owner.current))
|
||||
side = "red"
|
||||
else if(is_handofgod_bluecultist(owner.current))
|
||||
side = "blue"
|
||||
if(side == "ABORT")
|
||||
return 0
|
||||
|
||||
var/area/A = SSshuttle.emergency.areaInstance
|
||||
|
||||
for(var/mob/living/player in player_list)
|
||||
if(player.mind && player.stat != DEAD && get_area(player) == A)
|
||||
if(side == "red")
|
||||
if(is_handofgod_bluecultist(player))
|
||||
return 0
|
||||
else if(side == "blue")
|
||||
if(is_handofgod_redcultist(player))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
|
||||
/datum/objective/escape_followers
|
||||
dangerrating = 5
|
||||
|
||||
|
||||
/datum/objective/escape_followers/proc/gen_amount_goal(lower,upper)
|
||||
target_amount = rand(lower,upper)
|
||||
explanation_text = "Your will must surpass this station. Having [target_amount] followers escape on the shuttle or pods will allow that."
|
||||
return target_amount
|
||||
|
||||
|
||||
/datum/objective/escape_followers/check_completion()
|
||||
var/escaped = 0
|
||||
if(is_handofgod_god(owner.current))
|
||||
var/mob/camera/god/G = owner.current
|
||||
if(G.side == "red")
|
||||
for(var/datum/mind/follower_mind in ticker.mode.red_deity_followers)
|
||||
if(follower_mind.current && follower_mind.current.stat != DEAD)
|
||||
if(follower_mind.current.onCentcom())
|
||||
escaped++
|
||||
|
||||
if(G.side == "blue")
|
||||
for(var/datum/mind/follower_mind in ticker.mode.blue_deity_followers)
|
||||
if(follower_mind.current && follower_mind.current.stat != DEAD)
|
||||
if(follower_mind.current.onCentcom())
|
||||
escaped++
|
||||
|
||||
return (escaped >= target_amount)
|
||||
|
||||
|
||||
/datum/objective/sacrifice_prophet
|
||||
explanation_text = "A false prophet is preaching their god's faith on the station. Sacrificing them will show the mortals who the true god is."
|
||||
dangerrating = 10
|
||||
|
||||
|
||||
/datum/objective/sacrifice_prophet/check_completion()
|
||||
var/mob/camera/god/G = owner.current
|
||||
if(istype(G))
|
||||
return G.prophets_sacrificed_in_name
|
||||
return 0
|
||||
@@ -0,0 +1,363 @@
|
||||
/mob/camera/god/proc/ability_cost(cost = 0,structures = 0, requires_conduit = 0, can_place_near_enemy_nexus = 0)
|
||||
if(faith < cost)
|
||||
src << "<span class='danger'>You lack the faith!</span>"
|
||||
return 0
|
||||
|
||||
if(structures)
|
||||
if(!isturf(loc) || istype(loc, /turf/open/space))
|
||||
src << "<span class='danger'>Your structure would just float away, you need stable ground!</span>"
|
||||
return 0
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
if(T)
|
||||
if(T.density)
|
||||
src << "<span class='danger'>There is something blocking your structure!</span>"
|
||||
return 0
|
||||
|
||||
for(var/atom/movable/AM in T)
|
||||
if(AM == src)
|
||||
continue
|
||||
if(AM.density)
|
||||
src << "<span class='danger'>There is something blocking your structure!</span>"
|
||||
return 0
|
||||
|
||||
if(requires_conduit)
|
||||
//Organised this way as there can be multiple conduits, so it's more likely to be a conduit check.
|
||||
var/valid = 0
|
||||
|
||||
for(var/obj/structure/divine/conduit/C in conduits)
|
||||
if(get_dist(src, C) <= CONDUIT_RANGE)
|
||||
valid++
|
||||
break
|
||||
|
||||
if(!valid)
|
||||
if(get_dist(src, god_nexus) <= CONDUIT_RANGE)
|
||||
valid++
|
||||
|
||||
if(!valid)
|
||||
src << "<span class='danger'>You must be near your Nexus or a Conduit to do this!</span>"
|
||||
return 0
|
||||
|
||||
if(!can_place_near_enemy_nexus)
|
||||
var/datum/mind/enemy
|
||||
switch(side)
|
||||
if("red")
|
||||
if(ticker.mode.blue_deities.len)
|
||||
enemy = ticker.mode.blue_deities[1]
|
||||
if("blue")
|
||||
if(ticker.mode.red_deities.len)
|
||||
enemy = ticker.mode.red_deities[1]
|
||||
|
||||
if(enemy && is_handofgod_god(enemy.current))
|
||||
var/mob/camera/god/enemy_god = enemy.current
|
||||
if(enemy_god.god_nexus && (get_dist(src,enemy_god.god_nexus) <= CONDUIT_RANGE*2))
|
||||
src << "<span class='danger'>You are too close to the other god's stronghold!</span>"
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
/mob/camera/god/verb/returntonexus()
|
||||
set category = "Deity"
|
||||
set name = "Goto Nexus"
|
||||
set desc = "Teleports you to your next instantly."
|
||||
|
||||
if(god_nexus)
|
||||
Move(get_turf(god_nexus))
|
||||
else
|
||||
src << "You don't even have a Nexus, construct one."
|
||||
|
||||
|
||||
/mob/camera/god/verb/jumptofollower()
|
||||
set category = "Deity"
|
||||
set name = "Jump to Follower"
|
||||
set desc = "Teleports you to one of your followers."
|
||||
var/list/following = list()
|
||||
if(side == "red")
|
||||
following = ticker.mode.red_deity_followers|ticker.mode.red_deity_prophets
|
||||
else if(side == "blue")
|
||||
following = ticker.mode.blue_deity_followers|ticker.mode.blue_deity_prophets
|
||||
else
|
||||
src << "You are unaligned, and thus do not have followers"
|
||||
return
|
||||
|
||||
var/datum/mind/choice = input("Choose a follower","Jump to Follower") as null|anything in following
|
||||
if(choice && choice.current)
|
||||
Move(get_turf(choice.current))
|
||||
|
||||
|
||||
/mob/camera/god/verb/newprophet()
|
||||
set category = "Deity"
|
||||
set name = "Appoint Prophet (100)"
|
||||
set desc = "Appoint one of your followers as your Prophet, who can hear your words"
|
||||
|
||||
var/list/following = list()
|
||||
|
||||
if(!ability_cost(100))
|
||||
return
|
||||
if(side == "red")
|
||||
var/datum/mind/old_proph = locate() in ticker.mode.red_deity_prophets
|
||||
if(old_proph && old_proph.current && old_proph.current.stat != DEAD)
|
||||
src << "You can only have one prophet alive at a time."
|
||||
return
|
||||
else
|
||||
following = ticker.mode.red_deity_followers
|
||||
else if(side == "blue")
|
||||
var/datum/mind/old_proph = locate() in ticker.mode.blue_deity_prophets
|
||||
if(old_proph && old_proph.current && old_proph.current.stat != DEAD)
|
||||
src << "You can only have one prophet alive at a time."
|
||||
return
|
||||
else
|
||||
following = ticker.mode.blue_deity_followers
|
||||
|
||||
else
|
||||
src << "You are unalligned, and thus do not have prophets"
|
||||
return
|
||||
|
||||
var/datum/mind/choice = input("Choose a follower to make into your prophet","Prophet Uplifting") as null|anything in following
|
||||
if(choice && choice.current && choice.current.stat != DEAD)
|
||||
src << "You choose [choice.current] as your prophet."
|
||||
choice.make_Handofgod_prophet(side)
|
||||
speak2god = new()
|
||||
speak2god.god = src
|
||||
speak2god.Grant(choice.current)
|
||||
|
||||
//Prophet gear
|
||||
var/mob/living/carbon/human/H = choice.current
|
||||
var/popehat = null
|
||||
var/popestick = null
|
||||
var/success = ""
|
||||
switch(side)
|
||||
if("red")
|
||||
popehat = /obj/item/clothing/head/helmet/plate/crusader/prophet/red
|
||||
popestick = /obj/item/weapon/godstaff/red
|
||||
if("blue")
|
||||
popehat = /obj/item/clothing/head/helmet/plate/crusader/prophet/blue
|
||||
popestick = /obj/item/weapon/godstaff/blue
|
||||
|
||||
if(popehat)
|
||||
var/obj/item/clothing/head/helmet/plate/crusader/prophet/P = new popehat()
|
||||
|
||||
if(H.equip_to_slot_if_possible(P,slot_in_backpack,0,1,1))
|
||||
success = "It is in your backpack."
|
||||
else
|
||||
H.unEquip(H.head)
|
||||
H.equip_to_slot_or_del(P,slot_head)
|
||||
success = "It is on your head."
|
||||
|
||||
if(success)
|
||||
H << "<span class='boldnotice'>A powerful hat has been bestowed upon you, you will need to wear it to utilize your staff fully.</span>"
|
||||
H << "<span class='boldnotice'>[success]</span>"
|
||||
|
||||
if(popestick)
|
||||
var/obj/item/weapon/godstaff/G = new popestick()
|
||||
G.god = src
|
||||
if(!H.equip_to_slot_if_possible(G,slot_in_backpack,0,1,1))
|
||||
if(!H.put_in_hands(G))
|
||||
G.loc = get_turf(H)
|
||||
success = "It is on the floor..."
|
||||
else
|
||||
success = "It is in your hands..."
|
||||
else
|
||||
success = "It is in your backpack..."
|
||||
|
||||
if(success)
|
||||
H << "<span class='boldnotice'>A powerful staff has been bestowed upon you, you can use this to convert the false god's structures!</span>"
|
||||
H << "<span class='boldnotice'>[success]</span>"
|
||||
//end prophet gear
|
||||
|
||||
add_faith(-100)
|
||||
|
||||
|
||||
/mob/camera/god/verb/talk(msg as text)
|
||||
set category = "Deity"
|
||||
set name = "Talk to Anyone (20)"
|
||||
set desc = "Allows you to send a message to anyone, regardless of their faith."
|
||||
if(!ability_cost(20))
|
||||
return
|
||||
var/mob/choice = input("Choose who you wish to talk to", "Talk to ANYONE") as null|anything in mob_list
|
||||
if(choice)
|
||||
var/original = msg
|
||||
msg = "<B>You hear a voice coming from everywhere and nowhere... <i>[msg]</i></B>"
|
||||
choice << msg
|
||||
src << "You say the following to [choice], [original]"
|
||||
add_faith(-20)
|
||||
|
||||
|
||||
/mob/camera/god/verb/smite()
|
||||
set category = "Deity"
|
||||
set name = "Smite (40)"
|
||||
set desc = "Hits anything under you with a moderate amount of damage."
|
||||
|
||||
if(!ability_cost(40,0,1))
|
||||
return
|
||||
if(!range(7,god_nexus))
|
||||
src << "You lack the strength to smite this far from your nexus."
|
||||
return
|
||||
|
||||
var/has_smitten = 0 //Hast thou been smitten, infidel?
|
||||
for(var/mob/living/L in get_turf(src))
|
||||
L.adjustFireLoss(20)
|
||||
L.adjustBruteLoss(20)
|
||||
L << "<span class='danger'><B>You feel the wrath of [name]!<B></span>"
|
||||
has_smitten = 1
|
||||
if(has_smitten)
|
||||
add_faith(-40)
|
||||
|
||||
|
||||
/mob/camera/god/verb/disaster()
|
||||
set category = "Deity"
|
||||
set name = "Invoke Disaster (300)" //difficult to reach without lots of followers
|
||||
set desc = "Tug at the fibres of reality itself and bend it to your whims!"
|
||||
|
||||
if(!ability_cost(300,0,1))
|
||||
return
|
||||
|
||||
var/event = pick(/datum/round_event/meteor_wave, /datum/round_event/communications_blackout, /datum/round_event/radiation_storm, /datum/round_event/carp_migration,
|
||||
/datum/round_event/spacevine, /datum/round_event/vent_clog, /datum/round_event/wormholes)
|
||||
if(event)
|
||||
new event()
|
||||
add_faith(-300)
|
||||
|
||||
|
||||
/mob/camera/god/verb/constructnexus()
|
||||
set category = "Deity"
|
||||
set name = "Construct Nexus"
|
||||
set desc = "Instantly creates your nexus, You can only do this once, make sure you're happy with it!"
|
||||
|
||||
if(!ability_cost(0,1,0))
|
||||
return
|
||||
|
||||
place_nexus()
|
||||
|
||||
|
||||
/* //Transolocators have no sprite
|
||||
/mob/camera/god/verb/movenexus()
|
||||
set category = "Deity"
|
||||
set name = "Relocate Nexus (50)"
|
||||
set desc = "Instantly relocates your nexus to an existing translocator belonging to your faith, this destroys the translocator in the process"
|
||||
|
||||
if(ability_cost(50,0,0) && god_nexus)
|
||||
var/list/translocators = list()
|
||||
var/list/used_keys = list()
|
||||
for(var/obj/structure/divine/translocator/T in structures)
|
||||
translocators["[T.name] ([get_area(T)])"] = T
|
||||
|
||||
if(!translocators.len)
|
||||
src << "<span class='warning'>You have no translocators!</span>"
|
||||
return
|
||||
|
||||
var/picked = input(src,"Choose a translocator","Relocate Nexus") as null|anything in translocators
|
||||
if(!picked || !translocators[picked])
|
||||
return
|
||||
|
||||
var/obj/structure/divine/translocator/T = translocators[T]
|
||||
var/turf/Tturf = get_turf(T)
|
||||
god_nexus.loc = T
|
||||
translocators[picked] = null
|
||||
add_faith(-50)
|
||||
qdel(T)
|
||||
*/
|
||||
|
||||
/mob/camera/god/verb/construct_structures()
|
||||
set category = "Deity"
|
||||
set name = "Construct Structure (75)"
|
||||
set desc = "Create the foundation of a divine object."
|
||||
|
||||
if(!ability_cost(75,1,1))
|
||||
return
|
||||
|
||||
structure_construction_ui(src)
|
||||
|
||||
|
||||
/mob/camera/god/verb/construct_traps()
|
||||
set category = "Deity"
|
||||
set name = "Construct Trap (20)"
|
||||
set desc = "Creates a ward or trap."
|
||||
|
||||
if(!ability_cost(20,1,1))
|
||||
return
|
||||
|
||||
trap_construction_ui(src)
|
||||
|
||||
|
||||
|
||||
/mob/camera/god/verb/construct_items()
|
||||
set category = "Deity"
|
||||
set name = "Construct Items (20)"
|
||||
set desc = "Construct some items for your followers"
|
||||
|
||||
if(!ability_cost(20,1,1))
|
||||
return
|
||||
|
||||
var/list/item_types = list("claymore sword" = /obj/item/weapon/claymore/hog)
|
||||
if(side == "red")
|
||||
item_types["red banner"] = /obj/item/weapon/banner/red
|
||||
item_types["red bannerbackpack"] = /obj/item/weapon/storage/backpack/bannerpack/red
|
||||
item_types["red armour"] = /obj/item/weapon/storage/box/itemset/crusader/red
|
||||
|
||||
else if(side == "blue")
|
||||
item_types["blue banner"] = /obj/item/weapon/banner/blue
|
||||
item_types["blue bannerbackpack"] = /obj/item/weapon/storage/backpack/bannerpack/blue
|
||||
item_types["blue armour"] = /obj/item/weapon/storage/box/itemset/crusader/blue
|
||||
|
||||
|
||||
var/item = input("Choose what you wish to create.", "Divine Items") as null|anything in item_types
|
||||
if(!item || !item_types[item] || !ability_cost(20,1,1))
|
||||
return
|
||||
|
||||
src << "You produce \a [item]"
|
||||
add_faith(-20)
|
||||
|
||||
var/itemtype = item_types[item]
|
||||
new itemtype (get_turf(src))
|
||||
|
||||
|
||||
|
||||
/mob/camera/god/verb/veil_structures()
|
||||
set category = "Deity"
|
||||
set name = "Veil Structures (20)"
|
||||
set desc = "Hide your structures from sight and touch, but prevent yourself from using them."
|
||||
|
||||
if(!ability_cost(20,1,1))
|
||||
return
|
||||
|
||||
src << "You focus your powers and start dragging your influence into the spiritual plane."
|
||||
for(var/mob/M in orange(3,src))//Yes I know this is terrible, but visible message doesnt work for this
|
||||
M << "<span class='warning'>The air begins to shimmer...</span>"
|
||||
if(do_after(src, 30, 0, src))
|
||||
for(var/obj/structure/divine/R in orange(3,src))
|
||||
if(istype(R, /obj/structure/divine/nexus)|| istype(R, /obj/structure/divine/trap)||(src.side != R.side))
|
||||
continue
|
||||
R.visible_message("<span class='danger'>[R] fades away.</span>")
|
||||
R.invisibility = 55
|
||||
R.alpha = 100 //To help ghosts distinguish hidden structures
|
||||
R.density = 0
|
||||
R.deactivate()
|
||||
src << "You hide your influence from view"
|
||||
add_faith(-20)
|
||||
|
||||
|
||||
/mob/camera/god/verb/reveal_structures()
|
||||
set category = "Deity"
|
||||
set name = "Reveal Structures (20)"
|
||||
set desc = "Make your structures visible again and allow them to be used."
|
||||
|
||||
if(!ability_cost(20,1,1))
|
||||
return
|
||||
|
||||
src << "You focus your powers and start dragging your influence into the material plane."
|
||||
for(var/mob/M in orange(3,src))//Yes I know this is terrible, but visible message doesnt work for this
|
||||
M << "<span class='warning'>The air begins to shimmer...</span>"
|
||||
if(do_after(src, 40, 0, src))
|
||||
for(var/obj/structure/divine/R in orange(3,src))
|
||||
if(istype(R, /obj/structure/divine/nexus)|| istype(R, /obj/structure/divine/trap)||(src.side != R.side))
|
||||
continue
|
||||
R.visible_message("<span class='danger'>[R] suddenly appears!</span>")
|
||||
R.invisibility = 0
|
||||
R.alpha = initial(R.alpha)
|
||||
R.density = initial(R.density)
|
||||
R.activate()
|
||||
src << "You bring your influence into view"
|
||||
add_faith(-20)
|
||||
|
||||
@@ -0,0 +1,658 @@
|
||||
|
||||
/proc/build_hog_construction_lists()
|
||||
if(global_handofgod_traptypes.len && global_handofgod_structuretypes.len)
|
||||
return
|
||||
|
||||
var/list/types = subtypesof(/obj/structure/divine) - /obj/structure/divine/trap
|
||||
for(var/T in types)
|
||||
var/obj/structure/divine/D = T
|
||||
if(initial(D.constructable))
|
||||
if(initial(D.trap))
|
||||
global_handofgod_traptypes[initial(D.name)] = T
|
||||
else
|
||||
global_handofgod_structuretypes[initial(D.name)] = T
|
||||
|
||||
/obj/structure/divine
|
||||
name = "divine construction site"
|
||||
icon = 'icons/obj/hand_of_god_structures.dmi'
|
||||
desc = "An unfinished divine building"
|
||||
anchored = 1
|
||||
density = 1
|
||||
var/constructable = TRUE
|
||||
var/trap = FALSE
|
||||
var/metal_cost = 0
|
||||
var/glass_cost = 0
|
||||
var/lesser_gem_cost = 0
|
||||
var/greater_gem_cost = 0
|
||||
var/mob/camera/god/deity
|
||||
var/side = "neutral" //"blue" or "red", also used for colouring structures when construction is started by a deity
|
||||
var/health = 100
|
||||
var/maxhealth = 100
|
||||
var/deactivated = 0 //Structures being hidden can't be used. Mainly to prevent invisible defense pylons.
|
||||
var/autocolours = TRUE //do we colour to our side?
|
||||
|
||||
/obj/structure/divine/New()
|
||||
..()
|
||||
|
||||
/obj/structure/divine/proc/deactivate()
|
||||
deactivated = 1
|
||||
|
||||
/obj/structure/divine/proc/activate()
|
||||
deactivated = 0
|
||||
|
||||
|
||||
/obj/structure/divine/proc/update_icons()
|
||||
if(autocolours)
|
||||
icon_state = "[initial(icon_state)]-[side]"
|
||||
|
||||
|
||||
/obj/structure/divine/Destroy()
|
||||
if(deity)
|
||||
deity.structures -= src
|
||||
return ..()
|
||||
|
||||
|
||||
|
||||
/obj/structure/divine/attackby(obj/item/I, mob/user)
|
||||
|
||||
//Structure conversion/capture
|
||||
if(istype(I, /obj/item/weapon/godstaff))
|
||||
if(!is_handofgod_cultist(user))
|
||||
user << "<span class='notice'>You're not quite sure what the hell you're even doing.</span>"
|
||||
return
|
||||
var/obj/item/weapon/godstaff/G = I
|
||||
if(G.god && deity != G.god)
|
||||
assign_deity(G.god, alert_old_deity = TRUE)
|
||||
visible_message("<span class='boldnotice'>\The [src] has been captured by [user]!</span>")
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/structure/divine/attacked_by(obj/item/I, mob/living/user)
|
||||
..()
|
||||
take_damage(I.force, I.damtype, 1)
|
||||
|
||||
/obj/structure/divine/proc/take_damage(damage, damage_type = BRUTE, sound_effect = 1)
|
||||
switch(damage_type)
|
||||
if(BRUTE)
|
||||
if(sound_effect)
|
||||
if(damage)
|
||||
playsound(loc, 'sound/weapons/smash.ogg', 50, 1)
|
||||
else
|
||||
playsound(loc, 'sound/weapons/tap.ogg', 50, 1)
|
||||
if(BURN)
|
||||
if(sound_effect)
|
||||
playsound(src.loc, 'sound/items/Welder.ogg', 100, 1)
|
||||
else
|
||||
return
|
||||
health -= damage
|
||||
if(!health)
|
||||
visible_message("<span class='danger'>\The [src] was destroyed!</span>")
|
||||
qdel(src)
|
||||
|
||||
|
||||
/obj/structure/divine/bullet_act(obj/item/projectile/P)
|
||||
. = ..()
|
||||
take_damage(P.damage, P.damage_type, 0)
|
||||
|
||||
|
||||
/obj/structure/divine/attack_alien(mob/living/carbon/alien/humanoid/user)
|
||||
user.changeNext_move(CLICK_CD_MELEE)
|
||||
user.do_attack_animation(src)
|
||||
add_hiddenprint(user)
|
||||
visible_message("<span class='warning'>\The [user] slashes at [src]!</span>")
|
||||
playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1)
|
||||
take_damage(20, BRUTE, 0)
|
||||
|
||||
/obj/machinery/attack_animal(mob/living/simple_animal/M)
|
||||
M.changeNext_move(CLICK_CD_MELEE)
|
||||
M.do_attack_animation(src)
|
||||
if(M.melee_damage_upper > 0)
|
||||
M.visible_message("<span class='danger'>[M.name] smashes against \the [src.name].</span>",\
|
||||
"<span class='danger'>You smash against the [src.name].</span>")
|
||||
take_damage(rand(M.melee_damage_lower,M.melee_damage_upper), M.melee_damage_type, 1)
|
||||
|
||||
|
||||
/obj/structure/divine/proc/assign_deity(mob/camera/god/new_deity, alert_old_deity = TRUE)
|
||||
if(!new_deity)
|
||||
return 0
|
||||
if(deity)
|
||||
if(alert_old_deity)
|
||||
deity << "<span class='danger'><B>Your [name] was captured by [new_deity]'s cult!</B></span>"
|
||||
deity.structures -= src
|
||||
deity = new_deity
|
||||
deity.structures |= src
|
||||
side = deity.side
|
||||
update_icons()
|
||||
return 1
|
||||
|
||||
|
||||
/obj/structure/divine/construction_holder
|
||||
alpha = 125
|
||||
constructable = FALSE
|
||||
var/obj/structure/divine/construction_result = /obj/structure/divine //a path, but typed to /obj/structure/divine for initial()
|
||||
|
||||
|
||||
|
||||
/obj/structure/divine/construction_holder/assign_deity(mob/camera/god/new_deity, alert_old_deity = TRUE)
|
||||
if(..())
|
||||
color = side
|
||||
|
||||
|
||||
/obj/structure/divine/construction_holder/attack_god(mob/camera/god/user)
|
||||
if(user.side == side && construction_result)
|
||||
user.add_faith(75)
|
||||
visible_message("<span class='danger'>[user] has cancelled \the [initial(construction_result.name)]")
|
||||
qdel(src)
|
||||
|
||||
|
||||
/obj/structure/divine/construction_holder/proc/setup_construction(construct_type)
|
||||
if(ispath(construct_type))
|
||||
construction_result = construct_type
|
||||
name = "[initial(construction_result.name)] construction site "
|
||||
icon_state = initial(construction_result.icon_state)
|
||||
metal_cost = initial(construction_result.metal_cost)
|
||||
glass_cost = initial(construction_result.glass_cost)
|
||||
lesser_gem_cost = initial(construction_result.lesser_gem_cost)
|
||||
greater_gem_cost = initial(construction_result.greater_gem_cost)
|
||||
desc = "An unfinished [initial(construction_result.name)]."
|
||||
|
||||
|
||||
/obj/structure/divine/construction_holder/attackby(obj/item/I, mob/user)
|
||||
if(!I || !user)
|
||||
return 0
|
||||
|
||||
if(istype(I, /obj/item/stack/sheet/metal))
|
||||
var/obj/item/stack/sheet/metal/M = I
|
||||
if(metal_cost)
|
||||
var/spend = min(metal_cost, M.amount)
|
||||
user << "<span class='notice'>You add [spend] metal to \the [src]."
|
||||
metal_cost = max(0, metal_cost - spend)
|
||||
M.use(spend)
|
||||
check_completion()
|
||||
else
|
||||
user << "<span class='notice'>\The [src] does not require any more metal!"
|
||||
return
|
||||
|
||||
if(istype(I, /obj/item/stack/sheet/glass))
|
||||
var/obj/item/stack/sheet/glass/G = I
|
||||
if(glass_cost)
|
||||
var/spend = min(glass_cost, G.amount)
|
||||
user << "<span class='notice'>You add [spend] glass to \the [src]."
|
||||
glass_cost = max(0, glass_cost - spend)
|
||||
G.use(spend)
|
||||
check_completion()
|
||||
else
|
||||
user << "<span class='notice'>\The [src] does not require any more glass!"
|
||||
return
|
||||
|
||||
if(istype(I, /obj/item/stack/sheet/lessergem))
|
||||
var/obj/item/stack/sheet/lessergem/LG = I
|
||||
if(lesser_gem_cost)
|
||||
var/spend = min(lesser_gem_cost, LG.amount)
|
||||
user << "<span class='notice'>You add [spend] lesser gems to \the [src]."
|
||||
lesser_gem_cost = max(0, lesser_gem_cost - spend)
|
||||
LG.use(spend)
|
||||
check_completion()
|
||||
else
|
||||
user << "<span class='notice'>\The [src] does not require any more lesser gems!"
|
||||
return
|
||||
|
||||
if(istype(I, /obj/item/stack/sheet/greatergem))
|
||||
var/obj/item/stack/sheet/greatergem/GG = I //GG!
|
||||
if(greater_gem_cost)
|
||||
var/spend = min(greater_gem_cost, GG.amount)
|
||||
user << "<span class='notice'>You add [spend] greater gems to \the [src]."
|
||||
greater_gem_cost = max(0, greater_gem_cost - spend)
|
||||
GG.use(spend)
|
||||
check_completion()
|
||||
else
|
||||
user << "<span class='notice'>\The [src] does not require any more greater gems!"
|
||||
return
|
||||
|
||||
else
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/structure/divine/construction_holder/proc/check_completion()
|
||||
if(!metal_cost && !glass_cost && !lesser_gem_cost && !greater_gem_cost)
|
||||
visible_message("<span class='notice'>\The [initial(construction_result.name)] is complete!</span>")
|
||||
var/obj/structure/divine/D = new construction_result (get_turf(src))
|
||||
D.assign_deity(deity)
|
||||
qdel(src)
|
||||
|
||||
|
||||
/obj/structure/divine/construction_holder/examine(mob/user)
|
||||
..()
|
||||
|
||||
if(metal_cost || glass_cost || lesser_gem_cost || greater_gem_cost)
|
||||
user << "To finish construction it requires the following materials:"
|
||||
if(metal_cost)
|
||||
user << "[metal_cost] metal <IMG CLASS=icon SRC=icons/obj/items.dmi ICONSTATE='sheet-metal'>"
|
||||
if(glass_cost)
|
||||
user << "[glass_cost] glass <IMG CLASS=icon SRC=icons/obj/items.dmi ICONSTATE='sheet-glass'>"
|
||||
if(lesser_gem_cost)
|
||||
user << "[lesser_gem_cost] lesser gems <IMG CLASS=icon SRC=icons/obj/items.dmi ICONSTATE='sheet-lessergem'>"
|
||||
if(greater_gem_cost)
|
||||
user << "[greater_gem_cost] greater gems <IMG CLASS=icon SRC=icons/obj/items.dmi ICONSTATE='sheet-greatergem'>"
|
||||
|
||||
|
||||
/obj/structure/divine/nexus
|
||||
name = "nexus"
|
||||
desc = "It anchors a deity to this world. It radiates an unusual aura. Cultists protect this at all costs. It looks well protected from explosive shock."
|
||||
icon_state = "nexus"
|
||||
health = 500
|
||||
maxhealth = 500
|
||||
constructable = FALSE
|
||||
var/faith_regen_rate = 1
|
||||
var/list/powerpylons = list()
|
||||
|
||||
|
||||
/obj/structure/divine/nexus/ex_act()
|
||||
return
|
||||
|
||||
/obj/structure/divine/nexus/take_damage(damage, damage_type = BRUTE, sound_effect = 1)
|
||||
switch(damage_type)
|
||||
if(BRUTE)
|
||||
if(sound_effect)
|
||||
if(damage)
|
||||
playsound(loc, 'sound/weapons/smash.ogg', 50, 1)
|
||||
else
|
||||
playsound(loc, 'sound/weapons/tap.ogg', 50, 1)
|
||||
if(BURN)
|
||||
if(sound_effect)
|
||||
playsound(src.loc, 'sound/items/Welder.ogg', 100, 1)
|
||||
else
|
||||
return
|
||||
health -= damage
|
||||
if(deity)
|
||||
deity.update_health_hud()
|
||||
if(!health)
|
||||
if(!qdeleted(deity) && deity.nexus_required)
|
||||
deity << "<span class='danger'>Your nexus was destroyed. You feel yourself fading...</span>"
|
||||
qdel(deity)
|
||||
visible_message("<span class='danger'>\The [src] was destroyed!</span>")
|
||||
qdel(src)
|
||||
|
||||
|
||||
/obj/structure/divine/nexus/New()
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
|
||||
/obj/structure/divine/nexus/process()
|
||||
if(deity)
|
||||
deity.update_followers()
|
||||
deity.add_faith(faith_regen_rate + (powerpylons.len / 5) + (deity.alive_followers / 3))
|
||||
deity.max_faith = initial(deity.max_faith) + (deity.alive_followers*10) //10 followers = 100 max faith, so disaster() at around 20 followers
|
||||
deity.check_death()
|
||||
|
||||
|
||||
/obj/structure/divine/nexus/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/structure/divine/conduit
|
||||
name = "conduit"
|
||||
desc = "It allows a deity to extend their reach. Their powers are just as potent near a conduit as a nexus."
|
||||
icon_state = "conduit"
|
||||
health = 150
|
||||
maxhealth = 150
|
||||
metal_cost = 10
|
||||
glass_cost = 5
|
||||
|
||||
|
||||
/obj/structure/divine/conduit/assign_deity(mob/camera/god/new_deity, alert_old_deity = TRUE)
|
||||
if(deity)
|
||||
deity.conduits -= src
|
||||
..()
|
||||
if(deity)
|
||||
deity.conduits += src
|
||||
|
||||
/obj/structure/divine/conduit/deactivate()
|
||||
..()
|
||||
if(deity)
|
||||
deity.conduits -= src
|
||||
|
||||
/obj/structure/divine/conduit/activate()
|
||||
..()
|
||||
if(deity)
|
||||
deity.conduits += src
|
||||
|
||||
/* //No good sprites, and not enough items to make it viable yet
|
||||
/obj/structure/divine/forge
|
||||
name = "forge"
|
||||
desc = "A forge fueled by divine might, it allows the creation of sacred and powerful artifacts. It requires common materials to craft objects."
|
||||
icon_state = "forge"
|
||||
health = 250
|
||||
maxhealth = 250
|
||||
density = 0
|
||||
maxhealth = 250
|
||||
metal_cost = 40
|
||||
*/
|
||||
|
||||
/obj/structure/divine/convertaltar
|
||||
name = "conversion altar"
|
||||
desc = "An altar dedicated to a deity. Cultists can \"forcefully teach\" their non-aligned crewmembers to join their side and take up their deity."
|
||||
icon_state = "convertaltar"
|
||||
density = 0
|
||||
metal_cost = 10
|
||||
can_buckle = 1
|
||||
|
||||
|
||||
/obj/structure/divine/convertaltar/attack_hand(mob/living/user)
|
||||
..()
|
||||
if(deactivated)
|
||||
return
|
||||
var/mob/living/carbon/human/H = locate() in get_turf(src)
|
||||
if(!is_handofgod_cultist(user))
|
||||
user << "<span class='notice'>You try to use it, but unfortunately you don't know any rituals.</span>"
|
||||
return
|
||||
if(!H)
|
||||
return
|
||||
if(!H.mind)
|
||||
user << "<span class='danger'>Only sentients may serve your deity.</span>"
|
||||
return
|
||||
if((side == "red" && is_handofgod_redcultist(user) && !is_handofgod_redcultist(H)) || (side == "blue" && is_handofgod_bluecultist(user) && !is_handofgod_bluecultist(H)))
|
||||
user << "<span class='notice'>You invoke the conversion ritual.</span>"
|
||||
ticker.mode.add_hog_follower(H.mind, side)
|
||||
else
|
||||
user << "<span class='notice'>You invoke the conversion ritual.</span>"
|
||||
user << "<span class='danger'>But the altar ignores your words...</span>"
|
||||
|
||||
|
||||
/obj/structure/divine/sacrificealtar
|
||||
name = "sacrificial altar"
|
||||
desc = "An altar designed to perform blood sacrifice for a deity. The cultists performing the sacrifice will gain a powerful material to use in their forge. Sacrificing a prophet will yield even better results."
|
||||
icon_state = "sacrificealtar"
|
||||
density = 0
|
||||
metal_cost = 15
|
||||
can_buckle = 1
|
||||
|
||||
|
||||
/obj/structure/divine/sacrificealtar/attack_hand(mob/living/user)
|
||||
..()
|
||||
if(deactivated)
|
||||
return
|
||||
var/mob/living/L = locate() in get_turf(src)
|
||||
if(!is_handofgod_cultist(user))
|
||||
user << "<span class='notice'>You try to use it, but unfortunately you don't know any rituals.</span>"
|
||||
return
|
||||
if(!L)
|
||||
return
|
||||
if((side == "red" && is_handofgod_redcultist(user)) || (side == "blue" && is_handofgod_bluecultist(user)))
|
||||
if((side == "red" && is_handofgod_redcultist(L)) || (side == "blue" && is_handofgod_bluecultist(L)))
|
||||
user << "<span class='danger'>You cannot sacrifice a fellow cultist.</span>"
|
||||
return
|
||||
user << "<span class='notice'>You attempt to sacrifice [L] by invoking the sacrificial ritual.</span>"
|
||||
sacrifice(L)
|
||||
else
|
||||
user << "<span class='notice'>You attempt to sacrifice [L] by invoking the sacrificial ritual.</span>"
|
||||
user << "<span class='danger'>But the altar ignores your words...</span>"
|
||||
|
||||
|
||||
/obj/structure/divine/sacrificealtar/proc/sacrifice(mob/living/L)
|
||||
if(!L)
|
||||
L = locate() in get_turf(src)
|
||||
if(L)
|
||||
if(ismonkey(L))
|
||||
var/luck = rand(1,4)
|
||||
if(luck > 3)
|
||||
new /obj/item/stack/sheet/lessergem(get_turf(src))
|
||||
|
||||
else if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
|
||||
//Sacrifice altars can't teamkill
|
||||
if(side == "red" && is_handofgod_redcultist(H))
|
||||
return
|
||||
else if(side == "blue" && is_handofgod_bluecultist(H))
|
||||
return
|
||||
|
||||
if(is_handofgod_prophet(H))
|
||||
new /obj/item/stack/sheet/greatergem(get_turf(src))
|
||||
if(deity)
|
||||
deity.prophets_sacrificed_in_name++
|
||||
else
|
||||
new /obj/item/stack/sheet/lessergem(get_turf(src))
|
||||
|
||||
else if(isAI(L) || istype(L, /mob/living/carbon/alien/humanoid/royal/queen))
|
||||
new /obj/item/stack/sheet/greatergem(get_turf(src))
|
||||
else
|
||||
new /obj/item/stack/sheet/lessergem(get_turf(src))
|
||||
L.gib()
|
||||
|
||||
|
||||
/obj/structure/divine/healingfountain
|
||||
name = "healing fountain"
|
||||
desc = "A fountain containing the waters of life... or death, depending on where your allegiances lie."
|
||||
icon_state = "fountain"
|
||||
metal_cost = 10
|
||||
glass_cost = 5
|
||||
autocolours = FALSE
|
||||
var/time_between_uses = 1800
|
||||
var/last_process = 0
|
||||
var/cult_only = TRUE
|
||||
|
||||
/obj/structure/divine/healingfountain/anyone
|
||||
desc = "A fountain containing the waters of life."
|
||||
cult_only = FALSE
|
||||
|
||||
/obj/structure/divine/healingfountain/attack_hand(mob/living/user)
|
||||
if(deactivated)
|
||||
return
|
||||
if(last_process + time_between_uses > world.time)
|
||||
user << "<span class='notice'>The fountain appears to be empty.</span>"
|
||||
return
|
||||
last_process = world.time
|
||||
if(!is_handofgod_cultist(user) && cult_only)
|
||||
user << "<span class='danger'><B>The water burns!</b></spam>"
|
||||
user.reagents.add_reagent("hell_water",20)
|
||||
else
|
||||
user << "<span class='notice'>The water feels warm and soothing as you touch it. The fountain immediately dries up shortly afterwards.</span>"
|
||||
user.reagents.add_reagent("godblood",20)
|
||||
update_icons()
|
||||
addtimer(src, "update_icons", time_between_uses)
|
||||
|
||||
|
||||
/obj/structure/divine/healingfountain/update_icons()
|
||||
if(last_process + time_between_uses > world.time)
|
||||
icon_state = "fountain"
|
||||
else
|
||||
icon_state = "fountain-[side]"
|
||||
|
||||
|
||||
/obj/structure/divine/powerpylon
|
||||
name = "power pylon"
|
||||
desc = "A pylon which increases the deity's rate it can influence the world."
|
||||
icon_state = "powerpylon"
|
||||
density = 1
|
||||
health = 30
|
||||
maxhealth = 30
|
||||
metal_cost = 5
|
||||
glass_cost = 15
|
||||
|
||||
|
||||
/obj/structure/divine/powerpylon/New()
|
||||
..()
|
||||
if(deity && deity.god_nexus)
|
||||
deity.god_nexus.powerpylons += src
|
||||
|
||||
|
||||
/obj/structure/divine/powerpylon/Destroy()
|
||||
if(deity && deity.god_nexus)
|
||||
deity.god_nexus.powerpylons -= src
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/structure/divine/powerpylon/deactivate()
|
||||
..()
|
||||
if(deity)
|
||||
deity.god_nexus.powerpylons -= src
|
||||
|
||||
/obj/structure/divine/powerpylon/activate()
|
||||
..()
|
||||
if(deity)
|
||||
deity.god_nexus.powerpylons += src
|
||||
|
||||
/obj/structure/divine/defensepylon
|
||||
name = "defense pylon"
|
||||
desc = "A pylon which is blessed to withstand many blows, and fire strong bolts at nonbelievers. A god can toggle it."
|
||||
icon_state = "defensepylon"
|
||||
health = 150
|
||||
maxhealth = 150
|
||||
metal_cost = 25
|
||||
glass_cost = 30
|
||||
var/obj/machinery/porta_turret/defensepylon_internal_turret/pylon_gun
|
||||
|
||||
|
||||
/obj/structure/divine/defensepylon/New()
|
||||
..()
|
||||
pylon_gun = new(src)
|
||||
pylon_gun.base = src
|
||||
pylon_gun.faction = list("[side] god")
|
||||
|
||||
|
||||
/obj/structure/divine/defensepylon/Destroy()
|
||||
qdel(pylon_gun) //just in case
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/structure/divine/defensepylon/examine(mob/user)
|
||||
..()
|
||||
user << "<span class='notice'>\The [src] looks [pylon_gun.on ? "on" : "off"].</span>"
|
||||
|
||||
|
||||
/obj/structure/divine/defensepylon/assign_deity(mob/camera/god/new_deity, alert_old_deity = TRUE)
|
||||
if(..() && pylon_gun)
|
||||
pylon_gun.faction = list("[side] god")
|
||||
pylon_gun.side = side
|
||||
|
||||
/obj/structure/divine/defensepylon/attack_god(mob/camera/god/user)
|
||||
if(user.side == side)
|
||||
if(deactivated)
|
||||
user << "You need to reveal it first!"
|
||||
return
|
||||
pylon_gun.on = !pylon_gun.on
|
||||
icon_state = (pylon_gun.on) ? "defensepylon-[side]" : "defensepylon"
|
||||
|
||||
/obj/structure/divine/defensepylon/deactivate()
|
||||
..()
|
||||
pylon_gun.on = 0
|
||||
icon_state = (pylon_gun.on) ? "defensepylon-[side]" : "defensepylon"
|
||||
|
||||
/obj/structure/divine/defensepylon/activate()
|
||||
..()
|
||||
pylon_gun.on = 1
|
||||
icon_state = (pylon_gun.on) ? "defensepylon-[side]" : "defensepylon"
|
||||
|
||||
//This sits inside the defensepylon, to avoid copypasta
|
||||
/obj/machinery/porta_turret/defensepylon_internal_turret
|
||||
name = "defense pylon"
|
||||
desc = "A plyon which is blessed to withstand many blows, and fire strong bolts at nonbelievers."
|
||||
icon = 'icons/obj/hand_of_god_structures.dmi'
|
||||
installation = null
|
||||
always_up = 1
|
||||
use_power = 0
|
||||
has_cover = 0
|
||||
health = 200
|
||||
projectile = /obj/item/projectile/beam/pylon_bolt
|
||||
eprojectile = /obj/item/projectile/beam/pylon_bolt
|
||||
shot_sound = 'sound/weapons/emitter2.ogg'
|
||||
eshot_sound = 'sound/weapons/emitter2.ogg'
|
||||
base_icon_state = "defensepylon"
|
||||
active_state = ""
|
||||
off_state = ""
|
||||
faction = null
|
||||
emp_vunerable = 0
|
||||
var/side = "neutral"
|
||||
|
||||
/obj/machinery/porta_turret/defensepylon_internal_turret/setup()
|
||||
return
|
||||
|
||||
/obj/machinery/porta_turret/defensepylon_internal_turret/shootAt(atom/movable/target)
|
||||
var/obj/item/projectile/A = ..()
|
||||
if(A)
|
||||
A.color = side
|
||||
|
||||
/obj/machinery/porta_turret/defensepylon_internal_turret/assess_perp(mob/living/carbon/human/perp)
|
||||
if(perp.handcuffed) //dishonourable to kill somebody who might be converted.
|
||||
return 0
|
||||
var/badtarget = 0
|
||||
switch(side)
|
||||
if("blue")
|
||||
badtarget = is_handofgod_bluecultist(perp)
|
||||
if("red")
|
||||
badtarget = is_handofgod_redcultist(perp)
|
||||
else
|
||||
badtarget = 1
|
||||
if(badtarget)
|
||||
return 0
|
||||
return 10
|
||||
|
||||
|
||||
|
||||
/obj/item/projectile/beam/pylon_bolt
|
||||
name = "divine bolt"
|
||||
icon_state = "greyscale_bolt"
|
||||
damage = 15
|
||||
|
||||
|
||||
/obj/structure/divine/shrine
|
||||
name = "shrine"
|
||||
desc = "A shrine dedicated to a deity."
|
||||
icon_state = "shrine"
|
||||
metal_cost = 15
|
||||
glass_cost = 15
|
||||
|
||||
|
||||
/obj/structure/divine/shrine/assign_deity(mob/camera/god/new_deity, alert_old_deity = TRUE)
|
||||
if(..())
|
||||
name = "shrine to [new_deity.name]"
|
||||
desc = "A shrine dedicated to [new_deity.name]"
|
||||
|
||||
|
||||
|
||||
|
||||
//Functional, but need sprites
|
||||
/*
|
||||
/obj/structure/divine/translocator
|
||||
name = "translocator"
|
||||
desc = "A powerful structure, made with a greater gem. It allows a deity to move their nexus to where this stands"
|
||||
icon_state = "translocator"
|
||||
health = 100
|
||||
maxhealth = 100
|
||||
metal_cost = 20
|
||||
glass_cost = 20
|
||||
greater_gem_cost = 1
|
||||
|
||||
|
||||
/obj/structure/divine/lazarusaltar
|
||||
name = "lazarus altar"
|
||||
desc = "A very powerful altar capable of bringing life back to the recently deceased, made with a greater gem. It can revive anyone and will heal virtually all wounds, but they are but a shell of their former self."
|
||||
icon_state = "lazarusaltar"
|
||||
density = 0
|
||||
health = 100
|
||||
maxhealth = 100
|
||||
metal_cost = 20
|
||||
greater_gem_cost = 1
|
||||
|
||||
|
||||
/obj/structure/divine/lazarusaltar/attack_hand(mob/living/user)
|
||||
var/mob/living/L = locate() in get_turf(src)
|
||||
if(!is_handofgod_culstist(user))
|
||||
user << "<span class='notice'>You try to use it, but unfortunately you don't know any rituals.</span>"
|
||||
return
|
||||
if(!L)
|
||||
return
|
||||
|
||||
if((side == "red" && is_handofgod_redcultist(user))) || (side == "blue" && is_handofgod_bluecultist(user)))
|
||||
user << "<span class='notice'>You attempt to revive [L] by invoking the rebirth ritual.</span>"
|
||||
L.revive()
|
||||
L.adjustCloneLoss(50)
|
||||
L.adjustStaminaLoss(100)
|
||||
else
|
||||
user << "<span class='notice'>You attempt to revive [L] by invoking the rebirth ritual.</span>"
|
||||
user << "<span class='danger'>But the altar ignores your words...</span>"
|
||||
*/
|
||||
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
|
||||
/obj/structure/divine/trap
|
||||
name = "IT'S A TARP"
|
||||
desc = "stepping on me is a guaranteed bad day"
|
||||
icon_state = "trap"
|
||||
density = 0
|
||||
alpha = 30 //initially quite hidden when not "recharging"
|
||||
health = 20
|
||||
maxhealth = 20
|
||||
trap = TRUE
|
||||
autocolours = FALSE
|
||||
var/last_trigger = 0
|
||||
var/time_between_triggers = 600 //takes a minute to recharge
|
||||
|
||||
|
||||
/obj/structure/divine/trap/Crossed(atom/movable/AM)
|
||||
if(last_trigger + time_between_triggers > world.time)
|
||||
return
|
||||
alpha = initial(alpha)
|
||||
if(isliving(AM))
|
||||
var/mob/living/L = AM
|
||||
last_trigger = world.time
|
||||
alpha = 200
|
||||
trap_effect(L)
|
||||
animate(src, alpha = initial(alpha), time = time_between_triggers)
|
||||
|
||||
|
||||
/obj/structure/divine/trap/examine(mob/user)
|
||||
..()
|
||||
if(!isliving(user)) //bad ghosts, stop trying to powergame from beyond the grave
|
||||
return
|
||||
user << "You reveal a trap!"
|
||||
alpha = 200
|
||||
animate(src, alpha = initial(alpha), time = time_between_triggers)
|
||||
|
||||
|
||||
/obj/structure/divine/trap/proc/trap_effect(mob/living/L)
|
||||
return
|
||||
|
||||
|
||||
/obj/structure/divine/trap/stun
|
||||
name = "shock trap"
|
||||
desc = "A trap that will shock you, it will burn your flesh and render you immobile, You'd better avoid it."
|
||||
icon_state = "trap-shock"
|
||||
|
||||
|
||||
/obj/structure/divine/trap/stun/trap_effect(mob/living/L)
|
||||
L << "<span class='danger'><B>You are paralyzed from the intense shock!</B></span>"
|
||||
L.Weaken(5)
|
||||
var/turf/Lturf = get_turf(L)
|
||||
new /obj/effect/particle_effect/sparks/electricity(Lturf)
|
||||
new /obj/effect/particle_effect/sparks(Lturf)
|
||||
|
||||
|
||||
/obj/structure/divine/trap/fire
|
||||
name = "flame trap"
|
||||
desc = "A trap that will set you ablaze. You'd better avoid it."
|
||||
icon_state = "trap-fire"
|
||||
|
||||
|
||||
/obj/structure/divine/trap/fire/trap_effect(mob/living/L)
|
||||
L << "<span class='danger'><B>Spontaneous combustion!</B></span>"
|
||||
L.Weaken(1)
|
||||
var/turf/Lturf = get_turf(L)
|
||||
new /obj/effect/hotspot(Lturf)
|
||||
new /obj/effect/particle_effect/sparks(Lturf)
|
||||
|
||||
|
||||
/obj/structure/divine/trap/chill
|
||||
name = "frost trap"
|
||||
desc = "A trap that will chill you to the bone. You'd better avoid it."
|
||||
icon_state = "trap-frost"
|
||||
|
||||
|
||||
/obj/structure/divine/trap/chill/trap_effect(mob/living/L)
|
||||
L << "<span class='danger'><B>You're frozen solid!</B></span>"
|
||||
L.Weaken(1)
|
||||
L.bodytemperature -= 300
|
||||
new /obj/effect/particle_effect/sparks(get_turf(L))
|
||||
|
||||
|
||||
/obj/structure/divine/trap/damage
|
||||
name = "earth trap"
|
||||
desc = "A trap that will summon a small earthquake, just for you. You'd better avoid it."
|
||||
icon_state = "trap-earth"
|
||||
|
||||
|
||||
/obj/structure/divine/trap/damage/trap_effect(mob/living/L)
|
||||
L << "<span class='danger'><B>The ground quakes beneath your feet!</B></span>"
|
||||
L.Weaken(5)
|
||||
L.adjustBruteLoss(35)
|
||||
var/turf/Lturf = get_turf(L)
|
||||
new /obj/effect/particle_effect/sparks(Lturf)
|
||||
new /obj/structure/flora/rock(Lturf)
|
||||
|
||||
|
||||
/obj/structure/divine/trap/ward
|
||||
name = "divine ward"
|
||||
desc = "A divine barrier, It looks like you could destroy it with enough effort, or wait for it to dissipate..."
|
||||
icon_state = "ward"
|
||||
health = 150
|
||||
maxhealth = 150
|
||||
density = 1
|
||||
time_between_triggers = 1200 //Exists for 2 minutes
|
||||
|
||||
|
||||
/obj/structure/divine/trap/ward/New()
|
||||
..()
|
||||
QDEL_IN(src, time_between_triggers)
|
||||
@@ -0,0 +1,281 @@
|
||||
/datum/intercept_text
|
||||
var/text
|
||||
/*
|
||||
var/prob_correct_person_lower = 20
|
||||
var/prob_correct_person_higher = 80
|
||||
var/prob_correct_job_lower = 20
|
||||
var/prob_correct_job_higher = 80
|
||||
var/prob_correct_prints_lower = 20
|
||||
var/prob_correct_print_higher = 80
|
||||
var/prob_correct_objective_lower = 20
|
||||
var/prob_correct_objective_higher = 80
|
||||
*/
|
||||
var/list/org_names_1 = list(
|
||||
"Blighted",
|
||||
"Defiled",
|
||||
"Unholy",
|
||||
"Murderous",
|
||||
"Ugly",
|
||||
"French",
|
||||
"Blue",
|
||||
"Farmer"
|
||||
)
|
||||
var/list/org_names_2 = list(
|
||||
"Reapers",
|
||||
"Swarm",
|
||||
"Rogues",
|
||||
"Menace",
|
||||
"Jeff Worshippers",
|
||||
"Drunks",
|
||||
"Strikers",
|
||||
"Creed"
|
||||
)
|
||||
var/list/anomalies = list(
|
||||
"Huge electrical storm",
|
||||
"Photon emitter",
|
||||
"Meson generator",
|
||||
"Blue swirly thing"
|
||||
)
|
||||
var/list/SWF_names = list(
|
||||
"Grand Wizard",
|
||||
"His Most Unholy Master",
|
||||
"The Most Angry",
|
||||
"Bighands",
|
||||
"Tall Hat",
|
||||
"Deadly Sandals"
|
||||
)
|
||||
var/list/changeling_names = list(
|
||||
"Odo",
|
||||
"The Thing",
|
||||
"Booga",
|
||||
"The Goatee of Wrath",
|
||||
"Tam Lin",
|
||||
"Species 3157",
|
||||
"Small Prick"
|
||||
)
|
||||
|
||||
|
||||
/datum/intercept_text/proc/build(mode_type, datum/mind/correct_person)
|
||||
switch(mode_type)
|
||||
if("revolution")
|
||||
src.text = ""
|
||||
src.build_rev(correct_person)
|
||||
return src.text
|
||||
if("gang")
|
||||
src.text = ""
|
||||
src.build_gang(correct_person)
|
||||
return src.text
|
||||
if("cult")
|
||||
src.text = ""
|
||||
src.build_cult(correct_person)
|
||||
return src.text
|
||||
if("wizard")
|
||||
src.text = ""
|
||||
src.build_wizard(correct_person)
|
||||
return src.text
|
||||
if("nuke")
|
||||
src.text = ""
|
||||
src.build_nuke(correct_person)
|
||||
return src.text
|
||||
if("traitor")
|
||||
src.text = ""
|
||||
src.build_traitor(correct_person)
|
||||
return src.text
|
||||
if("changeling","traitorchan")
|
||||
src.text = ""
|
||||
src.build_changeling(correct_person)
|
||||
return src.text
|
||||
else
|
||||
return null
|
||||
|
||||
// NOTE: Commentted out was the code which showed the chance of someone being an antag. If you want to re-add it, just uncomment the code.
|
||||
|
||||
/*
|
||||
/datum/intercept_text/proc/pick_mob()
|
||||
var/list/dudes = list()
|
||||
for(var/mob/living/carbon/human/man in player_list)
|
||||
if (!man.mind) continue
|
||||
if (man.mind.assigned_role=="MODE") continue
|
||||
dudes += man
|
||||
if(dudes.len==0)
|
||||
return null
|
||||
return pick(dudes)
|
||||
|
||||
|
||||
/datum/intercept_text/proc/pick_fingerprints()
|
||||
var/mob/living/carbon/human/dude = src.pick_mob()
|
||||
//if (!dude) return pick_fingerprints() //who coded that is totally crasy or just a traitor. -- rastaf0
|
||||
if(dude)
|
||||
return num2text(md5(dude.dna.uni_identity))
|
||||
else
|
||||
return num2text(md5(num2text(rand(1,10000))))
|
||||
*/
|
||||
|
||||
/datum/intercept_text/proc/build_traitor(datum/mind/correct_person)
|
||||
var/name_1 = pick(src.org_names_1)
|
||||
var/name_2 = pick(src.org_names_2)
|
||||
|
||||
/*
|
||||
var/fingerprints
|
||||
var/traitor_name
|
||||
var/prob_right_dude = rand(prob_correct_person_lower, prob_correct_person_higher)
|
||||
if(prob(prob_right_dude) && ticker.mode == "traitor")
|
||||
if(correct_person:assigned_role=="MODE")
|
||||
traitor_name = pick_mob()
|
||||
else
|
||||
traitor_name = correct_person:current
|
||||
else if(prob(prob_right_dude))
|
||||
traitor_name = pick_mob()
|
||||
else
|
||||
fingerprints = pick_fingerprints()
|
||||
*/
|
||||
|
||||
src.text += "<BR><BR>The <B><U>[name_1] [name_2]</U></B> implied an undercover operative was acting on their behalf on the station currently."
|
||||
src.text += "It would be in your best interests to suspect everybody, as these undercover operatives could have implants which trigger them to have their memories removed until they are needed. He, or she, could even be a high ranking officer."
|
||||
src.text += "<BR><HR>"
|
||||
/*
|
||||
src.text += "After some investigation, we "
|
||||
if(traitor_name)
|
||||
src.text += "are [prob_right_dude]% sure that [traitor_name] may have been involved, and should be closely observed."
|
||||
src.text += "<BR>Note: This group are known to be untrustworthy, so do not act on this information without proper discourse."
|
||||
else
|
||||
src.text += "discovered the following set of fingerprints ([fingerprints]) on sensitive materials, and their owner should be closely observed."
|
||||
src.text += "However, these could also belong to a current Centcom employee, so do not act on this without reason."
|
||||
*/
|
||||
|
||||
|
||||
/datum/intercept_text/proc/build_cult(datum/mind/correct_person)
|
||||
var/name_1 = pick(src.org_names_1)
|
||||
var/name_2 = pick(src.org_names_2)
|
||||
/*
|
||||
var/traitor_name
|
||||
var/traitor_job
|
||||
var/prob_right_dude = rand(prob_correct_person_lower, prob_correct_person_higher)
|
||||
var/prob_right_job = rand(prob_correct_job_lower, prob_correct_job_higher)
|
||||
if(prob(prob_right_job) && is_convertable_to_cult(correct_person))
|
||||
if (correct_person)
|
||||
if(correct_person:assigned_role=="MODE")
|
||||
traitor_job = pick(get_all_jobs())
|
||||
else
|
||||
traitor_job = correct_person:assigned_role
|
||||
else
|
||||
var/list/job_tmp = get_all_jobs()
|
||||
job_tmp.Remove("Captain", "Chaplain", "AI", "Cyborg", "Security Officer", "Detective", "Head Of Security", "Head of Personnel", "Chief Engineer", "Research Director", "Chief Medical Officer")
|
||||
traitor_job = pick(job_tmp)
|
||||
if(prob(prob_right_dude) && ticker.mode == "cult")
|
||||
if(correct_person:assigned_role=="MODE")
|
||||
traitor_name = src.pick_mob()
|
||||
else
|
||||
traitor_name = correct_person:current
|
||||
else
|
||||
traitor_name = pick_mob()
|
||||
*/
|
||||
src.text += "<BR><BR>It has been brought to our attention that the <B><U>[name_1] [name_2]</U></B> have stumbled upon some dark secrets. They apparently want to spread the dangerous knowledge onto as many stations as they can."
|
||||
src.text += "Watch out for the following: praying to an unfamilar god, preaching the word of \[REDACTED\], sacrifices, magical dark power, living constructs of evil and a portal to the dimension of the underworld."
|
||||
src.text += "<BR><HR>"
|
||||
/*
|
||||
src.text += "Based on our intelligence, we are [prob_right_job]% sure that if true, someone doing the job of [traitor_job] on your station may have been converted "
|
||||
src.text += "and instilled with the idea of the flimsiness of the real world, seeking to destroy it. "
|
||||
if(prob(prob_right_dude))
|
||||
src.text += "<BR> In addition, we are [prob_right_dude]% sure that [traitor_name] may have also some in to contact with this "
|
||||
src.text += "organisation."
|
||||
src.text += "<BR>However, if this information is acted on without substantial evidence, those responsible will face severe repercussions."
|
||||
*/
|
||||
|
||||
|
||||
/datum/intercept_text/proc/build_rev(datum/mind/correct_person)
|
||||
var/name_1 = pick(src.org_names_1)
|
||||
var/name_2 = pick(src.org_names_2)
|
||||
/*
|
||||
var/traitor_name
|
||||
var/traitor_job
|
||||
var/prob_right_dude = rand(prob_correct_person_lower, prob_correct_person_higher)
|
||||
var/prob_right_job = rand(prob_correct_job_lower, prob_correct_job_higher)
|
||||
if(prob(prob_right_job) && is_convertable_to_rev(correct_person))
|
||||
if (correct_person)
|
||||
if(correct_person.assigned_role=="MODE")
|
||||
traitor_job = pick(get_all_jobs())
|
||||
else
|
||||
traitor_job = correct_person.assigned_role
|
||||
else
|
||||
var/list/job_tmp = get_all_jobs()
|
||||
job_tmp-=nonhuman_positions
|
||||
job_tmp-=command_positions
|
||||
job_tmp.Remove("Security Officer", "Detective", "Warden", "MODE")
|
||||
traitor_job = pick(job_tmp)
|
||||
if(prob(prob_right_dude) && ticker.mode.config_tag == "revolution")
|
||||
if(correct_person.assigned_role=="MODE")
|
||||
traitor_name = src.pick_mob()
|
||||
else
|
||||
traitor_name = correct_person.current
|
||||
else
|
||||
traitor_name = src.pick_mob()
|
||||
*/
|
||||
src.text += "<BR><BR>It has been brought to our attention that the <B><U>[name_1] [name_2]</U></B> are attempting to stir unrest on one of our stations in your sector."
|
||||
src.text += "Watch out for suspicious activity among the crew and make sure that all heads of staff report in periodically."
|
||||
src.text += "<BR><HR>"
|
||||
/*
|
||||
src.text += "Based on our intelligence, we are [prob_right_job]% sure that if true, someone doing the job of [traitor_job] on your station may have been brainwashed "
|
||||
src.text += "at a recent conference, and their department should be closely monitored for signs of mutiny. "
|
||||
if(prob(prob_right_dude))
|
||||
src.text += "<BR> In addition, we are [prob_right_dude]% sure that [traitor_name] may have also some in to contact with this "
|
||||
src.text += "organisation."
|
||||
src.text += "<BR>However, if this information is acted on without substantial evidence, those responsible will face severe repercussions."
|
||||
*/
|
||||
|
||||
/datum/intercept_text/proc/build_gang(datum/mind/correct_person)
|
||||
src.text += "<BR><BR>We have reports of criminal activity in close proximity to our operations within your sector."
|
||||
src.text += "Ensure law and order is maintained on the station and be on the lookout for territorial aggression within the crew."
|
||||
src.text += "In the event of a full-scale criminal takeover threat, sensitive research items are to be secured and the station evacuated ASAP."
|
||||
src.text += "<BR><HR>"
|
||||
|
||||
/datum/intercept_text/proc/build_wizard(datum/mind/correct_person)
|
||||
var/SWF_desc = pick(SWF_names)
|
||||
|
||||
src.text += "<BR><BR>The evil Space Wizards Federation have recently broke their most feared wizard, known only as <B>\"[SWF_desc]\"</B> out of space jail. "
|
||||
src.text += "He is on the run, last spotted in a system near your present location. If anybody suspicious is located aboard, please "
|
||||
src.text += "approach with EXTREME caution. Centcom also recommends that it would be wise to not inform the crew of this, due to their fearful nature."
|
||||
src.text += "Known attributes include: Brown sandals, a large blue hat, a voluptous white beard, and an inclination to cast spells."
|
||||
src.text += "<BR><HR>"
|
||||
|
||||
/datum/intercept_text/proc/build_nuke(datum/mind/correct_person)
|
||||
src.text += "<BR><BR>Centcom recently received a report of a plot to destroy one of our stations in your area. We believe the Nuclear Authentication Disc "
|
||||
src.text += "that is standard issue aboard your vessel may be a target. We recommend removal of this object, and it's storage in a safe "
|
||||
src.text += "environment. As this may cause panic among the crew, all efforts should be made to keep this information a secret from all but "
|
||||
src.text += "the most trusted crew-members."
|
||||
src.text += "<BR><HR>"
|
||||
|
||||
/datum/intercept_text/proc/build_changeling(datum/mind/correct_person)
|
||||
var/cname = pick(src.changeling_names)
|
||||
var/orgname1 = pick(src.org_names_1)
|
||||
var/orgname2 = pick(src.org_names_2)
|
||||
/*
|
||||
var/changeling_name
|
||||
var/changeling_job
|
||||
var/prob_right_dude = rand(prob_correct_person_lower, prob_correct_person_higher)
|
||||
var/prob_right_job = rand(prob_correct_job_lower, prob_correct_job_higher)
|
||||
if(prob(prob_right_job))
|
||||
if(correct_person)
|
||||
if(correct_person:assigned_role=="MODE")
|
||||
changeling_job = pick(get_all_jobs())
|
||||
else
|
||||
changeling_job = correct_person:assigned_role
|
||||
else
|
||||
changeling_job = pick(get_all_jobs())
|
||||
if(prob(prob_right_dude) && ticker.mode == "changeling")
|
||||
if(correct_person:assigned_role=="MODE")
|
||||
changeling_name = correct_person:current
|
||||
else
|
||||
changeling_name = src.pick_mob()
|
||||
else
|
||||
changeling_name = src.pick_mob()
|
||||
*/
|
||||
|
||||
src.text += "<BR><BR>We have received a report that a dangerous alien lifeform known only as <B><U>\"[cname]\"</U></B> may have infiltrated your crew. "
|
||||
/*
|
||||
src.text += "Our intelligence suggests a [prob_right_job]% chance that a [changeling_job] on board your station has been replaced by the alien. "
|
||||
src.text += "Additionally, the report indicates a [prob_right_dude]% chance that [changeling_name] may have been in contact with the lifeform at a recent social gathering. "
|
||||
*/
|
||||
src.text += "These lifeforms are associated with the <B><U>[orgname1] [orgname2]</U></B> and may be attempting to acquire sensitive materials on their behalf. "
|
||||
src.text += "Please take care not to alarm the crew, as <B><U>[cname]</U></B> may take advantage of a panic situation. Remember, they can be anybody, suspect everybody!"
|
||||
src.text += "<BR><HR>"
|
||||
@@ -0,0 +1,596 @@
|
||||
/datum/AI_Module
|
||||
var/uses = 0
|
||||
var/module_name
|
||||
var/mod_pick_name
|
||||
var/description = ""
|
||||
var/engaged = 0
|
||||
var/cost = 5
|
||||
var/one_time = 0
|
||||
|
||||
var/power_type
|
||||
|
||||
/datum/AI_Module/large/
|
||||
uses = 1
|
||||
|
||||
/datum/AI_Module/small/
|
||||
uses = 5
|
||||
|
||||
/datum/AI_Module/large/nuke_station
|
||||
module_name = "Doomsday Device"
|
||||
mod_pick_name = "nukestation"
|
||||
description = "Activate a weapon that will disintegrate all organic life on the station after a 450 second delay."
|
||||
cost = 130
|
||||
one_time = 1
|
||||
|
||||
power_type = /mob/living/silicon/ai/proc/nuke_station
|
||||
|
||||
/mob/living/silicon/ai/proc/nuke_station()
|
||||
set category = "Malfunction"
|
||||
set name = "Doomsday Device"
|
||||
|
||||
for(var/N in nuke_tiles)
|
||||
var/turf/T = N
|
||||
T.icon_state = "rcircuitanim" //This causes all blue "circuit" tiles on the map to change to animated red icon state.
|
||||
|
||||
src << "<span class='notice'>Nuclear device armed.</span>"
|
||||
priority_announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", 'sound/AI/aimalf.ogg')
|
||||
set_security_level("delta")
|
||||
SSshuttle.emergencyNoEscape = 1
|
||||
nuking = 1
|
||||
var/obj/machinery/doomsday_device/DOOM = new /obj/machinery/doomsday_device(src)
|
||||
doomsday_device = DOOM
|
||||
verbs -= /mob/living/silicon/ai/proc/nuke_station
|
||||
for(var/obj/item/weapon/pinpointer/point in pinpointer_list)
|
||||
for(var/mob/living/silicon/ai/A in ai_list)
|
||||
if((A.stat != DEAD) && A.nuking)
|
||||
point.the_disk = A //The pinpointer now tracks the AI core
|
||||
|
||||
/obj/machinery/doomsday_device
|
||||
icon = 'icons/obj/machines/nuke_terminal.dmi'
|
||||
name = "doomsday device"
|
||||
icon_state = "nuclearbomb_base"
|
||||
desc = "A weapon which disintegrates all organic life in a large area."
|
||||
anchored = 1
|
||||
density = 1
|
||||
verb_exclaim = "blares"
|
||||
var/timing = 1
|
||||
var/timer = 450
|
||||
|
||||
/obj/machinery/doomsday_device/process()
|
||||
var/turf/T = get_turf(src)
|
||||
if(!T || T.z != ZLEVEL_STATION)
|
||||
minor_announce("DOOMSDAY DEVICE OUT OF STATION RANGE, ABORTING", "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4", 1)
|
||||
SSshuttle.emergencyNoEscape = 0
|
||||
if(SSshuttle.emergency.mode == SHUTTLE_STRANDED)
|
||||
SSshuttle.emergency.mode = SHUTTLE_DOCKED
|
||||
SSshuttle.emergency.timer = world.time
|
||||
priority_announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg', "Priority")
|
||||
qdel(src)
|
||||
if(!timing)
|
||||
return
|
||||
if(timer <= 0)
|
||||
timing = 0
|
||||
detonate(T.z)
|
||||
qdel(src)
|
||||
else
|
||||
timer--
|
||||
if(!(timer%60))
|
||||
var/message = "[timer] SECONDS UNTIL DOOMSDAY DEVICE ACTIVATION!"
|
||||
minor_announce(message, "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4", 1)
|
||||
|
||||
/obj/machinery/doomsday_device/proc/detonate(z_level = 1)
|
||||
for(var/mob/M in player_list)
|
||||
M << 'sound/machines/Alarm.ogg'
|
||||
sleep(100)
|
||||
for(var/mob/living/L in mob_list)
|
||||
var/turf/T = get_turf(L)
|
||||
if(T.z != z_level)
|
||||
continue
|
||||
if(issilicon(L))
|
||||
continue
|
||||
L << "<span class='danger'><B>The blast wave from the [src] tears you atom from atom!</B></span>"
|
||||
L.dust()
|
||||
world << "<B>The AI cleansed the station of life with the doomsday device!</B>"
|
||||
ticker.force_ending = 1
|
||||
|
||||
/datum/AI_Module/large/fireproof_core
|
||||
module_name = "Core Upgrade"
|
||||
mod_pick_name = "coreup"
|
||||
description = "An upgrade to improve core resistance, making it immune to fire and heat. This effect is permanent."
|
||||
cost = 50
|
||||
one_time = 1
|
||||
|
||||
power_type = /mob/living/silicon/ai/proc/fireproof_core
|
||||
|
||||
/mob/living/silicon/ai/proc/fireproof_core()
|
||||
set category = "Malfunction"
|
||||
set name = "Fireproof Core"
|
||||
for(var/mob/living/silicon/ai/ai in player_list)
|
||||
ai.fire_res_on_core = 1
|
||||
src.verbs -= /mob/living/silicon/ai/proc/fireproof_core
|
||||
src << "<span class='notice'>Core fireproofed.</span>"
|
||||
|
||||
/datum/AI_Module/large/upgrade_turrets
|
||||
module_name = "AI Turret Upgrade"
|
||||
mod_pick_name = "turret"
|
||||
description = "Improves the power and health of all AI turrets. This effect is permanent."
|
||||
cost = 30
|
||||
one_time = 1
|
||||
|
||||
power_type = /mob/living/silicon/ai/proc/upgrade_turrets
|
||||
|
||||
/mob/living/silicon/ai/proc/upgrade_turrets()
|
||||
set category = "Malfunction"
|
||||
set name = "Upgrade Turrets"
|
||||
|
||||
if(!canUseTopic())
|
||||
return
|
||||
|
||||
src.verbs -= /mob/living/silicon/ai/proc/upgrade_turrets
|
||||
//Upgrade AI turrets around the world
|
||||
for(var/obj/machinery/porta_turret/ai/turret in machines)
|
||||
turret.health += 30
|
||||
turret.eprojectile = /obj/item/projectile/beam/laser/heavylaser //Once you see it, you will know what it means to FEAR.
|
||||
turret.eshot_sound = 'sound/weapons/lasercannonfire.ogg'
|
||||
src << "<span class='notice'>Turrets upgraded.</span>"
|
||||
|
||||
/datum/AI_Module/large/lockdown
|
||||
module_name = "Hostile Station Lockdown"
|
||||
mod_pick_name = "lockdown"
|
||||
description = "Overload the airlock, blast door and fire control networks, locking them down. Caution! This command also electrifies all airlocks. The networks will automatically reset after 90 seconds."
|
||||
cost = 30
|
||||
one_time = 1
|
||||
|
||||
power_type = /mob/living/silicon/ai/proc/lockdown
|
||||
|
||||
/mob/living/silicon/ai/proc/lockdown()
|
||||
set category = "Malfunction"
|
||||
set name = "Initiate Hostile Lockdown"
|
||||
|
||||
if(!canUseTopic())
|
||||
return
|
||||
|
||||
for(var/obj/machinery/door/D in airlocks)
|
||||
if(D.z != ZLEVEL_STATION)
|
||||
continue
|
||||
addtimer(D, "hostile_lockdown", 0, FALSE, src)
|
||||
addtimer(D, "disable_lockdown", 900)
|
||||
|
||||
var/obj/machinery/computer/communications/C = locate() in machines
|
||||
if(C)
|
||||
C.post_status("alert", "lockdown")
|
||||
|
||||
verbs -= /mob/living/silicon/ai/proc/lockdown
|
||||
minor_announce("Hostile runtime detected in door controllers. Isolation Lockdown protocols are now in effect. Please remain calm.","Network Alert:", 1)
|
||||
src << "<span class = 'warning'>Lockdown Initiated. Network reset in 90 seconds.</span>"
|
||||
addtimer(GLOBAL_PROC, "minor_announce", 900, FALSE,
|
||||
"Automatic system reboot complete. Have a secure day.",
|
||||
"Network reset:")
|
||||
|
||||
/datum/AI_Module/large/destroy_rcd
|
||||
module_name = "Destroy RCDs"
|
||||
mod_pick_name = "rcd"
|
||||
description = "Send a specialised pulse to detonate all hand-held and exosuit Rapid Cconstruction Devices on the station."
|
||||
cost = 25
|
||||
one_time = 1
|
||||
|
||||
power_type = /mob/living/silicon/ai/proc/disable_rcd
|
||||
|
||||
/mob/living/silicon/ai/proc/disable_rcd()
|
||||
set category = "Malfunction"
|
||||
set name = "Destroy RCDs"
|
||||
set desc = "Detonate all RCDs on the station, while sparing onboard cyborg RCDs."
|
||||
|
||||
if(!canUseTopic() || malf_cooldown)
|
||||
return
|
||||
|
||||
for(var/I in rcd_list)
|
||||
if(!istype(I, /obj/item/weapon/rcd/borg)) //Ensures that cyborg RCDs are spared.
|
||||
var/obj/item/weapon/rcd/RCD = I
|
||||
RCD.detonate_pulse()
|
||||
|
||||
src << "<span class='warning'>RCD detonation pulse emitted.</span>"
|
||||
malf_cooldown = 1
|
||||
spawn(100)
|
||||
malf_cooldown = 0
|
||||
|
||||
/datum/AI_Module/large/mecha_domination
|
||||
module_name = "Viral Mech Domination"
|
||||
mod_pick_name = "mechjack"
|
||||
description = "Hack into a mech's onboard computer, shunting all processes into it and ejecting any occupants. Once uploaded to the mech, it is impossible to leave.\
|
||||
Do not allow the mech to leave the station's vicinity or allow it to be destroyed."
|
||||
cost = 30
|
||||
one_time = 1
|
||||
|
||||
power_type = /mob/living/silicon/ai/proc/mech_takeover
|
||||
|
||||
/mob/living/silicon/ai/proc/mech_takeover()
|
||||
set name = "Compile Mecha Virus"
|
||||
set category = "Malfunction"
|
||||
set desc = "Target a mech by clicking it. Click the appropriate command when ready."
|
||||
if(stat)
|
||||
return
|
||||
can_dominate_mechs = 1 //Yep. This is all it does. Honk!
|
||||
src << "Virus package compiled. Select a target mech at any time. <b>You must remain on the station at all times. Loss of signal will result in total system lockout.</b>"
|
||||
verbs -= /mob/living/silicon/ai/proc/mech_takeover
|
||||
|
||||
/datum/AI_Module/large/break_fire_alarms
|
||||
module_name = "Thermal Sensor Override"
|
||||
mod_pick_name = "burnpigs"
|
||||
description = "Gives you the ability to override the thermal sensors on all fire alarms. This will remove their ability to scan for fire and thus their ability to alert. \
|
||||
Anyone can check the fire alarm's interface and may be tipped off by its status."
|
||||
one_time = 1
|
||||
cost = 25
|
||||
|
||||
power_type = /mob/living/silicon/ai/proc/break_fire_alarms
|
||||
|
||||
/mob/living/silicon/ai/proc/break_fire_alarms()
|
||||
set name = "Override Thermal Sensors"
|
||||
set category = "Malfunction"
|
||||
|
||||
if(!canUseTopic())
|
||||
return
|
||||
|
||||
for(var/obj/machinery/firealarm/F in machines)
|
||||
if(F.z != ZLEVEL_STATION)
|
||||
continue
|
||||
F.emagged = 1
|
||||
src << "<span class='notice'>All thermal sensors on the station have been disabled. Fire alerts will no longer be recognized.</span>"
|
||||
src.verbs -= /mob/living/silicon/ai/proc/break_fire_alarms
|
||||
|
||||
/datum/AI_Module/large/break_air_alarms
|
||||
module_name = "Air Alarm Safety Override"
|
||||
mod_pick_name = "allow_flooding"
|
||||
description = "Gives you the ability to disable safeties on all air alarms. This will allow you to use the environmental mode Flood, which disables scrubbers as well as pressure checks on vents. \
|
||||
Anyone can check the air alarm's interface and may be tipped off by their nonfunctionality."
|
||||
one_time = 1
|
||||
cost = 50
|
||||
|
||||
power_type = /mob/living/silicon/ai/proc/break_air_alarms
|
||||
|
||||
/mob/living/silicon/ai/proc/break_air_alarms()
|
||||
set name = "Disable Air Alarm Safeties"
|
||||
set category = "Malfunction"
|
||||
|
||||
if(!canUseTopic())
|
||||
return
|
||||
|
||||
for(var/obj/machinery/airalarm/AA in machines)
|
||||
if(AA.z != ZLEVEL_STATION)
|
||||
continue
|
||||
AA.emagged = 1
|
||||
src << "<span class='notice'>All air alarm safeties on the station have been overriden. Air alarms may now use the Flood environmental mode."
|
||||
src.verbs -= /mob/living/silicon/ai/proc/break_air_alarms
|
||||
|
||||
/datum/AI_Module/small/overload_machine
|
||||
module_name = "Machine Overload"
|
||||
mod_pick_name = "overload"
|
||||
description = "Overloads an electrical machine, causing a small explosion. 2 uses."
|
||||
uses = 2
|
||||
cost = 20
|
||||
|
||||
power_type = /mob/living/silicon/ai/proc/overload_machine
|
||||
|
||||
/mob/living/silicon/ai/proc/overload_machine(obj/machinery/M in machines)
|
||||
set name = "Overload Machine"
|
||||
set category = "Malfunction"
|
||||
|
||||
if(!canUseTopic())
|
||||
return
|
||||
|
||||
if (istype(M, /obj/machinery))
|
||||
for(var/datum/AI_Module/small/overload_machine/overload in current_modules)
|
||||
if(overload.uses > 0)
|
||||
overload.uses --
|
||||
audible_message("<span class='italics'>You hear a loud electrical buzzing sound!</span>")
|
||||
src << "<span class='warning'>Overloading machine circuitry...</span>"
|
||||
spawn(50)
|
||||
if(M)
|
||||
explosion(get_turf(M), 0,1,1,0)
|
||||
qdel(M)
|
||||
else src << "<span class='notice'>Out of uses.</span>"
|
||||
else src << "<span class='notice'>That's not a machine.</span>"
|
||||
|
||||
/datum/AI_Module/small/override_machine
|
||||
module_name = "Machine Override"
|
||||
mod_pick_name = "override"
|
||||
description = "Overrides a machine's programming, causing it to rise up and attack everyone except other machines. 4 uses."
|
||||
uses = 4
|
||||
cost = 30
|
||||
|
||||
power_type = /mob/living/silicon/ai/proc/override_machine
|
||||
|
||||
|
||||
/mob/living/silicon/ai/proc/override_machine(obj/machinery/M in machines)
|
||||
set name = "Override Machine"
|
||||
set category = "Malfunction"
|
||||
|
||||
if(!canUseTopic())
|
||||
return
|
||||
|
||||
if (istype(M, /obj/machinery))
|
||||
if(!M.can_be_overridden())
|
||||
src << "Can't override this device."
|
||||
for(var/datum/AI_Module/small/override_machine/override in current_modules)
|
||||
if(override.uses > 0)
|
||||
override.uses --
|
||||
audible_message("<span class='italics'>You hear a loud electrical buzzing sound!</span>")
|
||||
src << "<span class='warning'>Reprogramming machine behaviour...</span>"
|
||||
spawn(50)
|
||||
if(M && !qdeleted(M))
|
||||
new /mob/living/simple_animal/hostile/mimic/copy/machine(get_turf(M), M, src, 1)
|
||||
else src << "<span class='notice'>Out of uses.</span>"
|
||||
else src << "<span class='notice'>That's not a machine.</span>"
|
||||
|
||||
/datum/AI_Module/large/place_cyborg_transformer
|
||||
module_name = "Robotic Factory (Removes Shunting)"
|
||||
mod_pick_name = "cyborgtransformer"
|
||||
description = "Build a machine anywhere, using expensive nanomachines, that can convert a living human into a loyal cyborg slave when placed inside."
|
||||
cost = 100
|
||||
power_type = /mob/living/silicon/ai/proc/place_transformer
|
||||
var/list/turfOverlays = list()
|
||||
|
||||
/datum/AI_Module/large/place_cyborg_transformer/New()
|
||||
for(var/i=0;i<3;i++)
|
||||
var/image/I = image("icon"='icons/turf/overlays.dmi')
|
||||
turfOverlays += I
|
||||
..()
|
||||
|
||||
/mob/living/silicon/ai/proc/place_transformer()
|
||||
set name = "Place Robotic Factory"
|
||||
set category = "Malfunction"
|
||||
if(!canPlaceTransformer())
|
||||
return
|
||||
var/sure = alert(src, "Are you sure you want to place the machine here?", "Are you sure?", "Yes", "No")
|
||||
if(sure == "Yes")
|
||||
if(!canPlaceTransformer())
|
||||
return
|
||||
var/turf/T = get_turf(eyeobj)
|
||||
new /obj/machinery/transformer/conveyor(T)
|
||||
playsound(T, 'sound/effects/phasein.ogg', 100, 1)
|
||||
var/datum/AI_Module/large/place_cyborg_transformer/PCT = locate() in current_modules
|
||||
PCT.uses --
|
||||
can_shunt = 0
|
||||
src << "<span class='warning'>You cannot shunt anymore.</span>"
|
||||
|
||||
/mob/living/silicon/ai/proc/canPlaceTransformer()
|
||||
if(!eyeobj || !isturf(src.loc) || !canUseTopic())
|
||||
return
|
||||
var/datum/AI_Module/large/place_cyborg_transformer/PCT = locate() in current_modules
|
||||
if(!PCT || PCT.uses < 1)
|
||||
alert(src, "Out of uses.")
|
||||
return
|
||||
var/turf/middle = get_turf(eyeobj)
|
||||
var/list/turfs = list(middle, locate(middle.x - 1, middle.y, middle.z), locate(middle.x + 1, middle.y, middle.z))
|
||||
var/alert_msg = "There isn't enough room. Make sure you are placing the machine in a clear area and on a floor."
|
||||
var/success = 1
|
||||
if(turfs.len == 3)
|
||||
for(var/n=1;n<4,n++)
|
||||
var/fail
|
||||
var/turf/T = turfs[n]
|
||||
if(!istype(T, /turf/open/floor))
|
||||
fail = 1
|
||||
var/datum/camerachunk/C = cameranet.getCameraChunk(T.x, T.y, T.z)
|
||||
if(!C.visibleTurfs[T])
|
||||
alert_msg = "We cannot get camera vision of this location."
|
||||
fail = 1
|
||||
for(var/atom/movable/AM in T.contents)
|
||||
if(AM.density)
|
||||
fail = 1
|
||||
var/image/I = PCT.turfOverlays[n]
|
||||
I.loc = T
|
||||
client.images += I
|
||||
if(fail)
|
||||
success = 0
|
||||
I.icon_state = "redOverlay"
|
||||
else
|
||||
I.icon_state = "greenOverlay"
|
||||
spawn(30)
|
||||
if(client && (I.loc == T))
|
||||
client.images -= I
|
||||
if(success)
|
||||
return 1
|
||||
alert(src, alert_msg)
|
||||
|
||||
/datum/AI_Module/small/blackout
|
||||
module_name = "Blackout"
|
||||
mod_pick_name = "blackout"
|
||||
description = "Attempts to overload the lighting circuits on the station, destroying some bulbs. 3 uses."
|
||||
uses = 3
|
||||
cost = 15
|
||||
|
||||
power_type = /mob/living/silicon/ai/proc/blackout
|
||||
|
||||
/mob/living/silicon/ai/proc/blackout()
|
||||
set category = "Malfunction"
|
||||
set name = "Blackout"
|
||||
|
||||
if(!canUseTopic())
|
||||
return
|
||||
|
||||
for(var/datum/AI_Module/small/blackout/blackout in current_modules)
|
||||
if(blackout.uses > 0)
|
||||
blackout.uses --
|
||||
for(var/obj/machinery/power/apc/apc in machines)
|
||||
if(prob(30*apc.overload))
|
||||
apc.overload_lighting()
|
||||
else apc.overload++
|
||||
src << "<span class='notice'>Overcurrent applied to the powernet.</span>"
|
||||
else src << "<span class='notice'>Out of uses.</span>"
|
||||
|
||||
/datum/AI_Module/small/reactivate_cameras
|
||||
module_name = "Reactivate Camera Network"
|
||||
mod_pick_name = "recam"
|
||||
description = "Runs a network-wide diagnostic on the camera network, resetting focus and re-routing power to failed cameras. Can be used to repair up to 30 cameras."
|
||||
uses = 30
|
||||
cost = 10
|
||||
one_time = 1
|
||||
|
||||
power_type = /mob/living/silicon/ai/proc/reactivate_cameras
|
||||
|
||||
/mob/living/silicon/ai/proc/reactivate_cameras()
|
||||
set name = "Reactivate Cameranet"
|
||||
set category = "Malfunction"
|
||||
|
||||
if(!canUseTopic() || malf_cooldown)
|
||||
return
|
||||
var/fixedcams = 0 //Tells the AI how many cams it fixed. Stats are fun.
|
||||
|
||||
for(var/datum/AI_Module/small/reactivate_cameras/camera in current_modules)
|
||||
for(var/obj/machinery/camera/C in cameranet.cameras)
|
||||
var/initial_range = initial(C.view_range) //To prevent calling the proc twice
|
||||
if(camera.uses > 0)
|
||||
if(!C.status)
|
||||
C.toggle_cam(src, 0) //Reactivates the camera based on status. Badly named proc.
|
||||
fixedcams++
|
||||
camera.uses--
|
||||
if(C.view_range != initial_range)
|
||||
C.view_range = initial_range //Fixes cameras with bad focus.
|
||||
camera.uses--
|
||||
fixedcams++
|
||||
//If a camera is both deactivated and has bad focus, it will cost two uses to fully fix!
|
||||
else
|
||||
src << "<span class='warning'>Out of uses.</span>"
|
||||
verbs -= /mob/living/silicon/ai/proc/reactivate_cameras //It is useless now, clean it up.
|
||||
break
|
||||
src << "<span class='notice'>Diagnostic complete! Operations completed: [fixedcams].</span>"
|
||||
|
||||
malf_cooldown = 1
|
||||
spawn(30) //Lag protection
|
||||
malf_cooldown = 0
|
||||
|
||||
/datum/AI_Module/large/upgrade_cameras
|
||||
module_name = "Upgrade Camera Network"
|
||||
mod_pick_name = "upgradecam"
|
||||
description = "Install broad-spectrum scanning and electrical redundancy firmware to the camera network, enabling EMP-Proofing and light-amplified X-ray vision." //I <3 pointless technobabble
|
||||
//This used to have motion sensing as well, but testing quickly revealed that giving it to the whole cameranet is PURE HORROR.
|
||||
one_time = 1
|
||||
cost = 35 //Decent price for omniscience!
|
||||
|
||||
power_type = /mob/living/silicon/ai/proc/upgrade_cameras
|
||||
|
||||
/mob/living/silicon/ai/proc/upgrade_cameras()
|
||||
set name = "Upgrade Cameranet"
|
||||
set category = "Malfunction"
|
||||
|
||||
if(!canUseTopic())
|
||||
return
|
||||
|
||||
var/upgradedcams = 0
|
||||
see_override = SEE_INVISIBLE_MINIMUM //Night-vision, without which X-ray would be very limited in power.
|
||||
update_sight()
|
||||
|
||||
for(var/obj/machinery/camera/C in cameranet.cameras)
|
||||
if(C.assembly)
|
||||
var/upgraded = 0
|
||||
|
||||
if(!C.isXRay())
|
||||
C.upgradeXRay()
|
||||
//Update what it can see.
|
||||
cameranet.updateVisibility(C, 0)
|
||||
upgraded = 1
|
||||
|
||||
if(!C.isEmpProof())
|
||||
C.upgradeEmpProof()
|
||||
upgraded = 1
|
||||
|
||||
if(upgraded)
|
||||
upgradedcams++
|
||||
|
||||
src << "<span class='notice'>OTA firmware distribution complete! Cameras upgraded: [upgradedcams]. Light amplification system online.</span>"
|
||||
verbs -= /mob/living/silicon/ai/proc/upgrade_cameras
|
||||
|
||||
/datum/module_picker
|
||||
var/temp = null
|
||||
var/processing_time = 50
|
||||
var/list/possible_modules = list()
|
||||
|
||||
/datum/module_picker/New()
|
||||
for(var/type in typesof(/datum/AI_Module))
|
||||
var/datum/AI_Module/AM = new type
|
||||
if(AM.power_type != null)
|
||||
src.possible_modules += AM
|
||||
|
||||
/datum/module_picker/proc/remove_verbs(mob/living/silicon/ai/A)
|
||||
|
||||
for(var/datum/AI_Module/AM in possible_modules)
|
||||
A.verbs.Remove(AM.power_type)
|
||||
|
||||
|
||||
/datum/module_picker/proc/use(mob/user)
|
||||
var/dat
|
||||
dat = "<B>Select use of processing time: (currently #[src.processing_time] left.)</B><BR>"
|
||||
dat += "<HR>"
|
||||
dat += "<B>Install Module:</B><BR>"
|
||||
dat += "<I>The number afterwards is the amount of processing time it consumes.</I><BR>"
|
||||
for(var/datum/AI_Module/large/module in src.possible_modules)
|
||||
dat += "<A href='byond://?src=\ref[src];[module.mod_pick_name]=1'>[module.module_name]</A><A href='byond://?src=\ref[src];showdesc=[module.mod_pick_name]'>\[?\]</A> ([module.cost])<BR>"
|
||||
for(var/datum/AI_Module/small/module in src.possible_modules)
|
||||
dat += "<A href='byond://?src=\ref[src];[module.mod_pick_name]=1'>[module.module_name]</A><A href='byond://?src=\ref[src];showdesc=[module.mod_pick_name]'>\[?\]</A> ([module.cost])<BR>"
|
||||
dat += "<HR>"
|
||||
if (src.temp)
|
||||
dat += "[src.temp]"
|
||||
var/datum/browser/popup = new(user, "modpicker", "Malf Module Menu")
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
/datum/module_picker/Topic(href, href_list)
|
||||
..()
|
||||
|
||||
if(!isAI(usr))
|
||||
return
|
||||
var/mob/living/silicon/ai/A = usr
|
||||
|
||||
if(A.stat == DEAD)
|
||||
A <<"You are already dead!" //Omae Wa Mou Shindeiru
|
||||
return
|
||||
|
||||
for(var/datum/AI_Module/AM in possible_modules)
|
||||
if (href_list[AM.mod_pick_name])
|
||||
|
||||
// Cost check
|
||||
if(AM.cost > src.processing_time)
|
||||
temp = "You cannot afford this module."
|
||||
break
|
||||
|
||||
// Add new uses if we can, and it is allowed.
|
||||
var/datum/AI_Module/already_AM = locate(AM.type) in A.current_modules
|
||||
if(already_AM)
|
||||
if(!AM.one_time)
|
||||
already_AM.uses += AM.uses
|
||||
src.processing_time -= AM.cost
|
||||
temp = "Additional use added to [already_AM.module_name]"
|
||||
break
|
||||
else
|
||||
temp = "This module is only needed once."
|
||||
break
|
||||
|
||||
// Give the power and take away the money.
|
||||
A.view_core() //A BYOND bug requires you to be viewing your core before your verbs update
|
||||
A.verbs += AM.power_type
|
||||
A.current_modules += new AM.type
|
||||
temp = AM.description
|
||||
src.processing_time -= AM.cost
|
||||
|
||||
if(href_list["showdesc"])
|
||||
if(AM.mod_pick_name == href_list["showdesc"])
|
||||
temp = AM.description
|
||||
src.use(usr)
|
||||
|
||||
/datum/AI_Module/large/eavesdrop
|
||||
module_name = "Enhanced Surveillance"
|
||||
mod_pick_name = "eavesdrop"
|
||||
description = "Via a combination of hidden microphones and lip reading software, you are able to use your cameras to listen in on conversations."
|
||||
cost = 30
|
||||
one_time = 1
|
||||
|
||||
power_type = /mob/living/silicon/ai/proc/surveillance
|
||||
|
||||
/mob/living/silicon/ai/proc/surveillance()
|
||||
set category = "Malfunction"
|
||||
set name = "Enhanced Surveillance"
|
||||
|
||||
if(eyeobj)
|
||||
eyeobj.relay_speech = TRUE
|
||||
src << "<span class='notice'>OTA firmware distribution complete! Cameras upgraded: Enhanced surveillance package online.</span>"
|
||||
verbs -= /mob/living/silicon/ai/proc/surveillance
|
||||
@@ -0,0 +1,59 @@
|
||||
/datum/game_mode/meteor
|
||||
name = "meteor"
|
||||
config_tag = "meteor"
|
||||
var/meteordelay = 2000
|
||||
var/nometeors = 0
|
||||
var/rampupdelta = 5
|
||||
required_players = 0
|
||||
|
||||
|
||||
/datum/game_mode/meteor/announce()
|
||||
world << "<B>The current game mode is - Meteor!</B>"
|
||||
world << "<B>The space station has been stuck in a major meteor shower. You must escape from the station or at least live.</B>"
|
||||
|
||||
|
||||
/datum/game_mode/meteor/process()
|
||||
if(nometeors || meteordelay > world.time - round_start_time)
|
||||
return
|
||||
|
||||
var/list/wavetype = meteors_normal
|
||||
var/meteorminutes = (world.time - round_start_time - meteordelay) / 10 / 60
|
||||
|
||||
|
||||
if (prob(meteorminutes))
|
||||
wavetype = meteors_threatening
|
||||
|
||||
if (prob(meteorminutes/2))
|
||||
wavetype = meteors_catastrophic
|
||||
|
||||
var/ramp_up_final = Clamp(round(meteorminutes/rampupdelta), 1, 10)
|
||||
|
||||
spawn_meteors(ramp_up_final, wavetype)
|
||||
|
||||
|
||||
/datum/game_mode/meteor/declare_completion()
|
||||
var/text
|
||||
var/survivors = 0
|
||||
|
||||
for(var/mob/living/player in player_list)
|
||||
if(player.stat != DEAD)
|
||||
++survivors
|
||||
|
||||
if(player.onCentcom())
|
||||
text += "<br><b><font size=2>[player.real_name] escaped to the safety of Centcom.</font></b>"
|
||||
else if(player.onSyndieBase())
|
||||
text += "<br><b><font size=2>[player.real_name] escaped to the (relative) safety of Syndicate Space.</font></b>"
|
||||
else
|
||||
text += "<br><font size=1>[player.real_name] survived but is stranded without any hope of rescue.</font>"
|
||||
|
||||
|
||||
if(survivors)
|
||||
world << "<span class='boldnotice'>The following survived the meteor storm</span>:[text]"
|
||||
else
|
||||
world << "<span class='boldnotice'>Nobody survived the meteor storm!</span>"
|
||||
|
||||
feedback_set_details("round_end_result","end - evacuation")
|
||||
feedback_set("round_end_result",survivors)
|
||||
|
||||
..()
|
||||
return 1
|
||||
@@ -0,0 +1,334 @@
|
||||
/var/const/meteor_wave_delay = 625 //minimum wait between waves in tenths of seconds
|
||||
//set to at least 100 unless you want evarr ruining every round
|
||||
|
||||
//Meteors probability of spawning during a given wave
|
||||
/var/list/meteors_normal = list(/obj/effect/meteor/dust=3, /obj/effect/meteor/medium=8, /obj/effect/meteor/big=3, \
|
||||
/obj/effect/meteor/flaming=1, /obj/effect/meteor/irradiated=3) //for normal meteor event
|
||||
|
||||
/var/list/meteors_threatening = list(/obj/effect/meteor/medium=4, /obj/effect/meteor/big=8, \
|
||||
/obj/effect/meteor/flaming=3, /obj/effect/meteor/irradiated=3) //for threatening meteor event
|
||||
|
||||
/var/list/meteors_catastrophic = list(/obj/effect/meteor/medium=5, /obj/effect/meteor/big=75, \
|
||||
/obj/effect/meteor/flaming=10, /obj/effect/meteor/irradiated=10, /obj/effect/meteor/tunguska = 1) //for catastrophic meteor event
|
||||
|
||||
/var/list/meteorsB = list(/obj/effect/meteor/meaty=5, /obj/effect/meteor/meaty/xeno=1) //for meaty ore event
|
||||
|
||||
/var/list/meteorsC = list(/obj/effect/meteor/dust) //for space dust event
|
||||
|
||||
|
||||
///////////////////////////////
|
||||
//Meteor spawning global procs
|
||||
///////////////////////////////
|
||||
|
||||
/proc/spawn_meteors(number = 10, list/meteortypes)
|
||||
for(var/i = 0; i < number; i++)
|
||||
spawn_meteor(meteortypes)
|
||||
|
||||
/proc/spawn_meteor(list/meteortypes)
|
||||
var/turf/pickedstart
|
||||
var/turf/pickedgoal
|
||||
var/max_i = 10//number of tries to spawn meteor.
|
||||
while (!istype(pickedstart, /turf/open/space))
|
||||
var/startSide = pick(cardinal)
|
||||
pickedstart = spaceDebrisStartLoc(startSide, 1)
|
||||
pickedgoal = spaceDebrisFinishLoc(startSide, 1)
|
||||
max_i--
|
||||
if(max_i<=0)
|
||||
return
|
||||
var/Me = pickweight(meteortypes)
|
||||
var/obj/effect/meteor/M = new Me(pickedstart)
|
||||
M.dest = pickedgoal
|
||||
M.z_original = 1
|
||||
spawn(0)
|
||||
walk_towards(M, M.dest, 1)
|
||||
return
|
||||
|
||||
/proc/spaceDebrisStartLoc(startSide, Z)
|
||||
var/starty
|
||||
var/startx
|
||||
switch(startSide)
|
||||
if(1) //NORTH
|
||||
starty = world.maxy-(TRANSITIONEDGE+1)
|
||||
startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1))
|
||||
if(2) //EAST
|
||||
starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1))
|
||||
startx = world.maxx-(TRANSITIONEDGE+1)
|
||||
if(3) //SOUTH
|
||||
starty = (TRANSITIONEDGE+1)
|
||||
startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1))
|
||||
if(4) //WEST
|
||||
starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1))
|
||||
startx = (TRANSITIONEDGE+1)
|
||||
var/turf/T = locate(startx, starty, Z)
|
||||
return T
|
||||
|
||||
/proc/spaceDebrisFinishLoc(startSide, Z)
|
||||
var/endy
|
||||
var/endx
|
||||
switch(startSide)
|
||||
if(1) //NORTH
|
||||
endy = TRANSITIONEDGE
|
||||
endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE)
|
||||
if(2) //EAST
|
||||
endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE)
|
||||
endx = TRANSITIONEDGE
|
||||
if(3) //SOUTH
|
||||
endy = world.maxy-TRANSITIONEDGE
|
||||
endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE)
|
||||
if(4) //WEST
|
||||
endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE)
|
||||
endx = world.maxx-TRANSITIONEDGE
|
||||
var/turf/T = locate(endx, endy, Z)
|
||||
return T
|
||||
|
||||
///////////////////////
|
||||
//The meteor effect
|
||||
//////////////////////
|
||||
|
||||
/obj/effect/meteor
|
||||
name = "the concept of meteor"
|
||||
desc = "You should probably run instead of gawking at this."
|
||||
icon = 'icons/obj/meteor.dmi'
|
||||
icon_state = "small"
|
||||
density = 1
|
||||
anchored = 1
|
||||
var/hits = 4
|
||||
var/hitpwr = 2 //Level of ex_act to be called on hit.
|
||||
var/dest
|
||||
pass_flags = PASSTABLE
|
||||
var/heavy = 0
|
||||
var/meteorsound = 'sound/effects/meteorimpact.ogg'
|
||||
var/z_original = 1
|
||||
|
||||
var/list/meteordrop = list(/obj/item/weapon/ore/iron)
|
||||
var/dropamt = 2
|
||||
|
||||
/obj/effect/meteor/Move()
|
||||
if(z != z_original || loc == dest)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
. = ..() //process movement...
|
||||
|
||||
if(.)//.. if did move, ram the turf we get in
|
||||
var/turf/T = get_turf(loc)
|
||||
ram_turf(T)
|
||||
|
||||
if(prob(10) && !istype(T, /turf/open/space))//randomly takes a 'hit' from ramming
|
||||
get_hit()
|
||||
|
||||
return .
|
||||
|
||||
/obj/effect/meteor/Destroy()
|
||||
walk(src,0) //this cancels the walk_towards() proc
|
||||
return ..()
|
||||
|
||||
/obj/effect/meteor/New()
|
||||
..()
|
||||
SpinAnimation()
|
||||
|
||||
/obj/effect/meteor/Bump(atom/A)
|
||||
if(A)
|
||||
ram_turf(get_turf(A))
|
||||
playsound(src.loc, meteorsound, 40, 1)
|
||||
get_hit()
|
||||
|
||||
/obj/effect/meteor/proc/ram_turf(turf/T)
|
||||
//first bust whatever is in the turf
|
||||
for(var/atom/A in T)
|
||||
if(A != src)
|
||||
if(istype(A, /mob/living))
|
||||
A.visible_message("<span class='warning'>[src] slams into [A].</span>", "<span class='userdanger'>[src] slams into you!.</span>")
|
||||
A.ex_act(hitpwr)
|
||||
|
||||
//then, ram the turf if it still exists
|
||||
if(T)
|
||||
T.ex_act(hitpwr)
|
||||
|
||||
|
||||
|
||||
//process getting 'hit' by colliding with a dense object
|
||||
//or randomly when ramming turfs
|
||||
/obj/effect/meteor/proc/get_hit()
|
||||
hits--
|
||||
if(hits <= 0)
|
||||
make_debris()
|
||||
meteor_effect(heavy)
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/meteor/ex_act()
|
||||
return
|
||||
|
||||
/obj/effect/meteor/attackby(obj/item/weapon/W, mob/user, params)
|
||||
if(istype(W, /obj/item/weapon/pickaxe))
|
||||
make_debris()
|
||||
qdel(src)
|
||||
return
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/effect/meteor/proc/make_debris()
|
||||
for(var/throws = dropamt, throws > 0, throws--)
|
||||
var/thing_to_spawn = pick(meteordrop)
|
||||
new thing_to_spawn(get_turf(src))
|
||||
|
||||
/obj/effect/meteor/proc/meteor_effect(sound=1)
|
||||
if(sound)
|
||||
for(var/mob/M in player_list)
|
||||
var/turf/T = get_turf(M)
|
||||
if(!T || T.z != src.z)
|
||||
continue
|
||||
var/dist = get_dist(M.loc, src.loc)
|
||||
shake_camera(M, dist > 20 ? 2 : 4, dist > 20 ? 1 : 3)
|
||||
M.playsound_local(src.loc, meteorsound, 50, 1, get_rand_frequency(), 10)
|
||||
|
||||
///////////////////////
|
||||
//Meteor types
|
||||
///////////////////////
|
||||
|
||||
//Dust
|
||||
/obj/effect/meteor/dust
|
||||
name = "space dust"
|
||||
icon_state = "dust"
|
||||
pass_flags = PASSTABLE | PASSGRILLE
|
||||
hits = 1
|
||||
hitpwr = 3
|
||||
meteorsound = 'sound/weapons/Gunshot_smg.ogg'
|
||||
meteordrop = list(/obj/item/weapon/ore/glass)
|
||||
|
||||
//Medium-sized
|
||||
/obj/effect/meteor/medium
|
||||
name = "meteor"
|
||||
dropamt = 3
|
||||
|
||||
/obj/effect/meteor/medium/meteor_effect()
|
||||
..(heavy)
|
||||
explosion(src.loc, 0, 1, 2, 3, 0)
|
||||
|
||||
//Large-sized
|
||||
/obj/effect/meteor/big
|
||||
name = "big meteor"
|
||||
icon_state = "large"
|
||||
hits = 6
|
||||
heavy = 1
|
||||
dropamt = 4
|
||||
|
||||
/obj/effect/meteor/big/meteor_effect()
|
||||
..(heavy)
|
||||
explosion(src.loc, 1, 2, 3, 4, 0)
|
||||
|
||||
//Flaming meteor
|
||||
/obj/effect/meteor/flaming
|
||||
name = "flaming meteor"
|
||||
icon_state = "flaming"
|
||||
hits = 5
|
||||
heavy = 1
|
||||
meteorsound = 'sound/effects/bamf.ogg'
|
||||
meteordrop = list(/obj/item/weapon/ore/plasma)
|
||||
|
||||
/obj/effect/meteor/flaming/meteor_effect()
|
||||
..(heavy)
|
||||
explosion(src.loc, 1, 2, 3, 4, 0, 0, 5)
|
||||
|
||||
//Radiation meteor
|
||||
/obj/effect/meteor/irradiated
|
||||
name = "glowing meteor"
|
||||
icon_state = "glowing"
|
||||
heavy = 1
|
||||
meteordrop = list(/obj/item/weapon/ore/uranium)
|
||||
|
||||
|
||||
/obj/effect/meteor/irradiated/meteor_effect()
|
||||
..(heavy)
|
||||
explosion(src.loc, 0, 0, 4, 3, 0)
|
||||
new /obj/effect/decal/cleanable/greenglow(get_turf(src))
|
||||
radiation_pulse(get_turf(src), 2, 5, 50, 1)
|
||||
|
||||
//Meaty Ore
|
||||
/obj/effect/meteor/meaty
|
||||
name = "meaty ore"
|
||||
icon_state = "meateor"
|
||||
desc = "Just... don't think too hard about where this thing came from."
|
||||
hits = 2
|
||||
heavy = 1
|
||||
meteorsound = 'sound/effects/blobattack.ogg'
|
||||
meteordrop = list(/obj/item/weapon/reagent_containers/food/snacks/meat/slab/human, /obj/item/weapon/reagent_containers/food/snacks/meat/slab/human/mutant, /obj/item/organ/heart, /obj/item/organ/lungs, /obj/item/organ/tongue, /obj/item/organ/appendix/)
|
||||
var/meteorgibs = /obj/effect/gibspawner/generic
|
||||
|
||||
/obj/effect/meteor/meaty/New()
|
||||
for(var/obj/item/weapon/reagent_containers/food/snacks/meat/slab/human/mutant/M in meteordrop)
|
||||
meteordrop -= M
|
||||
meteordrop += pick(subtypesof(M))
|
||||
|
||||
for(var/obj/item/organ/tongue/T in meteordrop)
|
||||
meteordrop -= T
|
||||
meteordrop += pick(typesof(T))
|
||||
..()
|
||||
|
||||
/obj/effect/meteor/meaty/make_debris()
|
||||
..()
|
||||
new meteorgibs(get_turf(src))
|
||||
|
||||
|
||||
/obj/effect/meteor/meaty/ram_turf(turf/T)
|
||||
if(!istype(T, /turf/open/space))
|
||||
new /obj/effect/decal/cleanable/blood (T)
|
||||
|
||||
/obj/effect/meteor/meaty/Bump(atom/A)
|
||||
A.ex_act(hitpwr)
|
||||
get_hit()
|
||||
|
||||
//Meaty Ore Xeno edition
|
||||
/obj/effect/meteor/meaty/xeno
|
||||
color = "#5EFF00"
|
||||
meteordrop = list(/obj/item/weapon/reagent_containers/food/snacks/meat/slab/xeno, /obj/item/organ/tongue/alien)
|
||||
meteorgibs = /obj/effect/gibspawner/xeno
|
||||
|
||||
/obj/effect/meteor/meaty/xeno/New()
|
||||
meteordrop += subtypesof(/obj/item/organ/alien)
|
||||
..()
|
||||
|
||||
/obj/effect/meteor/meaty/xeno/ram_turf(turf/T)
|
||||
if(!istype(T, /turf/open/space))
|
||||
new /obj/effect/decal/cleanable/xenoblood (T)
|
||||
|
||||
//Station buster Tunguska
|
||||
/obj/effect/meteor/tunguska
|
||||
name = "tunguska meteor"
|
||||
icon_state = "flaming"
|
||||
desc = "Your life briefly passes before your eyes the moment you lay them on this monstruosity"
|
||||
hits = 30
|
||||
hitpwr = 1
|
||||
heavy = 1
|
||||
meteorsound = 'sound/effects/bamf.ogg'
|
||||
meteordrop = list(/obj/item/weapon/ore/plasma)
|
||||
|
||||
/obj/effect/meteor/tunguska/meteor_effect()
|
||||
..(heavy)
|
||||
explosion(src.loc, 5, 10, 15, 20, 0)
|
||||
|
||||
/obj/effect/meteor/tunguska/Bump()
|
||||
..()
|
||||
if(prob(20))
|
||||
explosion(src.loc,2,4,6,8)
|
||||
|
||||
//////////////////////////
|
||||
//Spookoween meteors
|
||||
/////////////////////////
|
||||
|
||||
/var/list/meteorsSPOOKY = list(/obj/effect/meteor/pumpkin)
|
||||
|
||||
/obj/effect/meteor/pumpkin
|
||||
name = "PUMPKING"
|
||||
desc = "THE PUMPKING'S COMING!"
|
||||
icon = 'icons/obj/meteor_spooky.dmi'
|
||||
icon_state = "pumpkin"
|
||||
hits = 10
|
||||
heavy = 1
|
||||
dropamt = 1
|
||||
meteordrop = list(/obj/item/clothing/head/hardhat/pumpkinhead, /obj/item/weapon/reagent_containers/food/snacks/grown/pumpkin)
|
||||
|
||||
/obj/effect/meteor/pumpkin/New()
|
||||
..()
|
||||
meteorsound = pick('sound/hallucinations/im_here1.ogg','sound/hallucinations/im_here2.ogg')
|
||||
//////////////////////////
|
||||
@@ -0,0 +1,464 @@
|
||||
/datum/game_mode
|
||||
var/abductor_teams = 0
|
||||
var/list/datum/mind/abductors = list()
|
||||
var/list/datum/mind/abductees = list()
|
||||
|
||||
/datum/game_mode/abduction
|
||||
name = "abduction"
|
||||
config_tag = "abduction"
|
||||
antag_flag = ROLE_ABDUCTOR
|
||||
recommended_enemies = 2
|
||||
required_players = 15
|
||||
var/max_teams = 4
|
||||
abductor_teams = 1
|
||||
var/list/datum/mind/scientists = list()
|
||||
var/list/datum/mind/agents = list()
|
||||
var/list/datum/objective/team_objectives = list()
|
||||
var/list/team_names = list()
|
||||
var/finished = 0
|
||||
|
||||
/datum/game_mode/abduction/announce()
|
||||
world << "<B>The current game mode is - Abduction!</B>"
|
||||
world << "There are alien <b>abductors</b> sent to [world.name] to perform nefarious experiments!"
|
||||
world << "<b>Abductors</b> - kidnap the crew and replace their organs with experimental ones."
|
||||
world << "<b>Crew</b> - don't get abducted and stop the abductors."
|
||||
|
||||
/datum/game_mode/abduction/pre_setup()
|
||||
abductor_teams = max(1, min(max_teams,round(num_players()/config.abductor_scaling_coeff)))
|
||||
var/possible_teams = max(1,round(antag_candidates.len / 2))
|
||||
abductor_teams = min(abductor_teams,possible_teams)
|
||||
|
||||
abductors.len = 2*abductor_teams
|
||||
scientists.len = abductor_teams
|
||||
agents.len = abductor_teams
|
||||
team_objectives.len = abductor_teams
|
||||
team_names.len = abductor_teams
|
||||
|
||||
for(var/i=1,i<=abductor_teams,i++)
|
||||
if(!make_abductor_team(i))
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
/datum/game_mode/abduction/proc/make_abductor_team(team_number,preset_agent=null,preset_scientist=null)
|
||||
//Team Name
|
||||
team_names[team_number] = "Mothership [pick(possible_changeling_IDs)]" //TODO Ensure unique and actual alieny names
|
||||
//Team Objective
|
||||
var/datum/objective/experiment/team_objective = new
|
||||
team_objective.team = team_number
|
||||
team_objectives[team_number] = team_objective
|
||||
//Team Members
|
||||
|
||||
if(!preset_agent || !preset_scientist)
|
||||
if(antag_candidates.len <=2)
|
||||
return 0
|
||||
|
||||
var/datum/mind/scientist
|
||||
var/datum/mind/agent
|
||||
|
||||
if(!preset_scientist)
|
||||
scientist = pick(antag_candidates)
|
||||
antag_candidates -= scientist
|
||||
else
|
||||
scientist = preset_scientist
|
||||
|
||||
if(!preset_agent)
|
||||
agent = pick(antag_candidates)
|
||||
antag_candidates -= agent
|
||||
else
|
||||
agent = preset_agent
|
||||
|
||||
|
||||
scientist.assigned_role = "abductor scientist"
|
||||
scientist.special_role = "abductor scientist"
|
||||
log_game("[scientist.key] (ckey) has been selected as an abductor team [team_number] scientist.")
|
||||
|
||||
agent.assigned_role = "abductor agent"
|
||||
agent.special_role = "abductor agent"
|
||||
log_game("[agent.key] (ckey) has been selected as an abductor team [team_number] agent.")
|
||||
|
||||
abductors |= agent
|
||||
abductors |= scientist
|
||||
scientists[team_number] = scientist
|
||||
agents[team_number] = agent
|
||||
return 1
|
||||
|
||||
/datum/game_mode/abduction/post_setup()
|
||||
//Spawn Team
|
||||
var/list/obj/effect/landmark/abductor/agent_landmarks = new
|
||||
var/list/obj/effect/landmark/abductor/scientist_landmarks = new
|
||||
agent_landmarks.len = max_teams
|
||||
scientist_landmarks.len = max_teams
|
||||
for(var/obj/effect/landmark/abductor/A in landmarks_list)
|
||||
if(istype(A,/obj/effect/landmark/abductor/agent))
|
||||
agent_landmarks[text2num(A.team)] = A
|
||||
else if(istype(A,/obj/effect/landmark/abductor/scientist))
|
||||
scientist_landmarks[text2num(A.team)] = A
|
||||
|
||||
var/datum/mind/agent
|
||||
var/obj/effect/landmark/L
|
||||
var/datum/mind/scientist
|
||||
var/team_name
|
||||
var/mob/living/carbon/human/H
|
||||
var/datum/species/abductor/S
|
||||
for(var/team_number=1,team_number<=abductor_teams,team_number++)
|
||||
team_name = team_names[team_number]
|
||||
agent = agents[team_number]
|
||||
H = agent.current
|
||||
L = agent_landmarks[team_number]
|
||||
H.loc = L.loc
|
||||
H.set_species(/datum/species/abductor)
|
||||
S = H.dna.species
|
||||
S.agent = 1
|
||||
S.team = team_number
|
||||
H.real_name = team_name + " Agent"
|
||||
equip_common(H,team_number)
|
||||
equip_agent(H,team_number)
|
||||
greet_agent(agent,team_number)
|
||||
|
||||
scientist = scientists[team_number]
|
||||
H = scientist.current
|
||||
L = scientist_landmarks[team_number]
|
||||
H.loc = L.loc
|
||||
H.set_species(/datum/species/abductor)
|
||||
S = H.dna.species
|
||||
S.scientist = 1
|
||||
S.team = team_number
|
||||
H.real_name = team_name + " Scientist"
|
||||
equip_common(H,team_number)
|
||||
equip_scientist(H,team_number)
|
||||
greet_scientist(scientist,team_number)
|
||||
return ..()
|
||||
|
||||
//Used for create antag buttons
|
||||
/datum/game_mode/abduction/proc/post_setup_team(team_number)
|
||||
var/list/obj/effect/landmark/abductor/agent_landmarks = new
|
||||
var/list/obj/effect/landmark/abductor/scientist_landmarks = new
|
||||
agent_landmarks.len = max_teams
|
||||
scientist_landmarks.len = max_teams
|
||||
for(var/obj/effect/landmark/abductor/A in landmarks_list)
|
||||
if(istype(A,/obj/effect/landmark/abductor/agent))
|
||||
agent_landmarks[text2num(A.team)] = A
|
||||
else if(istype(A,/obj/effect/landmark/abductor/scientist))
|
||||
scientist_landmarks[text2num(A.team)] = A
|
||||
|
||||
var/datum/mind/agent
|
||||
var/obj/effect/landmark/L
|
||||
var/datum/mind/scientist
|
||||
var/team_name
|
||||
var/mob/living/carbon/human/H
|
||||
var/datum/species/abductor/S
|
||||
|
||||
team_name = team_names[team_number]
|
||||
agent = agents[team_number]
|
||||
H = agent.current
|
||||
L = agent_landmarks[team_number]
|
||||
H.loc = L.loc
|
||||
H.set_species(/datum/species/abductor)
|
||||
S = H.dna.species
|
||||
S.agent = 1
|
||||
S.team = team_number
|
||||
H.real_name = team_name + " Agent"
|
||||
equip_common(H,team_number)
|
||||
equip_agent(H,team_number)
|
||||
greet_agent(agent,team_number)
|
||||
|
||||
|
||||
scientist = scientists[team_number]
|
||||
H = scientist.current
|
||||
L = scientist_landmarks[team_number]
|
||||
H.loc = L.loc
|
||||
H.set_species(/datum/species/abductor)
|
||||
S = H.dna.species
|
||||
S.scientist = 1
|
||||
S.team = team_number
|
||||
H.real_name = team_name + " Scientist"
|
||||
equip_common(H,team_number)
|
||||
equip_scientist(H,team_number)
|
||||
greet_scientist(scientist,team_number)
|
||||
|
||||
|
||||
|
||||
/datum/game_mode/abduction/proc/greet_agent(datum/mind/abductor,team_number)
|
||||
abductor.objectives += team_objectives[team_number]
|
||||
var/team_name = team_names[team_number]
|
||||
|
||||
abductor.current << "<span class='notice'>You are an agent of [team_name]!</span>"
|
||||
abductor.current << "<span class='notice'>With the help of your teammate, kidnap and experiment on station crew members!</span>"
|
||||
abductor.current << "<span class='notice'>Use your stealth technology and equipment to incapacitate humans for your scientist to retrieve.</span>"
|
||||
|
||||
var/obj_count = 1
|
||||
for(var/datum/objective/objective in abductor.objectives)
|
||||
abductor.current << "<B>Objective #[obj_count]</B>: [objective.explanation_text]"
|
||||
obj_count++
|
||||
return
|
||||
|
||||
/datum/game_mode/abduction/proc/greet_scientist(datum/mind/abductor,team_number)
|
||||
abductor.objectives += team_objectives[team_number]
|
||||
var/team_name = team_names[team_number]
|
||||
|
||||
abductor.current << "<span class='notice'>You are a scientist of [team_name]!</span>"
|
||||
abductor.current << "<span class='notice'>With the help of your teammate, kidnap and experiment on station crew members!</span>"
|
||||
abductor.current << "<span class='notice'>Use your tool and ship consoles to support the agent and retrieve human specimens.</span>"
|
||||
|
||||
var/obj_count = 1
|
||||
for(var/datum/objective/objective in abductor.objectives)
|
||||
abductor.current << "<B>Objective #[obj_count]</B>: [objective.explanation_text]"
|
||||
obj_count++
|
||||
return
|
||||
|
||||
/datum/game_mode/abduction/proc/equip_common(mob/living/carbon/human/agent,team_number)
|
||||
var/radio_freq = SYND_FREQ
|
||||
|
||||
var/obj/item/device/radio/R = new /obj/item/device/radio/headset/syndicate/alt(agent)
|
||||
R.set_frequency(radio_freq)
|
||||
agent.equip_to_slot_or_del(R, slot_ears)
|
||||
agent.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(agent), slot_shoes)
|
||||
agent.equip_to_slot_or_del(new /obj/item/clothing/under/color/grey(agent), slot_w_uniform) //they're greys gettit
|
||||
agent.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack(agent), slot_back)
|
||||
|
||||
/datum/game_mode/abduction/proc/get_team_console(team)
|
||||
var/obj/machinery/abductor/console/console
|
||||
for(var/obj/machinery/abductor/console/c in machines)
|
||||
if(c.team == team)
|
||||
console = c
|
||||
break
|
||||
return console
|
||||
|
||||
/datum/game_mode/abduction/proc/equip_agent(mob/living/carbon/human/agent,team_number)
|
||||
if(!team_number)
|
||||
var/datum/species/abductor/S = agent.dna.species
|
||||
team_number = S.team
|
||||
|
||||
var/obj/machinery/abductor/console/console = get_team_console(team_number)
|
||||
var/obj/item/clothing/suit/armor/abductor/vest/V = new /obj/item/clothing/suit/armor/abductor/vest(agent)
|
||||
if(console!=null)
|
||||
console.vest = V
|
||||
V.flags |= NODROP
|
||||
agent.equip_to_slot_or_del(V, slot_wear_suit)
|
||||
agent.equip_to_slot_or_del(new /obj/item/weapon/abductor_baton(agent), slot_belt)
|
||||
agent.equip_to_slot_or_del(new /obj/item/weapon/gun/energy/alien(agent), slot_in_backpack)
|
||||
agent.equip_to_slot_or_del(new /obj/item/device/abductor/silencer(agent), slot_in_backpack)
|
||||
agent.equip_to_slot_or_del(new /obj/item/clothing/head/helmet/abductor(agent), slot_head)
|
||||
|
||||
|
||||
/datum/game_mode/abduction/proc/equip_scientist(var/mob/living/carbon/human/scientist,var/team_number)
|
||||
if(!team_number)
|
||||
var/datum/species/abductor/S = scientist.dna.species
|
||||
team_number = S.team
|
||||
|
||||
var/obj/machinery/abductor/console/console = get_team_console(team_number)
|
||||
var/obj/item/device/abductor/gizmo/G = new /obj/item/device/abductor/gizmo(scientist)
|
||||
if(console!=null)
|
||||
console.gizmo = G
|
||||
G.console = console
|
||||
scientist.equip_to_slot_or_del(G, slot_in_backpack)
|
||||
|
||||
var/obj/item/weapon/implant/abductor/beamplant = new /obj/item/weapon/implant/abductor(scientist)
|
||||
beamplant.implant(scientist)
|
||||
|
||||
|
||||
/datum/game_mode/abduction/check_finished()
|
||||
if(!finished)
|
||||
for(var/team_number=1,team_number<=abductor_teams,team_number++)
|
||||
var/obj/machinery/abductor/console/con = get_team_console(team_number)
|
||||
var/datum/objective/objective = team_objectives[team_number]
|
||||
if (con.experiment.points >= objective.target_amount)
|
||||
SSshuttle.emergency.request(null, 0.5)
|
||||
finished = 1
|
||||
return ..()
|
||||
return ..()
|
||||
|
||||
/datum/game_mode/abduction/declare_completion()
|
||||
for(var/team_number=1,team_number<=abductor_teams,team_number++)
|
||||
var/obj/machinery/abductor/console/console = get_team_console(team_number)
|
||||
var/datum/objective/objective = team_objectives[team_number]
|
||||
var/team_name = team_names[team_number]
|
||||
if(console.experiment.points >= objective.target_amount)
|
||||
world << "<span class='greenannounce'>[team_name] team fullfilled its mission!</span>"
|
||||
else
|
||||
world << "<span class='boldannounce'>[team_name] team failed its mission.</span>"
|
||||
..()
|
||||
return 1
|
||||
|
||||
/datum/game_mode/proc/auto_declare_completion_abduction()
|
||||
var/text = ""
|
||||
if(abductors.len)
|
||||
text += "<br><span class='big'><b>The abductors were:</b></span>"
|
||||
for(var/datum/mind/abductor_mind in abductors)
|
||||
text += printplayer(abductor_mind)
|
||||
text += printobjectives(abductor_mind)
|
||||
text += "<br>"
|
||||
if(abductees.len)
|
||||
text += "<br><span class='big'><b>The abductees were:</b></span>"
|
||||
for(var/datum/mind/abductee_mind in abductees)
|
||||
text += printplayer(abductee_mind)
|
||||
text += printobjectives(abductee_mind)
|
||||
text += "<br>"
|
||||
world << text
|
||||
|
||||
//Landmarks
|
||||
// TODO: Split into seperate landmarks for prettier ships
|
||||
/obj/effect/landmark/abductor
|
||||
var/team = 1
|
||||
|
||||
/obj/effect/landmark/abductor/console/New()
|
||||
var/obj/machinery/abductor/console/c = new /obj/machinery/abductor/console(src.loc)
|
||||
c.team = team
|
||||
|
||||
spawn(5) // I'd do this properly when i got some time, temporary hack for mappers
|
||||
c.Initialize()
|
||||
qdel(src)
|
||||
|
||||
|
||||
/obj/effect/landmark/abductor/agent
|
||||
/obj/effect/landmark/abductor/scientist
|
||||
|
||||
|
||||
// OBJECTIVES
|
||||
/datum/objective/experiment
|
||||
dangerrating = 10
|
||||
target_amount = 6
|
||||
var/team
|
||||
|
||||
/datum/objective/experiment/New()
|
||||
explanation_text = "Experiment on [target_amount] humans."
|
||||
|
||||
/datum/objective/experiment/check_completion()
|
||||
var/ab_team = team
|
||||
if(owner)
|
||||
if(!owner.current || !ishuman(owner.current))
|
||||
return 0
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
if(H.dna.species.id != "abductor")
|
||||
return 0
|
||||
var/datum/species/abductor/S = H.dna.species
|
||||
ab_team = S.team
|
||||
for(var/obj/machinery/abductor/experiment/E in machines)
|
||||
if(E.team == ab_team)
|
||||
if(E.points >= target_amount)
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
return 0
|
||||
|
||||
/datum/game_mode/proc/update_abductor_icons_added(datum/mind/alien_mind)
|
||||
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_ABDUCTOR]
|
||||
hud.join_hud(alien_mind.current)
|
||||
set_antag_hud(alien_mind.current, ((alien_mind in abductors) ? "abductor" : "abductee"))
|
||||
|
||||
/datum/game_mode/proc/update_abductor_icons_removed(datum/mind/alien_mind)
|
||||
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_ABDUCTOR]
|
||||
hud.leave_hud(alien_mind.current)
|
||||
set_antag_hud(alien_mind.current, null)
|
||||
|
||||
/datum/objective/abductee
|
||||
dangerrating = 5
|
||||
completed = 1
|
||||
|
||||
/datum/objective/abductee/steal
|
||||
explanation_text = "Steal all"
|
||||
|
||||
/datum/objective/abductee/steal/New()
|
||||
var/target = pick(list("pets","lights","monkeys","fruits","shoes","bars of soap"))
|
||||
explanation_text+=" [target]."
|
||||
|
||||
/datum/objective/abductee/capture
|
||||
explanation_text = "Capture"
|
||||
|
||||
/datum/objective/abductee/capture/New()
|
||||
var/list/jobs = SSjob.occupations.Copy()
|
||||
for(var/datum/job/J in jobs)
|
||||
if(J.current_positions < 1)
|
||||
jobs -= J
|
||||
if(jobs.len > 0)
|
||||
var/datum/job/target = pick(jobs)
|
||||
explanation_text += " a [target.title]."
|
||||
else
|
||||
explanation_text += " someone."
|
||||
|
||||
/datum/objective/abductee/shuttle
|
||||
explanation_text = "You must escape the station! Get the shuttle called!"
|
||||
|
||||
/datum/objective/abductee/noclone
|
||||
explanation_text = "Don't allow anyone to be cloned."
|
||||
|
||||
/datum/objective/abductee/oxygen
|
||||
explanation_text = "The oxygen is killing them all and they don't even know it. Make sure no oxygen is on the station."
|
||||
|
||||
/datum/objective/abductee/blazeit
|
||||
explanation_text = "Your body must be improved. Ingest as many drugs as you can."
|
||||
|
||||
/datum/objective/abductee/yumyum
|
||||
explanation_text = "You are hungry. Eat as much food as you can find."
|
||||
|
||||
/datum/objective/abductee/insane
|
||||
explanation_text = "You see you see what they cannot you see the open door you seeE you SEeEe you SEe yOU seEee SHOW THEM ALL"
|
||||
|
||||
/datum/objective/abductee/cannotmove
|
||||
explanation_text = "Convince the crew that you are a paraplegic."
|
||||
|
||||
/datum/objective/abductee/deadbodies
|
||||
explanation_text = "Start a collection of corpses. Don't kill people to get these corpses."
|
||||
|
||||
/datum/objective/abductee/floors
|
||||
explanation_text = "Replace all the floor tiles with carpeting, wooden boards, or grass."
|
||||
|
||||
/datum/objective/abductee/POWERUNLIMITED
|
||||
explanation_text = "Flood the station's powernet with as much electricity as you can."
|
||||
|
||||
/datum/objective/abductee/pristine
|
||||
explanation_text = "Ensure the station is in absolutely pristine condition."
|
||||
|
||||
/datum/objective/abductee/window
|
||||
explanation_text = "Replace all normal windows with reinforced windows."
|
||||
|
||||
/datum/objective/abductee/nations
|
||||
explanation_text = "Ensure your department prospers over all else."
|
||||
|
||||
/datum/objective/abductee/abductception
|
||||
explanation_text = "You have been changed forever. Find the ones that did this to you and give them a taste of their own medicine."
|
||||
|
||||
/datum/objective/abductee/ghosts
|
||||
explanation_text = "Conduct a seance with the spirits of the afterlife."
|
||||
|
||||
/datum/objective/abductee/summon
|
||||
explanation_text = "Conduct a ritual to summon an elder god."
|
||||
|
||||
/datum/objective/abductee/machine
|
||||
explanation_text = "You are secretly an android. Interface with as many machines as you can to boost your own power."
|
||||
|
||||
/datum/objective/abductee/prevent
|
||||
explanation_text = "You have been enlightened. This knowledge must not escape. Ensure nobody else can become enlightened."
|
||||
|
||||
/datum/objective/abductee/calling
|
||||
explanation_text = "Call forth a spirit from the other side."
|
||||
|
||||
/datum/objective/abductee/calling/New()
|
||||
var/mob/dead/D = pick(dead_mob_list)
|
||||
if(D)
|
||||
explanation_text = "You know that [D] has perished. Call them from the spirit realm."
|
||||
|
||||
/datum/objective/abductee/social_experiment
|
||||
explanation_text = "This is a secret social experiment conducted by Nanotrasen. Convince the crew that this is the truth."
|
||||
|
||||
/datum/objective/abductee/vr
|
||||
explanation_text = "It's all an entirely virtual simulation within an underground vault. Convince the crew to escape the shackles of VR."
|
||||
|
||||
/datum/objective/abductee/pets
|
||||
explanation_text = "Nanotrasen is abusing the animals! Save as many as you can!"
|
||||
|
||||
/datum/objective/abductee/defect
|
||||
explanation_text = "Defect from your employer."
|
||||
|
||||
/datum/objective/abductee/promote
|
||||
explanation_text = "Climb the corporate ladder all the way to the top!"
|
||||
|
||||
/datum/objective/abductee/science
|
||||
explanation_text = "So much lies undiscovered. Look deeper into the machinations of the universe."
|
||||
|
||||
/datum/objective/abductee/build
|
||||
explanation_text = "Expand the station."
|
||||
|
||||
/datum/objective/abductee/pragnant
|
||||
explanation_text = "You are pregnant and soon due. Find a safe place to deliver your baby."
|
||||
@@ -0,0 +1,678 @@
|
||||
#define VEST_STEALTH 1
|
||||
#define VEST_COMBAT 2
|
||||
#define GIZMO_SCAN 1
|
||||
#define GIZMO_MARK 2
|
||||
|
||||
//AGENT VEST
|
||||
/obj/item/clothing/suit/armor/abductor/vest
|
||||
name = "agent vest"
|
||||
desc = "A vest outfitted with advanced stealth technology. It has two modes - combat and stealth."
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
icon_state = "vest_stealth"
|
||||
item_state = "armor"
|
||||
blood_overlay_type = "armor"
|
||||
origin_tech = "magnets=7;biotech=4;powerstorage=4;abductor=4"
|
||||
armor = list(melee = 15, bullet = 15, laser = 15, energy = 15, bomb = 15, bio = 15, rad = 15)
|
||||
actions_types = list(/datum/action/item_action/hands_free/activate)
|
||||
var/mode = VEST_STEALTH
|
||||
var/stealth_active = 0
|
||||
var/combat_cooldown = 10
|
||||
var/datum/icon_snapshot/disguise
|
||||
var/stealth_armor = list(melee = 15, bullet = 15, laser = 15, energy = 15, bomb = 15, bio = 15, rad = 15)
|
||||
var/combat_armor = list(melee = 50, bullet = 50, laser = 50, energy = 50, bomb = 50, bio = 50, rad = 50)
|
||||
|
||||
/obj/item/clothing/suit/armor/abductor/vest/proc/flip_mode()
|
||||
switch(mode)
|
||||
if(VEST_STEALTH)
|
||||
mode = VEST_COMBAT
|
||||
DeactivateStealth()
|
||||
armor = combat_armor
|
||||
icon_state = "vest_combat"
|
||||
if(VEST_COMBAT)// TO STEALTH
|
||||
mode = VEST_STEALTH
|
||||
armor = stealth_armor
|
||||
icon_state = "vest_stealth"
|
||||
if(istype(loc, /mob/living/carbon/human))
|
||||
var/mob/living/carbon/human/H = loc
|
||||
H.update_inv_wear_suit()
|
||||
for(var/X in actions)
|
||||
var/datum/action/A = X
|
||||
A.UpdateButtonIcon()
|
||||
|
||||
/obj/item/clothing/suit/armor/abductor/vest/item_action_slot_check(slot, mob/user)
|
||||
if(slot == slot_wear_suit) //we only give the mob the ability to activate the vest if he's actually wearing it.
|
||||
return 1
|
||||
|
||||
/obj/item/clothing/suit/armor/abductor/vest/proc/SetDisguise(datum/icon_snapshot/entry)
|
||||
disguise = entry
|
||||
|
||||
/obj/item/clothing/suit/armor/abductor/vest/proc/ActivateStealth()
|
||||
if(disguise == null)
|
||||
return
|
||||
stealth_active = 1
|
||||
if(istype(src.loc, /mob/living/carbon/human))
|
||||
var/mob/living/carbon/human/M = src.loc
|
||||
addtimer(GLOBAL_PROC, "anim", 0, FALSE, M.loc, M, 'icons/mob/mob.dmi',
|
||||
null, "cloak", null, M.dir)
|
||||
|
||||
M.name_override = disguise.name
|
||||
M.icon = disguise.icon
|
||||
M.icon_state = disguise.icon_state
|
||||
M.overlays = disguise.overlays
|
||||
M.update_inv_r_hand()
|
||||
M.update_inv_l_hand()
|
||||
|
||||
/obj/item/clothing/suit/armor/abductor/vest/proc/DeactivateStealth()
|
||||
if(!stealth_active)
|
||||
return
|
||||
stealth_active = 0
|
||||
if(istype(src.loc, /mob/living/carbon/human))
|
||||
var/mob/living/carbon/human/M = src.loc
|
||||
addtimer(GLOBAL_PROC, "anim", 0, FALSE, M.loc, M, 'icons/mob/mob.dmi',
|
||||
null, "uncloak", null, M.dir)
|
||||
M.name_override = null
|
||||
M.cut_overlays()
|
||||
M.regenerate_icons()
|
||||
|
||||
/obj/item/clothing/suit/armor/abductor/vest/hit_reaction()
|
||||
DeactivateStealth()
|
||||
return 0
|
||||
|
||||
/obj/item/clothing/suit/armor/abductor/vest/IsReflect()
|
||||
DeactivateStealth()
|
||||
return 0
|
||||
|
||||
/obj/item/clothing/suit/armor/abductor/vest/ui_action_click()
|
||||
switch(mode)
|
||||
if(VEST_COMBAT)
|
||||
Adrenaline()
|
||||
if(VEST_STEALTH)
|
||||
if(stealth_active)
|
||||
DeactivateStealth()
|
||||
else
|
||||
ActivateStealth()
|
||||
|
||||
/obj/item/clothing/suit/armor/abductor/vest/proc/Adrenaline()
|
||||
if(istype(src.loc, /mob/living/carbon/human))
|
||||
if(combat_cooldown != initial(combat_cooldown))
|
||||
src.loc << "<span class='warning'>Combat injection is still recharging.</span>"
|
||||
return
|
||||
var/mob/living/carbon/human/M = src.loc
|
||||
M.adjustStaminaLoss(-75)
|
||||
M.SetParalysis(0)
|
||||
M.SetStunned(0)
|
||||
M.SetWeakened(0)
|
||||
combat_cooldown = 0
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/clothing/suit/armor/abductor/vest/process()
|
||||
combat_cooldown++
|
||||
if(combat_cooldown==initial(combat_cooldown))
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/device/abductor/proc/AbductorCheck(user)
|
||||
if(isabductor(user))
|
||||
return TRUE
|
||||
user << "<span class='warning'>You can't figure how this works!</span>"
|
||||
return FALSE
|
||||
|
||||
/obj/item/device/abductor/proc/ScientistCheck(user)
|
||||
var/mob/living/carbon/human/H = user
|
||||
var/datum/species/abductor/S = H.dna.species
|
||||
return S.scientist
|
||||
|
||||
/obj/item/device/abductor/gizmo
|
||||
name = "science tool"
|
||||
desc = "A dual-mode tool for retrieving specimens and scanning appearances. Scanning can be done through cameras."
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
icon_state = "gizmo_scan"
|
||||
item_state = "silencer"
|
||||
origin_tech = "engineering=7;magnets=4;bluespace=4;abductor=3"
|
||||
var/mode = GIZMO_SCAN
|
||||
var/mob/living/marked = null
|
||||
var/obj/machinery/abductor/console/console
|
||||
|
||||
/obj/item/device/abductor/gizmo/attack_self(mob/user)
|
||||
if(!AbductorCheck(user))
|
||||
return
|
||||
if(!ScientistCheck(user))
|
||||
user << "<span class='warning'>You're not trained to use this!</span>"
|
||||
return
|
||||
if(mode == GIZMO_SCAN)
|
||||
mode = GIZMO_MARK
|
||||
icon_state = "gizmo_mark"
|
||||
else
|
||||
mode = GIZMO_SCAN
|
||||
icon_state = "gizmo_scan"
|
||||
user << "<span class='notice'>You switch the device to [mode==GIZMO_SCAN? "SCAN": "MARK"] MODE</span>"
|
||||
|
||||
/obj/item/device/abductor/gizmo/attack(mob/living/M, mob/user)
|
||||
if(!AbductorCheck(user))
|
||||
return
|
||||
if(!ScientistCheck(user))
|
||||
user << "<span class='notice'>You're not trained to use this</span>"
|
||||
return
|
||||
switch(mode)
|
||||
if(GIZMO_SCAN)
|
||||
scan(M, user)
|
||||
if(GIZMO_MARK)
|
||||
mark(M, user)
|
||||
|
||||
|
||||
/obj/item/device/abductor/gizmo/afterattack(atom/target, mob/living/user, flag, params)
|
||||
if(flag)
|
||||
return
|
||||
if(!AbductorCheck(user))
|
||||
return
|
||||
if(!ScientistCheck(user))
|
||||
user << "<span class='notice'>You're not trained to use this</span>"
|
||||
return
|
||||
switch(mode)
|
||||
if(GIZMO_SCAN)
|
||||
scan(target, user)
|
||||
if(GIZMO_MARK)
|
||||
mark(target, user)
|
||||
|
||||
/obj/item/device/abductor/gizmo/proc/scan(atom/target, mob/living/user)
|
||||
if(istype(target,/mob/living/carbon/human))
|
||||
if(console!=null)
|
||||
console.AddSnapshot(target)
|
||||
user << "<span class='notice'>You scan [target] and add them to the database.</span>"
|
||||
|
||||
/obj/item/device/abductor/gizmo/proc/mark(atom/target, mob/living/user)
|
||||
if(marked == target)
|
||||
user << "<span class='warning'>This specimen is already marked!</span>"
|
||||
return
|
||||
if(istype(target,/mob/living/carbon/human))
|
||||
if(isabductor(target))
|
||||
marked = target
|
||||
user << "<span class='notice'>You mark [target] for future retrieval.</span>"
|
||||
else
|
||||
prepare(target,user)
|
||||
else
|
||||
prepare(target,user)
|
||||
|
||||
/obj/item/device/abductor/gizmo/proc/prepare(atom/target, mob/living/user)
|
||||
if(get_dist(target,user)>1)
|
||||
user << "<span class='warning'>You need to be next to the specimen to prepare it for transport!</span>"
|
||||
return
|
||||
user << "<span class='notice'>You begin preparing [target] for transport...</span>"
|
||||
if(do_after(user, 100, target = target))
|
||||
marked = target
|
||||
user << "<span class='notice'>You finish preparing [target] for transport.</span>"
|
||||
|
||||
|
||||
/obj/item/device/abductor/silencer
|
||||
name = "abductor silencer"
|
||||
desc = "A compact device used to shut down communications equipment."
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
icon_state = "silencer"
|
||||
item_state = "gizmo"
|
||||
origin_tech = "materials=4;programming=7;abductor=3"
|
||||
|
||||
/obj/item/device/abductor/silencer/attack(mob/living/M, mob/user)
|
||||
if(!AbductorCheck(user))
|
||||
return
|
||||
radio_off(M, user)
|
||||
|
||||
/obj/item/device/abductor/silencer/afterattack(atom/target, mob/living/user, flag, params)
|
||||
if(flag)
|
||||
return
|
||||
if(!AbductorCheck(user))
|
||||
return
|
||||
radio_off(target, user)
|
||||
|
||||
/obj/item/device/abductor/silencer/proc/radio_off(atom/target, mob/living/user)
|
||||
if( !(user in (viewers(7,target))) )
|
||||
return
|
||||
|
||||
var/turf/targloc = get_turf(target)
|
||||
|
||||
var/mob/living/carbon/human/M
|
||||
for(M in view(2,targloc))
|
||||
if(M == user)
|
||||
continue
|
||||
user << "<span class='notice'>You silence [M]'s radio devices.</span>"
|
||||
radio_off_mob(M)
|
||||
|
||||
/obj/item/device/abductor/silencer/proc/radio_off_mob(mob/living/carbon/human/M)
|
||||
var/list/all_items = M.GetAllContents()
|
||||
|
||||
for(var/obj/I in all_items)
|
||||
if(istype(I,/obj/item/device/radio/))
|
||||
var/obj/item/device/radio/r = I
|
||||
r.listening = 0
|
||||
if(!istype(I,/obj/item/device/radio/headset))
|
||||
r.broadcasting = 0 //goddamned headset hacks
|
||||
|
||||
|
||||
/obj/item/weapon/implant/abductor
|
||||
name = "recall implant"
|
||||
desc = "Returns you to the mothership."
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
icon_state = "implant"
|
||||
activated = 1
|
||||
origin_tech = "materials=2;biotech=7;magnets=4;bluespace=4;abductor=5"
|
||||
var/obj/machinery/abductor/pad/home
|
||||
var/cooldown = 30
|
||||
|
||||
/obj/item/weapon/implant/abductor/activate()
|
||||
if(cooldown == initial(cooldown))
|
||||
home.Retrieve(imp_in,1)
|
||||
cooldown = 0
|
||||
START_PROCESSING(SSobj, src)
|
||||
else
|
||||
imp_in << "<span class='warning'>You must wait [30 - cooldown] seconds to use [src] again!</span>"
|
||||
|
||||
/obj/item/weapon/implant/abductor/process()
|
||||
if(cooldown < initial(cooldown))
|
||||
cooldown++
|
||||
if(cooldown == initial(cooldown))
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/weapon/implant/abductor/implant(var/mob/source, var/mob/user)
|
||||
if(..())
|
||||
var/obj/machinery/abductor/console/console
|
||||
if(ishuman(source))
|
||||
var/mob/living/carbon/human/H = source
|
||||
if(H.dna.species.id == "abductor")
|
||||
var/datum/species/abductor/S = H.dna.species
|
||||
console = get_team_console(S.team)
|
||||
home = console.pad
|
||||
|
||||
if(!home)
|
||||
console = get_team_console(pick(1, 2, 3, 4))
|
||||
home = console.pad
|
||||
return 1
|
||||
|
||||
/obj/item/weapon/implant/abductor/proc/get_team_console(var/team)
|
||||
var/obj/machinery/abductor/console/console
|
||||
for(var/obj/machinery/abductor/console/c in machines)
|
||||
if(c.team == team)
|
||||
console = c
|
||||
break
|
||||
return console
|
||||
|
||||
/obj/item/device/firing_pin/abductor
|
||||
name = "alien firing pin"
|
||||
icon_state = "firing_pin_ayy"
|
||||
desc = "This firing pin is slimy and warm; you can swear you feel it \
|
||||
constantly trying to mentally probe you."
|
||||
fail_message = "<span class='abductor'>\
|
||||
Firing error, please contact Command.</span>"
|
||||
|
||||
/obj/item/device/firing_pin/abductor/pin_auth(mob/living/user)
|
||||
. = isabductor(user)
|
||||
|
||||
/obj/item/weapon/gun/energy/alien
|
||||
name = "alien pistol"
|
||||
desc = "A complicated gun that fires bursts of high-intensity radiation."
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/declone)
|
||||
pin = /obj/item/device/firing_pin/abductor
|
||||
icon_state = "alienpistol"
|
||||
item_state = "alienpistol"
|
||||
origin_tech = "combat=4;magnets=7;powerstorage=3;abductor=3"
|
||||
trigger_guard = TRIGGER_GUARD_ALLOW_ALL
|
||||
|
||||
/obj/item/weapon/paper/abductor
|
||||
name = "Dissection Guide"
|
||||
icon_state = "alienpaper_words"
|
||||
info = {"<b>Dissection for Dummies</b><br>
|
||||
|
||||
<br>
|
||||
1.Acquire fresh specimen.<br>
|
||||
2.Put the specimen on operating table<br>
|
||||
3.Apply surgical drapes preparing for dissection<br>
|
||||
4.Apply scalpel to specimen torso<br>
|
||||
5.Retract skin from specimen's torso<br>
|
||||
6.Apply scalpel to specimen's torso<br>
|
||||
7.Search through the specimen's torso with your hands to remove any organs<br>
|
||||
8.Insert replacement gland (Retrieve one from gland storage)<br>
|
||||
9.Consider dressing the specimen back to not disturb the habitat <br>
|
||||
10.Put the specimen in the experiment machinery<br>
|
||||
11.Choose one of the machine options and follow displayed instructions<br>
|
||||
<br>
|
||||
Congratulations! You are now trained for xenobiology research!"}
|
||||
|
||||
/obj/item/weapon/paper/abductor/update_icon()
|
||||
return
|
||||
|
||||
/obj/item/weapon/paper/abductor/AltClick()
|
||||
return
|
||||
|
||||
#define BATON_STUN 0
|
||||
#define BATON_SLEEP 1
|
||||
#define BATON_CUFF 2
|
||||
#define BATON_PROBE 3
|
||||
#define BATON_MODES 4
|
||||
|
||||
/obj/item/weapon/abductor_baton
|
||||
name = "advanced baton"
|
||||
desc = "A quad-mode baton used for incapacitation and restraining of specimens."
|
||||
var/mode = BATON_STUN
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
icon_state = "wonderprodStun"
|
||||
item_state = "wonderprod"
|
||||
slot_flags = SLOT_BELT
|
||||
origin_tech = "materials=4;combat=4;biotech=7;abductor=4"
|
||||
force = 7
|
||||
w_class = 3
|
||||
actions_types = list(/datum/action/item_action/toggle_mode)
|
||||
|
||||
/obj/item/weapon/abductor_baton/proc/toggle(mob/living/user=usr)
|
||||
mode = (mode+1)%BATON_MODES
|
||||
var/txt
|
||||
switch(mode)
|
||||
if(BATON_STUN)
|
||||
txt = "stunning"
|
||||
if(BATON_SLEEP)
|
||||
txt = "sleep inducement"
|
||||
if(BATON_CUFF)
|
||||
txt = "restraining"
|
||||
if(BATON_PROBE)
|
||||
txt = "probing"
|
||||
|
||||
usr << "<span class='notice'>You switch the baton to [txt] mode.</span>"
|
||||
update_icon()
|
||||
|
||||
/obj/item/weapon/abductor_baton/update_icon()
|
||||
switch(mode)
|
||||
if(BATON_STUN)
|
||||
icon_state = "wonderprodStun"
|
||||
item_state = "wonderprodStun"
|
||||
if(BATON_SLEEP)
|
||||
icon_state = "wonderprodSleep"
|
||||
item_state = "wonderprodSleep"
|
||||
if(BATON_CUFF)
|
||||
icon_state = "wonderprodCuff"
|
||||
item_state = "wonderprodCuff"
|
||||
if(BATON_PROBE)
|
||||
icon_state = "wonderprodProbe"
|
||||
item_state = "wonderprodProbe"
|
||||
|
||||
/obj/item/weapon/abductor_baton/attack(mob/target, mob/living/user)
|
||||
if(!isabductor(user))
|
||||
return
|
||||
|
||||
if(isrobot(target))
|
||||
..()
|
||||
return
|
||||
|
||||
if(!isliving(target))
|
||||
return
|
||||
|
||||
var/mob/living/L = target
|
||||
|
||||
user.do_attack_animation(L)
|
||||
|
||||
if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
if(H.check_shields(0, "[user]'s [name]", src, MELEE_ATTACK))
|
||||
playsound(L, 'sound/weapons/Genhit.ogg', 50, 1)
|
||||
return 0
|
||||
|
||||
switch (mode)
|
||||
if(BATON_STUN)
|
||||
StunAttack(L,user)
|
||||
if(BATON_SLEEP)
|
||||
SleepAttack(L,user)
|
||||
if(BATON_CUFF)
|
||||
CuffAttack(L,user)
|
||||
if(BATON_PROBE)
|
||||
ProbeAttack(L,user)
|
||||
|
||||
/obj/item/weapon/abductor_baton/attack_self(mob/living/user)
|
||||
toggle(user)
|
||||
|
||||
/obj/item/weapon/abductor_baton/proc/StunAttack(mob/living/L,mob/living/user)
|
||||
user.lastattacked = L
|
||||
L.lastattacker = user
|
||||
|
||||
L.Stun(7)
|
||||
L.Weaken(7)
|
||||
L.apply_effect(STUTTER, 7)
|
||||
|
||||
L.visible_message("<span class='danger'>[user] has stunned [L] with [src]!</span>", \
|
||||
"<span class='userdanger'>[user] has stunned you with [src]!</span>")
|
||||
playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
|
||||
|
||||
if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
H.forcesay(hit_appends)
|
||||
|
||||
add_logs(user, L, "stunned")
|
||||
|
||||
/obj/item/weapon/abductor_baton/proc/SleepAttack(mob/living/L,mob/living/user)
|
||||
if(L.stunned || L.sleeping)
|
||||
L.visible_message("<span class='danger'>[user] has induced sleep in [L] with [src]!</span>", \
|
||||
"<span class='userdanger'>You suddenly feel very drowsy!</span>")
|
||||
playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
|
||||
L.Sleeping(60)
|
||||
add_logs(user, L, "put to sleep")
|
||||
else
|
||||
L.drowsyness += 1
|
||||
user << "<span class='warning'>Sleep inducement works fully only on stunned specimens! </span>"
|
||||
L.visible_message("<span class='danger'>[user] tried to induce sleep in [L] with [src]!</span>", \
|
||||
"<span class='userdanger'>You suddenly feel drowsy!</span>")
|
||||
|
||||
/obj/item/weapon/abductor_baton/proc/CuffAttack(mob/living/L,mob/living/user)
|
||||
if(!iscarbon(L))
|
||||
return
|
||||
var/mob/living/carbon/C = L
|
||||
if(!C.handcuffed)
|
||||
if(C.get_num_arms() >= 2)
|
||||
playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2)
|
||||
C.visible_message("<span class='danger'>[user] begins restraining [C] with [src]!</span>", \
|
||||
"<span class='userdanger'>[user] begins shaping an energy field around your hands!</span>")
|
||||
if(do_mob(user, C, 30) && C.get_num_arms() >= 2)
|
||||
if(!C.handcuffed)
|
||||
C.handcuffed = new /obj/item/weapon/restraints/handcuffs/energy/used(C)
|
||||
C.update_handcuffed()
|
||||
user << "<span class='notice'>You handcuff [C].</span>"
|
||||
add_logs(user, C, "handcuffed")
|
||||
else
|
||||
user << "<span class='warning'>You fail to handcuff [C].</span>"
|
||||
else
|
||||
user << "<span class='warning'>[C] doesn't have two hands...</span>"
|
||||
|
||||
/obj/item/weapon/abductor_baton/proc/ProbeAttack(mob/living/L,mob/living/user)
|
||||
L.visible_message("<span class='danger'>[user] probes [L] with [src]!</span>", \
|
||||
"<span class='userdanger'>[user] probes you!</span>")
|
||||
|
||||
var/species = "<span class='warning'>Unknown species</span>"
|
||||
var/helptext = "<span class='warning'>Species unsuitable for experiments.</span>"
|
||||
|
||||
if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
species = "<span clas=='notice'>[H.dna.species.name]</span>"
|
||||
if(L.mind && L.mind.changeling)
|
||||
species = "<span class='warning'>Changeling lifeform</span>"
|
||||
var/obj/item/organ/gland/temp = locate() in H.internal_organs
|
||||
if(temp)
|
||||
helptext = "<span class='warning'>Experimental gland detected!</span>"
|
||||
else
|
||||
helptext = "<span class='notice'>Subject suitable for experiments.</span>"
|
||||
|
||||
user << "<span class='notice'>Probing result:</span>[species]"
|
||||
user << "[helptext]"
|
||||
|
||||
/obj/item/weapon/restraints/handcuffs/energy
|
||||
name = "hard-light energy field"
|
||||
desc = "A hard-light field restraining the hands."
|
||||
icon_state = "cuff_white" // Needs sprite
|
||||
breakouttime = 450
|
||||
trashtype = /obj/item/weapon/restraints/handcuffs/energy/used
|
||||
origin_tech = "materials=4;magnets=5;abductor=2"
|
||||
|
||||
/obj/item/weapon/restraints/handcuffs/energy/used
|
||||
desc = "energy discharge"
|
||||
flags = DROPDEL
|
||||
|
||||
/obj/item/weapon/restraints/handcuffs/energy/used/dropped(mob/user)
|
||||
user.visible_message("<span class='danger'>[user]'s [src] break in a discharge of energy!</span>", \
|
||||
"<span class='userdanger'>[user]'s [src] break in a discharge of energy!</span>")
|
||||
var/datum/effect_system/spark_spread/S = new
|
||||
S.set_up(4,0,user.loc)
|
||||
S.start()
|
||||
. = ..()
|
||||
|
||||
/obj/item/weapon/abductor_baton/examine(mob/user)
|
||||
..()
|
||||
switch(mode)
|
||||
if(BATON_STUN)
|
||||
user <<"<span class='warning'>The baton is in stun mode.</span>"
|
||||
if(BATON_SLEEP)
|
||||
user <<"<span class='warning'>The baton is in sleep inducement mode.</span>"
|
||||
if(BATON_CUFF)
|
||||
user <<"<span class='warning'>The baton is in restraining mode.</span>"
|
||||
if(BATON_PROBE)
|
||||
user << "<span class='warning'>The baton is in probing mode.</span>"
|
||||
|
||||
|
||||
/obj/item/weapon/scalpel/alien
|
||||
name = "alien scalpel"
|
||||
desc = "It's a gleaming sharp knife made out of silvery-green metal."
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
origin_tech = "materials=2;biotech=2;abductor=2"
|
||||
|
||||
/obj/item/weapon/hemostat/alien
|
||||
name = "alien hemostat"
|
||||
desc = "You've never seen this before."
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
origin_tech = "materials=2;biotech=2;abductor=2"
|
||||
|
||||
/obj/item/weapon/retractor/alien
|
||||
name = "alien retractor"
|
||||
desc = "You're not sure if you want the veil pulled back."
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
origin_tech = "materials=2;biotech=2;abductor=2"
|
||||
|
||||
/obj/item/weapon/circular_saw/alien
|
||||
name = "alien saw"
|
||||
desc = "Do the aliens also lose this, and need to find an alien hatchet?"
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
origin_tech = "materials=2;biotech=2;abductor=2"
|
||||
|
||||
/obj/item/weapon/surgicaldrill/alien
|
||||
name = "alien drill"
|
||||
desc = "Maybe alien surgeons have finally found a use for the drill."
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
origin_tech = "materials=2;biotech=2;abductor=2"
|
||||
|
||||
/obj/item/weapon/cautery/alien
|
||||
name = "alien cautery"
|
||||
desc = "Why would bloodless aliens have a tool to stop bleeding? \
|
||||
Unless..."
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
origin_tech = "materials=2;biotech=2;abductor=2"
|
||||
|
||||
/obj/item/clothing/head/helmet/abductor
|
||||
name = "agent headgear"
|
||||
desc = "Abduct with style - spiky style. Prevents digital tracking."
|
||||
icon_state = "alienhelmet"
|
||||
item_state = "alienhelmet"
|
||||
blockTracking = 1
|
||||
origin_tech = "materials=7;magnets=4;abductor=3"
|
||||
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR
|
||||
|
||||
// Operating Table / Beds / Lockers
|
||||
|
||||
/obj/structure/table/optable/abductor
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
icon_state = "bed"
|
||||
can_buckle = 1
|
||||
buckle_lying = 1
|
||||
flags = NODECONSTRUCT
|
||||
|
||||
/obj/structure/table/optable/abductor/table_destroy()
|
||||
return //can't destroy the abductor's only optable.
|
||||
|
||||
/obj/structure/bed/abductor
|
||||
name = "resting contraption"
|
||||
desc = "This looks similar to contraptions from earth. Could aliens be stealing our technology?"
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
buildstacktype = /obj/item/stack/sheet/mineral/abductor
|
||||
icon_state = "bed"
|
||||
|
||||
/obj/structure/table_frame/abductor
|
||||
name = "alien table frame"
|
||||
desc = "A sturdy table frame made from alien alloy."
|
||||
icon_state = "alien_frame"
|
||||
framestack = /obj/item/stack/sheet/mineral/abductor
|
||||
framestackamount = 1
|
||||
density = 1
|
||||
|
||||
/obj/structure/table_frame/abductor/attackby(obj/item/I, mob/user, params)
|
||||
if(istype(I, /obj/item/weapon/wrench))
|
||||
user << "<span class='notice'>You start disassembling [src]...</span>"
|
||||
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
|
||||
if(do_after(user, 30/I.toolspeed, target = src))
|
||||
playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1)
|
||||
for(var/i = 1, i <= framestackamount, i++)
|
||||
new framestack(get_turf(src))
|
||||
qdel(src)
|
||||
return
|
||||
if(istype(I, /obj/item/stack/sheet/mineral/abductor))
|
||||
var/obj/item/stack/sheet/P = I
|
||||
if(P.get_amount() < 1)
|
||||
user << "<span class='warning'>You need one alien alloy sheet to do this!</span>"
|
||||
return
|
||||
user << "<span class='notice'>You start adding [P] to [src]...</span>"
|
||||
if(do_after(user, 50, target = src))
|
||||
P.use(1)
|
||||
new /obj/structure/table/abductor(src.loc)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
/obj/structure/table/abductor
|
||||
name = "alien table"
|
||||
desc = "Advanced flat surface technology at work!"
|
||||
icon = 'icons/obj/smooth_structures/alien_table.dmi'
|
||||
icon_state = "alien_table"
|
||||
buildstack = /obj/item/stack/sheet/mineral/abductor
|
||||
framestack = /obj/item/stack/sheet/mineral/abductor
|
||||
buildstackamount = 1
|
||||
framestackamount = 1
|
||||
canSmoothWith = null
|
||||
frame = /obj/structure/table_frame/abductor
|
||||
|
||||
|
||||
/obj/structure/closet/abductor
|
||||
name = "alien locker"
|
||||
desc = "Contains secrets of the universe."
|
||||
icon_state = "abductor"
|
||||
icon_door = "abductor"
|
||||
can_weld_shut = FALSE
|
||||
material_drop = /obj/item/stack/sheet/mineral/abductor
|
||||
|
||||
/obj/structure/door_assembly/door_assembly_abductor
|
||||
name = "alien airlock assembly"
|
||||
icon = 'icons/obj/doors/airlocks/abductor/abductor_airlock.dmi'
|
||||
overlays_file = 'icons/obj/doors/airlocks/abductor/overlays.dmi'
|
||||
typetext = "abductor"
|
||||
icontext = "abductor"
|
||||
airlock_type = /obj/machinery/door/airlock/abductor
|
||||
anchored = 1
|
||||
state = 1
|
||||
|
||||
/obj/structure/door_assembly/door_assembly_abductor/attackby(obj/item/W, mob/user, params)
|
||||
if(istype(W, /obj/item/weapon/weldingtool) && !anchored )
|
||||
var/obj/item/weapon/weldingtool/WT = W
|
||||
if(WT.remove_fuel(0,user))
|
||||
user.visible_message("<span class='warning'>[user] disassembles the airlock assembly.</span>", \
|
||||
"You start to disassemble the airlock assembly...")
|
||||
playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1)
|
||||
if(do_after(user, 40/W.toolspeed, target = src))
|
||||
if( !WT.isOn() )
|
||||
return
|
||||
user << "<span class='notice'>You disassemble the airlock assembly.</span>"
|
||||
new /obj/item/stack/sheet/mineral/abductor(get_turf(src), 4)
|
||||
qdel(src)
|
||||
else
|
||||
return
|
||||
else if(istype(W, /obj/item/weapon/airlock_painter))
|
||||
return // no repainting
|
||||
else if(istype(W, /obj/item/stack/sheet))
|
||||
return // no material modding
|
||||
else
|
||||
..()
|
||||
@@ -0,0 +1,55 @@
|
||||
/datum/surgery/organ_extraction
|
||||
name = "experimental dissection"
|
||||
steps = list(/datum/surgery_step/incise, /datum/surgery_step/clamp_bleeders, /datum/surgery_step/retract_skin,/datum/surgery_step/incise, /datum/surgery_step/extract_organ ,/datum/surgery_step/gland_insert)
|
||||
possible_locs = list("chest")
|
||||
ignore_clothes = 1
|
||||
|
||||
/datum/surgery/organ_extraction/can_start(mob/user, mob/living/carbon/target)
|
||||
if(!ishuman(user))
|
||||
return 0
|
||||
var/mob/living/carbon/human/H = user
|
||||
if(H.dna.species.id == "abductor")
|
||||
return 1
|
||||
if((locate(/obj/item/weapon/implant/abductor) in H))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
/datum/surgery_step/extract_organ
|
||||
name = "remove heart"
|
||||
accept_hand = 1
|
||||
time = 32
|
||||
var/obj/item/organ/IC = null
|
||||
var/list/organ_types = list(/obj/item/organ/heart)
|
||||
|
||||
/datum/surgery_step/extract_organ/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
|
||||
for(var/obj/item/I in target.internal_organs)
|
||||
if(I.type in organ_types)
|
||||
IC = I
|
||||
break
|
||||
user.visible_message("[user] starts to remove [target]'s organs.", "<span class='notice'>You start to remove [target]'s organs...</span>")
|
||||
|
||||
/datum/surgery_step/extract_organ/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
|
||||
if(IC)
|
||||
user.visible_message("[user] pulls [IC] out of [target]'s [target_zone]!", "<span class='notice'>You pull [IC] out of [target]'s [target_zone].</span>")
|
||||
user.put_in_hands(IC)
|
||||
IC.Remove(target, special = 1)
|
||||
return 1
|
||||
else
|
||||
user << "<span class='warning'>You don't find anything in [target]'s [target_zone]!</span>"
|
||||
return 0
|
||||
|
||||
/datum/surgery_step/gland_insert
|
||||
name = "insert gland"
|
||||
implements = list(/obj/item/organ/gland = 100)
|
||||
time = 32
|
||||
|
||||
/datum/surgery_step/gland_insert/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
|
||||
user.visible_message("[user] starts to insert [tool] into [target].", "<span class ='notice'>You start to insert [tool] into [target]...</span>")
|
||||
|
||||
/datum/surgery_step/gland_insert/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
|
||||
user.visible_message("[user] inserts [tool] into [target].", "<span class ='notice'>You insert [tool] into [target].</span>")
|
||||
user.drop_item()
|
||||
var/obj/item/organ/gland/gland = tool
|
||||
gland.Insert(target, 2)
|
||||
return 1
|
||||
@@ -0,0 +1,261 @@
|
||||
/obj/item/organ/gland
|
||||
name = "fleshy mass"
|
||||
desc = "A nausea-inducing hunk of twisting flesh and metal."
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
zone = "chest"
|
||||
slot = "gland"
|
||||
icon_state = "gland"
|
||||
status = ORGAN_ROBOTIC
|
||||
origin_tech = "materials=4;biotech=7;abductor=3"
|
||||
var/cooldown_low = 300
|
||||
var/cooldown_high = 300
|
||||
var/next_activation = 0
|
||||
var/uses // -1 For inifinite
|
||||
var/human_only = 0
|
||||
var/active = 0
|
||||
|
||||
/obj/item/organ/gland/proc/ownerCheck()
|
||||
if(ishuman(owner))
|
||||
return 1
|
||||
if(!human_only && iscarbon(owner))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/item/organ/gland/proc/Start()
|
||||
active = 1
|
||||
next_activation = world.time + rand(cooldown_low,cooldown_high)
|
||||
|
||||
|
||||
/obj/item/organ/gland/Remove(var/mob/living/carbon/M, special = 0)
|
||||
active = 0
|
||||
if(initial(uses) == 1)
|
||||
uses = initial(uses)
|
||||
..()
|
||||
|
||||
/obj/item/organ/gland/Insert(var/mob/living/carbon/M, special = 0)
|
||||
..()
|
||||
if(special != 2 && uses) // Special 2 means abductor surgery
|
||||
Start()
|
||||
|
||||
/obj/item/organ/gland/on_life()
|
||||
if(!active)
|
||||
return
|
||||
if(!ownerCheck())
|
||||
active = 0
|
||||
return
|
||||
if(next_activation <= world.time)
|
||||
activate()
|
||||
uses--
|
||||
next_activation = world.time + rand(cooldown_low,cooldown_high)
|
||||
if(!uses)
|
||||
active = 0
|
||||
|
||||
/obj/item/organ/gland/proc/activate()
|
||||
return
|
||||
|
||||
/obj/item/organ/gland/heals
|
||||
cooldown_low = 200
|
||||
cooldown_high = 400
|
||||
uses = -1
|
||||
icon_state = "health"
|
||||
|
||||
/obj/item/organ/gland/heals/activate()
|
||||
owner << "<span class='notice'>You feel curiously revitalized.</span>"
|
||||
owner.adjustBruteLoss(-20)
|
||||
owner.adjustOxyLoss(-20)
|
||||
owner.adjustFireLoss(-20)
|
||||
|
||||
/obj/item/organ/gland/slime
|
||||
cooldown_low = 600
|
||||
cooldown_high = 1200
|
||||
uses = -1
|
||||
icon_state = "slime"
|
||||
|
||||
/obj/item/organ/gland/slime/activate()
|
||||
owner << "<span class='warning'>You feel nauseous!</span>"
|
||||
owner.vomit(20)
|
||||
|
||||
var/mob/living/simple_animal/slime/Slime
|
||||
Slime = new(get_turf(owner), "grey")
|
||||
Slime.Friends = list(owner)
|
||||
Slime.Leader = owner
|
||||
|
||||
/obj/item/organ/gland/mindshock
|
||||
origin_tech = "materials=4;biotech=4;magnets=6;abductor=3"
|
||||
cooldown_low = 300
|
||||
cooldown_high = 300
|
||||
uses = -1
|
||||
icon_state = "mindshock"
|
||||
|
||||
/obj/item/organ/gland/mindshock/activate()
|
||||
owner << "<span class='notice'>You get a headache.</span>"
|
||||
|
||||
var/turf/T = get_turf(owner)
|
||||
for(var/mob/living/carbon/H in orange(4,T))
|
||||
if(H == owner)
|
||||
continue
|
||||
H << "<span class='alien'>You hear a buzz in your head.</span>"
|
||||
H.confused += 20
|
||||
|
||||
/obj/item/organ/gland/pop
|
||||
cooldown_low = 900
|
||||
cooldown_high = 1800
|
||||
uses = 6
|
||||
human_only = 1
|
||||
icon_state = "species"
|
||||
|
||||
/obj/item/organ/gland/pop/activate()
|
||||
owner << "<span class='notice'>You feel unlike yourself.</span>"
|
||||
var/species = pick(list(/datum/species/lizard,/datum/species/jelly/slime,/datum/species/pod,/datum/species/fly))
|
||||
owner.set_species(species)
|
||||
|
||||
/obj/item/organ/gland/ventcrawling
|
||||
origin_tech = "materials=4;biotech=5;bluespace=4;abductor=3"
|
||||
cooldown_low = 1800
|
||||
cooldown_high = 2400
|
||||
uses = 1
|
||||
icon_state = "vent"
|
||||
|
||||
/obj/item/organ/gland/ventcrawling/activate()
|
||||
owner << "<span class='notice'>You feel very stretchy.</span>"
|
||||
owner.ventcrawler = 2
|
||||
return
|
||||
|
||||
|
||||
/obj/item/organ/gland/viral
|
||||
cooldown_low = 1800
|
||||
cooldown_high = 2400
|
||||
uses = 1
|
||||
icon_state = "viral"
|
||||
|
||||
/obj/item/organ/gland/viral/activate()
|
||||
owner << "<span class='warning'>You feel sick.</span>"
|
||||
var/virus_type = pick(/datum/disease/beesease, /datum/disease/brainrot, /datum/disease/magnitis)
|
||||
var/datum/disease/D = new virus_type()
|
||||
D.carrier = 1
|
||||
owner.viruses += D
|
||||
D.affected_mob = owner
|
||||
D.holder = owner
|
||||
owner.med_hud_set_status()
|
||||
|
||||
|
||||
/obj/item/organ/gland/emp //TODO : Replace with something more interesting
|
||||
origin_tech = "materials=4;biotech=4;magnets=6;abductor=3"
|
||||
cooldown_low = 900
|
||||
cooldown_high = 1600
|
||||
uses = 10
|
||||
icon_state = "emp"
|
||||
|
||||
/obj/item/organ/gland/emp/activate()
|
||||
owner << "<span class='warning'>You feel a spike of pain in your head.</span>"
|
||||
empulse(get_turf(owner), 2, 5, 1)
|
||||
|
||||
/obj/item/organ/gland/spiderman
|
||||
cooldown_low = 450
|
||||
cooldown_high = 900
|
||||
uses = 10
|
||||
icon_state = "spider"
|
||||
|
||||
/obj/item/organ/gland/spiderman/activate()
|
||||
owner << "<span class='warning'>You feel something crawling in your skin.</span>"
|
||||
owner.faction |= "spiders"
|
||||
new /obj/effect/spider/spiderling(owner.loc)
|
||||
|
||||
/obj/item/organ/gland/egg
|
||||
cooldown_low = 300
|
||||
cooldown_high = 400
|
||||
uses = -1
|
||||
icon_state = "egg"
|
||||
|
||||
/obj/item/organ/gland/egg/activate()
|
||||
owner << "<span class='boldannounce'>You lay an egg!</span>"
|
||||
var/obj/item/weapon/reagent_containers/food/snacks/egg/egg = new(owner.loc)
|
||||
egg.reagents.add_reagent("sacid",20)
|
||||
egg.desc += " It smells bad."
|
||||
|
||||
/obj/item/organ/gland/bloody
|
||||
cooldown_low = 200
|
||||
cooldown_high = 400
|
||||
uses = -1
|
||||
|
||||
/obj/item/organ/gland/bloody/activate()
|
||||
owner.adjustBruteLoss(15)
|
||||
|
||||
owner.visible_message("<span class='danger'>[owner]'s skin erupts with blood!</span>",\
|
||||
"<span class='userdanger'>Blood pours from your skin!</span>")
|
||||
|
||||
for(var/turf/T in oview(3,owner)) //Make this respect walls and such
|
||||
owner.add_splatter_floor(T)
|
||||
for(var/mob/living/carbon/human/H in oview(3,owner)) //Blood decals for simple animals would be neat. aka Carp with blood on it.
|
||||
H.add_mob_blood(owner)
|
||||
|
||||
/obj/item/organ/gland/bodysnatch
|
||||
cooldown_low = 600
|
||||
cooldown_high = 600
|
||||
human_only = 1
|
||||
uses = 1
|
||||
|
||||
/obj/item/organ/gland/bodysnatch/activate()
|
||||
owner << "<span class='warning'>You feel something moving around inside you...</span>"
|
||||
//spawn cocoon with clone greytide snpc inside
|
||||
if(ishuman(owner))
|
||||
var/obj/effect/cocoon/abductor/C = new (get_turf(owner))
|
||||
C.Copy(owner)
|
||||
C.Start()
|
||||
owner.gib()
|
||||
return
|
||||
|
||||
/obj/effect/cocoon/abductor
|
||||
name = "slimy cocoon"
|
||||
desc = "Something is moving inside."
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
icon_state = "cocoon_large3"
|
||||
color = rgb(10,120,10)
|
||||
density = 1
|
||||
var/hatch_time = 0
|
||||
|
||||
/obj/effect/cocoon/abductor/proc/Copy(mob/living/carbon/human/H)
|
||||
var/mob/living/carbon/human/interactive/greytide/clone = new(src)
|
||||
clone.hardset_dna(H.dna.uni_identity,H.dna.struc_enzymes,H.real_name, H.dna.blood_type, H.dna.species.type, H.dna.features)
|
||||
|
||||
//There's no define for this / get all items ?
|
||||
var/list/slots = list(slot_back,slot_w_uniform,slot_wear_suit,\
|
||||
slot_wear_mask,slot_head,slot_shoes,slot_gloves,slot_ears,\
|
||||
slot_glasses,slot_belt,slot_s_store,slot_l_store,slot_r_store,slot_wear_id)
|
||||
|
||||
for(var/slot in slots)
|
||||
var/obj/item/I = H.get_item_by_slot(slot)
|
||||
if(I)
|
||||
clone.equip_to_slot_if_possible(I,slot)
|
||||
|
||||
/obj/effect/cocoon/abductor/proc/Start()
|
||||
hatch_time = world.time + 600
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/effect/cocoon/abductor/process()
|
||||
if(world.time > hatch_time)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
for(var/mob/M in contents)
|
||||
src.visible_message("<span class='warning'>[src] hatches!</span>")
|
||||
M.loc = src.loc
|
||||
qdel(src)
|
||||
|
||||
/obj/item/organ/gland/plasma
|
||||
cooldown_low = 2400
|
||||
cooldown_high = 3000
|
||||
origin_tech = "materials=4;biotech=4;plasmatech=6;abductor=3"
|
||||
uses = 1
|
||||
|
||||
/obj/item/organ/gland/plasma/activate()
|
||||
owner << "<span class='warning'>You feel bloated.</span>"
|
||||
sleep(150)
|
||||
if(!owner) return
|
||||
owner << "<span class='userdanger'>A massive stomachache overcomes you.</span>"
|
||||
sleep(50)
|
||||
if(!owner) return
|
||||
owner.visible_message("<span class='danger'>[owner] explodes in a cloud of plasma!</span>")
|
||||
var/turf/open/T = get_turf(owner)
|
||||
if(istype(T))
|
||||
T.atmos_spawn_air("plasma=300;TEMP=[T20C]")
|
||||
owner.gib()
|
||||
return
|
||||
@@ -0,0 +1,154 @@
|
||||
/obj/machinery/computer/camera_advanced/abductor
|
||||
name = "Human Observation Console"
|
||||
var/team = 0
|
||||
networks = list("SS13","Abductor")
|
||||
off_action = new/datum/action/innate/camera_off/abductor //specific datum
|
||||
var/datum/action/innate/teleport_in/tele_in_action = new
|
||||
var/datum/action/innate/teleport_out/tele_out_action = new
|
||||
var/datum/action/innate/teleport_self/tele_self_action = new
|
||||
var/datum/action/innate/vest_mode_swap/vest_mode_action = new
|
||||
var/datum/action/innate/vest_disguise_swap/vest_disguise_action = new
|
||||
var/datum/action/innate/set_droppoint/set_droppoint_action = new
|
||||
var/obj/machinery/abductor/console/console
|
||||
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
icon_state = "camera"
|
||||
|
||||
/obj/machinery/computer/camera_advanced/abductor/CreateEye()
|
||||
..()
|
||||
eyeobj.visible_icon = 1
|
||||
eyeobj.icon = 'icons/obj/abductor.dmi'
|
||||
eyeobj.icon_state = "camera_target"
|
||||
|
||||
/obj/machinery/computer/camera_advanced/abductor/GrantActions(mob/living/carbon/user)
|
||||
off_action.target = user
|
||||
off_action.Grant(user)
|
||||
|
||||
jump_action.target = user
|
||||
jump_action.Grant(user)
|
||||
//TODO : add null checks
|
||||
tele_in_action.target = console.pad
|
||||
tele_in_action.Grant(user)
|
||||
|
||||
tele_out_action.target = console
|
||||
tele_out_action.Grant(user)
|
||||
|
||||
tele_self_action.target = console.pad
|
||||
tele_self_action.Grant(user)
|
||||
|
||||
vest_mode_action.target = console
|
||||
vest_mode_action.Grant(user)
|
||||
|
||||
vest_disguise_action.target = console
|
||||
vest_disguise_action.Grant(user)
|
||||
|
||||
set_droppoint_action.target = console
|
||||
set_droppoint_action.Grant(user)
|
||||
|
||||
/obj/machinery/computer/camera_advanced/abductor/proc/IsScientist(mob/living/carbon/human/H)
|
||||
var/datum/species/abductor/S = H.dna.species
|
||||
return S.scientist
|
||||
|
||||
/obj/machinery/computer/camera_advanced/abductor/attack_hand(mob/user)
|
||||
if(!isabductor(user))
|
||||
return
|
||||
return ..()
|
||||
|
||||
/datum/action/innate/camera_off/abductor/Activate()
|
||||
if(!target || !iscarbon(target))
|
||||
return
|
||||
var/mob/living/carbon/C = target
|
||||
var/mob/camera/aiEye/remote/remote_eye = C.remote_control
|
||||
var/obj/machinery/computer/camera_advanced/abductor/origin = remote_eye.origin
|
||||
origin.current_user = null
|
||||
origin.jump_action.Remove(C)
|
||||
origin.tele_in_action.Remove(C)
|
||||
origin.tele_out_action.Remove(C)
|
||||
origin.tele_self_action.Remove(C)
|
||||
origin.vest_mode_action.Remove(C)
|
||||
origin.vest_disguise_action.Remove(C)
|
||||
origin.set_droppoint_action.Remove(C)
|
||||
remote_eye.eye_user = null
|
||||
C.reset_perspective(null)
|
||||
if(C.client)
|
||||
C.client.images -= remote_eye.user_image
|
||||
for(var/datum/camerachunk/chunk in remote_eye.visibleCameraChunks)
|
||||
C.client.images -= chunk.obscured
|
||||
C.remote_control = null
|
||||
C.unset_machine()
|
||||
src.Remove(C)
|
||||
|
||||
|
||||
/datum/action/innate/teleport_in
|
||||
name = "Send To"
|
||||
button_icon_state = "beam_down"
|
||||
|
||||
/datum/action/innate/teleport_in/Activate()
|
||||
if(!target || !iscarbon(owner))
|
||||
return
|
||||
var/mob/living/carbon/human/C = owner
|
||||
var/mob/camera/aiEye/remote/remote_eye = C.remote_control
|
||||
var/obj/machinery/abductor/pad/P = target
|
||||
|
||||
if(cameranet.checkTurfVis(remote_eye.loc))
|
||||
P.PadToLoc(remote_eye.loc)
|
||||
|
||||
/datum/action/innate/teleport_out
|
||||
name = "Retrieve"
|
||||
button_icon_state = "beam_up"
|
||||
|
||||
/datum/action/innate/teleport_out/Activate()
|
||||
if(!target || !iscarbon(owner))
|
||||
return
|
||||
var/obj/machinery/abductor/console/console = target
|
||||
|
||||
console.TeleporterRetrieve()
|
||||
|
||||
/datum/action/innate/teleport_self
|
||||
name = "Send Self"
|
||||
button_icon_state = "beam_down"
|
||||
|
||||
/datum/action/innate/teleport_self/Activate()
|
||||
if(!target || !iscarbon(owner))
|
||||
return
|
||||
var/mob/living/carbon/human/C = owner
|
||||
var/mob/camera/aiEye/remote/remote_eye = C.remote_control
|
||||
var/obj/machinery/abductor/pad/P = target
|
||||
|
||||
if(cameranet.checkTurfVis(remote_eye.loc))
|
||||
P.MobToLoc(remote_eye.loc,C)
|
||||
|
||||
/datum/action/innate/vest_mode_swap
|
||||
name = "Switch Vest Mode"
|
||||
button_icon_state = "vest_mode"
|
||||
|
||||
/datum/action/innate/vest_mode_swap/Activate()
|
||||
if(!target || !iscarbon(owner))
|
||||
return
|
||||
var/obj/machinery/abductor/console/console = target
|
||||
console.FlipVest()
|
||||
|
||||
|
||||
/datum/action/innate/vest_disguise_swap
|
||||
name = "Switch Vest Disguise"
|
||||
button_icon_state = "vest_disguise"
|
||||
|
||||
/datum/action/innate/vest_disguise_swap/Activate()
|
||||
if(!target || !iscarbon(owner))
|
||||
return
|
||||
var/obj/machinery/abductor/console/console = target
|
||||
console.SelectDisguise(remote=1)
|
||||
|
||||
/datum/action/innate/set_droppoint
|
||||
name = "Set Experiment Release Point"
|
||||
button_icon_state = "set_drop"
|
||||
|
||||
/datum/action/innate/set_droppoint/Activate()
|
||||
if(!target || !iscarbon(owner))
|
||||
return
|
||||
|
||||
var/mob/living/carbon/human/C = owner
|
||||
var/mob/camera/aiEye/remote/remote_eye = C.remote_control
|
||||
|
||||
var/obj/machinery/abductor/console/console = target
|
||||
console.SetDroppoint(remote_eye.loc,owner)
|
||||
@@ -0,0 +1,217 @@
|
||||
//Common
|
||||
|
||||
/obj/machinery/abductor
|
||||
var/team = 0
|
||||
|
||||
/obj/machinery/abductor/proc/IsAgent(mob/living/carbon/human/H)
|
||||
if(H.dna.species.id == "abductor")
|
||||
var/datum/species/abductor/S = H.dna.species
|
||||
return S.agent
|
||||
return 0
|
||||
|
||||
/obj/machinery/abductor/proc/IsScientist(mob/living/carbon/human/H)
|
||||
if(H.dna.species.id == "abductor")
|
||||
var/datum/species/abductor/S = H.dna.species
|
||||
return S.scientist
|
||||
return 0
|
||||
|
||||
//Console
|
||||
|
||||
/obj/machinery/abductor/console
|
||||
name = "Abductor console"
|
||||
desc = "Ship command center."
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
icon_state = "console"
|
||||
density = 1
|
||||
anchored = 1
|
||||
var/obj/item/device/abductor/gizmo/gizmo
|
||||
var/obj/item/clothing/suit/armor/abductor/vest/vest
|
||||
var/obj/machinery/abductor/experiment/experiment
|
||||
var/obj/machinery/abductor/pad/pad
|
||||
var/obj/machinery/computer/camera_advanced/abductor/camera
|
||||
var/list/datum/icon_snapshot/disguises = list()
|
||||
|
||||
/obj/machinery/abductor/console/attack_hand(mob/user)
|
||||
if(..())
|
||||
return
|
||||
if(!isabductor(user))
|
||||
user << "<span class='warning'>You start mashing alien buttons at random!</span>"
|
||||
if(do_after(user,100, target = src))
|
||||
TeleporterSend()
|
||||
return
|
||||
user.set_machine(src)
|
||||
var/dat = ""
|
||||
dat += "<H3> Abductsoft 3000 </H3>"
|
||||
|
||||
if(experiment != null)
|
||||
var/points = experiment.points
|
||||
dat += "Collected Samples : [points] <br>"
|
||||
dat += "<b>Transfer data in exchange for supplies:</b><br>"
|
||||
dat += "<a href='?src=\ref[src];dispense=baton'>Advanced Baton</A><br>"
|
||||
dat += "<a href='?src=\ref[src];dispense=helmet'>Agent Helmet</A><br>"
|
||||
dat += "<a href='?src=\ref[src];dispense=vest'>Agent Vest</A><br>"
|
||||
dat += "<a href='?src=\ref[src];dispense=silencer'>Radio Silencer</A><br>"
|
||||
dat += "<a href='?src=\ref[src];dispense=tool'>Science Tool</A><br>"
|
||||
else
|
||||
dat += "<span class='bad'>NO EXPERIMENT MACHINE DETECTED</span> <br>"
|
||||
|
||||
if(pad!=null)
|
||||
dat += "<span class='bad'>Emergency Teleporter System.</span>"
|
||||
dat += "<span class='bad'>Consider using primary observation console first.</span>"
|
||||
dat += "<a href='?src=\ref[src];teleporter_send=1'>Activate Teleporter</A><br>"
|
||||
if(gizmo!=null && gizmo.marked!=null)
|
||||
dat += "<a href='?src=\ref[src];teleporter_retrieve=1'>Retrieve Mark</A><br>"
|
||||
else
|
||||
dat += "<span class='linkOff'>Retrieve Mark</span><br>"
|
||||
else
|
||||
dat += "<span class='bad'>NO TELEPAD DETECTED</span></br>"
|
||||
|
||||
if(vest!=null)
|
||||
dat += "<h4> Agent Vest Mode </h4><br>"
|
||||
var/mode = vest.mode
|
||||
if(mode == VEST_STEALTH)
|
||||
dat += "<a href='?src=\ref[src];flip_vest=1'>Combat</A>"
|
||||
dat += "<span class='linkOff'>Stealth</span>"
|
||||
else
|
||||
dat += "<span class='linkOff'>Combat</span>"
|
||||
dat += "<a href='?src=\ref[src];flip_vest=1'>Stealth</A>"
|
||||
|
||||
dat+="<br>"
|
||||
dat += "<a href='?src=\ref[src];select_disguise=1'>Select Agent Vest Disguise</a><br>"
|
||||
dat += "<a href='?src=\ref[src];toggle_vest=1'>[vest.flags & NODROP ? "Unlock" : "Lock"] Vest</a><br>"
|
||||
else
|
||||
dat += "<span class='bad'>NO AGENT VEST DETECTED</span>"
|
||||
var/datum/browser/popup = new(user, "computer", "Abductor Console", 400, 500)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/abductor/console/Topic(href, href_list)
|
||||
if(..())
|
||||
return
|
||||
|
||||
usr.set_machine(src)
|
||||
if(href_list["teleporter_send"])
|
||||
TeleporterSend()
|
||||
else if(href_list["teleporter_retrieve"])
|
||||
TeleporterRetrieve()
|
||||
else if(href_list["flip_vest"])
|
||||
FlipVest()
|
||||
else if(href_list["toggle_vest"])
|
||||
toggle_vest()
|
||||
else if(href_list["select_disguise"])
|
||||
SelectDisguise()
|
||||
else if(href_list["dispense"])
|
||||
switch(href_list["dispense"])
|
||||
if("baton")
|
||||
Dispense(/obj/item/weapon/abductor_baton,cost=2)
|
||||
if("helmet")
|
||||
Dispense(/obj/item/clothing/head/helmet/abductor)
|
||||
if("silencer")
|
||||
Dispense(/obj/item/device/abductor/silencer)
|
||||
if("tool")
|
||||
Dispense(/obj/item/device/abductor/gizmo)
|
||||
if("vest")
|
||||
Dispense(/obj/item/clothing/suit/armor/abductor/vest)
|
||||
src.updateUsrDialog()
|
||||
|
||||
|
||||
/obj/machinery/abductor/console/proc/TeleporterRetrieve()
|
||||
if(gizmo!=null && pad!=null && gizmo.marked)
|
||||
pad.Retrieve(gizmo.marked)
|
||||
|
||||
/obj/machinery/abductor/console/proc/TeleporterSend()
|
||||
if(pad!=null)
|
||||
pad.Send()
|
||||
|
||||
/obj/machinery/abductor/console/proc/FlipVest()
|
||||
if(vest!=null)
|
||||
vest.flip_mode()
|
||||
|
||||
/obj/machinery/abductor/console/proc/SelectDisguise(remote=0)
|
||||
var/list/entries = list()
|
||||
var/tempname
|
||||
var/datum/icon_snapshot/temp
|
||||
for(var/i = 1; i <= disguises.len; i++)
|
||||
temp = disguises[i]
|
||||
tempname = temp.name
|
||||
entries["[tempname]"] = disguises[i]
|
||||
var/entry_name = input( "Choose Disguise", "Disguise") in entries
|
||||
var/datum/icon_snapshot/chosen = entries[entry_name]
|
||||
if(chosen && (remote || in_range(usr,src)))
|
||||
vest.SetDisguise(chosen)
|
||||
|
||||
/obj/machinery/abductor/console/proc/SetDroppoint(turf/open/location,user)
|
||||
if(!istype(location))
|
||||
user << "<span class='warning'>That place is not safe for the specimen.</span>"
|
||||
return
|
||||
|
||||
if(pad)
|
||||
pad.teleport_target = location
|
||||
user << "<span class='notice'>Location marked as test subject release point.</span>"
|
||||
|
||||
|
||||
/obj/machinery/abductor/console/proc/Initialize()
|
||||
|
||||
for(var/obj/machinery/abductor/pad/p in machines)
|
||||
if(p.team == team)
|
||||
pad = p
|
||||
break
|
||||
|
||||
for(var/obj/machinery/abductor/experiment/e in machines)
|
||||
if(e.team == team)
|
||||
experiment = e
|
||||
e.console = src
|
||||
|
||||
for(var/obj/machinery/computer/camera_advanced/abductor/c in machines)
|
||||
if(c.team == team)
|
||||
camera = c
|
||||
c.console = src
|
||||
|
||||
/obj/machinery/abductor/console/proc/AddSnapshot(mob/living/carbon/human/target)
|
||||
var/datum/icon_snapshot/entry = new
|
||||
entry.name = target.name
|
||||
entry.icon = target.icon
|
||||
entry.icon_state = target.icon_state
|
||||
entry.overlays = target.get_overlays_copy(list(L_HAND_LAYER,R_HAND_LAYER))
|
||||
for(var/i=1,i<=disguises.len,i++)
|
||||
var/datum/icon_snapshot/temp = disguises[i]
|
||||
if(temp.name == entry.name)
|
||||
disguises[i] = entry
|
||||
return
|
||||
disguises.Add(entry)
|
||||
|
||||
/obj/machinery/abductor/console/attackby(obj/O, mob/user, params)
|
||||
if(istype(O, /obj/item/device/abductor/gizmo))
|
||||
var/obj/item/device/abductor/gizmo/G = O
|
||||
user << "<span class='notice'>You link the tool to the console.</span>"
|
||||
gizmo = G
|
||||
G.console = src
|
||||
else if(istype(O, /obj/item/clothing/suit/armor/abductor/vest))
|
||||
var/obj/item/clothing/suit/armor/abductor/vest/V = O
|
||||
user << "<span class='notice'>You link the vest to the console.</span>"
|
||||
if(istype(vest))
|
||||
if(vest.flags & NODROP)
|
||||
toggle_vest()
|
||||
vest = V
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/machinery/abductor/console/proc/Dispense(item,cost=1)
|
||||
if(experiment && experiment.points >= cost)
|
||||
experiment.points-=cost
|
||||
say("Incoming supply!")
|
||||
if(pad)
|
||||
flick("alien-pad", pad)
|
||||
new item(pad.loc)
|
||||
else
|
||||
new item(src.loc)
|
||||
else
|
||||
say("Insufficent data!")
|
||||
|
||||
/obj/machinery/abductor/console/proc/toggle_vest()
|
||||
vest.flags ^= NODROP
|
||||
var/mob/M = vest.loc
|
||||
if(istype(M))
|
||||
M << "<span class='notice'>[src] is now \
|
||||
[vest.flags & NODROP ? "locked" : "unlocked"].</span>"
|
||||
@@ -0,0 +1,85 @@
|
||||
/obj/machinery/abductor/gland_dispenser
|
||||
name = "Replacement Organ Storage"
|
||||
desc = "A tank filled with replacement organs"
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
icon_state = "dispenser"
|
||||
density = 1
|
||||
anchored = 1
|
||||
var/list/gland_types
|
||||
var/list/gland_colors
|
||||
var/list/amounts
|
||||
|
||||
/obj/machinery/abductor/gland_dispenser/proc/random_color()
|
||||
//TODO : replace with presets or spectrum
|
||||
return rgb(rand(0,255),rand(0,255),rand(0,255))
|
||||
|
||||
/obj/machinery/abductor/gland_dispenser/New()
|
||||
gland_types = subtypesof(/obj/item/organ/gland)
|
||||
gland_types = shuffle(gland_types)
|
||||
gland_colors = new/list(gland_types.len)
|
||||
amounts = new/list(gland_types.len)
|
||||
for(var/i=1,i<=gland_types.len,i++)
|
||||
gland_colors[i] = random_color()
|
||||
amounts[i] = rand(1,5)
|
||||
|
||||
/obj/machinery/abductor/gland_dispenser/attack_hand(mob/user)
|
||||
if(..())
|
||||
return
|
||||
if(!isabductor(user))
|
||||
return
|
||||
user.set_machine(src)
|
||||
var/box_css = {"
|
||||
<style>
|
||||
a.box.gland {
|
||||
float: left;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: 5px;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: rgba(0,0,0,.2);
|
||||
text-align: center;
|
||||
}
|
||||
</style>"}
|
||||
var/dat = ""
|
||||
var/item_count = 0
|
||||
for(var/i=1,i<=gland_colors.len,i++)
|
||||
item_count++
|
||||
var/g_color = gland_colors[i]
|
||||
var/amount = amounts[i]
|
||||
dat += "<a class='box gland' style='background-color:[g_color]' href='?src=\ref[src];dispense=[i]'>[amount]</a>"
|
||||
if(item_count == 3) // Three boxes per line
|
||||
dat +="</br></br>"
|
||||
item_count = 0
|
||||
var/datum/browser/popup = new(user, "glands", "Gland Dispenser", 200, 200)
|
||||
popup.add_head_content(box_css)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
return
|
||||
|
||||
/obj/machinery/abductor/gland_dispenser/attackby(obj/item/weapon/W, mob/user, params)
|
||||
if(istype(W, /obj/item/organ/gland))
|
||||
if(!user.drop_item())
|
||||
return
|
||||
W.loc = src
|
||||
for(var/i=1,i<=gland_colors.len,i++)
|
||||
if(gland_types[i] == W.type)
|
||||
amounts[i]++
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/machinery/abductor/gland_dispenser/Topic(href, href_list)
|
||||
if(..())
|
||||
return
|
||||
usr.set_machine(src)
|
||||
|
||||
if(href_list["dispense"])
|
||||
Dispense(text2num(href_list["dispense"]))
|
||||
src.updateUsrDialog()
|
||||
|
||||
/obj/machinery/abductor/gland_dispenser/proc/Dispense(count)
|
||||
if(amounts[count]>0)
|
||||
amounts[count]--
|
||||
var/T = gland_types[count]
|
||||
new T(get_turf(src))
|
||||
@@ -0,0 +1,205 @@
|
||||
/obj/machinery/abductor/experiment
|
||||
name = "experimentation machine"
|
||||
desc = "A large man-sized tube sporting a complex array of surgical apparatus."
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
icon_state = "experiment-open"
|
||||
density = 0
|
||||
anchored = 1
|
||||
state_open = 1
|
||||
var/points = 0
|
||||
var/list/history = list()
|
||||
var/list/abductee_minds = list()
|
||||
var/flash = " - || - "
|
||||
var/obj/machinery/abductor/console/console
|
||||
|
||||
/obj/machinery/abductor/experiment/MouseDrop_T(mob/target, mob/user)
|
||||
if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user) || !ishuman(target))
|
||||
return
|
||||
if(isabductor(target))
|
||||
return
|
||||
close_machine(target)
|
||||
|
||||
/obj/machinery/abductor/experiment/attack_hand(mob/user)
|
||||
if(..())
|
||||
return
|
||||
|
||||
experimentUI(user)
|
||||
|
||||
/obj/machinery/abductor/experiment/open_machine()
|
||||
if(!state_open && !panel_open)
|
||||
..()
|
||||
|
||||
/obj/machinery/abductor/experiment/close_machine(mob/target)
|
||||
for(var/A in loc)
|
||||
if(isabductor(A))
|
||||
return
|
||||
if(state_open && !panel_open)
|
||||
..(target)
|
||||
|
||||
/obj/machinery/abductor/experiment/proc/dissection_icon(mob/living/carbon/human/H)
|
||||
var/icon/photo = null
|
||||
var/g = (H.gender == FEMALE) ? "f" : "m"
|
||||
if(!config.mutant_races || H.dna.species.use_skintones)
|
||||
photo = icon("icon" = 'icons/mob/human.dmi', "icon_state" = "[H.skin_tone]_[g]_s")
|
||||
else
|
||||
photo = icon("icon" = 'icons/mob/human.dmi', "icon_state" = "[H.dna.species.id]_[g]_s")
|
||||
photo.Blend("#[H.dna.features["mcolor"]]", ICON_MULTIPLY)
|
||||
|
||||
var/icon/eyes_s
|
||||
if(EYECOLOR in H.dna.species.specflags)
|
||||
eyes_s = icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "[H.dna.species.eyes]_s")
|
||||
eyes_s.Blend("#[H.eye_color]", ICON_MULTIPLY)
|
||||
|
||||
var/datum/sprite_accessory/S
|
||||
S = hair_styles_list[H.hair_style]
|
||||
if(S && (HAIR in H.dna.species.specflags))
|
||||
var/icon/hair_s = icon("icon" = S.icon, "icon_state" = "[S.icon_state]_s")
|
||||
hair_s.Blend("#[H.hair_color]", ICON_MULTIPLY)
|
||||
eyes_s.Blend(hair_s, ICON_OVERLAY)
|
||||
|
||||
S = facial_hair_styles_list[H.facial_hair_style]
|
||||
if(S && (FACEHAIR in H.dna.species.specflags))
|
||||
var/icon/facial_s = icon("icon" = S.icon, "icon_state" = "[S.icon_state]_s")
|
||||
facial_s.Blend("#[H.facial_hair_color]", ICON_MULTIPLY)
|
||||
eyes_s.Blend(facial_s, ICON_OVERLAY)
|
||||
|
||||
if(eyes_s)
|
||||
photo.Blend(eyes_s, ICON_OVERLAY)
|
||||
|
||||
var/icon/splat = icon("icon" = 'icons/mob/dam_human.dmi',"icon_state" = "chest30")
|
||||
photo.Blend(splat,ICON_OVERLAY)
|
||||
|
||||
return photo
|
||||
|
||||
/obj/machinery/abductor/experiment/proc/experimentUI(mob/user)
|
||||
var/dat
|
||||
dat += "<h3> Experiment </h3>"
|
||||
if(occupant)
|
||||
var/obj/item/weapon/photo/P = new
|
||||
P.photocreate(null, icon(dissection_icon(occupant), dir = SOUTH))
|
||||
user << browse_rsc(P.img, "dissection_img")
|
||||
dat += "<table><tr><td>"
|
||||
dat += "<img src=dissection_img height=80 width=80>" //Avert your eyes
|
||||
dat += "</td><td>"
|
||||
dat += "<a href='?src=\ref[src];experiment=1'>Probe</a><br>"
|
||||
dat += "<a href='?src=\ref[src];experiment=2'>Dissect</a><br>"
|
||||
dat += "<a href='?src=\ref[src];experiment=3'>Analyze</a><br>"
|
||||
dat += "</td></tr></table>"
|
||||
else
|
||||
dat += "<span class='linkOff'>Experiment </span>"
|
||||
|
||||
if(!occupant)
|
||||
dat += "<h3>Machine Unoccupied</h3>"
|
||||
else
|
||||
dat += "<h3>Subject Status : </h3>"
|
||||
dat += "[occupant.name] => "
|
||||
switch(occupant.stat)
|
||||
if(0)
|
||||
dat += "<span class='good'>Conscious</span>"
|
||||
if(1)
|
||||
dat += "<span class='average'>Unconscious</span>"
|
||||
else
|
||||
dat += "<span class='bad'>Deceased</span>"
|
||||
dat += "<br>"
|
||||
dat += "[flash]"
|
||||
dat += "<br>"
|
||||
dat += "<a href='?src=\ref[src];refresh=1'>Scan</a>"
|
||||
dat += "<a href='?src=\ref[src];[state_open ? "close=1'>Close</a>" : "open=1'>Open</a>"]"
|
||||
var/datum/browser/popup = new(user, "experiment", "Probing Console", 300, 300)
|
||||
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/abductor/experiment/Topic(href, href_list)
|
||||
if(..() || usr == occupant)
|
||||
return
|
||||
usr.set_machine(src)
|
||||
if(href_list["refresh"])
|
||||
updateUsrDialog()
|
||||
return
|
||||
if(href_list["open"])
|
||||
open_machine()
|
||||
return
|
||||
if(href_list["close"])
|
||||
close_machine()
|
||||
return
|
||||
if(occupant && occupant.stat != DEAD)
|
||||
if(href_list["experiment"])
|
||||
flash = Experiment(occupant,href_list["experiment"])
|
||||
updateUsrDialog()
|
||||
add_fingerprint(usr)
|
||||
|
||||
/obj/machinery/abductor/experiment/proc/Experiment(mob/occupant,type)
|
||||
var/mob/living/carbon/human/H = occupant
|
||||
var/point_reward = 0
|
||||
if(H in history)
|
||||
return "<span class='bad'>Specimen already in database.</span>"
|
||||
if(H.stat == DEAD)
|
||||
say("Specimen deceased - please provide fresh sample.")
|
||||
return "<span class='bad'>Specimen deceased.</span>"
|
||||
var/obj/item/organ/gland/GlandTest = locate() in H.internal_organs
|
||||
if(!GlandTest)
|
||||
say("Experimental dissection not detected!")
|
||||
return "<span class='bad'>No glands detected!</span>"
|
||||
if(H.mind != null && H.ckey != null)
|
||||
history += H
|
||||
abductee_minds += H.mind
|
||||
say("Processing specimen...")
|
||||
sleep(5)
|
||||
switch(text2num(type))
|
||||
if(1)
|
||||
H << "<span class='warning'>You feel violated.</span>"
|
||||
if(2)
|
||||
H << "<span class='warning'>You feel yourself being sliced apart and put back together.</span>"
|
||||
if(3)
|
||||
H << "<span class='warning'>You feel intensely watched.</span>"
|
||||
sleep(5)
|
||||
H << "<span class='warning'><b>Your mind snaps!</b></span>"
|
||||
var/objtype = pick(subtypesof(/datum/objective/abductee/))
|
||||
var/datum/objective/abductee/O = new objtype()
|
||||
ticker.mode.abductees += H.mind
|
||||
H.mind.objectives += O
|
||||
var/obj_count = 1
|
||||
H << "<span class='notice'>Your current objectives:</span>"
|
||||
for(var/datum/objective/objective in H.mind.objectives)
|
||||
H << "<B>Objective #[obj_count]</B>: [objective.explanation_text]"
|
||||
obj_count++
|
||||
ticker.mode.update_abductor_icons_added(H.mind)
|
||||
|
||||
for(var/obj/item/organ/gland/G in H.internal_organs)
|
||||
G.Start()
|
||||
point_reward++
|
||||
if(point_reward > 0)
|
||||
open_machine()
|
||||
SendBack(H)
|
||||
playsound(src.loc, 'sound/machines/ding.ogg', 50, 1)
|
||||
points += point_reward
|
||||
return "<span class='good'>Experiment successful! [point_reward] new data-points collected.</span>"
|
||||
else
|
||||
playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 1)
|
||||
return "<span class='bad'>Experiment failed! No replacement organ detected.</span>"
|
||||
else
|
||||
say("Brain activity nonexistant - disposing sample...")
|
||||
open_machine()
|
||||
SendBack(H)
|
||||
return "<span class='bad'>Specimen braindead - disposed.</span>"
|
||||
return "<span class='bad'>ERROR</span>"
|
||||
|
||||
|
||||
/obj/machinery/abductor/experiment/proc/SendBack(mob/living/carbon/human/H)
|
||||
H.Sleeping(8)
|
||||
if(console && console.pad && console.pad.teleport_target)
|
||||
H.forceMove(console.pad.teleport_target)
|
||||
H.uncuff()
|
||||
return
|
||||
//Area not chosen / It's not safe area - teleport to arrivals
|
||||
H.forceMove(pick(latejoin))
|
||||
H.uncuff()
|
||||
return
|
||||
|
||||
|
||||
/obj/machinery/abductor/experiment/update_icon()
|
||||
if(state_open)
|
||||
icon_state = "experiment-open"
|
||||
else
|
||||
icon_state = "experiment"
|
||||
@@ -0,0 +1,54 @@
|
||||
/obj/machinery/abductor/pad
|
||||
name = "Alien Telepad"
|
||||
desc = "Use this to transport to and from human habitat"
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
icon_state = "alien-pad-idle"
|
||||
anchored = 1
|
||||
var/turf/teleport_target
|
||||
|
||||
/obj/machinery/abductor/pad/proc/Warp(mob/living/target)
|
||||
target.Move(src.loc)
|
||||
|
||||
/obj/machinery/abductor/pad/proc/Send()
|
||||
if(teleport_target == null)
|
||||
teleport_target = teleportlocs[pick(teleportlocs)]
|
||||
flick("alien-pad", src)
|
||||
for(var/mob/living/target in loc)
|
||||
target.forceMove(teleport_target)
|
||||
spawn(0)
|
||||
anim(target.loc,target,'icons/mob/mob.dmi',,"uncloak",,target.dir)
|
||||
|
||||
/obj/machinery/abductor/pad/proc/Retrieve(mob/living/target)
|
||||
flick("alien-pad", src)
|
||||
spawn(0)
|
||||
anim(target.loc,target,'icons/mob/mob.dmi',,"uncloak",,target.dir)
|
||||
Warp(target)
|
||||
|
||||
/obj/machinery/abductor/pad/proc/MobToLoc(place,mob/living/target)
|
||||
PoolOrNew(/obj/effect/overlay/temp/teleport_abductor, place)
|
||||
sleep(80)
|
||||
flick("alien-pad", src)
|
||||
target.forceMove(place)
|
||||
anim(target.loc,target,'icons/mob/mob.dmi',,"uncloak",,target.dir)
|
||||
|
||||
/obj/machinery/abductor/pad/proc/PadToLoc(place)
|
||||
PoolOrNew(/obj/effect/overlay/temp/teleport_abductor, place)
|
||||
sleep(80)
|
||||
flick("alien-pad", src)
|
||||
for(var/mob/living/target in src.loc)
|
||||
target.forceMove(place)
|
||||
spawn(0)
|
||||
anim(target.loc,target,'icons/mob/mob.dmi',,"uncloak",,target.dir)
|
||||
|
||||
|
||||
/obj/effect/overlay/temp/teleport_abductor
|
||||
name = "Huh"
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
icon_state = "teleport"
|
||||
duration = 80
|
||||
|
||||
/obj/effect/overlay/temp/teleport_abductor/New()
|
||||
var/datum/effect_system/spark_spread/S = new
|
||||
S.set_up(10,0,loc)
|
||||
S.start()
|
||||
..()
|
||||
@@ -0,0 +1,566 @@
|
||||
////Deactivated swarmer shell////
|
||||
/obj/item/device/unactivated_swarmer
|
||||
name = "unactivated swarmer"
|
||||
desc = "A currently unactivated swarmer. Swarmers can self activate at any time, it would be wise to immediately dispose of this."
|
||||
icon = 'icons/mob/swarmer.dmi'
|
||||
icon_state = "swarmer_unactivated"
|
||||
origin_tech = "bluespace=4;materials=4;programming=7"
|
||||
materials = list(MAT_METAL=10000, MAT_GLASS=4000)
|
||||
|
||||
|
||||
/obj/item/device/unactivated_swarmer/New()
|
||||
if(!crit_fail)
|
||||
notify_ghosts("An unactivated swarmer has been created in [get_area(src)]!", enter_link = "<a href=?src=\ref[src];ghostjoin=1>(Click to enter)</a>", source = src, action = NOTIFY_ATTACK)
|
||||
..()
|
||||
|
||||
/obj/item/device/unactivated_swarmer/Topic(href, href_list)
|
||||
if(href_list["ghostjoin"])
|
||||
var/mob/dead/observer/ghost = usr
|
||||
if(istype(ghost))
|
||||
attack_ghost(ghost)
|
||||
|
||||
/obj/item/device/unactivated_swarmer/attackby(obj/item/weapon/W, mob/user, params)
|
||||
..()
|
||||
if(istype(W, /obj/item/weapon/screwdriver) && !crit_fail)
|
||||
user.visible_message("<span class='warning'>[usr.name] deactivates [src].</span>",
|
||||
"<span class='notice'>After some fiddling, you find a way to disable [src]'s power source.</span>",
|
||||
"<span class='italics'>You hear clicking.</span>")
|
||||
name = "deactivated swarmer"
|
||||
desc = "A shell of swarmer that was completely powered down. It no longer can activate itself."
|
||||
crit_fail = 1
|
||||
|
||||
/obj/item/device/unactivated_swarmer/attack_ghost(mob/user)
|
||||
if(crit_fail)
|
||||
user << "This swarmer shell is completely depowered. You cannot activate it."
|
||||
return
|
||||
|
||||
var/be_swarmer = alert("Become a swarmer? (Warning, You can no longer be cloned!)",,"Yes","No")
|
||||
if(be_swarmer == "No")
|
||||
return
|
||||
if(crit_fail)
|
||||
user << "Swarmer has been depowered."
|
||||
return
|
||||
if(qdeleted(src))
|
||||
user << "Swarmer has been occupied by someone else."
|
||||
return
|
||||
var/mob/living/simple_animal/hostile/swarmer/S = new /mob/living/simple_animal/hostile/swarmer(get_turf(loc))
|
||||
S.key = user.key
|
||||
qdel(src)
|
||||
|
||||
|
||||
/obj/item/device/unactivated_swarmer/deactivated
|
||||
name = "deactivated swarmer"
|
||||
desc = "A shell of swarmer that was completely powered down. It no longer can activate itself."
|
||||
crit_fail = 1
|
||||
|
||||
|
||||
////The Mob itself////
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer
|
||||
name = "Swarmer"
|
||||
unique_name = 1
|
||||
icon = 'icons/mob/swarmer.dmi'
|
||||
desc = "A robot of unknown design, they seek only to consume materials and replicate themselves indefinitely."
|
||||
speak_emote = list("tones")
|
||||
bubble_icon = "swarmer"
|
||||
health = 40
|
||||
maxHealth = 40
|
||||
status_flags = CANPUSH
|
||||
icon_state = "swarmer"
|
||||
icon_living = "swarmer"
|
||||
icon_dead = "swarmer_unactivated"
|
||||
icon_gib = null
|
||||
wander = 0
|
||||
harm_intent_damage = 5
|
||||
minbodytemp = 0
|
||||
maxbodytemp = 500
|
||||
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)
|
||||
unsuitable_atmos_damage = 0
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 15
|
||||
melee_damage_type = STAMINA
|
||||
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
|
||||
hud_possible = list(ANTAG_HUD, DIAG_STAT_HUD, DIAG_HUD)
|
||||
languages_spoken = SWARMER
|
||||
languages_understood = SWARMER
|
||||
environment_smash = 0
|
||||
attacktext = "shocks"
|
||||
attack_sound = 'sound/effects/EMPulse.ogg'
|
||||
friendly = "pinches"
|
||||
speed = 0
|
||||
faction = list("swarmer")
|
||||
AIStatus = AI_OFF
|
||||
pass_flags = PASSTABLE
|
||||
mob_size = MOB_SIZE_TINY
|
||||
ventcrawler = 2
|
||||
ranged = 1
|
||||
projectiletype = /obj/item/projectile/beam/disabler
|
||||
ranged_cooldown_time = 20
|
||||
projectilesound = 'sound/weapons/taser2.ogg'
|
||||
loot = list(/obj/effect/decal/cleanable/robot_debris, /obj/item/weapon/ore/bluespace_crystal/artificial)
|
||||
del_on_death = 1
|
||||
deathmessage = "explodes with a sharp pop!"
|
||||
var/resources = 0 //Resource points, generated by consuming metal/glass
|
||||
var/max_resources = 100
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/Login()
|
||||
..()
|
||||
src << "<b>You are a swarmer, a weapon of a long dead civilization. Until further orders from your original masters are received, you must continue to consume and replicate.</b>"
|
||||
src << "<b>Clicking on any object will try to consume it, either deconstructing it into its components, destroying it, or integrating any materials it has into you if successful.</b>"
|
||||
src << "<b>Ctrl-Clicking on a mob will attempt to remove it from the area and place it in a safe environment for storage.</b>"
|
||||
src << "<b>Objectives:</b>"
|
||||
src << "1. Consume resources and replicate until there are no more resources left."
|
||||
src << "2. Ensure that this location is fit for invasion at a later date; do not perform actions that would render it dangerous or inhospitable."
|
||||
src << "3. Biological resources will be harvested at a later date; do not harm them."
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/New()
|
||||
..()
|
||||
verbs -= /mob/living/verb/pulled
|
||||
var/datum/atom_hud/data/diagnostic/diag_hud = huds[DATA_HUD_DIAGNOSTIC]
|
||||
diag_hud.add_to_hud(src)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/med_hud_set_health()
|
||||
var/image/holder = hud_list[DIAG_HUD]
|
||||
holder.icon_state = "huddiag[RoundDiagBar(health/maxHealth)]"
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/med_hud_set_status()
|
||||
var/image/holder = hud_list[DIAG_STAT_HUD]
|
||||
holder.icon_state = "hudstat"
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/Stat()
|
||||
..()
|
||||
if(statpanel("Status"))
|
||||
stat("Resources:",resources)
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/get_spans()
|
||||
return ..() | SPAN_ROBOT
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/emp_act()
|
||||
if(health > 1)
|
||||
adjustHealth(health-1)
|
||||
else
|
||||
death()
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/CanPass(atom/movable/O)
|
||||
if(istype(O, /obj/item/projectile/beam/disabler))//Allows for swarmers to fight as a group without wasting their shots hitting each other
|
||||
return 1
|
||||
if(isswarmer(O))
|
||||
return 1
|
||||
..()
|
||||
|
||||
////CTRL CLICK FOR SWARMERS AND SWARMER_ACT()'S////
|
||||
/mob/living/simple_animal/hostile/swarmer/AttackingTarget()
|
||||
if(!isliving(target))
|
||||
target.swarmer_act(src)
|
||||
else
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/CtrlClickOn(atom/A)
|
||||
face_atom(A)
|
||||
if(!isturf(loc))
|
||||
return
|
||||
if(next_move > world.time)
|
||||
return
|
||||
if(!A.Adjacent(src))
|
||||
return
|
||||
A.swarmer_act(src)
|
||||
|
||||
/atom/proc/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S.DisIntegrate(src)
|
||||
|
||||
/obj/item/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S.Integrate(src)
|
||||
|
||||
/obj/item/weapon/gun/swarmer_act()//Stops you from eating the entire armory
|
||||
return
|
||||
|
||||
/turf/open/floor/swarmer_act()//ex_act() on turf calls it on its contents, this is to prevent attacking mobs by DisIntegrate()'ing the floor
|
||||
return
|
||||
|
||||
/obj/machinery/atmospherics/swarmer_act()
|
||||
return
|
||||
|
||||
/obj/structure/disposalpipe/swarmer_act()
|
||||
return
|
||||
|
||||
/obj/machinery/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S.DismantleMachine(src)
|
||||
|
||||
/obj/machinery/light/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S.DisIntegrate(src)
|
||||
|
||||
/obj/machinery/door/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S.DisIntegrate(src)
|
||||
|
||||
/obj/machinery/camera/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S.DisIntegrate(src)
|
||||
toggle_cam(S, 0)
|
||||
|
||||
/obj/machinery/particle_accelerator/control_box/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S.DisIntegrate(src)
|
||||
|
||||
/obj/machinery/field/generator/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S.DisIntegrate(src)
|
||||
|
||||
/obj/machinery/gravity_generator/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S.DisIntegrate(src)
|
||||
|
||||
/obj/machinery/vending/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)//It's more visually interesting than dismantling the machine
|
||||
S.DisIntegrate(src)
|
||||
|
||||
/obj/machinery/turretid/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S.DisIntegrate(src)
|
||||
|
||||
/obj/machinery/chem_dispenser/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S << "<span class='warning'>The volatile chemicals in this machine would destroy us. Aborting.</span>"
|
||||
|
||||
/obj/machinery/nuclearbomb/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S << "<span class='warning'>This device's destruction would result in the extermination of everything in the area. Aborting.</span>"
|
||||
|
||||
/obj/machinery/dominator/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S << "<span class='warning'>This device is attempting to corrupt our entire network; attempting to interact with it is too risky. Aborting.</span>"
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/gang/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S << "<span class='warning'>Searching... sensor malfunction! Target lost. Aborting.</span>"
|
||||
|
||||
/obj/effect/rune/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S << "<span class='warning'>Searching... sensor malfunction! Target lost. Aborting.</span>"
|
||||
|
||||
/obj/structure/reagent_dispensers/fueltank/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S << "<span class='warning'>Destroying this object would cause a chain reaction. Aborting.</span>"
|
||||
|
||||
/obj/structure/cable/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S << "<span class='warning'>Disrupting the power grid would bring no benefit to us. Aborting.</span>"
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S << "<span class='warning'>An inhospitable area may be created as a result of destroying this object. Aborting.</span>"
|
||||
|
||||
/obj/machinery/telecomms/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S << "<span class='warning'>This communications relay should be preserved, it will be a useful resource to our masters in the future. Aborting.</span>"
|
||||
|
||||
/obj/machinery/message_server/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S << "<span class='warning'>This communications relay should be preserved, it will be a useful resource to our masters in the future. Aborting.</span>"
|
||||
|
||||
/obj/machinery/blackbox_recorder/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S << "<span class='warning'>This machine has recorded large amounts of data on this structure and its inhabitants, it will be a useful resource to our masters in the future. Aborting. </span>"
|
||||
|
||||
/obj/machinery/power/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S << "<span class='warning'>Disrupting the power grid would bring no benefit to us. Aborting.</span>"
|
||||
|
||||
/obj/machinery/gateway/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S << "<span class='warning'>This bluespace source will be important to us later. Aborting.</span>"
|
||||
|
||||
/turf/closed/wall/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
for(var/turf/T in range(1, src))
|
||||
if(istype(T, /turf/open/space) || istype(T.loc, /area/space))
|
||||
S << "<span class='warning'>Destroying this object has the potential to cause a hull breach. Aborting.</span>"
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/structure/window/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
for(var/turf/T in range(1, src))
|
||||
if(istype(T, /turf/open/space) || istype(T.loc, /area/space))
|
||||
S << "<span class='warning'>Destroying this object has the potential to cause a hull breach. Aborting.</span>"
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/item/stack/cable_coil/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)//Wiring would be too effective as a resource
|
||||
S << "<span class='warning'>This object does not contain enough materials to work with.</span>"
|
||||
|
||||
/obj/machinery/porta_turret/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S << "<span class='warning'>Attempting to dismantle this machine would result in an immediate counterattack. Aborting.</span>"
|
||||
|
||||
/mob/living/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S.DisperseTarget(src)
|
||||
|
||||
/mob/living/simple_animal/slime/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S << "<span class='warning'>This biological resource is somehow resisting our bluespace transceiver. Aborting.</span>"
|
||||
|
||||
/obj/machinery/droneDispenser/swarmer/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
S << "<span class='warning'>This object is receiving unactivated swarmer shells to help us. Aborting.</span>"
|
||||
|
||||
/obj/item/device/unactivated_swarmer/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
|
||||
if(S.resources + 50 > S.max_resources)
|
||||
S << "<span class='warning'>We have too many resources to reconsume this shell. Aborting.</span>"
|
||||
else
|
||||
..()
|
||||
S.resources += 49 //refund the whole thing
|
||||
|
||||
////END CTRL CLICK FOR SWARMERS////
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/proc/Fabricate(var/atom/fabrication_object,var/fabrication_cost = 0)
|
||||
if(!isturf(loc))
|
||||
src << "<span class='warning'>This is not a suitable location for fabrication. We need more space.</span>"
|
||||
if(resources >= fabrication_cost)
|
||||
resources -= fabrication_cost
|
||||
else
|
||||
src << "<span class='warning'>You do not have the necessary resources to fabricate this object.</span>"
|
||||
return 0
|
||||
new fabrication_object(loc)
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/proc/Integrate(var/obj/item/target)
|
||||
if(resources >= max_resources)
|
||||
src << "<span class='warning'>We cannot hold more materials!</span>"
|
||||
return
|
||||
if((target.materials[MAT_METAL]) || (target.materials[MAT_GLASS]))
|
||||
resources++
|
||||
do_attack_animation(target)
|
||||
changeNext_move(CLICK_CD_MELEE)
|
||||
var/obj/effect/overlay/temp/swarmer/integrate/I = PoolOrNew(/obj/effect/overlay/temp/swarmer/integrate, get_turf(target))
|
||||
I.pixel_x = target.pixel_x
|
||||
I.pixel_y = target.pixel_y
|
||||
I.pixel_z = target.pixel_z
|
||||
if(istype(target, /obj/item/stack))
|
||||
var/obj/item/stack/S = target
|
||||
S.use(1)
|
||||
if(S.amount)
|
||||
return
|
||||
qdel(target)
|
||||
else
|
||||
src << "<span class='warning'>\the [target] is incompatible with our internal matter recycler.</span>"
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/proc/DisIntegrate(var/atom/movable/target)
|
||||
PoolOrNew(/obj/effect/overlay/temp/swarmer/disintegration, get_turf(target))
|
||||
do_attack_animation(target)
|
||||
changeNext_move(CLICK_CD_MELEE)
|
||||
target.ex_act(3)
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/proc/DisperseTarget(var/mob/living/target)
|
||||
if(target == src)
|
||||
return
|
||||
|
||||
if(src.z != ZLEVEL_STATION)
|
||||
src << "<span class='warning'>Our bluespace transceiver cannot \
|
||||
locate a viable bluespace link, our teleportation abilities \
|
||||
are useless in this area.</span>"
|
||||
return
|
||||
|
||||
src << "<span class='info'>Attempting to remove this being from \
|
||||
our presence.</span>"
|
||||
|
||||
if(!do_mob(src, target, 30))
|
||||
return
|
||||
|
||||
var/turf/open/floor/F = find_safe_turf(zlevel=ZLEVEL_STATION)
|
||||
if(!F)
|
||||
return
|
||||
// If we're getting rid of a human, slap some energy cuffs on
|
||||
// them to keep them away from us a little longer
|
||||
|
||||
var/mob/living/carbon/human/H = target
|
||||
if(ishuman(target) && (!H.handcuffed))
|
||||
H.handcuffed = new /obj/item/weapon/restraints/handcuffs/energy/used(H)
|
||||
H.update_handcuffed()
|
||||
add_logs(src, H, "handcuffed")
|
||||
|
||||
var/datum/effect_system/spark_spread/S = new
|
||||
S.set_up(4,0,get_turf(target))
|
||||
S.start()
|
||||
playsound(src,'sound/effects/sparks4.ogg',50,1)
|
||||
do_teleport(target, F, 0)
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/proc/DismantleMachine(var/obj/machinery/target)
|
||||
do_attack_animation(target)
|
||||
src << "<span class='info'>We begin to dismantle this machine. We will need to be uninterrupted.</span>"
|
||||
var/obj/effect/overlay/temp/swarmer/dismantle/D = PoolOrNew(/obj/effect/overlay/temp/swarmer/dismantle, get_turf(target))
|
||||
D.pixel_x = target.pixel_x
|
||||
D.pixel_y = target.pixel_y
|
||||
D.pixel_z = target.pixel_z
|
||||
if(do_mob(src, target, 100))
|
||||
src << "<span class='info'>Dismantling complete.</span>"
|
||||
var/obj/item/stack/sheet/metal/M = new /obj/item/stack/sheet/metal(target.loc)
|
||||
M.amount = 5
|
||||
for(var/obj/item/I in target.component_parts)
|
||||
I.loc = M.loc
|
||||
var/obj/effect/overlay/temp/swarmer/disintegration/N = PoolOrNew(/obj/effect/overlay/temp/swarmer/disintegration, get_turf(target))
|
||||
N.pixel_x = target.pixel_x
|
||||
N.pixel_y = target.pixel_y
|
||||
N.pixel_z = target.pixel_z
|
||||
target.dropContents()
|
||||
if(istype(target, /obj/machinery/computer))
|
||||
var/obj/machinery/computer/C = target
|
||||
if(C.circuit)
|
||||
C.circuit.loc = M.loc
|
||||
qdel(target)
|
||||
|
||||
|
||||
/obj/effect/swarmer //Default swarmer effect object visual feedback
|
||||
name = "swarmer ui"
|
||||
desc = null
|
||||
gender = NEUTER
|
||||
icon = 'icons/mob/swarmer.dmi'
|
||||
icon_state = "ui_light"
|
||||
mouse_opacity = 0
|
||||
layer = MOB_LAYER
|
||||
unacidable = 1
|
||||
|
||||
/obj/effect/overlay/temp/swarmer //temporary swarmer visual feedback objects
|
||||
icon = 'icons/mob/swarmer.dmi'
|
||||
layer = BELOW_MOB_LAYER
|
||||
|
||||
/obj/effect/overlay/temp/swarmer/disintegration
|
||||
icon_state = "disintegrate"
|
||||
duration = 10
|
||||
|
||||
/obj/effect/overlay/temp/swarmer/disintegration/New()
|
||||
playsound(src.loc, "sparks", 100, 1)
|
||||
..()
|
||||
|
||||
/obj/effect/overlay/temp/swarmer/dismantle
|
||||
icon_state = "dismantle"
|
||||
duration = 25
|
||||
|
||||
/obj/effect/overlay/temp/swarmer/integrate
|
||||
icon_state = "integrate"
|
||||
duration = 5
|
||||
|
||||
/obj/effect/swarmer/destructible //Default destroyable object for swarmer constructions
|
||||
luminosity = 1
|
||||
mouse_opacity = 1
|
||||
var/health = 30
|
||||
|
||||
/obj/effect/swarmer/destructible/proc/take_damage(damage, damage_type = BRUTE, sound_effect = 1)
|
||||
switch(damage_type)
|
||||
if(BRUTE)
|
||||
if(sound_effect)
|
||||
playsound(loc, 'sound/weapons/Egloves.ogg', 80, 1)
|
||||
if(BURN)
|
||||
if(sound_effect)
|
||||
playsound(src.loc, 'sound/items/Welder.ogg', 100, 1)
|
||||
else
|
||||
return
|
||||
health -= damage
|
||||
if(health <= 0)
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/swarmer/destructible/bullet_act(obj/item/projectile/P)
|
||||
. = ..()
|
||||
take_damage(P.damage, P.damage_type)
|
||||
|
||||
/obj/effect/swarmer/destructible/attacked_by(obj/item/I, mob/living/user)
|
||||
..()
|
||||
take_damage(I.force, I.damtype)
|
||||
|
||||
/obj/effect/swarmer/destructible/ex_act()
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/swarmer/destructible/blob_act(obj/effect/blob/B)
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/swarmer/destructible/emp_act()
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/swarmer/destructible/attack_alien(mob/living/user)
|
||||
user.do_attack_animation(src)
|
||||
user.changeNext_move(CLICK_CD_MELEE)
|
||||
take_damage(rand(20,30))
|
||||
|
||||
/obj/effect/swarmer/destructible/attack_animal(mob/living/simple_animal/S)
|
||||
S.do_attack_animation(src)
|
||||
S.changeNext_move(CLICK_CD_MELEE)
|
||||
if(S.melee_damage_upper)
|
||||
take_damage(rand(S.melee_damage_lower, S.melee_damage_upper), S.melee_damage_type)
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/proc/CreateTrap()
|
||||
set name = "Create trap"
|
||||
set category = "Swarmer"
|
||||
set desc = "Creates a simple trap that will non-lethally electrocute anything that steps on it. Costs 5 resources"
|
||||
if(/obj/effect/swarmer/destructible/trap in loc)
|
||||
src << "<span class='warning'>There is already a trap here. Aborting.</span>"
|
||||
return
|
||||
Fabricate(/obj/effect/swarmer/destructible/trap, 5)
|
||||
|
||||
/obj/effect/swarmer/destructible/trap
|
||||
name = "swarmer trap"
|
||||
desc = "A quickly assembled trap that electrifies living beings and overwhelms machine sensors. Will not retain its form if damaged enough."
|
||||
icon_state = "trap"
|
||||
luminosity = 1
|
||||
health = 10
|
||||
|
||||
/obj/effect/swarmer/destructible/trap/Crossed(var/atom/movable/AM)
|
||||
if(isliving(AM))
|
||||
var/mob/living/L = AM
|
||||
if(!istype(L, /mob/living/simple_animal/hostile/swarmer))
|
||||
playsound(loc,'sound/effects/snap.ogg',50, 1, -1)
|
||||
L.electrocute_act(0, src, 1, 1)
|
||||
if(isrobot(L))
|
||||
L.Weaken(5)
|
||||
qdel(src)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/proc/CreateBarricade()
|
||||
set name = "Create barricade"
|
||||
set category = "Swarmer"
|
||||
set desc = "Creates a barricade that will stop anything but swarmers and disabler beams from passing through."
|
||||
if(/obj/effect/swarmer/destructible/blockade in loc)
|
||||
src << "<span class='warning'>There is already a blockade here. Aborting.</span>"
|
||||
return
|
||||
if(resources < 5)
|
||||
src << "<span class='warning'>We do not have the resources for this!</span>"
|
||||
return
|
||||
if(do_mob(src, src, 10))
|
||||
Fabricate(/obj/effect/swarmer/destructible/blockade, 5)
|
||||
return
|
||||
|
||||
/obj/effect/swarmer/destructible/blockade
|
||||
name = "swarmer blockade"
|
||||
desc = "A quickly assembled energy blockade. Will not retain its form if damaged enough, but disabler beams and swarmers pass right through."
|
||||
icon_state = "barricade"
|
||||
luminosity = 1
|
||||
health = 50
|
||||
density = 1
|
||||
anchored = 1
|
||||
|
||||
/obj/effect/swarmer/destructible/blockade/CanPass(atom/movable/O)
|
||||
if(isswarmer(O))
|
||||
return 1
|
||||
if(istype(O, /obj/item/projectile/beam/disabler))
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/proc/CreateSwarmer()
|
||||
set name = "Replicate"
|
||||
set category = "Swarmer"
|
||||
set desc = "Creates a shell for a new swarmer. Swarmers will self activate."
|
||||
src << "<span class='info'>We are attempting to replicate ourselves. We will need to stand still until the process is complete.</span>"
|
||||
if(resources < 50)
|
||||
src << "<span class='warning'>We do not have the resources for this!</span>"
|
||||
return
|
||||
if(!isturf(loc))
|
||||
src << "<span class='warning'>This is not a suitable location for replicating ourselves. We need more room.</span>"
|
||||
return
|
||||
if(do_mob(src, src, 100))
|
||||
if(Fabricate(/obj/item/device/unactivated_swarmer, 50))
|
||||
playsound(loc,'sound/items/poster_being_created.ogg',50, 1, -1)
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/proc/RepairSelf()
|
||||
set name = "Self Repair"
|
||||
set category = "Swarmer"
|
||||
set desc = "Attempts to repair damage to our body. You will have to remain motionless until repairs are complete."
|
||||
if(!isturf(loc))
|
||||
return
|
||||
src << "<span class='info'>Attempting to repair damage to our body, stand by...</span>"
|
||||
if(do_mob(src, src, 100))
|
||||
adjustHealth(-100)
|
||||
src << "<span class='info'>We successfully repaired ourselves.</span>"
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/proc/ToggleLight()
|
||||
if(!luminosity)
|
||||
SetLuminosity(3)
|
||||
else
|
||||
SetLuminosity(0)
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/proc/ContactSwarmers()
|
||||
var/message = input(src, "Announce to other swarmers", "Swarmer contact")
|
||||
// TODO get swarmers their own colour rather than just boldtext
|
||||
var/rendered = "<B>Swarm communication - [src]</b> states: [say_quote(message, get_spans())]"
|
||||
if(message)
|
||||
for(var/mob/M in mob_list)
|
||||
if(isswarmer(M))
|
||||
M << rendered
|
||||
if(isobserver(M))
|
||||
var/link = FOLLOW_LINK(M, src)
|
||||
M << "[link] [rendered]"
|
||||
@@ -0,0 +1,31 @@
|
||||
/datum/round_event_control/spawn_swarmer
|
||||
name = "Spawn Swarmer Shell"
|
||||
typepath = /datum/round_event/spawn_swarmer
|
||||
weight = 7
|
||||
max_occurrences = 1 //Only once okay fam
|
||||
earliest_start = 18000 //30 minutes
|
||||
min_players = 15
|
||||
|
||||
|
||||
/datum/round_event/spawn_swarmer
|
||||
|
||||
|
||||
/datum/round_event/spawn_swarmer/start()
|
||||
if(find_swarmer())
|
||||
return 0
|
||||
if(!the_gateway)
|
||||
return 0
|
||||
new /obj/item/device/unactivated_swarmer(get_turf(the_gateway))
|
||||
if(prob(25)) //25% chance to announce it to the crew
|
||||
var/swarmer_report = "<font size=3><b>[command_name()] High-Priority Update</b></span>"
|
||||
swarmer_report += "<br><br>Our long-range sensors have detected an odd signal emanating from your station's gateway. We recommend immediate investigation of your gateway, as something may have come \
|
||||
through."
|
||||
print_command_report(swarmer_report,"Classified [command_name()] Update")
|
||||
priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/AI/commandreport.ogg')
|
||||
|
||||
|
||||
/datum/round_event/spawn_swarmer/proc/find_swarmer()
|
||||
for(var/mob/living/M in mob_list)
|
||||
if(istype(M, /mob/living/simple_animal/hostile/swarmer) && M.client) //If there is a swarmer with an active client, we've found our swarmer
|
||||
return 1
|
||||
return 0
|
||||
@@ -0,0 +1,765 @@
|
||||
#define STATE_JUDGE 0
|
||||
#define STATE_WRATH 1
|
||||
#define STATE_FLEE 2
|
||||
|
||||
/mob/living/simple_animal/hostile/hades
|
||||
name = "hades"
|
||||
real_name = "hades"
|
||||
desc = "A strange being, clad in dark robes. Their very presence radiates an uneasy power."
|
||||
speak_emote = list("preaches","announces","spits","conveys")
|
||||
emote_hear = list("hums.","prays.")
|
||||
response_help = "kneels before"
|
||||
response_disarm = "flails at"
|
||||
response_harm = "punches"
|
||||
icon = 'icons/mob/EvilPope.dmi'
|
||||
icon_state = "EvilPope"
|
||||
icon_living = "EvilPope"
|
||||
icon_dead = "popedeath"
|
||||
speed = 1
|
||||
a_intent = "harm"
|
||||
status_flags = CANPUSH
|
||||
attack_sound = 'sound/magic/MAGIC_MISSILE.ogg'
|
||||
death_sound = 'sound/magic/Teleport_diss.ogg'
|
||||
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 = INFINITY
|
||||
faction = list("hades")
|
||||
attacktext = "strikes with an unholy rage at"
|
||||
maxHealth = 1000
|
||||
health = 1000
|
||||
healable = 0
|
||||
environment_smash = 3
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 20
|
||||
see_in_dark = 8
|
||||
see_invisible = SEE_INVISIBLE_MINIMUM
|
||||
loot = list(/obj/effect/decal/cleanable/blood)
|
||||
del_on_death = 0
|
||||
deathmessage = "begins to sizzle and pop, their flesh peeling away like paper."
|
||||
|
||||
var/isDoingDeath = FALSE
|
||||
var/isFleeing = FALSE
|
||||
var/fleeTimes = 0
|
||||
var/currentState = STATE_JUDGE
|
||||
var/rageLevel = 0
|
||||
|
||||
var/maxWrathTimer = 150
|
||||
var/lastWrathTimer = 0
|
||||
|
||||
var/timeBetweenGrabs = 60
|
||||
var/lastGrabTime = 0
|
||||
|
||||
var/list/validSins = list("Greed","Gluttony","Pride","Lust","Envy","Sloth","Wrath")
|
||||
var/lastsinPerson = 0
|
||||
var/sinPersonTime = 300
|
||||
var/lastFlee = 0
|
||||
var/fleeTimer = 30
|
||||
var/fakesinPersonChance = 60
|
||||
var/list/sinPersonsayings = list("You revel in only your own greed.",\
|
||||
"There is nothing but your absolution.",\
|
||||
"Your choices have led you to this.",\
|
||||
"There is only one way out.",\
|
||||
"The only way to be free is to be free of yourself.",\
|
||||
"Wallow in sin, and give yourself unto darkness.",\
|
||||
"Only the truly sinful may stand.",\
|
||||
"Find yourself and you will find Absolution.",\
|
||||
"Forego the pain of this process, and submit.",\
|
||||
"We can be one in suffering.",\
|
||||
"You stand on the precipice of ascension, give in.",\
|
||||
"You cannot fathom what lies beyond",\
|
||||
"Repent your sins.",\
|
||||
"This is the eve of your last days.",\
|
||||
"Darkness comes.")
|
||||
|
||||
var/list/creepyasssounds = list('sound/hallucinations/behind_you1.ogg', 'sound/hallucinations/behind_you2.ogg', \
|
||||
'sound/hallucinations/growl3.ogg', 'sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg',\
|
||||
'sound/hallucinations/i_see_you1.ogg', 'sound/hallucinations/i_see_you2.ogg',\
|
||||
'sound/hallucinations/look_up1.ogg', 'sound/hallucinations/look_up2.ogg', 'sound/hallucinations/over_here1.ogg',\
|
||||
'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg',\
|
||||
'sound/hallucinations/turn_around1.ogg', 'sound/hallucinations/turn_around2.ogg')
|
||||
|
||||
var/obj/effect/proc_holder/spell/targeted/lightning/sinLightning
|
||||
|
||||
var/list/currentAcolytes = list()
|
||||
|
||||
/mob/living/simple_animal/hostile/hades/New()
|
||||
..()
|
||||
|
||||
sinLightning = new/obj/effect/proc_holder/spell/targeted/lightning(src)
|
||||
|
||||
sinLightning.charge_max = 1
|
||||
sinLightning.clothes_req = 0
|
||||
sinLightning.range = 32
|
||||
sinLightning.cooldown_min = 1
|
||||
|
||||
lastsinPerson = world.time
|
||||
var/list/possible_titles = list("Pope","Bishop","Lord","Cardinal","Deacon","Pontiff")
|
||||
var/chosen = "Hades, [pick(possible_titles)] of Sin"
|
||||
name = chosen
|
||||
real_name = chosen
|
||||
|
||||
world << "<span class='warning'><font size=4>[name] has entered your reality. Kneel before them.</font></span>"
|
||||
world << 'sound/effects/pope_entry.ogg'
|
||||
|
||||
Appear(get_turf(src))
|
||||
|
||||
/mob/living/simple_animal/hostile/hades/handle_environment(datum/gas_mixture/environment)
|
||||
//space popes are from space, they need not your fickle "oxygen"
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/hostile/hades/handle_temperature_damage()
|
||||
//space popes are from space, they don't uh.. something fire burny death
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/hostile/hades/death(gibbed)
|
||||
if(!isDoingDeath)
|
||||
notransform = TRUE
|
||||
anchored = TRUE
|
||||
src.visible_message("<span class='warning'><font size=4>[src] begins to twist and distort, before snapping backwards with a sickening crunch.</font></span>")
|
||||
spawn(20)
|
||||
src.visible_message("<span class='warning'><font size=4>[src] is being sucked back to their own realm, destabilizing the fabric of time and space itself!</font></span>")
|
||||
playsound(get_turf(src), 'sound/effects/hyperspace_begin.ogg', 100, 1)
|
||||
isDoingDeath = TRUE
|
||||
AIStatus = AI_OFF
|
||||
SpinAnimation()
|
||||
for(var/i in 1 to 5)
|
||||
for(var/turf/T in spiral_range_turfs(i,src))
|
||||
addtimer(src, "sinShed", i*10, FALSE, T)
|
||||
spawn(60) // required to be spawn so we can call death's ..() to complete death.
|
||||
SpinAnimation(0,0)
|
||||
explosion(get_turf(src), 0, 2, 4, 6, flame_range = 6)
|
||||
..()
|
||||
var/area/A = locate(/area/hades) in world
|
||||
if(A)
|
||||
var/turf/T = get_turf(locate(/obj/effect/landmark/event_spawn) in A)
|
||||
if(T)
|
||||
src.visible_message("<span class='warning'><font size=4>[src]'s Staff is flung free as their body explodes.</font></span>")
|
||||
var/obj/structure/ladder/unbreakable/hades/churchLadder = new/obj/structure/ladder/unbreakable/hades(T)
|
||||
var/obj/structure/ladder/unbreakable/hades/bodyLadder = new/obj/structure/ladder/unbreakable/hades(get_turf(src))
|
||||
var/obj/item/weapon/hades_staff/HS = new/obj/item/weapon/hades_staff(get_turf(src))
|
||||
HS.throw_at_fast(pick(orange(src,7)),10,1)
|
||||
churchLadder.up = bodyLadder
|
||||
bodyLadder.down = churchLadder
|
||||
qdel(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/hades/attackby(obj/item/I, mob/user, params)
|
||||
..()
|
||||
Defend(user,I)
|
||||
|
||||
/mob/living/simple_animal/hostile/hades/grabbedby(mob/living/carbon/user, supress_message = 0)
|
||||
..()
|
||||
Defend(user,user)
|
||||
|
||||
/mob/living/simple_animal/hostile/hades/hitby(atom/movable/AM, skipcatch, hitpush, blocked)
|
||||
..()
|
||||
lastsinPerson -= (sinPersonTime/4)
|
||||
if(istype(AM,/obj/item))
|
||||
var/obj/item/throwCast = AM
|
||||
Defend(throwCast.thrownby,AM)
|
||||
|
||||
/mob/living/simple_animal/hostile/hades/bullet_act(obj/item/projectile/P, def_zone)
|
||||
//don't call ..() because we're going to deflect it
|
||||
lastsinPerson -= (sinPersonTime/4)
|
||||
Defend(P.firer,P)
|
||||
return -1
|
||||
|
||||
/mob/living/simple_animal/hostile/hades/attack_hand(mob/living/carbon/human/M)
|
||||
..()
|
||||
lastsinPerson -= (sinPersonTime/4)
|
||||
Defend(M,M)
|
||||
|
||||
/mob/living/simple_animal/hostile/hades/proc/Defend(var/mob/attacker,var/source)
|
||||
if(!isDoingDeath)
|
||||
rageLevel += 5
|
||||
src.visible_message("<span class='warning'>[src] rounds on the [attacker], gazing at them with a [pick("cold","frosty","freezing","dark")] [pick("glare","gaze","glower","stare")].</span>")
|
||||
|
||||
if(istype(source,/obj/item/projectile))
|
||||
src.visible_message("<span class='warning'>[src] [pick("calmly","silently","nonchalantly")] waves their hand, deflecting the [source].</span>")
|
||||
var/obj/item/projectile/P = source
|
||||
if(P.starting)
|
||||
var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
|
||||
var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
|
||||
var/turf/curloc = get_turf(src)
|
||||
|
||||
P.original = locate(new_x, new_y, P.z)
|
||||
P.starting = curloc
|
||||
P.current = curloc
|
||||
P.firer = src
|
||||
P.yo = new_y - curloc.y
|
||||
P.xo = new_x - curloc.x
|
||||
P.Angle = null
|
||||
else
|
||||
if(prob(20))
|
||||
var/chosenDefend = rand(1,3)
|
||||
switch(chosenDefend)
|
||||
if(1)
|
||||
attacker.visible_message("<span class='warning'>[attacker] is lifted from the ground, shadowy powers tossing them aside.</span>")
|
||||
attacker.throw_at_fast(pick(orange(src,7)),10,1)
|
||||
if(2)
|
||||
attacker.visible_message("<span class='warning'>[attacker] crackles with electricity, a bolt leaping from [src] to them.</span>")
|
||||
sinLightning.Bolt(src,attacker,30,5,src)
|
||||
if(3)
|
||||
src.visible_message("<span class='warning'>[src] points his staff at [attacker], a swarm of eyeballs lurching fourth!</span>")
|
||||
for(var/i in 1 to 4)
|
||||
var/mob/living/simple_animal/hostile/carp/eyeball/E = new/mob/living/simple_animal/hostile/carp/eyeball(pick(orange(attacker,1)))
|
||||
E.faction = faction
|
||||
addtimer(E, "gib", 150, FALSE)
|
||||
|
||||
/mob/living/simple_animal/hostile/hades/proc/sinShed(var/turf/T)
|
||||
var/obj/effect/overlay/temp/cult/sparks/S = PoolOrNew(/obj/effect/overlay/temp/cult/sparks, T)
|
||||
S.anchored = FALSE
|
||||
S.throw_at_fast(src,10,1)
|
||||
PoolOrNew(/obj/effect/overlay/temp/hadesBlood, T)
|
||||
|
||||
/mob/living/simple_animal/hostile/hades/proc/Transfer(var/mob/living/taken, var/turf/transferTarget)
|
||||
if(transferTarget)
|
||||
playsound(get_turf(taken), 'sound/magic/Ethereal_Enter.ogg', 50, 1, -1)
|
||||
PoolOrNew(/obj/effect/overlay/temp/hadesFlick, get_turf(taken))
|
||||
taken.forceMove(transferTarget)
|
||||
Appear(get_turf(taken))
|
||||
|
||||
/mob/living/simple_animal/hostile/hades/proc/Appear(var/turf/where)
|
||||
var/obj/effect/timestop/hades/TS = new /obj/effect/timestop/hades(where)
|
||||
TS.immune = list(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/hades/Life()
|
||||
if(..() && !isDoingDeath) // appropriately check if we're alive now we leave a corpse
|
||||
if(health > maxHealth/4 && !isFleeing)
|
||||
if(rageLevel > 50)
|
||||
lastWrathTimer = world.time
|
||||
currentState = STATE_WRATH
|
||||
else
|
||||
currentState = STATE_JUDGE
|
||||
else
|
||||
if(world.time > lastFlee + fleeTimer)
|
||||
lastFlee = world.time
|
||||
isFleeing = TRUE
|
||||
currentState = STATE_FLEE
|
||||
|
||||
var/area/healthCheck = get_area(src)
|
||||
|
||||
if(istype(healthCheck,/area/chapel/main))
|
||||
if(currentState == STATE_FLEE)
|
||||
lastWrathTimer = world.time
|
||||
currentState = STATE_WRATH
|
||||
if(health < maxHealth/2)
|
||||
health += 10 // slowly regain hp in the chapel, up to a maximum of half our max
|
||||
|
||||
var/spokenThisTurn = FALSE
|
||||
for(var/mob/living/A in currentAcolytes)
|
||||
if(!A)
|
||||
currentAcolytes -= A
|
||||
continue
|
||||
if(A.health <= 0)
|
||||
rageLevel += 5
|
||||
if(!spokenThisTurn)
|
||||
spokenThisTurn = TRUE
|
||||
var/list/lossSayings = list("They were weak.","For every death, two more rise.",\
|
||||
"What is but one servant lost?","Darkness engulf you!","To the Pit with them.",\
|
||||
"Fools! All of you!","You can't stop me. You. Will. Be. JUDGED.")
|
||||
src.say(pick(lossSayings))
|
||||
currentAcolytes -= A
|
||||
A.gib()
|
||||
|
||||
if(currentState == STATE_WRATH) // we have been enraged.
|
||||
if(world.time > lastWrathTimer + maxWrathTimer)
|
||||
rageLevel = 0 // wind down if we're wrathful too long.
|
||||
rageLevel -= 1 // rage phase starts at 50, meaning roughly 20s of rage.
|
||||
if(currentAcolytes.len == 0)
|
||||
src.say("Rise, Servants. AID YOUR MASTER.")
|
||||
playsound(get_turf(src), 'sound/magic/CastSummon.ogg', 100, 1)
|
||||
for(var/i in 1 to 5)
|
||||
var/mob/living/simple_animal/hostile/hadesacolyte/HA = new/mob/living/simple_animal/hostile/hadesacolyte(get_turf(pick(orange(2,src))))
|
||||
HA.master = src
|
||||
currentAcolytes += HA
|
||||
if(target)
|
||||
if(world.time > lastGrabTime + timeBetweenGrabs)
|
||||
if(get_dist(src,target) > 4) // you can't run from us. upped to 4 to give more leeway.
|
||||
lastGrabTime = world.time
|
||||
var/list/fleeSayings = list("There is no escape from your sins, [target]","Fleeing will only make your punishment worse [target]!",\
|
||||
"There is nowhere you can hide, [target]!","You can't run, [target]!","Get back here, [target]!","You coward, [target]!",\
|
||||
"I will find you, [target]!","Return to me, [target]!")
|
||||
src.say(pick(fleeSayings))
|
||||
var/mob/living/toGrab = target
|
||||
toGrab.Beam(src,"blood",'icons/effects/beam.dmi',10)
|
||||
toGrab.Weaken(6)
|
||||
playsound(get_turf(src), 'sound/magic/CastSummon.ogg', 100, 1)
|
||||
toGrab.throw_at_fast(src,10,1)
|
||||
if(rageLevel >= 100)
|
||||
rageLevel = 50
|
||||
var/list/overboardSayings = list("Ashes! It will all be ashes!","I will bring about the apocolypse!",\
|
||||
"There will be nothing but your withered husks!","Face your doom, cretins!","There. Will. Be. ORDER!",\
|
||||
"I am your Lord, lay down your arms and submit.","Your souls will be cremated!",\
|
||||
"Only in death will you obey!","This is no person's fault but your own!")
|
||||
src.say(pick(overboardSayings))
|
||||
var/turf/StartLoc = get_turf(src)
|
||||
var/list/nearby = orange(6,src)
|
||||
var/slashCount = 0
|
||||
var/aoeType = rand(1,2) // just for future proofing
|
||||
for(var/mob/living/A in nearby)
|
||||
slashCount++
|
||||
A.Beam(src,"blood",'icons/effects/beam.dmi',10)
|
||||
spawn(slashCount + 3)
|
||||
if(aoeType == 1)
|
||||
// no more cheaping it out with non-player mobs like turrets or carps
|
||||
loc = get_turf(A)
|
||||
sinShed(StartLoc)
|
||||
A.attack_animal(src)
|
||||
PoolOrNew(/obj/effect/overlay/temp/hadesBlood,get_turf(A))
|
||||
playsound(get_turf(A), 'sound/magic/SummonItems_generic.ogg', 100, 1)
|
||||
if(aoeType == 2)
|
||||
sinShed(StartLoc)
|
||||
PoolOrNew(/obj/effect/overlay/temp/hadesBite,get_turf(A))
|
||||
A.Weaken(6)
|
||||
var/obj/effect/timestop/hades/large/TS = new /obj/effect/timestop/hades/large(StartLoc)
|
||||
TS.immune = list(src)
|
||||
spawn((slashCount+1)+3)
|
||||
loc = StartLoc
|
||||
|
||||
if(currentState == STATE_FLEE) // we've been wounded, let us flee and lick our wounds
|
||||
var/area/A = locate(/area/chapel/main) in world
|
||||
if(A)
|
||||
var/turf/T = get_turf(locate(/obj/effect/landmark/event_spawn) in A)
|
||||
if(!T)
|
||||
T = get_turf(src) // no event spawn in chapel, fall back to doing it on the spot.
|
||||
if(T)
|
||||
fleeTimes++
|
||||
Transfer(src,T)
|
||||
AIStatus = AI_OFF
|
||||
notransform = TRUE
|
||||
anchored = TRUE
|
||||
for(var/i in 1 to 5)
|
||||
spawn(i*10)
|
||||
for(var/turf/S in oview(i,src) - oview((i)-1,src))
|
||||
sinShed(S)
|
||||
health += maxHealth/(10*fleeTimes) // every flee we gain less HP
|
||||
spawn(50)
|
||||
isFleeing = FALSE
|
||||
notransform = FALSE
|
||||
anchored = FALSE
|
||||
AIStatus = AI_ON
|
||||
currentState = STATE_JUDGE
|
||||
lastsinPerson = 0 // immediately teleport away to judge
|
||||
|
||||
if(currentState == STATE_JUDGE) // our default state, judge a few people and tell them they're rude or something
|
||||
if(world.time > lastsinPerson + sinPersonTime)
|
||||
if(prob(fakesinPersonChance))
|
||||
lastsinPerson = world.time
|
||||
visible_message("<span class='warning'><font size=3>[pick(sinPersonsayings)]</font></span>")
|
||||
playsound(get_turf(src), pick(creepyasssounds), 100, 1)
|
||||
else
|
||||
lastsinPerson = world.time
|
||||
var/mob/living/carbon/human/sinPerson = pick(living_mob_list)
|
||||
var/depth = living_mob_list.len + 1 // just in case
|
||||
if(sinPerson) // no more finding nullcakes
|
||||
if(!sinPerson.ckey)
|
||||
while(!sinPerson.ckey && depth > 0)
|
||||
--depth
|
||||
var/checkPerson = pick(living_mob_list)
|
||||
if(checkPerson)
|
||||
sinPerson = checkPerson
|
||||
if(!sinPerson.ckey)
|
||||
// double check ensure that if the above loop fails to get a ckey target
|
||||
// we don't go and use the last mob checked, causing odd situations
|
||||
return
|
||||
if(sinPerson)
|
||||
if(prob(65)) // moderately high chance for us to go to them, else they come here.
|
||||
Transfer(src,get_turf(pick(oview(1,sinPerson))))
|
||||
else
|
||||
Transfer(sinPerson,get_turf(pick(oview(1,src))))
|
||||
var/sinPersonchoice = pick(validSins)
|
||||
switch(sinPersonchoice)
|
||||
if("Greed")
|
||||
src.say("Your sin, [sinPerson], is Greed.")
|
||||
if(prob(50))
|
||||
src.say("I will indulge your sin, [sinPerson].")
|
||||
sin_Greed(sinPerson, TRUE)
|
||||
else
|
||||
src.say("Your sin will be punished, [sinPerson]!")
|
||||
sin_Greed(sinPerson, FALSE)
|
||||
if("Gluttony")
|
||||
src.say("Your sin, [sinPerson], is Gluttony.")
|
||||
if(prob(50))
|
||||
src.say("I will indulge your sin, [sinPerson].")
|
||||
sin_Gluttony(sinPerson,TRUE)
|
||||
else
|
||||
src.say("Your sin will be punished, [sinPerson]!")
|
||||
sin_Gluttony(sinPerson,FALSE)
|
||||
if("Pride")
|
||||
src.say("Your sin, [sinPerson], is Pride.")
|
||||
if(prob(50))
|
||||
src.say("I will indulge your sin, [sinPerson].")
|
||||
sin_Pride(sinPerson,TRUE)
|
||||
else
|
||||
src.say("Your sin will be punished, [sinPerson]!")
|
||||
sin_Pride(sinPerson,FALSE)
|
||||
if("Lust")
|
||||
src.say("Your sin, [sinPerson], is Lust.")
|
||||
if(prob(50))
|
||||
src.say("I will indulge your sin, [sinPerson].")
|
||||
sin_Lust(sinPerson,TRUE)
|
||||
else
|
||||
src.say("Your sin will be punished, [sinPerson]!")
|
||||
sin_Lust(sinPerson,TRUE)
|
||||
if("Envy")
|
||||
src.say("Your sin, [sinPerson], is Envy.")
|
||||
if(prob(50))
|
||||
src.say("I will indulge your sin, [sinPerson].")
|
||||
sin_Envy(sinPerson,TRUE)
|
||||
else
|
||||
src.say("Your sin will be punished, [sinPerson]!")
|
||||
sin_Envy(sinPerson,FALSE)
|
||||
if("Sloth")
|
||||
src.say("Your sin, [sinPerson], is Sloth.")
|
||||
if(prob(50))
|
||||
src.say("I will indulge your sin, [sinPerson].")
|
||||
sin_Sloth(sinPerson,TRUE)
|
||||
else
|
||||
src.say("Your sin will be punished, [sinPerson]!")
|
||||
sin_Sloth(sinPerson,FALSE)
|
||||
if("Wrath")
|
||||
src.say("Your sin, [sinPerson], is Wrath.")
|
||||
if(prob(50))
|
||||
src.say("I will indulge your sin, [sinPerson].")
|
||||
sin_Wrath(sinPerson,TRUE)
|
||||
else
|
||||
src.say("Your sin will be punished, [sinPerson]!")
|
||||
sin_Wrath(sinPerson,FALSE)
|
||||
|
||||
|
||||
///Sin related things
|
||||
|
||||
//global Sin procs, shared between staff and pope
|
||||
|
||||
/proc/sin_Greed(var/mob/living/carbon/human/sinPerson, var/isIndulged)
|
||||
if(isIndulged)
|
||||
sinPerson << "<span class='warning'>You feel like you deserve more, in fact, you want everything.</span>"
|
||||
var/list/greed = list(/obj/item/stack/sheet/mineral/gold,/obj/item/stack/sheet/mineral/silver,/obj/item/stack/sheet/mineral/diamond)
|
||||
for(var/i in 1 to 10)
|
||||
var/greed_type = pick(greed)
|
||||
new greed_type(get_turf(sinPerson))
|
||||
else
|
||||
sinPerson << "<span class='warning'>Your body begins to shift and bend, changing to reflect your inner greed.</span>"
|
||||
var/mob/living/M = sinPerson.change_mob_type(/mob/living/simple_animal/cockroach,get_turf(sinPerson),"Greedroach",1)
|
||||
M.AddSpell(new/obj/effect/proc_holder/spell/targeted/mind_transfer)
|
||||
|
||||
/proc/sin_Gluttony(var/mob/living/carbon/human/sinPerson, var/isIndulged)
|
||||
if(isIndulged)
|
||||
sinPerson << "<span class='warning'>Your stomach growls, you feel hungry.</span>"
|
||||
var/list/allTypes = list()
|
||||
for(var/A in typesof(/obj/item/weapon/reagent_containers/food/snacks))
|
||||
var/obj/item/weapon/reagent_containers/food/snacks/O = A
|
||||
if(initial(O.cooked_type))
|
||||
allTypes += A
|
||||
for(var/i in 1 to 10)
|
||||
var/greed_type = pick(allTypes)
|
||||
new greed_type(get_turf(sinPerson))
|
||||
else
|
||||
sinPerson << "<span class='warning'>Your body begins to bloat and stretch, bile rising in your throat.</span>"
|
||||
sinPerson.reagents.add_reagent("nutriment",1000)
|
||||
|
||||
/proc/sin_Pride(var/mob/living/carbon/human/sinPerson, var/isIndulged)
|
||||
if(isIndulged)
|
||||
sinPerson << "<span class='warning'>You feel strong enough to take on the world.</span>"
|
||||
var/obj/item/weapon/twohanded/sin_pride/good = new/obj/item/weapon/twohanded/sin_pride(get_turf(sinPerson))
|
||||
good.name = "Indulged [good.name]"
|
||||
good.pride_direction = FALSE
|
||||
else
|
||||
sinPerson << "<span class='warning'>You feel small and weak, like the entire world is against you.</span>"
|
||||
var/obj/item/weapon/twohanded/sin_pride/bad = new/obj/item/weapon/twohanded/sin_pride(get_turf(sinPerson))
|
||||
bad.name = "Punished [bad.name]"
|
||||
bad.pride_direction = TRUE
|
||||
|
||||
/proc/sin_Lust(var/mob/living/carbon/human/sinPerson, var/isIndulged)
|
||||
if(isIndulged)
|
||||
sinPerson << "<span class='warning'>You feel confident, like everything and everyone is drawn to you.</span>"
|
||||
var/obj/item/lovestone/good = new/obj/item/lovestone(get_turf(sinPerson))
|
||||
good.name = "Indulged [good.name]"
|
||||
good.lust_direction = FALSE
|
||||
else
|
||||
sinPerson << "<span class='warning'>You feel lonely... the wish for the warmth of another spark through your mind.</span>"
|
||||
var/obj/item/lovestone/bad = new/obj/item/lovestone(get_turf(sinPerson))
|
||||
bad.name = "Punished [bad.name]"
|
||||
bad.lust_direction = TRUE
|
||||
|
||||
/proc/sin_Envy(var/mob/living/carbon/human/sinPerson, var/isIndulged)
|
||||
if(isIndulged)
|
||||
for(var/mob/living/carbon/human/H in player_list) // name lottery
|
||||
if(H == sinPerson)
|
||||
continue
|
||||
if(prob(25))
|
||||
sinPerson.name = H.name
|
||||
sinPerson.real_name = H.real_name
|
||||
var/datum/dna/lottery = H.dna
|
||||
lottery.transfer_identity(sinPerson, transfer_SE=1)
|
||||
sinPerson.updateappearance(mutcolor_update=1)
|
||||
sinPerson.domutcheck()
|
||||
sinPerson << "<span class='warning'>You feel envious of [sinPerson.name], and your body shifts to reflect that!</span>"
|
||||
else
|
||||
var/sinPersonspecies = pick(species_list)
|
||||
var/newtype = species_list[sinPersonspecies]
|
||||
sinPerson << "<span class='warning'>You wish for more from yourself.. your body shifts to suit your wish.</span>"
|
||||
sinPerson.set_species(newtype)
|
||||
|
||||
/proc/sin_Sloth(var/mob/living/carbon/human/sinPerson, var/isIndulged)
|
||||
if(isIndulged)
|
||||
sinPerson << "<span class='warning'>You feel tired...</span>"
|
||||
sinPerson.drowsyness += 100
|
||||
else
|
||||
sinPerson << "<span class='warning'>A chill comes over your body, the feeling of frostbite nipping at your fingers.</span>"
|
||||
sinPerson.reagents.add_reagent("frostoil", 50)
|
||||
|
||||
/proc/sin_Wrath(var/mob/living/carbon/human/sinPerson, var/isIndulged)
|
||||
if(isIndulged)
|
||||
sinPerson << "<span class='warning'>You feel wrathful, like you want to destroy everyone and everything.</span>"
|
||||
sinPerson.change_mob_type(/mob/living/simple_animal/slaughter,get_turf(sinPerson),"Wrath Demon",1)
|
||||
else
|
||||
sinPerson << "<span class='warning'>Your chest feels tight, and the world begins to spin around you.</span>"
|
||||
sinPerson.reagents.add_reagent("lexorin", 29)
|
||||
sinPerson.reagents.add_reagent("mindbreaker", 29)
|
||||
|
||||
/obj/effect/overlay/temp/hadesFlick
|
||||
name = "transdimensional waste"
|
||||
icon = 'icons/mob/mob.dmi'
|
||||
icon_state = "liquify"
|
||||
duration = 15
|
||||
|
||||
/obj/effect/overlay/temp/hadesBite
|
||||
name = "biting tendril"
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
icon_state = "tendril_bite"
|
||||
duration = 15
|
||||
|
||||
/obj/effect/overlay/temp/hadesBlood
|
||||
name = "blood plume"
|
||||
icon = 'icons/effects/128x128.dmi'
|
||||
icon_state = "spray_plume"
|
||||
duration = 30
|
||||
|
||||
/obj/effect/timestop/hades // custom timeslip to make him immune
|
||||
name = "Frozen Time"
|
||||
desc = "Time has slowed to a halt."
|
||||
|
||||
/obj/effect/timestop/hades/New()
|
||||
spawn(5)
|
||||
..()
|
||||
|
||||
/obj/effect/timestop/hades/large
|
||||
freezerange = 6
|
||||
icon_state = "huge_rune"
|
||||
icon = 'icons/effects/224x224.dmi'
|
||||
|
||||
|
||||
/obj/item/weapon/twohanded/sin_pride
|
||||
icon_state = "mjollnir0"
|
||||
name = "Pride-struck Hammer"
|
||||
desc = "It resonates an aura of Pride."
|
||||
force = 5
|
||||
throwforce = 15
|
||||
w_class = 4
|
||||
slot_flags = SLOT_BACK
|
||||
force_unwielded = 5
|
||||
force_wielded = 20
|
||||
attack_verb = list("attacked", "smashed", "crushed", "splattered", "cracked")
|
||||
hitsound = 'sound/weapons/blade1.ogg'
|
||||
var/pride_direction = FALSE
|
||||
|
||||
/obj/item/weapon/twohanded/sin_pride/update_icon()
|
||||
icon_state = "mjollnir[wielded]"
|
||||
return
|
||||
|
||||
/obj/item/weapon/twohanded/sin_pride/afterattack(atom/A as mob|obj|turf|area, mob/user, proximity)
|
||||
if(!proximity) return
|
||||
if(wielded)
|
||||
if(istype(A,/mob/living/carbon/human))
|
||||
var/mob/living/carbon/H = A
|
||||
var/mob/living/carbon/U = user
|
||||
if(H)
|
||||
if(pride_direction == FALSE)
|
||||
U.reagents.trans_to(H, user.reagents.total_volume, 1, 1, 0)
|
||||
U << "Your pride reflects on [H]."
|
||||
if(H.health > 0)
|
||||
U.health += force
|
||||
U.updatehealth()
|
||||
H.health -= force
|
||||
H.updatehealth()
|
||||
H << "You feel insecure, taking on [user]'s burden."
|
||||
else if(pride_direction == TRUE)
|
||||
H.reagents.trans_to(user, H.reagents.total_volume, 1, 1, 0)
|
||||
H << "Your pride reflects on [user]."
|
||||
if(U.health > 0)
|
||||
U.health -= force
|
||||
U.updatehealth()
|
||||
H.health += force
|
||||
H.updatehealth()
|
||||
U << "You feel insecure, taking on [H]'s burden."
|
||||
|
||||
/obj/item/lovestone
|
||||
name = "Stone of Lust"
|
||||
desc = "It lays within your hand, radiating pulses of uncomfortable warmth."
|
||||
icon = 'icons/obj/wizard.dmi'
|
||||
icon_state = "lovestone"
|
||||
item_state = "lovestone"
|
||||
w_class = 1
|
||||
var/lust_direction = FALSE
|
||||
var/lastUsage = 0
|
||||
var/usageTimer = 300
|
||||
|
||||
/obj/item/lovestone/attack_self(mob/user)
|
||||
if(world.time > lastUsage + usageTimer)
|
||||
lastUsage = world.time
|
||||
user.visible_message("<span class='warning'>[user] grips the [src] tightly, causing it to vibrate and pulse brightly.</span>")
|
||||
spawn(25)
|
||||
if(lust_direction == FALSE)
|
||||
var/list/throwAt = list()
|
||||
for(var/atom/movable/AM in oview(7,user))
|
||||
if(!AM.anchored && AM != user)
|
||||
throwAt.Add(AM)
|
||||
for(var/counter = 1, counter < throwAt.len, ++counter)
|
||||
var/atom/movable/cast = throwAt[counter]
|
||||
cast.throw_at_fast(user,10,1)
|
||||
else if(lust_direction == 1)
|
||||
var/mob/living/carbon/human/H = user
|
||||
var/mob/living/carbon/human/foundLover = locate(/mob/living/carbon/human) in orange(3,H)
|
||||
if(!foundLover)
|
||||
H << "As you hold the stone, loneliness grips you, your heart feeling heavy and you struggle to breath."
|
||||
for(var/i in 1 to 10)
|
||||
addtimer(H.reagents, "add_reagent", i*10, FALSE, "initropidril", i)
|
||||
else
|
||||
H << "You take comfort in the presence of [foundLover]"
|
||||
H.reagents.add_reagent("omnizine",25)
|
||||
H.Beam(foundLover,"r_beam",'icons/effects/beam.dmi',10)
|
||||
foundLover << "You take comfort in the presence of [H]"
|
||||
foundLover.reagents.add_reagent("omnizine",25)
|
||||
else
|
||||
user << "The stone lays inert. It is still recharging."
|
||||
|
||||
/mob/living/simple_animal/hostile/hadesacolyte
|
||||
name = "Acolyte of Hades"
|
||||
desc = "Darkness seethes from their every pore."
|
||||
icon_state = "hades_acolyte"
|
||||
icon_living = "hades_acolyte"
|
||||
icon_dead = "hades_acolyte_dead"
|
||||
speak_chance = 0
|
||||
turns_per_move = 5
|
||||
response_help = "trembles in fear of"
|
||||
response_disarm = "slaps wildly at"
|
||||
response_harm = "hits"
|
||||
speed = 1
|
||||
maxHealth = 45
|
||||
health = 45
|
||||
|
||||
harm_intent_damage = 5
|
||||
melee_damage_lower = 5
|
||||
melee_damage_upper = 15
|
||||
attacktext = "strikes at"
|
||||
attack_sound = 'sound/weapons/bladeslice.ogg'
|
||||
|
||||
butcher_results = list(/obj/item/clothing/mask/gas/cyborg/hades = 1,/obj/item/clothing/suit/hooded/chaplain_hoodie/hades = 1,/obj/item/weapon/hades_staff/fake = 1)
|
||||
|
||||
unsuitable_atmos_damage = 0
|
||||
del_on_death = 0
|
||||
faction = list("hades")
|
||||
|
||||
var/mob/living/simple_animal/hostile/hades/master
|
||||
|
||||
/mob/living/simple_animal/hostile/hadesacolyte/Life()
|
||||
if(..())
|
||||
if(master)
|
||||
if(get_dist(src,master) > 5)
|
||||
PoolOrNew(/obj/effect/overlay/temp/hadesFlick,get_turf(src))
|
||||
src.visible_message("<span class='warning'>[src] twists and distorts, before vanishing in a snap.</span>")
|
||||
src.forceMove(get_turf(pick(orange(2,master))))
|
||||
|
||||
//acolyte of hades outfit
|
||||
/obj/item/clothing/mask/gas/cyborg/hades
|
||||
name = "Skull Mask"
|
||||
desc = "It's a skull mask, made of a thin, cool metal."
|
||||
|
||||
/obj/item/clothing/suit/hooded/chaplain_hoodie/hades
|
||||
name = "Dark robes"
|
||||
desc = "A dark, soft robe."
|
||||
hooded = 1
|
||||
hoodtype = /obj/item/clothing/head/chaplain_hood/hades
|
||||
|
||||
/obj/item/clothing/head/chaplain_hood/hades
|
||||
name = "Dark hood"
|
||||
desc = "A dark, soft hood."
|
||||
body_parts_covered = HEAD
|
||||
flags_inv = HIDEHAIR|HIDEEARS
|
||||
//end hades outfit
|
||||
|
||||
/obj/item/hades_summoner
|
||||
name = "Dark Seed"
|
||||
desc = "The stone lays inert, but even when holding it you hear maddened whispers."
|
||||
icon = 'icons/obj/wizard.dmi'
|
||||
icon_state = "dark_seed_inert"
|
||||
item_state = "dark_seed_inert"
|
||||
w_class = 1
|
||||
var/isActivated = FALSE
|
||||
var/whoActivated
|
||||
var/countDownToSummon = 10
|
||||
var/absorbedHP = 0
|
||||
|
||||
/obj/item/hades_summoner/attack_self(mob/user)
|
||||
if(!isActivated)
|
||||
var/choice = input(user,"Rub the stone?",name) in list("Yes","No")
|
||||
if(choice == "Yes")
|
||||
if(prob(15))
|
||||
user << "<span class='warning'><font size=3>You rub the stone.. and a voice springs fourth!</font></span>"
|
||||
user << "<span class='warning'><font size=3>You hear a voice in your head.. 'Bring me the flesh of a living being.. and we shall bargain.'</font></span>"
|
||||
whoActivated = user
|
||||
isActivated = TRUE
|
||||
desc = "The stone hums with a soft glow, whispering to you."
|
||||
icon_state = "dark_seed"
|
||||
else
|
||||
user << "<span class='warning'><font size=3>The stone shivers, but nothing happens. Perhaps try again later?</font></span>"
|
||||
else
|
||||
if(user != whoActivated)
|
||||
user << "<span class='warning'><font size=3>The stone lays inert.</font></span>"
|
||||
else
|
||||
if(countDownToSummon > 0)
|
||||
user << "<span class='warning'><font size=3>You hear a voice in your head.. 'I still require [countDownToSummon] vessels worth of flesh. Bring them to me'</font></span>"
|
||||
else
|
||||
user << "<span class='warning'><font size=3>I thank you, acolyte.</font></span>"
|
||||
var/mob/living/simple_animal/hostile/hadesacolyte/HA = user.change_mob_type(/mob/living/simple_animal/hostile/hadesacolyte,get_turf(user),"[user.name]",1)
|
||||
var/mob/living/simple_animal/hostile/hades/H = new/mob/living/simple_animal/hostile/hades(get_turf(user))
|
||||
H.maxHealth = absorbedHP
|
||||
H.health = H.maxHealth
|
||||
if(H.maxHealth < initial(H.maxHealth))
|
||||
HA << "<span class='warning'><font size=3>You.. you fool! How dare you summon me with such dirty flesh!</font></span>"
|
||||
HA.faction -= "hades"
|
||||
if(H.maxHealth > initial(H.maxHealth))
|
||||
HA << "<span class='warning'><font size=3>Such.. power! You have exceeded yourself, acolyte. Drink of my might!</font></span>"
|
||||
HA.maxHealth = 250
|
||||
HA.health = 250
|
||||
qdel(src)
|
||||
|
||||
/obj/item/hades_summoner/afterattack(atom/A as mob|obj|turf|area, mob/user, proximity)
|
||||
if(!proximity)
|
||||
return
|
||||
if(istype(A,/mob/living))
|
||||
var/mob/living/M = A
|
||||
if(countDownToSummon > 0)
|
||||
if(M.health > 0 && M.maxHealth > 200)
|
||||
// no absorbing super strong creatures unless they're dead
|
||||
user << "<span class='warning'><font size=3>Such power.. Slay this [M] so that I may partake of its being.</font></span>"
|
||||
return
|
||||
if(!M.stat)
|
||||
user << "<span class='warning'><font size=3>[M] is still too tightly bound to the mortal world! You must either kill or knock them unconscious to sacrifice them.</font></span>"
|
||||
return
|
||||
user << "<span class='warning'><font size=3>I accept your offering.</font></span>"
|
||||
absorbedHP += M.maxHealth
|
||||
if(!M.ckey)
|
||||
M.gib()
|
||||
else
|
||||
M << "<span class='warning'><font size=3>You feel the flesh being stripped from your bones. You're overwhelmed with maddening pain, before reforming into another being!</font></span>"
|
||||
M.change_mob_type(/mob/living/simple_animal/hostile/hadesacolyte,get_turf(user),"[user.name]",1)
|
||||
countDownToSummon--
|
||||
if(countDownToSummon <= 0)
|
||||
user << "<span class='warning'><font size=3>I am ready to ascend, my acolyte.</font></span>"
|
||||
|
||||
|
||||
#undef STATE_JUDGE
|
||||
#undef STATE_WRATH
|
||||
#undef STATE_FLEE
|
||||
@@ -0,0 +1,177 @@
|
||||
/area/hades
|
||||
name = "Chapel of Sin"
|
||||
icon_state = "yellow"
|
||||
requires_power = 0
|
||||
has_gravity = 1
|
||||
|
||||
/turf/open/floor/plasteel/hades
|
||||
name = "Sin-touched Floor"
|
||||
icon_state = "cult"
|
||||
|
||||
/obj/structure/chair/hades
|
||||
name = "Cross of Hades"
|
||||
desc = "An inverted cross, with straps on it to support the weight of a living being."
|
||||
icon_state = "chair_hades"
|
||||
var/list/watchedSpikes = list()
|
||||
|
||||
/obj/structure/chair/hades/New()
|
||||
..()
|
||||
flags |= NODECONSTRUCT
|
||||
for(var/obj/structure/kitchenspike/KS in range(12))
|
||||
watchedSpikes += KS
|
||||
|
||||
/obj/structure/chair/hades/proc/considerReady()
|
||||
//buckled_mobs seems to work inconsistently, so we're doing some custom searching here.
|
||||
if(!buckled_mobs)
|
||||
return FALSE
|
||||
if(!buckled_mobs.len)
|
||||
return FALSE
|
||||
for(var/obj/structure/kitchenspike/KS in watchedSpikes)
|
||||
var/mob/living/M = locate(/mob/living) in get_turf(KS)
|
||||
if(!M)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/structure/chair/hades/proc/completeRitual()
|
||||
for(var/obj/structure/kitchenspike/KS in watchedSpikes)
|
||||
var/mob/living/M = locate(/mob/living) in get_turf(KS)
|
||||
M.gib()
|
||||
playsound(get_turf(src), 'sound/effects/pope_entry.ogg', 100, 1)
|
||||
sleep(100)
|
||||
playsound(get_turf(src), 'sound/effects/hyperspace_end.ogg', 100, 1)
|
||||
new/obj/item/weapon/hades_staff/imbued(get_turf(src))
|
||||
src.visible_message("<span class='warning'>[src] shatters into a thousand shards, a staff falling from it.</span>")
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/chair/hades/attackby(obj/item/weapon/W, mob/user, params)
|
||||
..()
|
||||
if(istype(W, /obj/item/weapon/hades_staff))
|
||||
var/obj/item/weapon/hades_staff/HS = W
|
||||
if(!HS.isKey)
|
||||
return
|
||||
src.visible_message("<span class='warning'>[user] inserts the [W] into the [src], giving it a quick turn.</span>")
|
||||
if(considerReady())
|
||||
qdel(W)
|
||||
src.visible_message("<span class='warning'>[src] shudders, the sound of moving gears arising...</span>")
|
||||
for(var/mob/living/M in buckled_mobs)
|
||||
M.gib()
|
||||
for(var/i in 1 to 4)
|
||||
addtimer(GLOBAL_PROC, "playsound", i*10, FALSE, get_turf(src), 'sound/effects/clang.ogg', 100, 1)
|
||||
spawn(50)
|
||||
src.visible_message("<span class='warning'>[src] begins to lower into the ground...</span>")
|
||||
icon_state = "chair_hades_slide"
|
||||
addtimer(src, "completeRitual", 50, FALSE)
|
||||
else
|
||||
src.visible_message("<span class='warning'>[src] clunks, the sound of grinding gears arising. Nothing happens.</span>")
|
||||
|
||||
/obj/structure/ladder/unbreakable/hades
|
||||
name = "Dimensional Rift"
|
||||
desc = "Where does it lead?"
|
||||
icon = 'icons/mob/EvilPope.dmi'
|
||||
icon_state = "popedeath"
|
||||
anchored = TRUE
|
||||
|
||||
/obj/structure/ladder/unbreakable/hades/update_icon()
|
||||
return
|
||||
|
||||
/obj/item/weapon/paper/hades_instructions
|
||||
name = "paper- 'Hastily Scrawled Letter'"
|
||||
info = "The Master has instructed us to collect corpses for the ritual, and told us to deposity them in the Ritual Room, behind a bookcase in the library. The Master has locked the device to only work with his key, so no more accidents happen."
|
||||
|
||||
/obj/item/weapon/hades_staff
|
||||
name = "Staff of Hades"
|
||||
desc = "A large, dark staff, with a set of key-like prongs on the end."
|
||||
icon_state = "staffofchange"
|
||||
icon = 'icons/obj/guns/magic.dmi'
|
||||
item_state = "staffofchange"
|
||||
slot_flags = SLOT_BELT | SLOT_BACK
|
||||
force = 25
|
||||
throwforce = 5
|
||||
w_class = 3
|
||||
hitsound = 'sound/weapons/bladeslice.ogg'
|
||||
attack_verb = list("slapped", "shattered", "blasphemed", "smashed", "whacked", "crushed", "hammered")
|
||||
block_chance = 25
|
||||
var/isKey = 1
|
||||
|
||||
/obj/item/weapon/hades_staff/fake
|
||||
name = "Inert Staff of Hades"
|
||||
desc = "A large, dark staff."
|
||||
isKey = 0
|
||||
|
||||
/obj/item/weapon/hades_staff/imbued
|
||||
name = "Imbued Staff of Hades"
|
||||
desc = " Bestowed with the power of wayward souls, this Staff allows the wielder to judge a target."
|
||||
force = 75
|
||||
throwforce = 35
|
||||
block_chance = 75
|
||||
var/lastJudge = 0
|
||||
var/judgeCooldown = 150
|
||||
|
||||
/obj/item/weapon/hades_staff/imbued/attack(mob/living/carbon/human/M, mob/living/carbon/human/user)
|
||||
if(!istype(M))
|
||||
return ..()
|
||||
|
||||
if(world.time > lastJudge + judgeCooldown)
|
||||
var/mob/living/sinPerson = M
|
||||
lastJudge = world.time
|
||||
var/sinPersonchoice = pick("Greed","Gluttony","Pride","Lust","Envy","Sloth","Wrath")
|
||||
switch(sinPersonchoice)
|
||||
if("Greed")
|
||||
src.say("Your sin, [sinPerson], is Greed.")
|
||||
if(prob(50))
|
||||
src.say("I will indulge your sin, [sinPerson].")
|
||||
sin_Greed(sinPerson, TRUE)
|
||||
else
|
||||
src.say("Your sin will be punished, [sinPerson]!")
|
||||
sin_Greed(sinPerson, FALSE)
|
||||
if("Gluttony")
|
||||
src.say("Your sin, [sinPerson], is Gluttony.")
|
||||
if(prob(50))
|
||||
src.say("I will indulge your sin, [sinPerson].")
|
||||
sin_Gluttony(sinPerson,TRUE)
|
||||
else
|
||||
src.say("Your sin will be punished, [sinPerson]!")
|
||||
sin_Gluttony(sinPerson,FALSE)
|
||||
if("Pride")
|
||||
src.say("Your sin, [sinPerson], is Pride.")
|
||||
if(prob(50))
|
||||
src.say("I will indulge your sin, [sinPerson].")
|
||||
sin_Pride(sinPerson,TRUE)
|
||||
else
|
||||
src.say("Your sin will be punished, [sinPerson]!")
|
||||
sin_Pride(sinPerson,FALSE)
|
||||
if("Lust")
|
||||
src.say("Your sin, [sinPerson], is Lust.")
|
||||
if(prob(50))
|
||||
src.say("I will indulge your sin, [sinPerson].")
|
||||
sin_Lust(sinPerson,TRUE)
|
||||
else
|
||||
src.say("Your sin will be punished, [sinPerson]!")
|
||||
sin_Lust(sinPerson,TRUE)
|
||||
if("Envy")
|
||||
src.say("Your sin, [sinPerson], is Envy.")
|
||||
if(prob(50))
|
||||
src.say("I will indulge your sin, [sinPerson].")
|
||||
sin_Envy(sinPerson,TRUE)
|
||||
else
|
||||
src.say("Your sin will be punished, [sinPerson]!")
|
||||
sin_Envy(sinPerson,FALSE)
|
||||
if("Sloth")
|
||||
src.say("Your sin, [sinPerson], is Sloth.")
|
||||
if(prob(50))
|
||||
src.say("I will indulge your sin, [sinPerson].")
|
||||
sin_Sloth(sinPerson,TRUE)
|
||||
else
|
||||
src.say("Your sin will be punished, [sinPerson]!")
|
||||
sin_Sloth(sinPerson,FALSE)
|
||||
if("Wrath")
|
||||
src.say("Your sin, [sinPerson], is Wrath.")
|
||||
if(prob(50))
|
||||
src.say("I will indulge your sin, [sinPerson].")
|
||||
sin_Wrath(sinPerson,TRUE)
|
||||
else
|
||||
src.say("Your sin will be punished, [sinPerson]!")
|
||||
sin_Wrath(sinPerson,FALSE)
|
||||
else
|
||||
..()
|
||||
user << "The [src] is still recharging."
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user