Merge branch 'master' of https://github.com/Citadel-Station-13/Citadel-Station-13 into Ghommie-cit595
This commit is contained in:
@@ -63,7 +63,7 @@
|
||||
addtimer(CALLBACK(src, .proc/emagNotify), 150)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/vr_sleeper/update_icon()
|
||||
/obj/machinery/vr_sleeper/update_icon_state()
|
||||
icon_state = "[initial(icon_state)][state_open ? "-open" : ""]"
|
||||
|
||||
/obj/machinery/vr_sleeper/open_machine()
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
/world/IsBanned(key,address,computer_id,type,real_bans_only=FALSE)
|
||||
var/static/key_cache = list()
|
||||
if(!real_bans_only)
|
||||
if(key_cache[key])
|
||||
if(key_cache[key] >= REALTIMEOFDAY)
|
||||
return list("reason"="concurrent connection attempts", "desc"="You are attempting to connect too fast. Try again.")
|
||||
key_cache[key] = 1
|
||||
key_cache[key] = REALTIMEOFDAY+10 //this shouldn't runtime, but if it does, expiry attempts will cover it to ensure genuine connection attemps wont get trapped in limbo
|
||||
|
||||
if (!key || !address || !computer_id)
|
||||
if(real_bans_only)
|
||||
|
||||
@@ -23,8 +23,7 @@ GLOBAL_PROTECT(protected_ranks)
|
||||
name = init_name
|
||||
if(!name)
|
||||
qdel(src)
|
||||
throw EXCEPTION("Admin rank created without name.")
|
||||
return
|
||||
CRASH("Admin rank created without name.")
|
||||
if(init_rights)
|
||||
rights = init_rights
|
||||
include_rights = rights
|
||||
|
||||
@@ -40,6 +40,9 @@
|
||||
H.dna.features["insect_wings"] = pick(GLOB.insect_wings_list)
|
||||
H.dna.features["deco_wings"] = pick(GLOB.deco_wings_list)
|
||||
H.dna.features["insect_fluff"] = pick(GLOB.insect_fluffs_list)
|
||||
H.dna.features["flavor_text"] = "" //Oh no.
|
||||
|
||||
SEND_SIGNAL(H, COMSIG_HUMAN_ON_RANDOMIZE)
|
||||
|
||||
H.update_body()
|
||||
H.update_hair()
|
||||
|
||||
@@ -39,12 +39,10 @@ GLOBAL_PROTECT(href_token)
|
||||
return
|
||||
if(!ckey)
|
||||
QDEL_IN(src, 0)
|
||||
throw EXCEPTION("Admin datum created without a ckey")
|
||||
return
|
||||
CRASH("Admin datum created without a ckey")
|
||||
if(!istype(R))
|
||||
QDEL_IN(src, 0)
|
||||
throw EXCEPTION("Admin datum created without a rank")
|
||||
return
|
||||
CRASH("Admin datum created without a rank")
|
||||
target = ckey
|
||||
name = "[ckey]'s admin datum ([R])"
|
||||
rank = R
|
||||
|
||||
@@ -21,6 +21,6 @@
|
||||
target.gib(1, 1)
|
||||
else
|
||||
target.adjustBruteLoss(min(99,(target.health - 1)))
|
||||
target.Knockdown(400)
|
||||
target.DefaultCombatKnockdown(400)
|
||||
target.stuttering = 20
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"emagged" = borg.emagged,
|
||||
"active_module" = "[borg.module.type]",
|
||||
"lawupdate" = borg.lawupdate,
|
||||
"lockdown" = borg.lockcharge,
|
||||
"lockdown" = borg.locked_down,
|
||||
"scrambledcodes" = borg.scrambledcodes
|
||||
)
|
||||
.["upgrades"] = list()
|
||||
@@ -122,8 +122,8 @@
|
||||
message_admins("[key_name_admin(user)] disabled lawsync on [ADMIN_LOOKUPFLW(borg)].")
|
||||
log_admin("[key_name(user)] disabled lawsync on [key_name(borg)].")
|
||||
if ("toggle_lockdown")
|
||||
borg.SetLockdown(!borg.lockcharge)
|
||||
if (borg.lockcharge)
|
||||
borg.SetLockdown(!borg.locked_down)
|
||||
if (borg.locked_down)
|
||||
message_admins("[key_name_admin(user)] locked down [ADMIN_LOOKUPFLW(borg)].")
|
||||
log_admin("[key_name(user)] locked down [key_name(borg)].")
|
||||
else
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
var/name = "team"
|
||||
var/member_name = "member"
|
||||
var/list/objectives = list() //common objectives, these won't be added or removed automatically, subtypes handle this, this is here for bookkeeping purposes.
|
||||
var/show_roundend_report = TRUE
|
||||
|
||||
/datum/team/New(starting_members)
|
||||
. = ..()
|
||||
@@ -25,6 +26,8 @@
|
||||
|
||||
//Display members/victory/failure/objectives for the team
|
||||
/datum/team/proc/roundend_report()
|
||||
if(!show_roundend_report)
|
||||
return
|
||||
var/list/report = list()
|
||||
|
||||
report += "<span class='header'>[name]:</span>"
|
||||
|
||||
@@ -116,14 +116,13 @@
|
||||
var/mob/living/carbon/human/M = loc
|
||||
M.adjustStaminaLoss(-75)
|
||||
M.SetUnconscious(0)
|
||||
M.SetStun(0)
|
||||
M.SetKnockdown(0)
|
||||
M.SetAllImmobility(0)
|
||||
combat_cooldown = 0
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/clothing/suit/armor/abductor/vest/process()
|
||||
combat_cooldown++
|
||||
if(combat_cooldown==initial(combat_cooldown))
|
||||
if(combat_cooldown == initial(combat_cooldown))
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/clothing/suit/armor/abductor/Destroy()
|
||||
@@ -420,8 +419,9 @@
|
||||
<br>
|
||||
Congratulations! You are now trained for invasive xenobiology research!"}
|
||||
|
||||
/obj/item/paper/guides/antag/abductor/update_icon()
|
||||
return
|
||||
/obj/item/paper/guides/antag/abductor/ComponentInitialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/update_icon_blocker)
|
||||
|
||||
/obj/item/paper/guides/antag/abductor/AltClick()
|
||||
return //otherwise it would fold into a paperplane.
|
||||
@@ -443,6 +443,10 @@
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
actions_types = list(/datum/action/item_action/toggle_mode)
|
||||
|
||||
/obj/item/abductor/baton/ComponentInitialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/update_icon_updates_onmob)
|
||||
|
||||
/obj/item/abductor/baton/proc/toggle(mob/living/user=usr)
|
||||
mode = (mode+1)%BATON_MODES
|
||||
var/txt
|
||||
@@ -459,7 +463,7 @@
|
||||
to_chat(usr, "<span class='notice'>You switch the baton to [txt] mode.</span>")
|
||||
update_icon()
|
||||
|
||||
/obj/item/abductor/baton/update_icon()
|
||||
/obj/item/abductor/baton/update_icon_state()
|
||||
switch(mode)
|
||||
if(BATON_STUN)
|
||||
icon_state = "wonderprodStun"
|
||||
@@ -512,7 +516,7 @@
|
||||
L.lastattackerckey = user.ckey
|
||||
|
||||
L.adjustStaminaLoss(35) //because previously it took 5-6 hits to actually "incapacitate" someone for the purposes of the sleep inducement
|
||||
L.Knockdown(140)
|
||||
L.DefaultCombatKnockdown(140)
|
||||
L.apply_effect(EFFECT_STUTTER, 7)
|
||||
SEND_SIGNAL(L, COMSIG_LIVING_MINOR_SHOCK)
|
||||
|
||||
|
||||
@@ -223,7 +223,7 @@
|
||||
return
|
||||
|
||||
|
||||
/obj/machinery/abductor/experiment/update_icon()
|
||||
/obj/machinery/abductor/experiment/update_icon_state()
|
||||
if(state_open)
|
||||
icon_state = "experiment-open"
|
||||
else
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/update_icons()
|
||||
if(overmind)
|
||||
add_atom_colour(overmind.blob_reagent_datum.color, FIXED_COLOUR_PRIORITY)
|
||||
add_atom_colour(overmind.blobstrain.color, FIXED_COLOUR_PRIORITY)
|
||||
else
|
||||
remove_atom_colour(FIXED_COLOUR_PRIORITY)
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
for(var/i in 1 to 2)
|
||||
var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(src)) //hello yes you are being healed
|
||||
if(overmind)
|
||||
H.color = overmind.blob_reagent_datum.complementary_color
|
||||
H.color = overmind.blobstrain.complementary_color
|
||||
else
|
||||
H.color = "#000000"
|
||||
adjustHealth(-maxHealth*0.0125)
|
||||
@@ -142,8 +142,10 @@
|
||||
// Create the reagents to put into the air
|
||||
create_reagents(10)
|
||||
|
||||
if(overmind && overmind.blob_reagent_datum)
|
||||
reagents.add_reagent(overmind.blob_reagent_datum.type, 10)
|
||||
|
||||
|
||||
if(overmind && overmind.blobstrain)
|
||||
overmind.blobstrain.on_sporedeath(src)
|
||||
else
|
||||
reagents.add_reagent(/datum/reagent/toxin/spore, 10)
|
||||
|
||||
@@ -167,14 +169,14 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/update_icons()
|
||||
if(overmind)
|
||||
add_atom_colour(overmind.blob_reagent_datum.complementary_color, FIXED_COLOUR_PRIORITY)
|
||||
add_atom_colour(overmind.blobstrain.complementary_color, FIXED_COLOUR_PRIORITY)
|
||||
else
|
||||
remove_atom_colour(FIXED_COLOUR_PRIORITY)
|
||||
if(is_zombie)
|
||||
copy_overlays(oldguy, TRUE)
|
||||
var/mutable_appearance/blob_head_overlay = mutable_appearance('icons/mob/blob.dmi', "blob_head")
|
||||
if(overmind)
|
||||
blob_head_overlay.color = overmind.blob_reagent_datum.complementary_color
|
||||
blob_head_overlay.color = overmind.blobstrain.complementary_color
|
||||
color = initial(color)//looks better.
|
||||
add_overlay(blob_head_overlay)
|
||||
|
||||
@@ -218,11 +220,16 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/Initialize()
|
||||
. = ..()
|
||||
if(!independent) //no pulling people deep into the blob
|
||||
verbs -= /mob/living/verb/pulled
|
||||
else
|
||||
if(independent)
|
||||
pass_flags &= ~PASSBLOB
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/start_pulling(atom/movable/AM, state, force = pull_force, supress_message = FALSE)
|
||||
if(!independent && ismob(AM))
|
||||
if(!supress_message)
|
||||
to_chat(src, "<span class='warning'>You are unable to grasp people in this form.</span>")
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/Life()
|
||||
if(..())
|
||||
var/list/blobs_in_area = range(2, src)
|
||||
@@ -238,14 +245,14 @@
|
||||
adjustHealth(-maxHealth*0.1)
|
||||
var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(src)) //hello yes you are being healed
|
||||
if(overmind)
|
||||
H.color = overmind.blob_reagent_datum.complementary_color
|
||||
H.color = overmind.blobstrain.complementary_color
|
||||
else
|
||||
H.color = "#000000"
|
||||
if(locate(/obj/structure/blob/node) in blobs_in_area)
|
||||
adjustHealth(-maxHealth*0.05)
|
||||
var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(src))
|
||||
if(overmind)
|
||||
H.color = overmind.blob_reagent_datum.complementary_color
|
||||
H.color = overmind.blobstrain.complementary_color
|
||||
else
|
||||
H.color = "#000000"
|
||||
if(damagesources)
|
||||
@@ -254,7 +261,7 @@
|
||||
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
|
||||
I.color = overmind.blobstrain.complementary_color
|
||||
flick_overlay_view(I, src, 8)
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
|
||||
@@ -269,16 +276,14 @@
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/AttackingTarget()
|
||||
. = ..()
|
||||
if(. && isliving(target) && overmind)
|
||||
var/mob/living/L = target
|
||||
var/mob_protection = L.get_permeability_protection()
|
||||
overmind.blob_reagent_datum.reaction_mob(L, VAPOR, 20, 0, mob_protection, overmind)//this will do between 10 and 20 damage(reduced by mob protection), depending on chemical, plus 4 from base brute damage.
|
||||
overmind.blobstrain.blobbernaut_attack(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
|
||||
attacktext = overmind.blobstrain.blobbernaut_message
|
||||
else
|
||||
melee_damage_lower = initial(melee_damage_lower)
|
||||
melee_damage_upper = initial(melee_damage_upper)
|
||||
|
||||
@@ -8,12 +8,8 @@
|
||||
explosion_block = 6
|
||||
point_return = -1
|
||||
health_regen = 0 //we regen in Life() instead of when pulsed
|
||||
var/core_regen = 2
|
||||
var/resource_delay = 0
|
||||
var/point_rate = 2
|
||||
|
||||
|
||||
/obj/structure/blob/core/Initialize(mapload, client/new_overmind = null, new_rate = 2, placed = 0)
|
||||
/obj/structure/blob/core/Initialize(mapload, client/new_overmind = null, placed = 0)
|
||||
GLOB.blob_cores += src
|
||||
START_PROCESSING(SSobj, src)
|
||||
GLOB.poi_list |= src
|
||||
@@ -22,7 +18,6 @@
|
||||
return INITIALIZE_HINT_QDEL
|
||||
if(overmind)
|
||||
update_icon()
|
||||
point_rate = new_rate
|
||||
addtimer(CALLBACK(src, .proc/generate_announcement), 1800)
|
||||
. = ..()
|
||||
|
||||
@@ -37,7 +32,7 @@
|
||||
color = null
|
||||
var/mutable_appearance/blob_overlay = mutable_appearance('icons/mob/blob.dmi', "blob")
|
||||
if(overmind)
|
||||
blob_overlay.color = overmind.blob_reagent_datum.color
|
||||
blob_overlay.color = overmind.blobstrain.color
|
||||
add_overlay(blob_overlay)
|
||||
add_overlay(mutable_appearance('icons/mob/blob.dmi', "blob_core_overlay"))
|
||||
|
||||
@@ -60,17 +55,13 @@
|
||||
if(overmind) //we should have an overmind, but...
|
||||
overmind.update_health_hud()
|
||||
|
||||
/obj/structure/blob/core/Life()
|
||||
/obj/structure/blob/core/process()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
if(!overmind)
|
||||
qdel(src)
|
||||
else
|
||||
if(resource_delay <= world.time)
|
||||
resource_delay = world.time + 10 // 1 second
|
||||
overmind.add_points(point_rate)
|
||||
obj_integrity = min(max_integrity, obj_integrity+core_regen)
|
||||
if(overmind)
|
||||
overmind.blobstrain.core_process()
|
||||
overmind.update_health_hud()
|
||||
Pulse_Area(overmind, 12, 4, 3)
|
||||
for(var/obj/structure/blob/normal/B in range(1, src))
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
color = null
|
||||
var/mutable_appearance/blob_overlay = mutable_appearance('icons/mob/blob.dmi', "blob")
|
||||
if(overmind)
|
||||
blob_overlay.color = overmind.blob_reagent_datum.color
|
||||
blob_overlay.color = overmind.blobstrain.color
|
||||
add_overlay(blob_overlay)
|
||||
add_overlay(mutable_appearance('icons/mob/blob.dmi', "blob_node_overlay"))
|
||||
|
||||
@@ -31,6 +31,6 @@
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/structure/blob/node/Life()
|
||||
/obj/structure/blob/node/process()
|
||||
if(overmind)
|
||||
Pulse_Area(overmind, 10, 3, 2)
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
GLOBAL_LIST_INIT(valid_blobstrains, subtypesof(/datum/blobstrain) - list(/datum/blobstrain/reagent, /datum/blobstrain/multiplex))
|
||||
|
||||
/datum/blobstrain
|
||||
var/name
|
||||
var/description
|
||||
var/color = "#000000"
|
||||
var/complementary_color = "#000000" //a color that's complementary to the normal blob color
|
||||
var/shortdesc = null //just damage and on_mob effects, doesn't include special, blob-tile only effects
|
||||
var/effectdesc = null //any long, blob-tile specific effects
|
||||
var/analyzerdescdamage = "Unknown. Report this bug to a coder, or just adminhelp."
|
||||
var/analyzerdesceffect = "N/A"
|
||||
var/blobbernaut_message = "slams" //blobbernaut attack verb
|
||||
var/message = "The blob strikes you" //message sent to any mob hit by the blob
|
||||
var/message_living = null //extension to first mob sent to only living mobs i.e. silicons have no skin to be burnt
|
||||
var/core_regen = 2
|
||||
var/resource_delay = 0
|
||||
var/point_rate = 2
|
||||
var/mob/camera/blob/overmind
|
||||
|
||||
/datum/blobstrain/New(mob/camera/blob/new_overmind)
|
||||
if (!istype(new_overmind))
|
||||
stack_trace("blobstrain created without overmind")
|
||||
overmind = new_overmind
|
||||
|
||||
/datum/blobstrain/proc/on_gain()
|
||||
overmind.color = complementary_color
|
||||
for(var/BL in GLOB.blobs)
|
||||
var/obj/structure/blob/B = BL
|
||||
B.update_icon()
|
||||
for(var/BLO in overmind.blob_mobs)
|
||||
var/mob/living/simple_animal/hostile/blob/BM = BLO
|
||||
BM.update_icons() //If it's getting a new strain, tell it what it does!
|
||||
to_chat(BM, "Your overmind's blob strain is now: <b><font color=\"[color]\">[name]</b></font>!")
|
||||
to_chat(BM, "The <b><font color=\"[color]\">[name]</b></font> strain [shortdesc ? "[shortdesc]" : "[description]"]")
|
||||
|
||||
/datum/blobstrain/proc/on_lose()
|
||||
|
||||
/datum/blobstrain/proc/on_sporedeath(mob/living/spore)
|
||||
|
||||
/datum/blobstrain/proc/send_message(mob/living/M)
|
||||
var/totalmessage = message
|
||||
if(message_living && !issilicon(M))
|
||||
totalmessage += message_living
|
||||
totalmessage += "!"
|
||||
to_chat(M, "<span class='userdanger'>[totalmessage]</span>")
|
||||
|
||||
/datum/blobstrain/proc/core_process()
|
||||
if(resource_delay <= world.time)
|
||||
resource_delay = world.time + 10 // 1 second
|
||||
overmind.add_points(point_rate)
|
||||
overmind.blob_core.obj_integrity = min(overmind.blob_core.max_integrity, overmind.blob_core.obj_integrity+core_regen)
|
||||
|
||||
/datum/blobstrain/proc/attack_living(var/mob/living/L) // When the blob attacks people
|
||||
send_message(L)
|
||||
|
||||
/datum/blobstrain/proc/blobbernaut_attack(mob/living/L) // When this blob's blobbernaut attacks people
|
||||
|
||||
/datum/blobstrain/proc/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag, coefficient = 1) //when the blob takes damage, do this
|
||||
return coefficient*damage
|
||||
|
||||
/datum/blobstrain/proc/death_reaction(obj/structure/blob/B, damage_flag, coefficient = 1) //when a blob dies, do this
|
||||
return
|
||||
|
||||
/datum/blobstrain/proc/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O, coefficient = 1) //when the blob expands, do this
|
||||
return
|
||||
|
||||
/datum/blobstrain/proc/tesla_reaction(obj/structure/blob/B, power, coefficient = 1) //when the blob is hit by a tesla bolt, do this
|
||||
return 1 //return 0 to ignore damage
|
||||
|
||||
/datum/blobstrain/proc/extinguish_reaction(obj/structure/blob/B, coefficient = 1) //when the blob is hit with water, do this
|
||||
return
|
||||
|
||||
/datum/blobstrain/proc/emp_reaction(obj/structure/blob/B, severity, coefficient = 1) //when the blob is hit with an emp, do this
|
||||
return
|
||||
33
code/modules/antagonists/blob/blob/blobstrains/_reagent.dm
Normal file
33
code/modules/antagonists/blob/blob/blobstrains/_reagent.dm
Normal file
@@ -0,0 +1,33 @@
|
||||
/datum/blobstrain/reagent // Blobs that mess with reagents, all "legacy" ones
|
||||
var/datum/reagent/reagent
|
||||
|
||||
/datum/blobstrain/reagent/New(mob/camera/blob/new_overmind)
|
||||
. = ..()
|
||||
reagent = new reagent()
|
||||
|
||||
|
||||
/datum/blobstrain/reagent/attack_living(var/mob/living/L)
|
||||
var/mob_protection = L.get_permeability_protection()
|
||||
reagent.reaction_mob(L, VAPOR, 25, 1, mob_protection, overmind)
|
||||
send_message(L)
|
||||
|
||||
/datum/blobstrain/reagent/blobbernaut_attack(mob/living/L)
|
||||
var/mob_protection = L.get_permeability_protection()
|
||||
reagent.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.
|
||||
|
||||
/datum/blobstrain/reagent/on_sporedeath(mob/living/spore)
|
||||
spore.reagents.add_reagent(reagent.type, 10)
|
||||
|
||||
// These can only be applied by blobs. They are what (reagent) blobs are made out of.
|
||||
/datum/reagent/blob
|
||||
name = "Unknown"
|
||||
description = "shouldn't exist and you should adminhelp immediately."
|
||||
color = "#FFFFFF"
|
||||
taste_description = "bad code and slime"
|
||||
can_synth = FALSE
|
||||
|
||||
|
||||
/datum/reagent/blob/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
|
||||
if(M.stat == DEAD || istype(M, /mob/living/simple_animal/hostile/blob))
|
||||
return 0 //the dead, and blob mobs, don't cause reactions
|
||||
return round(reac_volume * min(1.5 - touch_protection, 1), 0.1) //full touch protection means 50% volume, any prot below 0.5 means 100% volume.
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
//sets you on fire, does burn damage, explodes into flame when burnt, weak to water
|
||||
/datum/blobstrain/reagent/blazing_oil
|
||||
name = "Blazing Oil"
|
||||
description = "will do medium burn damage and set targets on fire."
|
||||
effectdesc = "will also release bursts of flame when burnt, but takes damage from water."
|
||||
analyzerdescdamage = "Does medium burn damage and sets targets on fire."
|
||||
analyzerdesceffect = "Releases fire when burnt, but takes damage from water and other extinguishing liquids."
|
||||
color = "#B68D00"
|
||||
complementary_color = "#BE5532"
|
||||
blobbernaut_message = "splashes"
|
||||
message = "The blob splashes you with burning oil"
|
||||
message_living = ", and you feel your skin char and melt"
|
||||
reagent = /datum/reagent/blob/blazing_oil
|
||||
|
||||
/datum/blobstrain/reagent/blazing_oil/extinguish_reaction(obj/structure/blob/B)
|
||||
B.take_damage(1.5, BURN, "energy")
|
||||
|
||||
/datum/blobstrain/reagent/blazing_oil/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
|
||||
if(damage_type == BURN && damage_flag != "energy")
|
||||
for(var/turf/open/T in range(1, B))
|
||||
var/obj/structure/blob/C = locate() in T
|
||||
if(!(C && C.overmind && C.overmind.blobstrain.type == B.overmind.blobstrain.type) && prob(80))
|
||||
new /obj/effect/hotspot(T)
|
||||
if(damage_flag == "fire")
|
||||
return 0
|
||||
return ..()
|
||||
|
||||
/datum/reagent/blob/blazing_oil
|
||||
name = "Blazing Oil"
|
||||
taste_description = "burning oil"
|
||||
color = "#B68D00"
|
||||
|
||||
/datum/reagent/blob/blazing_oil/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
|
||||
reac_volume = ..()
|
||||
M.adjust_fire_stacks(round(reac_volume/10))
|
||||
M.IgniteMob()
|
||||
if(M)
|
||||
M.apply_damage(0.8*reac_volume, BURN)
|
||||
if(iscarbon(M))
|
||||
M.emote("scream")
|
||||
@@ -0,0 +1,32 @@
|
||||
//does brute, burn, and toxin damage, and cools targets down
|
||||
/datum/blobstrain/reagent/cryogenic_poison
|
||||
name = "Cryogenic Poison"
|
||||
description = "will inject targets with a freezing poison that does high damage over time."
|
||||
analyzerdescdamage = "Injects targets with a freezing poison that will gradually solidify the target's internal organs."
|
||||
color = "#8BA6E9"
|
||||
complementary_color = "#7D6EB4"
|
||||
blobbernaut_message = "injects"
|
||||
message = "The blob stabs you"
|
||||
message_living = ", and you feel like your insides are solidifying"
|
||||
reagent = /datum/reagent/blob/cryogenic_poison
|
||||
|
||||
/datum/reagent/blob/cryogenic_poison
|
||||
name = "Cryogenic Poison"
|
||||
description = "will inject targets with a freezing poison that does high damage over time."
|
||||
color = "#8BA6E9"
|
||||
taste_description = "brain freeze"
|
||||
|
||||
/datum/reagent/blob/cryogenic_poison/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
|
||||
reac_volume = ..()
|
||||
if(M.reagents)
|
||||
M.reagents.add_reagent("frostoil", 0.3*reac_volume)
|
||||
M.reagents.add_reagent("ice", 0.3*reac_volume)
|
||||
M.reagents.add_reagent("cryogenic_poison", 0.3*reac_volume)
|
||||
M.apply_damage(0.2*reac_volume, BRUTE)
|
||||
|
||||
/datum/reagent/blob/cryogenic_poison/on_mob_life(mob/living/carbon/M)
|
||||
M.adjustBruteLoss(0.3*REAGENTS_EFFECT_MULTIPLIER, 0)
|
||||
M.adjustFireLoss(0.3*REAGENTS_EFFECT_MULTIPLIER, 0)
|
||||
M.adjustToxLoss(0.3*REAGENTS_EFFECT_MULTIPLIER, 0)
|
||||
. = 1
|
||||
..()
|
||||
@@ -0,0 +1,33 @@
|
||||
//does burn damage and EMPs, slightly fragile
|
||||
/datum/blobstrain/reagent/electromagnetic_web
|
||||
name = "Electromagnetic Web"
|
||||
color = "#83ECEC"
|
||||
complementary_color = "#EC8383"
|
||||
reagent = /datum/reagent/blob/electromagnetic_web
|
||||
|
||||
/datum/blobstrain/reagent/electromagnetic_web/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
|
||||
if(damage_type == BRUTE) //take full brute
|
||||
switch(B.brute_resist)
|
||||
if(0.5)
|
||||
return damage * 2
|
||||
if(0.25)
|
||||
return damage * 4
|
||||
if(0.1)
|
||||
return damage * 10
|
||||
return damage * 1.25 //a laser will do 25 damage, which will kill any normal blob
|
||||
|
||||
/datum/blobstrain/reagent/electromagnetic_web/death_reaction(obj/structure/blob/B, damage_flag)
|
||||
if(damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser")
|
||||
empulse(B.loc, 1, 3) //less than screen range, so you can stand out of range to avoid it
|
||||
|
||||
/datum/reagent/blob/electromagnetic_web
|
||||
name = "Electromagnetic Web"
|
||||
taste_description = "pop rocks"
|
||||
color = "#83ECEC"
|
||||
|
||||
/datum/reagent/blob/electromagnetic_web/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
|
||||
reac_volume = ..()
|
||||
if(prob(reac_volume*2))
|
||||
M.emp_act(EMP_LIGHT)
|
||||
if(M)
|
||||
M.apply_damage(reac_volume, BURN)
|
||||
@@ -0,0 +1,34 @@
|
||||
//does tons of oxygen damage and a little stamina, immune to tesla bolts, weak to EMP
|
||||
/datum/blobstrain/reagent/energized_jelly
|
||||
name = "Energized Jelly"
|
||||
description = "will cause low stamina and high oxygen damage, and cause targets to be unable to breathe."
|
||||
effectdesc = "will also conduct electricity, but takes damage from EMPs."
|
||||
analyzerdescdamage = "Does low stamina damage, high oxygen damage, and prevents targets from breathing."
|
||||
analyzerdesceffect = "Is immune to electricity and will easily conduct it, but is weak to EMPs."
|
||||
color = "#EFD65A"
|
||||
complementary_color = "#00E5B1"
|
||||
reagent = /datum/reagent/blob/energized_jelly
|
||||
|
||||
/datum/blobstrain/reagent/energized_jelly/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
|
||||
if((damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") && B.obj_integrity - damage <= 0 && prob(10))
|
||||
do_sparks(rand(2, 4), FALSE, B)
|
||||
return ..()
|
||||
|
||||
/datum/blobstrain/reagent/energized_jelly/tesla_reaction(obj/structure/blob/B, power)
|
||||
return 0
|
||||
|
||||
/datum/blobstrain/reagent/energized_jelly/emp_reaction(obj/structure/blob/B, severity)
|
||||
var/damage = rand(30, 50) - severity * rand(10, 15)
|
||||
B.take_damage(damage, BURN, "energy")
|
||||
|
||||
/datum/reagent/blob/energized_jelly
|
||||
name = "Energized Jelly"
|
||||
taste_description = "gelatin"
|
||||
color = "#EFD65A"
|
||||
|
||||
/datum/reagent/blob/energized_jelly/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
|
||||
reac_volume = ..()
|
||||
M.losebreath += round(0.2*reac_volume)
|
||||
M.adjustStaminaLoss(0.4*reac_volume)
|
||||
if(M)
|
||||
M.apply_damage(0.6*reac_volume, OXY)
|
||||
@@ -0,0 +1,40 @@
|
||||
//does aoe brute damage when hitting targets, is immune to explosions
|
||||
/datum/blobstrain/reagent/explosive_lattice
|
||||
name = "Explosive Lattice"
|
||||
description = "will do brute damage in an area around targets."
|
||||
effectdesc = "will also resist explosions, but takes increased damage from fire and other energy sources."
|
||||
analyzerdescdamage = "Does medium brute damage and causes damage to everyone near its targets."
|
||||
analyzerdesceffect = "Is highly resistant to explosions, but takes increased damage from fire and other energy sources."
|
||||
color = "#8B2500"
|
||||
complementary_color = "#00668B"
|
||||
blobbernaut_message = "blasts"
|
||||
message = "The blob blasts you"
|
||||
reagent = /datum/reagent/blob/explosive_lattice
|
||||
|
||||
/datum/blobstrain/reagent/explosive_lattice/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
|
||||
if(damage_flag == "bomb")
|
||||
return 0
|
||||
else if(damage_flag != "melee" && damage_flag != "bullet" && damage_flag != "laser")
|
||||
return damage * 1.5
|
||||
return ..()
|
||||
|
||||
/datum/reagent/blob/explosive_lattice
|
||||
name = "Explosive Lattice"
|
||||
taste_description = "the bomb"
|
||||
color = "#8B2500"
|
||||
|
||||
/datum/reagent/blob/explosive_lattice/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
|
||||
var/initial_volume = reac_volume
|
||||
reac_volume = ..()
|
||||
if(reac_volume >= 10) //if it's not a spore cloud, bad time incoming
|
||||
var/obj/effect/temp_visual/explosion/fast/E = new /obj/effect/temp_visual/explosion/fast(get_turf(M))
|
||||
E.alpha = 150
|
||||
for(var/mob/living/L in orange(get_turf(M), 1))
|
||||
if(ROLE_BLOB in L.faction) //no friendly fire
|
||||
continue
|
||||
var/aoe_volume = ..(L, TOUCH, initial_volume, 0, L.get_permeability_protection(), O)
|
||||
L.apply_damage(0.4*aoe_volume, BRUTE)
|
||||
if(M)
|
||||
M.apply_damage(0.6*reac_volume, BRUTE)
|
||||
else
|
||||
M.apply_damage(0.6*reac_volume, BRUTE)
|
||||
40
code/modules/antagonists/blob/blob/blobstrains/multiplex.dm
Normal file
40
code/modules/antagonists/blob/blob/blobstrains/multiplex.dm
Normal file
@@ -0,0 +1,40 @@
|
||||
/datum/blobstrain/multiplex
|
||||
var/list/blobstrains
|
||||
var/typeshare
|
||||
|
||||
/datum/blobstrain/multiplex/New(mob/camera/blob/new_overmind, list/blobstrains)
|
||||
. = ..()
|
||||
for (var/bt in blobstrains)
|
||||
if (ispath(bt, /datum/blobstrain))
|
||||
src.blobstrains += new bt(overmind)
|
||||
else if (istype(bt, /datum/blobstrain))
|
||||
var/datum/blobstrain/bts = bt
|
||||
bts.overmind = overmind
|
||||
src.blobstrains += bt
|
||||
typeshare = (0.8 * length(src.blobstrains)) - (length(src.blobstrains)-1) // 1 is 80%, 2 are 60% etc
|
||||
|
||||
/datum/blobstrain/multiplex/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag, coefficient = 1) //when the blob takes damage, do this
|
||||
for (var/datum/blobstrain/bt in blobstrains)
|
||||
. += bt.damage_reaction(B, damage, damage_type, damage_flag, coefficient*typeshare)
|
||||
|
||||
/datum/blobstrain/multiplex/death_reaction(obj/structure/blob/B, damage_flag, coefficient = 1) //when a blob dies, do this
|
||||
for (var/datum/blobstrain/bt in blobstrains)
|
||||
. += bt.death_reaction(B, damage_flag, coefficient*typeshare)
|
||||
|
||||
/datum/blobstrain/multiplex/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O, coefficient = 1) //when the blob expands, do this
|
||||
for (var/datum/blobstrain/bt in blobstrains)
|
||||
. += bt.expand_reaction(B, newB, T, O, coefficient*typeshare)
|
||||
|
||||
/datum/blobstrain/multiplex/tesla_reaction(obj/structure/blob/B, power, coefficient = 1) //when the blob is hit by a tesla bolt, do this
|
||||
for (var/datum/blobstrain/bt in blobstrains)
|
||||
. += bt.tesla_reaction(B, power, coefficient*typeshare)
|
||||
if (prob(. / length(blobstrains) * 100))
|
||||
return 1
|
||||
|
||||
/datum/blobstrain/multiplex/extinguish_reaction(obj/structure/blob/B, coefficient = 1) //when the blob is hit with water, do this
|
||||
for (var/datum/blobstrain/bt in blobstrains)
|
||||
. += bt.extinguish_reaction(B, coefficient*typeshare)
|
||||
|
||||
/datum/blobstrain/multiplex/emp_reaction(obj/structure/blob/B, severity, coefficient = 1) //when the blob is hit with an emp, do this
|
||||
for (var/datum/blobstrain/bt in blobstrains)
|
||||
. += bt.emp_reaction(B, severity, coefficient*typeshare)
|
||||
@@ -0,0 +1,38 @@
|
||||
//does massive brute and burn damage, but can only expand manually
|
||||
/datum/blobstrain/reagent/networked_fibers
|
||||
name = "Networked Fibers"
|
||||
description = "will do high brute and burn damage and will generate resources quicker, but can only expand manually."
|
||||
shortdesc = "will do high brute and burn damage."
|
||||
effectdesc = "will move your core when manually expanding near it."
|
||||
analyzerdescdamage = "Does high brute and burn damage."
|
||||
analyzerdesceffect = "Is highly mobile and generates resources rapidly."
|
||||
color = "#CDC0B0"
|
||||
complementary_color = "#FFF68F"
|
||||
reagent = /datum/reagent/blob/networked_fibers
|
||||
|
||||
/datum/blobstrain/reagent/networked_fibers/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O)
|
||||
if(!O && newB.overmind)
|
||||
if(!istype(B, /obj/structure/blob/node))
|
||||
newB.overmind.add_points(1)
|
||||
qdel(newB)
|
||||
else
|
||||
var/area/A = get_area(T)
|
||||
if(!isspaceturf(T) && !istype(A, /area/shuttle))
|
||||
for(var/obj/structure/blob/core/C in range(1, newB))
|
||||
if(C.overmind == O)
|
||||
newB.forceMove(get_turf(C))
|
||||
C.forceMove(T)
|
||||
C.setDir(get_dir(newB, C))
|
||||
O.add_points(1)
|
||||
|
||||
//does massive brute and burn damage, but can only expand manually
|
||||
/datum/reagent/blob/networked_fibers
|
||||
name = "Networked Fibers"
|
||||
taste_description = "efficiency"
|
||||
color = "#CDC0B0"
|
||||
|
||||
/datum/reagent/blob/networked_fibers/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
|
||||
reac_volume = ..()
|
||||
M.apply_damage(0.6*reac_volume, BRUTE)
|
||||
if(M)
|
||||
M.apply_damage(0.6*reac_volume, BURN)
|
||||
@@ -0,0 +1,51 @@
|
||||
//does low brute damage, oxygen damage, and stamina damage and wets tiles when damaged
|
||||
/datum/blobstrain/reagent/pressurized_slime
|
||||
name = "Pressurized Slime"
|
||||
description = "will do low brute, oxygen, and stamina damage, and wet tiles under targets."
|
||||
effectdesc = "will also wet tiles near blobs that are attacked or killed."
|
||||
analyzerdescdamage = "Does low brute damage, low oxygen damage, drains stamina, and wets tiles under targets, extinguishing them."
|
||||
analyzerdesceffect = "When attacked or killed, lubricates nearby tiles, extinguishing anything on them."
|
||||
color = "#AAAABB"
|
||||
complementary_color = "#BBBBAA"
|
||||
blobbernaut_message = "emits slime at"
|
||||
message = "The blob splashes into you"
|
||||
message_living = ", and you gasp for breath"
|
||||
reagent = /datum/reagent/blob/pressurized_slime
|
||||
|
||||
/datum/blobstrain/reagent/pressurized_slime/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
|
||||
if((damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") || damage_type != BURN)
|
||||
extinguisharea(B, damage)
|
||||
return ..()
|
||||
|
||||
/datum/blobstrain/reagent/pressurized_slime/death_reaction(obj/structure/blob/B, damage_flag)
|
||||
if(damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser")
|
||||
B.visible_message("<span class='boldwarning'>The blob ruptures, spraying the area with liquid!</span>")
|
||||
extinguisharea(B, 50)
|
||||
|
||||
/datum/blobstrain/reagent/pressurized_slime/proc/extinguisharea(obj/structure/blob/B, probchance)
|
||||
for(var/turf/open/T in range(1, B))
|
||||
if(prob(probchance))
|
||||
T.MakeSlippery(TURF_WET_LUBE, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS)
|
||||
for(var/obj/O in T)
|
||||
O.extinguish()
|
||||
for(var/mob/living/L in T)
|
||||
L.adjust_fire_stacks(-2.5)
|
||||
L.ExtinguishMob()
|
||||
|
||||
/datum/reagent/blob/pressurized_slime
|
||||
name = "Pressurized Slime"
|
||||
taste_description = "a sponge"
|
||||
color = "#AAAABB"
|
||||
|
||||
/datum/reagent/blob/pressurized_slime/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
|
||||
reac_volume = ..()
|
||||
var/turf/open/T = get_turf(M)
|
||||
if(istype(T) && prob(reac_volume))
|
||||
T.MakeSlippery(TURF_WET_LUBE, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS)
|
||||
M.adjust_fire_stacks(-(reac_volume / 10))
|
||||
M.ExtinguishMob()
|
||||
M.apply_damage(0.4*reac_volume, BRUTE)
|
||||
if(M)
|
||||
M.apply_damage(0.4*reac_volume, OXY)
|
||||
if(M)
|
||||
M.adjustStaminaLoss(0.2*reac_volume)
|
||||
@@ -0,0 +1,30 @@
|
||||
//does brute damage through armor and bio resistance
|
||||
/datum/blobstrain/reagent/reactive_spines
|
||||
name = "Reactive Spines"
|
||||
description = "will do medium brute damage through armor and bio resistance."
|
||||
effectdesc = "will also react when attacked with brute damage, attacking all near the attacked blob."
|
||||
analyzerdescdamage = "Does medium brute damage, ignoring armor and bio resistance."
|
||||
analyzerdesceffect = "When attacked with brute damage, will lash out, attacking everything near it."
|
||||
color = "#9ACD32"
|
||||
complementary_color = "#FFA500"
|
||||
blobbernaut_message = "stabs"
|
||||
message = "The blob stabs you"
|
||||
reagent = /datum/reagent/blob/reactive_spines
|
||||
|
||||
/datum/blobstrain/reagent/reactive_spines/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
|
||||
if(damage && damage_type == BRUTE && B.obj_integrity - damage > 0) //is there any damage, is it brute, and will we be alive
|
||||
if(damage_flag == "melee")
|
||||
B.visible_message("<span class='boldwarning'>The blob retaliates, lashing out!</span>")
|
||||
for(var/atom/A in range(1, B))
|
||||
A.blob_act(B)
|
||||
return ..()
|
||||
|
||||
/datum/reagent/blob/reactive_spines
|
||||
name = "Reactive Spines"
|
||||
taste_description = "rock"
|
||||
color = "#9ACD32"
|
||||
|
||||
/datum/reagent/blob/reactive_spines/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
|
||||
if(M.stat == DEAD || istype(M, /mob/living/simple_animal/hostile/blob))
|
||||
return 0 //the dead, and blob mobs, don't cause reactions
|
||||
M.adjustBruteLoss(0.8*reac_volume)
|
||||
@@ -0,0 +1,33 @@
|
||||
//does toxin damage, hallucination, targets think they're not hurt at all
|
||||
/datum/blobstrain/reagent/regenerative_materia
|
||||
name = "Regenerative Materia"
|
||||
description = "will do toxin damage and cause targets to believe they are fully healed."
|
||||
analyzerdescdamage = "Does toxin damage and injects a toxin that causes the target to believe they are fully healed."
|
||||
color = "#C8A5DC"
|
||||
complementary_color = "#CD7794"
|
||||
message_living = ", and you feel <i>alive</i>"
|
||||
reagent = /datum/reagent/blob/regenerative_materia
|
||||
|
||||
/datum/reagent/blob/regenerative_materia
|
||||
name = "Regenerative Materia"
|
||||
taste_description = "heaven"
|
||||
color = "#C8A5DC"
|
||||
|
||||
/datum/reagent/blob/regenerative_materia/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
|
||||
reac_volume = ..()
|
||||
M.adjust_drugginess(reac_volume)
|
||||
if(M.reagents)
|
||||
M.reagents.add_reagent("regenerative_materia", 0.2*reac_volume)
|
||||
M.reagents.add_reagent("spore", 0.2*reac_volume)
|
||||
M.apply_damage(0.7*reac_volume, TOX)
|
||||
|
||||
/datum/reagent/blob/regenerative_materia/on_mob_life(mob/living/carbon/C)
|
||||
C.adjustToxLoss(1*REAGENTS_EFFECT_MULTIPLIER)
|
||||
C.hal_screwyhud = SCREWYHUD_HEALTHY //fully healed, honest
|
||||
..()
|
||||
|
||||
/datum/reagent/blob/regenerative_materia/on_mob_delete(mob/living/M)
|
||||
if(iscarbon(M))
|
||||
var/mob/living/carbon/N = M
|
||||
N.hal_screwyhud = 0
|
||||
..()
|
||||
@@ -0,0 +1,34 @@
|
||||
/datum/blobstrain/reagent/replicating_foam
|
||||
description = "will do medium brute damage and occasionally expand again when expanding."
|
||||
shortdesc = "will do medium brute damage."
|
||||
effectdesc = "will also expand when attacked with burn damage, but takes more brute damage."
|
||||
color = "#7B5A57"
|
||||
complementary_color = "#57787B"
|
||||
analyzerdescdamage = "Does medium brute damage."
|
||||
analyzerdesceffect = "Expands when attacked with burn damage, will occasionally expand again when expanding, and is fragile to brute damage."
|
||||
reagent = /datum/reagent/blob/replicating_foam
|
||||
|
||||
|
||||
/datum/blobstrain/reagent/replicating_foam/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
|
||||
if(damage_type == BRUTE)
|
||||
damage = damage * 2
|
||||
else if(damage_type == BURN && damage > 0 && B.obj_integrity - damage > 0 && prob(60))
|
||||
var/obj/structure/blob/newB = B.expand(null, null, 0)
|
||||
if(newB)
|
||||
newB.obj_integrity = B.obj_integrity - damage
|
||||
newB.update_icon()
|
||||
return ..()
|
||||
|
||||
|
||||
/datum/blobstrain/reagent/replicating_foam/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O)
|
||||
if(prob(30))
|
||||
newB.expand(null, null, 0) //do it again!
|
||||
|
||||
/datum/reagent/blob/replicating_foam
|
||||
name = "Replicating Foam"
|
||||
taste_description = "duplication"
|
||||
color = "#7B5A57"
|
||||
|
||||
/datum/reagent/blob/replicating_foam/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
|
||||
reac_volume = ..()
|
||||
M.apply_damage(0.7*reac_volume, BRUTE)
|
||||
@@ -0,0 +1,35 @@
|
||||
//does brute damage, shifts away when damaged
|
||||
/datum/blobstrain/reagent/shifting_fragments
|
||||
description = "will do medium brute damage."
|
||||
effectdesc = "will also cause blob parts to shift away when attacked."
|
||||
analyzerdescdamage = "Does medium brute damage."
|
||||
analyzerdesceffect = "When attacked, may shift away from the attacker."
|
||||
color = "#C8963C"
|
||||
complementary_color = "#3C6EC8"
|
||||
reagent = /datum/reagent/blob/shifting_fragments
|
||||
|
||||
/datum/blobstrain/reagent/shifting_fragments/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O)
|
||||
if(istype(B, /obj/structure/blob/normal) || (istype(B, /obj/structure/blob/shield) && prob(25)))
|
||||
newB.forceMove(get_turf(B))
|
||||
B.forceMove(T)
|
||||
|
||||
/datum/blobstrain/reagent/shifting_fragments/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
|
||||
if((damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") && damage > 0 && B.obj_integrity - damage > 0 && prob(60-damage))
|
||||
var/list/blobstopick = list()
|
||||
for(var/obj/structure/blob/OB in orange(1, B))
|
||||
if((istype(OB, /obj/structure/blob/normal) || (istype(OB, /obj/structure/blob/shield) && prob(25))) && OB.overmind && OB.overmind.blobstrain.type == B.overmind.blobstrain.type)
|
||||
blobstopick += OB //as long as the blob picked is valid; ie, a normal or shield blob that has the same chemical as we do, we can swap with it
|
||||
if(blobstopick.len)
|
||||
var/obj/structure/blob/targeted = pick(blobstopick) //randomize the blob chosen, because otherwise it'd tend to the lower left
|
||||
var/turf/T = get_turf(targeted)
|
||||
targeted.forceMove(get_turf(B))
|
||||
B.forceMove(T) //swap the blobs
|
||||
return ..()
|
||||
|
||||
/datum/reagent/blob/shifting_fragments
|
||||
name = "Shifting Fragments"
|
||||
color = "#C8963C"
|
||||
|
||||
/datum/reagent/blob/shifting_fragments/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
|
||||
reac_volume = ..()
|
||||
M.apply_damage(0.7*reac_volume, BRUTE)
|
||||
@@ -0,0 +1,38 @@
|
||||
//does brute damage, bonus damage for each nearby blob, and spreads damage out
|
||||
/datum/blobstrain/reagent/synchronous_mesh
|
||||
name = "Synchronous Mesh"
|
||||
description = "will do massively increased brute damage for each blob near the target."
|
||||
effectdesc = "will also spread damage between each blob near the attacked blob."
|
||||
analyzerdescdamage = "Does brute damage, increasing for each blob near the target."
|
||||
analyzerdesceffect = "When attacked, spreads damage between all blobs near the attacked blob."
|
||||
color = "#65ADA2"
|
||||
complementary_color = "#AD6570"
|
||||
blobbernaut_message = "synchronously strikes"
|
||||
message = "The blobs strike you"
|
||||
|
||||
/datum/blobstrain/reagent/synchronous_mesh/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
|
||||
if(damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") //the cause isn't fire or bombs, so split the damage
|
||||
var/damagesplit = 1 //maximum split is 9, reducing the damage each blob takes to 11% but doing that damage to 9 blobs
|
||||
for(var/obj/structure/blob/C in orange(1, B))
|
||||
if(!istype(C, /obj/structure/blob/core) && !istype(C, /obj/structure/blob/node) && C.overmind && C.overmind.blobstrain.type == B.overmind.blobstrain.type) //if it doesn't have the same chemical or is a core or node, don't split damage to it
|
||||
damagesplit += 1
|
||||
for(var/obj/structure/blob/C in orange(1, B))
|
||||
if(!istype(C, /obj/structure/blob/core) && !istype(C, /obj/structure/blob/node) && C.overmind && C.overmind.blobstrain.type == B.overmind.blobstrain.type) //only hurt blobs that have the same overmind chemical and aren't cores or nodes
|
||||
C.take_damage(damage/damagesplit, CLONE, 0, 0)
|
||||
return damage / damagesplit
|
||||
else
|
||||
return damage * 1.25
|
||||
|
||||
/datum/reagent/blob/synchronous_mesh
|
||||
name = "Synchronous Mesh"
|
||||
taste_description = "toxic mold"
|
||||
color = "#65ADA2"
|
||||
|
||||
/datum/reagent/blob/synchronous_mesh/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
|
||||
reac_volume = ..()
|
||||
M.apply_damage(0.2*reac_volume, BRUTE)
|
||||
if(M && reac_volume)
|
||||
for(var/obj/structure/blob/B in range(1, M)) //if the target is completely surrounded, this is 2.4*reac_volume bonus damage, total of 2.6*reac_volume
|
||||
if(M)
|
||||
B.blob_attack_animation(M) //show them they're getting a bad time
|
||||
M.apply_damage(0.3*reac_volume, BRUTE)
|
||||
@@ -0,0 +1,46 @@
|
||||
//kills sleeping targets and turns them into blob zombies, produces fragile spores when killed or on expanding
|
||||
/datum/blobstrain/reagent/zombifying_pods
|
||||
name = "Zombifying Pods"
|
||||
description = "will do very low toxin damage and harvest sleeping targets for additional resources and a blob zombie."
|
||||
effectdesc = "will also produce fragile spores when killed and on expanding."
|
||||
shortdesc = "will do very low toxin damage and harvest sleeping targets for additional resources(for your overmind) and a blob zombie."
|
||||
analyzerdescdamage = "Does very low toxin damage and kills unconscious humans, turning them into blob zombies."
|
||||
analyzerdesceffect = "Produces spores when expanding and when killed."
|
||||
color = "#E88D5D"
|
||||
complementary_color = "#823ABB"
|
||||
message_living = ", and you feel tired"
|
||||
reagent = /datum/reagent/blob/zombifying_pods
|
||||
|
||||
/datum/blobstrain/reagent/zombifying_pods/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
|
||||
if((damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") && damage <= 20 && B.obj_integrity - damage <= 0 && prob(30)) //if the cause isn't fire or a bomb, the damage is less than 21, we're going to die from that damage, 20% chance of a shitty spore.
|
||||
B.visible_message("<span class='warning'><b>A spore floats free of the blob!</b></span>")
|
||||
var/mob/living/simple_animal/hostile/blob/blobspore/weak/BS = new/mob/living/simple_animal/hostile/blob/blobspore/weak(B.loc)
|
||||
BS.overmind = B.overmind
|
||||
BS.update_icons()
|
||||
B.overmind.blob_mobs.Add(BS)
|
||||
return ..()
|
||||
|
||||
/datum/blobstrain/reagent/zombifying_pods/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O)
|
||||
if(prob(10))
|
||||
var/mob/living/simple_animal/hostile/blob/blobspore/weak/BS = new/mob/living/simple_animal/hostile/blob/blobspore/weak(T)
|
||||
BS.overmind = B.overmind
|
||||
BS.update_icons()
|
||||
newB.overmind.blob_mobs.Add(BS)
|
||||
|
||||
/datum/reagent/blob/zombifying_pods
|
||||
name = "Zombifying Pods"
|
||||
color = "#E88D5D"
|
||||
|
||||
/datum/reagent/blob/zombifying_pods/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
|
||||
reac_volume = ..()
|
||||
M.apply_damage(0.6*reac_volume, TOX)
|
||||
if(O && ishuman(M) && M.stat == UNCONSCIOUS)
|
||||
M.death() //sleeping in a fight? bad plan.
|
||||
var/points = rand(5, 10)
|
||||
var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore/weak(get_turf(M))
|
||||
BS.overmind = O
|
||||
BS.update_icons()
|
||||
O.blob_mobs.Add(BS)
|
||||
BS.Zombify(M)
|
||||
O.add_points(points)
|
||||
to_chat(O, "<span class='notice'>Gained [points] resources from the zombification of [M].</span>")
|
||||
@@ -20,25 +20,25 @@ GLOBAL_LIST_EMPTY(blob_nodes)
|
||||
pass_flags = PASSBLOB
|
||||
faction = list(ROLE_BLOB)
|
||||
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
|
||||
call_life = TRUE
|
||||
hud_type = /datum/hud/blob_overmind
|
||||
var/obj/structure/blob/core/blob_core = null // The blob overmind's core
|
||||
var/blob_points = 0
|
||||
var/max_blob_points = 250
|
||||
var/last_attack = 0
|
||||
var/datum/reagent/blob/blob_reagent_datum = new/datum/reagent/blob()
|
||||
var/datum/blobstrain/blobstrain
|
||||
var/list/blob_mobs = list()
|
||||
var/list/resource_blobs = list()
|
||||
var/free_chem_rerolls = 1 //one free chemical reroll
|
||||
var/free_strain_rerolls = 1 //one free strain reroll
|
||||
var/last_reroll_time = 0 //time since we last rerolled, used to give free rerolls
|
||||
var/nodes_required = 1 //if the blob needs nodes to place resource and factory blobs
|
||||
var/placed = 0
|
||||
var/base_point_rate = 2 //for blob core placement
|
||||
var/manualplace_min_time = 600 //in deciseconds //a minute, to get bearings
|
||||
var/autoplace_max_time = 3600 //six minutes, as long as should be needed
|
||||
var/list/blobs_legit = list()
|
||||
var/max_count = 0 //The biggest it got before death
|
||||
var/blobwincount = 400
|
||||
var/victory_in_progress = FALSE
|
||||
var/rerolling = FALSE
|
||||
|
||||
/mob/camera/blob/Initialize(mapload, starting_points = 60)
|
||||
validate_location()
|
||||
@@ -50,13 +50,14 @@ GLOBAL_LIST_EMPTY(blob_nodes)
|
||||
name = new_name
|
||||
real_name = new_name
|
||||
last_attack = world.time
|
||||
var/datum/reagent/blob/BC = pick((subtypesof(/datum/reagent/blob)))
|
||||
blob_reagent_datum = new BC
|
||||
color = blob_reagent_datum.complementary_color
|
||||
var/datum/blobstrain/BS = pick(GLOB.valid_blobstrains)
|
||||
set_strain(BS)
|
||||
color = blobstrain.complementary_color
|
||||
if(blob_core)
|
||||
blob_core.update_icon()
|
||||
SSshuttle.registerHostileEnvironment(src)
|
||||
.= ..()
|
||||
. = ..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/mob/camera/blob/proc/validate_location()
|
||||
var/turf/T = get_turf(src)
|
||||
@@ -70,13 +71,28 @@ GLOBAL_LIST_EMPTY(blob_nodes)
|
||||
CRASH("No blobspawnpoints and blob spawned in nullspace.")
|
||||
forceMove(T)
|
||||
|
||||
/mob/camera/blob/proc/set_strain(datum/blobstrain/new_strain)
|
||||
if (ispath(new_strain))
|
||||
var/hadstrain = FALSE
|
||||
if (istype(blobstrain))
|
||||
blobstrain.on_lose()
|
||||
qdel(blobstrain)
|
||||
hadstrain = TRUE
|
||||
blobstrain = new new_strain(src)
|
||||
blobstrain.on_gain()
|
||||
if (hadstrain)
|
||||
to_chat(src, "Your strain is now: <b><font color=\"[blobstrain.color]\">[blobstrain.name]</b></font>!")
|
||||
to_chat(src, "The <b><font color=\"[blobstrain.color]\">[blobstrain.name]</b></font> strain [blobstrain.description]")
|
||||
if(blobstrain.effectdesc)
|
||||
to_chat(src, "The <b><font color=\"[blobstrain.color]\">[blobstrain.name]</b></font> strain [blobstrain.effectdesc]")
|
||||
|
||||
/mob/camera/blob/proc/is_valid_turf(turf/T)
|
||||
var/area/A = get_area(T)
|
||||
if((A && !A.blob_allowed) || !T || !is_station_level(T.z) || isspaceturf(T))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/mob/camera/blob/Life()
|
||||
/mob/camera/blob/process()
|
||||
if(!blob_core)
|
||||
if(!placed)
|
||||
if(manualplace_min_time && world.time >= manualplace_min_time)
|
||||
@@ -84,7 +100,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
|
||||
to_chat(src, "<span class='big'><font color=\"#EE4000\">You will automatically place your blob core in [DisplayTimeText(autoplace_max_time - world.time)].</font></span>")
|
||||
manualplace_min_time = 0
|
||||
if(autoplace_max_time && world.time >= autoplace_max_time)
|
||||
place_blob_core(base_point_rate, 1)
|
||||
place_blob_core(1)
|
||||
else
|
||||
qdel(src)
|
||||
else if(!victory_in_progress && (blobs_legit.len >= blobwincount))
|
||||
@@ -94,11 +110,12 @@ GLOBAL_LIST_EMPTY(blob_nodes)
|
||||
max_blob_points = INFINITY
|
||||
blob_points = INFINITY
|
||||
addtimer(CALLBACK(src, .proc/victory), 450)
|
||||
else if(!free_strain_rerolls && (last_reroll_time + BLOB_REROLL_TIME<world.time))
|
||||
to_chat(src, "<b><span class='big'><font color=\"#EE4000\">You have gained another free strain re-roll.</font></span></b>")
|
||||
free_strain_rerolls = 1
|
||||
|
||||
if(!victory_in_progress && max_count < blobs_legit.len)
|
||||
max_count = blobs_legit.len
|
||||
..()
|
||||
|
||||
|
||||
/mob/camera/blob/proc/victory()
|
||||
sound_to_playing_players('sound/machines/alarm.ogg')
|
||||
@@ -129,7 +146,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
|
||||
continue
|
||||
if(!A.blob_allowed)
|
||||
continue
|
||||
A.color = blob_reagent_datum.color
|
||||
A.color = blobstrain.color
|
||||
A.name = "blob"
|
||||
A.icon = 'icons/mob/blob.dmi'
|
||||
A.icon_state = "blob_shield"
|
||||
@@ -157,8 +174,12 @@ GLOBAL_LIST_EMPTY(blob_nodes)
|
||||
BM.overmind = null
|
||||
BM.update_icons()
|
||||
GLOB.overminds -= src
|
||||
blob_mobs = null
|
||||
resource_blobs = null
|
||||
blobs_legit = null
|
||||
|
||||
SSshuttle.clearHostileEnvironment(src)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
|
||||
return ..()
|
||||
|
||||
@@ -170,9 +191,9 @@ GLOBAL_LIST_EMPTY(blob_nodes)
|
||||
add_points(0)
|
||||
|
||||
/mob/camera/blob/examine(mob/user)
|
||||
. = ..()
|
||||
if(blob_reagent_datum)
|
||||
. += "Its chemical is <font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</font>."
|
||||
..()
|
||||
if(blobstrain)
|
||||
to_chat(user, "Its strain is <font color=\"[blobstrain.color]\">[blobstrain.name]</font>.")
|
||||
|
||||
/mob/camera/blob/update_health_hud()
|
||||
if(blob_core)
|
||||
@@ -211,7 +232,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
|
||||
src.log_talk(message, LOG_SAY)
|
||||
|
||||
var/message_a = say_quote(message)
|
||||
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>"
|
||||
var/rendered = "<span class='big'><font color=\"#EE4000\"><b>\[Blob Telepathy\] [name](<font color=\"[blobstrain.color]\">[blobstrain.name]</font>)</b> [message_a]</font></span>"
|
||||
|
||||
for(var/mob/M in GLOB.mob_list)
|
||||
if(isovermind(M) || istype(M, /mob/living/simple_animal/hostile/blob))
|
||||
@@ -230,8 +251,8 @@ GLOBAL_LIST_EMPTY(blob_nodes)
|
||||
stat(null, "Core Health: [blob_core.obj_integrity]")
|
||||
stat(null, "Power Stored: [blob_points]/[max_blob_points]")
|
||||
stat(null, "Blobs to Win: [blobs_legit.len]/[blobwincount]")
|
||||
if(free_chem_rerolls)
|
||||
stat(null, "You have [free_chem_rerolls] Free Chemical Reroll\s Remaining")
|
||||
if(free_strain_rerolls)
|
||||
stat(null, "You have [free_strain_rerolls] Free Strain 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)]")
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
// Power verbs
|
||||
|
||||
/mob/camera/blob/proc/place_blob_core(point_rate, placement_override , pop_override = FALSE)
|
||||
/mob/camera/blob/proc/place_blob_core(placement_override, pop_override = FALSE)
|
||||
if(placed && placement_override != -1)
|
||||
return 1
|
||||
if(!placement_override)
|
||||
@@ -47,7 +47,7 @@
|
||||
if(placed && blob_core)
|
||||
blob_core.forceMove(loc)
|
||||
else
|
||||
var/obj/structure/blob/core/core = new(get_turf(src), src, point_rate, 1)
|
||||
var/obj/structure/blob/core/core = new(get_turf(src), src, 1)
|
||||
core.overmind = src
|
||||
blobs_legit += src
|
||||
blob_core = core
|
||||
@@ -71,13 +71,13 @@
|
||||
var/list/nodes = list()
|
||||
for(var/i in 1 to GLOB.blob_nodes.len)
|
||||
var/obj/structure/blob/node/B = GLOB.blob_nodes[i]
|
||||
nodes["Blob Node #[i] ([B.overmind ? "[B.overmind.blob_reagent_datum.name]":"No Chemical"])"] = B
|
||||
nodes["Blob Node #[i] ([B.overmind ? "[B.overmind.blobstrain.name]":"No Strain"])"] = B
|
||||
var/node_name = input(src, "Choose a node to jump to.", "Node Jump") in nodes
|
||||
var/obj/structure/blob/node/chosen_node = nodes[node_name]
|
||||
if(chosen_node)
|
||||
forceMove(chosen_node.loc)
|
||||
|
||||
/mob/camera/blob/proc/createSpecial(price, blobType, nearEquals, needsNode, turf/T)
|
||||
/mob/camera/blob/proc/createSpecial(price, blobstrain, nearEquals, needsNode, turf/T)
|
||||
if(!T)
|
||||
T = get_turf(src)
|
||||
var/obj/structure/blob/B = (locate(/obj/structure/blob) in T)
|
||||
@@ -93,12 +93,12 @@
|
||||
return //handholdotron 2000
|
||||
if(nearEquals)
|
||||
for(var/obj/structure/blob/L in orange(nearEquals, T))
|
||||
if(L.type == blobType)
|
||||
if(L.type == blobstrain)
|
||||
to_chat(src, "<span class='warning'>There is a similar blob nearby, move more than [nearEquals] tiles away from it!</span>")
|
||||
return
|
||||
if(!can_buy(price))
|
||||
return
|
||||
var/obj/structure/blob/N = B.change_to(blobType, src)
|
||||
var/obj/structure/blob/N = B.change_to(blobstrain, src)
|
||||
return N
|
||||
|
||||
/mob/camera/blob/verb/toggle_node_req()
|
||||
@@ -123,6 +123,7 @@
|
||||
if(!can_buy(15))
|
||||
return
|
||||
if(S.obj_integrity < S.max_integrity * 0.5)
|
||||
add_points(BLOB_REFLECTOR_COST)
|
||||
to_chat(src, "<span class='warning'>This shield blob is too damaged to be modified properly!</span>")
|
||||
return
|
||||
to_chat(src, "<span class='warning'>You secrete a reflective ooze over the shield blob, allowing it to reflect projectiles at the cost of reduced intregrity.</span>")
|
||||
@@ -166,7 +167,9 @@
|
||||
if(!can_buy(40))
|
||||
return
|
||||
|
||||
var/list/mob/candidates = pollGhostCandidates("Do you want to play as a [blob_reagent_datum.name] blobbernaut?", ROLE_BLOB, null, ROLE_BLOB, 50) //players must answer rapidly
|
||||
B.naut = TRUE //temporary placeholder to prevent creation of more than one per factory.
|
||||
to_chat(src, "<span class='notice'>You attempt to produce a blobbernaut.</span>")
|
||||
var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a [blobstrain.name] blobbernaut?", ROLE_BLOB, null, ROLE_BLOB, 50) //players must answer rapidly
|
||||
if(LAZYLEN(candidates)) //if we got at least one candidate, they're a blobbernaut now.
|
||||
B.max_integrity = initial(B.max_integrity) * 0.25 //factories that produced a blobbernaut have much lower health
|
||||
B.obj_integrity = min(B.obj_integrity, B.max_integrity)
|
||||
@@ -188,8 +191,8 @@
|
||||
to_chat(blobber, "<b>You are a blobbernaut!</b>")
|
||||
to_chat(blobber, "You are powerful, hard to kill, and slowly regenerate near nodes and cores, <span class='cultlarge'>but will slowly die if not near the blob</span> or if the factory that made you is killed.")
|
||||
to_chat(blobber, "You can communicate with other blobbernauts and overminds via <b>:b</b>")
|
||||
to_chat(blobber, "Your overmind's blob reagent is: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!")
|
||||
to_chat(blobber, "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.shortdesc ? "[blob_reagent_datum.shortdesc]" : "[blob_reagent_datum.description]"]")
|
||||
to_chat(blobber, "Your overmind's blob reagent is: <b><font color=\"[blobstrain.color]\">[blobstrain.name]</b></font>!")
|
||||
to_chat(blobber, "The <b><font color=\"[blobstrain.color]\">[blobstrain.name]</b></font> reagent [blobstrain.shortdesc ? "[blobstrain.shortdesc]" : "[blobstrain.description]"]")
|
||||
else
|
||||
to_chat(src, "<span class='warning'>You could not conjure a sentience for your blobbernaut. Your points have been refunded. Try again later.</span>")
|
||||
add_points(40)
|
||||
@@ -265,9 +268,7 @@
|
||||
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)
|
||||
blobstrain.attack_living(L)
|
||||
var/obj/structure/blob/B = locate() in T
|
||||
if(B)
|
||||
if(attacksuccess) //if we successfully attacked a turf with a blob on it, don't refund shit
|
||||
@@ -331,41 +332,38 @@
|
||||
if(BM.stat == CONSCIOUS)
|
||||
BM.say(speak_text)
|
||||
|
||||
/mob/camera/blob/verb/chemical_reroll()
|
||||
/mob/camera/blob/verb/strain_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--
|
||||
set name = "Reactive Strain Adaptation (40)"
|
||||
set desc = "Replaces your strain with a random, different one."
|
||||
if(!rerolling && (free_strain_rerolls || can_buy(40)))
|
||||
rerolling = TRUE
|
||||
reroll_strain()
|
||||
rerolling = FALSE
|
||||
if(free_strain_rerolls)
|
||||
free_strain_rerolls--
|
||||
last_reroll_time = world.time
|
||||
|
||||
/mob/camera/blob/proc/set_chemical()
|
||||
var/datum/reagent/blob/BC = pick((subtypesof(/datum/reagent/blob) - blob_reagent_datum.type))
|
||||
blob_reagent_datum = new BC
|
||||
color = blob_reagent_datum.complementary_color
|
||||
for(var/BL in GLOB.blobs)
|
||||
var/obj/structure/blob/B = BL
|
||||
B.update_icon()
|
||||
for(var/BLO in blob_mobs)
|
||||
var/mob/living/simple_animal/hostile/blob/BM = BLO
|
||||
BM.update_icons() //If it's getting a new chemical, tell it what it does!
|
||||
to_chat(BM, "Your overmind's blob reagent is now: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!")
|
||||
to_chat(BM, "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.shortdesc ? "[blob_reagent_datum.shortdesc]" : "[blob_reagent_datum.description]"]")
|
||||
to_chat(src, "Your reagent is now: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!")
|
||||
to_chat(src, "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.description]")
|
||||
if(blob_reagent_datum.effectdesc)
|
||||
to_chat(src, "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.effectdesc]")
|
||||
/mob/camera/blob/proc/reroll_strain()
|
||||
var/list/choices = list()
|
||||
while (length(choices) < 4)
|
||||
var/datum/blobstrain/bs = pick((GLOB.valid_blobstrains))
|
||||
choices[initial(bs.name)] = bs
|
||||
|
||||
var/choice = input(usr, "Please choose a new strain","Strain") as anything in choices
|
||||
if (choice && choices[choice] && !QDELETED(src))
|
||||
var/datum/blobstrain/bs = choices[choice]
|
||||
set_strain(bs)
|
||||
|
||||
/mob/camera/blob/verb/blob_help()
|
||||
set category = "Blob"
|
||||
set name = "*Blob Help*"
|
||||
set desc = "Help on how to blob."
|
||||
to_chat(src, "<b>As the overmind, you can control the blob!</b>")
|
||||
to_chat(src, "Your blob reagent is: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!")
|
||||
to_chat(src, "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.description]")
|
||||
if(blob_reagent_datum.effectdesc)
|
||||
to_chat(src, "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.effectdesc]")
|
||||
to_chat(src, "Your blob reagent is: <b><font color=\"[blobstrain.color]\">[blobstrain.name]</b></font>!")
|
||||
to_chat(src, "The <b><font color=\"[blobstrain.color]\">[blobstrain.name]</b></font> reagent [blobstrain.description]")
|
||||
if(blobstrain.effectdesc)
|
||||
to_chat(src, "The <b><font color=\"[blobstrain.color]\">[blobstrain.name]</b></font> reagent [blobstrain.effectdesc]")
|
||||
to_chat(src, "<b>You can expand, which will attack people, damage objects, or place a Normal Blob if the tile is clear.</b>")
|
||||
to_chat(src, "<i>Normal Blobs</i> will expand your reach and can be upgraded into special blobs that perform certain functions.")
|
||||
to_chat(src, "<b>You can upgrade normal blobs into the following types of blob:</b>")
|
||||
|
||||
@@ -83,16 +83,10 @@
|
||||
|
||||
/obj/structure/blob/update_icon() //Updates color based on overmind color if we have an overmind.
|
||||
if(overmind)
|
||||
add_atom_colour(overmind.blob_reagent_datum.color, FIXED_COLOUR_PRIORITY)
|
||||
add_atom_colour(overmind.blobstrain.color, FIXED_COLOUR_PRIORITY)
|
||||
else
|
||||
remove_atom_colour(FIXED_COLOUR_PRIORITY)
|
||||
|
||||
/obj/structure/blob/process()
|
||||
Life()
|
||||
|
||||
/obj/structure/blob/proc/Life()
|
||||
return
|
||||
|
||||
/obj/structure/blob/proc/Pulse_Area(mob/camera/blob/pulsing_overmind, claim_range = 10, pulse_range = 3, expand_range = 2)
|
||||
if(QDELETED(pulsing_overmind))
|
||||
pulsing_overmind = overmind
|
||||
@@ -148,10 +142,10 @@
|
||||
O.setDir(dir)
|
||||
if(controller)
|
||||
var/mob/camera/blob/BO = controller
|
||||
O.color = BO.blob_reagent_datum.color
|
||||
O.color = BO.blobstrain.color
|
||||
O.alpha = 200
|
||||
else if(overmind)
|
||||
O.color = overmind.blob_reagent_datum.color
|
||||
O.color = overmind.blobstrain.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.
|
||||
@@ -192,7 +186,7 @@
|
||||
B.forceMove(T)
|
||||
B.update_icon()
|
||||
if(B.overmind && expand_reaction)
|
||||
B.overmind.blob_reagent_datum.expand_reaction(src, B, T, controller)
|
||||
B.overmind.blobstrain.expand_reaction(src, B, T, controller)
|
||||
return B
|
||||
else
|
||||
blob_attack_animation(T, controller)
|
||||
@@ -209,14 +203,14 @@
|
||||
return
|
||||
if(severity > 0)
|
||||
if(overmind)
|
||||
overmind.blob_reagent_datum.emp_reaction(src, severity)
|
||||
overmind.blobstrain.emp_reaction(src, severity)
|
||||
if(prob(100 - severity * 30))
|
||||
new /obj/effect/temp_visual/emp(get_turf(src))
|
||||
|
||||
/obj/structure/blob/tesla_act(power)
|
||||
..()
|
||||
if(overmind)
|
||||
if(overmind.blob_reagent_datum.tesla_reaction(src, power))
|
||||
if(overmind.blobstrain.tesla_reaction(src, power))
|
||||
take_damage(power/400, BURN, "energy")
|
||||
else
|
||||
take_damage(power/400, BURN, "energy")
|
||||
@@ -224,7 +218,7 @@
|
||||
/obj/structure/blob/extinguish()
|
||||
..()
|
||||
if(overmind)
|
||||
overmind.blob_reagent_datum.extinguish_reaction(src)
|
||||
overmind.blobstrain.extinguish_reaction(src)
|
||||
|
||||
/obj/structure/blob/hulk_damage()
|
||||
return 15
|
||||
@@ -243,13 +237,13 @@
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/structure/blob/proc/chemeffectreport()
|
||||
/obj/structure/blob/proc/chemeffectreport(mob/user)
|
||||
RETURN_TYPE(/list)
|
||||
. = list()
|
||||
if(overmind)
|
||||
. += "<b>Material: <font color=\"[overmind.blob_reagent_datum.color]\">[overmind.blob_reagent_datum.name]</font><span class='notice'>.</span></b>"
|
||||
. += "<b>Material Effects:</b> <span class='notice'>[overmind.blob_reagent_datum.analyzerdescdamage]</span>"
|
||||
. += "<b>Material Properties:</b> <span class='notice'>[overmind.blob_reagent_datum.analyzerdesceffect]</span><br>"
|
||||
to_chat(user, "<b>Material: <font color=\"[overmind.blobstrain.color]\">[overmind.blobstrain.name]</font><span class='notice'>.</span></b>")
|
||||
to_chat(user, "<b>Material Effects:</b> <span class='notice'>[overmind.blobstrain.analyzerdescdamage]</span>")
|
||||
to_chat(user, "<b>Material Properties:</b> <span class='notice'>[overmind.blobstrain.analyzerdesceffect]</span><br>")
|
||||
else
|
||||
. += "<b>No Material Detected!</b><br>"
|
||||
|
||||
@@ -288,7 +282,7 @@
|
||||
armor_protection = armor.getRating(damage_flag)
|
||||
damage_amount = round(damage_amount * (100 - armor_protection)*0.01, 0.1)
|
||||
if(overmind && damage_flag)
|
||||
damage_amount = overmind.blob_reagent_datum.damage_reaction(src, damage_amount, damage_type, damage_flag)
|
||||
damage_amount = overmind.blobstrain.damage_reaction(src, damage_amount, damage_type, damage_flag)
|
||||
return damage_amount
|
||||
|
||||
/obj/structure/blob/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
|
||||
@@ -298,13 +292,12 @@
|
||||
|
||||
/obj/structure/blob/obj_destruction(damage_flag)
|
||||
if(overmind)
|
||||
overmind.blob_reagent_datum.death_reaction(src, damage_flag)
|
||||
overmind.blobstrain.death_reaction(src, damage_flag)
|
||||
..()
|
||||
|
||||
/obj/structure/blob/proc/change_to(type, controller)
|
||||
if(!ispath(type))
|
||||
throw EXCEPTION("change_to(): invalid type for blob")
|
||||
return
|
||||
CRASH("change_to(): invalid type for blob")
|
||||
var/obj/structure/blob/B = new type(src.loc, controller)
|
||||
B.creation_action()
|
||||
B.update_icon()
|
||||
@@ -333,8 +326,8 @@
|
||||
|
||||
/obj/structure/blob/proc/get_chem_name()
|
||||
if(overmind)
|
||||
return overmind.blob_reagent_datum.name
|
||||
return "an unknown variant"
|
||||
return overmind.blobstrain.name
|
||||
return "some kind of organic tissue"
|
||||
|
||||
/obj/structure/blob/normal
|
||||
name = "normal blob"
|
||||
|
||||
@@ -315,7 +315,7 @@
|
||||
bloodsuckerdatum.handle_eat_human_food(food_nutrition)
|
||||
|
||||
|
||||
/datum/antagonist/bloodsucker/proc/handle_eat_human_food(var/food_nutrition) // Called from snacks.dm and drinks.dm
|
||||
/datum/antagonist/bloodsucker/proc/handle_eat_human_food(food_nutrition, puke_blood = TRUE, masquerade_override) // Called from snacks.dm and drinks.dm
|
||||
set waitfor = FALSE
|
||||
if(!owner.current || !iscarbon(owner.current))
|
||||
return
|
||||
@@ -324,14 +324,14 @@
|
||||
C.nutrition -= food_nutrition
|
||||
foodInGut += food_nutrition
|
||||
// Already ate some bad clams? Then we can back out, because we're already sick from it.
|
||||
if (foodInGut != food_nutrition)
|
||||
if(foodInGut != food_nutrition)
|
||||
return
|
||||
// Haven't eaten, but I'm in a Human Disguise.
|
||||
else if (poweron_masquerade)
|
||||
else if(poweron_masquerade && !masquerade_override)
|
||||
to_chat(C, "<span class='notice'>Your stomach turns, but your \"human disguise\" keeps the food down...for now.</span>")
|
||||
// Keep looping until we purge. If we have activated our Human Disguise, we ignore the food. But it'll come up eventually...
|
||||
var/sickphase = 0
|
||||
while (foodInGut)
|
||||
while(foodInGut)
|
||||
sleep(50)
|
||||
C.adjust_disgust(10 * sickphase)
|
||||
// Wait an interval...
|
||||
@@ -340,24 +340,29 @@
|
||||
if(C.stat == DEAD)
|
||||
return
|
||||
// Put up disguise? Then hold off the vomit.
|
||||
if(poweron_masquerade)
|
||||
if(poweron_masquerade && !masquerade_override)
|
||||
if(sickphase > 0)
|
||||
to_chat(C, "<span class='notice'>Your stomach settles temporarily. You regain your composure...for now.</span>")
|
||||
sickphase = 0
|
||||
continue
|
||||
switch(sickphase)
|
||||
if (1)
|
||||
if(1)
|
||||
to_chat(C, "<span class='warning'>You feel unwell. You can taste ash on your tongue.</span>")
|
||||
C.Stun(10)
|
||||
if (2)
|
||||
if(2)
|
||||
to_chat(C, "<span class='warning'>Your stomach turns. Whatever you ate tastes of grave dirt and brimstone.</span>")
|
||||
C.Dizzy(15)
|
||||
C.Stun(13)
|
||||
if (3)
|
||||
if(3)
|
||||
to_chat(C, "<span class='warning'>You purge the food of the living from your viscera! You've never felt worse.</span>")
|
||||
C.vomit(foodInGut * 4, foodInGut * 2, 0) // (var/lost_nutrition = 10, var/blood = 0, var/stun = 1, var/distance = 0, var/message = 1, var/toxic = 0)
|
||||
C.blood_volume = max(0, C.blood_volume - foodInGut * 2)
|
||||
//Puke blood only if puke_blood is true, and loose some blood, else just puke normally.
|
||||
if(puke_blood)
|
||||
C.blood_volume = max(0, C.blood_volume - foodInGut * 2)
|
||||
C.vomit(foodInGut * 4, foodInGut * 2, 0)
|
||||
else
|
||||
C.vomit(foodInGut * 4, FALSE, 0)
|
||||
C.Stun(30)
|
||||
//C.Dizzy(50)
|
||||
foodInGut = 0
|
||||
SEND_SIGNAL(C, COMSIG_ADD_MOOD_EVENT, "vampdisgust", /datum/mood_event/bloodsucker_disgust)
|
||||
sickphase ++
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
// Incap?
|
||||
if(must_be_capacitated)
|
||||
var/mob/living/L = owner
|
||||
if (L.incapacitated(TRUE, TRUE) || L.resting && !can_be_immobilized)
|
||||
if (L.incapacitated(TRUE, TRUE) || !CHECK_MOBILITY(L, MOBILITY_STAND) && !can_be_immobilized)
|
||||
if(display_error)
|
||||
to_chat(owner, "<span class='warning'>Not while you're incapacitated!</span>")
|
||||
return FALSE
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
|
||||
|
||||
// organ_internal.dm -- /obj/item/organ
|
||||
|
||||
|
||||
|
||||
// Do I have a stake in my heart?
|
||||
/mob/living/AmStaked()
|
||||
var/obj/item/bodypart/BP = get_bodypart("chest")
|
||||
@@ -13,16 +9,14 @@
|
||||
if (istype(I,/obj/item/stake/))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/proc/AmStaked()
|
||||
return FALSE
|
||||
|
||||
|
||||
/mob/living/proc/StakeCanKillMe()
|
||||
return IsSleeping() || stat >= UNCONSCIOUS || blood_volume <= 0 || HAS_TRAIT(src, TRAIT_DEATHCOMA) // NOTE: You can't go to sleep in a coffin with a stake in you.
|
||||
|
||||
|
||||
///obj/item/weapon/melee/stake
|
||||
/obj/item/stake/
|
||||
/obj/item/stake
|
||||
name = "wooden stake"
|
||||
desc = "A simple wooden stake carved to a sharp point."
|
||||
icon = 'icons/obj/items_and_weapons.dmi'
|
||||
@@ -96,6 +90,7 @@
|
||||
user.dropItemToGround(src, TRUE) //user.drop_item() // "drop item" doesn't seem to exist anymore. New proc is user.dropItemToGround() but it doesn't seem like it's needed now?
|
||||
var/obj/item/bodypart/B = C.get_bodypart("chest") // This was all taken from hitby() in human_defense.dm
|
||||
B.embedded_objects |= src
|
||||
embedded()
|
||||
add_mob_blood(target)//Place blood on the stake
|
||||
loc = C // Put INSIDE the character
|
||||
B.receive_damage(w_class * embedding.embedded_impact_pain_multiplier)
|
||||
@@ -112,8 +107,7 @@
|
||||
|
||||
// Can this target be staked? If someone stands up before this is complete, it fails. Best used on someone stationary.
|
||||
/mob/living/carbon/proc/can_be_staked()
|
||||
//return resting || IsKnockdown() || IsUnconscious() || (stat && (stat != SOFT_CRIT || pulledby)) || (has_trait(TRAIT_FAKEDEATH)) || resting || IsStun() || IsFrozen() || (pulledby && pulledby.grab_state >= GRAB_NECK)
|
||||
return (resting || lying || IsUnconscious() || pulledby && pulledby.grab_state >= GRAB_NECK)
|
||||
return !CHECK_MOBILITY(src, MOBILITY_STAND)
|
||||
// ABOVE: Taken from update_mobility() in living.dm
|
||||
|
||||
/obj/item/stake/hardened
|
||||
|
||||
@@ -42,25 +42,18 @@
|
||||
/obj/structure/closet/crate
|
||||
var/mob/living/resident // This lets bloodsuckers claim any "closet" as a Coffin, so long as they could get into it and close it. This locks it in place, too.
|
||||
|
||||
/obj/structure/closet/crate/coffin
|
||||
var/pryLidTimer = 250
|
||||
can_weld_shut = FALSE
|
||||
breakout_time = 200
|
||||
|
||||
|
||||
/obj/structure/closet/crate/coffin/blackcoffin
|
||||
name = "black coffin"
|
||||
desc = "For those departed who are not so dear."
|
||||
icon_state = "coffin"
|
||||
icon = 'icons/obj/vamp_obj.dmi'
|
||||
can_weld_shut = FALSE
|
||||
resistance_flags = 0 // Start off with no bonuses.
|
||||
open_sound = 'sound/bloodsucker/coffin_open.ogg'
|
||||
close_sound = 'sound/bloodsucker/coffin_close.ogg'
|
||||
breakout_time = 600
|
||||
pryLidTimer = 400
|
||||
resistance_flags = NONE
|
||||
integrity_failure = 70
|
||||
max_integrity = 100
|
||||
integrity_failure = 0.5
|
||||
armor = list("melee" = 50, "bullet" = 20, "laser" = 30, "energy" = 0, "bomb" = 50, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 60)
|
||||
|
||||
/obj/structure/closet/crate/coffin/meatcoffin
|
||||
@@ -68,8 +61,6 @@
|
||||
desc = "When you're ready to meat your maker, the steaks can never be too high."
|
||||
icon_state = "meatcoffin"
|
||||
icon = 'icons/obj/vamp_obj.dmi'
|
||||
can_weld_shut = FALSE
|
||||
resistance_flags = 0 // Start off with no bonuses.
|
||||
open_sound = 'sound/effects/footstep/slime1.ogg'
|
||||
close_sound = 'sound/effects/footstep/slime1.ogg'
|
||||
breakout_time = 200
|
||||
@@ -77,24 +68,23 @@
|
||||
resistance_flags = NONE
|
||||
material_drop = /obj/item/reagent_containers/food/snacks/meat/slab
|
||||
material_drop_amount = 3
|
||||
integrity_failure = 40
|
||||
integrity_failure = 0.57
|
||||
armor = list("melee" = 70, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 70, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 100)
|
||||
|
||||
|
||||
/obj/structure/closet/crate/coffin/metalcoffin
|
||||
name = "metal coffin"
|
||||
desc = "A big metal sardine can inside of another big metal sardine can, in space."
|
||||
icon_state = "metalcoffin"
|
||||
icon = 'icons/obj/vamp_obj.dmi'
|
||||
can_weld_shut = FALSE
|
||||
resistance_flags = FIRE_PROOF | LAVA_PROOF
|
||||
open_sound = 'sound/effects/pressureplate.ogg'
|
||||
close_sound = 'sound/effects/pressureplate.ogg'
|
||||
breakout_time = 300
|
||||
pryLidTimer = 200
|
||||
resistance_flags = NONE
|
||||
material_drop = /obj/item/stack/sheet/metal
|
||||
material_drop_amount = 5
|
||||
integrity_failure = 60
|
||||
max_integrity = 200
|
||||
integrity_failure = 0.25
|
||||
armor = list("melee" = 40, "bullet" = 15, "laser" = 50, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 60)
|
||||
|
||||
//////////////////////////////////////////////
|
||||
|
||||
@@ -205,7 +205,7 @@
|
||||
buckled_mob.pixel_y = buckled_mob.get_standard_pixel_y_offset(180)
|
||||
src.visible_message(text("<span class='danger'>[buckled_mob][buckled_mob.stat==DEAD?"'s corpse":""] slides off of the rack.</span>"))
|
||||
density = FALSE
|
||||
buckled_mob.AdjustKnockdown(30)
|
||||
buckled_mob.DefaultCombatKnockdown(30)
|
||||
update_icon()
|
||||
useLock = FALSE // Failsafe
|
||||
|
||||
@@ -258,7 +258,7 @@
|
||||
/obj/structure/bloodsucker/vassalrack/proc/torture_victim(mob/living/user, mob/living/target)
|
||||
var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
|
||||
// Check Bloodmob/living/M, force = FALSE, check_loc = TRUE
|
||||
var/convert_cost = 200 + 200 * bloodsuckerdatum.vassals
|
||||
var/convert_cost = 200 + 200 * bloodsuckerdatum.vassals
|
||||
if(user.blood_volume < convert_cost + 5)
|
||||
to_chat(user, "<span class='notice'>You don't have enough blood to initiate the Dark Communion with [target].</span>")
|
||||
return
|
||||
@@ -449,7 +449,7 @@
|
||||
/obj/structure/bloodsucker/candelabrum/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/structure/bloodsucker/candelabrum/update_icon()
|
||||
/obj/structure/bloodsucker/candelabrum/update_icon_state()
|
||||
icon_state = "candelabrum[lit ? "_lit" : ""]"
|
||||
|
||||
/obj/structure/bloodsucker/candelabrum/examine(mob/user)
|
||||
|
||||
@@ -72,8 +72,7 @@
|
||||
if(rand(5 + powerlevel) >= 5)
|
||||
target.visible_message("<span class='danger'>[user] lands a vicious punch, sending [target] away!</span>", \
|
||||
"<span class='userdanger'>[user] has landed a horrifying punch on you, sending you flying!!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
target.Knockdown(min(5, rand(10, 10 * powerlevel)) )
|
||||
|
||||
target.DefaultCombatKnockdown(min(5, rand(10, 10 * powerlevel)) )
|
||||
// Attack!
|
||||
playsound(get_turf(target), 'sound/weapons/punch4.ogg', 60, 1, -1)
|
||||
user.do_attack_animation(target, ATTACK_EFFECT_SMASH)
|
||||
@@ -145,7 +144,7 @@
|
||||
// Knock Down (if Living)
|
||||
if (isliving(M))
|
||||
var/mob/living/L = M
|
||||
L.Knockdown(pull_power * 10 + 20)
|
||||
L.DefaultCombatKnockdown(pull_power * 10 + 20)
|
||||
// Knock Back (before Knockdown, which probably cancels pull)
|
||||
var/send_dir = get_dir(owner, M)
|
||||
var/turf/T = get_ranged_target_turf(M, send_dir, pull_power)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
amToggle = TRUE
|
||||
warn_constant_cost = TRUE
|
||||
var/moveintent_was_run
|
||||
var/runintent
|
||||
var/walk_threshold = 0.4 // arbitrary number, to be changed. edit in last commit: this is fine after testing on box station for a bit
|
||||
var/lum
|
||||
|
||||
@@ -31,23 +32,25 @@
|
||||
var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
|
||||
var/mob/living/user = owner
|
||||
|
||||
moveintent_was_run = (user.m_intent == MOVE_INTENT_RUN)
|
||||
|
||||
while(bloodsuckerdatum && ContinueActive(user))
|
||||
// Pay Blood Toll (if awake)
|
||||
owner.alpha = max(35, owner.alpha - min(75, 10 + 5 * level_current))
|
||||
bloodsuckerdatum.AddBloodVolume(-0.2)
|
||||
|
||||
moveintent_was_run = (user.m_intent == MOVE_INTENT_RUN)
|
||||
runintent = (user.m_intent == MOVE_INTENT_RUN)
|
||||
var/turf/T = get_turf(user)
|
||||
lum = T.get_lumcount()
|
||||
|
||||
if(istype(owner.loc))
|
||||
if(lum > walk_threshold)
|
||||
if(moveintent_was_run)
|
||||
if(runintent)
|
||||
user.toggle_move_intent()
|
||||
ADD_TRAIT(user, TRAIT_NORUNNING, "cloak of darkness")
|
||||
|
||||
if(lum < walk_threshold)
|
||||
if(!moveintent_was_run)
|
||||
if(!runintent)
|
||||
user.toggle_move_intent()
|
||||
REMOVE_TRAIT(user, TRAIT_NORUNNING, "cloak of darkness")
|
||||
|
||||
@@ -65,5 +68,8 @@
|
||||
..()
|
||||
REMOVE_TRAIT(user, TRAIT_NORUNNING, "cloak of darkness")
|
||||
user.alpha = 255
|
||||
if(!moveintent_was_run)
|
||||
|
||||
runintent = (user.m_intent == MOVE_INTENT_RUN)
|
||||
|
||||
if(!runintent && moveintent_was_run)
|
||||
user.toggle_move_intent()
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
// Bloodsuckers not affected by "the Kiss" of another vampire
|
||||
if(!target.mind || !target.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
|
||||
target.Unconscious(50,0)
|
||||
target.Knockdown(40 + 5 * level_current,1)
|
||||
target.DefaultCombatKnockdown(40 + 5 * level_current,1)
|
||||
// NOTE: THis is based on level of power!
|
||||
if(ishuman(target))
|
||||
target.adjustStaminaLoss(5, forced = TRUE)// Base Stamina Damage
|
||||
@@ -321,4 +321,4 @@
|
||||
// My mouth is no longer full
|
||||
REMOVE_TRAIT(owner, TRAIT_MUTE, "bloodsucker_feed")
|
||||
// Let me move immediately
|
||||
user.update_canmove()
|
||||
user.update_mobility()
|
||||
|
||||
@@ -100,8 +100,8 @@
|
||||
var/mob/living/simple_animal/SA = pick(/mob/living/simple_animal/mouse,/mob/living/simple_animal/mouse,/mob/living/simple_animal/mouse, /mob/living/simple_animal/hostile/retaliate/bat) //prob(300) /mob/living/simple_animal/mouse,
|
||||
new SA (owner.loc)
|
||||
// TELEPORT: Move to Coffin & Close it!
|
||||
user.set_resting(TRUE, TRUE, FALSE)
|
||||
do_teleport(owner, bloodsuckerdatum.coffin, no_effects = TRUE, forced = TRUE, channel = TELEPORT_CHANNEL_QUANTUM)
|
||||
user.resting = TRUE
|
||||
user.Stun(30,1)
|
||||
// CLOSE LID: If fail, force me in.
|
||||
if(!bloodsuckerdatum.coffin.close(owner))
|
||||
|
||||
@@ -76,16 +76,17 @@
|
||||
sleep(speed)
|
||||
UnregisterSignal(owner, COMSIG_MOVABLE_MOVED)
|
||||
hit = null
|
||||
user.update_canmove()
|
||||
user.update_mobility()
|
||||
|
||||
/datum/action/bloodsucker/targeted/haste/DeactivatePower(mob/living/user = owner, mob/living/target)
|
||||
..() // activate = FALSE
|
||||
user.update_canmove()
|
||||
user.update_mobility()
|
||||
|
||||
/datum/action/bloodsucker/targeted/haste/proc/on_move()
|
||||
for(var/mob/living/L in dview(1, get_turf(owner)))
|
||||
if(!hit[L] && (L != owner))
|
||||
hit[L] = TRUE
|
||||
playsound(L, "sound/weapons/punch[rand(1,4)].ogg", 15, 1, -1)
|
||||
L.Knockdown(10 + level_current * 5, override_hardstun = 0.1)
|
||||
L.DefaultCombatKnockdown(10 + level_current * 5)
|
||||
L.Paralyze(0.1)
|
||||
L.spin(10, 1)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
button_icon_state = "power_lunge"
|
||||
bloodcost = 10
|
||||
cooldown = 120
|
||||
target_range = 5
|
||||
target_range = 3
|
||||
power_activates_immediately = TRUE
|
||||
message_Trigger = "Whom will you ensnare within your grasp?"
|
||||
must_be_capacitated = TRUE
|
||||
@@ -52,18 +52,19 @@
|
||||
// set waitfor = FALSE <---- DONT DO THIS!We WANT this power to hold up ClickWithPower(), so that we can unlock the power when it's done.
|
||||
var/mob/living/carbon/target = A
|
||||
var/turf/T = get_turf(target)
|
||||
var/mob/living/L = owner
|
||||
// Clear Vars
|
||||
owner.pulling = null
|
||||
// Will we Knock them Down?
|
||||
var/do_knockdown = !is_A_facing_B(target,owner) || owner.alpha <= 0 || istype(owner.loc, /obj/structure/closet)
|
||||
// CAUSES: Target has their back to me, I'm invisible, or I'm in a Closet
|
||||
// Step One: Heatseek toward Target's Turf
|
||||
addtimer(CALLBACK(owner, .proc/_walk, 0), 2 SECONDS)
|
||||
addtimer(CALLBACK(GLOBAL_PROC, .proc/_walk, owner, 0), 2 SECONDS)
|
||||
target.playsound_local(get_turf(owner), 'sound/bloodsucker/lunge_warn.ogg', 60, FALSE, pressure_affected = FALSE) // target-only telegraphing
|
||||
owner.playsound_local(owner, 'sound/bloodsucker/lunge_warn.ogg', 60, FALSE, pressure_affected = FALSE) // audio feedback to the user
|
||||
if(do_mob(owner, owner, 6, TRUE, TRUE))
|
||||
if(do_mob(owner, owner, 7, TRUE, TRUE))
|
||||
walk_towards(owner, T, 0.1, 10) // yes i know i shouldn't use this but i don't know how to work in anything better
|
||||
if(get_turf(owner) != T && !(isliving(target) && target.Adjacent(owner)) && owner.incapacitated() && owner.resting)
|
||||
if(get_turf(owner) != T && !(isliving(target) && target.Adjacent(owner)) && owner.incapacitated() && !CHECK_MOBILITY(L, MOBILITY_STAND))
|
||||
var/send_dir = get_dir(owner, T)
|
||||
new /datum/forced_movement(owner, get_ranged_target_turf(owner, send_dir, 1), 1, FALSE)
|
||||
owner.spin(10)
|
||||
@@ -80,8 +81,8 @@
|
||||
target.grabbedby(owner) // Taken from mutations.dm under changelings
|
||||
target.grippedby(owner, instant = TRUE) //instant aggro grab
|
||||
break
|
||||
sleep(i*3)
|
||||
sleep(3)
|
||||
|
||||
/datum/action/bloodsucker/targeted/lunge/DeactivatePower(mob/living/user = owner, mob/living/target)
|
||||
..() // activate = FALSE
|
||||
user.update_canmove()
|
||||
user.update_mobility()
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
button_icon_state = "power_mez"
|
||||
bloodcost = 30
|
||||
cooldown = 300
|
||||
target_range = 3
|
||||
target_range = 2
|
||||
power_activates_immediately = TRUE
|
||||
message_Trigger = "Whom will you subvert to your will?"
|
||||
must_be_capacitated = TRUE
|
||||
@@ -73,7 +73,7 @@
|
||||
to_chat(owner, "<span class='warning'>You're too far outside your victim's view.</span>")
|
||||
return FALSE
|
||||
|
||||
if(target.has_status_effect(STATUS_EFFECT_MESMERIZE)) // ?
|
||||
if(target.has_status_effect(STATUS_EFFECT_MESMERIZE)) // ignores facing once the windup has started
|
||||
return TRUE
|
||||
|
||||
// Check: Facing target?
|
||||
@@ -82,45 +82,56 @@
|
||||
to_chat(owner, "<span class='warning'>You must be facing your victim.</span>")
|
||||
return FALSE
|
||||
// Check: Target facing me?
|
||||
if(!target.resting && !is_A_facing_B(target,owner))
|
||||
if (CHECK_MOBILITY(target, MOBILITY_STAND) && !is_A_facing_B(target,owner))
|
||||
if(display_error)
|
||||
to_chat(owner, "<span class='warning'>Your victim must be facing you to see into your eyes.</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/action/bloodsucker/targeted/mesmerize/proc/ContinueTarget(atom/A)
|
||||
var/mob/living/carbon/target = A
|
||||
var/mob/living/user = owner
|
||||
|
||||
var/cancontinue=CheckCanTarget(target)
|
||||
if(!cancontinue)
|
||||
success = FALSE
|
||||
target.remove_status_effect(STATUS_EFFECT_MESMERIZE)
|
||||
user.remove_status_effect(STATUS_EFFECT_MESMERIZE)
|
||||
DeactivatePower()
|
||||
DeactivateRangedAbility()
|
||||
StartCooldown()
|
||||
to_chat(user, "<span class='warning'>[target] has escaped your gaze!</span>")
|
||||
UnregisterSignal(target, COMSIG_MOVABLE_MOVED)
|
||||
|
||||
/datum/action/bloodsucker/targeted/mesmerize/FireTargetedPower(atom/A)
|
||||
// set waitfor = FALSE <---- DONT DO THIS!We WANT this power to hold up ClickWithPower(), so that we can unlock the power when it's done.
|
||||
var/mob/living/carbon/target = A
|
||||
var/mob/living/user = owner
|
||||
|
||||
if(istype(target))
|
||||
success = TRUE
|
||||
var/power_time = 138 + level_current * 12
|
||||
target.apply_status_effect(STATUS_EFFECT_MESMERIZE, 30)
|
||||
user.apply_status_effect(STATUS_EFFECT_MESMERIZE, 30)
|
||||
if(do_mob(user, target, 30, TRUE, TRUE)) // 3 seconds windup
|
||||
success = CheckCanTarget(target)
|
||||
if(success) // target just has to be out of view when it is fully charged in order to avoid
|
||||
PowerActivatedSuccessfully() // blood & cooldown only altered if power activated successfully - less "fuck you"-y
|
||||
target.face_atom(user)
|
||||
target.apply_status_effect(STATUS_EFFECT_MESMERIZE, power_time) // pretty much purely cosmetic
|
||||
target.Stun(power_time)
|
||||
to_chat(user, "<span class='notice'>[target] is fixed in place by your hypnotic gaze.</span>")
|
||||
target.next_move = world.time + power_time // <--- Use direct change instead. We want an unmodified delay to their next move // target.changeNext_move(power_time) // check click.dm
|
||||
target.notransform = TRUE // <--- Fuck it. We tried using next_move, but they could STILL resist. We're just doing a hard freeze.
|
||||
else
|
||||
to_chat(user, "<span class='warning'>[target] has escaped your gaze!</span>")
|
||||
DeactivatePower()
|
||||
DeactivateRangedAbility()
|
||||
StartCooldown()
|
||||
// oops! if they knew how they could just spam stun the victim and themselves.
|
||||
|
||||
spawn(power_time)
|
||||
if(istype(target) && success)
|
||||
target.notransform = FALSE
|
||||
// They Woke Up! (Notice if within view)
|
||||
if(istype(user) && target.stat == CONSCIOUS && (target in view(10, get_turf(user))) )
|
||||
to_chat(user, "<span class='warning'>[target] has snapped out of their trance.</span>")
|
||||
|
||||
RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/ContinueTarget)
|
||||
|
||||
// 3 second windup
|
||||
sleep(30)
|
||||
if(success)
|
||||
PowerActivatedSuccessfully() // blood & cooldown only altered if power activated successfully - less "fuck you"-y
|
||||
target.face_atom(user)
|
||||
target.apply_status_effect(STATUS_EFFECT_MESMERIZE, power_time) // pretty much purely cosmetic
|
||||
target.Stun(power_time)
|
||||
to_chat(user, "<span class='notice'>[target] is fixed in place by your hypnotic gaze.</span>")
|
||||
target.next_move = world.time + power_time // <--- Use direct change instead. We want an unmodified delay to their next move // target.changeNext_move(power_time) // check click.dm
|
||||
target.notransform = TRUE // <--- Fuck it. We tried using next_move, but they could STILL resist. We're just doing a hard freeze.
|
||||
spawn(power_time)
|
||||
if(istype(target) && success)
|
||||
target.notransform = FALSE
|
||||
// They Woke Up! (Notice if within view)
|
||||
if(istype(user) && target.stat == CONSCIOUS && (target in view(10, get_turf(user))) )
|
||||
to_chat(user, "<span class='warning'>[target] has snapped out of their trance.</span>")
|
||||
|
||||
/datum/action/bloodsucker/targeted/mesmerize/ContinueActive(mob/living/user, mob/living/target)
|
||||
return ..() && CheckCanUse() && CheckCanTarget(target)
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
H.socks = random_socks(H.gender)
|
||||
//H.eye_color = random_eye_color()
|
||||
REMOVE_TRAIT(H, TRAIT_DISFIGURED, null) //
|
||||
H.dna.features = random_features()
|
||||
H.dna.features = random_features(H.dna.species?.id)
|
||||
|
||||
// Apply Appearance
|
||||
H.update_body() // Outfit and underware, also body.
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
if(istype(our_target, /datum/cellular_emporium))
|
||||
cellular_emporium = our_target
|
||||
else
|
||||
throw EXCEPTION("cellular_emporium action created with non emporium")
|
||||
CRASH("cellular_emporium action created with non emporium")
|
||||
|
||||
/datum/action/innate/cellular_emporium/Activate()
|
||||
cellular_emporium.ui_interact(owner)
|
||||
|
||||
@@ -13,17 +13,14 @@
|
||||
var/mob/living/carbon/human/H = user //SHOULD always be human, because req_human = 1
|
||||
if(!istype(H)) // req_human could be done in can_sting stuff.
|
||||
return
|
||||
var/datum/mutation/human/HM = GLOB.mutations_list[CHAMELEON]
|
||||
if(HM in H.dna.mutations)
|
||||
HM.force_lose(H)
|
||||
if(H.dna.get_mutation(CHAMELEON))
|
||||
H.dna.remove_mutation(CHAMELEON)
|
||||
else
|
||||
HM.force_give(H)
|
||||
H.dna.add_mutation(CHAMELEON)
|
||||
return TRUE
|
||||
|
||||
/obj/effect/proc_holder/changeling/chameleon_skin/on_refund(mob/user)
|
||||
action.Remove(user)
|
||||
if(user.has_dna())
|
||||
var/mob/living/carbon/C = user
|
||||
var/datum/mutation/human/HM = GLOB.mutations_list[CHAMELEON]
|
||||
if(HM in C.dna.mutations)
|
||||
HM.force_lose(C)
|
||||
C.dna.remove_mutation(CHAMELEON)
|
||||
@@ -18,7 +18,6 @@
|
||||
user.tod = STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)
|
||||
user.fakedeath("changeling") //play dead
|
||||
user.update_stat()
|
||||
user.update_canmove()
|
||||
|
||||
addtimer(CALLBACK(src, .proc/ready_to_regenerate, user), LING_FAKEDEATH_TIME, TIMER_UNIQUE)
|
||||
return TRUE
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
H.confused += 3
|
||||
for(var/mob/living/silicon/S in range(2,user))
|
||||
to_chat(S, "<span class='userdanger'>Your sensors are disabled by a shower of blood!</span>")
|
||||
S.Knockdown(60)
|
||||
S.DefaultCombatKnockdown(60)
|
||||
var/turf = get_turf(user)
|
||||
user.gib()
|
||||
. = TRUE
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
if(issilicon(M))
|
||||
SEND_SOUND(M, sound('sound/weapons/flash.ogg'))
|
||||
M.Knockdown(rand(100,200))
|
||||
M.DefaultCombatKnockdown(rand(100,200))
|
||||
|
||||
for(var/obj/machinery/light/L in range(4, user))
|
||||
L.on = 1
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
changeling.chem_recharge_slowdown -= 0.5
|
||||
if(stacks >= 20)
|
||||
to_chat(user, "<span class='danger'>We collapse in exhaustion.</span>")
|
||||
user.Knockdown(60)
|
||||
user.DefaultCombatKnockdown(60)
|
||||
user.emote("gasp")
|
||||
|
||||
INVOKE_ASYNC(src, .proc/muscle_loop, user)
|
||||
@@ -40,7 +40,7 @@
|
||||
if(user.stat != CONSCIOUS || user.staminaloss >= 90)
|
||||
active = !active
|
||||
to_chat(user, "<span class='notice'>Our muscles relax without the energy to strengthen them.</span>")
|
||||
user.Knockdown(40)
|
||||
user.DefaultCombatKnockdown(40)
|
||||
user.remove_movespeed_modifier(MOVESPEED_ID_CHANGELING_MUSCLES)
|
||||
changeling.chem_recharge_slowdown -= 0.5
|
||||
break
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
if(iscultist(L)) //No longer stuns cultists, instead sets them on fire and burns them
|
||||
to_chat(L, "<span class='heavy_brass'>\"Watch your step, wretch.\"</span>")
|
||||
L.adjustFireLoss(10)
|
||||
L.Knockdown(20, FALSE)
|
||||
L.DefaultCombatKnockdown(20, FALSE)
|
||||
L.adjust_fire_stacks(5) //Burn!
|
||||
L.IgniteMob()
|
||||
else
|
||||
@@ -155,7 +155,7 @@
|
||||
if(brutedamage || burndamage)
|
||||
L.adjustBruteLoss(-(brutedamage * 0.25))
|
||||
L.adjustFireLoss(-(burndamage * 0.25))
|
||||
L.Knockdown(50) //Completely defenseless for five seconds - mainly to give them time to read over the information they've just been presented with
|
||||
L.DefaultCombatKnockdown(50) //Completely defenseless for five seconds - mainly to give them time to read over the information they've just been presented with
|
||||
if(iscarbon(L))
|
||||
var/mob/living/carbon/C = L
|
||||
C.silent += 5
|
||||
@@ -251,6 +251,7 @@
|
||||
return TRUE
|
||||
|
||||
/obj/effect/clockwork/sigil/transmission/update_icon()
|
||||
. = ..()
|
||||
var/power_charge = get_clockwork_power()
|
||||
if(GLOB.ratvar_awakens)
|
||||
alpha = 255
|
||||
|
||||
@@ -196,7 +196,7 @@
|
||||
if(!iscultist(L))
|
||||
L.visible_message("<span class='warning'>[L]'s eyes blaze with brilliant light!</span>", \
|
||||
"<span class='userdanger'>Your vision suddenly screams with white-hot light!</span>")
|
||||
L.Knockdown(15, TRUE, FALSE, 15)
|
||||
L.DefaultCombatKnockdown(15, TRUE, FALSE, 15)
|
||||
L.apply_status_effect(STATUS_EFFECT_KINDLE)
|
||||
L.flash_act(1, 1)
|
||||
if(issilicon(target))
|
||||
|
||||
@@ -57,15 +57,15 @@
|
||||
else if(!..())
|
||||
if(!L.anti_magic_check())
|
||||
if(issilicon(L))
|
||||
L.Knockdown(100)
|
||||
L.DefaultCombatKnockdown(100)
|
||||
else if(iscultist(L))
|
||||
L.confused += CLAMP(10 - L.confused, 0, 5) // Spearthrow now confuses enemy cultists + just deals extra damage / sets on fire instead of hardstunning + damage
|
||||
to_chat(L, "<span class ='userdanger'>[src] crashes into you with burning force, sending you reeling!</span>")
|
||||
L.adjust_fire_stacks(2)
|
||||
L.Knockdown(1)
|
||||
L.DefaultCombatKnockdown(1)
|
||||
L.IgniteMob()
|
||||
else
|
||||
L.Knockdown(40)
|
||||
L.DefaultCombatKnockdown(40)
|
||||
GLOB.clockwork_vitality += L.adjustFireLoss(bonus_burn * 3) //normally a total of 40 damage, 70 with ratvar
|
||||
break_spear(T)
|
||||
else
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
var/datum/status_effect/belligerent/B = C.apply_status_effect(STATUS_EFFECT_BELLIGERENT)
|
||||
if(!QDELETED(B))
|
||||
B.duration = world.time + 30
|
||||
C.Knockdown(5) //knocks down for half a second if affected
|
||||
C.DefaultCombatKnockdown(5) //knocks down for half a second if affected
|
||||
sleep(!GLOB.ratvar_approaches ? 16 : 10)
|
||||
name = "judicial blast"
|
||||
layer = ABOVE_ALL_MOB_LAYER
|
||||
@@ -196,7 +196,7 @@
|
||||
L.visible_message("<span class='warning'>Strange energy flows into [L]'s [I.name]!</span>", \
|
||||
"<span class='userdanger'>Your [I.name] shields you from [src]!</span>")
|
||||
continue
|
||||
L.Knockdown(15) //knocks down briefly when exploding
|
||||
L.DefaultCombatKnockdown(15) //knocks down briefly when exploding
|
||||
if(!iscultist(L))
|
||||
L.visible_message("<span class='warning'>[L] is struck by a judicial explosion!</span>", \
|
||||
"<span class='userdanger'>[!issilicon(L) ? "An unseen force slams you into the ground!" : "ERROR: Motor servos disabled by external source!"]</span>")
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
/mob/living/simple_animal/hostile/clockwork/ratvar_act()
|
||||
fully_heal(TRUE)
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
|
||||
/mob/living/simple_animal/hostile/clockwork/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
|
||||
return 0 //ouch, my metal-unlikely-to-be-damaged-by-electricity-body
|
||||
|
||||
/mob/living/simple_animal/hostile/clockwork/examine(mob/user)
|
||||
|
||||
@@ -57,5 +57,5 @@
|
||||
L.confused = min(L.confused + 15, 50)
|
||||
L.dizziness = min(L.dizziness + 15, 50)
|
||||
if(L.confused >= 25)
|
||||
L.Knockdown(FLOOR(L.confused * 0.8, 1))
|
||||
L.DefaultCombatKnockdown(FLOOR(L.confused * 0.8, 1))
|
||||
take_damage(max_integrity)
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
if(buckled_mobs && LAZYLEN(buckled_mobs))
|
||||
var/mob/living/L = buckled_mobs[1]
|
||||
if(iscarbon(L))
|
||||
L.Knockdown(100)
|
||||
L.DefaultCombatKnockdown(100)
|
||||
L.visible_message("<span class='warning'>[L] is maimed as the skewer shatters while still in [L.p_their()] body!</span>")
|
||||
L.adjustBruteLoss(15)
|
||||
unbuckle_mob(L)
|
||||
@@ -117,6 +117,6 @@
|
||||
return
|
||||
skewee.visible_message("<span class='danger'>[skewee] comes free of [src] with a squelching pop!</span>", \
|
||||
"<span class='boldannounce'>You come free of [src]!</span>")
|
||||
skewee.Knockdown(30)
|
||||
skewee.DefaultCombatKnockdown(30)
|
||||
playsound(skewee, 'sound/misc/desceration-03.ogg', 50, TRUE)
|
||||
unbuckle_mob(skewee)
|
||||
|
||||
@@ -446,7 +446,7 @@
|
||||
var/atom/throw_target = get_edge_target_turf(L, user.dir)
|
||||
L.throw_at(throw_target, 7, 1, user)
|
||||
else if(!iscultist(L))
|
||||
L.Knockdown(160)
|
||||
L.DefaultCombatKnockdown(160)
|
||||
L.adjustStaminaLoss(140) //Ensures hard stamcrit
|
||||
L.flash_act(1,1)
|
||||
if(issilicon(target))
|
||||
|
||||
@@ -46,10 +46,11 @@
|
||||
/obj/item/melee/cultblade/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/butchering, 40, 100)
|
||||
AddElement(/datum/element/sword_point)
|
||||
|
||||
/obj/item/melee/cultblade/attack(mob/living/target, mob/living/carbon/human/user)
|
||||
if(!iscultist(user))
|
||||
user.Knockdown(100)
|
||||
user.DefaultCombatKnockdown(100)
|
||||
user.dropItemToGround(src, TRUE)
|
||||
user.visible_message("<span class='warning'>A powerful force shoves [user] away from [target]!</span>", \
|
||||
"<span class='cultlarge'>\"You shouldn't play with sharp things. You'll poke someone's eye out.\"</span>")
|
||||
@@ -148,7 +149,7 @@
|
||||
user.emote("scream")
|
||||
user.apply_damage(30, BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
|
||||
user.dropItemToGround(src, TRUE)
|
||||
user.Knockdown(50)
|
||||
user.DefaultCombatKnockdown(50)
|
||||
return
|
||||
force = initial(force)
|
||||
jaunt.Grant(user, src)
|
||||
@@ -263,7 +264,7 @@
|
||||
/obj/item/restraints/legcuffs/bola/cult/pickup(mob/living/user)
|
||||
if(!iscultist(user))
|
||||
to_chat(user, "<span class='warning'>The bola seems to take on a life of its own!</span>")
|
||||
throw_impact(user)
|
||||
ensnare(user)
|
||||
|
||||
/obj/item/restraints/legcuffs/bola/cult/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
|
||||
if(iscultist(hit_atom))
|
||||
@@ -371,7 +372,7 @@
|
||||
max = 40
|
||||
prefix = "darkened"
|
||||
|
||||
/obj/item/sharpener/cult/update_icon()
|
||||
/obj/item/sharpener/cult/update_icon_state()
|
||||
var/old_state = icon_state
|
||||
icon_state = "cult_sharpener[used ? "_used" : ""]"
|
||||
if(old_state != icon_state)
|
||||
@@ -405,7 +406,7 @@
|
||||
to_chat(user, "<span class='warning'>An overwhelming sense of nausea overpowers you!</span>")
|
||||
user.dropItemToGround(src, TRUE)
|
||||
user.Dizzy(30)
|
||||
user.Knockdown(100)
|
||||
user.DefaultCombatKnockdown(100)
|
||||
else
|
||||
to_chat(user, "<span class='cultlarge'>\"Trying to use things you don't own is bad, you know.\"</span>")
|
||||
to_chat(user, "<span class='userdanger'>The armor squeezes at your body!</span>")
|
||||
@@ -457,7 +458,7 @@
|
||||
to_chat(user, "<span class='warning'>An overwhelming sense of nausea overpowers you!</span>")
|
||||
user.dropItemToGround(src, TRUE)
|
||||
user.Dizzy(30)
|
||||
user.Knockdown(100)
|
||||
user.DefaultCombatKnockdown(100)
|
||||
else
|
||||
to_chat(user, "<span class='cultlarge'>\"Trying to use things you don't own is bad, you know.\"</span>")
|
||||
to_chat(user, "<span class='userdanger'>The robes squeeze at your body!</span>")
|
||||
@@ -478,7 +479,7 @@
|
||||
to_chat(user, "<span class='cultlarge'>\"You want to be blind, do you?\"</span>")
|
||||
user.dropItemToGround(src, TRUE)
|
||||
user.Dizzy(30)
|
||||
user.Knockdown(100)
|
||||
user.DefaultCombatKnockdown(100)
|
||||
user.blind_eyes(30)
|
||||
|
||||
/obj/item/reagent_containers/glass/beaker/unholywater
|
||||
@@ -499,7 +500,7 @@
|
||||
/obj/item/shuttle_curse/attack_self(mob/living/user)
|
||||
if(!iscultist(user))
|
||||
user.dropItemToGround(src, TRUE)
|
||||
user.Knockdown(100)
|
||||
user.DefaultCombatKnockdown(100)
|
||||
to_chat(user, "<span class='warning'>A powerful force shoves you away from [src]!</span>")
|
||||
return
|
||||
if(curselimit > 1)
|
||||
@@ -687,7 +688,7 @@
|
||||
qdel(spear_act)
|
||||
..()
|
||||
|
||||
/obj/item/twohanded/cult_spear/update_icon()
|
||||
/obj/item/twohanded/cult_spear/update_icon_state()
|
||||
icon_state = "bloodspear[wielded]"
|
||||
|
||||
/obj/item/twohanded/cult_spear/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
|
||||
@@ -705,10 +706,10 @@
|
||||
if(is_servant_of_ratvar(L))
|
||||
to_chat(L, "<span class='cultlarge'>\"Kneel for me, scum\"</span>")
|
||||
L.confused += CLAMP(10 - L.confused, 0, 5) //confuses and lightly knockdowns + damages hostile cultists instead of hardstunning like before
|
||||
L.Knockdown(15)
|
||||
L.DefaultCombatKnockdown(15)
|
||||
L.adjustBruteLoss(10)
|
||||
else
|
||||
L.Knockdown(50)
|
||||
L.DefaultCombatKnockdown(50)
|
||||
break_spear(T)
|
||||
else
|
||||
..()
|
||||
@@ -843,7 +844,7 @@
|
||||
INVOKE_ASYNC(src, .proc/pewpew, user, params)
|
||||
var/obj/structure/emergency_shield/invoker/N = new(user.loc)
|
||||
if(do_after(user, 90, target = user))
|
||||
user.Knockdown(40)
|
||||
user.DefaultCombatKnockdown(40)
|
||||
to_chat(user, "<span class='cult italic'>You have exhausted the power of this spell!</span>")
|
||||
firing = FALSE
|
||||
if(N)
|
||||
@@ -908,7 +909,7 @@
|
||||
else
|
||||
var/mob/living/L = target
|
||||
if(L.density)
|
||||
L.Knockdown(20)
|
||||
L.DefaultCombatKnockdown(20)
|
||||
L.adjustBruteLoss(45)
|
||||
playsound(L, 'sound/hallucinations/wail.ogg', 50, 1)
|
||||
L.emote("scream")
|
||||
@@ -944,7 +945,7 @@
|
||||
T.visible_message("<span class='warning'>The sheer force from [P] shatters the mirror shield!</span>")
|
||||
new /obj/effect/temp_visual/cult/sparks(T)
|
||||
playsound(T, 'sound/effects/glassbr3.ogg', 100)
|
||||
owner.Knockdown(25)
|
||||
owner.DefaultCombatKnockdown(25)
|
||||
qdel(src)
|
||||
return FALSE
|
||||
if(P.is_reflectable)
|
||||
@@ -1001,9 +1002,9 @@
|
||||
else if(!..())
|
||||
if(!L.anti_magic_check())
|
||||
if(is_servant_of_ratvar(L))
|
||||
L.Knockdown(60)
|
||||
L.DefaultCombatKnockdown(60)
|
||||
else
|
||||
L.Knockdown(30)
|
||||
L.DefaultCombatKnockdown(30)
|
||||
if(D.thrower)
|
||||
for(var/mob/living/Next in orange(2, T))
|
||||
if(!Next.density || iscultist(Next))
|
||||
|
||||
@@ -219,7 +219,7 @@ structure_check() searches for nearby cultist structures required for the invoca
|
||||
L.visible_message("<span class='warning'>[L]'s eyes glow a defiant yellow!</span>", \
|
||||
"<span class='cultlarge'>\"Stop resisting. You <i>will</i> be mi-\"</span>\n\
|
||||
<span class='large_brass'>\"Give up and you will feel pain unlike anything you've ever felt!\"</span>")
|
||||
L.Knockdown(80)
|
||||
L.DefaultCombatKnockdown(80)
|
||||
else if(is_convertable)
|
||||
do_convert(L, invokers)
|
||||
else
|
||||
@@ -908,7 +908,7 @@ structure_check() searches for nearby cultist structures required for the invoca
|
||||
if(affecting.key)
|
||||
affecting.visible_message("<span class='warning'>[affecting] slowly relaxes, the glow around [affecting.p_them()] dimming.</span>", \
|
||||
"<span class='danger'>You are re-united with your physical form. [src] releases its hold over you.</span>")
|
||||
affecting.Knockdown(40)
|
||||
affecting.DefaultCombatKnockdown(40)
|
||||
break
|
||||
if(affecting.health <= 10)
|
||||
to_chat(G, "<span class='cultitalic'>Your body can no longer sustain the connection!</span>")
|
||||
@@ -970,7 +970,7 @@ structure_check() searches for nearby cultist structures required for the invoca
|
||||
playsound(T, 'sound/magic/enter_blood.ogg', 100, 1)
|
||||
visible_message("<span class='warning'>A colossal shockwave of energy bursts from the rune, disintegrating it in the process!</span>")
|
||||
for(var/mob/living/L in range(src, 3))
|
||||
L.Knockdown(30)
|
||||
L.DefaultCombatKnockdown(30)
|
||||
empulse(T, 0.42*(intensity), 1)
|
||||
var/list/images = list()
|
||||
var/zmatch = T.z
|
||||
|
||||
@@ -477,7 +477,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
|
||||
if(SOULVALUE >= ARCH_THRESHOLD && ascendable)
|
||||
A.convert_to_archdevil()
|
||||
else
|
||||
throw EXCEPTION("Unable to find a blobstart landmark for hellish resurrection")
|
||||
CRASH("Unable to find a blobstart landmark for hellish resurrection")
|
||||
|
||||
|
||||
/datum/antagonist/devil/proc/update_hud()
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
if(BANE_HARVEST)
|
||||
if(istype(weapon, /obj/item/reagent_containers/food/snacks/grown/))
|
||||
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>")
|
||||
Knockdown(40)
|
||||
DefaultCombatKnockdown(40)
|
||||
qdel(weapon)
|
||||
return 2
|
||||
return 1
|
||||
@@ -70,16 +70,10 @@
|
||||
O.explanation_text = "Protect \the [M.current.real_name], the [M.assigned_role], from harm."
|
||||
objectives += O
|
||||
if(4) //flavor
|
||||
if(helping_station)
|
||||
var/datum/objective/flavor/ninja_helping/O = new /datum/objective/flavor/ninja_helping
|
||||
O.owner = owner
|
||||
O.forge_objective()
|
||||
objectives += O
|
||||
else
|
||||
var/datum/objective/flavor/ninja_syndie/O = new /datum/objective/flavor/ninja_helping
|
||||
O.owner = owner
|
||||
O.forge_objective()
|
||||
objectives += O
|
||||
var/datum/objective/flavor/O = helping_station ? new /datum/objective/flavor/ninja_helping : new /datum/objective/flavor/ninja_syndie
|
||||
O.owner = owner
|
||||
O.forge_objective()
|
||||
objectives += O
|
||||
else
|
||||
break
|
||||
var/datum/objective/O = new /datum/objective/survive()
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
var/interior = ""
|
||||
var/proper_bomb = TRUE //Please
|
||||
var/obj/effect/countdown/nuclearbomb/countdown
|
||||
var/nuclear_cooldown //used to stop global spam.
|
||||
|
||||
/obj/machinery/nuclearbomb/Initialize()
|
||||
. = ..()
|
||||
@@ -416,12 +417,16 @@
|
||||
return
|
||||
timing = !timing
|
||||
if(timing)
|
||||
if(nuclear_cooldown > world.time)
|
||||
to_chat(usr, "<span class='danger'>[src]'s timer protocols are currently on cooldown, please stand by.</span>")
|
||||
return
|
||||
previous_level = get_security_level()
|
||||
detonation_timer = world.time + (timer_set * 10)
|
||||
for(var/obj/item/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list)
|
||||
S.switch_mode_to(TRACK_INFILTRATOR)
|
||||
countdown.start()
|
||||
set_security_level("delta")
|
||||
nuclear_cooldown = world.time + 15 SECONDS
|
||||
|
||||
if(GLOB.war_declared)
|
||||
var/area/A = get_area(src)
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
else
|
||||
to_chat(user, "<span class='warning'>[user] fails to implant [M].</span>")
|
||||
|
||||
/obj/item/overthrow_converter/update_icon()
|
||||
/obj/item/overthrow_converter/update_icon_state()
|
||||
if(uses)
|
||||
icon_state = "implanter1"
|
||||
else
|
||||
|
||||
@@ -213,7 +213,7 @@
|
||||
continue
|
||||
L.Beam(M,icon_state="purple_lightning",time=5)
|
||||
if(!M.anti_magic_check(FALSE, TRUE))
|
||||
M.electrocute_act(shock_damage, L, safety=TRUE)
|
||||
M.electrocute_act(shock_damage, L, flags = SHOCK_NOGLOVES)
|
||||
do_sparks(4, FALSE, M)
|
||||
playsound(M, 'sound/machines/defib_zap.ogg', 50, 1, -1)
|
||||
|
||||
|
||||
@@ -76,8 +76,9 @@
|
||||
icon_state = "demon_heart-on"
|
||||
decay_factor = 0
|
||||
|
||||
/obj/item/organ/heart/demon/update_icon()
|
||||
return //always beating visually
|
||||
/obj/item/organ/heart/demon/ComponentInitialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/update_icon_blocker)
|
||||
|
||||
/obj/item/organ/heart/demon/attack(mob/M, mob/living/carbon/user, obj/target)
|
||||
if(M != user)
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
return 0
|
||||
|
||||
/obj/item/IntegrateAmount() //returns the amount of resources gained when eating this item
|
||||
if(custom_materials[getmaterialref(/datum/material/iron)] || custom_materials[getmaterialref(/datum/material/glass)])
|
||||
if(custom_materials[SSmaterials.GetMaterialRef(/datum/material/iron)] || custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)])
|
||||
return 1
|
||||
return ..()
|
||||
|
||||
@@ -492,8 +492,8 @@
|
||||
playsound(src,'sound/effects/sparks4.ogg',50,1)
|
||||
do_teleport(target, F, 0, channel = TELEPORT_CHANNEL_BLUESPACE)
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE)
|
||||
if(!tesla_shock)
|
||||
/mob/living/simple_animal/hostile/swarmer/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
|
||||
if(!(flags & SHOCK_TESLA))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
@@ -584,9 +584,9 @@
|
||||
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, 1)
|
||||
L.electrocute_act(0, src, 1, flags = SHOCK_NOGLOVES|SHOCK_ILLUSION)
|
||||
if(iscyborg(L))
|
||||
L.Knockdown(100)
|
||||
L.DefaultCombatKnockdown(100)
|
||||
qdel(src)
|
||||
..()
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
max_occurrences = 0
|
||||
earliest_start = 30 MINUTES
|
||||
min_players = 15
|
||||
gamemode_blacklist = list("dynamic")
|
||||
|
||||
|
||||
/datum/round_event/spawn_swarmer
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
var/should_give_codewords = TRUE
|
||||
var/should_equip = TRUE
|
||||
var/traitor_kind = TRAITOR_HUMAN //Set on initial assignment
|
||||
var/datum/contractor_hub/contractor_hub
|
||||
hijack_speed = 0.5 //10 seconds per hijack stage by default
|
||||
|
||||
/datum/antagonist/traitor/on_gain()
|
||||
@@ -413,6 +414,9 @@
|
||||
|
||||
var/special_role_text = lowertext(name)
|
||||
|
||||
if(contractor_hub)
|
||||
result += contractor_round_end()
|
||||
|
||||
if(traitorwin)
|
||||
result += "<span class='greentext'>The [special_role_text] was successful!</span>"
|
||||
else
|
||||
@@ -421,12 +425,44 @@
|
||||
|
||||
return result.Join("<br>")
|
||||
|
||||
/// Proc detailing contract kit buys/completed contracts/additional info
|
||||
/datum/antagonist/traitor/proc/contractor_round_end()
|
||||
var result = ""
|
||||
var total_spent_rep = 0
|
||||
|
||||
var/completed_contracts = 0
|
||||
var/tc_total = contractor_hub.contract_TC_payed_out + contractor_hub.contract_TC_to_redeem
|
||||
for(var/datum/syndicate_contract/contract in contractor_hub.assigned_contracts)
|
||||
if(contract.status == CONTRACT_STATUS_COMPLETE)
|
||||
completed_contracts++
|
||||
|
||||
var/contractor_item_icons = "" // Icons of purchases
|
||||
var/contractor_support_unit = "" // Set if they had a support unit - and shows appended to their contracts completed
|
||||
|
||||
for(var/datum/contractor_item/contractor_purchase in contractor_hub.purchased_items) // Get all the icons/total cost for all our items bought
|
||||
contractor_item_icons += "<span class='tooltip_container'>\[ <i class=\"fas [contractor_purchase.item_icon]\"></i><span class='tooltip_hover'><b>[contractor_purchase.name] - [contractor_purchase.cost] Rep</b><br><br>[contractor_purchase.desc]</span> \]</span>"
|
||||
total_spent_rep += contractor_purchase.cost
|
||||
if(istype(contractor_purchase, /datum/contractor_item/contractor_partner)) // Special case for reinforcements, we want to show their ckey and name on round end.
|
||||
var/datum/contractor_item/contractor_partner/partner = contractor_purchase
|
||||
contractor_support_unit += "<br><b>[partner.partner_mind.key]</b> played <b>[partner.partner_mind.current.name]</b>, their contractor support unit."
|
||||
if (contractor_hub.purchased_items.len)
|
||||
result += "<br>(used [total_spent_rep] Rep)"
|
||||
result += contractor_item_icons
|
||||
result += "<br>"
|
||||
if(completed_contracts > 0)
|
||||
var/pluralCheck = "contract"
|
||||
if(completed_contracts > 1)
|
||||
pluralCheck = "contracts"
|
||||
result += "Completed <span class='greentext'>[completed_contracts]</span> [pluralCheck] for a total of \
|
||||
<span class='greentext'>[tc_total] TC</span>!<br>"
|
||||
return result
|
||||
|
||||
/datum/antagonist/traitor/roundend_report_footer()
|
||||
var/phrases = jointext(GLOB.syndicate_code_phrase, ", ")
|
||||
var/responses = jointext(GLOB.syndicate_code_response, ", ")
|
||||
|
||||
var message = "<br><b>The code phrases were:</b> <span class='bluetext'>[phrases]</span><br>\
|
||||
<b>The code responses were:</b> <span class='redtext'>[responses]</span><br>"
|
||||
<b>The code responses were:</b> <span class='redtext'>[responses]</span><br>"
|
||||
|
||||
return message
|
||||
|
||||
|
||||
227
code/modules/antagonists/traitor/equipment/contractor.dm
Normal file
227
code/modules/antagonists/traitor/equipment/contractor.dm
Normal file
@@ -0,0 +1,227 @@
|
||||
// Support unit gets it's own very basic antag datum for admin logging.
|
||||
/datum/antagonist/traitor/contractor_support
|
||||
name = "Contractor Support Unit"
|
||||
antag_moodlet = /datum/mood_event/focused
|
||||
show_in_roundend = FALSE /// We're already adding them in to the contractor's roundend.
|
||||
give_objectives = TRUE /// We give them their own custom objective.
|
||||
show_in_antagpanel = FALSE /// Not a proper/full antag.
|
||||
should_equip = FALSE /// Don't give them an uplink.
|
||||
var/datum/team/contractor_team/contractor_team
|
||||
|
||||
/datum/team/contractor_team // Team for storing both the contractor and their support unit - only really for the HUD and admin logging.
|
||||
show_roundend_report = FALSE
|
||||
|
||||
/datum/antagonist/traitor/contractor_support/forge_traitor_objectives()
|
||||
var/datum/objective/generic_objective = new
|
||||
generic_objective.name = "Follow Contractor's Orders"
|
||||
generic_objective.explanation_text = "Follow your orders. Assist agents in this mission area."
|
||||
generic_objective.completed = TRUE
|
||||
add_objective(generic_objective)
|
||||
|
||||
/datum/contractor_hub
|
||||
var/contract_rep = 0
|
||||
var/list/hub_items = list()
|
||||
var/list/purchased_items = list()
|
||||
var/static/list/contractor_items = typecacheof(/datum/contractor_item/, TRUE)
|
||||
var/datum/syndicate_contract/current_contract
|
||||
var/list/datum/syndicate_contract/assigned_contracts = list()
|
||||
var/list/assigned_targets = list() // used as a blacklist to make sure we're not assigning targets already assigned
|
||||
var/contract_TC_payed_out = 0 // Keeping track for roundend reporting
|
||||
var/contract_TC_to_redeem = 0 // Used internally and roundend reporting - what TC we have available to cashout.
|
||||
|
||||
/datum/contractor_hub/proc/create_hub_items()
|
||||
for(var/path in contractor_items)
|
||||
var/datum/contractor_item/contractor_item = new path
|
||||
hub_items.Add(contractor_item)
|
||||
|
||||
/datum/contractor_hub/proc/create_contracts(datum/mind/owner) // 6 initial contracts
|
||||
var/list/to_generate = list(
|
||||
CONTRACT_PAYOUT_LARGE,
|
||||
CONTRACT_PAYOUT_MEDIUM,
|
||||
CONTRACT_PAYOUT_SMALL,
|
||||
CONTRACT_PAYOUT_SMALL,
|
||||
CONTRACT_PAYOUT_SMALL,
|
||||
CONTRACT_PAYOUT_SMALL
|
||||
)
|
||||
|
||||
var/lowest_TC_threshold = 30 // We don't want the sum of all the payouts to be under this amount
|
||||
var/total = 0
|
||||
var/lowest_paying_sum = 0
|
||||
var/datum/syndicate_contract/lowest_paying_contract
|
||||
|
||||
to_generate = shuffle(to_generate) // Randomise order, so we don't have contracts always in payout order.
|
||||
var/start_index = 1 // Support contract generation happening multiple times
|
||||
if(assigned_contracts.len != 0)
|
||||
start_index = assigned_contracts.len + 1
|
||||
|
||||
for(var/i = 1; i <= to_generate.len; i++) // Generate contracts, and find the lowest paying.
|
||||
var/datum/syndicate_contract/contract_to_add = new(owner, assigned_targets, to_generate[i])
|
||||
var/contract_payout_total = contract_to_add.contract.payout + contract_to_add.contract.payout_bonus
|
||||
assigned_targets.Add(contract_to_add.contract.target)
|
||||
if(!lowest_paying_contract || (contract_payout_total < lowest_paying_sum))
|
||||
lowest_paying_sum = contract_payout_total
|
||||
lowest_paying_contract = contract_to_add
|
||||
total += contract_payout_total
|
||||
contract_to_add.id = start_index
|
||||
assigned_contracts.Add(contract_to_add)
|
||||
start_index++
|
||||
if(total < lowest_TC_threshold) // If the threshold for TC payouts isn't reached, boost the lowest paying contract
|
||||
lowest_paying_contract.contract.payout_bonus += (lowest_TC_threshold - total)
|
||||
|
||||
/datum/contractor_item
|
||||
var/name // Name of item
|
||||
var/desc // description of item
|
||||
var/item // item path, no item path means the purchase needs it's own handle_purchase()
|
||||
var/item_icon = "fa-broadcast-tower" // fontawesome icon to use inside the hub - https://fontawesome.com/icons/
|
||||
var/limited = -1 // Any number above 0 for how many times it can be bought in a round for a single traitor. -1 is unlimited.
|
||||
var/cost // Cost of the item in contract rep.
|
||||
|
||||
/datum/contractor_item/contract_reroll
|
||||
name = "Contract Reroll"
|
||||
desc = "Request a reroll of your current contract list. Will generate a new target, payment, and dropoff for the contracts you currently have available."
|
||||
item_icon = "fa-dice"
|
||||
limited = 2
|
||||
cost = 0
|
||||
|
||||
/datum/contractor_item/contract_reroll/handle_purchase(var/datum/contractor_hub/hub)
|
||||
. = ..()
|
||||
if (.)
|
||||
var/list/new_target_list = list() // We're not regenerating already completed/aborted/extracting contracts, but we don't want to repeat their targets.
|
||||
for(var/datum/syndicate_contract/contract_check in hub.assigned_contracts)
|
||||
if (contract_check.status != CONTRACT_STATUS_ACTIVE && contract_check.status != CONTRACT_STATUS_INACTIVE)
|
||||
if (contract_check.contract.target)
|
||||
new_target_list.Add(contract_check.contract.target)
|
||||
continue
|
||||
for(var/datum/syndicate_contract/rerolling_contract in hub.assigned_contracts) // Reroll contracts without duplicates
|
||||
if (rerolling_contract.status != CONTRACT_STATUS_ACTIVE && rerolling_contract.status != CONTRACT_STATUS_INACTIVE)
|
||||
continue
|
||||
rerolling_contract.generate(new_target_list)
|
||||
new_target_list.Add(rerolling_contract.contract.target)
|
||||
hub.assigned_targets = new_target_list // Set our target list with the new set we've generated.
|
||||
|
||||
/datum/contractor_item/contractor_pinpointer
|
||||
name = "Contractor Pinpointer"
|
||||
desc = "A pinpointer that finds targets even without active suit sensors. Due to taking advantage of an exploit within the system, it can't pinpoint to the same accuracy as the traditional models. Becomes permanently locked to the user that first activates it."
|
||||
item = /obj/item/pinpointer/crew/contractor
|
||||
item_icon = "fa-search-location"
|
||||
limited = 2
|
||||
cost = 1
|
||||
|
||||
/datum/contractor_item/fulton_extraction_kit
|
||||
name = "Fulton Extraction Kit"
|
||||
desc = "For getting your target across the station to those difficult dropoffs. Place the beacon somewhere secure, and link the pack. Activating the pack on your target in space will send them over to the beacon - make sure they're not just going to run away though!"
|
||||
item = /obj/item/storage/box/contractor/fulton_extraction
|
||||
item_icon = "fa-parachute-box"
|
||||
limited = 1
|
||||
cost = 1
|
||||
|
||||
/datum/contractor_item/contractor_partner
|
||||
name = "Reinforcements"
|
||||
desc = "Upon purchase we'll contact available units in the area. Should there be an agent free, we'll send them down to assist you immediately. If no units are free, we give a full refund."
|
||||
item_icon = "fa-user-friends"
|
||||
limited = 1
|
||||
cost = 2
|
||||
var/datum/mind/partner_mind = null
|
||||
|
||||
/datum/contractor_item/contractor_partner/handle_purchase(var/datum/contractor_hub/hub, mob/living/user)
|
||||
. = ..()
|
||||
if (.)
|
||||
to_chat(user, "<span class='notice'>The uplink vibrates quietly, connecting to nearby agents...</span>")
|
||||
var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Contractor Support Unit for [user.real_name]?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_CONTRACTOR_SUPPORT)
|
||||
if(LAZYLEN(candidates))
|
||||
var/mob/dead/observer/C = pick(candidates)
|
||||
spawn_contractor_partner(user, C.key)
|
||||
else
|
||||
to_chat(user, "<span class='notice'>No available agents at this time, please try again later.</span>")
|
||||
limited += 1 // refund and add the limit back.
|
||||
hub.contract_rep += cost
|
||||
hub.purchased_items -= src
|
||||
|
||||
/datum/outfit/contractor_partner
|
||||
name = "Contractor Support Unit"
|
||||
uniform = /obj/item/clothing/under/chameleon
|
||||
suit = /obj/item/clothing/suit/chameleon
|
||||
back = /obj/item/storage/backpack
|
||||
belt = /obj/item/pda/chameleon
|
||||
mask = /obj/item/clothing/mask/cigarette/syndicate
|
||||
shoes = /obj/item/clothing/shoes/chameleon/noslip
|
||||
ears = /obj/item/radio/headset/chameleon
|
||||
id = /obj/item/card/id/syndicate
|
||||
r_hand = /obj/item/storage/toolbox/syndicate
|
||||
backpack_contents = list(/obj/item/storage/box/survival, /obj/item/implanter/uplink, /obj/item/clothing/mask/chameleon,
|
||||
/obj/item/storage/fancy/cigarettes/cigpack_syndicate, /obj/item/lighter)
|
||||
|
||||
/datum/outfit/contractor_partner/post_equip(mob/living/carbon/human/H, visualsOnly)
|
||||
. = ..()
|
||||
var/obj/item/clothing/mask/cigarette/syndicate/cig = H.get_item_by_slot(SLOT_WEAR_MASK)
|
||||
cig.light() // pre-light their cig for extra badass
|
||||
|
||||
/datum/contractor_item/contractor_partner/proc/spawn_contractor_partner(mob/living/user, key)
|
||||
var/mob/living/carbon/human/partner = new()
|
||||
var/datum/outfit/contractor_partner/partner_outfit = new()
|
||||
partner_outfit.equip(partner)
|
||||
var/obj/structure/closet/supplypod/arrival_pod = new()
|
||||
arrival_pod.style = STYLE_SYNDICATE
|
||||
arrival_pod.explosionSize = list(0,0,0,1)
|
||||
arrival_pod.bluespace = TRUE
|
||||
var/turf/free_location = find_obstruction_free_location(2, user)
|
||||
if (!free_location) // We really want to send them - if we can't find a nice location just land it on top of them.
|
||||
free_location = get_turf(user)
|
||||
partner.forceMove(arrival_pod)
|
||||
partner.ckey = key
|
||||
partner_mind = partner.mind // We give a reference to the mind that'll be the support unit
|
||||
partner_mind.make_Contractor_Support()
|
||||
to_chat(partner_mind.current, "\n<span class='alertwarning'>[user.real_name] is your superior. Follow any, and all orders given by them. You're here to support their mission only.</span>")
|
||||
to_chat(partner_mind.current, "<span class='alertwarning'>Should they perish, or be otherwise unavailable, you're to assist other active agents in this mission area to the best of your ability.</span>\n\n")
|
||||
new /obj/effect/abstract/DPtarget(free_location, arrival_pod)
|
||||
|
||||
/datum/contractor_item/blackout
|
||||
name = "Blackout"
|
||||
desc = "Request Syndicate Command to distrupt the station's powernet. Disables power across the station for a short duration."
|
||||
item_icon = "fa-bolt"
|
||||
limited = 2
|
||||
cost = 3
|
||||
|
||||
/datum/contractor_item/blackout/handle_purchase(var/datum/contractor_hub/hub)
|
||||
. = ..()
|
||||
if (.)
|
||||
power_fail(35, 50)
|
||||
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", "poweroff")
|
||||
|
||||
// Subtract cost, and spawn if it's an item.
|
||||
/datum/contractor_item/proc/handle_purchase(var/datum/contractor_hub/hub, mob/living/user)
|
||||
if (hub.contract_rep >= cost)
|
||||
hub.contract_rep -= cost
|
||||
else
|
||||
return FALSE
|
||||
if (limited >= 1)
|
||||
limited -= 1
|
||||
else if (limited == 0)
|
||||
return FALSE
|
||||
hub.purchased_items.Add(src)
|
||||
if (item && ispath(item))
|
||||
var/atom/item_to_create = new item(get_turf(user))
|
||||
|
||||
if(user.put_in_hands(item_to_create))
|
||||
to_chat(user, "<span class='notice'>Your purchase materializes into your hands!</span>")
|
||||
else
|
||||
to_chat(user, "<span class='notice'>Your purchase materializes onto the floor.</span>")
|
||||
return item_to_create
|
||||
return TRUE
|
||||
|
||||
/obj/item/pinpointer/crew/contractor
|
||||
name = "contractor pinpointer"
|
||||
desc = "A handheld tracking device that locks onto certain signals. Ignores suit sensors, but is much less accurate."
|
||||
icon_state = "pinpointer_syndicate"
|
||||
minimum_range = 25
|
||||
has_owner = TRUE
|
||||
ignore_suit_sensor_level = TRUE
|
||||
|
||||
/obj/item/storage/box/contractor/fulton_extraction
|
||||
name = "Fulton Extraction Kit"
|
||||
icon_state = "syndiebox"
|
||||
illustration = "writing_syndie"
|
||||
|
||||
/obj/item/storage/box/contractor/fulton_extraction/PopulateContents()
|
||||
new /obj/item/extraction_pack(src)
|
||||
new /obj/item/fulton_core(src)
|
||||
150
code/modules/antagonists/traitor/syndicate_contract.dm
Normal file
150
code/modules/antagonists/traitor/syndicate_contract.dm
Normal file
@@ -0,0 +1,150 @@
|
||||
/datum/syndicate_contract
|
||||
var/id = 0
|
||||
var/status = CONTRACT_STATUS_INACTIVE
|
||||
var/datum/objective/contract/contract = new()
|
||||
var/target_rank
|
||||
var/ransom = 0
|
||||
var/payout_type = null
|
||||
var/list/victim_belongings = list()
|
||||
|
||||
/datum/syndicate_contract/New(contract_owner, blacklist, type=CONTRACT_PAYOUT_SMALL)
|
||||
contract.owner = contract_owner
|
||||
payout_type = type
|
||||
generate(blacklist)
|
||||
|
||||
/datum/syndicate_contract/proc/generate(blacklist)
|
||||
contract.find_target(null, blacklist)
|
||||
var/datum/data/record/record = find_record("name", contract.target.name, GLOB.data_core.general)
|
||||
if(record)
|
||||
target_rank = record.fields["rank"]
|
||||
else
|
||||
target_rank = "Unknown"
|
||||
if (payout_type == CONTRACT_PAYOUT_LARGE)
|
||||
contract.payout_bonus = rand(9,13)
|
||||
else if(payout_type == CONTRACT_PAYOUT_MEDIUM)
|
||||
contract.payout_bonus = rand(6,8)
|
||||
else
|
||||
contract.payout_bonus = rand(2,4)
|
||||
contract.payout = rand(0, 2)
|
||||
contract.generate_dropoff()
|
||||
ransom = 100 * rand(18, 45)
|
||||
|
||||
/datum/syndicate_contract/proc/handle_extraction(var/mob/living/user)
|
||||
if (contract.target && contract.dropoff_check(user, contract.target.current))
|
||||
var/turf/free_location = find_obstruction_free_location(3, user, contract.dropoff)
|
||||
if(free_location) // We've got a valid location, launch.
|
||||
launch_extraction_pod(free_location)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
// Launch the pod to collect our victim.
|
||||
/datum/syndicate_contract/proc/launch_extraction_pod(turf/empty_pod_turf)
|
||||
var/obj/structure/closet/supplypod/extractionpod/empty_pod = new()
|
||||
RegisterSignal(empty_pod, COMSIG_ATOM_ENTERED, .proc/enter_check)
|
||||
empty_pod.stay_after_drop = TRUE
|
||||
empty_pod.reversing = TRUE
|
||||
empty_pod.explosionSize = list(0,0,0,1)
|
||||
empty_pod.leavingSound = 'sound/effects/podwoosh.ogg'
|
||||
new /obj/effect/abstract/DPtarget(empty_pod_turf, empty_pod)
|
||||
|
||||
/datum/syndicate_contract/proc/enter_check(datum/source, sent_mob)
|
||||
if(istype(source, /obj/structure/closet/supplypod/extractionpod))
|
||||
if(isliving(sent_mob))
|
||||
var/mob/living/M = sent_mob
|
||||
var/datum/antagonist/traitor/traitor_data = contract.owner.has_antag_datum(/datum/antagonist/traitor)
|
||||
if(M == contract.target.current)
|
||||
traitor_data.contractor_hub.contract_TC_to_redeem += contract.payout
|
||||
if(M.stat != DEAD)
|
||||
traitor_data.contractor_hub.contract_TC_to_redeem += contract.payout_bonus
|
||||
status = CONTRACT_STATUS_COMPLETE
|
||||
if(traitor_data.contractor_hub.current_contract == src)
|
||||
traitor_data.contractor_hub.current_contract = null
|
||||
traitor_data.contractor_hub.contract_rep += 2
|
||||
else
|
||||
status = CONTRACT_STATUS_ABORTED // Sending a target that wasn't even yours is as good as just aborting it
|
||||
if(traitor_data.contractor_hub.current_contract == src)
|
||||
traitor_data.contractor_hub.current_contract = null
|
||||
if(iscarbon(M))
|
||||
for(var/obj/item/W in M)
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
if(W == H.w_uniform || W == H.shoes)
|
||||
continue //So all they're left with are shoes and uniform.
|
||||
M.transferItemToLoc(W)
|
||||
victim_belongings.Add(W)
|
||||
var/obj/structure/closet/supplypod/extractionpod/pod = source
|
||||
pod.send_up(pod) // Handle the pod returning
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/target = M // After we remove items, at least give them what they need to live.
|
||||
target.dna.species.give_important_for_life(target)
|
||||
handleVictimExperience(M) // After pod is sent we start the victim narrative/heal.
|
||||
var/points_to_check = SSshuttle.points // This is slightly delayed because of the sleep calls above to handle the narrative. We don't want to tell the station instantly.
|
||||
if(points_to_check >= ransom)
|
||||
SSshuttle.points -= ransom
|
||||
else
|
||||
SSshuttle.points -= points_to_check
|
||||
priority_announce("One of your crew was captured by a rival organisation - we've needed to pay their ransom to bring them back. \
|
||||
As is policy we've taken a portion of the station's funds to offset the overall cost.", null, "attention", null, "Nanotrasen Asset Protection")
|
||||
|
||||
/datum/syndicate_contract/proc/handleVictimExperience(var/mob/living/M) // They're off to holding - handle the return timer and give some text about what's going on.
|
||||
addtimer(CALLBACK(src, .proc/returnVictim, M), (60 * 10) * 4) // Ship 'em back - dead or alive... 4 minutes wait.
|
||||
if(M.stat != DEAD) //Even if they weren't the target, we're still treating them the same.
|
||||
M.reagents.add_reagent(/datum/reagent/medicine/omnizine, 20) // Heal them up - gets them out of crit/soft crit.
|
||||
M.flash_act()
|
||||
M.confused += 10
|
||||
M.blur_eyes(5)
|
||||
to_chat(M, "<span class='warning'>You feel strange...</span>")
|
||||
sleep(60)
|
||||
to_chat(M, "<span class='warning'>That pod did something to you...</span>")
|
||||
M.Dizzy(35)
|
||||
sleep(65)
|
||||
to_chat(M, "<span class='warning'>Your head pounds... It feels like it's going to burst out your skull!</span>")
|
||||
M.flash_act()
|
||||
M.confused += 20
|
||||
M.blur_eyes(3)
|
||||
sleep(30)
|
||||
to_chat(M, "<span class='warning'>Your head pounds...</span>")
|
||||
sleep(100)
|
||||
M.flash_act()
|
||||
M.Unconscious(200)
|
||||
to_chat(M, "<span class='reallybig hypnophrase'>A million voices echo in your head... <i>\"Your mind held many valuable secrets - \
|
||||
we thank you for providing them. Your value is expended, and you will be ransomed back to your station. We always get paid, \
|
||||
so it's only a matter of time before we ship you back...\"</i></span>")
|
||||
M.blur_eyes(10)
|
||||
M.Dizzy(15)
|
||||
M.confused += 20
|
||||
|
||||
/datum/syndicate_contract/proc/returnVictim(var/mob/living/M) // We're returning the victim
|
||||
var/list/possible_drop_loc = list()
|
||||
for(var/turf/possible_drop in contract.dropoff.contents)
|
||||
if(!is_blocked_turf(possible_drop))
|
||||
possible_drop_loc.Add(possible_drop)
|
||||
if(possible_drop_loc.len > 0)
|
||||
var/pod_rand_loc = rand(1, possible_drop_loc.len)
|
||||
var/obj/structure/closet/supplypod/return_pod = new()
|
||||
return_pod.bluespace = TRUE
|
||||
return_pod.explosionSize = list(0,0,0,0)
|
||||
return_pod.style = STYLE_SYNDICATE
|
||||
do_sparks(8, FALSE, M)
|
||||
M.visible_message("<span class='notice'>[M] vanishes...</span>")
|
||||
for(var/obj/item/W in M)
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
if(W == H.w_uniform || W == H.shoes)
|
||||
continue //So all they're left with are shoes and uniform.
|
||||
M.dropItemToGround(W)
|
||||
for(var/obj/item/W in victim_belongings)
|
||||
W.forceMove(return_pod)
|
||||
M.forceMove(return_pod)
|
||||
M.flash_act()
|
||||
M.blur_eyes(30)
|
||||
M.Dizzy(35)
|
||||
M.confused += 20
|
||||
new /obj/effect/abstract/DPtarget(possible_drop_loc[pod_rand_loc], return_pod)
|
||||
else
|
||||
to_chat(M, "<span class='reallybig hypnophrase'>A million voices echo in your head... <i>\"Seems where you got sent here from won't \
|
||||
be able to handle our pod... You will die here instead.\"</i></span>")
|
||||
if(iscarbon(M))
|
||||
var/mob/living/carbon/C = M
|
||||
if(C.can_heartattack())
|
||||
C.set_heartattack(TRUE)
|
||||
@@ -24,5 +24,5 @@
|
||||
return
|
||||
H.dna.add_mutation(HULK)
|
||||
H.dna.add_mutation(XRAY)
|
||||
H.dna.add_mutation(COLDRES)
|
||||
H.dna.add_mutation(SPACEMUT)
|
||||
H.dna.add_mutation(TK)
|
||||
@@ -261,7 +261,7 @@
|
||||
GiveHint(target)
|
||||
else if(is_pointed(I))
|
||||
to_chat(target, "<span class='userdanger'>You feel a stabbing pain in [parse_zone(user.zone_selected)]!</span>")
|
||||
target.Knockdown(40)
|
||||
target.DefaultCombatKnockdown(40)
|
||||
GiveHint(target)
|
||||
else if(istype(I, /obj/item/bikehorn))
|
||||
to_chat(target, "<span class='userdanger'>HONK</span>")
|
||||
@@ -377,7 +377,10 @@
|
||||
/obj/item/warpwhistle/proc/end_effect(mob/living/carbon/user)
|
||||
user.invisibility = initial(user.invisibility)
|
||||
user.status_flags &= ~GODMODE
|
||||
user.canmove = TRUE
|
||||
REMOVE_TRAIT(user, TRAIT_MOBILITY_NOMOVE, src)
|
||||
REMOVE_TRAIT(user, TRAIT_MOBILITY_NOUSE, src)
|
||||
REMOVE_TRAIT(user, TRAIT_MOBILITY_NOPICKUP, src)
|
||||
user.update_mobility()
|
||||
|
||||
/obj/item/warpwhistle/attack_self(mob/living/carbon/user)
|
||||
if(!istype(user) || on_cooldown)
|
||||
@@ -390,7 +393,10 @@
|
||||
on_cooldown = TRUE
|
||||
last_user = user
|
||||
playsound(T,'sound/magic/warpwhistle.ogg', 200, 1)
|
||||
user.canmove = FALSE
|
||||
ADD_TRAIT(user, TRAIT_MOBILITY_NOMOVE, src)
|
||||
ADD_TRAIT(user, TRAIT_MOBILITY_NOUSE, src)
|
||||
ADD_TRAIT(user, TRAIT_MOBILITY_NOPICKUP, src)
|
||||
user.update_mobility()
|
||||
new /obj/effect/temp_visual/tornado(T)
|
||||
sleep(20)
|
||||
if(interrupted(user))
|
||||
@@ -412,7 +418,6 @@
|
||||
return
|
||||
if(T.z != potential_T.z || abs(get_dist_euclidian(potential_T,T)) > 50 - breakout)
|
||||
do_teleport(user, potential_T, channel = TELEPORT_CHANNEL_MAGIC)
|
||||
user.canmove = 0
|
||||
T = potential_T
|
||||
break
|
||||
breakout += 1
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
/obj/item/soulstone/proc/release_shades(mob/user)
|
||||
for(var/mob/living/simple_animal/shade/A in src)
|
||||
A.status_flags &= ~GODMODE
|
||||
A.canmove = TRUE
|
||||
A.forceMove(get_turf(user))
|
||||
A.mobility_flags = MOBILITY_FLAGS_DEFAULT
|
||||
A.cancel_camera()
|
||||
icon_state = "soulstone"
|
||||
name = initial(name)
|
||||
@@ -173,7 +173,7 @@
|
||||
else
|
||||
T.forceMove(src) //put shade in stone
|
||||
T.status_flags |= GODMODE
|
||||
T.canmove = FALSE
|
||||
T.mobility_flags = NONE
|
||||
T.health = T.maxHealth
|
||||
icon_state = "soulstone2"
|
||||
name = "soulstone: Shade of [T.real_name]"
|
||||
@@ -240,8 +240,8 @@
|
||||
T.dust_animation()
|
||||
QDEL_IN(T, 5)
|
||||
var/mob/living/simple_animal/shade/S = new /mob/living/simple_animal/shade(src)
|
||||
S.status_flags |= GODMODE //So they won't die inside the stone somehow
|
||||
S.canmove = FALSE//Can't move out of the soul stone
|
||||
S.status_flags |= GODMODE //So they won't die inside the stone somehow
|
||||
S.mobility_flags = NONE //Can't move out of the soul stone
|
||||
S.name = "Shade of [T.real_name]"
|
||||
S.real_name = "Shade of [T.real_name]"
|
||||
T.transfer_ckey(S)
|
||||
|
||||
@@ -19,15 +19,17 @@
|
||||
/obj/item/onetankbomb/examine(mob/user)
|
||||
bombtank.examine(user)
|
||||
|
||||
/obj/item/onetankbomb/update_icon()
|
||||
cut_overlays()
|
||||
/obj/item/onetankbomb/update_icon_state()
|
||||
if(bombtank)
|
||||
icon = bombtank.icon
|
||||
icon_state = bombtank.icon_state
|
||||
|
||||
/obj/item/onetankbomb/update_overlays()
|
||||
. = ..()
|
||||
if(bombassembly)
|
||||
add_overlay(bombassembly.icon_state)
|
||||
copy_overlays(bombassembly)
|
||||
add_overlay("bomb_assembly")
|
||||
. += bombassembly.icon_state
|
||||
. += bombassembly.overlays
|
||||
. += "bomb_assembly"
|
||||
|
||||
/obj/item/onetankbomb/wrench_act(mob/living/user, obj/item/I)
|
||||
to_chat(user, "<span class='notice'>You disassemble [src]!</span>")
|
||||
@@ -101,10 +103,10 @@
|
||||
bombassembly.setDir(dir)
|
||||
bombassembly.Move()
|
||||
|
||||
/obj/item/onetankbomb/dropped()
|
||||
/obj/item/onetankbomb/dropped(mob/user)
|
||||
. = ..()
|
||||
if(bombassembly)
|
||||
bombassembly.dropped()
|
||||
bombassembly.dropped(user)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
var/mob/living/silicon/robot/R = M
|
||||
log_combat(user, R, "flashed", src)
|
||||
update_icon(1)
|
||||
R.Knockdown(rand(80,120))
|
||||
R.DefaultCombatKnockdown(rand(80,120))
|
||||
var/diff = 5 * CONFUSION_STACK_MAX_MULTIPLIER - M.confused
|
||||
R.confused += min(5, diff)
|
||||
R.flash_act(affect_silicon = 1)
|
||||
@@ -197,14 +197,13 @@
|
||||
else
|
||||
to_chat(user, "<span class='warning'>This mind seems resistant to the flash!</span>")
|
||||
|
||||
|
||||
/obj/item/assembly/flash/cyborg
|
||||
|
||||
/obj/item/assembly/flash/cyborg/attack(mob/living/M, mob/user)
|
||||
. = ..()
|
||||
new /obj/effect/temp_visual/borgflash(get_turf(src))
|
||||
if(. && !CONFIG_GET(flag/disable_borg_flash_knockdown) && iscarbon(M) && !M.resting && !M.get_eye_protection())
|
||||
M.Knockdown(80)
|
||||
if(. && !CONFIG_GET(flag/disable_borg_flash_knockdown) && iscarbon(M) && CHECK_MOBILITY(M, MOBILITY_STAND) && !M.get_eye_protection())
|
||||
M.DefaultCombatKnockdown(80)
|
||||
|
||||
/obj/item/assembly/flash/cyborg/attack_self(mob/user)
|
||||
..()
|
||||
|
||||
@@ -83,9 +83,9 @@
|
||||
/obj/item/assembly_holder/dropped(mob/user)
|
||||
. = ..()
|
||||
if(a_left)
|
||||
a_left.dropped()
|
||||
a_left.dropped(user)
|
||||
if(a_right)
|
||||
a_right.dropped()
|
||||
a_right.dropped(user)
|
||||
|
||||
/obj/item/assembly_holder/attack_hand()//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess
|
||||
. = ..()
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
holder.update_icon()
|
||||
return
|
||||
|
||||
/obj/item/assembly/infra/dropped()
|
||||
/obj/item/assembly/infra/dropped(mob/user)
|
||||
. = ..()
|
||||
if(holder)
|
||||
holder_movement() //sync the dir of the device as well if it's contained in a TTV or an assembly holder
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
if("feet")
|
||||
if(!H.shoes || !(H.shoes.body_parts_covered & FEET))
|
||||
affecting = H.get_bodypart(pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
|
||||
H.Knockdown(60)
|
||||
H.DefaultCombatKnockdown(60)
|
||||
if(BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND)
|
||||
if(!H.gloves)
|
||||
affecting = H.get_bodypart(type)
|
||||
|
||||
@@ -624,10 +624,7 @@
|
||||
"set_internal_pressure" = 0
|
||||
))
|
||||
|
||||
/obj/machinery/airalarm/update_icon()
|
||||
set_light(0)
|
||||
cut_overlays()
|
||||
SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
|
||||
/obj/machinery/airalarm/update_icon_state()
|
||||
if(stat & NOPOWER)
|
||||
icon_state = "alarm0"
|
||||
return
|
||||
@@ -636,35 +633,39 @@
|
||||
icon_state = "alarmx"
|
||||
return
|
||||
|
||||
if(panel_open)
|
||||
switch(buildstage)
|
||||
if(2)
|
||||
icon_state = "alarmx"
|
||||
if(1)
|
||||
icon_state = "alarm_b2"
|
||||
if(0)
|
||||
icon_state = "alarm_b1"
|
||||
if(!panel_open)
|
||||
icon_state = "alarm1"
|
||||
return
|
||||
|
||||
icon_state = "alarm1"
|
||||
switch(buildstage)
|
||||
if(2)
|
||||
icon_state = "alarmx"
|
||||
if(1)
|
||||
icon_state = "alarm_b2"
|
||||
if(0)
|
||||
icon_state = "alarm_b1"
|
||||
|
||||
/obj/machinery/airalarm/update_overlays()
|
||||
. = ..()
|
||||
SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
|
||||
var/overlay_state = AALARM_OVERLAY_OFF
|
||||
var/area/A = get_base_area(src)
|
||||
switch(max(danger_level, A.atmosalm))
|
||||
if(0)
|
||||
add_overlay(AALARM_OVERLAY_GREEN)
|
||||
overlay_state = AALARM_OVERLAY_GREEN
|
||||
light_color = LIGHT_COLOR_GREEN
|
||||
set_light(brightness_on)
|
||||
if(1)
|
||||
add_overlay(AALARM_OVERLAY_WARN)
|
||||
overlay_state = AALARM_OVERLAY_WARN
|
||||
light_color = LIGHT_COLOR_LAVA
|
||||
set_light(brightness_on)
|
||||
if(2)
|
||||
add_overlay(AALARM_OVERLAY_DANGER)
|
||||
overlay_state = AALARM_OVERLAY_DANGER
|
||||
light_color = LIGHT_COLOR_RED
|
||||
set_light(brightness_on)
|
||||
|
||||
if(overlay_state != AALARM_OVERLAY_OFF)
|
||||
. += overlay_state
|
||||
set_light(brightness_on)
|
||||
else
|
||||
set_light(0)
|
||||
|
||||
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
|
||||
update_light()
|
||||
|
||||
@@ -314,10 +314,6 @@
|
||||
user.forceMove(loc)
|
||||
user.visible_message("<span class='notice'>You hear something squeezing through the ducts...</span>", "<span class='notice'>You climb out the ventilation system.")
|
||||
|
||||
user.canmove = FALSE
|
||||
addtimer(VARSET_CALLBACK(user, canmove, TRUE), 1)
|
||||
|
||||
|
||||
/obj/machinery/atmospherics/AltClick(mob/living/L)
|
||||
if(is_type_in_typecache(src, GLOB.ventcrawl_machinery))
|
||||
return L.handle_ventcrawl(src)
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
for(var/i in 1 to device_type)
|
||||
var/datum/pipeline/parent = parents[i]
|
||||
if(!parent)
|
||||
throw EXCEPTION("Component is missing a pipenet! Rebuilding...")
|
||||
stack_trace("Component is missing a pipenet! Rebuilding...")
|
||||
build_network()
|
||||
parent.update = 1
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@
|
||||
M.forceMove(get_turf(src))
|
||||
if(isliving(M))
|
||||
var/mob/living/L = M
|
||||
L.update_canmove()
|
||||
L.update_mobility()
|
||||
occupant = null
|
||||
update_icon()
|
||||
|
||||
@@ -277,10 +277,10 @@
|
||||
else
|
||||
. += "[src] seems empty."
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/MouseDrop_T(mob/target, mob/user)
|
||||
if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser())
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/MouseDrop_T(mob/living/carbon/target, mob/user)
|
||||
if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !istype(target) || !user.IsAdvancedToolUser())
|
||||
return
|
||||
if (target.IsKnockdown() || target.IsStun() || target.IsSleeping() || target.IsUnconscious())
|
||||
if(!CHECK_MOBILITY(target, MOBILITY_MOVE))
|
||||
close_machine(target)
|
||||
else
|
||||
user.visible_message("<b>[user]</b> starts shoving [target] inside [src].", "<span class='notice'>You start shoving [target] inside [src].</span>")
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
var/restricted = FALSE
|
||||
req_access = list()
|
||||
|
||||
var/update = 0
|
||||
var/static/list/label2types = list(
|
||||
"n2" = /obj/machinery/portable_atmospherics/canister/nitrogen,
|
||||
"o2" = /obj/machinery/portable_atmospherics/canister/oxygen,
|
||||
@@ -213,61 +212,26 @@
|
||||
air_contents.gases[/datum/gas/oxygen] = (O2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
|
||||
air_contents.gases[/datum/gas/nitrogen] = (N2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
|
||||
|
||||
#define HOLDING (1<<0)
|
||||
#define CONNECTED (1<<1)
|
||||
#define EMPTY (1<<2)
|
||||
#define LOW (1<<3)
|
||||
#define MEDIUM (1<<4)
|
||||
#define FULL (1<<5)
|
||||
#define DANGER (1<<6)
|
||||
/obj/machinery/portable_atmospherics/canister/update_icon()
|
||||
/obj/machinery/portable_atmospherics/canister/update_icon_state()
|
||||
if(stat & BROKEN)
|
||||
cut_overlays()
|
||||
icon_state = "[icon_state]-1"
|
||||
return
|
||||
|
||||
var/last_update = update
|
||||
update = 0
|
||||
/obj/machinery/portable_atmospherics/canister/update_overlays()
|
||||
. = ..()
|
||||
|
||||
if(holding)
|
||||
update |= HOLDING
|
||||
. += "can-open"
|
||||
if(connected_port)
|
||||
update |= CONNECTED
|
||||
. += "can-connector"
|
||||
var/pressure = air_contents.return_pressure()
|
||||
if(pressure < 10)
|
||||
update |= EMPTY
|
||||
else if(pressure < 5 * ONE_ATMOSPHERE)
|
||||
update |= LOW
|
||||
else if(pressure < 10 * ONE_ATMOSPHERE)
|
||||
update |= MEDIUM
|
||||
else if(pressure < 40 * ONE_ATMOSPHERE)
|
||||
update |= FULL
|
||||
else
|
||||
update |= DANGER
|
||||
|
||||
if(update == last_update)
|
||||
return
|
||||
|
||||
cut_overlays()
|
||||
if(update & HOLDING)
|
||||
add_overlay("can-open")
|
||||
if(update & CONNECTED)
|
||||
add_overlay("can-connector")
|
||||
if(update & LOW)
|
||||
add_overlay("can-o0")
|
||||
else if(update & MEDIUM)
|
||||
add_overlay("can-o1")
|
||||
else if(update & FULL)
|
||||
add_overlay("can-o2")
|
||||
else if(update & DANGER)
|
||||
add_overlay("can-o3")
|
||||
#undef HOLDING
|
||||
#undef CONNECTED
|
||||
#undef EMPTY
|
||||
#undef LOW
|
||||
#undef MEDIUM
|
||||
#undef FULL
|
||||
#undef DANGER
|
||||
if(pressure >= 40 * ONE_ATMOSPHERE)
|
||||
. += "can-o3"
|
||||
else if(pressure >= 10 * ONE_ATMOSPHERE)
|
||||
. += "can-o2"
|
||||
else if(pressure >= 5 * ONE_ATMOSPHERE)
|
||||
. += "can-o1"
|
||||
else if(pressure >= 10)
|
||||
. += "can-o0"
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
||||
if(exposed_temperature > temperature_resistance)
|
||||
|
||||
@@ -29,14 +29,16 @@
|
||||
QDEL_NULL(pump)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/portable_atmospherics/pump/update_icon()
|
||||
/obj/machinery/portable_atmospherics/pump/update_icon_state()
|
||||
icon_state = "psiphon:[on]"
|
||||
|
||||
cut_overlays()
|
||||
|
||||
/obj/machinery/portable_atmospherics/pump/update_overlays()
|
||||
. = ..()
|
||||
if(holding)
|
||||
add_overlay("siphon-open")
|
||||
. += "siphon-open"
|
||||
if(connected_port)
|
||||
add_overlay("siphon-connector")
|
||||
. += "siphon-connector"
|
||||
|
||||
/obj/machinery/portable_atmospherics/pump/process_atmos()
|
||||
..()
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
var/on = FALSE
|
||||
var/volume_rate = 1000
|
||||
var/use_overlays = TRUE
|
||||
volume = 1000
|
||||
|
||||
var/list/scrubbing = list(/datum/gas/plasma, /datum/gas/carbon_dioxide, /datum/gas/nitrous_oxide, /datum/gas/bz, /datum/gas/nitryl, /datum/gas/tritium, /datum/gas/hypernoblium, /datum/gas/water_vapor)
|
||||
@@ -15,14 +16,17 @@
|
||||
air_update_turf()
|
||||
return ..()
|
||||
|
||||
/obj/machinery/portable_atmospherics/scrubber/update_icon()
|
||||
/obj/machinery/portable_atmospherics/scrubber/update_icon_state()
|
||||
icon_state = "pscrubber:[on]"
|
||||
|
||||
cut_overlays()
|
||||
/obj/machinery/portable_atmospherics/scrubber/update_overlays()
|
||||
. = ..()
|
||||
if(!use_overlays)
|
||||
return
|
||||
if(holding)
|
||||
add_overlay("scrubber-open")
|
||||
. += "scrubber-open"
|
||||
if(connected_port)
|
||||
add_overlay("scrubber-connector")
|
||||
. += "scrubber-connector"
|
||||
|
||||
/obj/machinery/portable_atmospherics/scrubber/process_atmos()
|
||||
..()
|
||||
@@ -117,11 +121,12 @@
|
||||
volume = 50000
|
||||
|
||||
var/movable = FALSE
|
||||
use_overlays = FALSE
|
||||
|
||||
/obj/machinery/portable_atmospherics/scrubber/huge/movable
|
||||
movable = TRUE
|
||||
|
||||
/obj/machinery/portable_atmospherics/scrubber/huge/update_icon()
|
||||
/obj/machinery/portable_atmospherics/scrubber/huge/update_icon_state()
|
||||
icon_state = "scrubber:[on]"
|
||||
|
||||
/obj/machinery/portable_atmospherics/scrubber/huge/process_atmos()
|
||||
|
||||
@@ -379,7 +379,7 @@
|
||||
force = 75
|
||||
mag_type = /obj/item/ammo_box/magazine/m50/ctf
|
||||
|
||||
/obj/item/gun/ballistic/automatic/pistol/deagle/ctf/dropped()
|
||||
/obj/item/gun/ballistic/automatic/pistol/deagle/ctf/dropped(mob/user)
|
||||
. = ..()
|
||||
addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
|
||||
|
||||
@@ -402,14 +402,14 @@
|
||||
desc = "This looks like it could really hurt in melee."
|
||||
force = 50
|
||||
|
||||
/obj/item/gun/ballistic/automatic/laser/ctf/dropped()
|
||||
/obj/item/gun/ballistic/automatic/laser/ctf/dropped(mob/user)
|
||||
. = ..()
|
||||
addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
|
||||
|
||||
/obj/item/ammo_box/magazine/recharge/ctf
|
||||
ammo_type = /obj/item/ammo_casing/caseless/laser/ctf
|
||||
|
||||
/obj/item/ammo_box/magazine/recharge/ctf/dropped()
|
||||
/obj/item/ammo_box/magazine/recharge/ctf/dropped(mob/user)
|
||||
. = ..()
|
||||
addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
|
||||
|
||||
@@ -475,7 +475,7 @@
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
/obj/item/claymore/ctf/dropped()
|
||||
/obj/item/claymore/ctf/dropped(mob/user)
|
||||
. = ..()
|
||||
addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
|
||||
|
||||
|
||||
@@ -53,11 +53,8 @@ GLOBAL_DATUM(the_gateway, /obj/machinery/gateway/centerstation)
|
||||
ready = TRUE
|
||||
return ready
|
||||
|
||||
/obj/machinery/gateway/update_icon()
|
||||
if(active)
|
||||
icon_state = "on"
|
||||
return
|
||||
icon_state = "off"
|
||||
/obj/machinery/gateway/update_icon_state()
|
||||
icon_state = active ? "on" : "off"
|
||||
|
||||
/obj/machinery/gateway/attack_hand(mob/user)
|
||||
. = ..()
|
||||
@@ -100,11 +97,8 @@ GLOBAL_DATUM(the_gateway, /obj/machinery/gateway/centerstation)
|
||||
var/obj/machinery/gateway/centeraway/awaygate = null
|
||||
can_link = TRUE
|
||||
|
||||
/obj/machinery/gateway/centerstation/update_icon()
|
||||
if(active)
|
||||
icon_state = "oncenter"
|
||||
return
|
||||
icon_state = "offcenter"
|
||||
/obj/machinery/gateway/centerstation/update_icon_state()
|
||||
icon_state = active ? "oncenter" : "offcenter"
|
||||
|
||||
/obj/machinery/gateway/centerstation/process()
|
||||
if((stat & (NOPOWER)) && use_power)
|
||||
@@ -185,11 +179,8 @@ GLOBAL_DATUM(the_gateway, /obj/machinery/gateway/centerstation)
|
||||
stationgate = locate(/obj/machinery/gateway/centerstation)
|
||||
|
||||
|
||||
/obj/machinery/gateway/centeraway/update_icon()
|
||||
if(active)
|
||||
icon_state = "oncenter"
|
||||
return
|
||||
icon_state = "offcenter"
|
||||
/obj/machinery/gateway/centeraway/update_icon_state()
|
||||
icon_state = active ? "oncenter" : "offcenter"
|
||||
|
||||
/obj/machinery/gateway/centeraway/toggleon(mob/user)
|
||||
if(!detect())
|
||||
|
||||
@@ -337,8 +337,9 @@
|
||||
icon_state = "1"
|
||||
color = rgb(0,0,255)
|
||||
|
||||
/obj/structure/ladder/unbreakable/rune/update_icon()
|
||||
return
|
||||
/obj/structure/ladder/unbreakable/rune/ComponentInitialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/update_icon_blocker)
|
||||
|
||||
/obj/structure/ladder/unbreakable/rune/show_fluff_message(up,mob/user)
|
||||
user.visible_message("[user] activates \the [src].","<span class='notice'>You activate \the [src].</span>")
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
to_chat(user, "<B>Your wish is granted, but at a terrible cost...</B>")
|
||||
to_chat(user, "The Wish Granter punishes you for your selfishness, claiming your soul and warping your body to match the darkness in your heart.")
|
||||
user.dna.add_mutation(LASEREYES)
|
||||
user.dna.add_mutation(COLDRES)
|
||||
user.dna.add_mutation(SPACEMUT)
|
||||
user.dna.add_mutation(XRAY)
|
||||
user.set_species(/datum/species/shadow)
|
||||
if("Wealth")
|
||||
|
||||
@@ -35,5 +35,6 @@
|
||||
environs."
|
||||
|
||||
//we don't want the silly text overlay!
|
||||
/obj/item/paper/pamphlet/update_icon()
|
||||
return
|
||||
/obj/item/paper/pamphlet/ComponentInitialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/update_icon_blocker)
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
update_icon()
|
||||
return 1
|
||||
|
||||
/obj/screen/buildmode/mode/update_icon()
|
||||
/obj/screen/buildmode/mode/update_icon_state()
|
||||
icon_state = bd.mode.get_button_iconstate()
|
||||
|
||||
/obj/screen/buildmode/help
|
||||
@@ -44,9 +44,8 @@
|
||||
screen_loc = "NORTH,WEST+2"
|
||||
name = "Change Dir"
|
||||
|
||||
/obj/screen/buildmode/bdir/update_icon()
|
||||
/obj/screen/buildmode/bdir/update_icon_state()
|
||||
dir = bd.build_dir
|
||||
return
|
||||
|
||||
/obj/screen/buildmode/bdir/Click()
|
||||
bd.toggle_dirswitch()
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
if(!isitem(O))
|
||||
return 0
|
||||
var/obj/item/I = O
|
||||
if(!(getmaterialref(material_id) in I.custom_materials))
|
||||
if(!(SSmaterials.GetMaterialRef(material_id) in I.custom_materials))
|
||||
return 0
|
||||
|
||||
var/amount = I.custom_materials[getmaterialref(material_id)]
|
||||
var/amount = I.custom_materials[SSmaterials.GetMaterialRef(material_id)]
|
||||
|
||||
if(istype(I, /obj/item/stack/ore))
|
||||
amount *= 0.8 // Station's ore redemption equipment is really goddamn good.
|
||||
|
||||
@@ -193,8 +193,8 @@
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/bear,
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/xeno,
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/spider,
|
||||
/obj/item/reagent_containers/food/snacks/spidereggs,
|
||||
/obj/item/reagent_containers/food/snacks/meat/rawcrab,
|
||||
/obj/item/reagent_containers/food/snacks/meat/rawbacon,
|
||||
/obj/item/reagent_containers/food/snacks/spiderleg,
|
||||
/obj/item/reagent_containers/food/snacks/carpmeat,
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/human)
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
var/soundVolume = 80 //Volume to play sounds at. Ignores the cap
|
||||
var/bay //Used specifically for the centcom_podlauncher datum. Holds the current bay the user is launching objects from. Bays are specific rooms on the centcom map.
|
||||
var/list/explosionSize = list(0,0,2,3)
|
||||
var/stay_after_drop = FALSE
|
||||
var/specialised = TRUE // It's not a general use pod for cargo/admin use
|
||||
|
||||
/obj/structure/closet/supplypod/bluespacepod
|
||||
style = STYLE_BLUESPACE
|
||||
@@ -49,6 +51,15 @@
|
||||
explosionSize = list(0,0,1,2)
|
||||
landingDelay = 15 //Slightly quicker than the supplypod
|
||||
|
||||
/obj/structure/closet/supplypod/extractionpod
|
||||
name = "Syndicate Extraction Pod"
|
||||
desc = "A specalised, blood-red styled pod for extracting high-value targets out of active mission areas."
|
||||
specialised = TRUE
|
||||
style = STYLE_SYNDICATE
|
||||
bluespace = TRUE
|
||||
explosionSize = list(0,0,1,2)
|
||||
landingDelay = 25 //Slightly longer than others
|
||||
|
||||
/obj/structure/closet/supplypod/centcompod
|
||||
style = STYLE_CENTCOM
|
||||
bluespace = TRUE
|
||||
@@ -56,19 +67,25 @@
|
||||
landingDelay = 20 //Very speedy!
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
|
||||
/obj/structure/closet/supplypod/proc/specialisedPod()
|
||||
return 1
|
||||
|
||||
/obj/structure/closet/supplypod/extractionpod/specialisedPod(atom/movable/holder)
|
||||
holder.forceMove(pick(GLOB.holdingfacility)) // land in ninja jail
|
||||
open(holder, forced = TRUE)
|
||||
|
||||
/obj/structure/closet/supplypod/Initialize()
|
||||
. = ..()
|
||||
setStyle(style, TRUE) //Upon initialization, give the supplypod an iconstate, name, and description based on the "style" variable. This system is important for the centcom_podlauncher to function correctly
|
||||
|
||||
/obj/structure/closet/supplypod/update_icon()
|
||||
cut_overlays()
|
||||
/obj/structure/closet/supplypod/closet_update_overlays(list/new_overlays)
|
||||
. = new_overlays
|
||||
if (style == STYLE_SEETHROUGH || style == STYLE_INVISIBLE) //If we're invisible, we dont bother adding any overlays
|
||||
return
|
||||
if (opened)
|
||||
. += "[icon_state]_open"
|
||||
else
|
||||
if (opened)
|
||||
add_overlay("[icon_state]_open")
|
||||
else
|
||||
add_overlay("[icon_state]_door")
|
||||
. += "[icon_state]_door"
|
||||
|
||||
/obj/structure/closet/supplypod/proc/setStyle(chosenStyle, var/duringInit = FALSE) //Used to give the sprite an icon state, name, and description
|
||||
if (!duringInit && style == chosenStyle) //Check if the input style is already the same as the pod's style. This happens in centcom_podlauncher, and as such we set the style to STYLE_CENTCOM.
|
||||
@@ -76,7 +93,7 @@
|
||||
return
|
||||
style = chosenStyle
|
||||
icon_state = POD_STYLES[chosenStyle][POD_ICON_STATE] //POD_STYLES is a 2D array we treat as a dictionary. The style represents the verticle index, with the icon state, name, and desc being stored in the horizontal indexes of the 2D array.
|
||||
if (!adminNamed) //We dont want to name it ourselves if it has been specifically named by an admin using the centcom_podlauncher datum
|
||||
if (!adminNamed && !specialised) //We dont want to name it ourselves if it has been specifically named by an admin using the centcom_podlauncher datum
|
||||
name = POD_STYLES[chosenStyle][POD_NAME]
|
||||
desc = POD_STYLES[chosenStyle][POD_DESC]
|
||||
update_icon()
|
||||
@@ -96,6 +113,30 @@
|
||||
/obj/structure/closet/supplypod/toggle(mob/living/user) //Supplypods shouldn't be able to be manually opened under any circumstances, as the open() proc generates supply order datums
|
||||
return
|
||||
|
||||
/obj/structure/closet/supplypod/proc/handleReturningClose(atom/movable/holder, returntobay)
|
||||
opened = FALSE
|
||||
INVOKE_ASYNC(holder, .proc/setClosed) //Use the INVOKE_ASYNC proc to call setClosed() on whatever the holder may be, without giving the atom/movable base class a setClosed() proc definition
|
||||
for(var/atom/movable/O in get_turf(holder))
|
||||
if ((ismob(O) && !isliving(O)) || (is_type_in_typecache(O, GLOB.blacklisted_cargo_types) && !isliving(O))) //We dont want to take ghosts with us, and we don't want blacklisted items going, but we allow mobs.
|
||||
continue
|
||||
O.forceMove(holder) //Put objects inside before we close
|
||||
var/obj/effect/temp_visual/risingPod = new /obj/effect/abstract/DPfall(get_turf(holder), src) //Make a nice animation of flying back up
|
||||
risingPod.pixel_z = 0 //The initial value of risingPod's pixel_z is 200 because it normally comes down from a high spot
|
||||
animate(risingPod, pixel_z = 200, time = 10, easing = LINEAR_EASING) //Animate our rising pod
|
||||
if(returntobay)
|
||||
holder.forceMove(bay) //Move the pod back to centcom, where it belongs
|
||||
QDEL_IN(risingPod, 10)
|
||||
reversing = FALSE //Now that we're done reversing, we set this to false (otherwise we would get stuck in an infinite loop of calling the close proc at the bottom of open() )
|
||||
bluespace = TRUE //Make it so that the pod doesn't stay in centcom forever
|
||||
open(holder, forced = TRUE)
|
||||
else
|
||||
reversing = FALSE //Now that we're done reversing, we set this to false (otherwise we would get stuck in an infinite loop of calling the close proc at the bottom of open() )
|
||||
bluespace = TRUE //Make it so that the pod doesn't stay in centcom forever
|
||||
QDEL_IN(risingPod, 10)
|
||||
audible_message("<span class='notice'>The pod hisses, closing quickly and launching itself away from the station.</span>", "<span class='notice'>The ground vibrates, the nearby pod launching away from the station.</span>")
|
||||
stay_after_drop = FALSE
|
||||
specialisedPod(holder) // Do special actions for specialised pods - this is likely if we were already doing manual launches
|
||||
|
||||
/obj/structure/closet/supplypod/proc/preOpen() //Called before the open() proc. Handles anything that occurs right as the pod lands.
|
||||
var/turf/T = get_turf(src)
|
||||
var/list/B = explosionSize //Mostly because B is more readable than explosionSize :p
|
||||
@@ -172,7 +213,8 @@
|
||||
if (style == STYLE_SEETHROUGH)
|
||||
depart(src)
|
||||
else
|
||||
addtimer(CALLBACK(src, .proc/depart, holder), departureDelay) //Finish up the pod's duties after a certain amount of time
|
||||
if(!stay_after_drop) // Departing should be handled manually
|
||||
addtimer(CALLBACK(src, .proc/depart, holder), departureDelay) //Finish up the pod's duties after a certain amount of time
|
||||
|
||||
/obj/structure/closet/supplypod/proc/depart(atom/movable/holder)
|
||||
if (leavingSound)
|
||||
@@ -187,20 +229,18 @@
|
||||
qdel(holder)
|
||||
|
||||
/obj/structure/closet/supplypod/centcompod/close(atom/movable/holder) //Closes the supplypod and sends it back to centcom. Should only ever be called if the "reversing" variable is true
|
||||
opened = FALSE
|
||||
INVOKE_ASYNC(holder, .proc/setClosed) //Use the INVOKE_ASYNC proc to call setClosed() on whatever the holder may be, without giving the atom/movable base class a setClosed() proc definition
|
||||
for (var/atom/movable/O in get_turf(holder))
|
||||
if (ismob(O) && !isliving(O)) //We dont want to take ghosts with us
|
||||
continue
|
||||
O.forceMove(holder) //Put objects inside before we close
|
||||
var/obj/effect/temp_visual/risingPod = new /obj/effect/abstract/DPfall(get_turf(holder), src) //Make a nice animation of flying back up
|
||||
risingPod.pixel_z = 0 //The initial value of risingPod's pixel_z is 200 because it normally comes down from a high spot
|
||||
holder.forceMove(bay) //Move the pod back to centcom, where it belongs
|
||||
animate(risingPod, pixel_z = 200, time = 10, easing = LINEAR_EASING) //Animate our rising pod
|
||||
QDEL_IN(risingPod, 10)
|
||||
reversing = FALSE //Now that we're done reversing, we set this to false (otherwise we would get stuck in an infinite loop of calling the close proc at the bottom of open() )
|
||||
bluespace = TRUE //Make it so that the pod doesn't stay in centcom forever
|
||||
open(holder, forced = TRUE)
|
||||
handleReturningClose(holder, TRUE)
|
||||
|
||||
/obj/structure/closet/supplypod/extractionpod/close(atom/movable/holder) //handles closing, and returns pod - deletes itself when returned
|
||||
. = ..()
|
||||
return
|
||||
|
||||
/obj/structure/closet/supplypod/extractionpod/proc/send_up(atom/movable/holder)
|
||||
if(!holder)
|
||||
holder = src
|
||||
if(leavingSound)
|
||||
playsound(get_turf(holder), leavingSound, soundVolume, 0, 0)
|
||||
handleReturningClose(holder, FALSE)
|
||||
|
||||
/obj/structure/closet/supplypod/proc/setOpened() //Proc exists here, as well as in any atom that can assume the role of a "holder" of a supplypod. Check the open() proc for more details
|
||||
update_icon()
|
||||
|
||||
@@ -31,14 +31,14 @@
|
||||
ready = FALSE
|
||||
update_icon()
|
||||
|
||||
/obj/item/supplypod_beacon/update_icon()
|
||||
cut_overlays()
|
||||
/obj/item/supplypod_beacon/update_overlays()
|
||||
. = ..()
|
||||
if (launched)
|
||||
add_overlay("sp_green")
|
||||
. += "sp_green"
|
||||
else if (ready)
|
||||
add_overlay("sp_yellow")
|
||||
. += "sp_yellow"
|
||||
else if (linked)
|
||||
add_overlay("sp_orange")
|
||||
. += "sp_orange"
|
||||
|
||||
/obj/item/supplypod_beacon/proc/endLaunch()
|
||||
launched = FALSE
|
||||
|
||||
@@ -703,3 +703,10 @@ GLOBAL_LIST_EMPTY(asset_datums)
|
||||
|
||||
Insert(initial(D.id), I)
|
||||
return ..()
|
||||
|
||||
/datum/asset/simple/genetics
|
||||
assets = list(
|
||||
"dna_discovered.gif" = 'html/dna_discovered.gif',
|
||||
"dna_undiscovered.gif" = 'html/dna_undiscovered.gif',
|
||||
"dna_extra.gif" = 'html/dna_extra.gif'
|
||||
)
|
||||
@@ -123,7 +123,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
"has_cock" = FALSE,
|
||||
"cock_shape" = "Human",
|
||||
"cock_length" = 6,
|
||||
"cock_girth_ratio" = COCK_GIRTH_RATIO_DEF,
|
||||
"cock_diameter_ratio" = COCK_DIAMETER_RATIO_DEF,
|
||||
"cock_color" = "fff",
|
||||
"has_sheath" = FALSE,
|
||||
"sheath_color" = "fff",
|
||||
@@ -218,6 +218,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/icon/bgstate = "steel"
|
||||
var/list/bgstate_options = list("000", "midgrey", "FFF", "white", "steel", "techmaint", "dark", "plating", "reinforced")
|
||||
|
||||
var/show_mismatched_markings = FALSE //determines whether or not the markings lists should show markings that don't match the currently selected species. Intentionally left unsaved.
|
||||
|
||||
/datum/preferences/New(client/C)
|
||||
parent = C
|
||||
|
||||
@@ -419,6 +421,14 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
//Mutant stuff
|
||||
var/mutant_category = 0
|
||||
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
dat += "<h3>Show mismatched markings</h3>"
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=mismatched_markings;task=input'>[show_mismatched_markings ? "Yes" : "No"]</a>"
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS) //just in case someone sets the max rows to 1 or something dumb like that
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
|
||||
if("tail_lizard" in pref_species.default_features)
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
@@ -1626,6 +1636,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
else
|
||||
to_chat(user, "<span class='danger'>Invalid color. Your color is not bright enough.</span>")
|
||||
|
||||
if("mismatched_markings")
|
||||
show_mismatched_markings = !show_mismatched_markings
|
||||
|
||||
if("ipc_screen")
|
||||
var/new_ipc_screen
|
||||
new_ipc_screen = input(user, "Choose your character's screen:", "Character Preference") as null|anything in GLOB.ipc_screens_list
|
||||
@@ -1633,8 +1646,18 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
features["ipc_screen"] = new_ipc_screen
|
||||
|
||||
if("ipc_antenna")
|
||||
var/list/snowflake_antenna_list = list()
|
||||
//Potential todo: turn all of THIS into a define to reduce copypasta.
|
||||
for(var/path in GLOB.ipc_antennas_list)
|
||||
var/datum/sprite_accessory/antenna/instance = GLOB.ipc_antennas_list[path]
|
||||
if(istype(instance, /datum/sprite_accessory))
|
||||
var/datum/sprite_accessory/S = instance
|
||||
if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id))
|
||||
continue
|
||||
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey)))
|
||||
snowflake_antenna_list[S.name] = path
|
||||
var/new_ipc_antenna
|
||||
new_ipc_antenna = input(user, "Choose your character's antenna:", "Character Preference") as null|anything in GLOB.ipc_antennas_list
|
||||
new_ipc_antenna = input(user, "Choose your character's antenna:", "Character Preference") as null|anything in snowflake_antenna_list
|
||||
if(new_ipc_antenna)
|
||||
features["ipc_antenna"] = new_ipc_antenna
|
||||
|
||||
@@ -1654,6 +1677,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/datum/sprite_accessory/tails/human/instance = GLOB.tails_list_human[path]
|
||||
if(istype(instance, /datum/sprite_accessory))
|
||||
var/datum/sprite_accessory/S = instance
|
||||
if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id))
|
||||
continue
|
||||
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey)))
|
||||
snowflake_tails_list[S.name] = path
|
||||
var/new_tail
|
||||
@@ -1671,6 +1696,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/datum/sprite_accessory/mam_tails/instance = GLOB.mam_tails_list[path]
|
||||
if(istype(instance, /datum/sprite_accessory))
|
||||
var/datum/sprite_accessory/S = instance
|
||||
if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id))
|
||||
continue
|
||||
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey)))
|
||||
snowflake_tails_list[S.name] = path
|
||||
var/new_tail
|
||||
@@ -1694,6 +1721,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/datum/sprite_accessory/mam_snouts/instance = GLOB.snouts_list[path]
|
||||
if(istype(instance, /datum/sprite_accessory))
|
||||
var/datum/sprite_accessory/S = instance
|
||||
if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id))
|
||||
continue
|
||||
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey)))
|
||||
snowflake_snouts_list[S.name] = path
|
||||
var/new_snout
|
||||
@@ -1709,6 +1738,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/datum/sprite_accessory/mam_snouts/instance = GLOB.mam_snouts_list[path]
|
||||
if(istype(instance, /datum/sprite_accessory))
|
||||
var/datum/sprite_accessory/S = instance
|
||||
if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id))
|
||||
continue
|
||||
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey)))
|
||||
snowflake_mam_snouts_list[S.name] = path
|
||||
var/new_mam_snouts
|
||||
@@ -1806,6 +1837,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/datum/sprite_accessory/taur/instance = GLOB.taur_list[path]
|
||||
if(istype(instance, /datum/sprite_accessory))
|
||||
var/datum/sprite_accessory/S = instance
|
||||
if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id))
|
||||
continue
|
||||
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey)))
|
||||
snowflake_taur_list[S.name] = path
|
||||
var/new_taur
|
||||
@@ -1824,6 +1857,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/datum/sprite_accessory/ears/instance = GLOB.ears_list[path]
|
||||
if(istype(instance, /datum/sprite_accessory))
|
||||
var/datum/sprite_accessory/S = instance
|
||||
if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id))
|
||||
continue
|
||||
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey)))
|
||||
snowflake_ears_list[S.name] = path
|
||||
var/new_ears
|
||||
@@ -1837,6 +1872,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/datum/sprite_accessory/mam_ears/instance = GLOB.mam_ears_list[path]
|
||||
if(istype(instance, /datum/sprite_accessory))
|
||||
var/datum/sprite_accessory/S = instance
|
||||
if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id))
|
||||
continue
|
||||
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey)))
|
||||
snowflake_ears_list[S.name] = path
|
||||
var/new_ears
|
||||
@@ -1850,6 +1887,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/datum/sprite_accessory/mam_body_markings/instance = GLOB.mam_body_markings_list[path]
|
||||
if(istype(instance, /datum/sprite_accessory))
|
||||
var/datum/sprite_accessory/S = instance
|
||||
if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id))
|
||||
continue
|
||||
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey)))
|
||||
snowflake_markings_list[S.name] = path
|
||||
var/new_mam_body_markings
|
||||
@@ -2394,6 +2433,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
else
|
||||
character.Digitigrade_Leg_Swap(TRUE)
|
||||
|
||||
SEND_SIGNAL(character, COMSIG_HUMAN_PREFS_COPIED_TO, src, icon_updates, roundstart_checks)
|
||||
|
||||
//let's be sure the character updates
|
||||
if(icon_updates)
|
||||
character.update_body()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user