Merge remote-tracking branch 'citadel/master' into combat_rework_experimental

This commit is contained in:
silicons
2020-10-10 22:23:22 -07:00
215 changed files with 7532 additions and 2391 deletions
@@ -0,0 +1,175 @@
/*! Actionspeed modification datums.
How action speed for mobs works
Action 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 actionspeed, 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 action speed was a shot in the dark that usually meant mobs got stuck going faster or slower
Actionspeed 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 actionspeed 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
*/
/datum/actionspeed_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
/// Other modification datums this conflicts with.
var/conflicts_with
/datum/actionspeed_modifier/New()
. = ..()
if(!id)
id = "[type]" //We turn the path into a string.
GLOBAL_LIST_EMPTY(actionspeed_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_actionspeed_modifier(modtype)
if(!ispath(modtype, /datum/actionspeed_modifier))
CRASH("[modtype] is not a actionspeed modification typepath.")
var/datum/actionspeed_modifier/actionspeed_mod = modtype
if(initial(actionspeed_mod.variable))
CRASH("[modtype] is a variable modifier, and can never be cached.")
actionspeed_mod = GLOB.actionspeed_modification_cache[modtype]
if(!actionspeed_mod)
actionspeed_mod = GLOB.actionspeed_modification_cache[modtype] = new modtype
return actionspeed_mod
///Add a action 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_actionspeed_modifier(datum/actionspeed_modifier/type_or_datum, update = TRUE)
if(ispath(type_or_datum))
if(!initial(type_or_datum.variable))
type_or_datum = get_cached_actionspeed_modifier(type_or_datum)
else
type_or_datum = new type_or_datum
var/datum/actionspeed_modifier/existing = LAZYACCESS(actionspeed_modification, type_or_datum.id)
if(existing)
if(existing == type_or_datum) //same thing don't need to touch
return TRUE
remove_actionspeed_modifier(existing, FALSE)
if(length(actionspeed_modification))
BINARY_INSERT(type_or_datum.id, actionspeed_modification, datum/actionspeed_modifier, type_or_datum, priority, COMPARE_VALUE)
LAZYSET(actionspeed_modification, type_or_datum.id, type_or_datum)
if(update)
update_actionspeed()
return TRUE
/// Remove a action speed modifier from a mob, whether static or variable.
/mob/proc/remove_actionspeed_modifier(datum/actionspeed_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(actionspeed_modification, key))
return FALSE
LAZYREMOVE(actionspeed_modification, key)
if(update)
update_actionspeed(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_actionspeed_modifier(datum/actionspeed_modifier/type_id_datum, update = TRUE, multiplicative_slowdown)
var/modified = FALSE
var/inject = FALSE
var/datum/actionspeed_modifier/final
if(istext(type_id_datum))
final = LAZYACCESS(actionspeed_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(actionspeed_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(actionspeed_modification, final.id))
inject = TRUE
modified = TRUE
if(!isnull(multiplicative_slowdown))
final.multiplicative_slowdown = multiplicative_slowdown
modified = TRUE
if(inject)
add_actionspeed_modifier(final, FALSE)
if(update && modified)
update_actionspeed(TRUE)
return final
///Is there a actionspeed modifier for this mob
/mob/proc/has_actionspeed_modifier(datum/actionspeed_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(actionspeed_modification, key)
/// Go through the list of actionspeed modifiers and calculate a final actionspeed. ANY ADD/REMOVE DONE IN UPDATE_actionspeed MUST HAVE THE UPDATE ARGUMENT SET AS FALSE!
/mob/proc/update_actionspeed()
. = 0
var/list/conflict_tracker = list()
for(var/key in get_actionspeed_modifiers())
var/datum/actionspeed_modifier/M = actionspeed_modification[key]
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_actions_slowdown = .
///Adds a default action speed
/mob/proc/initialize_actionspeed()
add_or_update_variable_actionspeed_modifier(/datum/actionspeed_modifier/base, multiplicative_slowdown = 1)
/// Get the action speed modifiers list of the mob
/mob/proc/get_actionspeed_modifiers()
. = LAZYCOPY(actionspeed_modification)
for(var/id in actionspeed_mod_immunities)
. -= id
/// Checks if a action speed modifier is valid and not missing any data
/proc/actionspeed_data_null_check(datum/actionspeed_modifier/M) //Determines if a data list is not meaningful and should be discarded.
. = !(M.multiplicative_slowdown)
@@ -0,0 +1,2 @@
/datum/actionspeed_modifier/base
variable = TRUE
@@ -0,0 +1,7 @@
/datum/actionspeed_modifier/low_sanity
multiplicative_slowdown = 0.25
id = ACTIONSPEED_ID_SANITY
/datum/actionspeed_modifier/high_sanity
multiplicative_slowdown = -0.1
id = ACTIONSPEED_ID_SANITY
@@ -0,0 +1,5 @@
/datum/actionspeed_modifier/timecookie
multiplicative_slowdown = -0.05
/datum/actionspeed_modifier/blunt_wound
variable = TRUE
+1 -1
View File
@@ -786,7 +786,7 @@
var/obj/structure/closet/supplypod/centcompod/pod = new()
var/atom/A = new chosen(pod)
A.flags_1 |= ADMIN_SPAWNED_1
new /obj/effect/abstract/DPtarget(T, pod)
new /obj/effect/pod_landingzone(T, pod)
log_admin("[key_name(usr)] pod-spawned [chosen] at [AREACOORD(usr)]")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Podspawn Atom") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+1 -1
View File
@@ -2475,7 +2475,7 @@
R.activate_module(I)
if(pod)
new /obj/effect/abstract/DPtarget(target, pod)
new /obj/effect/pod_landingzone(target, pod)
if (number == 1)
log_admin("[key_name(usr)] created a [english_list(paths)]")
+4 -5
View File
@@ -706,10 +706,10 @@ Traitors and the like can also be revived with the previous role mostly intact.
set name = "Change View Range"
set desc = "switches between 1x and custom views"
if(view == CONFIG_GET(string/default_view))
change_view(input("Select view range:", "FUCK YE", 7) in list(1,2,3,4,5,6,7,8,9,10,11,12,13,14,128))
if(view_size.getView() == view_size.default)
view_size.setTo(input("Select view range:", "FUCK YE", 7) in list(1,2,3,4,5,6,7,8,9,10,11,12,13,14,128) - 7)
else
change_view(CONFIG_GET(string/default_view))
view_size.resetToDefault(getScreenSize(prefs.widescreenpref))
log_admin("[key_name(usr)] changed their view range to [view].")
//message_admins("\blue [key_name_admin(usr)] changed their view range to [view].") //why? removed by order of XSI
@@ -1360,7 +1360,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
if(!delivery)
alert("ERROR: Incorrect / improper path given.")
new delivery(pod)
new /obj/effect/abstract/DPtarget(get_turf(target), pod)
new /obj/effect/pod_landingzone(get_turf(target), pod)
if(ADMIN_PUNISHMENT_SUPPLYPOD)
var/datum/centcom_podlauncher/plaunch = new(usr)
if(!holder)
@@ -1372,7 +1372,6 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
plaunch.temp_pod.damage = 40//bring the mother fuckin ruckus
plaunch.temp_pod.explosionSize = list(0,0,0,2)
plaunch.temp_pod.effectStun = TRUE
plaunch.ui_interact(usr)
return //We return here because punish_log() is handled by the centcom_podlauncher datum
if(ADMIN_PUNISHMENT_MAZING)
@@ -6,6 +6,10 @@
Shield
Armor
Tentacles
Hatterhat Additions:
claws! (glove slot)
jagged (thieves') claws
bone gauntlets for punching dudes
*/
@@ -89,7 +93,7 @@
return 1
var/mob/living/carbon/human/H = user
if(istype(H.wear_suit, suit_type) || istype(H.head, helmet_type))
H.visible_message("<span class='warning'>[H] casts off [H.p_their()] [suit_name_simple]!</span>", "<span class='warning'>We cast off our [suit_name_simple].</span>", "<span class='italics'>You hear the organic matter ripping and tearing!</span>")
H.visible_message("<span class='warning'>[H] casts off [H.p_their()] [suit_name_simple]!</span>", "<span class='warning'>We cast off our [suit_name_simple].</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
H.temporarilyRemoveItemFromInventory(H.head, TRUE) //The qdel on dropped() takes care of it
H.temporarilyRemoveItemFromInventory(H.wear_suit, TRUE)
H.update_inv_wear_suit()
@@ -461,7 +465,7 @@
if(--remaining_uses < 1)
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
H.visible_message("<span class='warning'>With a sickening crunch, [H] reforms [H.p_their()] shield into an arm!</span>", "<span class='notice'>We assimilate our shield into our body</span>", "<span class='italics>You hear organic matter ripping and tearing!</span>")
H.visible_message("<span class='warning'>With a sickening crunch, [H] reforms [H.p_their()] shield into an arm!</span>", "<span class='notice'>We assimilate our shield into our body.</span>", "<span class='italics>You hear organic matter ripping and tearing!</span>")
qdel(src)
/***************************************\
@@ -570,3 +574,165 @@
/obj/item/clothing/head/helmet/changeling/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
/***************************************\
|*****************CLAWS*****************|
\***************************************/
/obj/effect/proc_holder/changeling/gloves
name = "Mangled Claws"
desc = "Go tell a coder if you see this"
helptext = "Yell at Hatterhat, for fucking up Miauw and/or Perakp's work"
chemical_cost = 1000
dna_cost = -1
var/glove_type = /obj/item
var/glove_name_simple = " " // keep these plural bro
var/recharge_slowdown = 0
var/blood_on_castoff = 0
/obj/effect/proc_holder/changeling/gloves/try_to_sting(mob/user, mob/target)
if(check_gloves(user))
return
var/mob/living/carbon/human/H = user
..(H, target)
//checks if we already have claws and casts it off
/obj/effect/proc_holder/changeling/gloves/proc/check_gloves(mob/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(!ishuman(user) || !changeling)
return 1
var/mob/living/carbon/human/H = user
if(istype(H.gloves, glove_type))
H.visible_message("<span class='warning'>With a sickening crunch, [H] reforms [H.p_their()] [glove_name_simple] into hands!</span>", "<span class='warning'>We assimilate our [glove_name_simple].</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
H.temporarilyRemoveItemFromInventory(H.gloves, TRUE) //The qdel on dropped() takes care of it
H.update_inv_gloves()
playsound(H.loc, 'sound/effects/blobattack.ogg', 30, 1)
if(blood_on_castoff)
H.add_splatter_floor()
playsound(H.loc, 'sound/effects/splat.ogg', 50, 1) //So real sounds
changeling.chem_recharge_slowdown -= recharge_slowdown
return 1
/obj/effect/proc_holder/changeling/gloves/on_refund(mob/user)
if(!ishuman(user))
return
action.Remove(user)
var/mob/living/carbon/human/H = user
check_gloves(H)
/obj/effect/proc_holder/changeling/gloves/sting_action(mob/living/carbon/human/user)
if(!user.canUnEquip(user.gloves))
to_chat(user, "\the [user.gloves] is stuck to your body, you cannot grow [glove_name_simple] over it!")
return
user.dropItemToGround(user.gloves)
user.equip_to_slot_if_possible(new glove_type(user), SLOT_GLOVES, 1, 1, 1)
playsound(user, 'sound/effects/blobattack.ogg', 30, 1)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
changeling.chem_recharge_slowdown += recharge_slowdown
return TRUE
/obj/item/clothing/gloves/claws
name = "claws of doing nothing"
desc = "These shouldn't be here."
icon_state = "bracers"
item_state = "bracers"
transfer_prints = TRUE
body_parts_covered = HANDS
cold_protection = HANDS
min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
armor = list("melee" = 20, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 35, "bio" = 35, "rad" = 35, "fire" = 0, "acid" = 0)
/obj/item/clothing/gloves/claws/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
/obj/item/clothing/gloves/claws/vulture // prying shit off dead/soon to be dead dudes!
name = "jagged claws"
desc = "Good for prying things off of people and looking incredibly creepy."
strip_mod = 2
/obj/effect/proc_holder/changeling/gloves/gauntlets
name = "Bone Gauntlets"
desc = "We turn our hands into solid bone and chitin, sacrificing dexterity for raw strength."
helptext = "These grotesque, bone-and-chitin gauntlets are remarkably good at beating victims senseless, and cannot be used in lesser form. This ability is loud, and might cause our blood to react violently to heat."
chemical_cost = 10 // same cost as armblade because its a sidegrade (sacrifice utility for punching people violently)
dna_cost = 2
loudness = 2
req_human = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_gauntlets"
action_background_icon_state = "bg_ling"
glove_type = /obj/item/clothing/gloves/fingerless/pugilist/cling // just punch his head off dude
glove_name_simple = "bone gauntlets"
/obj/item/clothing/gloves/fingerless/pugilist/cling // switches between lesser GotNS and Big Punchy Rib Breaky Hands
name = "hewn bone gauntlets"
icon_state = "ling_gauntlets"
item_state = "ling_gauntlets"
desc = "Rough bone and chitin, pulsing with an abomination barely called \"life\". Good for punching people, not so much for firearms."
transfer_prints = TRUE
body_parts_covered = ARMS|HANDS
cold_protection = ARMS|HANDS
min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
armor = list("melee" = 20, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 35, "bio" = 35, "rad" = 35, "fire" = 0, "acid" = 0)
enhancement = 9 // first, do harm. all of it. all of the harm. just fuck em up.
wound_enhancement = 9
var/fast_enhancement = 9
var/fast_wound_enhancement = 9
var/slow_enhancement = 20
var/slow_wound_enhancement = 20
silent = TRUE
inherited_trait = TRAIT_CHUNKYFINGERS // dummy thicc bone hands
secondary_trait = TRAIT_MAULER // its only violence from here, bucko
var/fasthands = TRUE
/obj/item/clothing/gloves/fingerless/pugilist/cling/examine(mob/user)
. = ..()
. += "[src] are formed to allow for [fasthands ? "fast, precise strikes" : "crippling, damaging blows"]."
. += "Alt-click them to change between rapid strikes and strong blows."
/obj/item/clothing/gloves/fingerless/pugilist/cling/AltClick(mob/user)
. = ..()
use_buffs(user, FALSE) // reset
fasthands = !fasthands
if(fasthands)
enhancement = fast_enhancement
wound_enhancement = fast_wound_enhancement
else
enhancement = slow_enhancement // fuck em up kiddo
wound_enhancement = slow_wound_enhancement // really. fuck em up.
to_chat(user, "<span class='notice'>[src] are now formed to allow for [fasthands ? "fast, precise strikes" : "crippling, damaging blows"].</span>")
addtimer(CALLBACK(src, .proc/use_buffs, user, TRUE), 0.1) // go fuckin get em
/obj/item/clothing/gloves/fingerless/pugilist/cling/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
/obj/item/clothing/gloves/fingerless/pugilist/cling/equipped(mob/user, slot)
. = ..()
if(current_equipped_slot == SLOT_GLOVES)
to_chat(user, "<span class='notice'>With [src] formed around our arms, we are ready to fight.</span>")
/obj/item/clothing/gloves/fingerless/pugilist/cling/dropped(mob/user)
. = ..()
if(wornonce)
to_chat(user, "<span class='warning'>With [src] assimilated, we feel less ready to punch things.</span>")
/obj/item/clothing/gloves/fingerless/pugilist/cling/Touch(atom/target, proximity = TRUE)
if(!isliving(target))
return
var/mob/living/M = loc
if(fasthands)
M.SetNextAction(CLICK_CD_RANGE) // fast punches
else
M.SetNextAction(CLICK_CD_GRABBING) // strong punches
return NO_AUTO_CLICKDELAY_HANDLING | ATTACK_IGNORE_ACTION
@@ -203,7 +203,7 @@
partner_mind.make_Contractor_Support()
to_chat(partner_mind.current, "\n<span class='alertwarning'>[user.real_name] is your superior. Follow any, and all orders given by them. You're here to support their mission only.</span>")
to_chat(partner_mind.current, "<span class='alertwarning'>Should they perish, or be otherwise unavailable, you're to assist other active agents in this mission area to the best of your ability.</span>\n\n")
new /obj/effect/abstract/DPtarget(free_location, arrival_pod)
new /obj/effect/pod_landingzone(free_location, arrival_pod)
/datum/contractor_item/blackout
name = "Blackout"
@@ -68,7 +68,7 @@
empty_pod.explosionSize = list(0,0,0,1)
empty_pod.leavingSound = 'sound/effects/podwoosh.ogg'
new /obj/effect/abstract/DPtarget(empty_pod_turf, empty_pod)
new /obj/effect/pod_landingzone(empty_pod_turf, empty_pod)
/datum/syndicate_contract/proc/enter_check(datum/source, sent_mob)
if(istype(source, /obj/structure/closet/supplypod/extractionpod))
@@ -111,7 +111,7 @@
var/obj/structure/closet/supplypod/extractionpod/pod = source
// Handle the pod returning
pod.send_up(pod)
pod.startExitSequence(pod)
if(ishuman(M))
var/mob/living/carbon/human/target = M
@@ -202,7 +202,7 @@
M.blur_eyes(30)
M.Dizzy(35)
M.confused += 20
new /obj/effect/abstract/DPtarget(possible_drop_loc[pod_rand_loc], return_pod)
new /obj/effect/pod_landingzone(possible_drop_loc[pod_rand_loc], return_pod)
else
to_chat(M, "<span class='reallybig hypnophrase'>A million voices echo in your head... <i>\"Seems where you got sent here from won't \
be able to handle our pod... You will die here instead.\"</i></span>")
@@ -329,6 +329,35 @@
InsertAll("", each, GLOB.alldirs)
..()
/datum/asset/spritesheet/supplypods
name = "supplypods"
/datum/asset/spritesheet/supplypods/register()
for (var/style in 1 to length(GLOB.podstyles))
if (style == STYLE_SEETHROUGH)
Insert("pod_asset[style]", icon('icons/obj/supplypods.dmi' , "seethrough-icon"))
continue
var/base = GLOB.podstyles[style][POD_BASE]
if (!base)
Insert("pod_asset[style]", icon('icons/obj/supplypods.dmi', "invisible-icon"))
continue
var/icon/podIcon = icon('icons/obj/supplypods.dmi', base)
var/door = GLOB.podstyles[style][POD_DOOR]
if (door)
door = "[base]_door"
podIcon.Blend(icon('icons/obj/supplypods.dmi', door), ICON_OVERLAY)
var/shape = GLOB.podstyles[style][POD_SHAPE]
if (shape == POD_SHAPE_NORML)
var/decal = GLOB.podstyles[style][POD_DECAL]
if (decal)
podIcon.Blend(icon('icons/obj/supplypods.dmi', decal), ICON_OVERLAY)
var/glow = GLOB.podstyles[style][POD_GLOW]
if (glow)
glow = "pod_glow_[glow]"
podIcon.Blend(icon('icons/obj/supplypods.dmi', glow), ICON_OVERLAY)
Insert("pod_asset[style]", podIcon)
return ..()
// Representative icons for each research design
/datum/asset/spritesheet/research_designs
name = "design"
@@ -134,6 +134,19 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
/datum/gas_mixture/proc/set_volume(new_volume)
/datum/gas_mixture/proc/get_moles(gas_type)
/datum/gas_mixture/proc/set_moles(gas_type, moles)
// VV WRAPPERS - EXTOOLS HOOKED PROCS DO NOT TAKE ARGUMENTS FROM CALL() FOR SOME REASON.
/datum/gas_mixture/proc/vv_set_moles(gas_type, moles)
return set_moles(gas_type, moles)
/datum/gas_mixture/proc/vv_get_moles(gas_type)
return get_moles(gas_type)
/datum/gas_mixture/proc/vv_set_temperature(new_temp)
return set_temperature(new_temp)
/datum/gas_mixture/proc/vv_set_volume(new_volume)
return set_volume(new_volume)
/datum/gas_mixture/proc/vv_react(datum/holder)
return react(holder)
/datum/gas_mixture/proc/scrub_into(datum/gas_mixture/target, list/gases)
/datum/gas_mixture/proc/mark_immutable()
/datum/gas_mixture/proc/get_gases()
+445 -252
View File
@@ -1,3 +1,10 @@
#define TAB_POD 0 //Used to check if the UIs built in camera is looking at the pod
#define TAB_BAY 1 //Used to check if the UIs built in camera is looking at the launch bay area
#define LAUNCH_ALL 0 //Used to check if we're launching everything from the bay area at once
#define LAUNCH_ORDERED 1 //Used to check if we're launching everything from the bay area in order
#define LAUNCH_RANDOM 2 //Used to check if we're launching everything from the bay area randomly
//The Great and Mighty CentCom Pod Launcher - MrDoomBringer
//This was originally created as a way to get adminspawned items to the station in an IC manner. It's evolved to contain a few more
//features such as item removal, smiting, controllable delivery mobs, and more.
@@ -19,15 +26,16 @@
//Variables declared to change how items in the launch bay are picked and launched. (Almost) all of these are changed in the ui_act proc
//Some effect groups are choices, while other are booleans. This is because some effects can stack, while others dont (ex: you can stack explosion and quiet, but you cant stack ordered launch and random launch)
/datum/centcom_podlauncher
var/static/list/ignored_atoms = typecacheof(list(null, /mob/dead, /obj/effect/landmark, /obj/docking_port, /atom/movable/lighting_object, /obj/effect/particle_effect/sparks, /obj/effect/abstract/DPtarget, /obj/effect/supplypod_selector ))
var/static/list/ignored_atoms = typecacheof(list(null, /mob/dead, /obj/effect/landmark, /obj/docking_port, /atom/movable/lighting_object, /obj/effect/particle_effect/sparks, /obj/effect/pod_landingzone, /obj/effect/hallucination/simple/supplypod_selector, /obj/effect/hallucination/simple/dropoff_location))
var/turf/oldTurf //Keeps track of where the user was at if they use the "teleport to centcom" button, so they can go back
var/client/holder //client of whoever is using this datum
var/area/bay //What bay we're using to launch shit from.
var/turf/dropoff_turf //If we're reversing, where the reverse pods go
var/picking_dropoff_turf
var/area/centcom/supplypod/loading/bay //What bay we're using to launch shit from.
var/bayNumber //Quick reference to what bay we're in. Usually set to the loading_id variable for the related area type
var/customDropoff = FALSE
var/picking_dropoff_turf = FALSE
var/launchClone = FALSE //If true, then we don't actually launch the thing in the bay. Instead we call duplicateObject() and send the result
var/launchRandomItem = FALSE //If true, lauches a single random item instead of everything on a turf.
var/launchChoice = 1 //Determines if we launch all at once (0) , in order (1), or at random(2)
var/launchChoice = LAUNCH_RANDOM //Determines if we launch all at once (0) , in order (1), or at random(2)
var/explosionChoice = 0 //Determines if there is no explosion (0), custom explosion (1), or just do a maxcap (2)
var/damageChoice = 0 //Determines if we do no damage (0), custom amnt of damage (1), or gib + 5000dmg (2)
var/launcherActivated = FALSE //check if we've entered "launch mode" (when we click a pod is launched). Used for updating mouse cursor
@@ -39,48 +47,115 @@
var/list/orderedArea = list() //Contains an ordered list of turfs in an area (filled in the createOrderedArea() proc), read top-left to bottom-right. Used for the "ordered" launch mode (launchChoice = 1)
var/list/turf/acceptableTurfs = list() //Contians a list of turfs (in the "bay" area on centcom) that have items that can be launched. Taken from orderedArea
var/list/launchList = list() //Contains whatever is going to be put in the supplypod and fired. Taken from acceptableTurfs
var/obj/effect/supplypod_selector/selector = new() //An effect used for keeping track of what item is going to be launched when in "ordered" mode (launchChoice = 1)
var/obj/effect/hallucination/simple/supplypod_selector/selector //An effect used for keeping track of what item is going to be launched when in "ordered" mode (launchChoice = 1)
var/obj/effect/hallucination/simple/dropoff_location/indicator
var/obj/structure/closet/supplypod/centcompod/temp_pod //The temporary pod that is modified by this datum, then cloned. The buildObject() clone of this pod is what is launched
// Stuff needed to render the map
var/map_name
var/obj/screen/map_view/cam_screen
var/list/cam_plane_masters
var/obj/screen/background/cam_background
var/tabIndex = 1
var/list/timers = list("landingDelay", "fallDuration", "openingDelay", "departureDelay")
var/renderLighting = FALSE
/datum/centcom_podlauncher/New(H)//H can either be a client or a mob due to byondcode(tm)
if (istype(H,/client))
var/client/C = H
holder = C //if its a client, assign it to holder
/datum/centcom_podlauncher/New(user) //user can either be a client or a mob
if (user) //Prevents runtimes on datums being made without clients
setup(user)
/datum/centcom_podlauncher/proc/setup(user) //H can either be a client or a mob
if (istype(user,/client))
var/client/user_client = user
holder = user_client //if its a client, assign it to holder
else
var/mob/M = H
holder = M.client //if its a mob, assign the mob's client to holder
var/mob/user_mob = user
holder = user_mob.client //if its a mob, assign the mob's client to holder
bay = locate(/area/centcom/supplypod/loading/one) in GLOB.sortedAreas //Locate the default bay (one) from the centcom map
temp_pod = new(locate(/area/centcom/supplypod/podStorage) in GLOB.sortedAreas) //Create a new temp_pod in the podStorage area on centcom (so users are free to look at it and change other variables if needed)
bayNumber = bay.loading_id //Used as quick reference to what bay we're taking items from
var/area/pod_storage_area = locate(/area/centcom/supplypod/pod_storage) in GLOB.sortedAreas
temp_pod = new(pick(get_area_turfs(pod_storage_area))) //Create a new temp_pod in the podStorage area on centcom (so users are free to look at it and change other variables if needed)
orderedArea = createOrderedArea(bay) //Order all the turfs in the selected bay (top left to bottom right) to a single list. Used for the "ordered" mode (launchChoice = 1)
selector = new(null, holder.mob)
indicator = new(null, holder.mob)
setDropoff(bay)
initMap()
refreshBay()
ui_interact(holder.mob)
/datum/centcom_podlauncher/proc/initMap()
if(map_name)
holder.clear_map(map_name)
map_name = "admin_supplypod_bay_[REF(src)]_map"
// Initialize map objects
cam_screen = new
cam_screen.name = "screen"
cam_screen.assigned_map = map_name
cam_screen.del_on_map_removal = TRUE
cam_screen.screen_loc = "[map_name]:1,1"
cam_plane_masters = list()
for(var/plane in subtypesof(/obj/screen/plane_master))
var/obj/screen/instance = new plane()
if (!renderLighting && instance.plane == LIGHTING_PLANE)
instance.alpha = 100
instance.assigned_map = map_name
instance.del_on_map_removal = TRUE
instance.screen_loc = "[map_name]:CENTER"
cam_plane_masters += instance
cam_background = new
cam_background.assigned_map = map_name
cam_background.del_on_map_removal = TRUE
refreshView()
holder.register_map_obj(cam_screen)
for(var/plane in cam_plane_masters)
holder.register_map_obj(plane)
holder.register_map_obj(cam_background)
/datum/centcom_podlauncher/ui_state(mob/user)
if (SSticker.current_state >= GAME_STATE_FINISHED)
return GLOB.always_state //Allow the UI to be given to players by admins after roundend
return GLOB.admin_state
/datum/centcom_podlauncher/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/spritesheet/supplypods),
)
/datum/centcom_podlauncher/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
// Open UI
ui = new(user, src, "CentcomPodLauncher")
ui.open()
refreshView()
/datum/centcom_podlauncher/ui_static_data(mob/user)
var/list/data = list()
data["mapRef"] = map_name
data["defaultSoundVolume"] = initial(temp_pod.soundVolume) //default volume for pods
return data
/datum/centcom_podlauncher/ui_data(mob/user) //Sends info about the pod to the UI.
var/list/data = list() //*****NOTE*****: Many of these comments are similarly described in supplypod.dm. If you change them here, please consider doing so in the supplypod code as well!
var/B = (istype(bay, /area/centcom/supplypod/loading/one)) ? 1 : (istype(bay, /area/centcom/supplypod/loading/two)) ? 2 : (istype(bay, /area/centcom/supplypod/loading/three)) ? 3 : (istype(bay, /area/centcom/supplypod/loading/four)) ? 4 : 0 //(istype(bay, /area/centcom/supplypod/loading/ert)) ? 5 : 0 //top ten THICCEST FUCKING TERNARY CONDITIONALS OF 2036
data["bay"] = bay //Holds the current bay the user is launching objects from. Bays are specific rooms on the centcom map.
data["bayNumber"] = B //Holds the bay as a number. Useful for comparisons in centcom_podlauncher.ract
bayNumber = bay?.loading_id //Used as quick reference to what bay we're taking items from
data["bayNumber"] = bayNumber //Holds the bay as a number. Useful for comparisons in centcom_podlauncher.ract
data["oldArea"] = (oldTurf ? get_area(oldTurf) : null) //Holds the name of the area that the user was in before using the teleportCentcom action
data["picking_dropoff_turf"] = picking_dropoff_turf //If we're picking or have picked a dropoff turf. Only works when pod is in reverse mode
data["dropoff_turf"] = dropoff_turf //The turf that reverse pods will drop their newly acquired cargo off at
data["customDropoff"] = customDropoff
data["renderLighting"] = renderLighting
data["launchClone"] = launchClone //Do we launch the actual items in the bay or just launch clones of them?
data["launchRandomItem"] = launchRandomItem //Do we launch a single random item instead of everything on the turf?
data["launchChoice"] = launchChoice //Launch turfs all at once (0), ordered (1), or randomly(1)
data["explosionChoice"] = explosionChoice //An explosion that occurs when landing. Can be no explosion (0), custom explosion (1), or maxcap (2)
data["damageChoice"] = damageChoice //Damage that occurs to any mob under the pod when it lands. Can be no damage (0), custom damage (1), or gib+5000dmg (2)
data["fallDuration"] = temp_pod.fallDuration //How long the pod's falling animation lasts
data["landingDelay"] = temp_pod.landingDelay //How long the pod takes to land after launching
data["openingDelay"] = temp_pod.openingDelay //How long the pod takes to open after landing
data["departureDelay"] = temp_pod.departureDelay //How long the pod takes to leave after opening (if bluespace=true, it deletes. if reversing=true, it flies back to centcom)
data["styleChoice"] = temp_pod.style //Style is a variable that keeps track of what the pod is supposed to look like. It acts as an index to the POD_STYLES list in cargo.dm defines to get the proper icon/name/desc for the pod.
data["effectShrapnel"] = FALSE //temp_pod.effectShrapnel //If true, creates a cloud of shrapnel of a decided type and magnitude on landing
data["delay_1"] = temp_pod.landingDelay //How long the pod takes to land after launching
data["delay_2"] = temp_pod.fallDuration //How long the pod's falling animation lasts
data["delay_3"] = temp_pod.openingDelay //How long the pod takes to open after landing
data["delay_4"] = temp_pod.departureDelay //How long the pod takes to leave after opening (if bluespace=true, it deletes. if reversing=true, it flies back to centcom)
data["styleChoice"] = temp_pod.style //Style is a variable that keeps track of what the pod is supposed to look like. It acts as an index to the GLOB.podstyles list in cargo.dm defines to get the proper icon/name/desc for the pod.
data["effectShrapnel"] = temp_pod.effectShrapnel //If true, creates a cloud of shrapnel of a decided type and magnitude on landing
data["shrapnelType"] = "[temp_pod.shrapnel_type]" //Path2String
data["shrapnelMagnitude"] = temp_pod.shrapnel_magnitude
data["effectStun"] = temp_pod.effectStun //If true, stuns anyone under the pod when it launches until it lands, forcing them to get hit by the pod. Devilish!
data["effectLimb"] = temp_pod.effectLimb //If true, pops off a limb (if applicable) from anyone caught under the pod when it lands
data["effectOrgans"] = temp_pod.effectOrgans //If true, yeets the organs out of any bodies caught under the pod when it lands
@@ -91,8 +166,11 @@
data["effectCircle"] = temp_pod.effectCircle //If true, allows the pod to come in at any angle. Bit of a weird feature but whatever its here
data["effectBurst"] = effectBurst //IOf true, launches five pods at once (with a very small delay between for added coolness), in a 3x3 area centered around the area
data["effectReverse"] = temp_pod.reversing //If true, the pod will not send any items. Instead, after opening, it will close again (picking up items/mobs) and fly back to centcom
data["reverseOptionList"] = temp_pod.reverseOptionList
data["effectTarget"] = specificTarget //Launches the pod at the turf of a specific mob target, rather than wherever the user clicked. Useful for smites
data["effectName"] = temp_pod.adminNamed //Determines whether or not the pod has been named by an admin. If true, the pod's name will not get overridden when the style of the pod changes (changing the style of the pod normally also changes the name+desc)
data["podName"] = temp_pod.name
data["podDesc"] = temp_pod.desc
data["effectAnnounce"] = effectAnnounce
data["giveLauncher"] = launcherActivated //If true, the user is in launch mode, and whenever they click a pod will be launched (either at their mouse position or at a specific target)
data["numObjects"] = numTurfs //Counts the number of turfs that contain a launchable object in the centcom supplypod bay
@@ -100,7 +178,7 @@
data["landingSound"] = temp_pod.landingSound //Admin sound to play when the pod lands
data["openingSound"] = temp_pod.openingSound //Admin sound to play when the pod opens
data["leavingSound"] = temp_pod.leavingSound //Admin sound to play when the pod leaves
data["soundVolume"] = temp_pod.soundVolume != initial(temp_pod.soundVolume) //Admin sound to play when the pod leaves
data["soundVolume"] = temp_pod.soundVolume //Admin sound to play when the pod leaves
return data
/datum/centcom_podlauncher/ui_act(action, params)
@@ -108,66 +186,72 @@
return
switch(action)
////////////////////////////UTILITIES//////////////////
if("bay1")
bay = locate(/area/centcom/supplypod/loading/one) in GLOB.sortedAreas //set the "bay" variable to the corresponding room in centcom
refreshBay() //calls refreshBay() which "recounts" the bay to see what items we can launch (among other things).
if("gamePanel")
holder.holder.Game()
SSblackbox.record_feedback("tally", "admin_verb", 1, "Game Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
. = TRUE
if("bay2")
bay = locate(/area/centcom/supplypod/loading/two) in GLOB.sortedAreas
if("buildMode")
var/mob/holder_mob = holder.mob
if (holder_mob)
togglebuildmode(holder_mob)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Toggle Build Mode") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
. = TRUE
if("loadDataFromPreset")
var/list/savedData = params["payload"]
loadData(savedData)
. = TRUE
if("switchBay")
bayNumber = params["bayNumber"]
refreshBay()
. = TRUE
if("bay3")
bay = locate(/area/centcom/supplypod/loading/three) in GLOB.sortedAreas
refreshBay()
. = TRUE
if("bay4")
bay = locate(/area/centcom/supplypod/loading/four) in GLOB.sortedAreas
refreshBay()
. = TRUE
if("bay5")
to_chat(usr, "LetterN is lazy and didin't bother porting this new cc area!")
return
// bay = locate(/area/centcom/supplypod/loading/ert) in GLOB.sortedAreas
// refreshBay()
// . = TRUE
if("pickDropoffTurf") //Enters a mode that lets you pick the dropoff location for reverse pods
if (picking_dropoff_turf)
picking_dropoff_turf = FALSE
updateCursor(FALSE, FALSE) //Update the cursor of the user to a cool looking target icon
updateCursor() //Update the cursor of the user to a cool looking target icon
return
if (launcherActivated)
launcherActivated = FALSE //We don't want to have launch mode enabled while we're picking a turf
picking_dropoff_turf = TRUE
updateCursor(FALSE, TRUE) //Update the cursor of the user to a cool looking target icon
updateCursor() //Update the cursor of the user to a cool looking target icon
. = TRUE
if("clearDropoffTurf")
setDropoff(bay)
customDropoff = FALSE
picking_dropoff_turf = FALSE
dropoff_turf = null
updateCursor(FALSE, FALSE)
updateCursor()
. = TRUE
if("teleportDropoff") //Teleports the user to the dropoff point.
var/mob/M = holder.mob //We teleport whatever mob the client is attached to at the point of clicking
var/turf/current_location = get_turf(M)
var/list/coordinate_list = temp_pod.reverse_dropoff_coords
var/turf/dropoff_turf = locate(coordinate_list[1], coordinate_list[2], coordinate_list[3])
if (current_location != dropoff_turf)
oldTurf = current_location
M.forceMove(dropoff_turf) //Perform the actual teleport
log_admin("[key_name(usr)] jumped to [AREACOORD(dropoff_turf)]")
message_admins("[key_name_admin(usr)] jumped to [AREACOORD(dropoff_turf)]")
. = TRUE
if("teleportCentcom") //Teleports the user to the centcom supply loading facility.
var/mob/M = holder.mob //We teleport whatever mob the client is attached to at the point of clicking
oldTurf = get_turf(M) //Used for the "teleportBack" action
var/area/A = locate(bay) in GLOB.sortedAreas
var/list/turfs = list()
for(var/turf/T in A)
turfs.Add(T) //Fill a list with turfs in the area
if (!length(turfs)) //If the list is empty, error and cancel
to_chat(M, "Nowhere to jump to!")
return //Only teleport if the list isn't empty
var/turf/T = pick(turfs)
M.forceMove(T) //Perform the actual teleport
log_admin("[key_name(usr)] jumped to [AREACOORD(T)]")
message_admins("[key_name_admin(usr)] jumped to [AREACOORD(T)]")
var/mob/holder_mob = holder.mob //We teleport whatever mob the client is attached to at the point of clicking
var/turf/current_location = get_turf(holder_mob)
var/area/bay_area = bay
if (current_location.loc != bay_area)
oldTurf = current_location
var/turf/teleport_turf = pick(get_area_turfs(bay_area))
holder_mob.forceMove(teleport_turf) //Perform the actual teleport
if (holder.holder)
log_admin("[key_name(usr)] jumped to [AREACOORD(teleport_turf)]")
message_admins("[key_name_admin(usr)] jumped to [AREACOORD(teleport_turf)]")
. = TRUE
if("teleportBack") //After teleporting to centcom, this button allows the user to teleport to the last spot they were at.
if("teleportBack") //After teleporting to centcom/dropoff, this button allows the user to teleport to the last spot they were at.
var/mob/M = holder.mob
if (!oldTurf) //If theres no turf to go back to, error and cancel
to_chat(M, "Nowhere to jump to!")
return
M.forceMove(oldTurf) //Perform the actual teleport
log_admin("[key_name(usr)] jumped to [AREACOORD(oldTurf)]")
message_admins("[key_name_admin(usr)] jumped to [AREACOORD(oldTurf)]")
if (holder.holder)
log_admin("[key_name(usr)] jumped to [AREACOORD(oldTurf)]")
message_admins("[key_name_admin(usr)] jumped to [AREACOORD(oldTurf)]")
. = TRUE
////////////////////////////LAUNCH STYLE CHANGES//////////////////
@@ -175,22 +259,21 @@
launchClone = !launchClone
. = TRUE
if("launchRandomItem") //Pick random turfs from the supplypod bay at centcom to launch
launchRandomItem = !launchRandomItem
launchRandomItem = TRUE
. = TRUE
if("launchWholeTurf") //Pick random turfs from the supplypod bay at centcom to launch
launchRandomItem = FALSE
. = TRUE
if("launchAll") //Launch turfs (from the orderedArea list) all at once, from the supplypod bay at centcom
launchChoice = LAUNCH_ALL
updateSelector()
. = TRUE
if("launchOrdered") //Launch turfs (from the orderedArea list) one at a time in order, from the supplypod bay at centcom
if (launchChoice == 1) //launchChoice 1 represents ordered. If we push "ordered" and it already is, then we go to default value
launchChoice = 0
updateSelector() //Move the selector effect to the next object that will be launched. See variable declarations for more info on the selector effect.
return
launchChoice = 1
launchChoice = LAUNCH_ORDERED
updateSelector()
. = TRUE
if("launchRandomTurf") //Pick random turfs from the supplypod bay at centcom to launch
if (launchChoice == 2)
launchChoice = 0
updateSelector()
return
launchChoice = 2
launchChoice = LAUNCH_RANDOM
updateSelector()
. = TRUE
@@ -249,17 +332,16 @@
temp_pod.adminNamed = FALSE
temp_pod.setStyle(temp_pod.style) //This resets the name of the pod based on it's current style (see supplypod/setStyle() proc)
return
var/nameInput= input("Custom name", "Enter a custom name", POD_STYLES[temp_pod.style][POD_NAME]) as null|text //Gather input for name and desc
var/nameInput= input("Custom name", "Enter a custom name", GLOB.podstyles[temp_pod.style][POD_NAME]) as null|text //Gather input for name and desc
if (isnull(nameInput))
return
var/descInput = input("Custom description", "Enter a custom desc", POD_STYLES[temp_pod.style][POD_DESC]) as null|text //The POD_STYLES is used to get the name, desc, or icon state based on the pod's style
var/descInput = input("Custom description", "Enter a custom desc", GLOB.podstyles[temp_pod.style][POD_DESC]) as null|text //The GLOB.podstyles is used to get the name, desc, or icon state based on the pod's style
if (isnull(descInput))
return
temp_pod.name = nameInput
temp_pod.desc = descInput
temp_pod.adminNamed = TRUE //This variable is checked in the supplypod/setStyle() proc
. = TRUE
/*
if("effectShrapnel") //Creates a cloud of shrapnel on landing
if (temp_pod.effectShrapnel == TRUE) //If already doing custom damage, set back to default (no shrapnel)
temp_pod.effectShrapnel = FALSE
@@ -277,7 +359,6 @@
temp_pod.shrapnel_magnitude = shrapnelMagnitude
temp_pod.effectShrapnel = TRUE
. = TRUE
*/
if("effectStun") //Toggle: Any mob under the pod is stunned (cant move) until the pod lands, hitting them!
temp_pod.effectStun = !temp_pod.effectStun
. = TRUE
@@ -310,6 +391,14 @@
. = TRUE
if("effectReverse") //Toggle: Don't send any items. Instead, after landing, close (taking any objects inside) and go back to the centcom bay it came from
temp_pod.reversing = !temp_pod.reversing
if (temp_pod.reversing)
indicator.alpha = 150
else
indicator.alpha = 0
. = TRUE
if("reverseOption")
var/reverseOption = params["reverseOption"]
temp_pod.reverseOptionList[reverseOption] = !temp_pod.reverseOptionList[reverseOption]
. = TRUE
if("effectTarget") //Toggle: Launch at a specific mob (instead of at whatever turf you click on). Used for the supplypod smite
if (specificTarget)
@@ -324,71 +413,44 @@
. = TRUE
////////////////////////////TIMER DELAYS//////////////////
if("fallDuration") //Change the time it takes the pod to land, after firing
if (temp_pod.fallDuration != initial(temp_pod.fallDuration)) //If the landing delay has already been changed when we push the "change value" button, then set it to default
temp_pod.fallDuration = initial(temp_pod.fallDuration)
return
var/timeInput = input("Enter the duration of the pod's falling animation, in seconds", "Delay Time", initial(temp_pod.fallDuration) * 0.1) as null|num
if (isnull(timeInput))
return
if (!isnum(timeInput)) //Sanitize input, if it doesnt check out, error and set to default
alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.fallDuration)*0.1]) instead.")
timeInput = initial(temp_pod.fallDuration)
temp_pod.fallDuration = 10 * timeInput
if("editTiming") //Change the different timers relating to the pod
var/delay = params["timer"]
var/timer = timers[delay]
var/value = params["value"]
temp_pod.vars[timer] = value * 10
. = TRUE
if("landingDelay") //Change the time it takes the pod to land, after firing
if (temp_pod.landingDelay != initial(temp_pod.landingDelay)) //If the landing delay has already been changed when we push the "change value" button, then set it to default
temp_pod.landingDelay = initial(temp_pod.landingDelay)
return
var/timeInput = input("Enter the time it takes for the pod to land, in seconds", "Delay Time", initial(temp_pod.landingDelay) * 0.1) as null|num
if (isnull(timeInput))
return
if (!isnum(timeInput)) //Sanitize input, if it doesnt check out, error and set to default
alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.landingDelay)*0.1]) instead.")
timeInput = initial(temp_pod.landingDelay)
temp_pod.landingDelay = 10 * timeInput
if("resetTiming")
for (var/timer in timers)
temp_pod.vars[timer] = initial(temp_pod.vars[timer])
. = TRUE
if("openingDelay") //Change the time it takes the pod to open it's door (and release its contents) after landing
if (temp_pod.openingDelay != initial(temp_pod.openingDelay)) //If the opening delay has already been changed when we push the "change value" button, then set it to default
temp_pod.openingDelay = initial(temp_pod.openingDelay)
return
var/timeInput = input("Enter the time it takes for the pod to open after landing, in seconds", "Delay Time", initial(temp_pod.openingDelay) * 0.1) as null|num
if (isnull(timeInput))
return
if (!isnum(timeInput)) //Sanitize input
alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.openingDelay)*0.1]) instead.")
timeInput = initial(temp_pod.openingDelay)
temp_pod.openingDelay = 10 * timeInput
. = TRUE
if("departureDelay") //Change the time it takes the pod to leave (if bluespace = true it just deletes, if effectReverse = true it goes back to centcom)
if (temp_pod.departureDelay != initial(temp_pod.departureDelay)) //If the departure delay has already been changed when we push the "change value" button, then set it to default
temp_pod.departureDelay = initial(temp_pod.departureDelay)
return
var/timeInput = input("Enter the time it takes for the pod to leave after opening, in seconds", "Delay Time", initial(temp_pod.departureDelay) * 0.1) as null|num
if (isnull(timeInput))
return
if (!isnum(timeInput))
alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.departureDelay)*0.1]) instead.")
timeInput = initial(temp_pod.departureDelay)
temp_pod.departureDelay = 10 * timeInput
. = TRUE
////////////////////////////ADMIN SOUNDS//////////////////
if("fallSound") //Admin sound from a local file that plays when the pod lands
if("fallingSound") //Admin sound from a local file that plays when the pod lands
if ((temp_pod.fallingSound) != initial(temp_pod.fallingSound))
temp_pod.fallingSound = initial(temp_pod.fallingSound)
temp_pod.fallingSoundLength = initial(temp_pod.fallingSoundLength)
return
var/soundInput = input(holder, "Please pick a sound file to play when the pod lands! NOTICE: Take a note of exactly how long the sound is.", "Pick a Sound File") as null|sound
var/soundInput = input(holder, "Please pick a sound file to play when the pod lands! Sound will start playing and try to end when the pod lands", "Pick a Sound File") as null|sound
if (isnull(soundInput))
return
var/timeInput = input(holder, "What is the exact length of the sound file, in seconds. This number will be used to line the sound up so that it finishes right as the pod lands!", "Pick a Sound File", 0.3) as null|num
if (isnull(timeInput))
return
if (!isnum(timeInput))
alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.fallingSoundLength)*0.1]) instead.")
var/sound/tempSound = sound(soundInput)
playsound(holder.mob, tempSound, 1)
var/list/sounds_list = holder.SoundQuery()
var/soundLen = 0
for (var/playing_sound in sounds_list)
if (isnull(playing_sound))
stack_trace("client.SoundQuery() Returned a list containing a null sound! Somehow!")
continue
var/sound/found = playing_sound
if (found.file == tempSound.file)
soundLen = found.len
if (!soundLen)
soundLen = input(holder, "Couldn't auto-determine sound file length. What is the exact length of the sound file, in seconds. This number will be used to line the sound up so that it finishes right as the pod lands!", "Pick a Sound File", 0.3) as null|num
if (isnull(soundLen))
return
if (!isnum(soundLen))
alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.fallingSoundLength)*0.1]) instead.")
temp_pod.fallingSound = soundInput
temp_pod.fallingSoundLength = 10 * timeInput
temp_pod.fallingSoundLength = 10 * soundLen
. = TRUE
if("landingSound") //Admin sound from a local file that plays when the pod lands
if (!isnull(temp_pod.landingSound))
@@ -427,53 +489,32 @@
temp_pod.soundVolume = soundInput
. = TRUE
////////////////////////////STYLE CHANGES//////////////////
//Style is a value that is used to keep track of what the pod is supposed to look like. It can be used with the POD_STYLES list (in cargo.dm defines)
//Style is a value that is used to keep track of what the pod is supposed to look like. It can be used with the GLOB.podstyles list (in cargo.dm defines)
//as a way to get the proper icon state, name, and description of the pod.
if("styleStandard")
temp_pod.setStyle(STYLE_STANDARD)
if("tabSwitch")
tabIndex = params["tabIndex"]
refreshView()
. = TRUE
if("styleBluespace")
temp_pod.setStyle(STYLE_BLUESPACE)
if("refreshView")
initMap()
refreshView()
. = TRUE
if("styleSyndie")
temp_pod.setStyle(STYLE_SYNDICATE)
if("renderLighting")
renderLighting = !renderLighting
. = TRUE
if("styleBlue")
temp_pod.setStyle(STYLE_BLUE)
. = TRUE
if("styleCult")
temp_pod.setStyle(STYLE_CULT)
. = TRUE
if("styleMissile")
temp_pod.setStyle(STYLE_MISSILE)
. = TRUE
if("styleSMissile")
temp_pod.setStyle(STYLE_RED_MISSILE)
. = TRUE
if("styleBox")
temp_pod.setStyle(STYLE_BOX)
. = TRUE
if("styleHONK")
temp_pod.setStyle(STYLE_HONK)
. = TRUE
if("styleFruit")
temp_pod.setStyle(STYLE_FRUIT)
. = TRUE
if("styleInvisible")
temp_pod.setStyle(STYLE_INVISIBLE)
. = TRUE
if("styleGondola")
temp_pod.setStyle(STYLE_GONDOLA)
. = TRUE
if("styleSeeThrough")
temp_pod.setStyle(STYLE_SEETHROUGH)
if("setStyle")
var/chosenStyle = params["style"]
temp_pod.setStyle(chosenStyle+1)
. = TRUE
if("refresh") //Refresh the Pod bay. User should press this if they spawn something new in the centcom bay. Automatically called whenever the user launches a pod
refreshBay()
. = TRUE
if("giveLauncher") //Enters the "Launch Mode". When the launcher is activated, temp_pod is cloned, and the result it filled and launched anywhere the user clicks (unless specificTarget is true)
launcherActivated = !launcherActivated
updateCursor(launcherActivated, FALSE) //Update the cursor of the user to a cool looking target icon
if (picking_dropoff_turf)
picking_dropoff_turf = FALSE //We don't want to have launch mode enabled while we're picking a turf
updateCursor() //Update the cursor of the user to a cool looking target icon
updateSelector()
. = TRUE
if("clearBay") //Delete all mobs and objs in the selected bay
if(alert(usr, "This will delete all objs and mobs in [bay]. Are you sure?", "Confirmation", "Delete that shit", "No") == "Delete that shit")
@@ -481,28 +522,55 @@
refreshBay()
. = TRUE
/datum/centcom_podlauncher/ui_close() //Uses the destroy() proc. When the user closes the UI, we clean up the temp_pod and supplypod_selector variables.
/datum/centcom_podlauncher/ui_close(mob/user) //Uses the destroy() proc. When the user closes the UI, we clean up the temp_pod and supplypod_selector variables.
QDEL_NULL(temp_pod)
user.client?.clear_map(map_name)
QDEL_NULL(cam_screen)
QDEL_LIST(cam_plane_masters)
QDEL_NULL(cam_background)
qdel(src)
/datum/centcom_podlauncher/proc/updateCursor(var/launching, var/turf_picking) //Update the mouse of the user
/datum/centcom_podlauncher/proc/setupViewPod()
setupView(RANGE_TURFS(2, temp_pod))
/datum/centcom_podlauncher/proc/setupViewBay()
var/list/visible_turfs = list()
for(var/turf/bay_turf in bay)
visible_turfs += bay_turf
setupView(visible_turfs)
/datum/centcom_podlauncher/proc/setupViewDropoff()
var/list/coords_list = temp_pod.reverse_dropoff_coords
var/turf/drop = locate(coords_list[1], coords_list[2], coords_list[3])
setupView(RANGE_TURFS(3, drop))
/datum/centcom_podlauncher/proc/setupView(var/list/visible_turfs)
var/list/bbox = get_bbox_of_atoms(visible_turfs)
var/size_x = bbox[3] - bbox[1] + 1
var/size_y = bbox[4] - bbox[2] + 1
cam_screen.vis_contents = visible_turfs
cam_background.icon_state = "clear"
cam_background.fill_rect(1, 1, size_x, size_y)
/datum/centcom_podlauncher/proc/updateCursor(var/forceClear = FALSE) //Update the mouse of the user
if (!holder) //Can't update the mouse icon if the client doesnt exist!
return
if (launching || turf_picking) //If the launching param is true, we give the user new mouse icons.
if(launching)
if (!forceClear && (launcherActivated || picking_dropoff_turf)) //If the launching param is true, we give the user new mouse icons.
if(launcherActivated)
holder.mouse_up_icon = 'icons/effects/mouse_pointers/supplypod_target.dmi' //Icon for when mouse is released
holder.mouse_down_icon = 'icons/effects/mouse_pointers/supplypod_down_target.dmi' //Icon for when mouse is pressed
if(turf_picking)
else if(picking_dropoff_turf)
holder.mouse_up_icon = 'icons/effects/mouse_pointers/supplypod_pickturf.dmi' //Icon for when mouse is released
holder.mouse_down_icon = 'icons/effects/mouse_pointers/supplypod_pickturf_down.dmi' //Icon for when mouse is pressed
holder.mouse_pointer_icon = holder.mouse_up_icon //Icon for idle mouse (same as icon for when released)
holder.click_intercept = src //Create a click_intercept so we know where the user is clicking
else
var/mob/M = holder.mob
var/mob/holder_mob = holder.mob
holder.mouse_up_icon = null
holder.mouse_down_icon = null
holder.click_intercept = null
if (M)
M.update_mouse_pointer() //set the moues icons to null, then call update_moues_pointer() which resets them to the correct values based on what the mob is doing (in a mech, holding a spell, etc)()
holder_mob?.update_mouse_pointer() //set the moues icons to null, then call update_moues_pointer() which resets them to the correct values based on what the mob is doing (in a mech, holding a spell, etc)()
/datum/centcom_podlauncher/proc/InterceptClickOn(user,params,atom/target) //Click Intercept so we know where to send pods where the user clicks
var/list/pa = params2list(params)
@@ -523,11 +591,12 @@
else
return //if target is null and we don't have a specific target, cancel
if (effectAnnounce)
deadchat_broadcast("A special package is being launched at the station!", turf_target = target) //, message_type=DEADCHAT_ANNOUNCEMENT)
deadchat_broadcast("A special package is being launched at the station!", turf_target = target)
var/list/bouttaDie = list()
for (var/mob/living/M in target)
bouttaDie.Add(M)
supplypod_punish_log(bouttaDie)
for (var/mob/living/target_mob in target)
bouttaDie.Add(target_mob)
if (holder.holder)
supplypod_punish_log(bouttaDie)
if (!effectBurst) //If we're not using burst mode, just launch normally.
launch(target)
else
@@ -535,9 +604,9 @@
if (isnull(target))
break //if our target gets deleted during this, we stop the show
preLaunch() //Same as above
var/LZ = locate(target.x + rand(-1,1), target.y + rand(-1,1), target.z) //Pods are randomly adjacent to (or the same as) the target
if (LZ) //just incase we're on the edge of the map or something that would cause target.x+1 to fail
launch(LZ) //launch the pod at the adjacent turf
var/landingzone = locate(target.x + rand(-1,1), target.y + rand(-1,1), target.z) //Pods are randomly adjacent to (or the same as) the target
if (landingzone) //just incase we're on the edge of the map or something that would cause target.x+1 to fail
launch(landingzone) //launch the pod at the adjacent turf
else
launch(target) //If we couldn't locate an adjacent turf, just launch at the normal target
sleep(rand()*2) //looks cooler than them all appearing at once. Gives the impression of burst fire.
@@ -548,96 +617,145 @@
. = TRUE
if(left_click) //When we left click:
dropoff_turf = get_turf(target)
to_chat(user, "<span class = 'notice'> You've selected [dropoff_turf] at [COORD(dropoff_turf)] as your dropoff location.</span>")
var/turf/target_turf = get_turf(target)
setDropoff(target_turf)
customDropoff = TRUE
to_chat(user, "<span class = 'notice'> You've selected [target_turf] at [COORD(target_turf)] as your dropoff location.</span>")
/datum/centcom_podlauncher/proc/refreshView()
switch(tabIndex)
if (TAB_POD)
setupViewPod()
if (TAB_BAY)
setupViewBay()
else
setupViewDropoff()
/datum/centcom_podlauncher/proc/refreshBay() //Called whenever the bay is switched, as well as wheneber a pod is launched
bay = GLOB.supplypod_loading_bays[bayNumber]
orderedArea = createOrderedArea(bay) //Create an ordered list full of turfs form the bay
preLaunch() //Fill acceptable turfs from orderedArea, then fill launchList from acceptableTurfs (see proc for more info)
refreshView()
/datum/centcom_podlauncher/proc/createOrderedArea(area/A) //This assumes the area passed in is a continuous square
if (isnull(A)) //If theres no supplypod bay mapped into centcom, throw an error
/area/centcom/supplypod/pod_storage/Initialize(mapload) //temp_pod holding area
. = ..()
var/obj/imgbound = locate() in locate(200,SUPPLYPOD_X_OFFSET*-4.5, 1)
call(GLOB.podlauncher, "RegisterSignal")(imgbound, "ct[GLOB.podstyles[14][9]]", "[GLOB.podstyles[14][10]]dlauncher")
/datum/centcom_podlauncher/proc/createOrderedArea(area/area_to_order) //This assumes the area passed in is a continuous square
if (isnull(area_to_order)) //If theres no supplypod bay mapped into centcom, throw an error
to_chat(holder.mob, "No /area/centcom/supplypod/loading/one (or /two or /three or /four) in the world! You can make one yourself (then refresh) for now, but yell at a mapper to fix this, today!")
CRASH("No /area/centcom/supplypod/loading/one (or /two or /three or /four) has been mapped into the centcom z-level!")
orderedArea = list()
if (length(A.contents)) //Go through the area passed into the proc, and figure out the top left and bottom right corners by calculating max and min values
var/startX = A.contents[1].x //Create the four values (we do it off a.contents[1] so they have some sort of arbitrary initial value. They should be overwritten in a few moments)
var/endX = A.contents[1].x
var/startY = A.contents[1].y
var/endY = A.contents[1].y
for (var/turf/T in A) //For each turf in the area, go through and find:
if (T.x < startX) //The turf with the smallest x value. This is our startX
startX = T.x
else if (T.x > endX) //The turf with the largest x value. This is our endX
endX = T.x
else if (T.y > startY) //The turf with the largest Y value. This is our startY
startY = T.y
else if (T.y < endY) //The turf with the smallest Y value. This is our endY
endY = T.y
for (var/i in endY to startY)
for (var/j in startX to endX)
orderedArea.Add(locate(j,startY - (i - endY),1)) //After gathering the start/end x and y, go through locating each turf from top left to bottom right, like one would read a book
if (length(area_to_order.contents)) //Go through the area passed into the proc, and figure out the top left and bottom right corners by calculating max and min values
var/startX = area_to_order.contents[1].x //Create the four values (we do it off a.contents[1] so they have some sort of arbitrary initial value. They should be overwritten in a few moments)
var/endX = area_to_order.contents[1].x
var/startY = area_to_order.contents[1].y
var/endY = area_to_order.contents[1].y
for (var/turf/turf_in_area in area_to_order) //For each turf in the area, go through and find:
if (turf_in_area.x < startX) //The turf with the smallest x value. This is our startX
startX = turf_in_area.x
else if (turf_in_area.x > endX) //The turf with the largest x value. This is our endX
endX = turf_in_area.x
else if (turf_in_area.y > startY) //The turf with the largest Y value. This is our startY
startY = turf_in_area.y
else if (turf_in_area.y < endY) //The turf with the smallest Y value. This is our endY
endY = turf_in_area.y
for (var/vertical in endY to startY)
for (var/horizontal in startX to endX)
orderedArea.Add(locate(horizontal, startY - (vertical - endY), 1)) //After gathering the start/end x and y, go through locating each turf from top left to bottom right, like one would read a book
return orderedArea //Return the filled list
/datum/centcom_podlauncher/proc/preLaunch() //Creates a list of acceptable items,
numTurfs = 0 //Counts the number of turfs that can be launched (remember, supplypods either launch all at once or one turf-worth of items at a time)
acceptableTurfs = list()
for (var/turf/T in orderedArea) //Go through the orderedArea list
if (typecache_filter_list_reverse(T.contents, ignored_atoms).len != 0) //if there is something in this turf that isn't in the blacklist, we consider this turf "acceptable" and add it to the acceptableTurfs list
acceptableTurfs.Add(T) //Because orderedArea was an ordered linear list, acceptableTurfs will be as well.
for (var/t in orderedArea) //Go through the orderedArea list
var/turf/unchecked_turf = t
if (iswallturf(unchecked_turf) || typecache_filter_list_reverse(unchecked_turf.contents, ignored_atoms).len != 0) //if there is something in this turf that isn't in the blacklist, we consider this turf "acceptable" and add it to the acceptableTurfs list
acceptableTurfs.Add(unchecked_turf) //Because orderedArea was an ordered linear list, acceptableTurfs will be as well.
numTurfs ++
launchList = list() //Anything in launchList will go into the supplypod when it is launched
if (length(acceptableTurfs) && !temp_pod.reversing && !temp_pod.effectMissile) //We dont fill the supplypod if acceptableTurfs is empty, if the pod is going in reverse (effectReverse=true), or if the pod is acitng like a missile (effectMissile=true)
switch(launchChoice)
if(0) //If we are launching all the turfs at once
for (var/turf/T in acceptableTurfs)
launchList |= typecache_filter_list_reverse(T.contents, ignored_atoms) //We filter any blacklisted atoms and add the rest to the launchList
if(1) //If we are launching one at a time
if(LAUNCH_ALL) //If we are launching all the turfs at once
for (var/t in acceptableTurfs)
var/turf/accepted_turf = t
launchList |= typecache_filter_list_reverse(accepted_turf.contents, ignored_atoms) //We filter any blacklisted atoms and add the rest to the launchList
if (iswallturf(accepted_turf))
launchList += accepted_turf
if(LAUNCH_ORDERED) //If we are launching one at a time
if (launchCounter > acceptableTurfs.len) //Check if the launchCounter, which acts as an index, is too high. If it is, reset it to 1
launchCounter = 1 //Note that the launchCounter index is incremented in the launch() proc
for (var/atom/movable/O in acceptableTurfs[launchCounter].contents) //Go through the acceptableTurfs list based on the launchCounter index
launchList |= typecache_filter_list_reverse(acceptableTurfs[launchCounter].contents, ignored_atoms) //Filter the specicic turf chosen from acceptableTurfs, and add it to the launchList
if(2) //If we are launching randomly
launchList |= typecache_filter_list_reverse(pick_n_take(acceptableTurfs).contents, ignored_atoms) //filter a random turf from the acceptableTurfs list and add it to the launchList
var/turf/next_turf_in_line = acceptableTurfs[launchCounter]
launchList |= typecache_filter_list_reverse(next_turf_in_line.contents, ignored_atoms) //Filter the specicic turf chosen from acceptableTurfs, and add it to the launchList
if (iswallturf(next_turf_in_line))
launchList += next_turf_in_line
if(LAUNCH_RANDOM) //If we are launching randomly
var/turf/acceptable_turf = pick_n_take(acceptableTurfs)
launchList |= typecache_filter_list_reverse(acceptable_turf.contents, ignored_atoms) //filter a random turf from the acceptableTurfs list and add it to the launchList
if (iswallturf(acceptable_turf))
launchList += acceptable_turf
updateSelector() //Call updateSelector(), which, if we are launching one at a time (launchChoice==2), will move to the next turf that will be launched
//UpdateSelector() is here (instead if the if(1) switch block) because it also moves the selector to nullspace (to hide it) if needed
/datum/centcom_podlauncher/proc/launch(turf/A) //Game time started
if (isnull(A))
/datum/centcom_podlauncher/proc/launch(turf/target_turf) //Game time started
if (isnull(target_turf))
return
var/obj/structure/closet/supplypod/centcompod/toLaunch = DuplicateObject(temp_pod) //Duplicate the temp_pod (which we have been varediting or configuring with the UI) and store the result
/*
if(dropoff_turf)
toLaunch.reverse_dropoff_turf = dropoff_turf
else
toLaunch.reverse_dropoff_turf = bay //Bay is currently a nonstatic expression, so it cant go into toLaunch using DuplicateObject
*/
toLaunch.update_icon()//we update_icon() here so that the door doesnt "flicker on" right after it lands
// var/shippingLane = GLOB.areas_by_type[/area/centcom/supplypod/fly_me_to_the_moon]
// toLaunch.forceMove(shippingLane) The shipping lane is temporarily closed due to ratvarian blockades
var/shippingLane = GLOB.areas_by_type[/area/centcom/supplypod/supplypod_temp_holding]
toLaunch.forceMove(shippingLane)
if (launchClone) //We arent launching the actual items from the bay, rather we are creating clones and launching those
if(launchRandomItem)
var/atom/movable/O = pick_n_take(launchList)
DuplicateObject(O).forceMove(toLaunch) //Duplicate a single atom/movable from launchList and forceMove it into the supplypod
var/launch_candidate = pick_n_take(launchList)
if(!isnull(launch_candidate))
if (iswallturf(launch_candidate))
var/atom/atom_to_launch = launch_candidate
toLaunch.turfs_in_cargo += atom_to_launch.type
else
var/atom/movable/movable_to_launch = launch_candidate
DuplicateObject(movable_to_launch).forceMove(toLaunch) //Duplicate a single atom/movable from launchList and forceMove it into the supplypod
else
for (var/atom/movable/O in launchList)
DuplicateObject(O).forceMove(toLaunch) //Duplicate each atom/movable in launchList and forceMove them into the supplypod
for (var/launch_candidate in launchList)
if (isnull(launch_candidate))
continue
if (iswallturf(launch_candidate))
var/turf/turf_to_launch = launch_candidate
toLaunch.turfs_in_cargo += turf_to_launch.type
else
var/atom/movable/movable_to_launch = launch_candidate
DuplicateObject(movable_to_launch).forceMove(toLaunch) //Duplicate each atom/movable in launchList and forceMove them into the supplypod
else
if(launchRandomItem)
var/atom/movable/O = pick_n_take(launchList)
O.forceMove(toLaunch) //and forceMove any atom/moveable into the supplypod
var/atom/random_item = pick_n_take(launchList)
if(!isnull(random_item))
if (iswallturf(random_item))
var/turf/wall = random_item
toLaunch.turfs_in_cargo += wall.type
wall.ScrapeAway()
else
var/atom/movable/random_item_movable = random_item
random_item_movable.forceMove(toLaunch) //and forceMove any atom/moveable into the supplypod
else
for (var/atom/movable/O in launchList) //If we aren't cloning the objects, just go through the launchList
O.forceMove(toLaunch) //and forceMove any atom/moveable into the supplypod
new /obj/effect/abstract/DPtarget(A, toLaunch) //Then, create the DPTarget effect, which will eventually forceMove the temp_pod to it's location
for (var/thing_to_launch in launchList) //If we aren't cloning the objects, just go through the launchList
if (isnull(thing_to_launch))
continue
if(iswallturf(thing_to_launch))
var/turf/wall = thing_to_launch
toLaunch.turfs_in_cargo += wall.type
wall.ScrapeAway()
else
var/atom/movable/movable_to_launch = thing_to_launch
movable_to_launch.forceMove(toLaunch) //and forceMove any atom/moveable into the supplypod
new /obj/effect/pod_landingzone(target_turf, toLaunch) //Then, create the DPTarget effect, which will eventually forceMove the temp_pod to it's location
if (launchClone)
launchCounter++ //We only need to increment launchCounter if we are cloning objects.
//If we aren't cloning objects, taking and removing the first item each time from the acceptableTurfs list will inherently iterate through the list in order
/datum/centcom_podlauncher/proc/updateSelector() //Ensures that the selector effect will showcase the next item if needed
if (launchChoice == 1 && length(acceptableTurfs) && !temp_pod.reversing && !temp_pod.effectMissile) //We only show the selector if we are taking items from the bay
var/index = launchCounter + 1 //launchCounter acts as an index to the ordered acceptableTurfs list, so adding one will show the next item in the list
if (launchChoice == LAUNCH_ORDERED && length(acceptableTurfs) > 1 && !temp_pod.reversing && !temp_pod.effectMissile) //We only show the selector if we are taking items from the bay
var/index = (launchCounter == 1 ? launchCounter : launchCounter + 1) //launchCounter acts as an index to the ordered acceptableTurfs list, so adding one will show the next item in the list. We don't want to do this for the very first item tho
if (index > acceptableTurfs.len) //out of bounds check
index = 1
selector.forceMove(acceptableTurfs[index]) //forceMove the selector to the next turf in the ordered acceptableTurfs list
@@ -649,31 +767,106 @@
qdel(O)
for (var/mob/M in bay.GetAllContents())
qdel(M)
for (var/bayturf in bay)
var/turf/turf_to_clear = bayturf
turf_to_clear.ChangeTurf(/turf/open/floor/plasteel)
/datum/centcom_podlauncher/Destroy() //The Destroy() proc. This is called by ui_close proc, or whenever the user leaves the game
updateCursor(FALSE, FALSE) //Make sure our moues cursor resets to default. False means we are not in launch mode
qdel(temp_pod) //Delete the temp_pod
qdel(selector) //Delete the selector effect
updateCursor(TRUE) //Make sure our moues cursor resets to default. False means we are not in launch mode
QDEL_NULL(temp_pod) //Delete the temp_pod
QDEL_NULL(selector) //Delete the selector effect
QDEL_NULL(indicator)
. = ..()
/datum/centcom_podlauncher/proc/supplypod_punish_log(var/list/whoDyin)
/datum/centcom_podlauncher/proc/supplypod_punish_log(list/whoDyin)
var/podString = effectBurst ? "5 pods" : "a pod"
var/whomString = ""
if (LAZYLEN(whoDyin))
for (var/mob/living/M in whoDyin)
whomString += "[key_name(M)], "
var/delayString = temp_pod.landingDelay == initial(temp_pod.landingDelay) ? "" : " Delay=[temp_pod.landingDelay*0.1]s"
var/damageString = temp_pod.damage == 0 ? "" : " Dmg=[temp_pod.damage]"
var/explosionString = ""
var/explosion_sum = temp_pod.explosionSize[1] + temp_pod.explosionSize[2] + temp_pod.explosionSize[3] + temp_pod.explosionSize[4]
if (explosion_sum != 0)
explosionString = " Boom=|"
for (var/X in temp_pod.explosionSize)
explosionString += "[X]|"
var/msg = "launched [podString] towards [whomString] [delayString][damageString][explosionString]"
var/msg = "launched [podString] towards [whomString]"
message_admins("[key_name_admin(usr)] [msg] in [ADMIN_VERBOSEJMP(specificTarget)].")
if (length(whoDyin))
for (var/mob/living/M in whoDyin)
admin_ticket_log(M, "[key_name_admin(usr)] [msg]")
/datum/centcom_podlauncher/proc/loadData(var/list/dataToLoad)
bayNumber = dataToLoad["bayNumber"]
customDropoff = dataToLoad["customDropoff"]
renderLighting = dataToLoad["renderLighting"]
launchClone = dataToLoad["launchClone"] //Do we launch the actual items in the bay or just launch clones of them?
launchRandomItem = dataToLoad["launchRandomItem"] //Do we launch a single random item instead of everything on the turf?
launchChoice = dataToLoad["launchChoice"] //Launch turfs all at once (0), ordered (1), or randomly(1)
explosionChoice = dataToLoad["explosionChoice"] //An explosion that occurs when landing. Can be no explosion (0), custom explosion (1), or maxcap (2)
damageChoice = dataToLoad["damageChoice"] //Damage that occurs to any mob under the pod when it lands. Can be no damage (0), custom damage (1), or gib+5000dmg (2)
temp_pod.landingDelay = dataToLoad["delay_1"] //How long the pod takes to land after launching
temp_pod.fallDuration = dataToLoad["delay_2"] //How long the pod's falling animation lasts
temp_pod.openingDelay = dataToLoad["delay_3"] //How long the pod takes to open after landing
temp_pod.departureDelay = dataToLoad["delay_4"] //How long the pod takes to leave after opening (if bluespace=true, it deletes. if reversing=true, it flies back to centcom)
temp_pod.setStyle(dataToLoad["styleChoice"]) //Style is a variable that keeps track of what the pod is supposed to look like. It acts as an index to the GLOB.podstyles list in cargo.dm defines to get the proper icon/name/desc for the pod.
temp_pod.effectShrapnel = dataToLoad["effectShrapnel"] //If true, creates a cloud of shrapnel of a decided type and magnitude on landing
temp_pod.shrapnel_type = text2path(dataToLoad["shrapnelType"])
temp_pod.shrapnel_magnitude = dataToLoad["shrapnelMagnitude"]
temp_pod.effectStun = dataToLoad["effectStun"]//If true, stuns anyone under the pod when it launches until it lands, forcing them to get hit by the pod. Devilish!
temp_pod.effectLimb = dataToLoad["effectLimb"]//If true, pops off a limb (if applicable) from anyone caught under the pod when it lands
temp_pod.effectOrgans = dataToLoad["effectOrgans"]//If true, yeets the organs out of any bodies caught under the pod when it lands
temp_pod.bluespace = dataToLoad["effectBluespace"] //If true, the pod deletes (in a shower of sparks) after landing
temp_pod.effectStealth = dataToLoad["effectStealth"]//If true, a target icon isn't displayed on the turf where the pod will land
temp_pod.effectQuiet = dataToLoad["effectQuiet"] //The female sniper. If true, the pod makes no noise (including related explosions, opening sounds, etc)
temp_pod.effectMissile = dataToLoad["effectMissile"] //If true, the pod deletes the second it lands. If you give it an explosion, it will act like a missile exploding as it hits the ground
temp_pod.effectCircle = dataToLoad["effectCircle"] //If true, allows the pod to come in at any angle. Bit of a weird feature but whatever its here
effectBurst = dataToLoad["effectBurst"] //IOf true, launches five pods at once (with a very small delay between for added coolness), in a 3x3 area centered around the area
temp_pod.reversing = dataToLoad["effectReverse"] //If true, the pod will not send any items. Instead, after opening, it will close again (picking up items/mobs) and fly back to centcom
temp_pod.reverseOptionList = dataToLoad["reverseOptionList"]
specificTarget = dataToLoad["effectTarget"] //Launches the pod at the turf of a specific mob target, rather than wherever the user clicked. Useful for smites
temp_pod.adminNamed = dataToLoad["effectName"] //Determines whether or not the pod has been named by an admin. If true, the pod's name will not get overridden when the style of the pod changes (changing the style of the pod normally also changes the name+desc)
temp_pod.name = dataToLoad["podName"]
temp_pod.desc = dataToLoad["podDesc"]
effectAnnounce = dataToLoad["effectAnnounce"]
numTurfs = dataToLoad["numObjects"] //Counts the number of turfs that contain a launchable object in the centcom supplypod bay
temp_pod.fallingSound = dataToLoad["fallingSound"]//Admin sound to play as the pod falls
temp_pod.landingSound = dataToLoad["landingSound"]//Admin sound to play when the pod lands
temp_pod.openingSound = dataToLoad["openingSound"]//Admin sound to play when the pod opens
temp_pod.leavingSound = dataToLoad["leavingSound"]//Admin sound to play when the pod leaves
temp_pod.soundVolume = dataToLoad["soundVolume"] //Admin sound to play when the pod leaves
picking_dropoff_turf = FALSE
launcherActivated = FALSE
updateCursor()
refreshView()
GLOBAL_DATUM_INIT(podlauncher, /datum/centcom_podlauncher, new)
//Proc for admins to enable others to use podlauncher after roundend
/datum/centcom_podlauncher/proc/give_podlauncher(mob/living/user, override)
if (SSticker.current_state < GAME_STATE_FINISHED)
return
if (!istype(user))
user = override
if (user)
setup(user)//setup the datum
//Set the dropoff location and indicator to either a specific turf or somewhere in an area
/datum/centcom_podlauncher/proc/setDropoff(target)
var/turf/target_turf
if (isturf(target))
target_turf = target
else if (isarea(target))
target_turf = pick(get_area_turfs(target))
else
CRASH("Improper type passed to setDropoff! Should be /turf or /area")
temp_pod.reverse_dropoff_coords = list(target_turf.x, target_turf.y, target_turf.z)
indicator.forceMove(target_turf)
/obj/effect/hallucination/simple/supplypod_selector
name = "Supply Selector (Only you can see this)"
image_icon = 'icons/obj/supplypods_32x32.dmi'
image_state = "selector"
image_layer = FLY_LAYER
alpha = 150
/obj/effect/hallucination/simple/dropoff_location
name = "Dropoff Location (Only you can see this)"
image_icon = 'icons/obj/supplypods_32x32.dmi'
image_state = "dropoff_indicator"
image_layer = FLY_LAYER
alpha = 0
+2 -2
View File
@@ -190,7 +190,7 @@
LZ = pick(empty_turfs)
if (SO.pack.cost <= points_to_check && LZ)//we need to call the cost check again because of the CHECK_TICK call
D.adjust_money(-SO.pack.cost)
new /obj/effect/abstract/DPtarget(LZ, podType, SO)
new /obj/effect/pod_landingzone(LZ, podType, SO)
. = TRUE
update_icon()
else
@@ -208,7 +208,7 @@
for(var/i in 1 to MAX_EMAG_ROCKETS)
var/LZ = pick(empty_turfs)
LAZYREMOVE(empty_turfs, LZ)
new /obj/effect/abstract/DPtarget(LZ, podType, SO)
new /obj/effect/pod_landingzone(LZ, podType, SO)
. = TRUE
update_icon()
CHECK_TICK
+5 -5
View File
@@ -42,7 +42,7 @@
set name = "Release Contents"
set category = "Gondola"
set desc = "Release any contents stored within your vast belly."
linked_pod.open(src, forced = TRUE)
linked_pod.open(src, TRUE)
/mob/living/simple_animal/pet/gondola/gondolapod/examine(mob/user)
..()
@@ -61,16 +61,16 @@
else
to_chat(src, "<span class='notice'>A closer look inside yourself reveals... nothing.</span>")
/mob/living/simple_animal/pet/gondola/gondolapod/proc/setOpened()
/mob/living/simple_animal/pet/gondola/gondolapod/setOpened()
opened = TRUE
update_icon()
addtimer(CALLBACK(src, .proc/setClosed), 50)
addtimer(CALLBACK(src, /atom.proc/setClosed), 50)
/mob/living/simple_animal/pet/gondola/gondolapod/proc/setClosed()
/mob/living/simple_animal/pet/gondola/gondolapod/setClosed()
opened = FALSE
update_icon()
/mob/living/simple_animal/pet/gondola/gondolapod/death()
qdel(linked_pod) //Will cause the open() proc for the linked supplypod to be called with the "broken" parameter set to true, meaning that it will dump its contents on death
qdel(src)
..()
..()
+489 -189
View File
@@ -1,21 +1,25 @@
//The "BDPtarget" temp visual is created by anything that "launches" a supplypod. It makes two things: a falling droppod animation, and the droppod itself.
//The "pod_landingzone" temp visual is created by anything that "launches" a supplypod. This is what animates the pod and makes the pod forcemove to the station.
//------------------------------------SUPPLY POD-------------------------------------//
/obj/structure/closet/supplypod
name = "supply pod" //Names and descriptions are normally created with the setStyle() proc during initialization, but we have these default values here as a failsafe
desc = "A Nanotrasen supply drop pod."
icon = 'icons/obj/supplypods.dmi'
icon_state = "supplypod"
pixel_x = -16 //2x2 sprite
pixel_y = -5
layer = TABLE_LAYER //So that the crate inside doesn't appear underneath
icon_state = "pod" //This is a common base sprite shared by a number of pods
pixel_x = SUPPLYPOD_X_OFFSET //2x2 sprite
layer = BELOW_OBJ_LAYER //So that the crate inside doesn't appear underneath
allow_objects = TRUE
allow_dense = TRUE
delivery_icon = null
can_weld_shut = FALSE
armor = list("melee" = 30, "bullet" = 50, "laser" = 50, "energy" = 100, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 80)
armor = list(MELEE = 30, BULLET = 50, LASER = 50, ENERGY = 100, BOMB = 100, BIO = 0, RAD = 0, FIRE = 100, ACID = 80)
anchored = TRUE //So it cant slide around after landing
anchorable = FALSE
flags_1 = PREVENT_CONTENTS_EXPLOSION_1
appearance_flags = KEEP_TOGETHER | PIXEL_SCALE
density = FALSE
///List of bitflags for supply pods, see: code\__DEFINES\obj_flags.dm
var/pod_flags = NONE
//*****NOTE*****: Many of these comments are similarly described in centcom_podlauncher.dm. If you change them here, please consider doing so in the centcom podlauncher code as well!
var/adminNamed = FALSE //Determines whether or not the pod has been named by an admin. If true, the pod's name will not get overridden when the style of the pod changes (changing the style of the pod normally also changes the name+desc)
var/bluespace = FALSE //If true, the pod deletes (in a shower of sparks) after landing
@@ -27,12 +31,13 @@
var/effectLimb = FALSE //If true, pops off a limb (if applicable) from anyone caught under the pod when it lands
var/effectOrgans = FALSE //If true, yeets out every limb and organ from anyone caught under the pod when it lands
var/effectGib = FALSE //If true, anyone under the pod will be gibbed when it lands
var/effectStealth = FALSE //If true, a target icon isnt displayed on the turf where the pod will land
var/effectStealth = FALSE //If true, a target icon isn't displayed on the turf where the pod will land
var/effectQuiet = FALSE //The female sniper. If true, the pod makes no noise (including related explosions, opening sounds, etc)
var/effectMissile = FALSE //If true, the pod deletes the second it lands. If you give it an explosion, it will act like a missile exploding as it hits the ground
var/effectCircle = FALSE //If true, allows the pod to come in at any angle. Bit of a weird feature but whatever its here
var/style = STYLE_STANDARD //Style is a variable that keeps track of what the pod is supposed to look like. It acts as an index to the POD_STYLES list in cargo.dm defines to get the proper icon/name/desc for the pod.
var/style = STYLE_STANDARD //Style is a variable that keeps track of what the pod is supposed to look like. It acts as an index to the GLOB.podstyles list in cargo.dm defines to get the proper icon/name/desc for the pod.
var/reversing = FALSE //If true, the pod will not send any items. Instead, after opening, it will close again (picking up items/mobs) and fly back to centcom
var/list/reverse_dropoff_coords //Turf that the reverse pod will drop off it's newly-acquired cargo to
var/fallDuration = 4
var/fallingSoundLength = 11
var/fallingSound = 'sound/weapons/mortar_long_whistle.ogg'//Admin sound to play before the pod lands
@@ -40,10 +45,20 @@
var/openingSound //Admin sound to play when the pod opens
var/leavingSound //Admin sound to play when the pod leaves
var/soundVolume = 80 //Volume to play sounds at. Ignores the cap
var/bay //Used specifically for the centcom_podlauncher datum. Holds the current bay the user is launching objects from. Bays are specific rooms on the centcom map.
var/list/explosionSize = list(0,0,2,3)
var/stay_after_drop = FALSE
var/specialised = TRUE // It's not a general use pod for cargo/admin use
var/specialised = FALSE // It's not a general use pod for cargo/admin use
var/rubble_type //Rubble effect associated with this supplypod
var/decal = "default" //What kind of extra decals we add to the pod to make it look nice
var/door = "pod_door"
var/fin_mask = "topfin"
var/obj/effect/supplypod_rubble/rubble
var/obj/effect/engineglow/glow_effect
var/effectShrapnel = FALSE
var/shrapnel_type = /obj/item/projectile/bullet/shrapnel
var/shrapnel_magnitude = 3
var/list/reverseOptionList = list("Mobs"=FALSE,"Objects"=FALSE,"Anchored"=FALSE,"Underfloor"=FALSE,"Wallmounted"=FALSE,"Floors"=FALSE,"Walls"=FALSE)
var/list/turfs_in_cargo = list()
/obj/structure/closet/supplypod/bluespacepod
style = STYLE_BLUESPACE
@@ -53,12 +68,12 @@
/obj/structure/closet/supplypod/extractionpod
name = "Syndicate Extraction Pod"
desc = "A specalised, blood-red styled pod for extracting high-value targets out of active mission areas."
desc = "A specalised, blood-red styled pod for extracting high-value targets out of active mission areas. <b>Targets must be manually stuffed inside the pod for proper delivery.</b>"
specialised = TRUE
style = STYLE_SYNDICATE
bluespace = TRUE
explosionSize = list(0,0,1,2)
landingDelay = 25 //Slightly longer than others
landingDelay = 25 //Longer than others
/obj/structure/closet/supplypod/centcompod
style = STYLE_CENTCOM
@@ -67,36 +82,102 @@
landingDelay = 20 //Very speedy!
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
/obj/structure/closet/supplypod/proc/specialisedPod()
return 1
/obj/structure/closet/supplypod/extractionpod/specialisedPod(atom/movable/holder)
holder.forceMove(pick(GLOB.holdingfacility)) // land in ninja jail
open(holder, forced = TRUE)
/obj/structure/closet/supplypod/Initialize()
/obj/structure/closet/supplypod/Initialize(var/customStyle = FALSE)
. = ..()
setStyle(style, TRUE) //Upon initialization, give the supplypod an iconstate, name, and description based on the "style" variable. This system is important for the centcom_podlauncher to function correctly
if (!loc)
var/shippingLane = GLOB.areas_by_type[/area/centcom/supplypod/supplypod_temp_holding] //temporary holder for supplypods mid-transit
forceMove(shippingLane)
if (customStyle)
style = customStyle
setStyle(style) //Upon initialization, give the supplypod an iconstate, name, and description based on the "style" variable. This system is important for the centcom_podlauncher to function correctly
/obj/structure/closet/supplypod/extractionpod/Initialize()
. = ..()
var/turf/picked_turf = pick(GLOB.holdingfacility)
reverse_dropoff_coords = list(picked_turf.x, picked_turf.y, picked_turf.z)
/obj/structure/closet/supplypod/proc/setStyle(chosenStyle) //Used to give the sprite an icon state, name, and description.
style = chosenStyle
var/base = GLOB.podstyles[chosenStyle][POD_BASE] //GLOB.podstyles is a 2D array we treat as a dictionary. The style represents the verticle index, with the icon state, name, and desc being stored in the horizontal indexes of the 2D array.
icon_state = base
decal = GLOB.podstyles[chosenStyle][POD_DECAL]
rubble_type = GLOB.podstyles[chosenStyle][POD_RUBBLE_TYPE]
if (!adminNamed && !specialised) //We dont want to name it ourselves if it has been specifically named by an admin using the centcom_podlauncher datum
name = GLOB.podstyles[chosenStyle][POD_NAME]
desc = GLOB.podstyles[chosenStyle][POD_DESC]
if (GLOB.podstyles[chosenStyle][POD_DOOR])
door = "[base]_door"
else
door = FALSE
update_icon()
/obj/structure/closet/supplypod/proc/SetReverseIcon()
fin_mask = "bottomfin"
if (GLOB.podstyles[style][POD_SHAPE] == POD_SHAPE_NORML)
icon_state = GLOB.podstyles[style][POD_BASE] + "_reverse"
pixel_x = initial(pixel_x)
transform = matrix()
update_icon()
/obj/structure/closet/supplypod/proc/backToNonReverseIcon()
fin_mask = initial(fin_mask)
if (GLOB.podstyles[style][POD_SHAPE] == POD_SHAPE_NORML)
icon_state = GLOB.podstyles[style][POD_BASE]
pixel_x = initial(pixel_x)
transform = matrix()
update_icon()
/obj/structure/closet/supplypod/closet_update_overlays(list/new_overlays)
. = new_overlays
if (style == STYLE_SEETHROUGH || style == STYLE_INVISIBLE) //If we're invisible, we dont bother adding any overlays
return
if (opened)
. += "[icon_state]_open"
else
. += "[icon_state]_door"
return
/obj/structure/closet/supplypod/proc/setStyle(chosenStyle, var/duringInit = FALSE) //Used to give the sprite an icon state, name, and description
if (!duringInit && style == chosenStyle) //Check if the input style is already the same as the pod's style. This happens in centcom_podlauncher, and as such we set the style to STYLE_CENTCOM.
setStyle(STYLE_CENTCOM) //We make sure to not check this during initialize() so the standard supplypod works correctly.
/obj/structure/closet/supplypod/update_overlays()
. = ..()
if (style == STYLE_INVISIBLE)
return
style = chosenStyle
icon_state = POD_STYLES[chosenStyle][POD_ICON_STATE] //POD_STYLES is a 2D array we treat as a dictionary. The style represents the verticle index, with the icon state, name, and desc being stored in the horizontal indexes of the 2D array.
if (!adminNamed && !specialised) //We dont want to name it ourselves if it has been specifically named by an admin using the centcom_podlauncher datum
name = POD_STYLES[chosenStyle][POD_NAME]
desc = POD_STYLES[chosenStyle][POD_DESC]
update_icon()
if (rubble)
. += rubble.getForeground(src)
if (style == STYLE_SEETHROUGH)
for (var/atom/A in contents)
var/mutable_appearance/itemIcon = new(A)
itemIcon.transform = matrix().Translate(-1 * SUPPLYPOD_X_OFFSET, 0)
. += itemIcon
for (var/t in turfs_in_cargo)//T is just a turf's type
var/turf/turf_type = t
var/mutable_appearance/itemIcon = mutable_appearance(initial(turf_type.icon), initial(turf_type.icon_state))
itemIcon.transform = matrix().Translate(-1 * SUPPLYPOD_X_OFFSET, 0)
. += itemIcon
return
if (opened) //We're opened means all we have to worry about is masking a decal if we have one
if (!decal) //We don't have a decal to mask
return
if (!door) //We have a decal but no door, so let's just add the decal
. += decal
return
var/icon/masked_decal = new(icon, decal) //The decal we want to apply
var/icon/door_masker = new(icon, door) //The door shape we want to 'cut out' of the decal
door_masker.MapColors(0,0,0,1, 0,0,0,1, 0,0,0,1, 1,1,1,0, 0,0,0,1)
door_masker.SwapColor("#ffffffff", null)
door_masker.Blend("#000000", ICON_SUBTRACT)
masked_decal.Blend(door_masker, ICON_ADD)
. += masked_decal
else //If we're closed
if (!door) //We have no door, lets see if we have a decal. If not, theres nothing we need to do
if (decal)
. += decal
return
else if (GLOB.podstyles[style][POD_SHAPE] != POD_SHAPE_NORML) //If we're not a normal pod shape (aka, if we don't have fins), just add the door without masking
. += door
else
var/icon/masked_door = new(icon, door) //The door we want to apply
var/icon/fin_masker = new(icon, "mask_[fin_mask]") //The fin shape we want to 'cut out' of the door
fin_masker.MapColors(0,0,0,1, 0,0,0,1, 0,0,0,1, 1,1,1,0, 0,0,0,1)
fin_masker.SwapColor("#ffffffff", null)
fin_masker.Blend("#000000", ICON_SUBTRACT)
masked_door.Blend(fin_masker, ICON_ADD)
. += masked_door
if (decal)
. += decal
/obj/structure/closet/supplypod/tool_interact(obj/item/W, mob/user)
if(bluespace) //We dont want to worry about interacting with bluespace pods, as they are due to delete themselves soon anyways.
@@ -110,187 +191,380 @@
/obj/structure/closet/supplypod/contents_explosion() //Supplypods also protect their contents from the harmful effects of fucking exploding.
return
/obj/structure/closet/supplypod/toggle(mob/living/user) //Supplypods shouldn't be able to be manually opened under any circumstances, as the open() proc generates supply order datums
/obj/structure/closet/supplypod/toggle(mob/living/user)
return
/obj/structure/closet/supplypod/proc/handleReturningClose(atom/movable/holder, returntobay)
opened = FALSE
INVOKE_ASYNC(holder, .proc/setClosed) //Use the INVOKE_ASYNC proc to call setClosed() on whatever the holder may be, without giving the atom/movable base class a setClosed() proc definition
for(var/atom/movable/O in get_turf(holder))
if ((ismob(O) && !isliving(O)) || (is_type_in_typecache(O, GLOB.blacklisted_cargo_types) && !isliving(O))) //We dont want to take ghosts with us, and we don't want blacklisted items going, but we allow mobs.
continue
O.forceMove(holder) //Put objects inside before we close
var/obj/effect/temp_visual/risingPod = new /obj/effect/abstract/DPfall(get_turf(holder), src) //Make a nice animation of flying back up
risingPod.pixel_z = 0 //The initial value of risingPod's pixel_z is 200 because it normally comes down from a high spot
animate(risingPod, pixel_z = 200, time = 10, easing = LINEAR_EASING) //Animate our rising pod
if(returntobay)
holder.forceMove(bay) //Move the pod back to centcom, where it belongs
QDEL_IN(risingPod, 10)
reversing = FALSE //Now that we're done reversing, we set this to false (otherwise we would get stuck in an infinite loop of calling the close proc at the bottom of open() )
bluespace = TRUE //Make it so that the pod doesn't stay in centcom forever
open(holder, forced = TRUE)
else
reversing = FALSE //Now that we're done reversing, we set this to false (otherwise we would get stuck in an infinite loop of calling the close proc at the bottom of open() )
bluespace = TRUE //Make it so that the pod doesn't stay in centcom forever
QDEL_IN(risingPod, 10)
audible_message("<span class='notice'>The pod hisses, closing quickly and launching itself away from the station.</span>", "<span class='notice'>The ground vibrates, the nearby pod launching away from the station.</span>")
stay_after_drop = FALSE
specialisedPod(holder) // Do special actions for specialised pods - this is likely if we were already doing manual launches
/obj/structure/closet/supplypod/open(mob/living/user, force = TRUE)
return
/obj/structure/closet/supplypod/proc/preOpen() //Called before the open() proc. Handles anything that occurs right as the pod lands.
var/turf/T = get_turf(src)
/obj/structure/closet/supplypod/proc/handleReturnAfterDeparting(atom/movable/holder = src)
reversing = FALSE //Now that we're done reversing, we set this to false (otherwise we would get stuck in an infinite loop of calling the close proc at the bottom of open_pod() )
bluespace = TRUE //Make it so that the pod doesn't stay in centcom forever
pod_flags &= ~FIRST_SOUNDS //Make it so we play sounds now
if (!effectQuiet && style != STYLE_SEETHROUGH)
audible_message("<span class='notice'>The pod hisses, closing and launching itself away from the station.</span>", "<span class='notice'>The ground vibrates, and you hear the sound of engines firing.</span>")
stay_after_drop = FALSE
holder.pixel_z = initial(holder.pixel_z)
holder.alpha = initial(holder.alpha)
var/shippingLane = GLOB.areas_by_type[/area/centcom/supplypod/supplypod_temp_holding]
forceMove(shippingLane) //Move to the centcom-z-level until the pod_landingzone says we can drop back down again
if (!reverse_dropoff_coords) //If we're centcom-launched, the reverse dropoff turf will be a centcom loading bay. If we're an extraction pod, it should be the ninja jail. Thus, this shouldn't ever really happen.
var/obj/error_landmark = locate(/obj/effect/landmark/error) in GLOB.landmarks_list
var/turf/error_landmark_turf = get_turf(error_landmark)
reverse_dropoff_coords = list(error_landmark_turf.x, error_landmark_turf.y, error_landmark_turf.z)
landingDelay = initial(landingDelay) //Reset the landing timers so we land on whatever turf we're aiming at normally. Will be changed to be editable later (tm)
fallDuration = initial(fallDuration) //This is so if someone adds a really long dramatic landing time they don't have to sit through it twice on the pod's return trip
openingDelay = initial(openingDelay)
backToNonReverseIcon()
var/turf/return_turf = locate(reverse_dropoff_coords[1], reverse_dropoff_coords[2], reverse_dropoff_coords[3])
new /obj/effect/pod_landingzone(return_turf, src)
/obj/structure/closet/supplypod/proc/preOpen() //Called before the open_pod() proc. Handles anything that occurs right as the pod lands.
var/turf/turf_underneath = get_turf(src)
var/list/B = explosionSize //Mostly because B is more readable than explosionSize :p
if (landingSound)
playsound(get_turf(src), landingSound, soundVolume, 0, 0)
for (var/mob/living/M in T)
if (effectLimb && iscarbon(M)) //If effectLimb is true (which means we pop limbs off when we hit people):
var/mob/living/carbon/CM = M
for (var/obj/item/bodypart/bodypart in CM.bodyparts) //Look at the bodyparts in our poor mob beneath our pod as it lands
if(bodypart.body_part != HEAD && bodypart.body_part != CHEST)//we dont want to kill him, just teach em a lesson!
if (bodypart.dismemberable)
bodypart.dismember() //Using the power of flextape i've sawed this man's limb in half!
break
if (effectOrgans && iscarbon(M)) //effectOrgans means remove every organ in our mob
var/mob/living/carbon/CM = M
for(var/X in CM.internal_organs)
var/destination = get_edge_target_turf(T, pick(GLOB.alldirs)) //Pick a random direction to toss them in
var/obj/item/organ/O = X
O.Remove() //Note that this isn't the same proc as for lists
O.forceMove(T) //Move the organ outta the body
O.throw_at(destination, 2, 3) //Thow the organ at a random tile 3 spots away
sleep(1)
for (var/obj/item/bodypart/bodypart in CM.bodyparts) //Look at the bodyparts in our poor mob beneath our pod as it lands
var/destination = get_edge_target_turf(T, pick(GLOB.alldirs))
if (bodypart.dismemberable)
bodypart.dismember() //Using the power of flextape i've sawed this man's bodypart in half!
bodypart.throw_at(destination, 2, 3)
density = TRUE //Density is originally false so the pod doesn't block anything while it's still falling through the air
AddComponent(/datum/component/pellet_cloud, projectile_type=shrapnel_type, magnitude=shrapnel_magnitude)
if(effectShrapnel)
SEND_SIGNAL(src, COMSIG_SUPPLYPOD_LANDED)
for (var/mob/living/target_living in turf_underneath)
if (iscarbon(target_living)) //If effectLimb is true (which means we pop limbs off when we hit people):
if (effectLimb)
var/mob/living/carbon/carbon_target_mob = target_living
for (var/bp in carbon_target_mob.bodyparts) //Look at the bodyparts in our poor mob beneath our pod as it lands
var/obj/item/bodypart/bodypart = bp
if(bodypart.body_part != HEAD && bodypart.body_part != CHEST)//we dont want to kill him, just teach em a lesson!
if (bodypart.dismemberable)
bodypart.dismember() //Using the power of flextape i've sawed this man's limb in half!
break
if (effectOrgans) //effectOrgans means remove every organ in our mob
var/mob/living/carbon/carbon_target_mob = target_living
for(var/organ in carbon_target_mob.internal_organs)
var/destination = get_edge_target_turf(turf_underneath, pick(GLOB.alldirs)) //Pick a random direction to toss them in
var/obj/item/organ/organ_to_yeet = organ
organ_to_yeet.Remove(carbon_target_mob) //Note that this isn't the same proc as for lists
organ_to_yeet.forceMove(turf_underneath) //Move the organ outta the body
organ_to_yeet.throw_at(destination, 2, 3) //Thow the organ at a random tile 3 spots away
sleep(1)
for (var/bp in carbon_target_mob.bodyparts) //Look at the bodyparts in our poor mob beneath our pod as it lands
var/obj/item/bodypart/bodypart = bp
var/destination = get_edge_target_turf(turf_underneath, pick(GLOB.alldirs))
if (bodypart.dismemberable)
bodypart.dismember() //Using the power of flextape i've sawed this man's bodypart in half!
bodypart.throw_at(destination, 2, 3)
sleep(1)
if (effectGib) //effectGib is on, that means whatever's underneath us better be fucking oof'd on
M.adjustBruteLoss(5000) //THATS A LOT OF DAMAGE (called just in case gib() doesnt work on em)
M.gib() //After adjusting the fuck outta that brute loss we finish the job with some satisfying gibs
target_living.adjustBruteLoss(5000) //THATS A LOT OF DAMAGE (called just in case gib() doesnt work on em)
if (!QDELETED(target_living))
target_living.gib() //After adjusting the fuck outta that brute loss we finish the job with some satisfying gibs
else
M.adjustBruteLoss(damage)
target_living.adjustBruteLoss(damage)
var/explosion_sum = B[1] + B[2] + B[3] + B[4]
if (explosion_sum != 0) //If the explosion list isn't all zeroes, call an explosion
explosion(get_turf(src), B[1], B[2], B[3], flame_range = B[4], silent = effectQuiet, ignorecap = istype(src, /obj/structure/closet/supplypod/centcompod)) //less advanced equipment than bluespace pod, so larger explosion when landing
else if (!effectQuiet) //If our explosion list IS all zeroes, we still make a nice explosion sound (unless the effectQuiet var is true)
playsound(src, "explosion", landingSound ? 15 : 80, 1)
explosion(turf_underneath, B[1], B[2], B[3], flame_range = B[4], silent = effectQuiet, ignorecap = istype(src, /obj/structure/closet/supplypod/centcompod)) //less advanced equipment than bluespace pod, so larger explosion when landing
else if (!effectQuiet && !(pod_flags & FIRST_SOUNDS)) //If our explosion list IS all zeroes, we still make a nice explosion sound (unless the effectQuiet var is true)
playsound(src, "explosion", landingSound ? soundVolume * 0.25 : soundVolume, TRUE)
if (landingSound)
playsound(turf_underneath, landingSound, soundVolume, FALSE, FALSE)
if (effectMissile) //If we are acting like a missile, then right after we land and finish fucking shit up w explosions, we should delete
opened = TRUE //We set opened to TRUE to avoid spending time trying to open (due to being deleted) during the Destroy() proc
qdel(src)
return
if (style == STYLE_GONDOLA) //Checks if we are supposed to be a gondola pod. If so, create a gondolapod mob, and move this pod to nullspace. I'd like to give a shout out, to my man oranges
var/mob/living/simple_animal/pet/gondola/gondolapod/benis = new(get_turf(src), src)
var/mob/living/simple_animal/pet/gondola/gondolapod/benis = new(turf_underneath, src)
benis.contents |= contents //Move the contents of this supplypod into the gondolapod mob.
moveToNullspace()
addtimer(CALLBACK(src, .proc/open, benis), openingDelay) //After the openingDelay passes, we use the open proc from this supplyprod while referencing the contents of the "holder", in this case the gondolapod mob
addtimer(CALLBACK(src, .proc/open_pod, benis), openingDelay) //After the openingDelay passes, we use the open proc from this supplyprod while referencing the contents of the "holder", in this case the gondolapod mob
else if (style == STYLE_SEETHROUGH)
open_pod(src)
else
addtimer(CALLBACK(src, .proc/open, src), openingDelay) //After the openingDelay passes, we use the open proc from this supplypod, while referencing this supplypod's contents
addtimer(CALLBACK(src, .proc/open_pod, src), openingDelay) //After the openingDelay passes, we use the open proc from this supplypod, while referencing this supplypod's contents
/obj/structure/closet/supplypod/open(atom/movable/holder, var/broken = FALSE, var/forced = FALSE) //The holder var represents an atom whose contents we will be working with
var/turf/T = get_turf(holder) //Get the turf of whoever's contents we're talking about
/obj/structure/closet/supplypod/proc/open_pod(atom/movable/holder, broken = FALSE, forced = FALSE) //The holder var represents an atom whose contents we will be working with
if (!holder)
return
if(opened)
if (opened) //This is to ensure we don't open something that has already been opened
return
opened = TRUE //This is to ensure we don't open something that has already been opened
var/mob/M
holder.setOpened()
var/turf/turf_underneath = get_turf(holder) //Get the turf of whoever's contents we're talking about
if (istype(holder, /mob)) //Allows mobs to assume the role of the holder, meaning we look at the mob's contents rather than the supplypod's contents. Typically by this point the supplypod's contents have already been moved over to the mob's contents
M = holder
if (M.key && !forced && !broken) //If we are player controlled, then we shouldnt open unless the opening is manual, or if it is due to being destroyed (represented by the "broken" parameter)
var/mob/holder_as_mob = holder
if (holder_as_mob.key && !forced && !broken) //If we are player controlled, then we shouldn't open unless the opening is manual, or if it is due to being destroyed (represented by the "broken" parameter)
return
if (openingSound)
playsound(get_turf(holder), openingSound, soundVolume, 0, 0) //Special admin sound to play
INVOKE_ASYNC(holder, .proc/setOpened) //Use the INVOKE_ASYNC proc to call setOpened() on whatever the holder may be, without giving the atom/movable base class a setOpened() proc definition
if (style == STYLE_SEETHROUGH)
update_icon()
for (var/atom/movable/O in holder.contents) //Go through the contents of the holder
O.forceMove(T) //move everything from the contents of the holder to the turf of the holder
if (!effectQuiet && !openingSound && style != STYLE_SEETHROUGH) //If we aren't being quiet, play the default pod open sound
playsound(get_turf(holder), open_sound, 15, 1, -3)
playsound(get_turf(holder), openingSound, soundVolume, FALSE, FALSE) //Special admin sound to play
for (var/turf_type in turfs_in_cargo)
turf_underneath.PlaceOnTop(turf_type)
for (var/cargo in contents)
var/atom/movable/movable_cargo = cargo
movable_cargo.forceMove(turf_underneath)
if (!effectQuiet && !openingSound && style != STYLE_SEETHROUGH && !(pod_flags & FIRST_SOUNDS)) //If we aren't being quiet, play the default pod open sound
playsound(get_turf(holder), open_sound, 15, TRUE, -3)
if (broken) //If the pod is opening because it's been destroyed, we end here
return
if (style == STYLE_SEETHROUGH)
depart(src)
startExitSequence(src)
else
if (reversing)
addtimer(CALLBACK(src, .proc/SetReverseIcon), departureDelay/2) //Finish up the pod's duties after a certain amount of time
if(!stay_after_drop) // Departing should be handled manually
addtimer(CALLBACK(src, .proc/depart, holder), departureDelay) //Finish up the pod's duties after a certain amount of time
addtimer(CALLBACK(src, .proc/startExitSequence, holder), departureDelay*(4/5)) //Finish up the pod's duties after a certain amount of time
/obj/structure/closet/supplypod/proc/depart(atom/movable/holder)
/obj/structure/closet/supplypod/proc/startExitSequence(atom/movable/holder)
if (leavingSound)
playsound(get_turf(holder), leavingSound, soundVolume, 0, 0)
playsound(get_turf(holder), leavingSound, soundVolume, FALSE, FALSE)
if (reversing) //If we're reversing, we call the close proc. This sends the pod back up to centcom
close(holder)
else if (bluespace) //If we're a bluespace pod, then delete ourselves (along with our holder, if a seperate holder exists)
deleteRubble()
if (!effectQuiet && style != STYLE_INVISIBLE && style != STYLE_SEETHROUGH)
do_sparks(5, TRUE, holder) //Create some sparks right before closing
qdel(src) //Delete ourselves and the holder
if (holder != src)
qdel(holder)
/obj/structure/closet/supplypod/centcompod/close(atom/movable/holder) //Closes the supplypod and sends it back to centcom. Should only ever be called if the "reversing" variable is true
handleReturningClose(holder, TRUE)
/obj/structure/closet/supplypod/close(atom/movable/holder) //Closes the supplypod and sends it back to centcom. Should only ever be called if the "reversing" variable is true
if (!holder)
return
take_contents(holder)
playsound(holder, close_sound, soundVolume*0.75, TRUE, -3)
holder.setClosed()
addtimer(CALLBACK(src, .proc/preReturn, holder), departureDelay * 0.2) //Start to leave a bit after closing for cinematic effect
/obj/structure/closet/supplypod/extractionpod/close(atom/movable/holder) //handles closing, and returns pod - deletes itself when returned
. = ..()
return
/obj/structure/closet/supplypod/take_contents(atom/movable/holder)
var/turf/turf_underneath = holder.drop_location()
for(var/atom_to_check in turf_underneath)
if(atom_to_check != src && !insert(atom_to_check, holder)) // Can't insert that
continue
insert(turf_underneath, holder)
/obj/structure/closet/supplypod/extractionpod/proc/send_up(atom/movable/holder)
if(!holder)
holder = src
if(leavingSound)
playsound(get_turf(holder), leavingSound, soundVolume, 0, 0)
handleReturningClose(holder, FALSE)
/obj/structure/closet/supplypod/insert(atom/to_insert, atom/movable/holder)
if(insertion_allowed(to_insert))
if(isturf(to_insert))
var/turf/turf_to_insert = to_insert
turfs_in_cargo += turf_to_insert.type
turf_to_insert.ScrapeAway()
else
var/atom/movable/movable_to_insert = to_insert
movable_to_insert.forceMove(holder)
return TRUE
else
return FALSE
/obj/structure/closet/supplypod/proc/setOpened() //Proc exists here, as well as in any atom that can assume the role of a "holder" of a supplypod. Check the open() proc for more details
/obj/structure/closet/supplypod/insertion_allowed(atom/to_insert)
if(to_insert.invisibility == INVISIBILITY_ABSTRACT)
return FALSE
if(ismob(to_insert))
if(!reverseOptionList["Mobs"])
return FALSE
if(!isliving(to_insert)) //let's not put ghosts or camera mobs inside
return FALSE
var/mob/living/mob_to_insert = to_insert
if(mob_to_insert.anchored || mob_to_insert.incorporeal_move)
return FALSE
mob_to_insert.stop_pulling()
else if(isobj(to_insert))
var/obj/obj_to_insert = to_insert
if(istype(obj_to_insert, /obj/structure/closet/supplypod))
return FALSE
if(istype(obj_to_insert, /obj/effect/supplypod_smoke))
return FALSE
if(istype(obj_to_insert, /obj/effect/pod_landingzone))
return FALSE
if(istype(obj_to_insert, /obj/effect/supplypod_rubble))
return FALSE
if(obj_to_insert.level == 1)
return FALSE // underfloor, until we get hide components.
/*
if((obj_to_insert.comp_lookup && obj_to_insert.comp_lookup[COMSIG_OBJ_HIDE]) && reverseOptionList["Underfloor"])
return TRUE
else if ((obj_to_insert.comp_lookup && obj_to_insert.comp_lookup[COMSIG_OBJ_HIDE]) && !reverseOptionList["Underfloor"])
return FALSE
*/
if(isProbablyWallMounted(obj_to_insert) && reverseOptionList["Wallmounted"])
return TRUE
else if (isProbablyWallMounted(obj_to_insert) && !reverseOptionList["Wallmounted"])
return FALSE
if(!obj_to_insert.anchored && reverseOptionList["Unanchored"])
return TRUE
if(obj_to_insert.anchored && reverseOptionList["Anchored"])
return TRUE
return FALSE
else if (isturf(to_insert))
if(isfloorturf(to_insert) && reverseOptionList["Floors"])
return TRUE
if(isfloorturf(to_insert) && !reverseOptionList["Floors"])
return FALSE
if(isclosedturf(to_insert) && reverseOptionList["Walls"])
return TRUE
if(isclosedturf(to_insert) && !reverseOptionList["Walls"])
return FALSE
return FALSE
return TRUE
/obj/structure/closet/supplypod/proc/preReturn(atom/movable/holder)
deleteRubble()
animate(holder, alpha = 0, time = 8, easing = QUAD_EASING|EASE_IN, flags = ANIMATION_PARALLEL)
animate(holder, pixel_z = 400, time = 10, easing = QUAD_EASING|EASE_IN, flags = ANIMATION_PARALLEL) //Animate our rising pod
addtimer(CALLBACK(src, .proc/handleReturnAfterDeparting, holder), 15) //Finish up the pod's duties after a certain amount of time
/obj/structure/closet/supplypod/setOpened() //Proc exists here, as well as in any atom that can assume the role of a "holder" of a supplypod. Check the open_pod() proc for more details
opened = TRUE
density = FALSE
update_icon()
/obj/structure/closet/supplypod/proc/setClosed() //Ditto
/obj/structure/closet/supplypod/extractionpod/setOpened()
opened = TRUE
density = TRUE
update_icon()
/obj/structure/closet/supplypod/setClosed() //Ditto
opened = FALSE
density = TRUE
update_icon()
/obj/structure/closet/supplypod/proc/tryMakeRubble(turf/T) //Ditto
if (rubble_type == RUBBLE_NONE)
return
if (rubble)
return
if (effectMissile)
return
if (isspaceturf(T) || isclosedturf(T))
return
rubble = new /obj/effect/supplypod_rubble(T)
rubble.setStyle(rubble_type, src)
update_icon()
/obj/structure/closet/supplypod/Moved()
deleteRubble()
return ..()
/obj/structure/closet/supplypod/proc/deleteRubble()
rubble?.fadeAway()
rubble = null
update_icon()
/obj/structure/closet/supplypod/proc/addGlow()
if (GLOB.podstyles[style][POD_SHAPE] != POD_SHAPE_NORML)
return
glow_effect = new(src)
glow_effect.icon_state = "pod_glow_" + GLOB.podstyles[style][POD_GLOW]
vis_contents += glow_effect
glow_effect.layer = GASFIRE_LAYER
/obj/structure/closet/supplypod/proc/endGlow()
if(!glow_effect)
return
glow_effect.layer = LOW_ITEM_LAYER
glow_effect.fadeAway(openingDelay)
/obj/structure/closet/supplypod/Destroy()
open(src, broken = TRUE) //Lets dump our contents by opening up
. = ..()
//------------------------------------FALLING SUPPLY POD-------------------------------------//
/obj/effect/abstract/DPfall //Falling pod
name = ""
icon = 'icons/obj/supplypods.dmi'
pixel_x = -16
pixel_y = -5
pixel_z = 200
desc = "Get out of the way!"
layer = FLY_LAYER//that wasnt flying, that was falling with style!
icon_state = ""
/obj/effect/abstract/DPfall/Initialize(dropLocation, obj/structure/closet/supplypod/pod)
if (pod.style == STYLE_SEETHROUGH)
pixel_x = -16
pixel_y = 0
for (var/atom/movable/O in pod.contents)
var/icon/I = getFlatIcon(O) //im so sorry
add_overlay(I)
else if (pod.style != STYLE_INVISIBLE) //Check to ensure the pod isn't invisible
icon_state = "[pod.icon_state]_falling"
name = pod.name
. = ..()
deleteRubble()
open_pod(src, broken = TRUE) //Lets dump our contents by opening up
return ..()
//------------------------------------TEMPORARY_VISUAL-------------------------------------//
/obj/effect/abstract/DPtarget //This is the object that forceMoves the supplypod to it's location
/obj/effect/supplypod_smoke //Falling pod smoke
name = ""
icon = 'icons/obj/supplypods_32x32.dmi'
icon_state = "smoke"
desc = ""
layer = PROJECTILE_HIT_THRESHHOLD_LAYER
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
alpha = 0
/obj/effect/engineglow //Falling pod smoke
name = ""
icon = 'icons/obj/supplypods.dmi'
icon_state = "pod_engineglow"
desc = ""
layer = GASFIRE_LAYER
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
alpha = 255
/obj/effect/engineglow/proc/fadeAway(leaveTime)
var/duration = min(leaveTime, 25)
animate(src, alpha=0, time = duration)
QDEL_IN(src, duration + 5)
/obj/effect/supplypod_smoke/proc/drawSelf(amount)
alpha = max(0, 255-(amount*20))
/obj/effect/supplypod_rubble //This is the object that forceMoves the supplypod to it's location
name = "Debris"
desc = "A small crater of rubble. Closer inspection reveals the debris to be made primarily of space-grade metal fragments. You're pretty sure that this will disperse before too long."
icon = 'icons/obj/supplypods.dmi'
layer = PROJECTILE_HIT_THRESHHOLD_LAYER // We want this to go right below the layer of supplypods and supplypod_rubble's forground.
icon_state = "rubble_bg"
anchored = TRUE
pixel_x = SUPPLYPOD_X_OFFSET
var/foreground = "rubble_fg"
var/verticle_offset = 0
/obj/effect/supplypod_rubble/proc/getForeground(obj/structure/closet/supplypod/pod)
var/mutable_appearance/rubble_overlay = mutable_appearance('icons/obj/supplypods.dmi', foreground)
rubble_overlay.appearance_flags = KEEP_APART|RESET_TRANSFORM
rubble_overlay.transform = matrix().Translate(SUPPLYPOD_X_OFFSET - pod.pixel_x, verticle_offset)
return rubble_overlay
/obj/effect/supplypod_rubble/proc/fadeAway()
animate(src, alpha=0, time = 30)
QDEL_IN(src, 35)
/obj/effect/supplypod_rubble/proc/setStyle(type, obj/structure/closet/supplypod/pod)
if (type == RUBBLE_WIDE)
icon_state += "_wide"
foreground += "_wide"
if (type == RUBBLE_THIN)
icon_state += "_thin"
foreground += "_thin"
if (pod.style == STYLE_BOX)
verticle_offset = -2
else
verticle_offset = initial(verticle_offset)
pixel_y = verticle_offset
/obj/effect/pod_landingzone_effect
name = ""
desc = ""
icon = 'icons/obj/supplypods_32x32.dmi'
icon_state = "LZ_Slider"
layer = PROJECTILE_HIT_THRESHHOLD_LAYER
/obj/effect/pod_landingzone_effect/Initialize(mapload, obj/structure/closet/supplypod/pod)
transform = matrix() * 1.5
animate(src, transform = matrix()*0.01, time = pod.landingDelay+pod.fallDuration)
..()
/obj/effect/pod_landingzone //This is the object that forceMoves the supplypod to it's location
name = "Landing Zone Indicator"
desc = "A holographic projection designating the landing zone of something. It's probably best to stand back."
icon = 'icons/mob/actions/actions_items.dmi'
icon_state = "sniper_zoom"
icon = 'icons/obj/supplypods_32x32.dmi'
icon_state = "LZ"
layer = PROJECTILE_HIT_THRESHHOLD_LAYER
light_range = 2
var/obj/effect/temp_visual/fallingPod //Temporary "falling pod" that we animate
var/obj/structure/closet/supplypod/pod //The supplyPod that will be landing ontop of this target
anchored = TRUE
alpha = 0
var/obj/structure/closet/supplypod/pod //The supplyPod that will be landing ontop of this pod_landingzone
var/obj/effect/pod_landingzone_effect/helper
var/list/smoke_effects = new /list(13)
/obj/effect/abstract/DPtarget/Initialize(mapload, podParam, single_order = null)
/obj/effect/ex_act()
return
/obj/effect/pod_landingzone/Initialize(mapload, podParam, single_order = null, clientman)
. = ..()
if (ispath(podParam)) //We can pass either a path for a pod (as expressconsoles do), or a reference to an instantiated pod (as the centcom_podlauncher does)
podParam = new podParam() //If its just a path, instantiate it
pod = podParam
if (!pod.effectStealth)
helper = new (drop_location(), pod)
alpha = 255
animate(src, transform = matrix().Turn(90), time = pod.landingDelay+pod.fallDuration)
if (single_order)
if (istype(single_order, /datum/supply_order))
var/datum/supply_order/SO = single_order
@@ -298,49 +572,76 @@
else if (istype(single_order, /atom/movable))
var/atom/movable/O = single_order
O.forceMove(pod)
for (var/mob/living/M in pod) //If there are any mobs in the supplypod, we want to forceMove them into the target. This is so that they can see where they are about to land, AND so that they don't get sent to the nullspace error room (as the pod is currently in nullspace)
M.forceMove(src)
if(pod.effectStun) //If effectStun is true, stun any mobs caught on this target until the pod gets a chance to hit them
for (var/mob/living/M in get_turf(src))
M.Stun(pod.landingDelay+10, ignore_canstun = TRUE)//you aint goin nowhere, kid.
if (pod.effectStealth) //If effectStealth is true we want to be invisible
icon_state = ""
for (var/mob/living/mob_in_pod in pod) //If there are any mobs in the supplypod, we want to set their view to the pod_landingzone. This is so that they can see where they are about to land
mob_in_pod.reset_perspective(src)
if(pod.effectStun) //If effectStun is true, stun any mobs caught on this pod_landingzone until the pod gets a chance to hit them
for (var/mob/living/target_living in get_turf(src))
target_living.Stun(pod.landingDelay+10, ignore_canstun = TRUE)//you ain't goin nowhere, kid.
if (pod.fallDuration == initial(pod.fallDuration) && pod.landingDelay + pod.fallDuration < pod.fallingSoundLength)
pod.fallingSoundLength = 3 //The default falling sound is a little long, so if the landing time is shorter than the default falling sound, use a special, shorter default falling sound
pod.fallingSound = 'sound/weapons/mortar_whistle.ogg'
var/soundStartTime = pod.landingDelay - pod.fallingSoundLength + pod.fallDuration
if (soundStartTime < 0)
soundStartTime = 1
if (!pod.effectQuiet)
if (!pod.effectQuiet && !(pod.pod_flags & FIRST_SOUNDS))
addtimer(CALLBACK(src, .proc/playFallingSound), soundStartTime)
addtimer(CALLBACK(src, .proc/beginLaunch, pod.effectCircle), pod.landingDelay)
/obj/effect/abstract/DPtarget/proc/playFallingSound()
playsound(src, pod.fallingSound, pod.soundVolume, 1, 6)
/obj/effect/pod_landingzone/proc/playFallingSound()
playsound(src, pod.fallingSound, pod.soundVolume, TRUE, 6)
/obj/effect/abstract/DPtarget/proc/beginLaunch(effectCircle) //Begin the animation for the pod falling. The effectCircle param determines whether the pod gets to come in from any descent angle
fallingPod = new /obj/effect/abstract/DPfall(drop_location(), pod)
var/matrix/M = matrix(fallingPod.transform) //Create a new matrix that we can rotate
/obj/effect/pod_landingzone/proc/beginLaunch(effectCircle) //Begin the animation for the pod falling. The effectCircle param determines whether the pod gets to come in from any descent angle
pod.addGlow()
pod.update_icon()
if (pod.style != STYLE_INVISIBLE)
pod.add_filter("motionblur",1,list("type"="motion_blur", "x"=0, "y"=3))
pod.forceMove(drop_location())
for (var/mob/living/M in pod) //Remember earlier (initialization) when we moved mobs into the pod_landingzone so they wouldnt get lost in nullspace? Time to get them out
M.reset_perspective(null)
var/angle = effectCircle ? rand(0,360) : rand(70,110) //The angle that we can come in from
fallingPod.pixel_x = cos(angle)*400 //Use some ADVANCED MATHEMATICS to set the animated pod's position to somewhere on the edge of a circle with the center being the target
fallingPod.pixel_z = sin(angle)*400
var/rotation = Get_Pixel_Angle(fallingPod.pixel_z, fallingPod.pixel_x) //CUSTOM HOMEBREWED proc that is just arctan with extra steps
M.Turn(rotation) //Turn our matrix accordingly
fallingPod.transform = M //Transform the animated pod according to the matrix
M = matrix(pod.transform) //Make another matrix based on the pod
M.Turn(rotation) //Turn the matrix
pod.transform = M //Turn the actual pod (Won't be visible until endLaunch() proc tho)
animate(fallingPod, pixel_z = 0, pixel_x = -16, time = pod.fallDuration, , easing = LINEAR_EASING) //Make the pod fall! At an angle!
pod.pixel_x = cos(angle)*32*length(smoke_effects) //Use some ADVANCED MATHEMATICS to set the animated pod's position to somewhere on the edge of a circle with the center being the pod_landingzone
pod.pixel_z = sin(angle)*32*length(smoke_effects)
var/rotation = Get_Pixel_Angle(pod.pixel_z, pod.pixel_x) //CUSTOM HOMEBREWED proc that is just arctan with extra steps
setupSmoke(rotation)
pod.transform = matrix().Turn(rotation)
pod.layer = FLY_LAYER
if (pod.style != STYLE_INVISIBLE)
animate(pod.get_filter("motionblur"), y = 0, time = pod.fallDuration, flags = ANIMATION_PARALLEL)
animate(pod, pixel_z = -1 * abs(sin(rotation))*4, pixel_x = SUPPLYPOD_X_OFFSET + (sin(rotation) * 20), time = pod.fallDuration, easing = LINEAR_EASING, flags = ANIMATION_PARALLEL) //Make the pod fall! At an angle!
addtimer(CALLBACK(src, .proc/endLaunch), pod.fallDuration, TIMER_CLIENT_TIME) //Go onto the last step after a very short falling animation
/obj/effect/abstract/DPtarget/proc/endLaunch()
pod.update_icon()
pod.forceMove(drop_location()) //The fallingPod animation is over, now's a good time to forceMove the actual pod into position
QDEL_NULL(fallingPod) //Delete the falling pod effect, because at this point its animation is over. We dont use temp_visual because we want to manually delete it as soon as the pod appears
for (var/mob/living/M in src) //Remember earlier (initialization) when we moved mobs into the DPTarget so they wouldnt get lost in nullspace? Time to get them out
M.forceMove(pod)
/obj/effect/pod_landingzone/proc/setupSmoke(rotation)
if (pod.style == STYLE_INVISIBLE || pod.style == STYLE_SEETHROUGH)
return
for ( var/i in 1 to length(smoke_effects))
var/obj/effect/supplypod_smoke/smoke_part = new (drop_location())
if (i == 1)
smoke_part.layer = FLY_LAYER
smoke_part.icon_state = "smoke_start"
smoke_part.transform = matrix().Turn(rotation)
smoke_effects[i] = smoke_part
smoke_part.pixel_x = sin(rotation)*32 * i
smoke_part.pixel_y = abs(cos(rotation))*32 * i
smoke_part.filters += filter(type = "blur", size = 4)
var/time = (pod.fallDuration / length(smoke_effects))*(length(smoke_effects)-i)
addtimer(CALLBACK(smoke_part, /obj/effect/supplypod_smoke/.proc/drawSelf, i), time, TIMER_CLIENT_TIME) //Go onto the last step after a very short falling animation
QDEL_IN(smoke_part, pod.fallDuration + 35)
/obj/effect/pod_landingzone/proc/drawSmoke()
if (pod.style == STYLE_INVISIBLE || pod.style == STYLE_SEETHROUGH)
return
for (var/obj/effect/supplypod_smoke/smoke_part in smoke_effects)
animate(smoke_part, alpha = 0, time = 20, flags = ANIMATION_PARALLEL)
animate(smoke_part.filters[1], size = 6, time = 15, easing = CUBIC_EASING|EASE_OUT, flags = ANIMATION_PARALLEL)
/obj/effect/pod_landingzone/proc/endLaunch()
pod.tryMakeRubble(drop_location())
pod.layer = initial(pod.layer)
pod.endGlow()
QDEL_NULL(helper)
pod.preOpen() //Begin supplypod open procedures. Here effects like explosions, damage, and other dangerous (and potentially admin-caused, if the centcom_podlauncher datum was used) memes will take place
qdel(src) //ditto
drawSmoke()
qdel(src) //The pod_landingzone's purpose is complete. It can rest easy now
//------------------------------------UPGRADES-------------------------------------//
/obj/item/disk/cargo/bluespace_pod //Disk that can be inserted into the Express Console to allow for Advanced Bluespace Pods
@@ -348,5 +649,4 @@
desc = "This disk provides a firmware update to the Express Supply Console, granting the use of Nanotrasen's Bluespace Drop Pods to the supply department."
icon = 'icons/obj/module.dmi'
icon_state = "cargodisk"
item_state = "card-id"
w_class = WEIGHT_CLASS_SMALL
+10 -3
View File
@@ -126,9 +126,8 @@
/// Messages currently seen by this client
var/list/seen_messages
/// datum wrapper for client view
var/datum/view_data/view_size
/// viewsize datum for holding our view size
var/datum/viewData/view_size
/// our current tab
var/stat_tab
@@ -162,6 +161,14 @@
var/parallax_layers_max = 3
var/parallax_animate_timer
/**
* Assoc list with all the active maps - when a screen obj is added to
* a map, it's put in here as well.
*
* Format: list(<mapname> = list(/obj/screen))
*/
var/list/screen_maps = list()
// List of all asset filenames sent to this client by the asset cache, along with their assoicated md5s
var/list/sent_assets = list()
/// List of all completed blocking send jobs awaiting acknowledgement by send_asset
+10 -19
View File
@@ -268,7 +268,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
else
prefs = new /datum/preferences(src)
GLOB.preferences_datums[ckey] = prefs
addtimer(CALLBACK(src, .proc/ensure_keys_set), 0) //prevents possible race conditions
addtimer(CALLBACK(src, .proc/ensure_keys_set), 10) //prevents possible race conditions
prefs.last_ip = address //these are gonna be used for banning
prefs.last_id = computer_id //these are gonna be used for banning
@@ -336,10 +337,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
qdel(src)
return
// if(SSinput.initialized) placed here on tg.
// set_macros()
// update_movement_keys()
// Initialize tgui panel
tgui_panel.initialize()
src << browse(file('html/statbrowser.html'), "window=statbrowser")
@@ -472,16 +469,16 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if (menuitem)
menuitem.Load_checked(src)
// view_size = new(src, getScreenSize(prefs.widescreenpref))
// view_size.resetFormat()
// view_size.setZoomMode()
// fit_viewport()
view_size = new(src, getScreenSize(prefs.widescreenpref))
view_size.resetFormat()
view_size.setZoomMode()
fit_viewport()
Master.UpdateTickRate()
/client/proc/ensure_keys_set()
if(SSinput.initialized)
set_macros()
update_movement_keys(prefs)
update_movement_keys(prefs)
//////////////
//DISCONNECT//
@@ -914,8 +911,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
return FALSE
if (NAMEOF(src, key))
return FALSE
if(NAMEOF(src, view))
change_view(var_value)
if (NAMEOF(src, view))
view_size.setDefault(var_value)
return TRUE
. = ..()
@@ -925,7 +922,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
var/y = viewscale[2]
x = clamp(x+change, min, max)
y = clamp(y+change, min,max)
change_view("[x]x[y]")
view_size.setDefault("[x]x[y]")
/client/proc/update_movement_keys(datum/preferences/direct_prefs)
var/datum/preferences/D = prefs || direct_prefs
@@ -948,12 +945,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if (isnull(new_size))
CRASH("change_view called without argument.")
//CIT CHANGES START HERE - makes change_view change DEFAULT_VIEW to 15x15 depending on preferences
if(prefs && CONFIG_GET(string/default_view))
if(!prefs.widescreenpref && new_size == CONFIG_GET(string/default_view))
new_size = "15x15"
//END OF CIT CHANGES
var/list/old_view = getviewsize(view)
view = new_size
var/list/actualview = getviewsize(view)
+49 -3
View File
@@ -201,7 +201,15 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/parallax
var/ambientocclusion = TRUE
var/auto_fit_viewport = TRUE
///Should we automatically fit the viewport?
var/auto_fit_viewport = FALSE
///Should we be in the widescreen mode set by the config?
var/widescreenpref = TRUE
///What size should pixels be displayed as? 0 is strech to fit
var/pixel_size = 0
///What scaling method should we use?
var/scaling_method = "normal"
var/uplink_spawn_loc = UPLINK_PDA
@@ -241,7 +249,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/screenshake = 100
var/damagescreenshake = 2
var/arousable = TRUE
var/widescreenpref = TRUE
var/autostand = TRUE
var/auto_ooc = FALSE
@@ -754,6 +761,20 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<b>HUD Button Flashes:</b> <a href='?_src_=prefs;preference=hud_toggle_flash'>[hud_toggle_flash ? "Enabled" : "Disabled"]</a><br>"
dat += "<b>HUD Button Flash Color:</b> <span style='border: 1px solid #161616; background-color: [hud_toggle_color];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=hud_toggle_color;task=input'>Change</a><br>"
/* CITADEL EDIT - We're using top menu instead
button_name = pixel_size
dat += "<b>Pixel Scaling:</b> <a href='?_src_=prefs;preference=pixel_size'>[(button_name) ? "Pixel Perfect [button_name]x" : "Stretch to fit"]</a><br>"
switch(scaling_method)
if(SCALING_METHOD_NORMAL)
button_name = "Nearest Neighbor"
if(SCALING_METHOD_DISTORT)
button_name = "Point Sampling"
if(SCALING_METHOD_BLUR)
button_name = "Bilinear"
dat += "<b>Scaling Method:</b> <a href='?_src_=prefs;preference=scaling_method'>[button_name]</a><br>"
*/
if (CONFIG_GET(flag/maprotation) && CONFIG_GET(flag/tgstyle_maprotation))
var/p_map = preferred_map
if (!p_map)
@@ -2239,7 +2260,32 @@ GLOBAL_LIST_EMPTY(preferences_datums)
features["has_womb"] = !features["has_womb"]
if("widescreenpref")
widescreenpref = !widescreenpref
user.client.change_view(CONFIG_GET(string/default_view))
user.client.view_size.setDefault(getScreenSize(widescreenpref))
if("pixel_size")
switch(pixel_size)
if(PIXEL_SCALING_AUTO)
pixel_size = PIXEL_SCALING_1X
if(PIXEL_SCALING_1X)
pixel_size = PIXEL_SCALING_1_2X
if(PIXEL_SCALING_1_2X)
pixel_size = PIXEL_SCALING_2X
if(PIXEL_SCALING_2X)
pixel_size = PIXEL_SCALING_3X
if(PIXEL_SCALING_3X)
pixel_size = PIXEL_SCALING_AUTO
user.client.view_size.apply() //Let's winset() it so it actually works
if("scaling_method")
switch(scaling_method)
if(SCALING_METHOD_NORMAL)
scaling_method = SCALING_METHOD_DISTORT
if(SCALING_METHOD_DISTORT)
scaling_method = SCALING_METHOD_BLUR
if(SCALING_METHOD_BLUR)
scaling_method = SCALING_METHOD_NORMAL
user.client.view_size.setZoomMode()
if("autostand")
autostand = !autostand
if("auto_ooc")
+6 -2
View File
@@ -280,6 +280,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["parallax"] >> parallax
S["ambientocclusion"] >> ambientocclusion
S["auto_fit_viewport"] >> auto_fit_viewport
S["widescreenpref"] >> widescreenpref
S["pixel_size"] >> pixel_size
S["scaling_method"] >> scaling_method
S["hud_toggle_flash"] >> hud_toggle_flash
S["hud_toggle_color"] >> hud_toggle_color
S["menuoptions"] >> menuoptions
@@ -297,7 +300,6 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["arousable"] >> arousable
S["screenshake"] >> screenshake
S["damagescreenshake"] >> damagescreenshake
S["widescreenpref"] >> widescreenpref
S["autostand"] >> autostand
S["cit_toggles"] >> cit_toggles
S["preferred_chaos"] >> preferred_chaos
@@ -333,6 +335,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
parallax = sanitize_integer(parallax, PARALLAX_INSANE, PARALLAX_DISABLE, null)
ambientocclusion = sanitize_integer(ambientocclusion, 0, 1, initial(ambientocclusion))
auto_fit_viewport = sanitize_integer(auto_fit_viewport, 0, 1, initial(auto_fit_viewport))
widescreenpref = sanitize_integer(widescreenpref, 0, 1, initial(widescreenpref))
pixel_size = sanitize_integer(pixel_size, PIXEL_SCALING_AUTO, PIXEL_SCALING_3X, initial(pixel_size))
scaling_method = sanitize_text(scaling_method, initial(scaling_method))
hud_toggle_flash = sanitize_integer(hud_toggle_flash, 0, 1, initial(hud_toggle_flash))
hud_toggle_color = sanitize_hexcolor(hud_toggle_color, 6, 1, initial(hud_toggle_color))
ghost_form = sanitize_inlist(ghost_form, GLOB.ghost_forms, initial(ghost_form))
@@ -346,7 +351,6 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
pda_skin = sanitize_inlist(pda_skin, GLOB.pda_reskins, PDA_SKIN_ALT)
screenshake = sanitize_integer(screenshake, 0, 800, initial(screenshake))
damagescreenshake = sanitize_integer(damagescreenshake, 0, 2, initial(damagescreenshake))
widescreenpref = sanitize_integer(widescreenpref, 0, 1, initial(widescreenpref))
autostand = sanitize_integer(autostand, 0, 1, initial(autostand))
cit_toggles = sanitize_integer(cit_toggles, 0, 16777215, initial(cit_toggles))
auto_ooc = sanitize_integer(auto_ooc, 0, 1, initial(auto_ooc))
+1 -3
View File
@@ -45,13 +45,12 @@
var/dynamic_fhair_suffix = ""//mask > head for facial hair
//basically a restriction list.
var/list/species_restricted = null
var/list/species_restricted
//Basically syntax is species_restricted = list("Species Name","Species Name")
//Add a "exclude" string to do the opposite, making it only only species listed that can't wear it.
//You append this to clothing objects
// How much clothing damage has been dealt to each of the limbs of the clothing, assuming it covers more than one limb
var/list/damage_by_parts
// How much integrity is in a specific limb before that limb is disabled (for use in [/obj/item/clothing/proc/take_damage_zone], and only if we cover multiple zones.) Set to 0 to disable shredding.
@@ -477,7 +476,6 @@ BLIND // can't see anything
return TRUE
/// If we're a clothing with at least 1 shredded/disabled zone, give the wearer a periodic heads up letting them know their clothes are damaged
/obj/item/clothing/proc/bristle(mob/living/L)
if(!istype(L))
+33 -12
View File
@@ -20,8 +20,14 @@
body_parts_covered = ARMS
cold_protection = ARMS
strip_delay = 300 //you can't just yank them off
/// did you ever get around to wearing these or no
var/wornonce = FALSE
///Extra damage through the punch.
var/enhancement = 0 //it's a +0 to your punches because it isn't magical
///extra wound bonus through the punch (MAYBE DON'T BE GENEROUS WITH THIS)
var/wound_enhancement = 0
/// do we give the flavortext for wearing them
var/silent = FALSE
///Main trait added by the gloves to the user on wear.
var/inherited_trait = TRAIT_NOGUNS //what are you, dishonoroable?
///Secondary trait added by the gloves to the user on wear.
@@ -29,7 +35,18 @@
/obj/item/clothing/gloves/fingerless/pugilist/equipped(mob/user, slot)
. = ..()
if(slot == SLOT_GLOVES)
if(current_equipped_slot == SLOT_GLOVES)
use_buffs(user, TRUE)
wornonce = TRUE
/obj/item/clothing/gloves/fingerless/pugilist/dropped(mob/user)
. = ..()
if(wornonce)
use_buffs(user, FALSE)
wornonce = FALSE
/obj/item/clothing/gloves/fingerless/pugilist/proc/use_buffs(mob/user, buff)
if(buff) // tarukaja
if(ishuman(user))
var/mob/living/carbon/human/H = user
ADD_TRAIT(H, TRAIT_PUGILIST, GLOVE_TRAIT)
@@ -37,17 +54,20 @@
ADD_TRAIT(H, secondary_trait, GLOVE_TRAIT)
H.dna.species.punchdamagehigh += enhancement
H.dna.species.punchdamagelow += enhancement
/obj/item/clothing/gloves/fingerless/pugilist/dropped(mob/user)
REMOVE_TRAIT(user, TRAIT_PUGILIST, GLOVE_TRAIT)
REMOVE_TRAIT(user, inherited_trait, GLOVE_TRAIT)
REMOVE_TRAIT(user, secondary_trait, GLOVE_TRAIT)
if(ishuman(user))
var/mob/living/carbon/human/H = user
H.dna.species.punchdamagehigh -= enhancement
H.dna.species.punchdamagelow -= enhancement
return ..()
H.dna.species.punchwoundbonus += wound_enhancement
if(!silent)
to_chat(H, "<span class='notice'>With [src] on your arms, you feel ready to punch things.</span>")
else // dekaja
REMOVE_TRAIT(user, TRAIT_PUGILIST, GLOVE_TRAIT)
REMOVE_TRAIT(user, inherited_trait, GLOVE_TRAIT)
REMOVE_TRAIT(user, secondary_trait, GLOVE_TRAIT)
if(ishuman(user))
var/mob/living/carbon/human/H = user
H.dna.species.punchdamagehigh -= enhancement
H.dna.species.punchdamagelow -= enhancement
H.dna.species.punchwoundbonus -= wound_enhancement
if(!silent)
to_chat(user, "<span class='warning'>With [src] off of your arms, you feel less ready to punch things.</span>")
/obj/item/clothing/gloves/fingerless/pugilist/chaplain
name = "armwraps of unyielding resolve"
@@ -93,6 +113,7 @@
icon_state = "rapid"
item_state = "rapid"
enhancement = 10 //omae wa mou shindeiru
wound_enhancement = 10
var/warcry = "AT"
secondary_trait = TRAIT_NOSOFTCRIT //basically extra health
+2
View File
@@ -65,12 +65,14 @@
heat_protection = HANDS
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
resistance_flags = NONE
strip_mod = 1.2 // because apparently black gloves had this
/obj/item/clothing/gloves/tackler/combat/insulated
name = "guerrilla gloves"
desc = "Superior quality combative gloves, good for performing tackle takedowns as well as absorbing electrical shocks."
siemens_coefficient = 0
permeability_coefficient = 0.05
strip_mod = 1.5 // and combat gloves had this??
/obj/item/clothing/gloves/tackler/combat/insulated/infiltrator
name = "insidious guerrilla gloves"
+2 -2
View File
@@ -355,7 +355,7 @@
/obj/item/clothing/mask/paper/Initialize(mapload)
.=..()
. = ..()
papermask_designs = list(
"Blank" = image(icon = src.icon, icon_state = "plainmask"),
"Neutral" = image(icon = src.icon, icon_state = "neutralmask"),
@@ -384,7 +384,7 @@
"Vertical" = "verticalmask", "Horizontal" = "horizontalmask", "X" ="xmask",
"Bugeyes" = "bugmask", "Double" = "doublemask", "Mark" = "markmask")
var/choice = show_radial_menu(user,src, papermask_designs, custom_check = FALSE, radius = 36, require_near = TRUE)
var/choice = show_radial_menu(user, src, papermask_designs, custom_check = FALSE, radius = 36, require_near = TRUE)
if(src && choice && !user.incapacitated() && in_range(user,src))
icon_state = options[choice]
+37
View File
@@ -0,0 +1,37 @@
/datum/round_event_control/shuttle_catastrophe
name = "Shuttle Catastrophe"
typepath = /datum/round_event/shuttle_catastrophe
weight = 10
max_occurrences = 1
/datum/round_event_control/shuttle_catastrophe/canSpawnEvent(players, gamemode)
if(SSshuttle.emergency.name == "Build your own shuttle kit")
return FALSE //don't undo manual player engineering, it also would unload people and ghost them, there's just a lot of problems
return ..()
/datum/round_event/shuttle_catastrophe
var/datum/map_template/shuttle/new_shuttle
/datum/round_event/shuttle_catastrophe/announce(fake)
var/cause = pick("was attacked by [syndicate_name()] Operatives", "mysteriously teleported away", "had its refuelling crew mutiny",
"was found with its engines stolen", "\[REDACTED\]", "flew into the sunset, and melted", "learned something from a very wise cow, and left on its own",
"had cloning devices on it", "had its shuttle inspector put the shuttle in reverse instead of park, causing the shuttle to crash into the hangar")
priority_announce("Your emergency shuttle [cause]. Your replacement shuttle will be the [new_shuttle.name] until further notice.", "CentCom Spacecraft Engineering")
/datum/round_event/shuttle_catastrophe/setup()
var/list/valid_shuttle_templates = list()
for(var/shuttle_id in SSmapping.shuttle_templates)
var/datum/map_template/shuttle/template = SSmapping.shuttle_templates[shuttle_id]
if(template.can_be_bought && template.credit_cost < INFINITY) //if we could get it from the communications console, it's cool for us to get it here
valid_shuttle_templates += template
new_shuttle = pick(valid_shuttle_templates)
/datum/round_event/shuttle_catastrophe/start()
SSshuttle.shuttle_purchased = SHUTTLEPURCHASE_FORCED
SSshuttle.unload_preview()
SSshuttle.load_template(new_shuttle)
SSshuttle.existing_shuttle = SSshuttle.emergency
SSshuttle.action_load(new_shuttle)
log_shuttle("Shuttle Catastrophe set a new shuttle, [new_shuttle.name].")
+1 -1
View File
@@ -55,7 +55,7 @@
crate.update_icon()
var/obj/structure/closet/supplypod/pod = make_pod()
crate.forceMove(pod)
new /obj/effect/abstract/DPtarget(LZ, pod)
new /obj/effect/pod_landingzone(LZ, pod)
///Handles the creation of the pod, in case it needs to be modified beforehand
/datum/round_event/stray_cargo/proc/make_pod()
@@ -618,3 +618,9 @@
icon_state = "monkey_energy"
list_reagents = list(/datum/reagent/consumable/monkey_energy = 50)
foodtype = SUGAR | JUNKFOOD
/obj/item/reagent_containers/food/drinks/dwarf_mug //dwarf family heirloom (just a cup that always has the manly dorf icon!)
name = "ale mug"
desc = "An old stained mug used for filling with dwarven ale."
icon_state = "manlydorfglass"
isGlass = FALSE //it's a wooden mug!
@@ -291,3 +291,12 @@
tastes = list("creamy peas"= 2, "parsnip" = 1)
filling_color = "#9dc530"
foodtype = VEGETABLES
/obj/item/reagent_containers/food/snacks/soup/facehuggerpot
name = "pot of face hugger jambalaya"
desc = "An entire pot of an extremely spicy dish made using extremely exotic ingredients. Highly recommend by an interdimensional businessman."
icon_state = "facehuggerpot"
bonus_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/consumable/nutriment/vitamin = 2)
list_reagents = list(/datum/reagent/consumable/nutriment = 10, /datum/reagent/consumable/nutriment/vitamin = 4)
tastes = list("face huggers" = 1)
foodtype = MEAT
@@ -291,3 +291,18 @@
)
result = /obj/item/reagent_containers/food/snacks/soup/peasoup
subcategory = CAT_SOUP
/datum/crafting_recipe/food/facehuggerjambalayapot
name = "Facehugger jambalaya"
reqs = list(
/datum/reagent/bluespace = 10,
/datum/reagent/consumable/ethanol/booger =10,
/obj/item/organ/heart= 3,
/obj/item/clothing/mask/facehugger = 1,
/obj/item/reagent_containers/food/snacks/egg = 1,
/datum/reagent/consumable/blackpepper = 5,
/datum/reagent/toxin/acid = 5
)
result = /obj/item/reagent_containers/food/snacks/soup/facehuggerpot
subcategory = CAT_SOUP
+8
View File
@@ -42,6 +42,14 @@
icon = 'icons/obj/hydroponics/equipment.dmi'
icon_state = "hydrotray3"
/obj/machinery/hydroponics/constructable/ComponentInitialize()
. = ..()
AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS, null, CALLBACK(src, .proc/can_be_rotated))
AddComponent(/datum/component/plumbing/simple_demand)
/obj/machinery/hydroponics/constructable/proc/can_be_rotated(mob/user, rotation_type)
return !anchored
/obj/machinery/hydroponics/constructable/RefreshParts()
var/tmp_capacity = 0
for (var/obj/item/stock_parts/matter_bin/M in component_parts)
+1 -1
View File
@@ -154,7 +154,7 @@
/datum/keybinding/living/toggle_sprint/down(client/user)
var/mob/living/L = user.mob
L.default_toggle_sprint(TRUE)
L.default_toggle_sprint()
return TRUE
/datum/keybinding/mob/toggle_move_intent
+5
View File
@@ -135,7 +135,12 @@
//That's done remove from priority even if it failed
if(forced)
//TODO : handle forced ruins with multiple variants
// this might work?
forced_ruins -= current_pick
if(!current_pick.allow_duplicates)
for(var/datum/map_template/ruin/R in forced_ruins)
if(R.id == current_pick.id)
forced_ruins -= R
forced = FALSE
if(failed_to_place)
+5 -5
View File
@@ -408,7 +408,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(mind.current.key && mind.current.key[1] != "@") //makes sure we don't accidentally kick any clients
to_chat(usr, "<span class='warning'>Another consciousness is in your body...It is resisting you.</span>")
return
client.change_view(CONFIG_GET(string/default_view))
client.view_size.setDefault(getScreenSize(client.prefs.widescreenpref))//Let's reset so people can't become allseeing gods
transfer_ckey(mind.current, FALSE)
SStgui.on_transfer(src, mind.current) // Transfer NanoUIs.
mind.current.client.init_verbs()
@@ -568,15 +568,15 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
set desc = "Change your view range."
var/max_view = client.prefs.unlock_content ? GHOST_MAX_VIEW_RANGE_MEMBER : GHOST_MAX_VIEW_RANGE_DEFAULT
if(client.view == CONFIG_GET(string/default_view))
if(client.view_size.getView() == client.view_size.default)
var/list/views = list()
for(var/i in 7 to max_view)
views |= i
var/new_view = input("Choose your new view", "Modify view range", 7) as null|anything in views
var/new_view = input("Choose your new view", "Modify view range", 0) as null|anything in views
if(new_view)
client.change_view(clamp(new_view, 7, max_view))
client.view_size.setTo(clamp(new_view, 7, max_view) - 7)
else
client.change_view(CONFIG_GET(string/default_view))
client.view_size.resetToDefault()
/mob/dead/observer/verb/add_view_range(input as num)
set name = "Add View Range"
+2 -17
View File
@@ -988,30 +988,15 @@
O.held_index = r_arm_index_next //2, 4, 6, 8...
hand_bodyparts += O
/mob/living/carbon/do_after_coefficent()
. = ..()
var/datum/component/mood/mood = src.GetComponent(/datum/component/mood) //Currently, only carbons or higher use mood, move this once that changes.
if(mood)
switch(mood.sanity) //Alters do_after delay based on how sane you are
if(SANITY_INSANE to SANITY_DISTURBED)
. *= 1.25
if(SANITY_NEUTRAL to SANITY_GREAT)
. *= 0.90
for(var/i in status_effects)
var/datum/status_effect/S = i
. *= S.interact_speed_modifier()
/mob/living/carbon/proc/create_internal_organs()
for(var/X in internal_organs)
var/obj/item/organ/I = X
I.Insert(src)
/mob/living/carbon/proc/update_disabled_bodyparts()
/mob/living/carbon/proc/update_disabled_bodyparts(silent = FALSE)
for(var/B in bodyparts)
var/obj/item/bodypart/BP = B
BP.update_disabled()
BP.update_disabled(silent)
/mob/living/carbon/vv_get_dropdown()
. = ..()
+6 -10
View File
@@ -254,10 +254,10 @@
if(href_list["pockets"])
var/strip_mod = 1
var/strip_silence = FALSE
var/obj/item/clothing/gloves/g = gloves
if (istype(g))
strip_mod = g.strip_mod
strip_silence = g.strip_silence
var/obj/item/clothing/gloves/G = gloves
if(istype(G))
strip_mod = G.strip_mod
strip_silence = G.strip_silence
var/pocket_side = href_list["pockets"]
var/pocket_id = (pocket_side == "right" ? SLOT_R_STORE : SLOT_L_STORE)
var/obj/item/pocket_item = (pocket_id == SLOT_R_STORE ? r_store : l_store)
@@ -1045,7 +1045,8 @@
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying)
return
if(!HAS_TRAIT(src, TRAIT_IGNOREDAMAGESLOWDOWN)) //if we want to ignore slowdown from damage, but not from equipment
var/health_deficiency = ((maxHealth) - health + (getStaminaLoss()*0.75))//CIT CHANGE - reduces the impact of staminaloss and makes stamina buffer influence it
var/scaling = maxHealth / 100
var/health_deficiency = ((maxHealth / scaling) - (health / scaling) + (getStaminaLoss()*0.75))//CIT CHANGE - reduces the impact of staminaloss and makes stamina buffer influence it
if(health_deficiency >= 40)
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)
@@ -1056,11 +1057,6 @@
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown)
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying)
/mob/living/carbon/human/do_after_coefficent()
. = ..()
. *= physiology.do_after_speed
/mob/living/carbon/human/is_bleeding()
if(NOBLOOD in dna.species.species_traits || bleedsuppress)
return FALSE
@@ -100,3 +100,8 @@
if(dna.species.space_move(src))
return TRUE
return ..()
/mob/living/carbon/human/CanPass(atom/movable/mover, turf/target)
if(dna.species.species_pass_check())
return TRUE
return ..()
@@ -23,8 +23,6 @@
var/hunger_mod = 1 //% of hunger rate taken per tick.
var/do_after_speed = 1 //Speed mod for do_after. Lower is better. If temporarily adjusting, please only modify using *= and /=, so you don't interrupt other calculations.
/// footstep type override for both shoeless and not footstep sounds.
var/footstep_type
+28 -13
View File
@@ -64,7 +64,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/stunmod = 1 // multiplier for stun duration
var/punchdamagelow = 1 //lowest possible punch damage. if this is set to 0, punches will always miss
var/punchdamagehigh = 10 //highest possible punch damage
var/punchstunthreshold = 10//damage at which punches from this race will stun //yes it should be to the attacked race but it's not useful that way even if it's logical
var/punchstunthreshold = 10 //damage at which punches from this race will stun //yes it should be to the attacked race but it's not useful that way even if it's logical
var/punchwoundbonus = 0 // additional wound bonus. generally zero.
var/siemens_coeff = 1 //base electrocution coefficient
var/damage_overlay_type = "human" //what kind of damage overlays (if any) appear on our species when wounded?
var/fixed_mut_color = "" //to use MUTCOLOR with a fixed color that's independent of dna.feature["mcolor"]
@@ -106,7 +107,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/whitelisted = 0 //Is this species restricted to certain players?
var/whitelist = list() //List the ckeys that can use this species, if it's whitelisted.: list("John Doe", "poopface666", "SeeALiggerPullTheTrigger") Spaces & capitalization can be included or ignored entirely for each key as it checks for both.
var/icon_limbs //Overrides the icon used for the limbs of this species. Mainly for downstream, and also because hardcoded icons disgust me. Implemented and maintained as a favor in return for a downstream's implementation of synths.
var/species_type
var/species_category
var/tail_type //type of tail i.e. mam_tail
var/wagging_type //type of wagging i.e. waggingtail_lizard
@@ -591,9 +592,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
else
var/left_state = DEFAULT_LEFT_EYE_STATE
var/right_state = DEFAULT_RIGHT_EYE_STATE
message_admins("okay so our eye type is [eye_type] and we can index it to know [GLOB.eye_types[eye_type]]")
if(eye_type in GLOB.eye_types)
message_admins("to know that it's in!")
left_state = eye_type + "_left_eye"
right_state = eye_type + "_right_eye"
var/mutable_appearance/left_eye = mutable_appearance('icons/mob/eyes.dmi', left_state, -BODY_LAYER)
@@ -1389,6 +1388,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
user.do_attack_animation(target, ATTACK_EFFECT_PUNCH)
var/damage = rand(user.dna.species.punchdamagelow, user.dna.species.punchdamagehigh)
var/punchwoundbonus = user.dna.species.punchwoundbonus
var/puncherstam = user.getStaminaLoss()
var/puncherbrute = user.getBruteLoss()
var/punchedstam = target.getStaminaLoss()
@@ -1404,6 +1404,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
//END OF CITADEL CHANGES
var/obj/item/bodypart/affecting = target.get_bodypart(ran_zone(user.zone_selected))
if(HAS_TRAIT(user, TRAIT_PUGILIST))
affecting = target.get_bodypart(check_zone(user.zone_selected)) // if you're going the based unarmed route you won't miss
if(!affecting) //Maybe the bodypart is missing? Or things just went wrong..
affecting = target.get_bodypart(BODY_ZONE_CHEST) //target chest instead, as failsafe. Or hugbox? You decide.
@@ -1415,8 +1417,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(user.dna.species.punchdamagelow)
if(atk_verb == ATTACK_EFFECT_KICK) //kicks never miss (provided your species deals more than 0 damage)
miss_chance = 0
else if(HAS_TRAIT(user, TRAIT_PUGILIST)) //pugilists have a flat 10% miss chance
miss_chance = 10
else if(HAS_TRAIT(user, TRAIT_PUGILIST)) //pugilists, being good at Punching People, also never miss
miss_chance = 0
else
miss_chance = min(10 + max(puncherstam * 0.5, puncherbrute * 0.5), 100) //probability of miss has a base of 10, and modified based on half brute total. Capped at max 100 to prevent weirdness in prob()
@@ -1430,12 +1432,13 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/armor_block = target.run_armor_check(affecting, "melee")
if(HAS_TRAIT(user, TRAIT_MAULER)) // maulers get 15 armorpierce because if you're going to punch someone you might as well do a good job of it
armor_block = target.run_armor_check(affecting, "melee", armour_penetration = 15) // lot of good that sec jumpsuit did you
playsound(target.loc, user.dna.species.attack_sound, 25, 1, -1)
target.visible_message("<span class='danger'>[user] [atk_verb]s [target]!</span>", \
"<span class='userdanger'>[user] [atk_verb]s you!</span>", null, COMBAT_MESSAGE_RANGE, null, \
user, "<span class='danger'>You [atk_verb] [target]!</span>")
target.visible_message("<span class='danger'>[user] [atk_verb]ed [target]!</span>", \
"<span class='userdanger'>[user] [atk_verb]ed you!</span>", null, COMBAT_MESSAGE_RANGE, null, \
user, "<span class='danger'>You [atk_verb]ed [target]!</span>")
target.lastattacker = user.real_name
target.lastattackerckey = user.ckey
@@ -1445,11 +1448,15 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
target.dismembering_strike(user, affecting.body_zone)
if(atk_verb == ATTACK_EFFECT_KICK)//kicks deal 1.5x raw damage + 0.5x stamina damage
target.apply_damage(damage*1.5, attack_type, affecting, armor_block)
target.apply_damage(damage*1.5, attack_type, affecting, armor_block, wound_bonus = punchwoundbonus)
target.apply_damage(damage*0.5, STAMINA, affecting, armor_block)
log_combat(user, target, "kicked")
else//other attacks deal full raw damage + 2x in stamina damage
target.apply_damage(damage, attack_type, affecting, armor_block)
else if(HAS_TRAIT(user, TRAIT_MAULER)) // mauler punches deal 1.3x raw damage + 1x stam damage, and have some armor pierce
target.apply_damage(damage*1.3, attack_type, affecting, armor_block, wound_bonus = punchwoundbonus)
target.apply_damage(damage, STAMINA, affecting, armor_block)
log_combat(user, target, "punched (mauler)")
else //other attacks deal full raw damage + 2x in stamina damage
target.apply_damage(damage, attack_type, affecting, armor_block, wound_bonus = punchwoundbonus)
target.apply_damage(damage*2, STAMINA, affecting, armor_block)
log_combat(user, target, "punched")
@@ -1956,6 +1963,14 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(HAS_BONE in species_traits)
. |= BIO_JUST_BONE
//a check for if you should render any overlays or not
/datum/species/proc/should_render(mob/living/carbon/human/H)
return TRUE
//a check for if you want to forcibly make CanPass return TRUE for the mob with this species
/datum/species/proc/species_pass_check()
return FALSE
/////////////
//BREATHING//
/////////////
@@ -1,12 +1,12 @@
/datum/species/abductor
name = "Abductor"
id = "abductor"
id = SPECIES_ABDUCTOR
say_mod = "gibbers"
sexes = FALSE
species_traits = list(NOBLOOD,NOEYES,NOGENITALS,NOAROUSAL,HAS_FLESH,HAS_BONE)
inherent_traits = list(TRAIT_VIRUSIMMUNE,TRAIT_CHUNKYFINGERS,TRAIT_NOHUNGER,TRAIT_NOBREATH)
mutanttongue = /obj/item/organ/tongue/abductor
species_type = "alien"
species_category = SPECIES_CATEGORY_ALIEN
/datum/species/abductor/on_species_gain(mob/living/carbon/C, datum/species/old_species)
. = ..()
@@ -1,6 +1,6 @@
/datum/species/android
name = "Android"
id = "android"
id = SPECIES_ANDROID
say_mod = "states"
species_traits = list(NOBLOOD,NOGENITALS,NOAROUSAL,ROBOTIC_LIMBS)
inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_NOFIRE,TRAIT_PIERCEIMMUNE,TRAIT_NOHUNGER,TRAIT_LIMBATTACHMENT)
@@ -11,7 +11,7 @@
mutanttongue = /obj/item/organ/tongue/robot
species_language_holder = /datum/language_holder/synthetic
limbs_id = "synth"
species_type = "robotic"
species_category = SPECIES_CATEGORY_ROBOT
/datum/species/android/on_species_gain(mob/living/carbon/C)
. = ..()
@@ -1,6 +1,6 @@
/datum/species/angel
name = "Angel"
id = "angel"
id = SPECIES_ANGEL
default_color = "FFFFFF"
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,HAS_FLESH,HAS_BONE)
mutant_bodyparts = list("tail_human" = "None", "ears" = "None", "wings" = "Angel")
@@ -9,7 +9,7 @@
blacklisted = 1
limbs_id = "human"
skinned_type = /obj/item/stack/sheet/animalhide/human
species_type = "human" //they're a kind of human
species_category = SPECIES_CATEGORY_BASIC //they're a kind of human
var/datum/action/innate/flight/fly
@@ -1,6 +1,6 @@
/datum/species/mammal
name = "Anthromorph"
id = "mammal"
id = SPECIES_MAMMAL
default_color = "4B4B4B"
species_traits = list(MUTCOLORS,EYECOLOR,LIPS,HAIR,HORNCOLOR,WINGCOLOR,HAS_FLESH,HAS_BONE)
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_BEAST
@@ -15,6 +15,6 @@
tail_type = "mam_tail"
wagging_type = "mam_waggingtail"
species_type = "furry"
species_category = SPECIES_CATEGORY_FURRY
allowed_limb_ids = list("mammal","aquatic","avian")
allowed_limb_ids = list("mammal","aquatic","avian")
@@ -1,6 +1,6 @@
/datum/species/insect
name = "Anthromorphic Insect"
id = "insect"
id = SPECIES_INSECT
say_mod = "chitters"
default_color = "00FF00"
species_traits = list(LIPS,EYECOLOR,HAIR,FACEHAIR,MUTCOLORS,HORNCOLOR,WINGCOLOR,HAS_FLESH,HAS_BONE)
@@ -18,7 +18,7 @@
tail_type = "mam_tail"
wagging_type = "mam_waggingtail"
species_type = "insect"
species_category = SPECIES_CATEGORY_INSECT
allowed_limb_ids = list("insect","apid","moth","moth_not_greyscale")
@@ -18,4 +18,4 @@
inherent_traits = list(TRAIT_RADIMMUNE,TRAIT_VIRUSIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_NOLIMBDISABLE,TRAIT_NOHUNGER)
sexes = 0
gib_types = /obj/effect/gibspawner/robot
species_type = "robotic"
species_category = SPECIES_CATEGORY_ROBOT
@@ -1,6 +1,6 @@
/datum/species/dullahan
name = "Dullahan"
id = "dullahan"
id = SPECIES_DULLAHAN
default_color = "FFFFFF"
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,HAS_FLESH,HAS_BONE)
inherent_traits = list(TRAIT_NOHUNGER,TRAIT_NOBREATH)
@@ -14,7 +14,7 @@
limbs_id = "human"
skinned_type = /obj/item/stack/sheet/animalhide/human
has_field_of_vision = FALSE //Too much of a trouble, their vision is already bound to their severed head.
species_type = "undead"
species_category = SPECIES_CATEGORY_UNDEAD
var/pumpkin = FALSE
var/obj/item/dullahan_relay/myhead
@@ -4,7 +4,7 @@ GLOBAL_LIST_INIT(dwarf_last, world.file2list("strings/names/dwarf_last.txt")) //
/datum/species/dwarf //not to be confused with the genetic manlets
name = "Dwarf"
id = "dwarf" //Also called Homo sapiens pumilionis
id = SPECIES_DWARF //Also called Homo sapiens pumilionis
default_color = "FFFFFF"
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,HAS_FLESH,HAS_BONE)
inherent_traits = list(TRAIT_DWARF,TRAIT_SNOB)
@@ -18,7 +18,7 @@ GLOBAL_LIST_INIT(dwarf_last, world.file2list("strings/names/dwarf_last.txt")) //
mutant_organs = list(/obj/item/organ/dwarfgland) //Dwarven alcohol gland, literal gland warrior
mutantliver = /obj/item/organ/liver/dwarf //Dwarven super liver (Otherwise they r doomed)
species_language_holder = /datum/language_holder/dwarf
species_type = "human" //a kind of human
species_category = SPECIES_CATEGORY_BASIC //a kind of human
/mob/living/carbon/human/species/dwarf //species admin spawn path
race = /datum/species/dwarf //and the race the path is set to.
@@ -2,7 +2,7 @@
/datum/species/ethereal
name = "Ethereal"
id = "ethereal"
id = SPECIES_ETHEREAL
attack_verb = "burn"
attack_sound = 'sound/weapons/etherealhit.ogg'
miss_sound = 'sound/weapons/etherealmiss.ogg'
@@ -1,7 +1,7 @@
//Subtype of human
/datum/species/human/felinid
name = "Felinid"
id = "felinid"
id = SPECIES_FELINID
limbs_id = "human"
mutant_bodyparts = list("mam_tail" = "Cat", "mam_ears" = "Cat", "deco_wings" = "None")
@@ -11,7 +11,7 @@
tail_type = "mam_tail"
wagging_type = "mam_waggingtail"
species_type = "furry"
species_category = SPECIES_CATEGORY_FURRY
/datum/species/human/felinid/on_species_gain(mob/living/carbon/C, datum/species/old_species, pref_load)
if(ishuman(C))
@@ -1,6 +1,6 @@
/datum/species/fly
name = "Anthromorphic Fly"
id = "fly"
id = SPECIES_FLY
say_mod = "buzzes"
species_traits = list(NOEYES,HAS_FLESH,HAS_BONE)
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_BUG
@@ -12,7 +12,7 @@
liked_food = GROSS
exotic_bloodtype = "BUG"
exotic_blood_color = BLOOD_COLOR_BUG
species_type = "insect"
species_category = SPECIES_CATEGORY_INSECT
/datum/species/fly/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H)
if(istype(chem, /datum/reagent/toxin/pestkiller))
@@ -1,7 +1,7 @@
/datum/species/golem
// Animated beings of stone. They have increased defenses, and do not need to breathe. They're also slow as fuuuck.
name = "Golem"
id = "iron golem"
id = SPECIES_GOLEM
species_traits = list(NOBLOOD,MUTCOLORS,NO_UNDERWEAR,NOGENITALS,NOAROUSAL)
inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOFIRE,TRAIT_CHUNKYFINGERS,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER)
inherent_biotypes = MOB_HUMANOID|MOB_MINERAL
@@ -32,7 +32,7 @@
var/special_name_chance = 5
var/owner //dobby is a free golem
species_type = "golem"
species_category = SPECIES_CATEGORY_GOLEM
/datum/species/golem/random_name(gender,unique,lastname)
var/golem_surname = pick(GLOB.golem_names)
@@ -1,6 +1,6 @@
/datum/species/human
name = "Human"
id = "human"
id = SPECIES_HUMAN
default_color = "FFFFFF"
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,MUTCOLORS_PARTSONLY,WINGCOLOR,HAS_FLESH,HAS_BONE)
@@ -12,7 +12,7 @@
tail_type = "tail_human"
wagging_type = "waggingtail_human"
species_type = "human"
species_category = SPECIES_CATEGORY_BASIC
/datum/species/human/spec_death(gibbed, mob/living/carbon/human/H)
if(H)
@@ -1,6 +1,6 @@
/datum/species/ipc
name = "I.P.C."
id = "ipc"
id = SPECIES_IPC
say_mod = "beeps"
default_color = "00FF00"
blacklisted = 0
@@ -28,7 +28,7 @@
exotic_bloodtype = "HF"
exotic_blood_color = BLOOD_COLOR_OIL
species_type = "robotic"
species_category = SPECIES_CATEGORY_ROBOT
var/datum/action/innate/monitor_change/screen
@@ -1,7 +1,7 @@
/datum/species/jelly
// Entirely alien beings that seem to be made entirely out of gel. They have three eyes and a skeleton visible within them.
name = "Xenobiological Jelly Entity"
id = "jelly"
id = SPECIES_JELLY
default_color = "00FF90"
say_mod = "chirps"
species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,WINGCOLOR,HAS_FLESH)
@@ -16,7 +16,8 @@
exotic_blood_color = "BLOOD_COLOR_SLIME"
damage_overlay_type = ""
var/datum/action/innate/regenerate_limbs/regenerate_limbs
var/datum/action/innate/slime_change/slime_change //CIT CHANGE
var/datum/action/innate/slime_change/slime_change
var/datum/action/innate/slime_puddle/slime_puddle
liked_food = TOXIC | MEAT
disliked_food = null
toxic_food = ANTITOXIC
@@ -28,19 +29,22 @@
tail_type = "mam_tail"
wagging_type = "mam_waggingtail"
species_type = "jelly"
species_category = SPECIES_CATEGORY_JELLY
/obj/item/organ/brain/jelly
name = "slime nucleus"
desc = "A slimey membranous mass from a slime person"
icon_state = "brain-slime"
/datum/species/jelly/on_species_loss(mob/living/carbon/C)
if(slime_puddle && slime_puddle.is_puddle)
slime_puddle.Activate()
if(regenerate_limbs)
regenerate_limbs.Remove(C)
if(slime_change) //CIT CHANGE
slime_change.Remove(C) //CIT CHANGE
if(slime_change)
slime_change.Remove(C)
if(slime_puddle)
slime_puddle.Remove(C)
C.faction -= "slime"
..()
C.faction -= "slime"
@@ -50,8 +54,10 @@
if(ishuman(C))
regenerate_limbs = new
regenerate_limbs.Grant(C)
slime_change = new //CIT CHANGE
slime_change.Grant(C) //CIT CHANGE
slime_change = new
slime_change.Grant(C)
slime_puddle = new
slime_puddle.Grant(C)
C.faction |= "slime"
/datum/species/jelly/handle_body(mob/living/carbon/human/H)
@@ -59,6 +65,18 @@
//update blood color to body color
exotic_blood_color = "#" + H.dna.features["mcolor"]
/datum/species/jelly/should_render()
if(slime_puddle && slime_puddle.is_puddle)
return FALSE
else
return ..()
/datum/species/jelly/species_pass_check()
if(slime_puddle && slime_puddle.is_puddle)
return TRUE
else
return ..()
/datum/species/jelly/spec_life(mob/living/carbon/human/H)
if(H.stat == DEAD || HAS_TRAIT(H, TRAIT_NOMARROW)) //can't farm slime jelly from a dead slime/jelly person indefinitely, and no regeneration for blooduskers
return
@@ -140,7 +158,7 @@
/datum/species/jelly/slime
name = "Xenobiological Slime Entity"
id = "slime"
id = SPECIES_SLIME
default_color = "00FFFF"
species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR)
say_mod = "says"
@@ -449,7 +467,7 @@
/datum/species/jelly/roundstartslime
name = "Xenobiological Slime Hybrid"
id = "slimeperson"
id = SPECIES_SLIME_HYBRID
limbs_id = "slime"
default_color = "00FFFF"
species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR)
@@ -670,6 +688,88 @@
else
return
/datum/action/innate/slime_puddle
name = "Puddle Transformation"
check_flags = AB_CHECK_CONSCIOUS
button_icon_state = "slimepuddle"
icon_icon = 'icons/mob/actions/actions_slime.dmi'
background_icon_state = "bg_alien"
required_mobility_flags = MOBILITY_STAND
var/is_puddle = FALSE
var/in_transformation_duration = 12
var/out_transformation_duration = 7
var/puddle_into_effect = /obj/effect/temp_visual/slime_puddle
var/puddle_from_effect = /obj/effect/temp_visual/slime_puddle/reverse
var/puddle_icon = 'icons/mob/mob.dmi'
var/puddle_state = "puddle"
var/tracked_overlay
var/datum/component/squeak/squeak
var/transforming = FALSE
var/last_use
/datum/action/innate/slime_puddle/IsAvailable()
if(!transforming)
return ..()
else
return FALSE
/datum/action/innate/slime_puddle/Activate()
if(isjellyperson(owner) && IsAvailable())
transforming = TRUE
UpdateButtonIcon()
var/mob/living/carbon/human/H = owner
var/mutcolor = "#" + H.dna.features["mcolor"]
if(!is_puddle)
if(CHECK_MOBILITY(H, MOBILITY_USE))
is_puddle = TRUE
owner.cut_overlays()
var/obj/effect/puddle_effect = new puddle_into_effect(get_turf(owner), owner.dir)
puddle_effect.color = mutcolor
H.Stun(in_transformation_duration, ignore_canstun = TRUE)
ADD_TRAIT(H, TRAIT_PARALYSIS_L_ARM, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_PARALYSIS_R_ARM, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_MOBILITY_NOPICKUP, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_MOBILITY_NOUSE, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_SPRINT_LOCKED, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_COMBAT_MODE_LOCKED, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_MOBILITY_NOREST, SLIMEPUDDLE_TRAIT)
H.add_movespeed_modifier(/datum/movespeed_modifier/slime_puddle)
H.update_disabled_bodyparts(silent = TRUE)
H.layer -= 1 //go one layer down so people go over you
ENABLE_BITFIELD(H.pass_flags, PASSMOB)
squeak = H.AddComponent(/datum/component/squeak, custom_sounds = list('sound/effects/blobattack.ogg'))
sleep(in_transformation_duration)
var/mutable_appearance/puddle_overlay = mutable_appearance(icon = puddle_icon, icon_state = puddle_state)
puddle_overlay.color = mutcolor
tracked_overlay = puddle_overlay
owner.add_overlay(puddle_overlay)
transforming = FALSE
UpdateButtonIcon()
else
owner.cut_overlay(tracked_overlay)
var/obj/effect/puddle_effect = new puddle_from_effect(get_turf(owner), owner.dir)
puddle_effect.color = mutcolor
H.Stun(out_transformation_duration, ignore_canstun = TRUE)
sleep(out_transformation_duration)
REMOVE_TRAIT(H, TRAIT_PARALYSIS_L_ARM, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_PARALYSIS_R_ARM, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_MOBILITY_NOPICKUP, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_MOBILITY_NOUSE, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_SPRINT_LOCKED, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_COMBAT_MODE_LOCKED, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_MOBILITY_NOREST, SLIMEPUDDLE_TRAIT)
H.update_disabled_bodyparts(silent = TRUE)
H.remove_movespeed_modifier(/datum/movespeed_modifier/slime_puddle)
H.layer += 1 //go one layer back above!
DISABLE_BITFIELD(H.pass_flags, PASSMOB)
is_puddle = FALSE
if(squeak)
squeak.RemoveComponent()
owner.regenerate_icons()
transforming = FALSE
UpdateButtonIcon()
else
to_chat(owner, "<span class='warning'>You need to be standing up to do this!") //just assume they're a slime because it's such a weird edgecase to have it and not be one (it shouldn't even be possible)
///////////////////////////////////LUMINESCENTS//////////////////////////////////////////
@@ -677,7 +777,7 @@
/datum/species/jelly/luminescent
name = "Luminescent Slime Entity"
id = "lum"
id = SPECIES_SLIME_LUMI
say_mod = "says"
var/glow_intensity = LUMINESCENT_DEFAULT_GLOW
var/obj/effect/dummy/luminescent_glow/glow
@@ -844,7 +944,7 @@
/datum/species/jelly/stargazer
name = "Stargazer Slime Entity"
id = "stargazer"
id = SPECIES_STARGAZER
var/datum/action/innate/project_thought/project_thought
var/datum/action/innate/link_minds/link_minds
var/list/mob/living/linked_mobs = list()
@@ -1,7 +1,7 @@
/datum/species/lizard
// Reptilian humanoids with scaled skin and tails.
name = "Anthromorphic Lizard"
id = "lizard"
id = SPECIES_LIZARD
say_mod = "hisses"
default_color = "00FF00"
species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,LIPS,HORNCOLOR,WINGCOLOR,HAS_FLESH,HAS_BONE)
@@ -28,7 +28,7 @@
tail_type = "tail_lizard"
wagging_type = "waggingtail_lizard"
species_type = "lizard"
species_category = "lizard"
/datum/species/lizard/random_name(gender,unique,lastname)
if(unique)
@@ -46,7 +46,7 @@
*/
/datum/species/lizard/ashwalker
name = "Ash Walker"
id = "ashlizard"
id = SPECIES_ASHWALKER
limbs_id = "lizard"
species_traits = list(MUTCOLORS,EYECOLOR,LIPS,DIGITIGRADE)
inherent_traits = list(TRAIT_CHUNKYFINGERS)
@@ -1,6 +1,6 @@
/datum/species/mush //mush mush codecuck
name = "Anthromorphic Mushroom"
id = "mush"
id = SPECIES_MUSHROOM
mutant_bodyparts = list("caps" = "Round")
fixed_mut_color = "DBBF92"
@@ -21,7 +21,7 @@
burnmod = 1.25
heatmod = 1.5
species_type = "plant"
species_category = SPECIES_CATEGORY_PLANT
mutanteyes = /obj/item/organ/eyes/night_vision/mushroom
var/datum/martial_art/mushpunch/mush
@@ -1,6 +1,6 @@
/datum/species/plasmaman
name = "Plasmaman"
id = "plasmaman"
id = SPECIES_PLASMAMAN
say_mod = "rattles"
sexes = 0
meat = /obj/item/stack/sheet/mineral/plasma
@@ -22,7 +22,7 @@
liked_food = VEGETABLES
outfit_important_for_life = /datum/outfit/plasmaman
species_type = "skeleton"
species_category = SPECIES_CATEGORY_SKELETON
/datum/species/plasmaman/spec_life(mob/living/carbon/human/H)
var/datum/gas_mixture/environment = H.loc.return_air()
@@ -1,7 +1,7 @@
/datum/species/pod
// A mutation caused by a human being ressurected in a revival pod. These regain health in light, and begin to wither in darkness.
name = "Anthromorphic Plant"
id = "pod"
id = SPECIES_POD
default_color = "59CE00"
species_traits = list(MUTCOLORS,EYECOLOR,CAN_SCAR,HAS_FLESH,HAS_BONE)
attack_verb = "slash"
@@ -19,7 +19,7 @@
var/light_burnheal = -1
var/light_bruteheal = -1
species_type = "plant"
species_category = SPECIES_CATEGORY_PLANT
allowed_limb_ids = list("pod","mush")
@@ -66,7 +66,7 @@
/datum/species/pod/pseudo_weak
name = "Anthromorphic Plant"
id = "podweak"
id = SPECIES_POD_WEAK
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,MUTCOLORS)
mutant_bodyparts = list("mcolor" = "FFFFFF","mcolor2" = "FFFFFF","mcolor3" = "FFFFFF", "mam_snouts" = "Husky", "mam_tail" = "Husky", "mam_ears" = "Husky", "mam_body_markings" = "Husky", "taur" = "None", "legs" = "Normal Legs")
limbs_id = "pod"
@@ -4,7 +4,7 @@
/datum/species/shadow
// Humans cursed to stay in the darkness, lest their life forces drain. They regain health in shadow and die in light.
name = "???"
id = "shadow"
id = SPECIES_SHADOW
sexes = 0
blacklisted = 1
ignored_by = list(/mob/living/simple_animal/hostile/faithless)
@@ -15,7 +15,7 @@
dangerous_existence = 1
mutanteyes = /obj/item/organ/eyes/night_vision
species_type = "shadow"
species_category = SPECIES_CATEGORY_SHADOW
/datum/species/shadow/on_species_gain(mob/living/carbon/C, datum/species/old_species)
. = ..()
@@ -32,7 +32,7 @@
/datum/species/shadow/nightmare
name = "Nightmare"
id = "nightmare"
id = SPECIES_NIGHTMARE
limbs_id = "shadow"
burnmod = 1.5
blacklisted = TRUE
@@ -1,6 +1,6 @@
/datum/species/skeleton
name = "Skeleton"
id = "skeleton"
id = SPECIES_SKELETON
say_mod = "rattles"
blacklisted = 0
sexes = 0
@@ -15,7 +15,7 @@
brutemod = 1.25
burnmod = 1.25
species_type = "skeleton" //they have their own category that's disassociated from undead, paired with plasmapeople
species_category = SPECIES_CATEGORY_SKELETON //they have their own category that's disassociated from undead, paired with plasmapeople
/datum/species/skeleton/New()
if(SSevents.holidays && SSevents.holidays[HALLOWEEN]) //skeletons are stronger during the spooky season!
@@ -31,7 +31,7 @@
/datum/species/skeleton/space
name = "Spooky Spacey Skeleton"
id = "spaceskeleton"
id = SPECIES_SKELETON_SPACE
limbs_id = "skeleton"
blacklisted = 1
inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NOHUNGER,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT, TRAIT_FAKEDEATH, TRAIT_CALCIUM_HEALER)
@@ -1,6 +1,6 @@
/datum/species/synthliz
name = "Synthetic Lizardperson"
id = "synthliz"
id = SPECIES_SYNTH_LIZARD
say_mod = "beeps"
default_color = "00FF00"
species_traits = list(MUTCOLORS,NOTRANSSTING,EYECOLOR,LIPS,HAIR,ROBOTIC_LIMBS,HAS_FLESH,HAS_BONE)
@@ -27,4 +27,4 @@
tail_type = "mam_tail"
wagging_type = "mam_waggingtail"
species_type = "robotic"
species_category = SPECIES_CATEGORY_ROBOT
@@ -1,6 +1,6 @@
/datum/species/synth
name = "Synthetic" //inherited from the real species, for health scanners and things
id = "synth"
id = SPECIES_SYNTH
say_mod = "beep boops" //inherited from a user's real species
sexes = 0
species_traits = list(NOTRANSSTING,NOGENITALS,NOAROUSAL) //all of these + whatever we inherit from the real species
@@ -17,11 +17,11 @@
var/disguise_fail_health = 75 //When their health gets to this level their synthflesh partially falls off
var/datum/species/fake_species = null //a species to do most of our work for us, unless we're damaged
species_language_holder = /datum/language_holder/synthetic
species_type = "robotic"
species_category = SPECIES_CATEGORY_ROBOT
/datum/species/synth/military
name = "Military Synth"
id = "military_synth"
id = SPECIES_SYNTH_MIL
armor = 25
punchdamagelow = 10
punchdamagehigh = 19
@@ -1,6 +1,6 @@
/datum/species/vampire
name = "Vampire"
id = "vampire"
id = SPECIES_VAMPIRE
default_color = "FFFFFF"
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,DRINKSBLOOD,HAS_FLESH,HAS_BONE)
inherent_traits = list(TRAIT_NOHUNGER,TRAIT_NOBREATH)
@@ -14,7 +14,7 @@
limbs_id = "human"
skinned_type = /obj/item/stack/sheet/animalhide/human
var/info_text = "You are a <span class='danger'>Vampire</span>. You will slowly but constantly lose blood if outside of a coffin. If inside a coffin, you will slowly heal. You may gain more blood by grabbing a live victim and using your drain ability."
species_type = "undead"
species_category = SPECIES_CATEGORY_UNDEAD
/datum/species/vampire/check_roundstart_eligible()
if(SSevents.holidays && SSevents.holidays[HALLOWEEN])
@@ -1,7 +1,7 @@
/datum/species/xeno
// A cloning mistake, crossing human and xenomorph DNA
name = "Xenomorph Hybrid"
id = "xeno"
id = SPECIES_XENOHYBRID
say_mod = "hisses"
default_color = "00FF00"
species_traits = list(MUTCOLORS,EYECOLOR,LIPS,CAN_SCAR)
@@ -15,4 +15,4 @@
exotic_bloodtype = "X*"
damage_overlay_type = "xeno"
liked_food = MEAT
species_type = "alien"
species_category = SPECIES_CATEGORY_ALIEN
@@ -15,7 +15,7 @@
var/static/list/spooks = list('sound/hallucinations/growl1.ogg','sound/hallucinations/growl2.ogg','sound/hallucinations/growl3.ogg','sound/hallucinations/veryfar_noise.ogg','sound/hallucinations/wail.ogg')
disliked_food = NONE
liked_food = GROSS | MEAT | RAW
species_type = "undead"
species_category = SPECIES_CATEGORY_UNDEAD
/datum/species/zombie/notspaceproof
id = "notspaceproofzombie"
@@ -98,7 +98,7 @@
// Your skin falls off
/datum/species/krokodil_addict
name = "Human"
name = SPECIES_HUMAN
id = "goofzombies"
limbs_id = "zombie" //They look like zombies
sexes = 0
@@ -50,19 +50,21 @@ There are several things that need to be remembered:
//HAIR OVERLAY
/mob/living/carbon/human/update_hair()
dna.species.handle_hair(src)
if(dna.species.should_render())
dna.species.handle_hair(src)
//used when putting/removing clothes that hide certain mutant body parts to just update those and not update the whole body.
/mob/living/carbon/human/proc/update_mutant_bodyparts()
dna.species.handle_mutant_bodyparts(src)
if(dna.species.should_render())
dna.species.handle_mutant_bodyparts(src)
/mob/living/carbon/human/update_body(update_genitals = FALSE)
remove_overlay(BODY_LAYER)
dna.species.handle_body(src)
..()
if(update_genitals)
update_genitals()
if(dna.species.should_render())
remove_overlay(BODY_LAYER)
dna.species.handle_body(src)
..()
if(update_genitals)
update_genitals()
/mob/living/carbon/human/update_fire()
..((fire_stacks > 3) ? "Standing" : "Generic_mob_burning")
@@ -71,381 +73,385 @@ There are several things that need to be remembered:
/* --------------------------------------- */
//For legacy support.
/mob/living/carbon/human/regenerate_icons()
if(!..())
icon_render_key = null //invalidate bodyparts cache
update_body(TRUE)
update_hair()
update_inv_w_uniform()
update_inv_wear_id()
update_inv_gloves()
update_inv_glasses()
update_inv_ears()
update_inv_shoes()
update_inv_s_store()
update_inv_wear_mask()
update_inv_head()
update_inv_belt()
update_inv_back()
update_inv_wear_suit()
update_inv_pockets()
update_inv_neck()
update_transform()
//mutations
update_mutations_overlay()
//damage overlays
update_damage_overlays()
if(dna.species.should_render())
if(!..())
icon_render_key = null //invalidate bodyparts cache
update_body(TRUE)
update_hair()
update_inv_w_uniform()
update_inv_wear_id()
update_inv_gloves()
update_inv_glasses()
update_inv_ears()
update_inv_shoes()
update_inv_s_store()
update_inv_wear_mask()
update_inv_head()
update_inv_belt()
update_inv_back()
update_inv_wear_suit()
update_inv_pockets()
update_inv_neck()
update_transform()
//mutations
update_mutations_overlay()
//damage overlays
update_damage_overlays()
/* --------------------------------------- */
//vvvvvv UPDATE_INV PROCS vvvvvv
/mob/living/carbon/human/update_inv_w_uniform()
remove_overlay(UNIFORM_LAYER)
if(dna.species.should_render())
remove_overlay(UNIFORM_LAYER)
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_W_UNIFORM]
inv.update_icon()
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_W_UNIFORM]
inv.update_icon()
if(istype(w_uniform, /obj/item/clothing/under))
var/obj/item/clothing/under/U = w_uniform
U.screen_loc = ui_iclothing
if(client && hud_used && hud_used.hud_shown)
if(hud_used.inventory_shown)
client.screen += w_uniform
update_observer_view(w_uniform,1)
if(istype(w_uniform, /obj/item/clothing/under))
var/obj/item/clothing/under/U = w_uniform
U.screen_loc = ui_iclothing
if(client && hud_used && hud_used.hud_shown)
if(hud_used.inventory_shown)
client.screen += w_uniform
update_observer_view(w_uniform,1)
if(wear_suit && (wear_suit.flags_inv & HIDEJUMPSUIT))
return
if(wear_suit && (wear_suit.flags_inv & HIDEJUMPSUIT))
return
var/target_overlay = U.icon_state
if(U.adjusted == ALT_STYLE)
target_overlay = "[target_overlay]_d"
var/target_overlay = U.icon_state
if(U.adjusted == ALT_STYLE)
target_overlay = "[target_overlay]_d"
var/alt_worn = U.mob_overlay_icon || 'icons/mob/clothing/uniform.dmi'
var/variant_flag = NONE
var/alt_worn = U.mob_overlay_icon || 'icons/mob/clothing/uniform.dmi'
var/variant_flag = NONE
if((DIGITIGRADE in dna.species.species_traits) && U.mutantrace_variation & STYLE_DIGITIGRADE && !(U.mutantrace_variation & STYLE_NO_ANTHRO_ICON))
alt_worn = U.anthro_mob_worn_overlay || 'icons/mob/clothing/uniform_digi.dmi'
variant_flag |= STYLE_DIGITIGRADE
if((DIGITIGRADE in dna.species.species_traits) && U.mutantrace_variation & STYLE_DIGITIGRADE && !(U.mutantrace_variation & STYLE_NO_ANTHRO_ICON))
alt_worn = U.anthro_mob_worn_overlay || 'icons/mob/clothing/uniform_digi.dmi'
variant_flag |= STYLE_DIGITIGRADE
var/mask
if(dna.species.mutant_bodyparts["taur"])
var/datum/sprite_accessory/taur/T = GLOB.taur_list[dna.features["taur"]]
var/clip_flag = U.mutantrace_variation & T?.hide_legs
if(clip_flag)
variant_flag |= clip_flag
mask = T.alpha_mask_state
var/mask
if(dna.species.mutant_bodyparts["taur"])
var/datum/sprite_accessory/taur/T = GLOB.taur_list[dna.features["taur"]]
var/clip_flag = U.mutantrace_variation & T?.hide_legs
if(clip_flag)
variant_flag |= clip_flag
mask = T.alpha_mask_state
var/mutable_appearance/uniform_overlay
var/mutable_appearance/uniform_overlay
var/gendered = (dna?.species.sexes && dna.features["body_model"] == FEMALE) ? U.fitted : NO_FEMALE_UNIFORM
uniform_overlay = U.build_worn_icon( UNIFORM_LAYER, alt_worn, FALSE, gendered, target_overlay, variant_flag, FALSE, mask)
var/gendered = (dna?.species.sexes && dna.features["body_model"] == FEMALE) ? U.fitted : NO_FEMALE_UNIFORM
uniform_overlay = U.build_worn_icon( UNIFORM_LAYER, alt_worn, FALSE, gendered, target_overlay, variant_flag, FALSE, mask)
if(OFFSET_UNIFORM in dna.species.offset_features)
uniform_overlay.pixel_x += dna.species.offset_features[OFFSET_UNIFORM][1]
uniform_overlay.pixel_y += dna.species.offset_features[OFFSET_UNIFORM][2]
overlays_standing[UNIFORM_LAYER] = uniform_overlay
apply_overlay(UNIFORM_LAYER)
update_mutant_bodyparts()
if(OFFSET_UNIFORM in dna.species.offset_features)
uniform_overlay.pixel_x += dna.species.offset_features[OFFSET_UNIFORM][1]
uniform_overlay.pixel_y += dna.species.offset_features[OFFSET_UNIFORM][2]
overlays_standing[UNIFORM_LAYER] = uniform_overlay
apply_overlay(UNIFORM_LAYER)
update_mutant_bodyparts()
/mob/living/carbon/human/update_inv_wear_id()
remove_overlay(ID_LAYER)
if(dna.species.should_render())
remove_overlay(ID_LAYER)
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_WEAR_ID]
inv.update_icon()
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_WEAR_ID]
inv.update_icon()
var/mutable_appearance/id_overlay = overlays_standing[ID_LAYER]
var/mutable_appearance/id_overlay = overlays_standing[ID_LAYER]
if(wear_id)
wear_id.screen_loc = ui_id
if(client && hud_used && hud_used.hud_shown)
client.screen += wear_id
update_observer_view(wear_id)
if(wear_id)
wear_id.screen_loc = ui_id
if(client && hud_used && hud_used.hud_shown)
client.screen += wear_id
update_observer_view(wear_id)
//TODO: add an icon file for ID slot stuff, so it's less snowflakey
id_overlay = wear_id.build_worn_icon(default_layer = ID_LAYER, default_icon_file = 'icons/mob/mob.dmi', override_state = wear_id.item_state)
if(OFFSET_ID in dna.species.offset_features)
id_overlay.pixel_x += dna.species.offset_features[OFFSET_ID][1]
id_overlay.pixel_y += dna.species.offset_features[OFFSET_ID][2]
overlays_standing[ID_LAYER] = id_overlay
apply_overlay(ID_LAYER)
//TODO: add an icon file for ID slot stuff, so it's less snowflakey
id_overlay = wear_id.build_worn_icon(default_layer = ID_LAYER, default_icon_file = 'icons/mob/mob.dmi', override_state = wear_id.item_state)
if(OFFSET_ID in dna.species.offset_features)
id_overlay.pixel_x += dna.species.offset_features[OFFSET_ID][1]
id_overlay.pixel_y += dna.species.offset_features[OFFSET_ID][2]
overlays_standing[ID_LAYER] = id_overlay
apply_overlay(ID_LAYER)
/mob/living/carbon/human/update_inv_gloves()
remove_overlay(GLOVES_LAYER)
if(dna.species.should_render())
remove_overlay(GLOVES_LAYER)
if(client && hud_used && hud_used.inv_slots[SLOT_GLOVES])
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_GLOVES]
inv.update_icon()
if(client && hud_used && hud_used.inv_slots[SLOT_GLOVES])
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_GLOVES]
inv.update_icon()
if(!gloves && bloody_hands)
var/mutable_appearance/bloody_overlay = mutable_appearance('icons/effects/blood.dmi', "bloodyhands", -GLOVES_LAYER, color = blood_DNA_to_color())
if(get_num_arms(FALSE) < 2)
if(has_left_hand(FALSE))
bloody_overlay.icon_state = "bloodyhands_left"
else if(has_right_hand(FALSE))
bloody_overlay.icon_state = "bloodyhands_right"
if(!gloves && bloody_hands)
var/mutable_appearance/bloody_overlay = mutable_appearance('icons/effects/blood.dmi', "bloodyhands", -GLOVES_LAYER, color = blood_DNA_to_color())
if(get_num_arms(FALSE) < 2)
if(has_left_hand(FALSE))
bloody_overlay.icon_state = "bloodyhands_left"
else if(has_right_hand(FALSE))
bloody_overlay.icon_state = "bloodyhands_right"
overlays_standing[GLOVES_LAYER] = bloody_overlay
overlays_standing[GLOVES_LAYER] = bloody_overlay
var/mutable_appearance/gloves_overlay = overlays_standing[GLOVES_LAYER]
if(gloves)
gloves.screen_loc = ui_gloves
if(client && hud_used && hud_used.hud_shown)
if(hud_used.inventory_shown)
client.screen += gloves
update_observer_view(gloves,1)
overlays_standing[GLOVES_LAYER] = gloves.build_worn_icon(default_layer = GLOVES_LAYER, default_icon_file = 'icons/mob/clothing/hands.dmi')
gloves_overlay = overlays_standing[GLOVES_LAYER]
if(OFFSET_GLOVES in dna.species.offset_features)
gloves_overlay.pixel_x += dna.species.offset_features[OFFSET_GLOVES][1]
gloves_overlay.pixel_y += dna.species.offset_features[OFFSET_GLOVES][2]
overlays_standing[GLOVES_LAYER] = gloves_overlay
apply_overlay(GLOVES_LAYER)
var/mutable_appearance/gloves_overlay = overlays_standing[GLOVES_LAYER]
if(gloves)
gloves.screen_loc = ui_gloves
if(client && hud_used && hud_used.hud_shown)
if(hud_used.inventory_shown)
client.screen += gloves
update_observer_view(gloves,1)
overlays_standing[GLOVES_LAYER] = gloves.build_worn_icon(default_layer = GLOVES_LAYER, default_icon_file = 'icons/mob/clothing/hands.dmi')
gloves_overlay = overlays_standing[GLOVES_LAYER]
if(OFFSET_GLOVES in dna.species.offset_features)
gloves_overlay.pixel_x += dna.species.offset_features[OFFSET_GLOVES][1]
gloves_overlay.pixel_y += dna.species.offset_features[OFFSET_GLOVES][2]
overlays_standing[GLOVES_LAYER] = gloves_overlay
apply_overlay(GLOVES_LAYER)
/mob/living/carbon/human/update_inv_glasses()
remove_overlay(GLASSES_LAYER)
if(dna.species.should_render())
remove_overlay(GLASSES_LAYER)
if(!get_bodypart(BODY_ZONE_HEAD)) //decapitated
return
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_GLASSES]
inv.update_icon()
if(glasses)
glasses.screen_loc = ui_glasses //...draw the item in the inventory screen
if(client && hud_used && hud_used.hud_shown)
if(hud_used.inventory_shown) //if the inventory is open ...
client.screen += glasses //Either way, add the item to the HUD
update_observer_view(glasses,1)
if(!(head && (head.flags_inv & HIDEEYES)) && !(wear_mask && (wear_mask.flags_inv & HIDEEYES)))
overlays_standing[GLASSES_LAYER] = glasses.build_worn_icon(default_layer = GLASSES_LAYER, default_icon_file = 'icons/mob/clothing/eyes.dmi', override_state = glasses.icon_state)
var/mutable_appearance/glasses_overlay = overlays_standing[GLASSES_LAYER]
if(glasses_overlay)
if(OFFSET_GLASSES in dna.species.offset_features)
glasses_overlay.pixel_x += dna.species.offset_features[OFFSET_GLASSES][1]
glasses_overlay.pixel_y += dna.species.offset_features[OFFSET_GLASSES][2]
overlays_standing[GLASSES_LAYER] = glasses_overlay
apply_overlay(GLASSES_LAYER)
/mob/living/carbon/human/update_inv_ears()
remove_overlay(EARS_LAYER)
if(!get_bodypart(BODY_ZONE_HEAD)) //decapitated
return
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_EARS]
inv.update_icon()
if(ears)
ears.screen_loc = ui_ears //move the item to the appropriate screen loc
if(client && hud_used && hud_used.hud_shown)
if(hud_used.inventory_shown) //if the inventory is open
client.screen += ears //add it to the client's screen
update_observer_view(ears,1)
overlays_standing[EARS_LAYER] = ears.build_worn_icon(default_layer = EARS_LAYER, default_icon_file = 'icons/mob/ears.dmi')
var/mutable_appearance/ears_overlay = overlays_standing[EARS_LAYER]
if(OFFSET_EARS in dna.species.offset_features)
ears_overlay.pixel_x += dna.species.offset_features[OFFSET_EARS][1]
ears_overlay.pixel_y += dna.species.offset_features[OFFSET_EARS][2]
overlays_standing[EARS_LAYER] = ears_overlay
apply_overlay(EARS_LAYER)
/mob/living/carbon/human/update_inv_shoes()
remove_overlay(SHOES_LAYER)
if(get_num_legs(FALSE) <2)
return
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_SHOES]
inv.update_icon()
if(dna.species.mutant_bodyparts["taur"])
var/datum/sprite_accessory/taur/T = GLOB.taur_list[dna.features["taur"]]
if(T?.hide_legs) //If only they actually made shoes unwearable. Please don't making cosmetics, guys.
if(!get_bodypart(BODY_ZONE_HEAD)) //decapitated
return
if(shoes)
var/obj/item/clothing/shoes/S = shoes
shoes.screen_loc = ui_shoes //move the item to the appropriate screen loc
if(client && hud_used && hud_used.hud_shown)
if(hud_used.inventory_shown) //if the inventory is open
client.screen += shoes //add it to client's screen
update_observer_view(shoes,1)
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_GLASSES]
inv.update_icon()
var/alt_icon = S.mob_overlay_icon || 'icons/mob/clothing/feet.dmi'
var/variation_flag = NONE
if((DIGITIGRADE in dna.species.species_traits) && S.mutantrace_variation & STYLE_DIGITIGRADE && !(S.mutantrace_variation & STYLE_NO_ANTHRO_ICON))
alt_icon = S.anthro_mob_worn_overlay || 'icons/mob/clothing/feet_digi.dmi'
variation_flag |= STYLE_DIGITIGRADE
if(glasses)
glasses.screen_loc = ui_glasses //...draw the item in the inventory screen
if(client && hud_used && hud_used.hud_shown)
if(hud_used.inventory_shown) //if the inventory is open ...
client.screen += glasses //Either way, add the item to the HUD
update_observer_view(glasses,1)
if(!(head && (head.flags_inv & HIDEEYES)) && !(wear_mask && (wear_mask.flags_inv & HIDEEYES)))
overlays_standing[GLASSES_LAYER] = glasses.build_worn_icon(default_layer = GLASSES_LAYER, default_icon_file = 'icons/mob/clothing/eyes.dmi', override_state = glasses.icon_state)
var/mutable_appearance/glasses_overlay = overlays_standing[GLASSES_LAYER]
if(glasses_overlay)
if(OFFSET_GLASSES in dna.species.offset_features)
glasses_overlay.pixel_x += dna.species.offset_features[OFFSET_GLASSES][1]
glasses_overlay.pixel_y += dna.species.offset_features[OFFSET_GLASSES][2]
overlays_standing[GLASSES_LAYER] = glasses_overlay
apply_overlay(GLASSES_LAYER)
overlays_standing[SHOES_LAYER] = shoes.build_worn_icon(SHOES_LAYER, alt_icon, FALSE, NO_FEMALE_UNIFORM, S.icon_state, variation_flag, FALSE)
var/mutable_appearance/shoes_overlay = overlays_standing[SHOES_LAYER]
if(OFFSET_SHOES in dna.species.offset_features)
shoes_overlay.pixel_x += dna.species.offset_features[OFFSET_SHOES][1]
shoes_overlay.pixel_y += dna.species.offset_features[OFFSET_SHOES][2]
overlays_standing[SHOES_LAYER] = shoes_overlay
apply_overlay(SHOES_LAYER)
/mob/living/carbon/human/update_inv_ears()
if(dna.species.should_render())
remove_overlay(EARS_LAYER)
/mob/living/carbon/human/update_inv_s_store()
remove_overlay(SUIT_STORE_LAYER)
if(!get_bodypart(BODY_ZONE_HEAD)) //decapitated
return
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_S_STORE]
inv.update_icon()
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_EARS]
inv.update_icon()
if(s_store)
s_store.screen_loc = ui_sstore1
if(client && hud_used && hud_used.hud_shown)
client.screen += s_store
update_observer_view(s_store)
var/t_state = s_store.item_state
if(!t_state)
t_state = s_store.icon_state
overlays_standing[SUIT_STORE_LAYER] = mutable_appearance(((s_store.mob_overlay_icon) ? s_store.mob_overlay_icon : 'icons/mob/clothing/belt_mirror.dmi'), t_state, -SUIT_STORE_LAYER)
var/mutable_appearance/s_store_overlay = overlays_standing[SUIT_STORE_LAYER]
if(OFFSET_S_STORE in dna.species.offset_features)
s_store_overlay.pixel_x += dna.species.offset_features[OFFSET_S_STORE][1]
s_store_overlay.pixel_y += dna.species.offset_features[OFFSET_S_STORE][2]
overlays_standing[SUIT_STORE_LAYER] = s_store_overlay
apply_overlay(SUIT_STORE_LAYER)
if(ears)
ears.screen_loc = ui_ears //move the item to the appropriate screen loc
if(client && hud_used && hud_used.hud_shown)
if(hud_used.inventory_shown) //if the inventory is open
client.screen += ears //add it to the client's screen
update_observer_view(ears,1)
overlays_standing[EARS_LAYER] = ears.build_worn_icon(default_layer = EARS_LAYER, default_icon_file = 'icons/mob/ears.dmi')
var/mutable_appearance/ears_overlay = overlays_standing[EARS_LAYER]
if(OFFSET_EARS in dna.species.offset_features)
ears_overlay.pixel_x += dna.species.offset_features[OFFSET_EARS][1]
ears_overlay.pixel_y += dna.species.offset_features[OFFSET_EARS][2]
overlays_standing[EARS_LAYER] = ears_overlay
apply_overlay(EARS_LAYER)
/mob/living/carbon/human/update_inv_head()
remove_overlay(HEAD_LAYER)
/mob/living/carbon/human/update_inv_shoes()
if(dna.species.should_render())
remove_overlay(SHOES_LAYER)
if(!get_bodypart(BODY_ZONE_HEAD)) //Decapitated
return
if(get_num_legs(FALSE) <2)
return
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_HEAD]
inv.update_icon()
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_SHOES]
inv.update_icon()
if(head)
head.screen_loc = ui_head
if(client && hud_used && hud_used.hud_shown)
if(hud_used.inventory_shown)
client.screen += head
update_observer_view(head,1)
remove_overlay(HEAD_LAYER)
var/obj/item/clothing/head/H = head
var/alt_icon = H.mob_overlay_icon || 'icons/mob/clothing/head.dmi'
var/muzzled = FALSE
var/variation_flag = NONE
if(dna.species.mutant_bodyparts["mam_snouts"] && dna.features["mam_snouts"] != "None")
muzzled = TRUE
else if(dna.species.mutant_bodyparts["snout"] && dna.features["snout"] != "None")
muzzled = TRUE
if(muzzled && H.mutantrace_variation & STYLE_MUZZLE && !(H.mutantrace_variation & STYLE_NO_ANTHRO_ICON))
alt_icon = H.anthro_mob_worn_overlay || 'icons/mob/clothing/head_muzzled.dmi'
variation_flag |= STYLE_MUZZLE
overlays_standing[HEAD_LAYER] = H.build_worn_icon(HEAD_LAYER, alt_icon, FALSE, NO_FEMALE_UNIFORM, H.icon_state, variation_flag, FALSE)
var/mutable_appearance/head_overlay = overlays_standing[HEAD_LAYER]
if(OFFSET_HEAD in dna.species.offset_features)
head_overlay.pixel_x += dna.species.offset_features[OFFSET_HEAD][1]
head_overlay.pixel_y += dna.species.offset_features[OFFSET_HEAD][2]
overlays_standing[HEAD_LAYER] = head_overlay
apply_overlay(HEAD_LAYER)
update_mutant_bodyparts()
/mob/living/carbon/human/update_inv_belt()
remove_overlay(BELT_LAYER)
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_BELT]
inv.update_icon()
if(belt)
belt.screen_loc = ui_belt
if(client && hud_used && hud_used.hud_shown)
client.screen += belt
update_observer_view(belt)
overlays_standing[BELT_LAYER] = belt.build_worn_icon(default_layer = BELT_LAYER, default_icon_file = 'icons/mob/clothing/belt.dmi')
var/mutable_appearance/belt_overlay = overlays_standing[BELT_LAYER]
if(OFFSET_BELT in dna.species.offset_features)
belt_overlay.pixel_x += dna.species.offset_features[OFFSET_BELT][1]
belt_overlay.pixel_y += dna.species.offset_features[OFFSET_BELT][2]
overlays_standing[BELT_LAYER] = belt_overlay
apply_overlay(BELT_LAYER)
/mob/living/carbon/human/update_inv_wear_suit()
remove_overlay(SUIT_LAYER)
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_WEAR_SUIT]
inv.update_icon()
if(wear_suit)
var/obj/item/clothing/suit/S = wear_suit
wear_suit.screen_loc = ui_oclothing
if(client && hud_used && hud_used.hud_shown)
if(hud_used.inventory_shown)
client.screen += wear_suit
update_observer_view(wear_suit,1)
var/worn_icon = wear_suit.mob_overlay_icon || 'icons/mob/clothing/suit.dmi'
var/worn_state = wear_suit.icon_state
var/center = FALSE
var/dimension_x = 32
var/dimension_y = 32
var/variation_flag = NONE
var/datum/sprite_accessory/taur/T
if(dna.species.mutant_bodyparts["taur"])
T = GLOB.taur_list[dna.features["taur"]]
var/datum/sprite_accessory/taur/T = GLOB.taur_list[dna.features["taur"]]
if(T?.hide_legs) //If only they actually made shoes unwearable. Please don't making cosmetics, guys.
return
if(S.mutantrace_variation)
if(shoes)
var/obj/item/clothing/shoes/S = shoes
shoes.screen_loc = ui_shoes //move the item to the appropriate screen loc
if(client && hud_used && hud_used.hud_shown)
if(hud_used.inventory_shown) //if the inventory is open
client.screen += shoes //add it to client's screen
update_observer_view(shoes,1)
if(T?.taur_mode)
var/init_worn_icon = worn_icon
variation_flag |= S.mutantrace_variation & T.taur_mode || S.mutantrace_variation & T.alt_taur_mode
switch(variation_flag)
if(STYLE_HOOF_TAURIC)
worn_icon = 'icons/mob/clothing/taur_hooved.dmi'
if(STYLE_SNEK_TAURIC)
worn_icon = 'icons/mob/clothing/taur_naga.dmi'
if(STYLE_PAW_TAURIC)
worn_icon = 'icons/mob/clothing/taur_canine.dmi'
if(worn_icon != init_worn_icon) //worn icon sprite was changed, taur offsets will have to be applied.
if(S.taur_mob_worn_overlay) //not going to make several new variables for all taur types. Nope.
var/static/list/icon_to_state = list('icons/mob/clothing/taur_hooved.dmi' = "_hooved", 'icons/mob/clothing/taur_naga.dmi' = "_naga", 'icons/mob/clothing/taur_canine.dmi' = "_paws")
worn_state += icon_to_state[worn_icon]
worn_icon = S.taur_mob_worn_overlay
center = T.center
dimension_x = T.dimension_x
dimension_y = T.dimension_y
else if((DIGITIGRADE in dna.species.species_traits) && S.mutantrace_variation & STYLE_DIGITIGRADE && !(S.mutantrace_variation & STYLE_NO_ANTHRO_ICON)) //not a taur, but digitigrade legs.
worn_icon = S.anthro_mob_worn_overlay || 'icons/mob/clothing/suit_digi.dmi'
var/alt_icon = S.mob_overlay_icon || 'icons/mob/clothing/feet.dmi'
var/variation_flag = NONE
if((DIGITIGRADE in dna.species.species_traits) && S.mutantrace_variation & STYLE_DIGITIGRADE && !(S.mutantrace_variation & STYLE_NO_ANTHRO_ICON))
alt_icon = S.anthro_mob_worn_overlay || 'icons/mob/clothing/feet_digi.dmi'
variation_flag |= STYLE_DIGITIGRADE
overlays_standing[SUIT_LAYER] = S.build_worn_icon(SUIT_LAYER, worn_icon, FALSE, NO_FEMALE_UNIFORM, worn_state, variation_flag, FALSE)
var/mutable_appearance/suit_overlay = overlays_standing[SUIT_LAYER]
if(OFFSET_SUIT in dna.species.offset_features)
suit_overlay.pixel_x += dna.species.offset_features[OFFSET_SUIT][1]
suit_overlay.pixel_y += dna.species.offset_features[OFFSET_SUIT][2]
if(center)
suit_overlay = center_image(suit_overlay, dimension_x, dimension_y)
overlays_standing[SUIT_LAYER] = suit_overlay
update_hair()
update_mutant_bodyparts()
overlays_standing[SHOES_LAYER] = shoes.build_worn_icon(SHOES_LAYER, alt_icon, FALSE, NO_FEMALE_UNIFORM, S.icon_state, variation_flag, FALSE)
var/mutable_appearance/shoes_overlay = overlays_standing[SHOES_LAYER]
if(OFFSET_SHOES in dna.species.offset_features)
shoes_overlay.pixel_x += dna.species.offset_features[OFFSET_SHOES][1]
shoes_overlay.pixel_y += dna.species.offset_features[OFFSET_SHOES][2]
overlays_standing[SHOES_LAYER] = shoes_overlay
apply_overlay(SHOES_LAYER)
apply_overlay(SUIT_LAYER)
/mob/living/carbon/human/update_inv_s_store()
if(dna.species.should_render())
remove_overlay(SUIT_STORE_LAYER)
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_S_STORE]
inv.update_icon()
if(s_store)
s_store.screen_loc = ui_sstore1
if(client && hud_used && hud_used.hud_shown)
client.screen += s_store
update_observer_view(s_store)
var/t_state = s_store.item_state
if(!t_state)
t_state = s_store.icon_state
overlays_standing[SUIT_STORE_LAYER] = mutable_appearance(((s_store.mob_overlay_icon) ? s_store.mob_overlay_icon : 'icons/mob/clothing/belt_mirror.dmi'), t_state, -SUIT_STORE_LAYER)
var/mutable_appearance/s_store_overlay = overlays_standing[SUIT_STORE_LAYER]
if(OFFSET_S_STORE in dna.species.offset_features)
s_store_overlay.pixel_x += dna.species.offset_features[OFFSET_S_STORE][1]
s_store_overlay.pixel_y += dna.species.offset_features[OFFSET_S_STORE][2]
overlays_standing[SUIT_STORE_LAYER] = s_store_overlay
apply_overlay(SUIT_STORE_LAYER)
/mob/living/carbon/human/update_inv_head()
if(dna.species.should_render())
remove_overlay(HEAD_LAYER)
if(!get_bodypart(BODY_ZONE_HEAD)) //Decapitated
return
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_HEAD]
inv.update_icon()
if(head)
head.screen_loc = ui_head
if(client && hud_used && hud_used.hud_shown)
if(hud_used.inventory_shown)
client.screen += head
update_observer_view(head,1)
remove_overlay(HEAD_LAYER)
var/obj/item/clothing/head/H = head
var/alt_icon = H.mob_overlay_icon || 'icons/mob/clothing/head.dmi'
var/muzzled = FALSE
var/variation_flag = NONE
if(dna.species.mutant_bodyparts["mam_snouts"] && dna.features["mam_snouts"] != "None")
muzzled = TRUE
else if(dna.species.mutant_bodyparts["snout"] && dna.features["snout"] != "None")
muzzled = TRUE
if(muzzled && H.mutantrace_variation & STYLE_MUZZLE && !(H.mutantrace_variation & STYLE_NO_ANTHRO_ICON))
alt_icon = H.anthro_mob_worn_overlay || 'icons/mob/clothing/head_muzzled.dmi'
variation_flag |= STYLE_MUZZLE
overlays_standing[HEAD_LAYER] = H.build_worn_icon(HEAD_LAYER, alt_icon, FALSE, NO_FEMALE_UNIFORM, H.icon_state, variation_flag, FALSE)
var/mutable_appearance/head_overlay = overlays_standing[HEAD_LAYER]
if(OFFSET_HEAD in dna.species.offset_features)
head_overlay.pixel_x += dna.species.offset_features[OFFSET_HEAD][1]
head_overlay.pixel_y += dna.species.offset_features[OFFSET_HEAD][2]
overlays_standing[HEAD_LAYER] = head_overlay
apply_overlay(HEAD_LAYER)
update_mutant_bodyparts()
/mob/living/carbon/human/update_inv_belt()
if(dna.species.should_render())
remove_overlay(BELT_LAYER)
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_BELT]
inv.update_icon()
if(belt)
belt.screen_loc = ui_belt
if(client && hud_used && hud_used.hud_shown)
client.screen += belt
update_observer_view(belt)
overlays_standing[BELT_LAYER] = belt.build_worn_icon(default_layer = BELT_LAYER, default_icon_file = 'icons/mob/clothing/belt.dmi')
var/mutable_appearance/belt_overlay = overlays_standing[BELT_LAYER]
if(OFFSET_BELT in dna.species.offset_features)
belt_overlay.pixel_x += dna.species.offset_features[OFFSET_BELT][1]
belt_overlay.pixel_y += dna.species.offset_features[OFFSET_BELT][2]
overlays_standing[BELT_LAYER] = belt_overlay
apply_overlay(BELT_LAYER)
/mob/living/carbon/human/update_inv_wear_suit()
if(dna.species.should_render())
remove_overlay(SUIT_LAYER)
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_WEAR_SUIT]
inv.update_icon()
if(wear_suit)
var/obj/item/clothing/suit/S = wear_suit
wear_suit.screen_loc = ui_oclothing
if(client && hud_used && hud_used.hud_shown)
if(hud_used.inventory_shown)
client.screen += wear_suit
update_observer_view(wear_suit,1)
var/worn_icon = wear_suit.mob_overlay_icon || 'icons/mob/clothing/suit.dmi'
var/worn_state = wear_suit.icon_state
var/center = FALSE
var/dimension_x = 32
var/dimension_y = 32
var/variation_flag = NONE
var/datum/sprite_accessory/taur/T
if(dna.species.mutant_bodyparts["taur"])
T = GLOB.taur_list[dna.features["taur"]]
if(S.mutantrace_variation)
if(T?.taur_mode)
var/init_worn_icon = worn_icon
variation_flag |= S.mutantrace_variation & T.taur_mode || S.mutantrace_variation & T.alt_taur_mode
switch(variation_flag)
if(STYLE_HOOF_TAURIC)
worn_icon = 'icons/mob/clothing/taur_hooved.dmi'
if(STYLE_SNEK_TAURIC)
worn_icon = 'icons/mob/clothing/taur_naga.dmi'
if(STYLE_PAW_TAURIC)
worn_icon = 'icons/mob/clothing/taur_canine.dmi'
if(worn_icon != init_worn_icon) //worn icon sprite was changed, taur offsets will have to be applied.
if(S.taur_mob_worn_overlay) //not going to make several new variables for all taur types. Nope.
var/static/list/icon_to_state = list('icons/mob/clothing/taur_hooved.dmi' = "_hooved", 'icons/mob/clothing/taur_naga.dmi' = "_naga", 'icons/mob/clothing/taur_canine.dmi' = "_paws")
worn_state += icon_to_state[worn_icon]
worn_icon = S.taur_mob_worn_overlay
center = T.center
dimension_x = T.dimension_x
dimension_y = T.dimension_y
else if((DIGITIGRADE in dna.species.species_traits) && S.mutantrace_variation & STYLE_DIGITIGRADE && !(S.mutantrace_variation & STYLE_NO_ANTHRO_ICON)) //not a taur, but digitigrade legs.
worn_icon = S.anthro_mob_worn_overlay || 'icons/mob/clothing/suit_digi.dmi'
variation_flag |= STYLE_DIGITIGRADE
overlays_standing[SUIT_LAYER] = S.build_worn_icon(SUIT_LAYER, worn_icon, FALSE, NO_FEMALE_UNIFORM, worn_state, variation_flag, FALSE)
var/mutable_appearance/suit_overlay = overlays_standing[SUIT_LAYER]
if(OFFSET_SUIT in dna.species.offset_features)
suit_overlay.pixel_x += dna.species.offset_features[OFFSET_SUIT][1]
suit_overlay.pixel_y += dna.species.offset_features[OFFSET_SUIT][2]
if(center)
suit_overlay = center_image(suit_overlay, dimension_x, dimension_y)
overlays_standing[SUIT_LAYER] = suit_overlay
update_hair()
update_mutant_bodyparts()
apply_overlay(SUIT_LAYER)
/mob/living/carbon/human/update_inv_pockets()
if(client && hud_used)
@@ -471,55 +477,57 @@ There are several things that need to be remembered:
/mob/living/carbon/human/update_inv_wear_mask()
remove_overlay(FACEMASK_LAYER)
if(!get_bodypart(BODY_ZONE_HEAD)) //Decapitated
return
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_WEAR_MASK]
inv.update_icon()
if(wear_mask)
wear_mask.screen_loc = ui_mask
if(client && hud_used && hud_used.hud_shown)
if(hud_used.inventory_shown)
client.screen += wear_mask
update_observer_view(wear_mask,1)
var/obj/item/clothing/mask/M = wear_mask
if(dna.species.should_render())
remove_overlay(FACEMASK_LAYER)
var/alt_icon = M.mob_overlay_icon || 'icons/mob/clothing/mask.dmi'
var/muzzled = FALSE
var/variation_flag = NONE
if(head && (head.flags_inv & HIDEMASK))
if(!get_bodypart(BODY_ZONE_HEAD)) //Decapitated
return
if(dna.species.mutant_bodyparts["mam_snouts"] && dna.features["mam_snouts"] != "None")
muzzled = TRUE
else if(dna.species.mutant_bodyparts["snout"] && dna.features["snout"] != "None")
muzzled = TRUE
if(muzzled && M.mutantrace_variation & STYLE_MUZZLE && !(M.mutantrace_variation & STYLE_NO_ANTHRO_ICON))
alt_icon = M.anthro_mob_worn_overlay || 'icons/mob/clothing/mask_muzzled.dmi'
variation_flag |= STYLE_MUZZLE
var/mutable_appearance/mask_overlay = M.build_worn_icon(FACEMASK_LAYER, alt_icon, FALSE, NO_FEMALE_UNIFORM, wear_mask.icon_state, variation_flag, FALSE)
if(client && hud_used)
var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_WEAR_MASK]
inv.update_icon()
if(OFFSET_FACEMASK in dna.species.offset_features)
mask_overlay.pixel_x += dna.species.offset_features[OFFSET_FACEMASK][1]
mask_overlay.pixel_y += dna.species.offset_features[OFFSET_FACEMASK][2]
overlays_standing[FACEMASK_LAYER] = mask_overlay
apply_overlay(FACEMASK_LAYER)
update_mutant_bodyparts() //e.g. upgate needed because mask now hides lizard snout
if(wear_mask)
wear_mask.screen_loc = ui_mask
if(client && hud_used && hud_used.hud_shown)
if(hud_used.inventory_shown)
client.screen += wear_mask
update_observer_view(wear_mask,1)
var/obj/item/clothing/mask/M = wear_mask
remove_overlay(FACEMASK_LAYER)
var/alt_icon = M.mob_overlay_icon || 'icons/mob/clothing/mask.dmi'
var/muzzled = FALSE
var/variation_flag = NONE
if(head && (head.flags_inv & HIDEMASK))
return
if(dna.species.mutant_bodyparts["mam_snouts"] && dna.features["mam_snouts"] != "None")
muzzled = TRUE
else if(dna.species.mutant_bodyparts["snout"] && dna.features["snout"] != "None")
muzzled = TRUE
if(muzzled && M.mutantrace_variation & STYLE_MUZZLE && !(M.mutantrace_variation & STYLE_NO_ANTHRO_ICON))
alt_icon = M.anthro_mob_worn_overlay || 'icons/mob/clothing/mask_muzzled.dmi'
variation_flag |= STYLE_MUZZLE
var/mutable_appearance/mask_overlay = M.build_worn_icon(FACEMASK_LAYER, alt_icon, FALSE, NO_FEMALE_UNIFORM, wear_mask.icon_state, variation_flag, FALSE)
if(OFFSET_FACEMASK in dna.species.offset_features)
mask_overlay.pixel_x += dna.species.offset_features[OFFSET_FACEMASK][1]
mask_overlay.pixel_y += dna.species.offset_features[OFFSET_FACEMASK][2]
overlays_standing[FACEMASK_LAYER] = mask_overlay
apply_overlay(FACEMASK_LAYER)
update_mutant_bodyparts() //e.g. upgate needed because mask now hides lizard snout
/mob/living/carbon/human/update_inv_back()
..()
var/mutable_appearance/back_overlay = overlays_standing[BACK_LAYER]
if(back_overlay)
remove_overlay(BACK_LAYER)
if(OFFSET_BACK in dna.species.offset_features)
back_overlay.pixel_x += dna.species.offset_features[OFFSET_BACK][1]
back_overlay.pixel_y += dna.species.offset_features[OFFSET_BACK][2]
overlays_standing[BACK_LAYER] = back_overlay
apply_overlay(BACK_LAYER)
if(dna.species.should_render())
..()
var/mutable_appearance/back_overlay = overlays_standing[BACK_LAYER]
if(back_overlay)
remove_overlay(BACK_LAYER)
if(OFFSET_BACK in dna.species.offset_features)
back_overlay.pixel_x += dna.species.offset_features[OFFSET_BACK][1]
back_overlay.pixel_y += dna.species.offset_features[OFFSET_BACK][2]
overlays_standing[BACK_LAYER] = back_overlay
apply_overlay(BACK_LAYER)
/proc/wear_alpha_masked_version(state, icon, layer, female, alpha_mask)
var/mask = "-[alpha_mask]"
@@ -710,8 +718,6 @@ use_mob_overlay_icon: if FALSE, it will always use the default_icon_file even if
..()
update_hair()
/mob/living/carbon/human/proc/update_observer_view(obj/item/I, inventory)
if(observers && observers.len)
for(var/M in observers)
@@ -729,15 +735,17 @@ use_mob_overlay_icon: if FALSE, it will always use the default_icon_file even if
// Only renders the head of the human
/mob/living/carbon/human/proc/update_body_parts_head_only()
if (!dna)
if(!dna)
return
if (!dna.species)
if(!dna.species)
return
if(dna.species.should_render())
return
var/obj/item/bodypart/HD = get_bodypart("head")
if (!istype(HD))
if(!istype(HD))
return
HD.update_limb()
+5 -5
View File
@@ -844,12 +844,12 @@
return
var/strip_mod = 1
var/strip_silence = FALSE
if (ishuman(src)) //carbon doesn't actually wear gloves
if(ishuman(src)) //carbon doesn't actually wear gloves
var/mob/living/carbon/C = src
var/obj/item/clothing/gloves/g = C.gloves
if (istype(g))
strip_mod = g.strip_mod
strip_silence = g.strip_silence
var/obj/item/clothing/gloves/G = C.gloves
if(istype(G))
strip_mod = G.strip_mod
strip_silence = G.strip_silence
if (!strip_silence)
who.visible_message("<span class='danger'>[src] tries to remove [who]'s [what.name].</span>", \
"<span class='userdanger'>[src] tries to remove your [what.name].</span>", target = src,
@@ -237,6 +237,7 @@
active_block_do_stamina_damage(owner, object, stamina_cost, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return)
block_return[BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATED] = damage - final_damage
block_return[BLOCK_RETURN_SET_DAMAGE_TO] = final_damage
block_return[BLOCK_RETURN_MITIGATION_PERCENT] = clamp(1 - (final_damage / damage), 0, 1)
. = BLOCK_SHOULD_CHANGE_DAMAGE
if((final_damage <= 0) || (damage <= 0))
. |= BLOCK_SUCCESS //full block
+4 -1
View File
@@ -5,12 +5,15 @@
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_MOBILITY_NOMOVE), .proc/update_mobility)
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_MOBILITY_NOPICKUP), .proc/update_mobility)
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_MOBILITY_NOUSE), .proc/update_mobility)
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_MOBILITY_NOREST), .proc/update_mobility)
//Stuff like mobility flag updates, resting updates, etc.
//Force-set resting variable, without needing to resist/etc.
/mob/living/proc/set_resting(new_resting, silent = FALSE, updating = TRUE)
if(new_resting != resting)
if(resting && HAS_TRAIT(src, TRAIT_MOBILITY_NOREST)) //forcibly block resting from all sources - BE CAREFUL WITH THIS TRAIT
return
resting = new_resting
if(!silent)
to_chat(src, "<span class='notice'>You are now [resting? "resting" : "getting up"].</span>")
@@ -87,7 +90,7 @@
var/canstand_involuntary = conscious && !stat_softcrit && !knockdown && !chokehold && !paralyze && (ignore_legs || has_legs) && !(buckled && buckled.buckle_lying) && !(combat_flags & COMBAT_FLAG_HARD_STAMCRIT)
var/canstand = canstand_involuntary && !resting
var/should_be_lying = !canstand
var/should_be_lying = !canstand && !HAS_TRAIT(src, TRAIT_MOBILITY_NOREST)
if(buckled)
if(buckled.buckle_lying != -1)
should_be_lying = buckled.buckle_lying
@@ -52,6 +52,19 @@
var/lose_patience_timer_id //id for a timer to call LoseTarget(), used to stop mobs fixating on a target they can't reach
var/lose_patience_timeout = 300 //30 seconds by default, so there's no major changes to AI behaviour, beyond actually bailing if stuck forever
///When a target is found, will the mob attempt to charge at it's target?
var/charger = FALSE
///Tracks if the target is actively charging.
var/charge_state = FALSE
///In a charge, how many tiles will the charger travel?
var/charge_distance = 3
///How often can the charging mob actually charge? Effects the cooldown between charges.
var/charge_frequency = 6 SECONDS
///If the mob is charging, how long will it stun it's target on success, and itself on failure?
var/knockdown_time = 3 SECONDS
///Declares a cooldown for potential charges right off the bat.
COOLDOWN_DECLARE(charge_cooldown)
/mob/living/simple_animal/hostile/Initialize()
. = ..()
@@ -292,6 +305,9 @@
if(ranged) //We ranged? Shoot at em
if(!target.Adjacent(targets_from) && ranged_cooldown <= world.time) //But make sure they're not in range for a melee attack and our range attack is off cooldown
OpenFire(target)
if(charger && (target_distance > minimum_distance) && (target_distance <= charge_distance))//Attempt to close the distance with a charge.
enter_charge(target)
return TRUE
if(!Process_Spacemove()) //Drifting
walk(src,0)
return 1
@@ -599,3 +615,64 @@ mob/living/simple_animal/hostile/proc/DestroySurroundings() // for use with mega
. += M
else if (M.loc.type in hostile_machines)
. += M.loc
/**
* Proc that handles a charge attack windup for a mob.
*/
/mob/living/simple_animal/hostile/proc/enter_charge(var/atom/target)
if((mobility_flags & (MOBILITY_MOVE | MOBILITY_STAND)) != (MOBILITY_MOVE | MOBILITY_STAND) || charge_state)
return FALSE
if(!(COOLDOWN_FINISHED(src, charge_cooldown)) || !has_gravity() || !target.has_gravity())
return FALSE
Shake(15, 15, 1 SECONDS)
addtimer(CALLBACK(src, .proc/handle_charge_target, target), 1.5 SECONDS, TIMER_STOPPABLE)
/**
* Proc that throws the mob at the target after the windup.
*/
/mob/living/simple_animal/hostile/proc/handle_charge_target(var/atom/target)
charge_state = TRUE
throw_at(target, charge_distance, 1, src, FALSE, TRUE, callback = CALLBACK(src, .proc/charge_end))
COOLDOWN_START(src, charge_cooldown, charge_frequency)
return TRUE
/**
* Proc that handles a charge attack after it's concluded.
*/
/mob/living/simple_animal/hostile/proc/charge_end()
charge_state = FALSE
/**
* Proc that handles the charge impact of the charging mob.
*/
/mob/living/simple_animal/hostile/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!charge_state)
return ..()
if(hit_atom)
if(isliving(hit_atom))
var/mob/living/L = hit_atom
var/blocked = FALSE
if(ishuman(hit_atom))
var/mob/living/carbon/human/H = hit_atom
var/list/return_list = list()
if(H.mob_run_block(src, 0, "the [name]", ATTACK_TYPE_TACKLE, 0, src, null, return_list) & BLOCK_SUCCESS)
blocked = TRUE
if(!blocked)
blocked = return_list[BLOCK_RETURN_MITIGATION_PERCENT]
if(!blocked)
L.visible_message("<span class='danger'>[src] charges on [L]!</span>", "<span class='userdanger'>[src] charges into you!</span>")
L.Knockdown(knockdown_time)
else
Stun((knockdown_time * 2), 1, 1)
charge_end()
else if(hit_atom.density && !hit_atom.CanPass(src))
visible_message("<span class='danger'>[src] smashes into [hit_atom]!</span>")
Stun((knockdown_time * 2), 1, 1)
if(charge_state)
charge_state = FALSE
update_icons()
update_mobility()
@@ -0,0 +1,42 @@
/**
* Lobstrosities, the poster boy of charging AI mobs. Drops crab meat and bones.
* Outside of charging, it's intended behavior is that it is generally slow moving, but makes up for that with a knockdown attack to score additional hits.
*/
/mob/living/simple_animal/hostile/asteroid/lobstrosity
name = "arctic lobstrosity"
desc = "A marvel of evolution gone wrong, the frosty ice produces underground lakes where these ill tempered seafood gather. Beware its charge."
icon = 'icons/mob/icemoon/icemoon_monsters.dmi'
icon_state = "arctic_lobstrosity"
icon_living = "arctic_lobstrosity"
icon_dead = "arctic_lobstrosity_dead"
mob_biotypes = MOB_ORGANIC|MOB_BEAST
mouse_opacity = MOUSE_OPACITY_ICON
friendly_verb_continuous = "chitters at"
friendly_verb_simple = "chits at"
speak_emote = list("chitters")
speed = 3
move_to_delay = 20
maxHealth = 150
health = 150
obj_damage = 15
melee_damage_lower = 15
melee_damage_upper = 19
attack_verb_continuous = "snips"
attack_verb_simple = "snip"
attack_sound = 'sound/weapons/bite.ogg'
weather_immunities = list("snow")
vision_range = 5
aggro_vision_range = 7
charger = TRUE
charge_distance = 4
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/crab = 2, /obj/item/stack/sheet/bone = 2)
robust_searching = TRUE
footstep_type = FOOTSTEP_MOB_CLAW
gold_core_spawnable = HOSTILE_SPAWN
/mob/living/simple_animal/hostile/asteroid/lobstrosity/lava
name = "tropical lobstrosity"
desc = "A marvel of evolution gone wrong, the sulfur lakes of lavaland have given them a vibrant, red hued shell. Beware its charge."
icon_state = "lobstrosity"
icon_living = "lobstrosity"
icon_dead = "lobstrosity_dead"
@@ -6,6 +6,7 @@
gender = PLURAL //placeholder
///How much blud it has for bloodsucking
blood_volume = 550
rad_flags = RAD_NO_CONTAMINATE
status_flags = CANPUSH
+1 -2
View File
@@ -39,8 +39,7 @@
update_client_colour()
update_mouse_pointer()
if(client)
client.change_view(CONFIG_GET(string/default_view)) // Resets the client.view in case it was changed.
client.view_size?.resetToDefault()
if(client.player_details && istype(client.player_details))
if(client.player_details.player_actions.len)
for(var/datum/action/A in client.player_details.player_actions)
+1
View File
@@ -39,6 +39,7 @@
. = ..()
update_config_movespeed()
update_movespeed(TRUE)
initialize_actionspeed()
hook_vr("mob_new",list(src))
/mob/GenerateTag()
+7
View File
@@ -58,6 +58,13 @@
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
/// List of action speed modifiers applying to this mob
var/list/actionspeed_modification //Lazy list, see mob_movespeed.dm
/// List of action speed modifiers ignored by this mob. List -> List (id) -> List (sources)
var/list/actionspeed_mod_immunities //Lazy list, see mob_movespeed.dm
/// The calculated mob action speed slowdown based on the modifiers list
var/cached_multiplicative_actions_slowdown
/////////////////
var/name_archive //For admin things like possession
@@ -18,3 +18,7 @@
blacklisted_movetypes = (FLOATING|CRAWLING)
variable = TRUE
flags = IGNORE_NOSLOW
/datum/movespeed_modifier/slime_puddle
multiplicative_slowdown = 2
flags = IGNORE_NOSLOW
+1 -2
View File
@@ -160,8 +160,7 @@
/obj/machinery/ticket_machine/proc/reset_cooldown()
ready = TRUE
/obj/machinery/ticket_machine/attack_hand(mob/living/carbon/user)
. = ..()
/obj/machinery/ticket_machine/on_attack_hand(mob/living/carbon/user)
INVOKE_ASYNC(src, .proc/attempt_ticket, user)
/obj/machinery/ticket_machine/proc/attempt_ticket(mob/living/carbon/user)
@@ -30,20 +30,8 @@
. = ..()
START_PROCESSING(SSobj, src)
create_reagents(100 , AMOUNT_VISIBLE)
AddComponent(/datum/component/plumbing/simple_demand)
/obj/machinery/hydroponics/constructable/automagic/ComponentInitialize()
. = ..()
AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS, null, CALLBACK(src, .proc/can_be_rotated))
/obj/machinery/hydroponics/constructable/proc/can_be_rotated(mob/user, rotation_type)
return !anchored
/obj/machinery/hydroponics/constructable/automagic/process()
if(reagents)
reagents.add_reagent(reagents)
reagents.clear_reagents()
if(dead)
dead = 0
qdel(myseed)
+7
View File
@@ -407,3 +407,10 @@
/obj/item/stock_parts/cell/toymagburst
name = "toy mag burst rifle power supply"
maxcharge = 4000
/obj/item/stock_parts/cell/family
name = "broken power cell"
desc = "An old faulty power cell. You can see your family name faintly etched onto it."
maxcharge = 100
self_recharge = -5 //it loses power over time instead of gaining
rating = 1
+27
View File
@@ -53,6 +53,33 @@
power_gen = 1250 // 2500 on T1, 10000 on T4.
circuit = /obj/item/circuitboard/machine/rtg/advanced
/obj/machinery/power/rtg/advanced/fullupgrade //fully ugpraded stock parts
desc = "An advanced RTG capable of moderating isotope decay, increasing power output but reducing lifetime. It uses plasma-fueled radiation collectors to increase output even further. This model is fully upgraded with the latest tech available in this quadrant."
/obj/machinery/power/rtg/advanced/fullupgrade/Initialize()
. = ..()
//This looks terrifying. And apparently instancing vars and modifying the amount variable causes runtime errors. Guess we're sticking to copy pasta, thanks, byond.
component_parts = list()
component_parts += new /obj/item/circuitboard/machine/rtg/advanced(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/uranium(null)
component_parts += new /obj/item/stack/sheet/mineral/plasma(null)
component_parts += new /obj/item/stack/sheet/mineral/plasma(null)
component_parts += new /obj/item/stack/sheet/mineral/plasma(null)
component_parts += new /obj/item/stack/sheet/mineral/plasma(null)
component_parts += new /obj/item/stack/sheet/mineral/plasma(null)
component_parts += new /obj/item/stock_parts/capacitor/quadratic(null)
component_parts += new /obj/item/stock_parts/micro_laser/quadultra(null)
RefreshParts()
// Void Core, power source for Abductor ships and bases.
// Provides a lot of power, but tends to explode when mistreated.
+3 -3
View File
@@ -364,7 +364,7 @@
icon_state_on = "protoemitter_+a"
can_buckle = TRUE
buckle_lying = FALSE
var/view_range = 12
var/view_range = 4.5
var/datum/action/innate/protoemitter/firing/auto
//BUCKLE HOOKS
@@ -379,7 +379,7 @@
buckled_mob.pixel_x = 0
buckled_mob.pixel_y = 0
if(buckled_mob.client)
buckled_mob.client.change_view(CONFIG_GET(string/default_view))
buckled_mob.client.view_size.resetToDefault()
auto.Remove(buckled_mob)
. = ..()
@@ -395,7 +395,7 @@
M.pixel_y = 14
layer = 4.1
if(M.client)
M.client.change_view(view_range)
M.client.view_size.setTo(view_range)
if(!auto)
auto = new()
auto.Grant(M, src)
+14 -23
View File
@@ -134,7 +134,7 @@
/obj/item/gun/equipped(mob/living/user, slot)
. = ..()
if(zoomed && user.get_active_held_item() != src)
zoom(user, FALSE) //we can only stay zoomed in if it's in our hands //yeah and we only unzoom if we're actually zoomed using the gun!!
zoom(user, user.dir, FALSE) //we can only stay zoomed in if it's in our hands //yeah and we only unzoom if we're actually zoomed using the gun!!
//called after the gun has successfully fired its chambered ammo.
/obj/item/gun/proc/process_chamber(mob/living/user)
@@ -439,7 +439,7 @@
/obj/item/gun/ui_action_click(mob/user, action)
if(istype(action, /datum/action/item_action/toggle_scope_zoom))
zoom(user)
zoom(user, user.dir)
else if(istype(action, alight))
toggle_gunlight()
@@ -554,14 +554,19 @@
. = ..()
if(!.)
var/obj/item/gun/G = target
G.zoom(owner, FALSE)
G.zoom(owner, owner.dir)
/datum/action/item_action/toggle_scope_zoom/Remove(mob/living/L)
var/obj/item/gun/G = target
G.zoom(L, FALSE)
G.zoom(L, L.dir)
return ..()
/obj/item/gun/proc/zoom(mob/living/user, forced_zoom)
/obj/item/gun/proc/rotate(atom/thing, old_dir, new_dir)
if(ismob(thing))
var/mob/lad = thing
lad.client.view_size.zoomOut(zoom_out_amt, zoom_amt, new_dir)
/obj/item/gun/proc/zoom(mob/living/user, direct, forced_zoom)
if(!(user?.client))
return
@@ -573,25 +578,11 @@
zoomed = !zoomed
if(zoomed)
var/_x = 0
var/_y = 0
switch(user.dir)
if(NORTH)
_y = zoom_amt
if(EAST)
_x = zoom_amt
if(SOUTH)
_y = -zoom_amt
if(WEST)
_x = -zoom_amt
user.client.change_view(zoom_out_amt)
user.client.pixel_x = world.icon_size*_x
user.client.pixel_y = world.icon_size*_y
RegisterSignal(user, COMSIG_ATOM_DIR_CHANGE, .proc/rotate)
user.client.view_size.zoomOut(zoom_out_amt, zoom_amt, direct)
else
user.client.change_view(CONFIG_GET(string/default_view))
user.client.pixel_x = 0
user.client.pixel_y = 0
UnregisterSignal(user, COMSIG_ATOM_DIR_CHANGE)
user.client.view_size.zoomIn()
/obj/item/gun/handle_atom_del(atom/A)
if(A == chambered)
@@ -374,7 +374,7 @@
inaccuracy_modifier = 0.5
zoomable = TRUE
zoom_amt = 10 //Long range, enough to see in front of you, but no tiles behind you.
zoom_out_amt = 13
zoom_out_amt = 5
slot_flags = ITEM_SLOT_BACK
automatic_burst_overlay = FALSE
actions_types = list()
@@ -69,7 +69,8 @@
//ZOOMING
var/zoom_current_view_increase = 0
var/zoom_target_view_increase = 10
///The radius you want to zoom by
var/zoom_target_view_increase = 9.5
var/zooming = FALSE
var/zoom_lock = ZOOM_LOCK_OFF
var/zooming_angle
@@ -133,7 +134,7 @@
if(zoom_lock == ZOOM_LOCK_OFF)
return
zooming = TRUE
current_user.client.change_view(world.view + zoom_target_view_increase)
current_user.client.view_size.setTo(zoom_target_view_increase)
zoom_current_view_increase = zoom_target_view_increase
/obj/item/gun/energy/beam_rifle/proc/stop_zooming(mob/user)
@@ -146,9 +147,8 @@
user = current_user
if(!user || !user.client)
return FALSE
animate(user.client, pixel_x = 0, pixel_y = 0, 0, FALSE, LINEAR_EASING, ANIMATION_END_NOW)
user.client.view_size.zoomIn()
zoom_current_view_increase = 0
user.client.change_view(CONFIG_GET(string/default_view))
zooming_angle = 0
current_zoom_x = 0
current_zoom_y = 0
@@ -73,4 +73,4 @@
explosion(target, 0, 1, 1, 2)
return BULLET_ACT_HIT
//if(istype(target, /turf/closed) || ismecha(target))
new /obj/item/broken_missile(get_turf(src), 1)
new /obj/item/broken_missile(get_turf(src), 1)
@@ -2283,7 +2283,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
boozepwr = 50
/datum/reagent/consumable/ethanol/species_drink/on_mob_life(mob/living/carbon/C)
if(C.dna.species && C.dna.species.species_type == species_required) //species have a species_type variable that refers to one of the drinks
if(C.dna.species && C.dna.species.species_category == species_required) //species have a species_category variable that refers to one of the drinks
quality = RACE_DRINK
else
C.adjust_disgust(disgust)
@@ -181,6 +181,14 @@
build_path = /obj/item/storage/belt/janitor
category = list("initial","Organic Materials")
/datum/design/plantbelt
name = "Botanical Belt"
id = "plantbelt"
build_type = BIOGENERATOR
materials = list(/datum/material/biomass= 300)
build_path = /obj/item/storage/belt/plant
category = list("initial","Organic Materials")
/datum/design/s_holster
name = "Shoulder Holster"
id = "s_holster"
@@ -32,7 +32,7 @@
materials = list(/datum/material/glass = 3000, /datum/material/plasma = 3000, /datum/material/diamond = 250, /datum/material/bluespace = 250)
build_path = /obj/item/reagent_containers/glass/beaker/bluespace
category = list("Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_SERVICE
/datum/design/noreactbeaker
name = "Cryostasis Beaker"
@@ -973,6 +973,10 @@
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
/////////////////////////////////////////
//////////// Plumbing //////////
/////////////////////////////////////////
/datum/design/acclimator
name = "Plumbing Acclimator"
desc = "A heating and cooling device for pipes!"
@@ -1126,3 +1130,14 @@
build_path = /obj/item/construction/plumbing
category = list("Misc","Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
/datum/design/rplunger
name = "Reinforced Plunger"
desc = "A plunger designed for heavy duty clogs."
id = "rplunger"
build_type = PROTOLATHE
materials = list(/datum/material/plasma = 1000, /datum/material/iron = 1000, /datum/material/glass = 1000)
construction_time = 15
build_path = /obj/item/plunger/reinforced
category = list ("Misc","Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_CARGO
@@ -38,7 +38,7 @@
display_name = "Advanced Plumbing Technology"
description = "Plumbing RCD."
prereq_ids = list("plumbing", "adv_engi")
design_ids = list("plumb_rcd", "autohydrotray")
design_ids = list("plumb_rcd", "autohydrotray", "rplunger")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
//////////////////////Cybernetics/////////////////////
@@ -335,15 +335,11 @@ datum/status_effect/rebreathing/tick()
duration = 600
/datum/status_effect/timecookie/on_apply()
if(ishuman(owner))
var/mob/living/carbon/human/H
H.physiology.do_after_speed *= 0.95
owner.add_actionspeed_modifier(/datum/actionspeed_modifier/timecookie)
return ..()
/datum/status_effect/timecookie/on_remove()
if(ishuman(owner))
var/mob/living/carbon/human/H
H.physiology.do_after_speed /= 0.95
owner.remove_actionspeed_modifier(/datum/actionspeed_modifier/timecookie)
return ..()
/datum/status_effect/lovecookie
@@ -68,7 +68,7 @@
lock_override = NONE
shuttlePortId = "caravantrade1_custom"
jumpto_ports = list("whiteship_away" = 1, "whiteship_home" = 1, "whiteship_z4" = 1, "caravantrade1_ambush" = 1)
view_range = 14
view_range = 6.5
x_offset = -5
y_offset = -5
designate_time = 100
@@ -92,7 +92,7 @@
lock_override = NONE
shuttlePortId = "caravanpirate_custom"
jumpto_ports = list("caravanpirate_ambush" = 1)
view_range = 14
view_range = 6.5
x_offset = 3
y_offset = -6
@@ -116,7 +116,7 @@
lock_override = NONE
shuttlePortId = "caravansyndicate1_custom"
jumpto_ports = list("caravansyndicate1_ambush" = 1, "caravansyndicate1_listeningpost" = 1)
view_range = 7
view_range = 0
x_offset = 2
y_offset = 0
@@ -140,7 +140,7 @@
lock_override = NONE
shuttlePortId = "caravansyndicate2_custom"
jumpto_ports = list("caravansyndicate2_ambush" = 1, "caravansyndicate1_listeningpost" = 1)
view_range = 7
view_range = 0
x_offset = 0
y_offset = 2
@@ -164,6 +164,6 @@
lock_override = NONE
shuttlePortId = "caravansyndicate3_custom"
jumpto_ports = list("caravansyndicate3_ambush" = 1, "caravansyndicate3_listeningpost" = 1)
view_range = 10
view_range = 2.5
x_offset = -1
y_offset = -3
+4 -3
View File
@@ -2,6 +2,7 @@
name = "navigation computer"
desc = "Used to designate a precise transit location for a spacecraft."
jump_action = null
should_supress_view_changes = FALSE
var/datum/action/innate/shuttledocker_rotate/rotate_action = new
var/datum/action/innate/shuttledocker_place/place_action = new
var/shuttleId = ""
@@ -10,7 +11,7 @@
var/list/jumpto_ports = list() //hashset of ports to jump to and ignore for collision purposes
var/obj/docking_port/stationary/my_port //the custom docking port placed by this console
var/obj/docking_port/mobile/shuttle_port //the mobile docking port of the connected shuttle
var/view_range = 7
var/view_range = 0
var/x_offset = 0
var/y_offset = 0
var/list/whitelist_turfs = list(/turf/open/space, /turf/open/floor/plating, /turf/open/lava)
@@ -88,7 +89,7 @@
to_add += SSshuttle.hidden_shuttle_turf_images
user.client.images += to_add
user.client.change_view(view_range)
user.client.view_size.setTo(view_range)
/obj/machinery/computer/camera_advanced/shuttle_docker/remove_eye_control(mob/living/user)
..()
@@ -101,7 +102,7 @@
to_remove += SSshuttle.hidden_shuttle_turf_images
user.client.images -= to_remove
user.client.change_view(CONFIG_GET(string/default_view))
user.client.view_size.resetToDefault()
/obj/machinery/computer/camera_advanced/shuttle_docker/proc/placeLandingSpot()
if(designating_target_loc || !current_user)
+1 -1
View File
@@ -60,7 +60,7 @@
lock_override = CAMERA_LOCK_STATION
shuttlePortId = "syndicate_custom"
jumpto_ports = list("syndicate_ne" = 1, "syndicate_nw" = 1, "syndicate_n" = 1, "syndicate_se" = 1, "syndicate_sw" = 1, "syndicate_s" = 1)
view_range = 13
view_range = 5.5
x_offset = -7
y_offset = -1
space_turfs_only = FALSE
+2 -2
View File
@@ -25,7 +25,7 @@
lock_override = NONE
shuttlePortId = "whiteship_custom"
jumpto_ports = list("whiteship_away" = 1, "whiteship_home" = 1, "whiteship_z4" = 1)
view_range = 18
view_range = 10
x_offset = -6
y_offset = -10
designate_time = 100
@@ -36,7 +36,7 @@
shuttleId = "whiteship_pod"
shuttlePortId = "whiteship_pod_custom"
jumpto_ports = list("whiteship_pod_home" = 1)
view_range = 7
view_range = 0
x_offset = -2
y_offset = 0
designate_time = 0
@@ -43,7 +43,7 @@
for(var/mob/C in targets)
if(!C.client)
continue
C.client.change_view(input("Select view range:", "Range", 4) in ranges)
C.client.view_size.setTo((input("Select view range:", "Range", 4) in ranges) - 7)
/obj/effect/proc_holder/spell/targeted/summon_friend
name = "Summon Friend"
+4 -4
View File
@@ -483,12 +483,12 @@
//Checks disabled status thresholds
//Checks disabled status thresholds
/obj/item/bodypart/proc/update_disabled()
/obj/item/bodypart/proc/update_disabled(silent = FALSE)
if(!owner)
return
set_disabled(is_disabled())
set_disabled(is_disabled(silent), silent)
/obj/item/bodypart/proc/is_disabled()
/obj/item/bodypart/proc/is_disabled(silent = FALSE)
if(!owner)
return
if(HAS_TRAIT(owner, TRAIT_PARALYSIS))
@@ -500,7 +500,7 @@
if(can_dismember() && !HAS_TRAIT(owner, TRAIT_NODISMEMBER))
. = disabled //inertia, to avoid limbs healing 0.1 damage and being re-enabled
if(get_damage(TRUE) >= max_damage * (HAS_TRAIT(owner, TRAIT_EASYLIMBDISABLE) ? 0.6 : 1)) //Easy limb disable disables the limb at 40% health instead of 0%
if(!last_maxed)
if(!last_maxed && !silent)
owner.emote("scream")
last_maxed = TRUE
if(!is_organic_limb() || stamina_dam >= max_damage)
@@ -434,8 +434,8 @@
var/datum/scar/scaries = new
var/datum/wound/loss/phantom_loss = new // stolen valor, really
scaries.generate(L, phantom_loss)
if(HAS_TRAIT(src, ROBOTIC_LIMBS)) //Snowflake trait moment, but needed.
L.attach_limb(src, 1)
if(ROBOTIC_LIMBS in dna.species.species_traits) //Snowflake trait moment, but needed.
L.render_like_organic = TRUE
L.change_bodypart_status(BODYPART_ROBOTIC, FALSE, TRUE) //Haha what if IPC-lings actually regenerated the right limbs instead of organic ones? That'd be pretty cool, right?
L.attach_limb(src, 1)
return TRUE
+20 -18
View File
@@ -75,11 +75,11 @@
return BODYPART_DISABLED_PARALYSIS
return ..()
/obj/item/bodypart/l_arm/set_disabled(new_disabled)
/obj/item/bodypart/l_arm/set_disabled(new_disabled, silent = FALSE)
. = ..()
if(!.)
return
if(owner.stat < UNCONSCIOUS)
if(owner.stat < UNCONSCIOUS && !silent)
switch(disabled)
if(BODYPART_DISABLED_DAMAGE)
owner.emote("scream")
@@ -136,11 +136,11 @@
return BODYPART_DISABLED_PARALYSIS
return ..()
/obj/item/bodypart/r_arm/set_disabled(new_disabled)
/obj/item/bodypart/r_arm/set_disabled(new_disabled, silent = FALSE)
. = ..()
if(!.)
return
if(owner.stat < UNCONSCIOUS)
if(owner.stat < UNCONSCIOUS && !silent)
switch(disabled)
if(BODYPART_DISABLED_DAMAGE)
owner.emote("scream")
@@ -196,16 +196,17 @@
return BODYPART_DISABLED_PARALYSIS
return ..()
/obj/item/bodypart/l_leg/set_disabled(new_disabled)
/obj/item/bodypart/l_leg/set_disabled(new_disabled, silent = FALSE)
. = ..()
if(!. || owner.stat >= UNCONSCIOUS)
return
switch(disabled)
if(BODYPART_DISABLED_DAMAGE)
owner.emote("scream")
to_chat(owner, "<span class='userdanger'>Your [name] is too damaged to function!</span>")
if(BODYPART_DISABLED_PARALYSIS)
to_chat(owner, "<span class='userdanger'>You can't feel your [name]!</span>")
if(!silent)
switch(disabled)
if(BODYPART_DISABLED_DAMAGE)
owner.emote("scream")
to_chat(owner, "<span class='userdanger'>Your [name] is too damaged to function!</span>")
if(BODYPART_DISABLED_PARALYSIS)
to_chat(owner, "<span class='userdanger'>You can't feel your [name]!</span>")
/obj/item/bodypart/l_leg/digitigrade
name = "left digitigrade leg"
@@ -253,16 +254,17 @@
return BODYPART_DISABLED_PARALYSIS
return ..()
/obj/item/bodypart/r_leg/set_disabled(new_disabled)
/obj/item/bodypart/r_leg/set_disabled(new_disabled, silent = FALSE)
. = ..()
if(!. || owner.stat >= UNCONSCIOUS)
return
switch(disabled)
if(BODYPART_DISABLED_DAMAGE)
owner.emote("scream")
to_chat(owner, "<span class='userdanger'>Your [name] is too damaged to function!</span>")
if(BODYPART_DISABLED_PARALYSIS)
to_chat(owner, "<span class='userdanger'>You can't feel your [name]!</span>")
if(!silent)
switch(disabled)
if(BODYPART_DISABLED_DAMAGE)
owner.emote("scream")
to_chat(owner, "<span class='userdanger'>Your [name] is too damaged to function!</span>")
if(BODYPART_DISABLED_PARALYSIS)
to_chat(owner, "<span class='userdanger'>You can't feel your [name]!</span>")
/obj/item/bodypart/r_leg/digitigrade
name = "right digitigrade leg"
+18 -13
View File
@@ -120,12 +120,17 @@
window.location = 'byond://winset?id='+tooltip.control+';anchor1=0,0;size=999x999';
//Get the real icon size according to the client view
//FYI, this bit is even more borrowed from goon, our widescreen broke tooltips so I took a look at how they do it
//To improve our code. Thanks gooncoders, very cool
var mapWidth = map['view-size'].x,
mapHeight = map['view-size'].y,
tilesShown = tooltip.client_view_h
realIconSize = mapHeight / tilesShown,
resizeRatio = realIconSize / tooltip.tileSize,
//Calculate letterboxing offsets
tilesShownX = tooltip.client_view_w
tilesShownY = tooltip.client_view_h
realIconSizeX = mapWidth / tilesShownX,
realIconSizeY = mapHeight / tilesShownY,
resizeRatioX = realIconSizeX / tooltip.tileSize,
resizeRatioY = realIconSizeY / tooltip.tileSize,
//Calculate letterboxing offsets
leftOffset = (map.size.x - mapWidth) / 2,
topOffset = (map.size.y - mapHeight) / 2;
@@ -168,7 +173,7 @@
if ((iconX + westOffset) !== enteredX) { //Cursor entered on the offset tile
left = left + (westOffset < 0 ? 1 : -1);
}
leftOffset = leftOffset + (westOffset * resizeRatio);
leftOffset = leftOffset + (westOffset * resizeRatioX);
}
}
@@ -179,13 +184,13 @@
if (northOffset !== 0) {
if ((iconY + northOffset) === enteredY) { //Cursor entered on the original tile
top--;
topOffset = topOffset - ((tooltip.tileSize + northOffset) * resizeRatio);
topOffset = topOffset - ((tooltip.tileSize + northOffset) * resizeRatioY);
} else { //Cursor entered on the offset tile
if (northOffset < 0) { //Offset southwards
topOffset = topOffset - ((tooltip.tileSize + northOffset) * resizeRatio);
topOffset = topOffset - ((tooltip.tileSize + northOffset) * resizeRatioY);
} else { //Offset northwards
top--;
topOffset = topOffset - (northOffset * resizeRatio);
topOffset = topOffset - (northOffset * resizeRatioY);
}
}
}
@@ -198,12 +203,12 @@
}
//Clamp values
left = (left < 0 ? 0 : (left > tilesShown ? tilesShown : left));
top = (top < 0 ? 0 : (top > tilesShown ? tilesShown : top));
left = (left < 0 ? 0 : (left > tilesShownX ? tilesShownX : left));
top = (top < 0 ? 0 : (top > tilesShownY ? tilesShownY : top));
//Calculate where on the screen the popup should appear (below the hovered tile)
var posX = Math.round(((left - 1) * realIconSize) + leftOffset + tooltip.padding); //-1 to position at the left of the target tile
var posY = Math.round(((tilesShown - top + 1) * realIconSize) + topOffset + tooltip.padding); //+1 to position at the bottom of the target tile
var posX = Math.round(((left - 1) * realIconSizeX) + leftOffset + tooltip.padding); //-1 to position at the left of the target tile
var posY = Math.round(((tilesShownY - top + 1) * realIconSizeY) + topOffset + tooltip.padding); //+1 to position at the bottom of the target tile
//alert(mapWidth+' | '+mapHeight+' | '+tilesShown+' | '+realIconSize+' | '+leftOffset+' | '+topOffset+' | '+left+' | '+top+' | '+posX+' | '+posY); //DEBUG
@@ -221,7 +226,7 @@
docHeight = $wrap.outerHeight();
if (posY + docHeight > map.size.y) { //Is the bottom edge below the window? Snap it up if so
posY = (posY - docHeight) - realIconSize - tooltip.padding;
posY = (posY - docHeight) - realIconSizeY - tooltip.padding;
}
//Actually size, move and show the tooltip box
@@ -92,8 +92,8 @@
cost = 6
exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
/datum/uplink_item/device_tools/guerillagloves
name = "Guerilla Gloves"
/datum/uplink_item/device_tools/guerrillagloves
name = "Guerrilla Gloves"
desc = "A pair of highly robust combat gripper gloves that excels at performing takedowns at close range, with an added lining of insulation. Careful not to hit a wall!"
item = /obj/item/clothing/gloves/tackler/combat/insulated
include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
@@ -31,7 +31,7 @@
/datum/uplink_item/implants/warp
name = "Warp Implant"
desc = "An implant injected into the body and later activated at the user's will. It will inject eigenstasium which saves the user's location and teleports them there after five seconds. Lasts only fifteen times."
desc = "An implant injected into the body and later activated at the user's will. Allows the user to teleport to where they were 10 seconds ago. Has a 10 second cooldown."
item = /obj/item/storage/box/syndie_kit/imp_warp
cost = 6
exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)