This commit is contained in:
Ghommie
2020-05-01 21:49:52 +02:00
268 changed files with 5984 additions and 7619 deletions

View File

@@ -429,6 +429,10 @@
for(var/datum/dynamic_ruleset/roundstart/rule in GLOB.dynamic_forced_roundstart_ruleset)
dat += {"<A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_remove=\ref[rule]'>-> [rule.name] <-</A><br>"}
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_clear=1'>(Clear Rulesets)</A><br>"
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_storyteller=1'>(Force Storyteller)</A><br>"
if (GLOB.dynamic_forced_storyteller)
var/datum/dynamic_storyteller/S = GLOB.dynamic_forced_storyteller
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_storyteller_clear=1'>-> [initial(S.name)] <-</A><br>"
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_options=1'>(Dynamic mode options)</A><br>"
else if (SSticker.IsRoundInProgress())
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_latejoin=1'>(Force Next Latejoin Ruleset)</A><br>"
@@ -690,7 +694,7 @@
var/prev_dynamic_voting = CONFIG_GET(flag/dynamic_voting)
CONFIG_SET(flag/dynamic_voting,!prev_dynamic_voting)
if (!prev_dynamic_voting)
to_chat(world, "<B>Vote is now a ranked choice of dynamic storytellers.</B>")
to_chat(world, "<B>Vote is now between dynamic storytellers.</B>")
else
to_chat(world, "<B>Vote is now between extended and secret.</B>")
log_admin("[key_name(usr)] [prev_dynamic_voting ? "disabled" : "enabled"] dynamic voting.")

View File

@@ -81,7 +81,7 @@ GLOBAL_PROTECT(admin_verbs_admin)
)
GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/DB_ban_panel, /client/proc/stickybanpanel))
GLOBAL_PROTECT(admin_verbs_ban)
GLOBAL_LIST_INIT(admin_verbs_sounds, list(/client/proc/play_local_sound, /client/proc/play_sound, /client/proc/set_round_end_sound))
GLOBAL_LIST_INIT(admin_verbs_sounds, list(/client/proc/play_local_sound, /client/proc/play_sound, /client/proc/manual_play_web_sound, /client/proc/set_round_end_sound))
GLOBAL_PROTECT(admin_verbs_sounds)
GLOBAL_LIST_INIT(admin_verbs_fun, list(
/client/proc/cmd_admin_dress,
@@ -364,7 +364,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
log_admin("[key_name(usr)] admin ghosted.")
message_admins("[key_name_admin(usr)] admin ghosted.")
var/mob/body = mob
body.ghostize(1)
body.ghostize(1, voluntary = TRUE)
if(body && !body.key)
body.key = "@[key]" //Haaaaaaaack. But the people have spoken. If it breaks; blame adminbus
SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin Ghost") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!

View File

@@ -120,7 +120,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
//adv proc call this, ya nerds
/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
if(target == GLOBAL_PROC)
return call(procname)(arglist(arguments))
return call(text2path("/proc/[procname]"))(arglist(arguments))
else if(target != world)
return call(target, procname)(arglist(arguments))
else

View File

@@ -1394,6 +1394,32 @@
log_admin("[key_name(usr)] removed [rule] from the forced roundstart rulesets.")
message_admins("[key_name(usr)] removed [rule] from the forced roundstart rulesets.", 1)
else if(href_list["f_dynamic_storyteller"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode.", null, null, null, null)
var/list/choices = list()
for(var/T in config.storyteller_cache)
var/datum/dynamic_storyteller/S = T
choices[initial(S.name)] = T
var/choice = choices[input("Select storyteller:", "Storyteller", "Classic") as null|anything in choices]
if(choice)
GLOB.dynamic_forced_storyteller = choice
log_admin("[key_name(usr)] forced the storyteller to [GLOB.dynamic_forced_storyteller].")
message_admins("[key_name(usr)] forced the storyteller to [GLOB.dynamic_forced_storyteller].")
Game()
else if(href_list["f_dynamic_storyteller_clear"])
if(!check_rights(R_ADMIN))
return
GLOB.dynamic_forced_storyteller = null
Game()
log_admin("[key_name(usr)] cleared the forced storyteller. The mode will pick one as normal.")
message_admins("[key_name(usr)] cleared the forced storyteller. The mode will pick one as normal.", 1)
else if(href_list["f_dynamic_latejoin"])
if(!check_rights(R_ADMIN))
return

View File

@@ -138,6 +138,49 @@
SSblackbox.record_feedback("tally", "admin_verb", 1, "Play Internet Sound")
/client/proc/manual_play_web_sound()
set category = "Fun"
set name = "Manual Play Internet Sound"
if(!check_rights(R_SOUNDS))
return
var/web_sound_input = input("Enter content stream URL (fetch this from local youtube-dl!)", "Play Internet Sound via direct URL") as text|null
if(istext(web_sound_input))
if(!length(web_sound_input))
log_admin("[key_name(src)] stopped web sound")
message_admins("[key_name(src)] stopped web sound")
var/mob/M
for(var/i in GLOB.player_list)
M = i
M?.client?.chatOutput?.stopMusic()
return
else
if(web_sound_input && !findtext(web_sound_input, GLOB.is_http_protocol))
to_chat(src, "<span class='boldwarning'>BLOCKED: Content URL not using http(s) protocol</span>")
return
var/freq = input(usr, "What frequency would you like the sound to play at?",, 1) as null|num
if(isnull(freq))
return
if(!freq)
freq = 1
SSblackbox.record_feedback("nested tally", "played_url", 1, list("[ckey]", "[web_sound_input]"))
var/logstr = "[key_name(src)] played web sound at freq [freq]: [web_sound_input]"
log_admin(logstr)
message_admins(logstr)
var/mob/M
var/client/C
var/datum/chatOutput/O
for(var/i in GLOB.player_list)
M = i
C = M.client
if(!(C?.prefs?.toggles & SOUND_MIDI))
continue
O = C.chatOutput
if(!O || O.broken || !O.loaded)
continue
O.sendMusic(web_sound_input, freq)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Manual Play Internet Sound")
/client/proc/set_round_end_sound(S as sound)
set category = "Fun"
set name = "Set Round End Sound"

View File

@@ -1379,3 +1379,47 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
else
message_admins("[key_name_admin(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name_admin(C)]")
log_admin("[key_name(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name(C)]")
/// Allow admin to add or remove traits of datum
/datum/admins/proc/modify_traits(datum/D)
if(!D)
return
var/add_or_remove = input("Remove/Add?", "Trait Remove/Add") as null|anything in list("Add","Remove")
if(!add_or_remove)
return
var/list/availible_traits = list()
switch(add_or_remove)
if("Add")
for(var/key in GLOB.traits_by_type)
if(istype(D,key))
availible_traits += GLOB.traits_by_type[key]
if("Remove")
if(!GLOB.trait_name_map)
GLOB.trait_name_map = generate_trait_name_map()
for(var/trait in D.status_traits)
var/name = GLOB.trait_name_map[trait] || trait
availible_traits[name] = trait
var/chosen_trait = input("Select trait to modify", "Trait") as null|anything in sortList(availible_traits)
if(!chosen_trait)
return
chosen_trait = availible_traits[chosen_trait]
var/source = "adminabuse"
switch(add_or_remove)
if("Add") //Not doing source choosing here intentionally to make this bit faster to use, you can always vv it.
ADD_TRAIT(D,chosen_trait,source)
if("Remove")
var/specific = input("All or specific source ?", "Trait Remove/Add") as null|anything in list("All","Specific")
if(!specific)
return
switch(specific)
if("All")
source = null
if("Specific")
source = input("Source to be removed","Trait Remove/Add") as null|anything in sortList(D.status_traits[chosen_trait])
if(!source)
return
REMOVE_TRAIT(D,chosen_trait,source)

View File

@@ -194,9 +194,10 @@ GLOBAL_PROTECT(VVpixelmovement)
else
variable = L[index]
//EXPERIMENTAL - Keep old associated value while modifying key, if any
var/found = L[variable]
if(!isnull(found))
old_assoc_value = found
if(IS_VALID_ASSOC_KEY(variable))
var/found = L[variable]
if(!isnull(found))
old_assoc_value = found
//
default = vv_get_class(objectvar, variable)

View File

@@ -47,5 +47,33 @@
usr.client.debug_variables(src)
if(href_list[VV_HK_MARK])
usr.client.mark_datum(target)
if(href_list[VV_HK_ADDCOMPONENT])
if(!check_rights(NONE))
return
var/list/names = list()
var/list/componentsubtypes = subtypesof(/datum/component)
names += "---Components---"
names += componentsubtypes
names += "---Elements---"
names += subtypesof(/datum/element)
var/result = input(usr, "Choose a component/element to add","better know what ur fuckin doin pal") as null|anything in names
if(!usr || !result || result == "---Components---" || result == "---Elements---")
return
if(QDELETED(src))
to_chat(usr, "That thing doesn't exist anymore!")
return
var/list/lst = get_callproc_args()
if(!lst)
return
var/datumname = "error"
lst.Insert(1, result)
if(result in componentsubtypes)
datumname = "component"
target._AddComponent(lst)
else
datumname = "element"
target._AddElement(lst)
log_admin("[key_name(usr)] has added [result] [datumname] to [key_name(src)].")
message_admins("<span class='notice'>[key_name_admin(usr)] has added [result] [datumname] to [key_name_admin(src)].</span>")
if(href_list[VV_HK_CALLPROC])
usr.client.callproc_datum(target)

View File

@@ -6,6 +6,7 @@
status = ORGAN_ROBOTIC
beating = TRUE
organ_flags = ORGAN_NO_SPOIL
no_pump = TRUE
var/true_name = "baseline placebo referencer"
var/cooldown_low = 300
var/cooldown_high = 300
@@ -92,6 +93,7 @@
update_gland_hud()
/obj/item/organ/heart/gland/on_life()
. = ..()
if(!beating)
// alien glands are immune to stopping.
beating = TRUE

View File

@@ -14,10 +14,10 @@
set waitfor = FALSE // Don't make on_gain() wait for this function to finish. This lets this code run on the side.
var/notice_healing
while(owner && !AmFinalDeath()) // owner.has_antag_datum(ANTAG_DATUM_BLOODSUCKER) == src
if(owner.current.stat == CONSCIOUS && !poweron_feed && !HAS_TRAIT(owner.current, TRAIT_DEATHCOMA)) // Deduct Blood
if(owner.current.stat == CONSCIOUS && !poweron_feed && !HAS_TRAIT(owner.current, TRAIT_FAKEDEATH)) // Deduct Blood
AddBloodVolume(passive_blood_drain) // -.1 currently
if(HandleHealing(1)) // Heal
if(notice_healing == FALSE && owner.current.blood_volume > 0)
if(!notice_healing && owner.current.blood_volume > 0)
to_chat(owner, "<span class='notice'>The power of your blood begins knitting your wounds...</span>")
notice_healing = TRUE
else if(notice_healing == TRUE)
@@ -25,7 +25,7 @@
HandleStarving() // Death
HandleDeath() // Standard Update
update_hud()// Daytime Sleep in Coffin
if(SSticker.mode.is_daylight() && !HAS_TRAIT_FROM(owner.current, TRAIT_DEATHCOMA, "bloodsucker"))
if(SSticker.mode.is_daylight() && !HAS_TRAIT_FROM(owner.current, TRAIT_FAKEDEATH, "bloodsucker"))
if(istype(owner.current.loc, /obj/structure/closet/crate/coffin))
Torpor_Begin()
// Wait before next pass
@@ -83,7 +83,7 @@
// NOTE: Mult of 0 is just a TEST to see if we are injured and need to go into Torpor!
//It is called from your coffin on close (by you only)
var/actual_regen = regen_rate + additional_regen
if(poweron_masquerade == TRUE || owner.current.AmStaked())
if(poweron_masquerade|| owner.current.AmStaked())
return FALSE
if(owner.current.reagents.has_reagent(/datum/reagent/consumable/garlic))
return FALSE
@@ -101,8 +101,8 @@
var/mob/living/carbon/C = owner.current
var/costMult = 1 // Coffin makes it cheaper
var/fireheal = 0 // BURN: Heal in Coffin while Fakedeath, or when damage above maxhealth (you can never fully heal fire)
var/amInCoffinWhileTorpor = istype(C.loc, /obj/structure/closet/crate/coffin) && (mult == 0 || HAS_TRAIT(C, TRAIT_FAKEDEATH)) // Check for mult 0 OR death coma. (mult 0 means we're testing from coffin)
if(amInCoffinWhileTorpor)
// Check for mult 0 OR death coma. (mult 0 means we're testing from coffin)
if(istype(C.loc, /obj/structure/closet/crate/coffin) && (mult == 0 || HAS_TRAIT(C, TRAIT_FAKEDEATH)))
mult *= 4 // Increase multiplier if we're sleeping in a coffin.
fireheal = min(C.getFireLoss(), regen_rate) // NOTE: Burn damage ONLY heals in torpor.
C.ExtinguishMob()
@@ -112,6 +112,9 @@
CheckVampOrgans() // Heart, Eyes
if(check_limbs(costMult))
return TRUE
else if(owner.current.stat >= UNCONSCIOUS) //Faster regeneration and slight burn healing while unconcious
mult *= 2
fireheal = min(C.getFireLoss(), regen_rate * 0.2)
// BRUTE: Always Heal
var/bruteheal = min(C.getBruteLoss(), actual_regen)
@@ -120,8 +123,6 @@
if(bruteheal + fireheal + toxinheal > 0) // Just a check? Don't heal/spend, and return.
if(mult == 0)
return TRUE
if(owner.current.stat >= UNCONSCIOUS) //Faster regeneration while unconcious, so you dont have to wait all day
mult *= 2
// We have damage. Let's heal (one time)
C.adjustBruteLoss(-bruteheal * mult, forced = TRUE)// Heal BRUTE / BURN in random portions throughout the body.
C.adjustFireLoss(-fireheal * mult, forced = TRUE)
@@ -146,12 +147,6 @@
to_chat(C, "<span class='notice'>Your flesh knits as it regrows your [L]!</span>")
playsound(C, 'sound/magic/demon_consume.ogg', 50, TRUE)
return TRUE
/*for(var/obj/item/bodypart/BP in C.bodyparts)
if(!istype(BP) && !BP.status == 2)
return FALSE
to_chat(C, "<span class='notice'>Your body expels the [BP]!</span>")
BP.drop_limb()
return TRUE */
/datum/antagonist/bloodsucker/proc/CureDisabilities()
var/mob/living/carbon/C = owner.current
@@ -176,7 +171,7 @@
// EMPTY: Frenzy!
// BLOOD_VOLUME_GOOD: [336] Pale (handled in bloodsucker_integration.dm
// BLOOD_VOLUME_BAD: [224] Jitter
if(owner.current.blood_volume < BLOOD_VOLUME_BAD && !prob(0.5))
if(owner.current.blood_volume < BLOOD_VOLUME_BAD && !prob(0.5 && HAS_TRAIT(owner, TRAIT_FAKEDEATH)) && !poweron_masquerade)
owner.current.Jitter(3)
// BLOOD_VOLUME_SURVIVE: [122] Blur Vision
if(owner.current.blood_volume < BLOOD_VOLUME_BAD / 2)
@@ -230,16 +225,16 @@
Torpor_Begin()
to_chat(owner, "<span class='danger'>Your immortal body will not yet relinquish your soul to the abyss. You enter Torpor.</span>")
sleep(30) //To avoid spam
if(poweron_masquerade == TRUE)
if(poweron_masquerade)
to_chat(owner, "<span class='warning'>Your wounds will not heal until you disable the <span class='boldnotice'>Masquerade</span> power.</span>")
// End Torpor:
else // No damage, OR toxin healed AND brute healed and NOT in coffin (since you cannot heal burn)
if(total_damage <= 0 || total_toxloss <= 0 && total_brute <= 0 && !istype(owner.current.loc, /obj/structure/closet/crate/coffin))
// Not Daytime, Not in Torpor
if(!SSticker.mode.is_daylight() && HAS_TRAIT_FROM(owner.current, TRAIT_FAKEDEATH, "bloodsucker"))
// Not Daytime, Not in Torpor, enough health to not die the moment you end torpor
if(!SSticker.mode.is_daylight() && HAS_TRAIT_FROM(owner.current, TRAIT_FAKEDEATH, "bloodsucker") && total_damage < owner.current.getMaxHealth())
Torpor_End()
// Fake Unconscious
if(poweron_masquerade == TRUE && total_damage >= owner.current.getMaxHealth() - HEALTH_THRESHOLD_FULLCRIT)
if(poweron_masquerade && total_damage >= owner.current.getMaxHealth() - HEALTH_THRESHOLD_FULLCRIT)
owner.current.Unconscious(20, 1)
/datum/antagonist/bloodsucker/proc/Torpor_Begin(amInCoffin = FALSE)
@@ -249,6 +244,7 @@
ADD_TRAIT(owner.current, TRAIT_NODEATH, "bloodsucker") // Without this, you'll just keep dying while you recover.
ADD_TRAIT(owner.current, TRAIT_RESISTHIGHPRESSURE, "bloodsucker") // So you can heal in space. Otherwise you just...heal forever.
ADD_TRAIT(owner.current, TRAIT_RESISTLOWPRESSURE, "bloodsucker")
owner.current.Jitter(0)
// Visuals
owner.current.update_sight()
owner.current.reload_fullscreen()
@@ -256,6 +252,9 @@
for(var/datum/action/bloodsucker/power in powers)
if(power.active && !power.can_use_in_torpor)
power.DeactivatePower()
if(owner.current.suiciding)
owner.current.suiciding = FALSE //Youll die but not for long.
to_chat(owner.current, "<span class='warning'>Your body keeps you going, even as you try to end yourself.</span>")
/datum/antagonist/bloodsucker/proc/Torpor_End()
owner.current.stat = SOFT_CRIT

View File

@@ -45,15 +45,15 @@
// (FINAL LIL WARNING)
while(time_til_cycle > 5)
sleep(10)
if (cancel_me)
if(cancel_me)
return
//sleep(TIME_BLOODSUCKER_DAY_FINAL_WARN - 50)
warn_daylight(3,"<span class = 'userdanger'>Seek cover, for Sol rises!</span>")
// Part 3: Night Ending
while (time_til_cycle > 0)
while(time_til_cycle > 0)
sleep(10)
if (cancel_me)
if(cancel_me)
return
//sleep(50)
warn_daylight(4,"<span class = 'userdanger'>Solar flares bombard the station with deadly UV light!</span><br><span class = ''>Stay in cover for the next [TIME_BLOODSUCKER_DAY / 60] minutes or risk Final Death!</span>",\
@@ -69,11 +69,11 @@
while(time_til_cycle > 0)
punish_vamps()
sleep(TIME_BLOODSUCKER_BURN_INTERVAL)
if (cancel_me)
if(cancel_me)
return
//daylight_time -= TIME_BLOODSUCKER_BURN_INTERVAL
// Issue Level Up!
if(!issued_XP && time_til_cycle <= 15)
if(!issued_XP && time_til_cycle <= 5)
issued_XP = TRUE
vamps_rank_up()

View File

@@ -227,7 +227,7 @@
// Traits
for(var/T in defaultTraits)
REMOVE_TRAIT(owner.current, T, BLOODSUCKER_TRAIT)
if(had_toxlover == TRUE)
if(had_toxlover)
ADD_TRAIT(owner.current, TRAIT_TOXINLOVER, SPECIES_TRAIT)
// Traits: Species

View File

@@ -18,14 +18,11 @@
. = ..()
if(!.)
return
// must have nobody around to see the cloak
var/watchers = viewers(9,get_turf(owner))
for(var/mob/living/M in watchers)
for(var/mob/living/M in viewers(9, owner))
if(M != owner)
to_chat(owner, "<span class='warning'>You may only vanish into the shadows unseen.</span>")
return FALSE
return TRUE
/datum/action/bloodsucker/cloak/ActivatePower()

View File

@@ -52,6 +52,7 @@
REMOVE_TRAIT(user, TRAIT_NOHARDCRIT, "bloodsucker")
REMOVE_TRAIT(user, TRAIT_NOSOFTCRIT, "bloodsucker")
REMOVE_TRAIT(user, TRAIT_VIRUSIMMUNE, "bloodsucker")
REMOVE_TRAIT(user, TRAIT_NOBREATH, "bloodsucker")
var/obj/item/organ/heart/vampheart/H = user.getorganslot(ORGAN_SLOT_HEART)
var/obj/item/organ/eyes/vassal/bloodsucker/E = user.getorganslot(ORGAN_SLOT_EYES)
E.flash_protect = 0
@@ -93,6 +94,7 @@
ADD_TRAIT(user, TRAIT_NOHARDCRIT, "bloodsucker")
ADD_TRAIT(user, TRAIT_NOSOFTCRIT, "bloodsucker")
ADD_TRAIT(user, TRAIT_VIRUSIMMUNE, "bloodsucker")
ADD_TRAIT(user, TRAIT_NOBREATH, "bloodsucker")
// HEART
var/obj/item/organ/heart/H = user.getorganslot(ORGAN_SLOT_HEART)

View File

@@ -28,7 +28,7 @@
/datum/action/bloodsucker/targeted/trespass/CheckValidTarget(atom/A)
// Can't target my tile
if (A == get_turf(owner) || get_turf(A) == get_turf(owner))
if(A == get_turf(owner) || get_turf(A) == get_turf(owner))
return FALSE
return TRUE // All we care about is destination. Anything you click is fine.
@@ -43,13 +43,13 @@
// Are either tiles WALLS?
var/turf/from_turf = get_turf(owner)
var/this_dir // = get_dir(from_turf, target_turf)
for (var/i=1 to 2)
for(var/i=1 to 2)
// Keep Prev Direction if we've reached final turf
if (from_turf != final_turf)
if(from_turf != final_turf)
this_dir = get_dir(from_turf, final_turf) // Recalculate dir so we don't overshoot on a diagonal.
from_turf = get_step(from_turf, this_dir)
// ERROR! Wall!
if (iswallturf(from_turf))
if(iswallturf(from_turf))
if (display_error)
var/wallwarning = (i == 1) ? "in the way" : "at your destination"
to_chat(owner, "<span class='warning'>There is a solid wall [wallwarning].</span>")
@@ -84,7 +84,7 @@
user.next_move = world.time + mist_delay
user.Stun(mist_delay, ignore_canstun = TRUE)
user.notransform = TRUE
user.density = 0
user.density = FALSE
var/invis_was = user.invisibility
user.invisibility = INVISIBILITY_MAXIMUM
@@ -94,7 +94,7 @@
sleep(mist_delay / 2)
// Move & Freeze
if (isturf(target_turf))
if(isturf(target_turf))
do_teleport(owner, target_turf, no_effects=TRUE, channel = TELEPORT_CHANNEL_QUANTUM) // in teleport.dm?
user.next_move = world.time + mist_delay / 2
user.Stun(mist_delay / 2, ignore_canstun = TRUE)

View File

@@ -93,7 +93,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.species?.id)
H.dna.features = random_features(H.dna.species?.id, H.gender)
// Apply Appearance
H.update_body(TRUE) // Outfit and underwear, also body and privates.

View File

@@ -21,7 +21,7 @@
to_chat(user, "<span class='notice'>Our muscles tense and strengthen.</span>")
changeling.chem_recharge_slowdown += 0.5
else
user.remove_movespeed_modifier(MOVESPEED_ID_CHANGELING_MUSCLES)
user.remove_movespeed_modifier(/datum/movespeed_modifier/strained_muscles)
to_chat(user, "<span class='notice'>Our muscles relax.</span>")
changeling.chem_recharge_slowdown -= 0.5
if(stacks >= 20)
@@ -36,12 +36,12 @@
/obj/effect/proc_holder/changeling/strained_muscles/proc/muscle_loop(mob/living/carbon/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
while(active)
user.add_movespeed_modifier(MOVESPEED_ID_CHANGELING_MUSCLES, update=TRUE, priority=100, multiplicative_slowdown=-1, blacklisted_movetypes=(FLYING|FLOATING))
user.add_movespeed_modifier(/datum/movespeed_modifier/strained_muscles)
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.DefaultCombatKnockdown(40)
user.remove_movespeed_modifier(MOVESPEED_ID_CHANGELING_MUSCLES)
user.remove_movespeed_modifier(/datum/movespeed_modifier/strained_muscles)
changeling.chem_recharge_slowdown -= 0.5
break

View File

@@ -539,7 +539,7 @@
if(SSshuttle.emergency.mode == SHUTTLE_CALL)
var/cursetime = 1800
var/timer = SSshuttle.emergency.timeLeft(1) + cursetime
var/security_num = seclevel2num(get_security_level())
var/security_num = SECLEVEL2NUM(NUM2SECLEVEL(GLOB.security_level))
var/set_coefficient = 1
switch(security_num)
if(SEC_LEVEL_GREEN)

View File

@@ -41,7 +41,7 @@
STOP_PROCESSING(SSobj, core)
update_icon()
GLOB.poi_list |= src
previous_level = get_security_level()
previous_level = NUM2SECLEVEL(GLOB.security_level)
/obj/machinery/nuclearbomb/Destroy()
safety = FALSE
@@ -419,7 +419,7 @@
return
timing = !timing
if(timing)
previous_level = get_security_level()
previous_level = NUM2SECLEVEL(GLOB.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)

View File

@@ -64,8 +64,8 @@
/mob/living/simple_animal/slaughter/phasein()
. = ..()
add_movespeed_modifier(MOVESPEED_ID_SLAUGHTER, update=TRUE, priority=100, multiplicative_slowdown=-1)
addtimer(CALLBACK(src, .proc/remove_movespeed_modifier, MOVESPEED_ID_SLAUGHTER, TRUE), 6 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
add_movespeed_modifier(/datum/movespeed_modifier/slaughter)
addtimer(CALLBACK(src, .proc/remove_movespeed_modifier, /datum/movespeed_modifier/slaughter), 6 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
//The loot from killing a slaughter demon - can be consumed to allow the user to blood crawl

View File

@@ -48,7 +48,7 @@
var/datum/traitor_class/class = GLOB.traitor_classes[C]
var/weight = LOGISTIC_FUNCTION(1.5*class.weight,chaos_weight,class.chaos,0)
weights[C] = weight * 1000
var/choice = pickweightAllowZero(weights)
var/choice = pickweight(weights, 0)
if(!choice)
choice = TRAITOR_HUMAN // it's an "easter egg"
var/datum/traitor_class/actual_class = GLOB.traitor_classes[choice]

View File

@@ -159,7 +159,7 @@
/obj/item/scrying/attack_self(mob/user)
to_chat(user, "<span class='notice'>You can see...everything!</span>")
visible_message("<span class='danger'>[user] stares into [src], their eyes glazing over.</span>")
user.ghostize(1)
user.ghostize(1, voluntary = TRUE)
/////////////////////////////////////////Necromantic Stone///////////////////

View File

@@ -294,7 +294,7 @@
name = "Staff of Change"
desc = "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself."
item_path = /obj/item/gun/magic/staff/change
dynamic_requirement = 60
dynamic_requirement = 200
/datum/spellbook_entry/item/staffanimation
name = "Staff of Animation"
@@ -361,7 +361,7 @@
desc = "A collection of wands that allow for a wide variety of utility. Wands have a limited number of charges, so be conservative in use. Comes in a handy belt."
item_path = /obj/item/storage/belt/wands/full
category = "Defensive"
dynamic_requirement = 60
dynamic_requirement = 200
/datum/spellbook_entry/item/armor
name = "Mastercrafted Armor Set"
@@ -386,7 +386,7 @@
name = "Plasma Fist"
desc = "A forbidden martial art designed on the surging power of plasma. Use it to harness the ancient power."
item_path = /obj/item/book/granter/martial/plasma_fist
cost = 3
cost = 2
/datum/spellbook_entry/item/guardian
name = "Guardian Deck"

View File

@@ -225,6 +225,7 @@
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE))
return
target_temperature = min_temperature
to_chat(user,"<span class='notice'>You minimize the temperature on the [src].</span>")
investigate_log("was set to [target_temperature] K by [key_name(usr)]", INVESTIGATE_ATMOS)
message_admins("[src.name] was minimized by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]")
return TRUE
@@ -257,6 +258,7 @@
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE))
return
target_temperature = max_temperature
to_chat(user,"<span class='notice'>You maximize the temperature on the [src].</span>")
investigate_log("was set to [target_temperature] K by [key_name(usr)]", INVESTIGATE_ATMOS)
message_admins("[src.name] was maximized by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]")
return TRUE

View File

@@ -1,15 +1,19 @@
// How much "space" we give the edge of the map
GLOBAL_LIST_INIT(potentialRandomZlevels, generateMapList(filename = "[global.config.directory]/awaymissionconfig.txt"))
/proc/createRandomZlevel()
if(GLOB.awaydestinations.len) //crude, but it saves another var!
/proc/createRandomZlevel(name = AWAY_MISSION_NAME, list/traits = list(ZTRAIT_AWAY = TRUE), list/potential_levels = GLOB.potential_away_levels)
if(GLOB.random_zlevels_generated[name])
stack_trace("[name] level already generated.")
return
if(!length(potential_levels))
stack_trace("No potential [name] level to load has been found.")
return
if(GLOB.potentialRandomZlevels && GLOB.potentialRandomZlevels.len)
to_chat(world, "<span class='boldannounce'>Loading away mission...</span>")
var/map = pick(GLOB.potentialRandomZlevels)
load_new_z_level(map, "Away Mission")
to_chat(world, "<span class='boldannounce'>Away mission loaded.</span>")
var/start_time = REALTIMEOFDAY
var/map = pick(potential_levels)
if(!load_new_z_level(map, name, traits))
INIT_ANNOUNCE("Failed to load [name]! map filepath: [map]!")
return
INIT_ANNOUNCE("Loaded [name] in [(REALTIMEOFDAY - start_time)/10]s!")
GLOB.random_zlevels_generated[name] = TRUE
/proc/reset_gateway_spawns(reset = FALSE)
for(var/obj/machinery/gateway/G in world)

View File

@@ -262,7 +262,15 @@
return
random_look(owner)
/obj/item/clothing/under/chameleon
// Forgive me for my sins...
#define CHAMELEON_CLOTHING_DEFINE(path) \
##path/syndicate/Initialize(mapload){\
. = ..();\
AddComponent(/datum/component/identification/syndicate, ID_COMPONENT_DEL_ON_IDENTIFY, ID_COMPONENT_EFFECT_NO_ACTIONS, ID_COMPONENT_IDENTIFY_WITH_DECONSTRUCTOR);\
}\
##path
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/under/chameleon)
//starts off as black
name = "black jumpsuit"
icon_state = "black"
@@ -300,7 +308,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/clothing/suit/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/suit/chameleon)
name = "armor"
desc = "A slim armored vest that protects against most types of damage."
icon_state = "armor"
@@ -329,7 +337,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/clothing/glasses/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/glasses/chameleon)
name = "Optical Meson Scanner"
desc = "Used by engineering and mining staff to see basic structural and terrain layouts through walls, regardless of lighting condition."
icon_state = "meson"
@@ -357,7 +365,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/clothing/gloves/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/gloves/chameleon)
desc = "These gloves will protect the wearer from electric shock."
name = "insulated gloves"
icon_state = "yellow"
@@ -368,6 +376,9 @@
var/datum/action/item_action/chameleon/change/chameleon_action
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/gloves/chameleon/insulated)
siemens_coefficient = 0
/obj/item/clothing/gloves/chameleon/Initialize()
. = ..()
chameleon_action = new(src)
@@ -386,7 +397,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/clothing/head/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/head/chameleon)
name = "grey cap"
desc = "It's a baseball hat in a tasteful grey colour."
icon_state = "greysoft"
@@ -429,7 +440,7 @@
var/datum/action/item_action/chameleon/drone/randomise/randomise_action = new(src)
randomise_action.UpdateButtonIcon()
/obj/item/clothing/mask/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/mask/chameleon)
name = "gas mask"
desc = "A face-covering mask that can be connected to an air supply. While good for concealing your identity, it isn't good for blocking gas flow." //More accurate
icon_state = "gas_alt"
@@ -486,7 +497,7 @@
/obj/item/clothing/mask/chameleon/drone/attack_self(mob/user)
to_chat(user, "<span class='notice'>[src] does not have a voice changer.</span>")
/obj/item/clothing/shoes/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/shoes/chameleon)
name = "black shoes"
icon_state = "black"
desc = "A pair of black shoes."
@@ -511,7 +522,7 @@
return
chameleon_action.emp_randomise()
/obj/item/clothing/shoes/chameleon/noslip
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/shoes/chameleon/noslip)
name = "black shoes"
icon_state = "black"
desc = "A pair of black shoes."
@@ -521,7 +532,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/storage/backpack/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/storage/backpack/chameleon)
name = "backpack"
var/datum/action/item_action/chameleon/change/chameleon_action
@@ -542,7 +553,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/storage/belt/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/storage/belt/chameleon)
name = "toolbelt"
desc = "Holds tools."
var/datum/action/item_action/chameleon/change/chameleon_action
@@ -570,7 +581,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/radio/headset/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/radio/headset/chameleon)
name = "radio headset"
var/datum/action/item_action/chameleon/change/chameleon_action
@@ -591,7 +602,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/pda/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/pda/chameleon)
name = "PDA"
var/datum/action/item_action/chameleon/change/pda/chameleon_action
@@ -613,7 +624,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/stamp/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/stamp/chameleon)
var/datum/action/item_action/chameleon/change/chameleon_action
/obj/item/stamp/chameleon/Initialize()
@@ -627,7 +638,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/clothing/neck/cloak/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/neck/cloak/chameleon)
name = "black tie"
desc = "A neosilk clip-on tie."
icon = 'icons/obj/clothing/neck.dmi'

View File

@@ -1,4 +1,4 @@
/obj/item/clothing/under/dress/skirt
/obj/item/clothing/under/dress
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
/obj/item/clothing/under/dress/skirt
@@ -182,7 +182,6 @@
icon_state = "bride_white"
item_state = "bride_white"
can_adjust = FALSE
mutantrace_variation = NONE
/obj/item/clothing/under/dress/wedding/orange
name = "orange wedding dress"
@@ -213,7 +212,6 @@
desc = "A fancy skirt made with polychromic threads."
icon_state = "polyskirt"
item_state = "rainbow"
mutantrace_variation = NONE
var/list/poly_colors = list("#FFFFFF", "#F08080", "#808080")
/obj/item/clothing/under/dress/skirt/polychromic/ComponentInitialize()
@@ -226,5 +224,4 @@
icon_state = "polypleat"
item_state = "rainbow"
body_parts_covered = CHEST|GROIN|ARMS
mutantrace_variation = NONE
poly_colors = list("#8CC6FF", "#808080", "#FF3535")

View File

@@ -57,8 +57,7 @@
icon_state = "tactifool"
item_state = "bl_suit"
has_sensor = TRUE
mutantrace_variation = NONE
armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0)
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
/obj/item/clothing/under/syndicate/sniper
name = "Tactical turtleneck suit"

View File

@@ -22,7 +22,7 @@
if(turfs.len) //Pick a turf to spawn at if we can
var/turf/T = pick(turfs)
new /datum/spacevine_controller(T) //spawn a controller at turf
new /datum/spacevine_controller(T, pick(subtypesof(/datum/spacevine_mutation)), rand(30,100), rand(5,10), src) //spawn a controller at turf with randomized stats and a single random mutation
/datum/spacevine_mutation
@@ -227,13 +227,13 @@
quality = NEGATIVE
/datum/spacevine_mutation/thorns/on_cross(obj/structure/spacevine/holder, mob/living/crosser)
if(prob(severity) && istype(crosser) && !isvineimmune(holder))
if(prob(severity) && istype(crosser) && !isvineimmune(crosser))
var/mob/living/M = crosser
M.adjustBruteLoss(5)
to_chat(M, "<span class='alert'>You cut yourself on the thorny vines.</span>")
/datum/spacevine_mutation/thorns/on_hit(obj/structure/spacevine/holder, mob/living/hitter, obj/item/I, expected_damage)
if(prob(severity) && istype(hitter) && !isvineimmune(holder))
if(prob(severity) && istype(hitter) && !isvineimmune(hitter))
var/mob/living/M = hitter
M.adjustBruteLoss(5)
to_chat(M, "<span class='alert'>You cut yourself on the thorny vines.</span>")
@@ -251,7 +251,7 @@
holder.obj_integrity = holder.max_integrity
/datum/spacevine_mutation/woodening/on_hit(obj/structure/spacevine/holder, mob/living/hitter, obj/item/I, expected_damage)
if(I.get_sharpness())
if(I?.get_sharpness())
. = expected_damage * 0.5
else
. = expected_damage
@@ -344,16 +344,17 @@
switch(damage_type)
if(BRUTE)
if(damage_amount)
playsound(src, 'sound/weapons/slash.ogg', 50, 1)
playsound(src, 'sound/weapons/slash.ogg', 50, TRUE)
else
playsound(src, 'sound/weapons/tap.ogg', 50, 1)
playsound(src, 'sound/weapons/tap.ogg', 50, TRUE)
if(BURN)
playsound(src.loc, 'sound/items/welder.ogg', 100, 1)
playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE)
/obj/structure/spacevine/Crossed(mob/crosser)
if(isliving(crosser))
for(var/datum/spacevine_mutation/SM in mutations)
SM.on_cross(src, crosser)
/obj/structure/spacevine/Crossed(atom/movable/AM)
if(!isliving(AM))
return
for(var/datum/spacevine_mutation/SM in mutations)
SM.on_cross(src, AM)
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/structure/spacevine/attack_hand(mob/user)
@@ -378,10 +379,9 @@
var/list/vine_mutations_list
var/mutativeness = 1
/datum/spacevine_controller/New(turf/location, list/muts, potency, production)
/datum/spacevine_controller/New(turf/location, list/muts, potency, production, datum/round_event/event = null)
vines = list()
growth_queue = list()
spawn_spacevine_piece(location, null, muts)
START_PROCESSING(SSobj, src)
vine_mutations_list = list()
init_subtypes(/datum/spacevine_mutation/, vine_mutations_list)
@@ -428,6 +428,7 @@
for(var/datum/spacevine_mutation/SM in SV.mutations)
SM.on_birth(SV)
location.Entered(SV)
return SV
/datum/spacevine_controller/proc/VineDestroyed(obj/structure/spacevine/S)
S.master = null
@@ -531,14 +532,13 @@
qdel(src)
/obj/structure/spacevine/CanPass(atom/movable/mover, turf/target)
. = ..()
if(isvineimmune(mover))
. = TRUE
else
. = ..()
return TRUE
/proc/isvineimmune(atom/A)
. = FALSE
if(isliving(A))
var/mob/living/M = A
if(("vines" in M.faction) || ("plants" in M.faction))
. = TRUE
return TRUE
return FALSE

View File

@@ -23,6 +23,9 @@
/datum/round_event/wormholes/start()
for(var/turf/open/floor/T in world)
if(is_station_level(T.z))
var/area/A = get_area(T)
if(A.outdoors)
continue
pick_turfs += T
for(var/i = 1, i <= number_of_wormholes, i++)

View File

@@ -396,6 +396,16 @@
list_reagents = list(/datum/reagent/consumable/orangejuice = 100)
foodtype = FRUIT| BREAKFAST
/obj/item/reagent_containers/food/drinks/bottle/bio_carton
name = "small carton box"
desc = "A small biodegradable carton box made from plant biomatter."
icon_state = "eco_box"
item_state = "carton"
lefthand_file = 'icons/mob/inhands/equipment/kitchen_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/kitchen_righthand.dmi'
volume = 50
isGlass = FALSE
/obj/item/reagent_containers/food/drinks/bottle/cream
name = "milk cream"
desc = "It's cream. Made from milk. What else did you think you'd find in there?"

View File

@@ -40,14 +40,14 @@
updateUsrDialog()
/obj/machinery/biogenerator/RefreshParts()
var/E = 0
var/P = 0
var/max_storage = 40
var/E = 0.5
var/P = 0.5
var/max_storage = 20
for(var/obj/item/stock_parts/matter_bin/B in component_parts)
P += B.rating
max_storage = 40 * B.rating
P += B.rating * 0.5
max_storage = max(20 * B.rating, max_storage)
for(var/obj/item/stock_parts/manipulator/M in component_parts)
E += M.rating
E += M.rating * 0.5
efficiency = E
productivity = P
max_items = max_storage
@@ -196,7 +196,7 @@
dat += "<A href='?src=[REF(src)];create=[D.id];amount=5'>x5</A>"
if(ispath(D.build_path, /obj/item/stack))
dat += "<A href='?src=[REF(src)];create=[D.id];amount=10'>x10</A>"
dat += "([D.materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]/efficiency])<br>"
dat += "([CEILING(D.materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]/efficiency, 1)])<br>"
dat += "</div>"
else
dat += "<div class='statusDisplay'>No container inside, please insert container.</div>"
@@ -214,12 +214,16 @@
to_chat(usr, "<span class='warning'>The biogenerator is in the process of working.</span>")
return
var/S = 0
var/total = 0
for(var/obj/item/reagent_containers/food/snacks/grown/I in contents)
S += 5
if(I.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment) < 0.1)
points += 1*productivity
else points += I.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment)*10*productivity
var/nutri_amount = I.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment)
if(nutri_amount < 0.1)
total += 1*productivity
else
total += nutri_amount*10*productivity
qdel(I)
points += round(total)
if(S)
processing = TRUE
update_icon()
@@ -235,12 +239,13 @@
/obj/machinery/biogenerator/proc/check_cost(list/materials, multiplier = 1, remove_points = TRUE)
if(materials.len != 1 || materials[1] != SSmaterials.GetMaterialRef(/datum/material/biomass))
return FALSE
if (materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]*multiplier/efficiency > points)
var/cost = CEILING(materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]*multiplier/efficiency, 1)
if (cost > points)
menustat = "nopoints"
return FALSE
else
if(remove_points)
points -= materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]*multiplier/efficiency
points -= cost
update_icon()
updateUsrDialog()
return TRUE

View File

@@ -79,7 +79,9 @@
for(var/I in assembly_components)
var/obj/item/integrated_circuit/IC = I
. += IC.external_examine(user)
var/text = IC.external_examine(user)
if(text)
. += text
if(opened)
interact(user)

View File

@@ -36,7 +36,9 @@ a creative player the means to solve many problems. Circuits are held inside an
/obj/item/integrated_circuit/examine(mob/user)
interact(user)
. = ..()
. += external_examine(user)
var/text = external_examine(user)
if(text)
. += text
// Can be called via electronic_assembly/attackby()
/obj/item/integrated_circuit/proc/additem(var/obj/item/I, var/mob/living/user)
@@ -57,7 +59,7 @@ a creative player the means to solve many problems. Circuits are held inside an
var/datum/integrated_io/activate/A = activators[k]
if(A.linked.len)
to_chat(user, "The '[A]' is connected to [A.get_linked_to_desc()].")
any_examine(user)
to_chat(user, any_examine(user))
interact(user)
// This should be used when someone is examining from an 'outside' perspective, e.g. reading a screen or LED.

View File

@@ -66,6 +66,11 @@
// How much threat this job is worth in dynamic. Is subtracted if the player's not an antag, added if they are.
var/threat = 0
/// Starting skill levels.
var/list/starting_skills
/// Skill affinities to set
var/list/skill_affinities
//Only override this proc
//H is usually a human unless an /equip override transformed it
/datum/job/proc/after_spawn(mob/living/H, mob/M, latejoin = FALSE)
@@ -142,7 +147,6 @@
return TRUE //Available in 0 days = available right now = player is old enough to play.
return FALSE
/datum/job/proc/available_in_days(client/C)
if(!C)
return 0
@@ -166,6 +170,17 @@
/datum/job/proc/radio_help_message(mob/M)
to_chat(M, "<b>Prefix your message with :h to speak on your department's radio. To see other prefixes, look closely at your headset.</b>")
/datum/job/proc/standard_assign_skills(datum/mind/M)
if(!starting_skills)
return
for(var/skill in starting_skills)
M.skill_holder.boost_skill_value_to(skill, starting_skills[skill])
// do wipe affinities though
M.skill_holder.skill_affinities = list()
for(var/skill in skill_affinities)
M.skill_holder.skill_affinities[skill] = skill_affinities[skill]
UNSETEMPTY(M.skill_holder.skill_affinities) //if we didn't set any.
/datum/outfit/job
name = "Standard Gear"

View File

@@ -19,6 +19,9 @@
display_order = JOB_DISPLAY_ORDER_CHEMIST
threat = 1.5
starting_skills = list(/datum/skill/numerical/surgery = STARTING_SKILL_SURGERY_MEDICAL)
skill_affinities = list(/datum/skill/numerical/surgery = STARTING_SKILL_AFFINITY_SURGERY_MEDICAL)
/datum/outfit/job/chemist
name = "Chemist"
jobtype = /datum/job/chemist

View File

@@ -29,6 +29,9 @@
blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/insanity)
threat = 2
starting_skills = list(/datum/skill/numerical/surgery = STARTING_SKILL_SURGERY_MEDICAL)
skill_affinities = list(/datum/skill/numerical/surgery = STARTING_SKILL_AFFINITY_SURGERY_MEDICAL)
/datum/outfit/job/cmo
name = "Chief Medical Officer"
jobtype = /datum/job/cmo

View File

@@ -19,6 +19,8 @@
display_order = JOB_DISPLAY_ORDER_GENETICIST
threat = 1.5
starting_skills = list(/datum/skill/numerical/surgery = STARTING_SKILL_SURGERY_MEDICAL)
/datum/outfit/job/geneticist
name = "Geneticist"
jobtype = /datum/job/geneticist

View File

@@ -17,6 +17,9 @@
display_order = JOB_DISPLAY_ORDER_MEDICAL_DOCTOR
threat = 0.5
starting_skills = list(/datum/skill/numerical/surgery = STARTING_SKILL_SURGERY_MEDICAL)
skill_affinities = list(/datum/skill/numerical/surgery = STARTING_SKILL_AFFINITY_SURGERY_MEDICAL)
/datum/outfit/job/doctor
name = "Medical Doctor"
jobtype = /datum/job/doctor

View File

@@ -19,6 +19,9 @@
threat = 0.5
starting_skills = list(/datum/skill/numerical/surgery = STARTING_SKILL_SURGERY_MEDICAL)
skill_affinities = list(/datum/skill/numerical/surgery = STARTING_SKILL_AFFINITY_SURGERY_MEDICAL)
/datum/outfit/job/paramedic
name = "Paramedic"
jobtype = /datum/job/paramedic

View File

@@ -20,6 +20,9 @@
threat = 1.5
starting_skills = list(/datum/skill/numerical/surgery = STARTING_SKILL_SURGERY_MEDICAL)
skill_affinities = list(/datum/skill/numerical/surgery = STARTING_SKILL_AFFINITY_SURGERY_MEDICAL)
/datum/outfit/job/virologist
name = "Virologist"
jobtype = /datum/job/virologist

View File

@@ -55,11 +55,11 @@
SSmachines.setup_template_powernets(cables)
SSair.setup_template_machinery(atmos_machines)
/datum/map_template/proc/load_new_z()
/datum/map_template/proc/load_new_z(list/traits = list(ZTRAIT_AWAY = TRUE))
var/x = round((world.maxx - width)/2)
var/y = round((world.maxy - height)/2)
var/datum/space_level/level = SSmapping.add_new_zlevel(name, list(ZTRAIT_AWAY = TRUE))
var/datum/space_level/level = SSmapping.add_new_zlevel(name, traits)
var/datum/parsed_map/parsed = load_map(file(mappath), x, y, level.z_value, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE)
var/list/bounds = parsed.bounds
if(!bounds)
@@ -121,6 +121,6 @@
//for your ever biggening badminnery kevinz000
//❤ - Cyberboss
/proc/load_new_z_level(var/file, var/name)
/proc/load_new_z_level(file, name, list/traits)
var/datum/map_template/template = new(file, name)
template.load_new_z()
return template.load_new_z(traits)

View File

@@ -65,7 +65,7 @@
qdel(src)
/obj/item/organ/regenerative_core/on_life()
..()
. = ..()
if(owner.health < owner.crit_threshold)
ui_action_click()

View File

@@ -392,6 +392,8 @@
character.update_parallax_teleport()
job.standard_assign_skills(character.mind)
SSticker.minds += character.mind
var/mob/living/carbon/human/humanc

View File

@@ -23,11 +23,7 @@
if(!pref_species)
var/rando_race = pick(GLOB.roundstart_races)
pref_species = new rando_race()
features = random_features(pref_species?.id)
if(gender == MALE || gender != FEMALE)
features["body_model"] = gender
else if(gender == PLURAL)
features["body_model"] = pick(MALE,FEMALE)
features = random_features(pref_species?.id, gender)
age = rand(AGE_MIN,AGE_MAX)
/datum/preferences/proc/update_preview_icon(equip_job = TRUE)

View File

@@ -937,3 +937,20 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
spawners_menu = new(src)
spawners_menu.ui_interact(src)
/mob/dead/observer/verb/game_info()
set name = "Game info"
set desc = "Shows various info relating to the game mode, antagonists etc."
set category = "Ghost"
if(!started_as_observer && can_reenter_corpse)
to_chat(src, "You cannot see this info unless you are an observer or you've chosen Do Not Resuscitate!")
return
var/list/stuff = list("[SSticker.mode.name]")
stuff += "Antagonists:\n"
for(var/datum/antagonist/A in GLOB.antagonists)
if(A.owner)
stuff += "[A.owner] the [A.name]"
var/ghost_info = SSticker.mode.ghost_info()
if(ghost_info)
stuff += ghost_info
to_chat(src,stuff.Join("\n"))

View File

@@ -245,23 +245,17 @@
. = adjusted_amount
*/
/obj/item/organ/brain/on_life()
if(damage >= BRAIN_DAMAGE_DEATH) //rip
to_chat(owner, "<span class='userdanger'>The last spark of life in your brain fizzles out...</span>")
owner.death()
brain_death = TRUE
return
..()
/obj/item/organ/brain/on_death()
if(damage <= BRAIN_DAMAGE_DEATH) //rip
brain_death = FALSE
..()
/obj/item/organ/brain/applyOrganDamage(var/d, var/maximum = maxHealth)
..()
. = ..()
if(!. || !owner)
return
if(damage >= BRAIN_DAMAGE_DEATH) //rip
if(owner.stat != DEAD)
to_chat(owner, "<span class='userdanger'>The last spark of life in your brain fizzles out...</span>")
owner.death()
brain_death = TRUE
else
brain_death = FALSE
/obj/item/organ/brain/check_damage_thresholds(mob/M)
. = ..()

View File

@@ -12,6 +12,13 @@
bubble_icon = "alien"
type_of_meat = /obj/item/reagent_containers/food/snacks/meat/slab/xeno
/// How much brute damage without armor piercing they do against mobs in melee
var/meleeSlashHumanPower = 20
/// How much power they have for DefaultCombatKnockdown when attacking humans
var/meleeKnockdownPower = 100
/// How much brute damage they do to simple animals
var/meleeSlashSAPower = 35
var/obj/item/card/id/wear_id = null // Fix for station bounced radios -- Skie
var/has_fine_manipulation = 0
var/move_delay_add = 0 // movement delay to add

View File

@@ -4,6 +4,7 @@
maxHealth = 125
health = 125
icon_state = "aliend"
meleeKnockdownPower = 80
/mob/living/carbon/alien/humanoid/drone/Initialize()
AddAbility(new/obj/effect/proc_holder/alien/evolve(null))

View File

@@ -1,9 +1,12 @@
/mob/living/carbon/alien/humanoid/hunter
name = "alien hunter"
caste = "h"
maxHealth = 125
health = 125
maxHealth = 170
health = 170
icon_state = "alienh"
meleeKnockdownPower = 75
meleeSlashHumanPower = 20
meleeSlashSAPower = 45
var/obj/screen/leap_icon = null
/mob/living/carbon/alien/humanoid/hunter/create_internal_organs()
@@ -68,6 +71,7 @@
else
L.visible_message("<span class ='danger'>[src] pounces on [L]!</span>", "<span class ='userdanger'>[src] pounces on you!</span>")
L.DefaultCombatKnockdown(100)
L.Stagger(4 SECONDS)
sleep(2)//Runtime prevention (infinite bump() calls on hulks)
step_towards(src,L)

View File

@@ -1,9 +1,10 @@
/mob/living/carbon/alien/humanoid/sentinel
name = "alien sentinel"
caste = "s"
maxHealth = 150
health = 150
maxHealth = 140
health = 140
icon_state = "aliens"
meleeSlashHumanPower = 15
/mob/living/carbon/alien/humanoid/sentinel/Initialize()
AddAbility(new /obj/effect/proc_holder/alien/sneak)

View File

@@ -112,7 +112,6 @@
return A
return FALSE
/mob/living/carbon/alien/humanoid/check_breath(datum/gas_mixture/breath)
if(breath && breath.total_moles() > 0 && !sneaking)
playsound(get_turf(src), pick('sound/voice/lowHiss2.ogg', 'sound/voice/lowHiss3.ogg', 'sound/voice/lowHiss4.ogg'), 50, 0, -5)

View File

@@ -1,4 +1,3 @@
/mob/living/carbon/alien/humanoid/grabbedby(mob/living/carbon/user, supress_message = 0)
if(user == src && pulling && grab_state >= GRAB_AGGRESSIVE && !pulling.anchored && iscarbon(pulling))
devour_mob(pulling, devour_time = 60)

View File

@@ -1,5 +1,3 @@
/mob/living/carbon/alien/humanoid/proc/adjust_body_temperature(current, loc_temp, boost)
var/temperature = current
var/difference = abs(current-loc_temp) //get difference

View File

@@ -11,6 +11,10 @@
pressure_resistance = 200 //Because big, stompy xenos should not be blown around like paper.
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/xeno = 20, /obj/item/stack/sheet/animalhide/xeno = 3)
meleeKnockdownPower = 125
meleeSlashHumanPower = 30
meleeSlashSAPower = 60
var/alt_inhands_file = 'icons/mob/alienqueen.dmi'
/mob/living/carbon/alien/humanoid/royal/can_inject(mob/user, error_msg, target_zone, penetrate_thick = FALSE, bypass_immunity = FALSE)

View File

@@ -6,6 +6,7 @@
mob_size = MOB_SIZE_SMALL
density = FALSE
hud_type = /datum/hud/larva
mouse_opacity = MOUSE_OPACITY_OPAQUE
maxHealth = 25
health = 25

View File

@@ -77,6 +77,9 @@
alien_powers = list(/obj/effect/proc_holder/alien/transfer)
/obj/item/organ/alien/plasmavessel/on_life()
. = ..()
if(!.)
return
//If there are alien weeds on the ground then heal if needed or give some plasma
if(locate(/obj/structure/alien/weeds) in owner.loc)
if(owner.health >= owner.maxHealth)

View File

@@ -23,6 +23,8 @@
/obj/item/organ/body_egg/alien_embryo/on_life()
. = ..()
if(!owner)
return
switch(stage)
if(2, 3)
if(prob(2))

View File

@@ -7,6 +7,7 @@
update_body_parts() //to update the carbon's new bodyparts appearance
GLOB.carbon_list += src
blood_volume = (BLOOD_VOLUME_NORMAL * blood_ratio)
add_movespeed_modifier(/datum/movespeed_modifier/carbon_crawling)
/mob/living/carbon/Destroy()
//This must be done first, so the mob ghosts correctly before DNA etc is nulled
@@ -574,9 +575,9 @@
become_husk("burn")
med_hud_set_health()
if(stat == SOFT_CRIT)
add_movespeed_modifier(MOVESPEED_ID_CARBON_SOFTCRIT, TRUE, multiplicative_slowdown = SOFTCRIT_ADD_SLOWDOWN)
add_movespeed_modifier(/datum/movespeed_modifier/carbon_softcrit)
else
remove_movespeed_modifier(MOVESPEED_ID_CARBON_SOFTCRIT, TRUE)
remove_movespeed_modifier(/datum/movespeed_modifier/carbon_softcrit)
/mob/living/carbon/update_stamina()
var/stam = getStaminaLoss()

View File

@@ -38,7 +38,7 @@
adjustCloneLoss(damage_amount, forced = forced)
if(STAMINA)
if(BP)
if(damage > 0 ? BP.receive_damage(0, 0, damage_amount) : BP.heal_damage(0, 0, abs(damage_amount)))
if(damage > 0 ? BP.receive_damage(0, 0, damage_amount) : BP.heal_damage(0, 0, abs(damage_amount), FALSE, FALSE))
update_damage_overlays()
else
adjustStaminaLoss(damage_amount, forced = forced)

View File

@@ -1041,7 +1041,7 @@
return FALSE
/mob/living/carbon/human/proc/clear_shove_slowdown()
remove_movespeed_modifier(MOVESPEED_ID_SHOVE)
remove_movespeed_modifier(/datum/movespeed_modifier/shove)
var/active_item = get_active_held_item()
if(is_type_in_typecache(active_item, GLOB.shove_disarming_types))
visible_message("<span class='warning'>[src.name] regains their grip on \the [active_item]!</span>", "<span class='warning'>You regain your grip on \the [active_item]</span>", null, COMBAT_MESSAGE_RANGE)
@@ -1050,17 +1050,17 @@
. = ..()
if(HAS_TRAIT(src, TRAIT_IGNORESLOWDOWN))
remove_movespeed_modifier(MOVESPEED_ID_DAMAGE_SLOWDOWN)
remove_movespeed_modifier(MOVESPEED_ID_DAMAGE_SLOWDOWN_FLYING)
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown)
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying)
return
var/stambufferinfluence = (bufferedstam*(100/stambuffer))*0.2 //CIT CHANGE - makes stamina buffer influence movedelay
var/health_deficiency = ((100 + stambufferinfluence) - health + (getStaminaLoss()*0.75))//CIT CHANGE - reduces the impact of staminaloss and makes stamina buffer influence it
if(health_deficiency >= 40)
add_movespeed_modifier(MOVESPEED_ID_DAMAGE_SLOWDOWN, override = TRUE, multiplicative_slowdown = ((health_deficiency-39) / 75), blacklisted_movetypes = FLOATING|FLYING)
add_movespeed_modifier(MOVESPEED_ID_DAMAGE_SLOWDOWN_FLYING, override = TRUE, multiplicative_slowdown = ((health_deficiency-39) / 25), movetypes = FLYING, blacklisted_movetypes = FLOATING)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown, TRUE, (health_deficiency-39) / 75)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying, TRUE, (health_deficiency-39) / 25)
else
remove_movespeed_modifier(MOVESPEED_ID_DAMAGE_SLOWDOWN)
remove_movespeed_modifier(MOVESPEED_ID_DAMAGE_SLOWDOWN_FLYING)
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown)
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying)
/mob/living/carbon/human/do_after_coefficent()
. = ..()

View File

@@ -155,7 +155,7 @@
if(M.a_intent == INTENT_HARM)
if (w_uniform)
w_uniform.add_fingerprint(M)
var/damage = prob(90) ? 20 : 0
var/damage = prob(90) ? M.meleeSlashHumanPower : 0
if(!damage)
playsound(loc, 'sound/weapons/slashmiss.ogg', 50, 1, -1)
visible_message("<span class='danger'>[M] has lunged at [src]!</span>", \
@@ -182,10 +182,7 @@
"<span class='userdanger'>[M] disarmed [src]!</span>")
else
playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1)
if(!lying) //CITADEL EDIT
DefaultCombatKnockdown(100, TRUE, FALSE, 30, 25)
else
DefaultCombatKnockdown(100)
DefaultCombatKnockdown(M.meleeKnockdownPower)
log_combat(M, src, "tackled")
visible_message("<span class='danger'>[M] has tackled down [src]!</span>", \
"<span class='userdanger'>[M] has tackled down [src]!</span>")

View File

@@ -1,11 +1,13 @@
/mob/living/carbon/human/get_movespeed_modifiers()
var/list/considering = ..()
. = considering
if(HAS_TRAIT(src, TRAIT_IGNORESLOWDOWN))
for(var/id in .)
var/list/data = .[id]
if(data[MOVESPEED_DATA_INDEX_FLAGS] & IGNORE_NOSLOW)
.[id] = data
. = list()
for(var/id in considering)
var/datum/movespeed_modifier/M = considering[id]
if(M.flags & IGNORE_NOSLOW || M.multiplicative_slowdown < 0)
.[id] = M
return
return considering
/mob/living/carbon/human/movement_delay()
. = ..()

View File

@@ -331,7 +331,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(mutant_bodyparts["meat_type"]) //I can't believe it's come to the meat
H.type_of_meat = GLOB.meat_types[H.dna.features["meat_type"]]
C.add_movespeed_modifier(MOVESPEED_ID_SPECIES, TRUE, 100, override=TRUE, multiplicative_slowdown=speedmod, movetypes=(~FLYING))
C.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/species, TRUE, multiplicative_slowdown = speedmod)
SEND_SIGNAL(C, COMSIG_SPECIES_GAIN, src, old_species)
@@ -349,7 +349,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
for(var/X in inherent_traits)
REMOVE_TRAIT(C, X, SPECIES_TRAIT)
C.remove_movespeed_modifier(MOVESPEED_ID_SPECIES)
C.remove_movespeed_modifier(/datum/movespeed_modifier/species)
if(mutant_bodyparts["meat_type"])
C.type_of_meat = GLOB.meat_types[C.dna.features["meat_type"]]
@@ -1299,14 +1299,14 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(H.overeatduration < 100)
to_chat(H, "<span class='notice'>You feel fit again!</span>")
REMOVE_TRAIT(H, TRAIT_FAT, OBESITY)
H.remove_movespeed_modifier(MOVESPEED_ID_FAT)
H.remove_movespeed_modifier(/datum/movespeed_modifier/obesity)
H.update_inv_w_uniform()
H.update_inv_wear_suit()
else
if(H.overeatduration >= 100)
to_chat(H, "<span class='danger'>You suddenly feel blubbery!</span>")
ADD_TRAIT(H, TRAIT_FAT, OBESITY)
H.add_movespeed_modifier(MOVESPEED_ID_FAT, multiplicative_slowdown = 1.5)
H.add_movespeed_modifier(/datum/movespeed_modifier/obesity)
H.update_inv_w_uniform()
H.update_inv_wear_suit()
@@ -1362,9 +1362,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(!HAS_TRAIT(H, TRAIT_NOHUNGER))
var/hungry = (500 - H.nutrition) / 5 //So overeat would be 100 and default level would be 80
if(hungry >= 70)
H.add_movespeed_modifier(MOVESPEED_ID_HUNGRY, override = TRUE, multiplicative_slowdown = (hungry / 50))
H.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/hunger, multiplicative_slowdown = (hungry / 50))
else
H.remove_movespeed_modifier(MOVESPEED_ID_HUNGRY)
H.remove_movespeed_modifier(/datum/movespeed_modifier/hunger)
switch(H.nutrition)
if(NUTRITION_LEVEL_FULL to INFINITY)
@@ -1615,7 +1615,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
"You hear a slap."
)
return FALSE
else
user.do_attack_animation(target, ATTACK_EFFECT_DISARM)
@@ -1623,10 +1623,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
user.adjustStaminaLossBuffered(1)
else
user.adjustStaminaLossBuffered(3)
if(attacker_style && attacker_style.disarm_act(user,target))
return TRUE
if(target.w_uniform)
target.w_uniform.add_fingerprint(user)
//var/randomized_zone = ran_zone(user.zone_selected) CIT CHANGE - comments out to prevent compiling errors
@@ -1659,7 +1659,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
randn -= 25 //if you are a pugilist, you're slapping that item from them pretty reliably
if(HAS_TRAIT(target, TRAIT_PUGILIST))
randn += 25 //meanwhile, pugilists are less likely to get disarmed
if(randn <= 35)//CIT CHANGE - changes this back to a 35% chance to accomodate for the above being commented out in favor of right-click pushing
var/obj/item/I = null
if(target.pulling)
@@ -1932,8 +1932,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/obj/item/target_held_item = target.get_active_held_item()
if(!is_type_in_typecache(target_held_item, GLOB.shove_disarming_types))
target_held_item = null
if(!target.has_movespeed_modifier(MOVESPEED_ID_SHOVE))
target.add_movespeed_modifier(MOVESPEED_ID_SHOVE, multiplicative_slowdown = SHOVE_SLOWDOWN_STRENGTH)
if(!target.has_movespeed_modifier(/datum/movespeed_modifier/shove))
target.add_movespeed_modifier(/datum/movespeed_modifier/shove)
if(target_held_item)
if(!HAS_TRAIT(target_held_item, TRAIT_NODROP))
target.visible_message("<span class='danger'>[target.name]'s grip on \the [target_held_item] loosens!</span>",
@@ -2084,7 +2084,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "cold")
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "hot", /datum/mood_event/hot)
H.remove_movespeed_modifier(MOVESPEED_ID_COLD)
H.remove_movespeed_modifier(/datum/movespeed_modifier/cold)
var/burn_damage
var/firemodifier = H.fire_stacks / 50
@@ -2101,8 +2101,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
else if(H.bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT && !HAS_TRAIT(H, TRAIT_RESISTCOLD))
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "hot")
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "cold", /datum/mood_event/cold)
//Sorry for the nasty oneline but I don't want to assign a variable on something run pretty frequently
H.add_movespeed_modifier(MOVESPEED_ID_COLD, override = TRUE, multiplicative_slowdown = ((BODYTEMP_COLD_DAMAGE_LIMIT - H.bodytemperature) / COLD_SLOWDOWN_FACTOR), blacklisted_movetypes = FLOATING)
//Apply cold slowdown
H.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/cold, multiplicative_slowdown = ((BODYTEMP_COLD_DAMAGE_LIMIT - H.bodytemperature) / COLD_SLOWDOWN_FACTOR))
switch(H.bodytemperature)
if(200 to BODYTEMP_COLD_DAMAGE_LIMIT)
H.apply_damage(COLD_DAMAGE_LEVEL_1*coldmod*H.physiology.cold_mod, BURN)
@@ -2112,7 +2112,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
H.apply_damage(COLD_DAMAGE_LEVEL_3*coldmod*H.physiology.cold_mod, BURN)
else
H.remove_movespeed_modifier(MOVESPEED_ID_COLD)
H.remove_movespeed_modifier(/datum/movespeed_modifier/cold)
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "cold")
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "hot")

View File

@@ -101,12 +101,11 @@ GLOBAL_LIST_INIT(dwarf_last, world.file2list("strings/names/dwarf_last.txt")) //
/obj/item/organ/dwarfgland/on_life() //Primary loop to hook into to start delayed loops for other loops..
. = ..()
dwarf_cycle_ticker()
if(owner && owner.stat != DEAD)
dwarf_cycle_ticker()
//Handles the delayed tick cycle by just adding on increments per each on_life() tick
/obj/item/organ/dwarfgland/proc/dwarf_cycle_ticker()
if(owner.stat == DEAD)
return //We make sure they are not dead, so they don't increment any tickers.
dwarf_eth_ticker++
dwarf_filth_ticker++

View File

@@ -91,4 +91,9 @@
if((C.dna.features["spines"] != "None" ) && (C.dna.features["tail_lizard"] == "None")) //tbh, it's kinda ugly for them not to have a tail yet have floating spines
C.dna.features["tail_lizard"] = "Smooth"
C.update_body()
if(C.dna.features["legs"] != "digitigrade")
C.dna.features["legs"] = "digitigrade"
for(var/obj/item/bodypart/leggie in C.bodyparts)
if(leggie.body_zone == BODY_ZONE_L_LEG || leggie.body_zone == BODY_ZONE_R_LEG)
leggie.update_limb(FALSE, C)
return ..()

View File

@@ -65,12 +65,11 @@
/mob/living/carbon/monkey/on_reagent_change()
. = ..()
remove_movespeed_modifier(MOVESPEED_ID_MONKEY_REAGENT_SPEEDMOD, TRUE)
var/amount
if(reagents.has_reagent(/datum/reagent/medicine/morphine))
amount = -1
if(amount)
add_movespeed_modifier(MOVESPEED_ID_MONKEY_REAGENT_SPEEDMOD, TRUE, 100, override = TRUE, multiplicative_slowdown = amount)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/monkey_reagent_speedmod, TRUE, amount)
/mob/living/carbon/monkey/updatehealth()
. = ..()
@@ -78,7 +77,7 @@
var/health_deficiency = (100 - health)
if(health_deficiency >= 45)
slow += (health_deficiency / 25)
add_movespeed_modifier(MOVESPEED_ID_MONKEY_HEALTH_SPEEDMOD, TRUE, 100, override = TRUE, multiplicative_slowdown = slow)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/monkey_health_speedmod, TRUE, slow)
/mob/living/carbon/monkey/adjust_bodytemperature(amount)
. = ..()
@@ -87,7 +86,7 @@
slow += (283.222 - bodytemperature) / 10 * 1.75
if(slow <= 0)
return
add_movespeed_modifier(MOVESPEED_ID_MONKEY_TEMPERATURE_SPEEDMOD, TRUE, 100, override = TRUE, multiplicative_slowdown = amount)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/monkey_temperature_speedmod, TRUE, slow)
/mob/living/carbon/monkey/Stat()
..()
@@ -99,7 +98,6 @@
if(changeling)
stat("Chemical Storage", "[changeling.chem_charges]/[changeling.chem_storage]")
stat("Absorbed DNA", changeling.absorbedcount)
return
/mob/living/carbon/monkey/verb/removeinternal()

View File

@@ -94,11 +94,13 @@
if(should_be_lying)
mobility_flags &= ~MOBILITY_STAND
setMovetype(movement_type | CRAWLING)
if(!lying) //force them on the ground
lying = pick(90, 270)
if(has_gravity() && !buckled)
playsound(src, "bodyfall", 20, 1)
else
setMovetype(movement_type & ~CRAWLING)
mobility_flags |= MOBILITY_STAND
lying = 0
@@ -161,8 +163,8 @@
if(!has_legs && has_arms < 2)
limbless_slowdown += 6 - (has_arms * 3)
if(limbless_slowdown)
add_movespeed_modifier(MOVESPEED_ID_LIVING_LIMBLESS, update=TRUE, priority=100, override=TRUE, multiplicative_slowdown=limbless_slowdown, blacklisted_movetypes = FLYING|FLOATING)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/limbless, multiplicative_slowdown = limbless_slowdown)
else
remove_movespeed_modifier(MOVESPEED_ID_LIVING_LIMBLESS, update=TRUE)
remove_movespeed_modifier(/datum/movespeed_modifier/limbless)
return mobility_flags

View File

@@ -36,38 +36,26 @@
sprint_stamina_cost = CONFIG_GET(number/movedelay/sprint_stamina_cost)
return ..()
/mob/living/movement_delay(ignorewalk = 0)
. = ..()
if(!CHECK_MOBILITY(src, MOBILITY_STAND))
. += 6
/// whether or not we can slide under another living mob. defaults to if we're not dense. CanPass should check "overriding circumstances" like buckled mobs/having PASSMOB flag, etc.
/mob/living/proc/can_move_under_living(mob/living/other)
return !density
/mob/living/proc/update_move_intent_slowdown()
var/mod = 0
if(m_intent == MOVE_INTENT_WALK)
mod = CONFIG_GET(number/movedelay/walk_delay)
else
mod = CONFIG_GET(number/movedelay/run_delay)
if(!isnum(mod))
mod = 1
add_movespeed_modifier(MOVESPEED_ID_MOB_WALK_RUN_CONFIG_SPEED, TRUE, 100, override = TRUE, multiplicative_slowdown = mod)
add_movespeed_modifier((m_intent == MOVE_INTENT_WALK)? /datum/movespeed_modifier/config_walk_run/walk : /datum/movespeed_modifier/config_walk_run/run)
/mob/living/proc/update_turf_movespeed(turf/open/T)
if(isopenturf(T) && !is_flying())
add_movespeed_modifier(MOVESPEED_ID_LIVING_TURF_SPEEDMOD, TRUE, 100, override = TRUE, multiplicative_slowdown = T.slowdown)
if(isopenturf(T))
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/turf_slowdown, multiplicative_slowdown = T.slowdown)
else
remove_movespeed_modifier(MOVESPEED_ID_LIVING_TURF_SPEEDMOD)
remove_movespeed_modifier(/datum/movespeed_modifier/turf_slowdown)
/mob/living/proc/update_pull_movespeed()
if(pulling && isliving(pulling))
var/mob/living/L = pulling
if(drag_slowdown && L.lying && !L.buckled && grab_state < GRAB_AGGRESSIVE)
add_movespeed_modifier(MOVESPEED_ID_PRONE_DRAGGING, multiplicative_slowdown = PULL_PRONE_SLOWDOWN)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/bulky_drag, multiplicative_slowdown = PULL_PRONE_SLOWDOWN)
return
remove_movespeed_modifier(MOVESPEED_ID_PRONE_DRAGGING)
remove_movespeed_modifier(/datum/movespeed_modifier/bulky_drag)
/mob/living/canZMove(dir, turf/target)
return can_zTravel(target, dir) && (movement_type & FLYING)

View File

@@ -273,9 +273,9 @@
/mob/living/silicon/pai/Process_Spacemove(movement_dir = 0)
. = ..()
if(!.)
add_movespeed_modifier(MOVESPEED_ID_PAI_SPACEWALK_SPEEDMOD, TRUE, 100, multiplicative_slowdown = 2)
add_movespeed_modifier(/datum/movespeed_modifier/pai_spacewalk)
return TRUE
remove_movespeed_modifier(MOVESPEED_ID_PAI_SPACEWALK_SPEEDMOD, TRUE)
remove_movespeed_modifier(/datum/movespeed_modifier/pai_spacewalk)
return TRUE
/mob/living/silicon/pai/examine(mob/user)

View File

@@ -237,7 +237,7 @@
return
if(!CONFIG_GET(flag/disable_secborg) && GLOB.security_level < CONFIG_GET(number/minimum_secborg_alert))
to_chat(src, "<span class='notice'>NOTICE: Due to local station regulations, the security cyborg module and its variants are only available during [num2seclevel(CONFIG_GET(number/minimum_secborg_alert))] alert and greater.</span>")
to_chat(src, "<span class='notice'>NOTICE: Due to local station regulations, the security cyborg module and its variants are only available during [NUM2SECLEVEL(CONFIG_GET(number/minimum_secborg_alert))] alert and greater.</span>")
var/list/modulelist = list("Standard" = /obj/item/robot_module/standard, \
"Engineering" = /obj/item/robot_module/engineering, \

View File

@@ -1,3 +1,5 @@
/mob/living/silicon/KnockToFloor(disarm_items = FALSE, silent = TRUE, updating = TRUE)
return
/mob/living/silicon/grippedby(mob/living/user, instant = FALSE)
return //can't upgrade a simple pull into a more aggressive grab.

View File

@@ -61,11 +61,10 @@
"<span class='userdanger'>[M] [response_disarm] [name]!</span>", null, COMBAT_MESSAGE_RANGE)
log_combat(M, src, "disarmed")
else
var/damage = rand(15, 30)
visible_message("<span class='danger'>[M] has slashed at [src]!</span>", \
"<span class='userdanger'>[M] has slashed at [src]!</span>", null, COMBAT_MESSAGE_RANGE)
playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
attack_threshold_check(damage)
attack_threshold_check(M.meleeSlashSAPower)
log_combat(M, src, "attacked")
/mob/living/simple_animal/attack_larva(mob/living/carbon/alien/larva/L)

View File

@@ -19,6 +19,11 @@
path_image_color = "#993299"
weather_immunities = list("lava","ash")
var/clean_time = 50 //How long do we take to clean?
var/broom = FALSE //Do we have an speed buff from a broom?
var/adv_mop = FALSE //Do we have a cleaning buff from a better mop?
var/blood = 1
var/trash = 0
var/pests = 0
@@ -75,6 +80,26 @@
to_chat(user, "<span class='warning'>Please close the access panel before locking it.</span>")
else
to_chat(user, "<span class='notice'>\The [src] doesn't seem to respect your authority.</span>")
if(istype(W, /obj/item/mop/advanced))
if(bot_core.allowed(user) && open && adv_mop == TRUE)
to_chat(user, "<span class='notice'>You replace \the [src] old mop with a new better one!</span>")
adv_mop = TRUE
clean_time = 20 //2.5 the speed!
window_name = "Automatic Station Cleaner v2.1 BETA" //New!
qdel(W)
else
to_chat(user, "<span class='notice'>\the [src] already has this mop!</span>")
if(istype(W, /obj/item/twohanded/broom))
if(bot_core.allowed(user) && open && broom == TRUE)
to_chat(user, "<span class='notice'>You add to \the [src] a broom speeding it up!</span>")
broom = TRUE
base_speed = 1 //2x faster!
qdel(W)
else
to_chat(user, "<span class='notice'>\the [src] already has a broom!</span>")
else
return ..()
@@ -221,7 +246,7 @@
icon_state = "cleanbot-c"
visible_message("<span class='notice'>[src] begins to clean up [A].</span>")
mode = BOT_CLEANING
spawn(50)
spawn(clean_time)
if(mode == BOT_CLEANING)
if(A && isturf(A.loc))
var/atom/movable/AM = A

View File

@@ -264,6 +264,9 @@
if((last_newpatient_speak + 300) < world.time) //Don't spam these messages!
var/list/messagevoice = list("Hey, [H.name]! Hold on, I'm coming." = 'sound/voice/medbot/coming.ogg',"Wait [H.name]! I want to help!" = 'sound/voice/medbot/help.ogg',"[H.name], you appear to be injured!" = 'sound/voice/medbot/injured.ogg')
var/message = pick(messagevoice)
if(prob(1) && ISINRANGE_EX(H.getFireLoss(), 0, 20))
message = "Notices your minor burns*OwO what's this?"
messagevoice[message] = 'sound/voice/medbot/owo.ogg'
speak(message)
playsound(loc, messagevoice[message], 50, 0)
last_newpatient_speak = world.time

View File

@@ -7,7 +7,7 @@
icon_living = "carp"
icon_dead = "carp_dead"
icon_gib = "carp_gib"
threat = 0.2
threat = 0.1
mob_biotypes = MOB_ORGANIC|MOB_BEAST
speak_chance = 0
turns_per_move = 5

View File

@@ -186,10 +186,10 @@
. = ..()
if(slowed_by_webs)
if(!(locate(/obj/structure/spider/stickyweb) in loc))
remove_movespeed_modifier(MOVESPEED_ID_TARANTULA_WEB)
remove_movespeed_modifier(/datum/movespeed_modifier/tarantula_web)
slowed_by_webs = FALSE
else if(locate(/obj/structure/spider/stickyweb) in loc)
add_movespeed_modifier(MOVESPEED_ID_TARANTULA_WEB, priority=100, multiplicative_slowdown=3)
add_movespeed_modifier(/datum/movespeed_modifier/tarantula_web)
slowed_by_webs = TRUE
//midwives are the queen of the spiders, can send messages to all them and web faster. That rare round where you get a queen spider and turn your 'for honor' players into 'r6siege' players will be a fun one.

View File

@@ -1,5 +1,13 @@
/**
* Kudzu Flower Bud
*
* A flower created by flowering kudzu which spawns a venus human trap after a certain amount of time has passed.
*
* A flower created by kudzu with the flowering mutation. Spawns a venus human trap after 2 minutes under normal circumstances.
* Also spawns 4 vines going out in diagonal directions from the bud. Any living creature not aligned with plants is damaged by these vines.
* Once it grows a venus human trap, the bud itself will destroy itself.
*
*/
/obj/structure/alien/resin/flower_bud_enemy //inheriting basic attack/damage stuff from alien structures
name = "flower bud"
desc = "A large pulsating plant..."
@@ -9,9 +17,9 @@
opacity = 0
canSmoothWith = list()
smooth = SMOOTH_FALSE
/// The amount of time it takes to create a venus human trap, in deciseconds
var/growth_time = 1200
/obj/structure/alien/resin/flower_bud_enemy/Initialize()
. = ..()
var/list/anchors = list()
@@ -25,36 +33,49 @@
B.sleep_time = 10 //these shouldn't move, so let's slow down updates to 1 second (any slower and the deletion of the vines would be too slow)
addtimer(CALLBACK(src, .proc/bear_fruit), growth_time)
/**
* Spawns a venus human trap, then qdels itself.
*
* Displays a message, spawns a human venus trap, then qdels itself.
*/
/obj/structure/alien/resin/flower_bud_enemy/proc/bear_fruit()
visible_message("<span class='danger'>the plant has borne fruit!</span>")
visible_message("<span class='danger'>The plant has borne fruit!</span>")
new /mob/living/simple_animal/hostile/venus_human_trap(get_turf(src))
qdel(src)
/obj/effect/ebeam/vine
name = "thick vine"
mouse_opacity = MOUSE_OPACITY_ICON
desc = "A thick vine, painful to the touch."
/obj/effect/ebeam/vine/Crossed(atom/movable/AM)
. = ..()
if(isliving(AM))
var/mob/living/L = AM
if(!("vines" in L.faction))
if(!isvineimmune(L))
L.adjustBruteLoss(5)
to_chat(L, "<span class='alert'>You cut yourself on the thorny vines.</span>")
/**
* Venus Human Trap
*
* The result of a kudzu flower bud, these enemies use vines to drag prey close to them for attack.
*
* A carnivorious plant which uses vines to catch and ensnare prey. Spawns from kudzu flower buds.
* Each one has a maximum of four vines, which can be attached to a variety of things. Carbons are stunned when a vine is attached to them, and movable entities are pulled closer over time.
* Attempting to attach a vine to something with a vine already attached to it will pull all movable targets closer on command.
* Once the prey is in melee range, melee attacks from the venus human trap heals itself for 10% of its max health, assuming the target is alive.
* Akin to certain spiders, venus human traps can also be possessed and controlled by ghosts.
*
*/
/mob/living/simple_animal/hostile/venus_human_trap
name = "venus human trap"
desc = "Now you know how the fly feels."
icon_state = "venus_human_trap"
threat = 1
layer = SPACEVINE_MOB_LAYER
health = 50
maxHealth = 50
ranged = 1
ranged = TRUE
harm_intent_damage = 5
obj_damage = 60
melee_damage_lower = 25
@@ -63,65 +84,110 @@
attack_sound = 'sound/weapons/bladeslice.ogg'
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
unsuitable_atmos_damage = 0
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
faction = list("hostile","vines","plants")
var/list/grasping = list()
var/max_grasps = 4
var/grasp_chance = 20
var/grasp_pull_chance = 85
var/grasp_range = 4
del_on_death = 1
initial_language_holder = /datum/language_holder/venus
del_on_death = TRUE
/// A list of all the plant's vines
var/list/vines = list()
/// The maximum amount of vines a plant can have at one time
var/max_vines = 4
/// How far away a plant can attach a vine to something
var/vine_grab_distance = 5
/// Whether or not this plant is ghost possessable
var/playable_plant = FALSE //Normal plants can **not** have players.
/mob/living/simple_animal/hostile/venus_human_trap/Destroy()
for(var/L in grasping)
var/datum/beam/B = grasping[L]
if(B)
qdel(B)
grasping = null
return ..()
/mob/living/simple_animal/hostile/venus_human_trap/ghost_playable
playable_plant = TRUE //For admins that want to buss some harmless plants
/mob/living/simple_animal/hostile/venus_human_trap/handle_automated_action()
if(..())
for(var/mob/living/L in grasping)
if(L.stat == DEAD)
var/datum/beam/B = grasping[L]
if(B)
B.End()
grasping -= L
//Can attack+pull multiple times per cycle
if(L.Adjacent(src))
L.attack_animal(src)
else
if(prob(grasp_pull_chance))
setDir(get_dir(src,L) )//staaaare
step(L,get_dir(L,src)) //reel them in
L.DefaultCombatKnockdown(60) //you can't get away now~
if(grasping.len < max_grasps)
grasping:
for(var/mob/living/L in view(grasp_range, src))
if(L == src || faction_check_mob(L) || (L in grasping) || L == target)
continue
for(var/t in getline(src,L))
for(var/a in t)
var/atom/A = a
if(A.density && A != L)
continue grasping
if(prob(grasp_chance))
to_chat(L, "<span class='userdanger'>\The [src] has you entangled!</span>")
grasping[L] = Beam(L, "vine", time=INFINITY, maxdistance=5, beam_type=/obj/effect/ebeam/vine)
break //only take 1 new victim per cycle
/mob/living/simple_animal/hostile/venus_human_trap/Life()
. = ..()
pull_vines()
/mob/living/simple_animal/hostile/venus_human_trap/AttackingTarget()
. = ..()
if(isliving(target))
var/mob/living/L = target
if(L.stat != DEAD)
adjustHealth(-maxHealth * 0.1)
/mob/living/simple_animal/hostile/venus_human_trap/OpenFire(atom/the_target)
var/dist = get_dist(src,the_target)
Beam(the_target, "vine", time=dist*2, maxdistance=dist+2, beam_type=/obj/effect/ebeam/vine)
the_target.attack_animal(src)
for(var/datum/beam/B in vines)
if(B.target == the_target)
pull_vines()
ranged_cooldown = world.time + (ranged_cooldown_time * 0.5)
return
if(get_dist(src,the_target) > vine_grab_distance || vines.len == max_vines)
return
for(var/turf/T in getline(src,target))
if (T.density)
return
for(var/obj/O in T)
if(O.density)
return
var/datum/beam/newVine = Beam(the_target, "vine", time=INFINITY, maxdistance = vine_grab_distance, beam_type=/obj/effect/ebeam/vine)
RegisterSignal(newVine, COMSIG_PARENT_QDELETING, .proc/remove_vine, newVine)
vines += newVine
if(isliving(the_target))
var/mob/living/L = the_target
L.Paralyze(20)
ranged_cooldown = world.time + ranged_cooldown_time
/mob/living/simple_animal/hostile/venus_human_trap/CanAttack(atom/the_target)
/mob/living/simple_animal/hostile/venus_human_trap/Login()
. = ..()
to_chat(src, "<span class='boldwarning'>You a venus human trap! Protect the kudzu at all costs, and feast on those who oppose you!</span>")
/mob/living/simple_animal/hostile/venus_human_trap/attack_ghost(mob/user)
. = ..()
if(.)
if(the_target in grasping)
return 0
return
humanize_plant(user)
/**
* Sets a ghost to control the plant if the plant is eligible
*
* Asks the interacting ghost if they would like to control the plant.
* If they answer yes, and another ghost hasn't taken control, sets the ghost to control the plant.
* Arguments:
* * mob/user - The ghost to possibly control the plant
*/
/mob/living/simple_animal/hostile/venus_human_trap/proc/humanize_plant(mob/user)
if(key || !playable_plant || stat)
return
var/plant_ask = alert("Become a venus human trap?", "Are you reverse vegan?", "Yes", "No")
if(plant_ask == "No" || QDELETED(src))
return
if(key)
to_chat(user, "<span class='warning'>Someone else already took this plant!</span>")
return
key = user.key
log_game("[key_name(src)] took control of [name].")
/**
* Manages how the vines should affect the things they're attached to.
*
* Pulls all movable targets of the vines closer to the plant
* If the target is on the same tile as the plant, destroy the vine
* Removes any QDELETED vines from the vines list.
*/
/mob/living/simple_animal/hostile/venus_human_trap/proc/pull_vines()
for(var/datum/beam/B in vines)
if(istype(B.target, /atom/movable))
var/atom/movable/AM = B.target
if(!AM.anchored)
step(AM,get_dir(AM,src))
if(get_dist(src,B.target) == 0)
B.End()
/**
* Removes a vine from the list.
*
* Removes the vine from our list.
* Called specifically when the vine is about to be destroyed, so we don't have any null references.
* Arguments:
* * datum/beam/vine - The vine to be removed from the list.
*/
mob/living/simple_animal/hostile/venus_human_trap/proc/remove_vine(datum/beam/vine, force)
vines -= vine

View File

@@ -292,8 +292,8 @@
/mob/living/simple_animal/proc/update_simplemob_varspeed()
if(speed == 0)
remove_movespeed_modifier(MOVESPEED_ID_SIMPLEMOB_VARSPEED, TRUE)
add_movespeed_modifier(MOVESPEED_ID_SIMPLEMOB_VARSPEED, TRUE, 100, multiplicative_slowdown = speed, override = TRUE)
remove_movespeed_modifier(/datum/movespeed_modifier/simplemob_varspeed)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/simplemob_varspeed, multiplicative_slowdown = speed)
/mob/living/simple_animal/Stat()
..()

View File

@@ -147,25 +147,25 @@
/mob/living/simple_animal/slime/on_reagent_change()
. = ..()
remove_movespeed_modifier(MOVESPEED_ID_SLIME_REAGENTMOD, TRUE)
remove_movespeed_modifier(/datum/movespeed_modifier/slime_reagentmod)
var/amount = 0
if(reagents.has_reagent(/datum/reagent/medicine/morphine)) // morphine slows slimes down
amount = 2
if(reagents.has_reagent(/datum/reagent/consumable/frostoil)) // Frostoil also makes them move VEEERRYYYYY slow
amount = 5
if(amount)
add_movespeed_modifier(MOVESPEED_ID_SLIME_REAGENTMOD, TRUE, 100, override = TRUE, multiplicative_slowdown = amount)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/slime_reagentmod, multiplicative_slowdown = amount)
/mob/living/simple_animal/slime/updatehealth()
. = ..()
remove_movespeed_modifier(MOVESPEED_ID_SLIME_HEALTHMOD, FALSE)
remove_movespeed_modifier(/datum/movespeed_modifier/slime_healthmod)
var/health_deficiency = (100 - health)
var/mod = 0
if(health_deficiency >= 45)
mod += (health_deficiency / 25)
if(health <= 0)
mod += 2
add_movespeed_modifier(MOVESPEED_ID_SLIME_HEALTHMOD, TRUE, 100, multiplicative_slowdown = mod)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/slime_healthmod, multiplicative_slowdown = mod)
/mob/living/simple_animal/slime/adjust_bodytemperature()
. = ..()
@@ -173,9 +173,8 @@
if(bodytemperature >= 330.23) // 135 F or 57.08 C
mod = -1 // slimes become supercharged at high temperatures
else if(bodytemperature < 183.222)
mod = (283.222 - bodytemperature) / 10 * 1.75
if(mod)
add_movespeed_modifier(MOVESPEED_ID_SLIME_TEMPMOD, TRUE, 100, override = TRUE, multiplicative_slowdown = mod)
mod = min(15, (283.222 - bodytemperature) / 10 * 1.75)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/slime_tempmod, multiplicative_slowdown = mod)
/mob/living/simple_animal/slime/ObjBump(obj/O)
if(!client && powerlevel > 0)

View File

@@ -622,10 +622,40 @@
tod = STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)
update_stat()
///Unignores all slowdowns that lack the IGNORE_NOSLOW flag.
/mob/living/proc/unignore_slowdown(source)
REMOVE_TRAIT(src, TRAIT_IGNORESLOWDOWN, source)
update_movespeed(FALSE)
update_movespeed()
///Ignores all slowdowns that lack the IGNORE_NOSLOW flag.
/mob/living/proc/ignore_slowdown(source)
ADD_TRAIT(src, TRAIT_IGNORESLOWDOWN, source)
update_movespeed(FALSE)
update_movespeed()
///Ignores specific slowdowns. Accepts a list of slowdowns.
/mob/living/proc/add_movespeed_mod_immunities(source, slowdown_type, update = TRUE)
if(islist(slowdown_type))
for(var/listed_type in slowdown_type)
if(ispath(listed_type))
listed_type = "[listed_type]" //Path2String
LAZYADDASSOC(movespeed_mod_immunities, listed_type, source)
else
if(ispath(slowdown_type))
slowdown_type = "[slowdown_type]" //Path2String
LAZYADDASSOC(movespeed_mod_immunities, slowdown_type, source)
if(update)
update_movespeed()
///Unignores specific slowdowns. Accepts a list of slowdowns.
/mob/living/proc/remove_movespeed_mod_immunities(source, slowdown_type, update = TRUE)
if(islist(slowdown_type))
for(var/listed_type in slowdown_type)
if(ispath(listed_type))
listed_type = "[listed_type]" //Path2String
LAZYREMOVEASSOC(movespeed_mod_immunities, listed_type, source)
else
if(ispath(slowdown_type))
slowdown_type = "[slowdown_type]" //Path2String
LAZYREMOVEASSOC(movespeed_mod_immunities, slowdown_type, source)
if(update)
update_movespeed()

View File

@@ -1033,17 +1033,22 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
/// Updates the grab state of the mob and updates movespeed
/mob/setGrabState(newstate)
. = ..()
if(grab_state == GRAB_PASSIVE)
remove_movespeed_modifier(MOVESPEED_ID_MOB_GRAB_STATE, update=TRUE)
else
add_movespeed_modifier(MOVESPEED_ID_MOB_GRAB_STATE, update=TRUE, priority=100, override=TRUE, multiplicative_slowdown=grab_state*3, blacklisted_movetypes=FLOATING)
switch(grab_state)
if(GRAB_PASSIVE)
remove_movespeed_modifier(MOVESPEED_ID_MOB_GRAB_STATE)
if(GRAB_AGGRESSIVE)
add_movespeed_modifier(/datum/movespeed_modifier/grab_slowdown/aggressive)
if(GRAB_NECK)
add_movespeed_modifier(/datum/movespeed_modifier/grab_slowdown/neck)
if(GRAB_KILL)
add_movespeed_modifier(/datum/movespeed_modifier/grab_slowdown/kill)
/mob/proc/update_equipment_speed_mods()
var/speedies = equipped_speed_mods()
if(!speedies)
remove_movespeed_modifier(MOVESPEED_ID_MOB_EQUIPMENT, update=TRUE)
remove_movespeed_modifier(/datum/movespeed_modifier/equipment_speedmod, update=TRUE)
else
add_movespeed_modifier(MOVESPEED_ID_MOB_EQUIPMENT, update=TRUE, priority=100, override=TRUE, multiplicative_slowdown=speedies, blacklisted_movetypes=FLOATING)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/equipment_speedmod, multiplicative_slowdown = speedies)
/// Gets the combined speed modification of all worn items
/// Except base mob type doesnt really wear items

View File

@@ -42,8 +42,11 @@
var/lying_prev = 0
var/is_shifted = FALSE
//MOVEMENT SPEED
/// List of movement speed modifiers applying to this mob
var/list/movespeed_modification //Lazy list, see mob_movespeed.dm
/// List of movement speed modifiers ignored by this mob. List -> List (id) -> List (sources)
var/list/movespeed_mod_immunities //Lazy list, see mob_movespeed.dm
/// The calculated mob speed slowdown based on the modifiers list
var/cached_multiplicative_slowdown
/////////////////

View File

@@ -80,12 +80,12 @@
var/oldloc = mob.loc
if(L.confused)
var/newdir = 0
if(L.confused > 40)
var/newdir = NONE
if((L.confused > 50) && prob(min(L.confused * 0.5, 50)))
newdir = pick(GLOB.alldirs)
else if(prob(L.confused * 1.5))
else if(prob(L.confused))
newdir = angle2dir(dir2angle(direction) + pick(90, -90))
else if(prob(L.confused * 3))
else if(prob(L.confused * 2))
newdir = angle2dir(dir2angle(direction) + pick(45, -45))
if(newdir)
direction = newdir
@@ -251,9 +251,9 @@
/mob/proc/update_gravity(has_gravity, override=FALSE)
var/speed_change = max(0, has_gravity - STANDARD_GRAVITY)
if(!speed_change)
remove_movespeed_modifier(MOVESPEED_ID_MOB_GRAVITY, update=TRUE)
remove_movespeed_modifier(/datum/movespeed_modifier/gravity)
else
add_movespeed_modifier(MOVESPEED_ID_MOB_GRAVITY, update=TRUE, priority=100, override=TRUE, multiplicative_slowdown=speed_change, blacklisted_movetypes=FLOATING)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/gravity, multiplicative_slowdown = speed_change)
//bodypart selection - Cyberboss
//8 toggles through head - eyes - mouth

View File

@@ -1,126 +0,0 @@
/*Current movespeed modification list format: list(id = list(
priority,
flags,
legacy slowdown/speedup amount,
movetype_flags,
blacklisted_movetypes,
conflict
))
*/
//ANY ADD/REMOVE DONE IN UPDATE_MOVESPEED MUST HAVE THE UPDATE ARGUMENT SET AS FALSE!
/mob/proc/add_movespeed_modifier(id, update=TRUE, priority=0, flags=NONE, override=FALSE, multiplicative_slowdown=0, movetypes=ALL, blacklisted_movetypes=NONE, conflict=FALSE)
var/list/temp = list(priority, flags, multiplicative_slowdown, movetypes, blacklisted_movetypes, conflict) //build the modification list
var/resort = TRUE
if(LAZYACCESS(movespeed_modification, id))
var/list/existing_data = movespeed_modification[id]
if(movespeed_modifier_identical_check(existing_data, temp))
return FALSE
if(!override)
return FALSE
if(priority == existing_data[MOVESPEED_DATA_INDEX_PRIORITY])
resort = FALSE // We don't need to re-sort if we're replacing something already there and it's the same priority
LAZYSET(movespeed_modification, id, temp)
if(update)
update_movespeed(resort)
return TRUE
/mob/proc/remove_movespeed_modifier(id, update = TRUE)
if(!LAZYACCESS(movespeed_modification, id))
return FALSE
LAZYREMOVE(movespeed_modification, id)
UNSETEMPTY(movespeed_modification)
if(update)
update_movespeed(FALSE)
return TRUE
/mob/vv_edit_var(var_name, var_value)
var/slowdown_edit = (var_name == NAMEOF(src, cached_multiplicative_slowdown))
var/diff
if(slowdown_edit && isnum(cached_multiplicative_slowdown) && isnum(var_value))
remove_movespeed_modifier(MOVESPEED_ID_ADMIN_VAREDIT)
diff = var_value - cached_multiplicative_slowdown
. = ..()
if(. && slowdown_edit && isnum(diff))
add_movespeed_modifier(MOVESPEED_ID_ADMIN_VAREDIT, TRUE, 100, override = TRUE, multiplicative_slowdown = diff)
/mob/proc/has_movespeed_modifier(id)
return LAZYACCESS(movespeed_modification, id)
/mob/proc/update_config_movespeed()
add_movespeed_modifier(MOVESPEED_ID_CONFIG_SPEEDMOD, FALSE, 100, override = TRUE, multiplicative_slowdown = get_config_multiplicative_speed())
/mob/proc/get_config_multiplicative_speed()
if(!islist(GLOB.mob_config_movespeed_type_lookup) || !GLOB.mob_config_movespeed_type_lookup[type])
return 0
else
return GLOB.mob_config_movespeed_type_lookup[type]
/mob/proc/update_movespeed(resort = TRUE)
if(resort)
sort_movespeed_modlist()
. = 0
var/list/conflict_tracker = list()
for(var/id in get_movespeed_modifiers())
var/list/data = movespeed_modification[id]
if(!(data[MOVESPEED_DATA_INDEX_MOVETYPE] & movement_type)) // We don't affect any of these move types, skip
continue
if(data[MOVESPEED_DATA_INDEX_BL_MOVETYPE] & movement_type) // There's a movetype here that disables this modifier, skip
continue
var/conflict = data[MOVESPEED_DATA_INDEX_CONFLICT]
var/amt = data[MOVESPEED_DATA_INDEX_MULTIPLICATIVE_SLOWDOWN]
if(conflict)
// Conflicting modifiers prioritize the larger slowdown or the larger speedup
// We purposefuly don't handle mixing speedups and slowdowns on the same id
if(abs(conflict_tracker[conflict]) < abs(amt))
conflict_tracker[conflict] = amt
else
continue
. += amt
cached_multiplicative_slowdown = .
/mob/proc/get_movespeed_modifiers()
return movespeed_modification
/mob/proc/movespeed_modifier_identical_check(list/mod1, list/mod2)
if(!islist(mod1) || !islist(mod2) || mod1.len < MOVESPEED_DATA_INDEX_MAX || mod2.len < MOVESPEED_DATA_INDEX_MAX)
return FALSE
for(var/i in 1 to MOVESPEED_DATA_INDEX_MAX)
if(mod1[i] != mod2[i])
return FALSE
return TRUE
/mob/proc/total_multiplicative_slowdown()
. = 0
for(var/id in get_movespeed_modifiers())
var/list/data = movespeed_modification[id]
. += data[MOVESPEED_DATA_INDEX_MULTIPLICATIVE_SLOWDOWN]
/proc/movespeed_data_null_check(list/data) //Determines if a data list is not meaningful and should be discarded.
. = TRUE
if(data[MOVESPEED_DATA_INDEX_MULTIPLICATIVE_SLOWDOWN])
. = FALSE
/mob/proc/sort_movespeed_modlist() //Verifies it too. Sorts highest priority (first applied) to lowest priority (last applied)
if(!movespeed_modification)
return
var/list/assembled = list()
for(var/our_id in movespeed_modification)
var/list/our_data = movespeed_modification[our_id]
if(!islist(our_data) || (our_data.len < MOVESPEED_DATA_INDEX_PRIORITY) || movespeed_data_null_check(our_data))
movespeed_modification -= our_id
continue
var/our_priority = our_data[MOVESPEED_DATA_INDEX_PRIORITY]
var/resolved = FALSE
for(var/their_id in assembled)
var/list/their_data = assembled[their_id]
if(their_data[MOVESPEED_DATA_INDEX_PRIORITY] < our_priority)
assembled.Insert(assembled.Find(their_id), our_id)
assembled[our_id] = our_data
resolved = TRUE
break
if(!resolved)
assembled[our_id] = our_data
movespeed_modification = assembled
UNSETEMPTY(movespeed_modification)

View File

@@ -0,0 +1,217 @@
/*! Movespeed modification datums.
How move speed for mobs works
Move speed is now calculated by using modifier datums which are added to mobs. Some of them (nonvariable ones) are globally cached, the variable ones are instanced and changed based on need.
This gives us the ability to have multiple sources of movespeed, reliabily keep them applied and remove them when they should be
THey can have unique sources and a bunch of extra fancy flags that control behaviour
Previously trying to update move speed was a shot in the dark that usually meant mobs got stuck going faster or slower
Movespeed modification list is a simple key = datum system. Key will be the datum's ID if it is overridden to not be null, or type if it is not.
DO NOT override datum IDs unless you are going to have multiple types that must overwrite each other. It's more efficient to use types, ID functionality is only kept for cases where dynamic creation of modifiers need to be done.
When update movespeed is called, the list of items is iterated, according to flags priority and a bunch of conditions
this spits out a final calculated value which is used as a modifer to last_move + modifier for calculating when a mob
can next move
Key procs
* [add_movespeed_modifier](mob.html#proc/add_movespeed_modifier)
* [remove_movespeed_modifier](mob.html#proc/remove_movespeed_modifier)
* [has_movespeed_modifier](mob.html#proc/has_movespeed_modifier)
* [update_movespeed](mob.html#proc/update_movespeed)
*/
/datum/movespeed_modifier
/// Whether or not this is a variable modifier. Variable modifiers can NOT be ever auto-cached. ONLY CHECKED VIA INITIAL(), EFFECTIVELY READ ONLY (and for very good reason)
var/variable = FALSE
/// Unique ID. You can never have different modifications with the same ID. By default, this SHOULD NOT be set. Only set it for cases where you're dynamically making modifiers/need to have two types overwrite each other. If unset, uses path (converted to text) as ID.
var/id
/// Higher ones override lower priorities. This is NOT used for ID, ID must be unique, if it isn't unique the newer one overwrites automatically if overriding.
var/priority = 0
var/flags = NONE
/// Multiplicative slowdown
var/multiplicative_slowdown = 0
/// Movetypes this applies to
var/movetypes = ALL
/// Movetypes this never applies to
var/blacklisted_movetypes = NONE
/// Other modification datums this conflicts with.
var/conflicts_with
/datum/movespeed_modifier/New()
. = ..()
if(!id)
id = "[type]" //We turn the path into a string.
GLOBAL_LIST_EMPTY(movespeed_modification_cache)
/// Grabs a STATIC MODIFIER datum from cache. YOU MUST NEVER EDIT THESE DATUMS, OR IT WILL AFFECT ANYTHING ELSE USING IT TOO!
/proc/get_cached_movespeed_modifier(modtype)
if(!ispath(modtype, /datum/movespeed_modifier))
CRASH("[modtype] is not a movespeed modification typepath.")
var/datum/movespeed_modifier/M = modtype
if(initial(M.variable))
CRASH("[modtype] is a variable modifier, and can never be cached.")
M = GLOB.movespeed_modification_cache[modtype]
if(!M)
M = GLOB.movespeed_modification_cache[modtype] = new modtype
return M
///Add a move speed modifier to a mob. If a variable subtype is passed in as the first argument, it will make a new datum. If ID conflicts, it will overwrite the old ID.
/mob/proc/add_movespeed_modifier(datum/movespeed_modifier/type_or_datum, update = TRUE)
if(ispath(type_or_datum))
if(!initial(type_or_datum.variable))
type_or_datum = get_cached_movespeed_modifier(type_or_datum)
else
type_or_datum = new type_or_datum
var/datum/movespeed_modifier/existing = LAZYACCESS(movespeed_modification, type_or_datum.id)
if(existing)
if(existing == type_or_datum) //same thing don't need to touch
return TRUE
remove_movespeed_modifier(existing, FALSE)
if(length(movespeed_modification))
BINARY_INSERT(type_or_datum.id, movespeed_modification, datum/movespeed_modifier, type_or_datum, priority, COMPARE_VALUE)
LAZYSET(movespeed_modification, type_or_datum.id, type_or_datum)
if(update)
update_movespeed()
return TRUE
/// Remove a move speed modifier from a mob, whether static or variable.
/mob/proc/remove_movespeed_modifier(datum/movespeed_modifier/type_id_datum, update = TRUE)
var/key
if(ispath(type_id_datum))
key = initial(type_id_datum.id) || "[type_id_datum]" //id if set, path set to string if not.
else if(!istext(type_id_datum)) //if it isn't text it has to be a datum, as it isn't a type.
key = type_id_datum.id
else //assume it's an id
key = type_id_datum
if(!LAZYACCESS(movespeed_modification, key))
return FALSE
LAZYREMOVE(movespeed_modification, key)
if(update)
update_movespeed(FALSE)
return TRUE
/*! Used for variable slowdowns like hunger/health loss/etc, works somewhat like the old list-based modification adds. Returns the modifier datum if successful
How this SHOULD work is:
1. Ensures type_id_datum one way or another refers to a /variable datum. This makes sure it can't be cached. This includes if it's already in the modification list.
2. Instantiate a new datum if type_id_datum isn't already instantiated + in the list, using the type. Obviously, wouldn't work for ID only.
3. Add the datum if necessary using the regular add proc
4. If any of the rest of the args are not null (see: multiplicative slowdown), modify the datum
5. Update if necessary
*/
/mob/proc/add_or_update_variable_movespeed_modifier(datum/movespeed_modifier/type_id_datum, update = TRUE, multiplicative_slowdown)
var/modified = FALSE
var/inject = FALSE
var/datum/movespeed_modifier/final
if(istext(type_id_datum))
final = LAZYACCESS(movespeed_modification, type_id_datum)
if(!final)
CRASH("Couldn't find existing modification when provided a text ID.")
else if(ispath(type_id_datum))
if(!initial(type_id_datum.variable))
CRASH("Not a variable modifier")
final = LAZYACCESS(movespeed_modification, initial(type_id_datum.id) || "[type_id_datum]")
if(!final)
final = new type_id_datum
inject = TRUE
modified = TRUE
else
if(!initial(type_id_datum.variable))
CRASH("Not a variable modifier")
final = type_id_datum
if(!LAZYACCESS(movespeed_modification, final.id))
inject = TRUE
modified = TRUE
if(!isnull(multiplicative_slowdown))
final.multiplicative_slowdown = multiplicative_slowdown
modified = TRUE
if(inject)
add_movespeed_modifier(final, FALSE)
if(update && modified)
update_movespeed(TRUE)
return final
/// Handles the special case of editing the movement var
/mob/vv_edit_var(var_name, var_value)
var/slowdown_edit = (var_name == NAMEOF(src, cached_multiplicative_slowdown))
var/diff
if(slowdown_edit && isnum(cached_multiplicative_slowdown) && isnum(var_value))
remove_movespeed_modifier(/datum/movespeed_modifier/admin_varedit)
diff = var_value - cached_multiplicative_slowdown
. = ..()
if(. && slowdown_edit && isnum(diff))
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/admin_varedit, multiplicative_slowdown = diff)
///Is there a movespeed modifier for this mob
/mob/proc/has_movespeed_modifier(datum/movespeed_modifier/datum_type_id)
var/key
if(ispath(datum_type_id))
key = initial(datum_type_id.id) || "[datum_type_id]"
else if(istext(datum_type_id))
key = datum_type_id
else
key = datum_type_id.id
return LAZYACCESS(movespeed_modification, key)
/// Set or update the global movespeed config on a mob
/mob/proc/update_config_movespeed()
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/mob_config_speedmod, multiplicative_slowdown = get_config_multiplicative_speed())
/// Get the global config movespeed of a mob by type
/mob/proc/get_config_multiplicative_speed()
if(!islist(GLOB.mob_config_movespeed_type_lookup) || !GLOB.mob_config_movespeed_type_lookup[type])
return 0
else
return GLOB.mob_config_movespeed_type_lookup[type]
/// Go through the list of movespeed modifiers and calculate a final movespeed. ANY ADD/REMOVE DONE IN UPDATE_MOVESPEED MUST HAVE THE UPDATE ARGUMENT SET AS FALSE!
/mob/proc/update_movespeed()
. = 0
var/list/conflict_tracker = list()
for(var/key in get_movespeed_modifiers())
var/datum/movespeed_modifier/M = movespeed_modification[key]
if(!(M.movetypes & movement_type)) // We don't affect any of these move types, skip
continue
if(M.blacklisted_movetypes & movement_type) // There's a movetype here that disables this modifier, skip
continue
var/conflict = M.conflicts_with
var/amt = M.multiplicative_slowdown
if(conflict)
// Conflicting modifiers prioritize the larger slowdown or the larger speedup
// We purposefuly don't handle mixing speedups and slowdowns on the same id
if(abs(conflict_tracker[conflict]) < abs(amt))
conflict_tracker[conflict] = amt
else
continue
. += amt
cached_multiplicative_slowdown = .
/// Get the move speed modifiers list of the mob
/mob/proc/get_movespeed_modifiers()
. = LAZYCOPY(movespeed_modification)
for(var/id in movespeed_mod_immunities)
. -= id
/// Calculate the total slowdown of all movespeed modifiers
/mob/proc/total_multiplicative_slowdown()
. = 0
for(var/id in get_movespeed_modifiers())
var/datum/movespeed_modifier/M = movespeed_modification[id]
. += M.multiplicative_slowdown
/// Checks if a move speed modifier is valid and not missing any data
/proc/movespeed_data_null_check(datum/movespeed_modifier/M) //Determines if a data list is not meaningful and should be discarded.
. = TRUE
if(M.multiplicative_slowdown)
. = FALSE

View File

@@ -0,0 +1,21 @@
/datum/movespeed_modifier/shrink_ray
movetypes = GROUND
multiplicative_slowdown = 4
flags = IGNORE_NOSLOW
/datum/movespeed_modifier/snail_crawl
multiplicative_slowdown = -7
movetypes = GROUND
/datum/movespeed_modifier/sanity
id = MOVESPEED_ID_SANITY
blacklisted_movetypes = FLYING
/datum/movespeed_modifier/sanity/insane
multiplicative_slowdown = 1.5
/datum/movespeed_modifier/sanity/crazy
multiplicative_slowdown = 1
/datum/movespeed_modifier/sanity/disturbed
multiplicative_slowdown = 0.5

View File

@@ -0,0 +1,20 @@
/datum/movespeed_modifier/strained_muscles
multiplicative_slowdown = -1
blacklisted_movetypes = (FLYING|FLOATING)
/datum/movespeed_modifier/pai_spacewalk
multiplicative_slowdown = 2
flags = IGNORE_NOSLOW
/datum/movespeed_modifier/species
movetypes = ~FLYING
variable = TRUE
/datum/movespeed_modifier/dna_vault_speedup
blacklisted_movetypes = (FLYING|FLOATING)
multiplicative_slowdown = -1
/datum/movespeed_modifier/small_stride
blacklisted_movetypes = (FLOATING|CRAWLING)
variable = TRUE
flags = IGNORE_NOSLOW

View File

@@ -0,0 +1,12 @@
/datum/movespeed_modifier/jetpack
conflicts_with = MOVE_CONFLICT_JETPACK
movetypes = FLOATING
/datum/movespeed_modifier/jetpack/cybernetic
multiplicative_slowdown = -0.5
/datum/movespeed_modifier/jetpack/fullspeed
multiplicative_slowdown = -2
/datum/movespeed_modifier/die_of_fate
multiplicative_slowdown = 1

View File

@@ -0,0 +1,6 @@
/datum/movespeed_modifier/admin_varedit
variable = TRUE
/datum/movespeed_modifier/yellow_orb
multiplicative_slowdown = -2
blacklisted_movetypes = (FLYING|FLOATING)

View File

@@ -0,0 +1,120 @@
/datum/movespeed_modifier/obesity
multiplicative_slowdown = 1.5
/datum/movespeed_modifier/monkey_reagent_speedmod
blacklisted_movetypes = FLOATING
variable = TRUE
/datum/movespeed_modifier/monkey_health_speedmod
blacklisted_movetypes = FLOATING
variable = TRUE
/datum/movespeed_modifier/monkey_temperature_speedmod
blacklisted_movetypes = FLOATING
variable = TRUE
/datum/movespeed_modifier/hunger
variable = TRUE
/datum/movespeed_modifier/slaughter
multiplicative_slowdown = -1
/datum/movespeed_modifier/damage_slowdown
blacklisted_movetypes = FLOATING|FLYING
variable = TRUE
/datum/movespeed_modifier/damage_slowdown_flying
movetypes = FLOATING
variable = TRUE
/datum/movespeed_modifier/equipment_speedmod
variable = TRUE
blacklisted_movetypes = FLOATING
/datum/movespeed_modifier/grab_slowdown
id = MOVESPEED_ID_MOB_GRAB_STATE
blacklisted_movetypes = FLOATING
/datum/movespeed_modifier/grab_slowdown/aggressive
multiplicative_slowdown = 3
/datum/movespeed_modifier/grab_slowdown/neck
multiplicative_slowdown = 6
/datum/movespeed_modifier/grab_slowdown/kill
multiplicative_slowdown = 9
/datum/movespeed_modifier/slime_reagentmod
variable = TRUE
/datum/movespeed_modifier/slime_healthmod
variable = TRUE
/datum/movespeed_modifier/config_walk_run
multiplicative_slowdown = 1
id = MOVESPEED_ID_MOB_WALK_RUN
flags = IGNORE_NOSLOW
/datum/movespeed_modifier/config_walk_run/proc/sync()
/datum/movespeed_modifier/config_walk_run/walk/sync()
var/mod = CONFIG_GET(number/movedelay/walk_delay)
multiplicative_slowdown = isnum(mod)? mod : initial(multiplicative_slowdown)
/datum/movespeed_modifier/config_walk_run/run/sync()
var/mod = CONFIG_GET(number/movedelay/run_delay)
multiplicative_slowdown = isnum(mod)? mod : initial(multiplicative_slowdown)
/datum/movespeed_modifier/turf_slowdown
movetypes = GROUND
blacklisted_movetypes = (FLYING|FLOATING)
variable = TRUE
/datum/movespeed_modifier/bulky_drag
variable = TRUE
/datum/movespeed_modifier/cold
blacklisted_movetypes = FLOATING
variable = TRUE
/datum/movespeed_modifier/shove
multiplicative_slowdown = SHOVE_SLOWDOWN_STRENGTH
/datum/movespeed_modifier/human_carry
variable = TRUE
/datum/movespeed_modifier/limbless
variable = TRUE
movetypes = GROUND
flags = IGNORE_NOSLOW
/datum/movespeed_modifier/simplemob_varspeed
variable = TRUE
flags = IGNORE_NOSLOW
/datum/movespeed_modifier/tarantula_web
multiplicative_slowdown = 3
/datum/movespeed_modifier/gravity
blacklisted_movetypes = FLOATING
variable = TRUE
flags = IGNORE_NOSLOW
/datum/movespeed_modifier/carbon_softcrit
multiplicative_slowdown = SOFTCRIT_ADD_SLOWDOWN
/datum/movespeed_modifier/slime_tempmod
variable = TRUE
/datum/movespeed_modifier/carbon_crawling
multiplicative_slowdown = CRAWLING_ADD_SLOWDOWN
movetypes = CRAWLING
flags = IGNORE_NOSLOW
/datum/movespeed_modifier/mob_config_speedmod
variable = TRUE
flags = IGNORE_NOSLOW
/datum/movespeed_modifier/liver_cirrhosis
blacklisted_movetypes = FLOATING
variable = TRUE

View File

@@ -0,0 +1,14 @@
/datum/movespeed_modifier/reagent
blacklisted_movetypes = (FLYING|FLOATING)
/datum/movespeed_modifier/reagent/stimulants
multiplicative_slowdown = -0.5
/datum/movespeed_modifier/reagent/changelinghaste
multiplicative_slowdown = -2
/datum/movespeed_modifier/reagent/skooma
multiplicative_slowdown = -1
/datum/movespeed_modifier/reagent/nitryl
multiplicative_slowdown = -1

View File

@@ -0,0 +1,44 @@
/datum/movespeed_modifier/status_effect/bloodchill
multiplicative_slowdown = 3
/datum/movespeed_modifier/status_effect/bonechill
multiplicative_slowdown = 3
/datum/movespeed_modifier/status_effect/tarfoot
multiplicative_slowdown = 0.5
blacklisted_movetypes = (FLYING|FLOATING)
/datum/movespeed_modifier/status_effect/sepia
variable = TRUE
blacklisted_movetypes = (FLYING|FLOATING)
/datum/movespeed_modifier/status_effect/mesmerize
blacklisted_movetypes = CRAWLING
multiplicative_slowdown = 5
priority = 64
/datum/movespeed_modifier/status_effect/tased
multiplicative_slowdown = 1.5
priority = 50
/datum/movespeed_modifier/status_effect/tased/no_combat_mode
multiplicative_slowdown = 8
priority = 100
/datum/movespeed_modifier/status_effect/electrostaff
multiplicative_slowdown = 1
movetypes = GROUND
//no comment.
/datum/movespeed_modifier/status_effect/breast_hypertrophy
blacklisted_movetypes = FLOATING
variable = TRUE
//this shouldn't even exist.
/datum/movespeed_modifier/status_effect/penis_hypertrophy
blacklisted_movetypes = FLOATING
variable = TRUE
/datum/movespeed_modifier/status_effect/mkultra
multiplicative_slowdown = -2
blacklisted_movetypes= FLYING|FLOATING

View File

@@ -533,7 +533,7 @@ By design, d1 is the smallest direction and d2 is the highest
if(affecting && affecting.status == BODYPART_ROBOTIC)
if(user == H)
user.visible_message("<span class='notice'>[user] starts to fix some of the wires in [H]'s [affecting.name].</span>", "<span class='notice'>You start fixing some of the wires in [H]'s [affecting.name].</span>")
if(!do_after(user, H, 50))
if(!do_mob(user, H, 50))
return
if(item_heal_robotic(H, user, 0, 15))
use(1)

View File

@@ -3,6 +3,10 @@
select_name = "ion"
fire_sound = 'sound/weapons/ionrifle.ogg'
/obj/item/ammo_casing/energy/ion/hos
projectile_type = /obj/item/projectile/ion/weak
e_cost = 300
/obj/item/ammo_casing/energy/declone
projectile_type = /obj/item/projectile/energy/declone
select_name = "declone"

View File

@@ -16,9 +16,6 @@
fire_sound = 'sound/weapons/gunshot.ogg'
e_cost = 100
/obj/item/ammo_casing/energy/electrode/hos
projectile_type = /obj/item/projectile/energy/electrode/security/hos
e_cost = 200
/obj/item/ammo_casing/energy/electrode/old
e_cost = 1000

View File

@@ -41,9 +41,12 @@
to_chat(user, "<span class='warning'>You're too exhausted for that.</span>")//CIT CHANGE - ditto
return//CIT CHANGE - ditto
pump(user, TRUE)
recentpump = world.time + 10
if(istype(user))//CIT CHANGE - makes pumping shotguns cost a lil bit of stamina.
user.adjustStaminaLossBuffered(2) //CIT CHANGE - DITTO. make this scale inversely to the strength stat when stats/skills are added
if(HAS_TRAIT(user, TRAIT_FAST_PUMP))
recentpump = world.time + 2
else
recentpump = world.time + 10
if(istype(user))//CIT CHANGE - makes pumping shotguns cost a lil bit of stamina.
user.adjustStaminaLossBuffered(2) //CIT CHANGE - DITTO. make this scale inversely to the strength stat when stats/skills are added
return
/obj/item/gun/ballistic/shotgun/blow_up(mob/user)
@@ -90,7 +93,7 @@
fire_delay = 7
mag_type = /obj/item/ammo_box/magazine/internal/shot/riot
sawn_desc = "Come with me if you want to live."
unique_reskin = list("Tatical" = "riotshotgun",
unique_reskin = list("Tactical" = "riotshotgun",
"Wood Stock" = "wood_riotshotgun"
)
@@ -212,7 +215,7 @@
fire_delay = 5
mag_type = /obj/item/ammo_box/magazine/internal/shot/com
w_class = WEIGHT_CLASS_HUGE
unique_reskin = list("Tatical" = "cshotgun",
unique_reskin = list("Tactical" = "cshotgun",
"Slick" = "cshotgun_slick"
)

View File

@@ -52,13 +52,16 @@
/obj/item/gun/energy/e_gun/hos
name = "\improper X-01 MultiPhase Energy Gun"
desc = "This is an expensive, modern recreation of an antique laser gun. This gun has several unique firemodes, but lacks the ability to recharge over time."
desc = "This is an expensive, modern recreation of an antique laser gun. This gun has several unique firemodes, but lacks the ability to recharge over time in exchange for inbuilt advanced firearm EMP shielding."
icon_state = "hoslaser"
force = 10
ammo_type = list(/obj/item/ammo_casing/energy/electrode/hos, /obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser/hos)
ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser/hos, /obj/item/ammo_casing/energy/ion/hos)
ammo_x_offset = 4
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
/obj/item/gun/energy/e_gun/hos/emp_act(severity)
return
/obj/item/gun/energy/e_gun/dragnet
name = "\improper DRAGnet"
desc = "The \"Dynamic Rapid-Apprehension of the Guilty\" net is a revolution in law enforcement technology."

View File

@@ -241,3 +241,87 @@
to_chat(user, "<span class='warning'>The pin beeps, refusing to fire.</span>")
return FALSE
return TRUE
/obj/item/firing_pin/security_level
name = "security level firing pin"
desc = "A sophisticated firing pin that authorizes operation based on its settings and current security level."
icon_state = "firing_pin_sec_level"
var/min_sec_level = SEC_LEVEL_GREEN
var/max_sec_level = SEC_LEVEL_DELTA
var/only_lethals = FALSE
var/can_toggle = TRUE
/obj/item/firing_pin/security_level/Initialize()
. = ..()
fail_message = "<span class='warning'>INVALID SECURITY LEVEL. CURRENT: [uppertext(NUM2SECLEVEL(GLOB.security_level))]. \
MIN: [uppertext(NUM2SECLEVEL(min_sec_level))]. MAX: [uppertext(NUM2SECLEVEL(max_sec_level))]. \
ONLY LETHALS: [only_lethals ? "YES" : "NO"].</span>"
update_icon()
/obj/item/firing_pin/security_level/examine(mob/user)
. = ..()
var/lethal = only_lethals ? "only lethal " : ""
if(min_sec_level != max_sec_level)
. += "<span class='notice'>It's currently set to disallow [lethal]operation when the security level isn't between <b>[NUM2SECLEVEL(min_sec_level)]</b> and <b>[NUM2SECLEVEL(max_sec_level)]</b>.</span>"
else
. += "<span class='notice'>It's currently set to disallow [lethal]operation when the security level isn't <b>[NUM2SECLEVEL(min_sec_level)]</b>.</span>"
if(can_toggle)
. += "<span class='notice'>You can use a <b>multitool</b> to modify its settings.</span>"
/obj/item/firing_pin/security_level/multitool_act(mob/living/user, obj/item/I)
. = TRUE
if(!can_toggle || !user.canUseTopic(src, BE_CLOSE))
return
var/selection = alert(user, "Which setting would you want to modify?", "Firing Pin Settings", "Minimum Level Setting", "Maximum Level Setting", "Lethals Only Toggle")
if(QDELETED(src) || QDELETED(user) || !user.canUseTopic(src, BE_CLOSE))
return
var/static/list/till_designs_pr_isnt_merged = list("green", "blue", "amber", "red", "delta")
switch(selection)
if("Minimum Level Setting")
var/input = input(user, "Input the new minimum level setting.", "Firing Pin Settings", NUM2SECLEVEL(min_sec_level)) as null|anything in till_designs_pr_isnt_merged
if(!input)
return
min_sec_level = till_designs_pr_isnt_merged.Find(input) - 1
if(min_sec_level > max_sec_level)
max_sec_level = SEC_LEVEL_DELTA
if("Maximum Level Setting")
var/input = input(user, "Input the new maximum level setting.", "Firing Pin Settings", NUM2SECLEVEL(max_sec_level)) as null|anything in till_designs_pr_isnt_merged
if(!input)
return
max_sec_level = till_designs_pr_isnt_merged.Find(input) - 1
if(max_sec_level < max_sec_level)
min_sec_level = SEC_LEVEL_GREEN
if("Lethals Only Toggle")
only_lethals = !only_lethals
fail_message = "<span class='warning'>INVALID SECURITY LEVEL. CURRENT: [uppertext(NUM2SECLEVEL(GLOB.security_level))]. \
MIN: [uppertext(NUM2SECLEVEL(min_sec_level))]. MAX: [uppertext(NUM2SECLEVEL(max_sec_level))]. \
ONLY LETHALS: [only_lethals ? "YES" : "NO"].</span>"
update_icon()
/obj/item/firing_pin/security_level/update_overlays()
. = ..()
var/offset = 0
for(var/level in list(min_sec_level, max_sec_level))
var/mutable_appearance/overlay = mutable_appearance(icon, "pin_sec_level_overlay")
overlay.pixel_x += offset
offset += 4
switch(level)
if(SEC_LEVEL_GREEN)
overlay.color = "#b2ff59" //light green
if(SEC_LEVEL_BLUE)
overlay.color = "#99ccff" //light blue
if(SEC_LEVEL_AMBER)
overlay.color = "#ffae42" //light yellow/orange
if(SEC_LEVEL_RED)
overlay.color = "#ff3f34" //light red
else
overlay.color = "#fe59c2" //neon fuchsia
. += overlay
var/mutable_appearance/overlay = mutable_appearance(icon, "pin_sec_level_overlay")
overlay.pixel_x += offset
overlay.color = only_lethals ? "#b2ff59" : "#ff3f34"
. += overlay
/obj/item/firing_pin/security_level/pin_auth(mob/living/user)
return (only_lethals && !(gun.chambered?.harmful)) || ISINRANGE(GLOB.security_level, min_sec_level, max_sec_level)

View File

@@ -315,6 +315,8 @@
objs += O
var/obj/O = safepick(objs)
if(O)
if(length(O.buckled_mobs))
return pick(O.buckled_mobs)
return O
//Nothing else is here that we can hit, hit the turf if we haven't.
if(!(T in permutated) && can_hit_target(T, permutated, T == original, TRUE))

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