Revert "Revert "Merge branch 'master' into robotic-limbs-PT2""
This reverts commit 27a099c6bf.
This commit is contained in:
@@ -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
|
||||
@@ -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!
|
||||
|
||||
@@ -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)]")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -38,8 +38,9 @@
|
||||
var/mimicing = ""
|
||||
var/canrespec = 0
|
||||
var/changeling_speak = 0
|
||||
var/loudfactor = 0 //Used for blood tests. At 4, blood tests will succeed. At 10, blood tests will result in an explosion.
|
||||
var/bloodtestwarnings = 0 //Used to track if the ling has been notified that they will pass blood tests.
|
||||
var/loudfactor = 0 //Used for blood tests. This is is the average loudness of the ling's abilities calculated with the below two vars
|
||||
var/loudtotal = 0 //Used to keep track of the sum of the ling's loudness
|
||||
var/totalpurchases = 0 //Used to keep track of how many purchases the ling's made after free abilities have been added
|
||||
var/datum/dna/chosen_dna
|
||||
var/obj/effect/proc_holder/changeling/sting/chosen_sting
|
||||
var/datum/cellular_emporium/cellular_emporium
|
||||
@@ -139,8 +140,6 @@
|
||||
/datum/antagonist/changeling/proc/reset_powers()
|
||||
if(purchasedpowers)
|
||||
remove_changeling_powers()
|
||||
loudfactor = 0
|
||||
bloodtestwarnings = 0
|
||||
//Repurchase free powers.
|
||||
for(var/path in all_powers)
|
||||
var/obj/effect/proc_holder/changeling/S = new path()
|
||||
@@ -148,6 +147,9 @@
|
||||
if(!has_sting(S))
|
||||
purchasedpowers += S
|
||||
S.on_purchase(owner.current,TRUE)
|
||||
loudfactor = 0
|
||||
loudtotal = 0
|
||||
totalpurchases = 0
|
||||
|
||||
/datum/antagonist/changeling/proc/has_sting(obj/effect/proc_holder/changeling/power)
|
||||
for(var/obj/effect/proc_holder/changeling/P in purchasedpowers)
|
||||
@@ -192,13 +194,18 @@
|
||||
geneticpoints -= thepower.dna_cost
|
||||
purchasedpowers += thepower
|
||||
thepower.on_purchase(owner.current)
|
||||
loudfactor += thepower.loudness
|
||||
if(loudfactor >= 4 && !bloodtestwarnings)
|
||||
to_chat(owner.current, "<span class='warning'>Our blood is growing flammable. Our blood will react violently to heat.</span>")
|
||||
bloodtestwarnings = 1
|
||||
if(loudfactor >= 10 && bloodtestwarnings < 2)
|
||||
to_chat(owner.current, "<span class='warning'>Our blood has grown extremely flammable. Our blood will react explosively to heat.</span>")
|
||||
bloodtestwarnings = 2
|
||||
loudtotal += thepower.loudness
|
||||
totalpurchases++
|
||||
var/oldloudness = loudfactor
|
||||
loudfactor = loudtotal/max(totalpurchases,1)
|
||||
if(loudfactor >= LINGBLOOD_DETECTION_THRESHOLD && oldloudness < LINGBLOOD_DETECTION_THRESHOLD)
|
||||
to_chat(owner.current, "<span class='warning'>Our blood has grown flammable. Our blood will now react violently to heat.</span>")
|
||||
else if(loudfactor < LINGBLOOD_DETECTION_THRESHOLD && oldloudness >= LINGBLOOD_DETECTION_THRESHOLD)
|
||||
to_chat(owner.current, "<span class='notice'>Our blood has stabilized, and will no longer react violently to heat.</span>")
|
||||
if(loudfactor > LINGBLOOD_EXPLOSION_THRESHOLD && oldloudness <= LINGBLOOD_EXPLOSION_THRESHOLD)
|
||||
to_chat(owner.current, "<span class='warning'>Our blood has grown extremely flammable. Our blood will now react explosively to heat.</span>")
|
||||
else if(loudfactor <= LINGBLOOD_EXPLOSION_THRESHOLD && oldloudness > LINGBLOOD_EXPLOSION_THRESHOLD)
|
||||
to_chat(owner.current, "<span class='notice'>Our blood has slightly stabilized, and will no longer explode when exposed to heat.</span>")
|
||||
|
||||
/datum/antagonist/changeling/proc/readapt()
|
||||
if(!ishuman(owner.current))
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -98,6 +98,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/use_custom_skin_tone = FALSE
|
||||
var/left_eye_color = "000000" //Eye color
|
||||
var/right_eye_color = "000000"
|
||||
var/eye_type = DEFAULT_EYES_TYPE //Eye type
|
||||
var/split_eye_colors = FALSE
|
||||
var/datum/species/pref_species = new /datum/species/human() //Mutant race
|
||||
var/list/features = list("mcolor" = "FFFFFF",
|
||||
@@ -200,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
|
||||
|
||||
@@ -240,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
|
||||
|
||||
@@ -287,7 +295,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
/datum/preferences/proc/ShowChoices(mob/user)
|
||||
if(!user || !user.client)
|
||||
return
|
||||
update_preview_icon(current_tab != 2)
|
||||
update_preview_icon(current_tab)
|
||||
var/list/dat = list("<center>")
|
||||
|
||||
dat += "<a href='?_src_=prefs;preference=tab;tab=0' [current_tab == 0 ? "class='linkOn'" : ""]>Character Settings</a>"
|
||||
@@ -474,25 +482,28 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
if (CONFIG_GET(number/body_size_min) != CONFIG_GET(number/body_size_max))
|
||||
dat += "<b>Sprite Size:</b> <a href='?_src_=prefs;preference=body_size;task=input'>[features["body_size"]*100]%</a><br>"
|
||||
|
||||
if((EYECOLOR in pref_species.species_traits) && !(NOEYES in pref_species.species_traits))
|
||||
if(!use_skintones && !mutant_colors)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
if(left_eye_color != right_eye_color)
|
||||
split_eye_colors = TRUE
|
||||
dat += "<h3>Heterochromia</h3>"
|
||||
dat += "</b><a style='display:block;width:100px' href='?_src_=prefs;preference=toggle_split_eyes;task=input'>[split_eye_colors ? "Enabled" : "Disabled"]</a>"
|
||||
if(!split_eye_colors)
|
||||
dat += "<h3>Eye Color</h3>"
|
||||
dat += "<span style='border: 1px solid #161616; background-color: #[left_eye_color];'> </span> <a href='?_src_=prefs;preference=eyes;task=input'>Change</a>"
|
||||
if(!(NOEYES in pref_species.species_traits))
|
||||
dat += "<h3>Eye Type</h3>"
|
||||
dat += "</b><a style='display:block;width:100px' href='?_src_=prefs;preference=eye_type;task=input'>[eye_type]</a><BR>"
|
||||
if((EYECOLOR in pref_species.species_traits))
|
||||
if(!use_skintones && !mutant_colors)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
if(left_eye_color != right_eye_color)
|
||||
split_eye_colors = TRUE
|
||||
dat += "<h3>Heterochromia</h3>"
|
||||
dat += "</b><a style='display:block;width:100px' href='?_src_=prefs;preference=toggle_split_eyes;task=input'>[split_eye_colors ? "Enabled" : "Disabled"]</a>"
|
||||
if(!split_eye_colors)
|
||||
dat += "<h3>Eye Color</h3>"
|
||||
dat += "<span style='border: 1px solid #161616; background-color: #[left_eye_color];'> </span> <a href='?_src_=prefs;preference=eyes;task=input'>Change</a>"
|
||||
dat += "</td>"
|
||||
else
|
||||
dat += "<h3>Left Eye Color</h3>"
|
||||
dat += "<span style='border: 1px solid #161616; background-color: #[left_eye_color];'> </span> <a href='?_src_=prefs;preference=eye_left;task=input'>Change</a>"
|
||||
dat += "<h3>Right Eye Color</h3>"
|
||||
dat += "<span style='border: 1px solid #161616; background-color: #[right_eye_color];'> </span> <a href='?_src_=prefs;preference=eye_right;task=input'>Change</a><BR>"
|
||||
dat += "</td>"
|
||||
else if(use_skintones || mutant_colors)
|
||||
dat += "</td>"
|
||||
else
|
||||
dat += "<h3>Left Eye Color</h3>"
|
||||
dat += "<span style='border: 1px solid #161616; background-color: #[left_eye_color];'> </span> <a href='?_src_=prefs;preference=eye_left;task=input'>Change</a>"
|
||||
dat += "<h3>Right Eye Color</h3>"
|
||||
dat += "<span style='border: 1px solid #161616; background-color: #[right_eye_color];'> </span> <a href='?_src_=prefs;preference=eye_right;task=input'>Change</a><BR>"
|
||||
dat += "</td>"
|
||||
else if(use_skintones || mutant_colors)
|
||||
dat += "</td>"
|
||||
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
dat += "<h2>Speech preferences</h2>"
|
||||
@@ -750,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];'> </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)
|
||||
@@ -1584,6 +1609,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
if(new_eyes)
|
||||
right_eye_color = sanitize_hexcolor(new_eyes, 6)
|
||||
|
||||
if("eye_type")
|
||||
var/new_eye_type = input(user, "Choose your character's eye type.", "Character Preference") as null|anything in GLOB.eye_types
|
||||
if(new_eye_type)
|
||||
eye_type = new_eye_type
|
||||
|
||||
if("toggle_split_eyes")
|
||||
split_eye_colors = !split_eye_colors
|
||||
right_eye_color = left_eye_color
|
||||
@@ -1617,6 +1647,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
if(features["mcolor3"] == "#000000" || (!(MUTCOLORS_PARTSONLY in pref_species.species_traits) && ReadHSV(temp_hsv)[3] < ReadHSV("#202020")[3]))
|
||||
features["mcolor3"] = pref_species.default_color
|
||||
|
||||
//switch to the type of eyes the species uses
|
||||
eye_type = pref_species.eye_type
|
||||
|
||||
if("custom_species")
|
||||
var/new_species = reject_bad_name(input(user, "Choose your species subtype, if unique. This will show up on examinations and health scans. Do not abuse this:", "Character Preference", custom_species) as null|text)
|
||||
if(new_species)
|
||||
@@ -2227,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")
|
||||
@@ -2601,6 +2659,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
|
||||
character.dna.features = features.Copy()
|
||||
character.set_species(chosen_species, icon_update = FALSE, pref_load = TRUE)
|
||||
character.dna.species.eye_type = eye_type
|
||||
if(chosen_limb_id && (chosen_limb_id in character.dna.species.allowed_limb_ids))
|
||||
character.dna.species.mutant_bodyparts["limbs_id"] = chosen_limb_id
|
||||
character.dna.real_name = character.real_name
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// You do not need to raise this if you are adding new values that have sane defaults.
|
||||
// Only raise this value when changing the meaning/format/name/layout of an existing value
|
||||
// where you would want the updater procs below to run
|
||||
#define SAVEFILE_VERSION_MAX 36
|
||||
#define SAVEFILE_VERSION_MAX 37
|
||||
|
||||
/*
|
||||
SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn
|
||||
@@ -204,10 +204,19 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
if(S["species"] == "lizard")
|
||||
features["mam_snouts"] = features["snout"]
|
||||
|
||||
if(current_version < 36)
|
||||
if(current_version < 36) //introduction of heterochromia
|
||||
left_eye_color = S["eye_color"]
|
||||
right_eye_color = S["eye_color"]
|
||||
|
||||
if(current_version < 37) //introduction of chooseable eye types/sprites
|
||||
if(S["species"] == "insect")
|
||||
left_eye_color = "#000000"
|
||||
right_eye_color = "#000000"
|
||||
if(chosen_limb_id == "moth" || chosen_limb_id == "moth_not_greyscale") //these actually have slightly different eyes!
|
||||
eye_type = "moth"
|
||||
else
|
||||
eye_type = "insect"
|
||||
|
||||
/datum/preferences/proc/load_path(ckey,filename="preferences.sav")
|
||||
if(!ckey)
|
||||
return
|
||||
@@ -271,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
|
||||
@@ -288,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
|
||||
@@ -324,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))
|
||||
@@ -337,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))
|
||||
@@ -509,6 +522,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
S["age"] >> age
|
||||
S["hair_color"] >> hair_color
|
||||
S["facial_hair_color"] >> facial_hair_color
|
||||
S["eye_type"] >> eye_type
|
||||
S["left_eye_color"] >> left_eye_color
|
||||
S["right_eye_color"] >> right_eye_color
|
||||
S["use_custom_skin_tone"] >> use_custom_skin_tone
|
||||
@@ -692,6 +706,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age))
|
||||
hair_color = sanitize_hexcolor(hair_color, 6, FALSE)
|
||||
facial_hair_color = sanitize_hexcolor(facial_hair_color, 6, FALSE)
|
||||
eye_type = sanitize_inlist(eye_type, GLOB.eye_types, DEFAULT_EYES_TYPE)
|
||||
left_eye_color = sanitize_hexcolor(left_eye_color, 6, FALSE)
|
||||
right_eye_color = sanitize_hexcolor(right_eye_color, 6, FALSE)
|
||||
|
||||
@@ -822,6 +837,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
WRITE_FILE(S["age"] , age)
|
||||
WRITE_FILE(S["hair_color"] , hair_color)
|
||||
WRITE_FILE(S["facial_hair_color"] , facial_hair_color)
|
||||
WRITE_FILE(S["eye_type"] , eye_type)
|
||||
WRITE_FILE(S["left_eye_color"] , left_eye_color)
|
||||
WRITE_FILE(S["right_eye_color"] , right_eye_color)
|
||||
WRITE_FILE(S["use_custom_skin_tone"] , use_custom_skin_tone)
|
||||
|
||||
@@ -190,7 +190,7 @@
|
||||
/obj/item/clothing/head/wig
|
||||
name = "wig"
|
||||
desc = "A bunch of hair without a head attached."
|
||||
icon = 'icons/mob/human_face.dmi' // default icon for all hairs
|
||||
icon = 'icons/mob/hair.dmi' // default icon for all hairs
|
||||
icon_state = "hair_vlong"
|
||||
flags_inv = HIDEHAIR
|
||||
color = "#000"
|
||||
|
||||
@@ -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].")
|
||||
@@ -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!
|
||||
|
||||
@@ -130,9 +130,13 @@
|
||||
/obj/item/reagent_containers/pill = 2,
|
||||
/obj/item/clothing/shoes = 8,
|
||||
/obj/item/clothing/head = 3,
|
||||
/obj/item/reagent_containers/food/snacks = 3,
|
||||
/obj/item/reagent_containers/food/snacks = 5,
|
||||
/obj/item/reagent_containers/syringe/dart = 2,
|
||||
/obj/item/reagent_containers/food/drinks/soda_cans = 5)
|
||||
/obj/item/reagent_containers/food/drinks/soda_cans = 5,
|
||||
/obj/item/reagent_containers/food/drinks/drinkingglass = 4,
|
||||
/obj/item/reagent_containers/food/drinks = 6,
|
||||
/obj/item/reagent_containers/food/snacks/grown/apple = 1,
|
||||
/obj/item/reagent_containers/food/snacks/grown/banana = 2)
|
||||
if(length >= 5)
|
||||
return TRUE
|
||||
//var/metalist = pickweight(GLOB.maintenance_loot)
|
||||
|
||||
@@ -48,8 +48,8 @@
|
||||
var/progression = list() //Keep track of where people are in the story.
|
||||
var/active = TRUE //Turn this to false to keep normal mob behavour
|
||||
var/cached_z
|
||||
/// I'm busy chatting, don't move.
|
||||
var/busy_chatting = FALSE
|
||||
/// I'm busy, don't move.
|
||||
var/busy = FALSE
|
||||
|
||||
/mob/living/simple_animal/jacq/Initialize()
|
||||
..()
|
||||
@@ -68,11 +68,14 @@
|
||||
playsound(loc, 'sound/spookoween/ahaha.ogg', 100, 0.25)
|
||||
var/mob/living/simple_animal/jacq/Jacq = new src.type(loc)
|
||||
Jacq.progression = progression
|
||||
if(ckey) //transfer over any ghost posessions
|
||||
Jacq.key = key
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/jacq/death() //What is alive may never die
|
||||
visible_message("<b>[src]</b> cackles, <span class='spooky'>\"You'll nae get rid a me that easily!\"</span>")
|
||||
playsound(loc, 'sound/spookoween/ahaha.ogg', 100, 0.25)
|
||||
fully_heal(FALSE)
|
||||
health = 25
|
||||
poof()
|
||||
|
||||
@@ -81,9 +84,9 @@
|
||||
say("Hello there [gender_check(M)]!")
|
||||
return ..()
|
||||
if(!ckey)
|
||||
busy_chatting = FALSE
|
||||
stopmove()
|
||||
chit_chat(M)
|
||||
busy_chatting = TRUE
|
||||
canmove()
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/jacq/attack_paw(mob/living/carbon/monkey/M)
|
||||
@@ -91,11 +94,28 @@
|
||||
say("Hello there [gender_check(M)]!")
|
||||
return ..()
|
||||
if(!ckey)
|
||||
busy_chatting = FALSE
|
||||
stopmove()
|
||||
chit_chat(M)
|
||||
busy_chatting = TRUE
|
||||
canmove()
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/jacq/proc/canmove()
|
||||
busy = FALSE
|
||||
update_mobility()
|
||||
|
||||
/mob/living/simple_animal/jacq/proc/stopmove()
|
||||
if(ckey) //if someone is in her, don't disable her movement!
|
||||
canmove()
|
||||
return
|
||||
busy = TRUE
|
||||
update_mobility()
|
||||
|
||||
/mob/living/simple_animal/jacq/proc/jacqrunes(message, mob/living/carbon/C) //Displays speechtext over Jacq for the user only.
|
||||
var/atom/hearer = C
|
||||
var/list/spans = list("spooky")
|
||||
new /datum/chatmessage(message, src, hearer, spans)
|
||||
|
||||
|
||||
/mob/living/simple_animal/jacq/proc/poof()
|
||||
last_poof = world.realtime
|
||||
var/datum/reagents/R = new/datum/reagents(100)//Hey, just in case.
|
||||
@@ -104,7 +124,7 @@
|
||||
s.set_up(R, 0, loc)
|
||||
s.start()
|
||||
visible_message("<b>[src]</b> disappears in a puff of smoke!")
|
||||
busy_chatting = TRUE
|
||||
canmove()
|
||||
health = 25
|
||||
|
||||
//Try to go to populated areas
|
||||
@@ -120,7 +140,7 @@
|
||||
continue
|
||||
targets += H
|
||||
|
||||
if(!targets)
|
||||
if(!targets.len)
|
||||
targets = GLOB.generic_event_spawns
|
||||
|
||||
for(var/i in 1 to 6) //Attempts a jump up to 6 times.
|
||||
@@ -151,14 +171,16 @@
|
||||
|
||||
if(!progression["[C.real_name]"] || !(progression["[C.real_name]"] & JACQ_HELLO))
|
||||
visible_message("<b>[src]</b> smiles ominously at [C], <span class='spooky'>\"Well halo there [gender]! Ah'm Jacqueline, tae great Pumpqueen, great tae meet ye.\"</span>")
|
||||
jacqrunes("Well halo there [gender]! Ah'm Jacqueline, tae great Pumpqueen, great tae meet ye.", C)
|
||||
sleep(20)
|
||||
visible_message("<b>[src]</b> continues, <span class='spooky'>\"Ah'm sure yae well stunned, but ah've got nae taem fer that. Ah'm after the candies around this station. If yae get mae enoof o the wee buggers, Ah'll give ye a treat, or if yae feeling bold, Ah ken trick ye instead.</span>\" giving [C] a wide grin.")
|
||||
jacqrunes("Ah'm sure yae well stunned, but ah've got nae taem fer that. Ah'm after the candies around this station. If yae get mae enoof o the wee buggers, Ah'll give ye a treat, or if yae feeling bold, Ah ken trick ye instead.", C)
|
||||
if(!progression["[C.real_name]"])
|
||||
progression["[C.real_name]"] = NONE //TO MAKE SURE THAT THE LIST ENTRY EXISTS.
|
||||
|
||||
progression["[C.real_name]"] = progression["[C.real_name]"] | JACQ_HELLO
|
||||
|
||||
var/choices = list("Trick", "Treat", "How do I get candies?")
|
||||
var/choices = list("Trick", "Treat", "How do I get candies?", "Do I know you from somewhere?")
|
||||
var/choice = input(C, "Trick or Treat?", "Trick or Treat?") in choices
|
||||
switch(choice)
|
||||
if("Trick")
|
||||
@@ -168,14 +190,20 @@
|
||||
if(check_candies(C))
|
||||
treat(C, gender)
|
||||
else
|
||||
visible_message("<b>[src]</b> raises an eyebrow, <span class='spooky'>\"You've nae got any candies Ah want! They're the orange round ones, now bugger off an go get em first.\"</span>")
|
||||
visible_message("<b>[src]</b> raises aneyebrow, <span class='spooky'>\"You've nae got any candies Ah want! They're the orange round ones, now bugger off an go get em first.\"</span>")
|
||||
jacqrunes("You've nae got any candies Ah want! They're the orange round ones, now bugger off an go get em first.", C)
|
||||
return
|
||||
if("How do I get candies?")
|
||||
visible_message("<b>[src]</b> says, <span class='spooky'>\"Gae find my familiar; Bartholomew. Ee's tendin the cauldron which ken bring oot t' magic energy in items scattered aroond. Knowing him, ee's probably gone tae somewhere with books.\"</span>")
|
||||
jacqrunes("Gae find my familiar; Bartholomew. Ee's tendin the cauldron which ken bring oot t' magic energy in items scattered aroond. Knowing him, ee's probably gone tae somewhere with books.", C)
|
||||
return
|
||||
if("Do I know you from somewhere?")
|
||||
visible_message("<b>[src]</b> says, <span class='spooky'>\"Aye ye micht dae, ah was kicking aboot round 'ere aboot a year ago when ah had a wee... altercation wit the witch academy n' ran oot here tae crash oan me sis's floor. Course she pushed me tae git it a' sorted oot lik', bit nae before ah hud a wee bit o' fun oan this station. Or maybe ye jest recognise me ma's prized pumpkin atop me nonce.\"</span>")
|
||||
jacqrunes("Aye ye micht dae, ah was kicking aboot round 'ere aboot a year ago when ah had a wee... altercation wit the witch academy n' ran oot here tae crash oan me sis's floor. Course she pushed me tae git it a' sorted oot lik', bit nae before ah hud a wee bit o' fun oan this station. Or maybe ye jest recognise me ma's prized pumpkin atop me nonce.", C)
|
||||
|
||||
/mob/living/simple_animal/jacq/proc/treat(mob/living/carbon/C, gender)
|
||||
visible_message("<b>[src]</b> gives off a glowing smile, <span class='spooky'>\"What ken Ah offer ye? I can magic up an object, a potion or a plushie fer ye.\"</span>")
|
||||
jacqrunes("What ken Ah offer ye? I can magic up an object, a potion or a plushie fer ye.", C)
|
||||
var/choices_reward = list("Object - 3 candies", "Potion - 2 candies", "Jacqueline Tracker - 2 candies", "Plushie - 1 candy", "Can I get to know you instead?", "Become a pumpkinhead dullahan (perma) - 4 candies")
|
||||
var/choice_reward = input(usr, "Trick or Treat?", "Trick or Treat?") in choices_reward
|
||||
|
||||
@@ -184,8 +212,10 @@
|
||||
if("Become a pumpkinhead dullahan (perma) - 4 candies")
|
||||
if(!take_candies(C, 4))
|
||||
visible_message("<b>[src]</b> raises an eyebrown, <span class='spooky'>\"It's 4 candies for that [gender]! Thems the rules!\"</span>")
|
||||
jacqrunes("It's 4 candies for that [gender]! Thems the rules!", C)
|
||||
return
|
||||
visible_message("<b>[src]</b> waves their arms around, <span class='spooky'>\"Off comes your head, a pumpkin taking it's stead!\"</span>")
|
||||
jacqrunes("Off comes your head, a pumpkin taking it's stead!", C)
|
||||
C.reagents.add_reagent(/datum/reagent/mutationtoxin/pumpkinhead, 5)
|
||||
sleep(20)
|
||||
poof()
|
||||
@@ -194,6 +224,7 @@
|
||||
if("Object - 3 candies")
|
||||
if(!take_candies(C, 3))
|
||||
visible_message("<b>[src]</b> raises an eyebrown, <span class='spooky'>\"It's 3 candies per trinket [gender]! Thems the rules!\"</span>")
|
||||
jacqrunes("It's 3 candies per trinket [gender]! Thems the rules!", C)
|
||||
return
|
||||
|
||||
var/new_obj = pick(subtypesof(/obj))
|
||||
@@ -203,36 +234,43 @@
|
||||
var/reward = new new_obj(C.loc)
|
||||
C.put_in_hands(reward)
|
||||
visible_message("<b>[src]</b> waves her hands, magicking up a [reward] from thin air, <span class='spooky'>\"There ye are [gender], enjoy! \"</span>")
|
||||
jacqrunes("There ye are [gender], enjoy!", C)
|
||||
sleep(20)
|
||||
poof()
|
||||
return
|
||||
if("Potion - 2 candies")
|
||||
if(!take_candies(C, 2))
|
||||
visible_message("<b>[src]</b> raises an eyebrow, <span class='spooky'>\"It's 2 candies per potion [gender]! Thems the rules!\"</span>")
|
||||
jacqrunes("It's 2 candies per potion [gender]! Thems the rules!", C)
|
||||
return
|
||||
|
||||
var/reward = new /obj/item/reagent_containers/potion_container(C.loc)
|
||||
C.put_in_hands(reward)
|
||||
visible_message("<b>[src]</b> waves her hands, magicking up a [reward] from thin air, <span class='spooky'>\"There ye are [gender], enjoy! \"</span>")
|
||||
jacqrunes("There ye are [gender], enjoy!", C)
|
||||
sleep(20)
|
||||
poof()
|
||||
return
|
||||
if("Plushie - 1 candy")
|
||||
if(!take_candies(C, 1))
|
||||
visible_message("<b>[src]</b> raises an eyebrow, <span class='spooky'>\"It's 1 candy per plushie [gender]! Thems the rules!\"</span>")
|
||||
jacqrunes("It's 1 candy per plushie [gender]! Thems the rules!", C)
|
||||
return
|
||||
|
||||
new /obj/item/toy/plush/random(C.loc)
|
||||
visible_message("<b>[src]</b> waves her hands, magicking up a plushie from thin air, <span class='spooky'>\"There ye are [gender], enjoy! \"</span>")
|
||||
jacqrunes("There ye are [gender], enjoy!", C)
|
||||
sleep(20)
|
||||
poof()
|
||||
return
|
||||
if("Jacqueline Tracker - 2 candies")
|
||||
if(!take_candies(C, 2))
|
||||
visible_message("<b>[src]</b> raises an eyebrow, <span class='spooky'>\"It's 1 candy per plushie [gender]! Thems the rules!\"</span>")
|
||||
jacqrunes("It's 1 candy per plushie [gender]! Thems the rules!", C)
|
||||
return
|
||||
new /obj/item/pinpointer/jacq(C.loc)
|
||||
visible_message("<b>[src]</b> waves her hands, magicking up a tracker from thin air, <span class='spooky'>\"Feels weird to magic up a tracker fer meself but, here ye are [gender], enjoy! \"</span>")
|
||||
jacqrunes("Feels weird to magic up a tracker fer meself but, here ye are [gender], enjoy!", C)
|
||||
sleep(20)
|
||||
poof()
|
||||
return
|
||||
@@ -252,7 +290,7 @@
|
||||
choices += "What is that on your head?"
|
||||
if(!(progression["[C.real_name]"] & JACQ_EXPELL))
|
||||
if(progression["[C.real_name]"] & JACQ_WITCH)
|
||||
choices += "So you got ex-spell-ed?"
|
||||
choices += "What is it like being a witch?"
|
||||
else
|
||||
choices += "Are you a witch?"
|
||||
|
||||
@@ -270,47 +308,79 @@
|
||||
//If you've nothing to ask
|
||||
if(!LAZYLEN(choices))
|
||||
visible_message("<b>[src]</b> sighs, <span class='spooky'>\"Ah'm all questioned oot fer noo, [gender].\"</span>")
|
||||
jacqrunes("Ah'm all questioned oot fer noo, [gender]", C)
|
||||
return
|
||||
//Otherwise, lets go!
|
||||
visible_message("<b>[src]</b> says, <span class='spooky'>\"A question? Sure, it'll cost you a candy though!\"</span>")
|
||||
jacqrunes("A question? Sure, it'll cost you a candy though!", C)
|
||||
choices += "Nevermind"
|
||||
//Candies for chitchats
|
||||
var/choice = input(C, "What do you want to ask?", "What do you want to ask?") in choices
|
||||
if(!take_candies(C, 1))
|
||||
visible_message("<b>[src]</b> raises an eyebrow, <span class='spooky'>\"It's a candy per question [gender]! Thems the rules!\"</span>")
|
||||
jacqrunes("It's a candy per question [gender]! Thems the rules!", C)
|
||||
return
|
||||
//Talking
|
||||
switch(choice)
|
||||
if("Why do you want the candies?")
|
||||
visible_message("<b>[src]</b> says, <span class='spooky'>\"Ave ye tried them? They're full of all sorts of reagents. Ah'm after them so ah ken magic em up an hopefully find rare stuff fer me brews. Honestly it's a lot easier magicking up tatt fer ye lot than runnin aroond on me own like. I'd ask me familiars but most a my familiars are funny fellows 'n constantly bugger off on adventures when given simple objectives like; Go grab me a tea cake or watch over me cauldron. Ah mean, ye might run into Bartholomew my cat. Ee's supposed tae be tending my cauldron, but I've nae idea where ee's got tae.\"</span>")
|
||||
jacqrunes("Ave ye tried them? They're full of all sorts of reagents. Ah'm after them so ah ken magic em up an hopefully find rare stuff fer me brews. Honestly it's a lot easier magicking up tatt fer ye lot than runnin aroond on me own like. I'd ask me familiars but most a my familiars are funny fellows 'n constantly bugger off on adventures when given simple objectives like; Go grab me a tea cake or watch over me cauldron. Ah mean, ye might run into Bartholomew my cat. Ee's supposed tae be tending my cauldron, but I've nae idea where ee's got tae.", C)
|
||||
progression["[C.real_name]"] = progression["[C.real_name]"] | JACQ_CANDIES
|
||||
sleep(30)
|
||||
|
||||
if("You really came all this way for candy?")
|
||||
visible_message("<b>[src]</b> l ooks tae the side sheepishly, <span class='spooky'>\"Aye, well, tae be honest, Ah'm here tae see me sis, but dunnae let her knew that. She's an alchemist too like, but she dunnae use a caldron like mae, she buggered off like tae her posh ivory tower tae learn bloody chemistry instead!\"</span> <b>[src]</b> scowls, <span class='spooky'>\"She's tae black sheep o' the family too, so we dunnae see eye tae eye sometimes on alchemy. Ah mean, she puts <i> moles </i> in her brews! Ye dunnae put moles in yer brews! Yae threw your brews at tae wee bastards an blew em up!\"</span> <b>[src]</b> sighs, <span class='spooky'>\"But she's a heart o gold so.. Ah wanted tae see her an check up oon her, make sure she's okay.\"</span>")
|
||||
visible_message("<b>[src]</b> looks to the side sheepishly, <span class='spooky'>\"Aye, well, tae be honest, Ah'm here tae see me sis, but dunnae let her knew that. She's an alchemist too like, but she dunnae use a caldron like mae, she buggered off like tae her posh ivory tower tae learn bloody chemistry instead!\"</span> <b>[src]</b> scowls, <span class='spooky'>\"She's tae black sheep o' the family too, so we dunnae see eye tae eye sometimes on alchemy. Ah mean, she puts <i> moles </i> in her brews! Ye dunnae put moles in yer brews! Yae threw your brews at tae wee bastards an blew em up!\"</span> <b>[src]</b> sighs, <span class='spooky'>\"But she's a heart o gold so.. Ah wanted tae see her an check up oon her, make sure she's okay.\"</span>")
|
||||
jacqrunes("Aye, well, tae be honest, Ah'm here tae see me sis, but dunnae let her knew that. She's an alchemist too like, but she dunnae use a caldron like mae, she buggered off like tae her posh ivory tower tae learn bloody chemistry instead", C)
|
||||
progression["[C.real_name]"] = progression["[C.real_name]"] | JACQ_FAR
|
||||
sleep(30)
|
||||
sleep(10)
|
||||
jacqrunes("She's tae black sheep o' the family too, so we dunnae see eye tae eye sometimes on alchemy. Ah mean, she puts moles in her brews! Ye dunnae put moles in yer brews! Yae threw your brews at tae wee bastards an blew em up!", C)
|
||||
sleep(10)
|
||||
jacqrunes("But she's a heart o gold so.. Ah wanted tae see her an check up oon her, make sure she's okay.", C)
|
||||
sleep(10)
|
||||
|
||||
if("What is that on your head?")
|
||||
visible_message("<b>[src]</b> pats the pumpkin atop her head, <span class='spooky'>\"This thing? This ain't nae ordinary pumpkin! Me Ma grew this monster ooer a year o love, dedication an hard work. Honestly it felt like she loved this thing more than any of us, which Ah knew ain't true an it's not like she was hartless or anything but.. well, we had a falling oot when Ah got back home with all me stuff in tow. An all she had done is sent me owl after owl over t' last year aboot this bloody pumpkin and ah had enough. So ah took it, an put it on me head. You know, as ye do. Ah am the great Pumpqueen after all, Ah deserve this.\"</span>")
|
||||
visible_message("<b>[src]</b> pats the pumpkin atop her head, <span class='spooky'>\"This thing? This ain't nae ordinary pumpkin! Me Ma grew this monster ooer a year o love, dedication an hard work. Honestly I got bloody sick o'er harpin' on aboot it, all she had done is sent me owl after owl over aboot this bloody pumpkin since i left the nest and ah had enough after a ... mild altercation like. So I nabbed it last halloween. Caught bloody hell fer it ah did, course me sis talked to me ma and, well, simmered her doon. We've all but made up noo, but seein' as ah'm the great Pumpqueen after all, I cannae be seen withoot it like.\"</span>")
|
||||
jacqrunes("This thing? This ain't nae ordinary pumpkin! Me Ma grew this monster ooer a year o love, dedication an hard work. Honestly I got bloody sick o'er harpin' on aboot it, all she had done is sent me owl after owl over aboot this bloody pumpkin since i left the nest and ah had enough after a ... mild altercation like. So I nabbed it last halloween. Caught bloody hell fer it ah did, course me sis talked to me ma and, well, simmered her doon. We've all but made up noo, but seein' as ah'm the great Pumpqueen after all, I cannae be seen withoot it like.", C)
|
||||
progression["[C.real_name]"] = progression["[C.real_name]"] | JACQ_HEAD
|
||||
sleep(30)
|
||||
|
||||
//Year 1 answer: for anyone who is interested!
|
||||
/*This thing? This ain't nae ordinary pumpkin! Me Ma grew this monster ooer a year o love, dedication an hard work. Honestly it felt like she loved this thing more than any of us, which Ah knew ain't true an it's not like she was hartless or anything but.. well, we had a falling oot when Ah got back home with all me stuff in tow. An all she had done is sent me owl after owl over t' last year aboot this bloody pumpkin and ah had enough. So ah took it, an put it on me head. You know, as ye do. Ah am the great Pumpqueen after all, Ah deserve this.*/
|
||||
|
||||
if("Are you a witch?")
|
||||
visible_message("<b>[src]</b> grumbles, <span class='spooky'>\"If ye must know, Ah got kicked oot of the witch academy fer being too much of a \"loose cannon\". A bloody loose cannon? Nae they were just pissed off Ah had the brass tae proclaim myself as the Pumpqueen! And also maybe the time Ah went and blew up one of the towers by trying tae make a huge batch of astrogen might've had something tae do with it. Ah mean it would've worked fine if the cauldrons weren't so shite and were actually upgraded by the faculty. So technically no, I'm not a witch.\"</span>")
|
||||
visible_message("<b>[src]</b> smirks, <span class='spooky'>\"Weel, after getting kicked oot over a <i>very minor</i> explosion last year, ah wis able tae git back in 'n' finish me witching degree thanks tae me sis! 'twas bloody brutal finishing th' degree though, ah will tell ye, ye take a peek o'er at t' other witches all prim 'n' proper like 'n' it gets in yer heid, ye'r nae as guid. But ah bloody well ended up summoning Nar'Sie last year for the lot o' yous. Aye a'm a loose cannon - but ah get results.\"</span> thumping her chest with pride as she finishes speaking.")
|
||||
jacqrunes("Weel, after getting kicked oot over a very minor explosion last year, ah wis able tae git back in 'n' finish me witching degree thanks tae me sis! 'twas bloody brutal finishing th' degree though, ah will tell ye, ye take a peek o'er at t' other witches all prim 'n' proper like 'n' it gets in yer heid, ye'r nae as guid. But ah bloody well ended up summoning Nar'Sie last year for the lot o' yous. Aye a'm a loose cannon - but ah get results." , C)
|
||||
progression["[C.real_name]"] = progression["[C.real_name]"] | JACQ_WITCH
|
||||
sleep(30)
|
||||
|
||||
if("So you got ex-spell-ed?")
|
||||
visible_message("<b>[src]</b> Gives you a blank look at the pun, before continuing, <span class='spooky'>\"Not quite, Ah know Ah ken get back into the academy, it's only an explosion, they happen all the time, but, tae be fair it's my fault that things came tae their explosive climax. You don't know what it's like when you're after a witch doctorate, everyone else is doing well, everyone's making new spells and the like, and I'm just good at making explosions really, or fireworks. So, Ah did something Ah knew was dangerous, because Ah had tae do something tae stand oot, but Ah know this life ain't fer me, Ah don't want tae be locked up in dusty towers, grinding reagent after reagent together, trying tae find new reactions, some of the wizards in there haven't left fer years. Ah want tae live, Ah want tae fly around on a broom, turn people into cats fer a day and disappear cackling! That's what got me into witchcraft!\"</span> she throws her arms up in the arm, spinning the pumpkin upon her head slightly. She carefully spins it back to face you, giving oot a soft sigh, <span class='spooky'>\"Ah know my mother's obsession with this dumb thing on my head is just her trying tae fill the void of me and my sis moving oot, and it really shouldn't be on my head. And Ah know that I'm really here tae get help from my sis.. She's the sensible one, and she gives good hugs.\"</span>")
|
||||
sleep(30)
|
||||
//Year 1 answer for anyone who is interested:
|
||||
/* Are you a witch?
|
||||
If ye must know, Ah got kicked oot of the witch academy fer being too much of a \"loose cannon\". A bloody loose cannon? Nae they were just pissed off Ah had the brass tae proclaim myself as the Pumpqueen! And also maybe the time Ah went and blew up one of the towers by trying tae make a huge batch of astrogen might've had something tae do with it. Ah mean it would've worked fine if the cauldrons weren't so shite and were actually upgraded by the faculty. So technically no, I'm not a witch.*/
|
||||
|
||||
if("What is it like being a witch?") //What is it like being a witch?
|
||||
|
||||
visible_message("<b>[src]</b> says, <span class='spooky'>\"Tae be honest, ah dunnae. Ah mean ah have a piece o’ paper that implies that a’m, ‘n’ a few folk who seem keen in shoving me back in their magic tower, but I don’t know if that’s fer me really.\" </span> she gives the back of her pumpkin a quick scratch as she continues <span class='spooky'> \"What draws me tae witchcraft is the chaos, ah live tae see th’ utter madness that comes fae the spells ah cast ‘n’ th’ like, ‘n’ ah know you do tae, it’s why you’re ‘ere, aye? Ah kin conjure some utterly chaotic things, and ah love tae do it, just tae see what I’ll git! It’s why visiting here is so fun, ye wee lot scurrying about, getting me candies, hoping I’ll conjure up something utterly chaotic!\" </span> she throws her arms up in the arm with a laugh, spinning the pumpkin upon her head slightly. She carefully spins it back to face you, <span class='spooky'> \"But o’ course life ain’t that easy, ye can’t just go around being a pure agent o’ chaos, ‘n’ ah feel like the reality o’ the world be trying tae dull me, to shove me in a boring dusty auld room where ah spend t’ rest o’ me days researching some boggin’ magic till ah die. Ah want tae bloody well change t’ world, ah want to do something that leaves an impact, but ah dunnae want t’ kill people doing it.\"</span> she gives out a soft sigh, \"Who knows, maybe I’ll bugger off ‘n’ make some pumkinmobiles or something daft like that.\" </span>")
|
||||
jacqrunes("Tae be honest, ah dunnae. Ah mean ah have a piece o’ paper that implies that a’m, ‘n’ a few folk who seem keen in shoving me back in their magic tower, but I don’t know if that’s fer me really." , C)
|
||||
sleep(10)
|
||||
jacqrunes("What draws me tae witchcraft is the chaos, ah live tae see th’ utter madness that comes fae the spells ah cast ‘n’ th’ like, ‘n’ ah know you do tae, it’s why you’re ‘ere, aye? Ah kin conjure some utterly chaotic things, and ah love tae do it, just tae see what I’ll git! It’s why visiting here is so fun, ye wee lot scurrying about, getting me candies, hoping I’ll conjure up something utterly chaotic!" , C)
|
||||
sleep(10)
|
||||
jacqrunes("But o’ course life ain’t that easy, ye can’t just go around being a pure agent o’ chaos, ‘n’ ah feel like the reality o’ the world be trying tae dull me, to shove me in a boring dusty auld room where ah spend t’ rest o’ me days researching some boggin’ magic till ah die. Ah want tae bloody well change t’ world, ah want to do something that leaves an impact, but ah dunnae want t’ kill people doing it.", C)
|
||||
sleep(10)
|
||||
jacqrunes("Who knows, maybe I’ll bugger off ‘n’ make some pumkinmobiles or something daft like that.", C)
|
||||
sleep(25)
|
||||
visible_message("<b>[src]</b> says, <span class='spooky'>\"Thanks [C], Ah guess Ah didn't realise Ah needed someone tae talk tae but, I'm glad ye spent all your candies talking tae me. Funny how things seem much worse in yer head.\"</span>")
|
||||
jacqrunes("Thanks [C], Ah guess Ah didn't realise Ah needed someone tae talk tae but, I'm glad ye spent all your candies talking tae me. Funny how things seem much worse in yer head." , C)
|
||||
progression["[C.real_name]"] = progression["[C.real_name]"] | JACQ_EXPELL
|
||||
sleep(30)
|
||||
|
||||
//Year 1 answer for anyone who is interested:
|
||||
/*"So you got ex-spell-ed?"
|
||||
Gives you a blank look at the pun, before continuing, <span class='spooky'>\"Not quite, Ah know Ah ken get back into the academy, it's only an explosion, they happen all the time, but, tae be fair it's my fault that things came tae their explosive climax. You don't know what it's like when you're after a witch doctorate, everyone else is doing well, everyone's making new spells and the like, and I'm just good at making explosions really, or fireworks. So, Ah did something Ah knew was dangerous, because Ah had tae do something tae stand oot, but Ah know this life ain't fer me, Ah don't want tae be locked up in dusty towers, grinding reagent after reagent together, trying tae find new reactions, some of the wizards in there haven't left fer years. Ah want tae live, Ah want tae fly around on a broom, turn people into cats fer a day and disappear cackling! That's what got me into witchcraft!\"</span> she throws her arms up in the arm, spinning the pumpkin upon her head slightly. She carefully spins it back to face you, giving oot a soft sigh, <span class='spooky'>\"Ah know my mother's obsession with this dumb thing on my head is just her trying tae fill the void of me and my sis moving oot, and it really shouldn't be on my head. And Ah know that I'm really here tae get help from my sis.. She's the sensible one, and she gives good hugs.
|
||||
*/
|
||||
|
||||
if("Can I take you out on a date?")
|
||||
visible_message("<b>[src]</b> blushes, <span class='spooky'>\"...You want tae ask me oot on a date? Me? After all that nonsense Ah just said? It seems a waste of a candy honestly.\"</span>")
|
||||
//progression["[C.real_name]"] = progression["[C.real_name]"] | JACQ_DATE
|
||||
jacqrunes("...You want tae ask me oot on a date? Me? After all that nonsense Ah just said? It seems a waste of a candy honestly." , C)
|
||||
visible_message("<b>[src]</b> looks to the side, deep in thought.</span>")
|
||||
dating_start(C, gender)
|
||||
|
||||
@@ -328,15 +398,18 @@
|
||||
switch(option)
|
||||
if(1)
|
||||
visible_message("<b>[src]</b> waves their arms around, <span class='spooky'>\"Hocus pocus, making friends is now your focus!\"</span>")
|
||||
jacqrunes("Hocus pocus, making friends is now your focus!", C)
|
||||
var/message = pick("make a tasty sandwich for", "compose a poem for", "aquire a nice outfit to give to", "strike up a conversation about pumpkins with", "write a letter and deliver it to", "give a nice hat to")
|
||||
var/mob/living/L2 = pick(GLOB.player_list)
|
||||
message += " [L2.name]."
|
||||
to_chat(C, "<span class='big warning'> You feel an overwhelming desire to [message]")
|
||||
if(2)
|
||||
visible_message("<b>[src]</b> waves their arms around, <span class='spooky'>\"If only you had a better upbringing, your ears are now full of my singing!\"</span>")
|
||||
C.client.tgui_panel?.play_music("https://puu.sh/ExBbv.mp4")
|
||||
jacqrunes("If only you had a better upbringing, your ears are now full of my singing!", C)
|
||||
C.client.tgui_panel?.play_music("https://katlin.dog/v/spooky.mp4")
|
||||
if(3)
|
||||
visible_message("<b>[src]</b> waves their arms around, <span class='spooky'>\"You're cute little bumpkin, On your head is a pumpkin!\"</span>")
|
||||
jacqrunes("You're cute little bumpkin, On your head is a pumpkin!", C)
|
||||
if(C.head)
|
||||
var/obj/item/W = C.head
|
||||
C.dropItemToGround(W, TRUE)
|
||||
@@ -344,12 +417,15 @@
|
||||
C.equip_to_slot(jaqc_latern, SLOT_HEAD, 1, 1)
|
||||
if(4)
|
||||
visible_message("<b>[src]</b> waves their arms around, <span class='spooky'>\"In your body there's something amiss, you'll find it's a chem made by my sis!\"</span>")
|
||||
jacqrunes("In your body there's something amiss, you'll find it's a chem made by my sis!", C)
|
||||
C.reagents.add_reagent(/datum/reagent/fermi/eigenstate, 30)
|
||||
if(5)
|
||||
visible_message("<b>[src]</b> waves their arms around, <span class='spooky'>\"A new familiar for me, and you'll see it's thee!\"</span>")
|
||||
C.reagents.add_reagent("secretcatchem", 30)
|
||||
jacqrunes("A new familiar for me, and you'll see it's thee!", C)
|
||||
C.reagents.add_reagent(/datum/reagent/fermi/secretcatchem, 30)
|
||||
if(6)
|
||||
visible_message("<b>[src]</b> waves their arms around, <span class='spooky'>\"While you may not be a ghost, for this sheet you'll always be it's host.\"</span>")
|
||||
jacqrunes("While you may not be a ghost, for this sheet you'll always be it's host.", C)
|
||||
var/mob/living/carbon/human/H = C
|
||||
if(H.wear_suit)
|
||||
var/obj/item/W = H.wear_suit
|
||||
@@ -370,22 +446,30 @@
|
||||
message_admins("[C2]/[C2.key] has agreed to go on a date with [C] as Jacqueline.")
|
||||
log_game("HALLOWEEN: [C2]/[C2.key] has agreed to go on a date with [C] as Jacqueline")
|
||||
to_chat(src, "<span class='big spooky'>You are Jacqueline the great pumpqueen, witch Extraordinaire! You're a very Scottish lass with a kind heart, but also a little crazy. You also blew up the wizarding school and you're suspended for a while, so you visited the station before heading home. On your head lies the prize pumpkin of your Mother's pumpkin patch. You're currently on a date with [C] and well, I didn't think anyone would get this far. <i> Please be good so I can do events like this in the future. </i> </span>")
|
||||
return
|
||||
canmove()
|
||||
return TRUE
|
||||
else
|
||||
candies =- C2
|
||||
visible_message("<b>[src]</b> looks to the side, <span class='spooky'>\"Look, Ah like ye but, Ah don't think Ah can right now. If ye can't tell, the stations covered in volatile candies, I've a few other laddies and lassies running after me treats, and tae top it all off, I've the gods breathing down me neck, watching every treat Ah make fer the lot of yous.\" she sighs, \"But that's not a no, right? That's.. just a nae right noo.\"</span>")
|
||||
jacqrunes("Look, Ah like ye but, Ah don't think Ah can right now. If ye can't tell, the stations covered in volatile candies, I've a few other laddies and lassies running after me treats, and tae top it all off, I've the gods breathing down me neck, watching every treat Ah make fer the lot of yous.", C)
|
||||
sleep(20)
|
||||
jacqrunes("But that's not a no, right? That's.. just a nae right noo.", C)
|
||||
visible_message("<b>[src]</b> takes off the pumpkin on her head, a rich blush on her cheeks. She leans over planting a kiss upon your forehead quickly befere popping the pumpkin back on her head.")
|
||||
sleep(10)
|
||||
sleep(20)
|
||||
visible_message("<b>[src]</b> waves their arms around, <span class='spooky'>\"There, that aught tae be worth a candy.\"</span>")
|
||||
jacqrunes("There, that aught tae be worth a candy.", C)
|
||||
sleep(20)
|
||||
poof()
|
||||
|
||||
/mob/living/simple_animal/jacq/update_mobility()
|
||||
. = ..()
|
||||
if(busy_chatting)
|
||||
if(busy)
|
||||
DISABLE_BITFIELD(., MOBILITY_MOVE)
|
||||
mobility_flags = .
|
||||
else
|
||||
ENABLE_BITFIELD(., MOBILITY_MOVE)
|
||||
mobility_flags = .
|
||||
|
||||
|
||||
|
||||
/obj/item/clothing/head/hardhat/pumpkinhead/jaqc
|
||||
name = "Jacq o' latern"
|
||||
|
||||
@@ -285,7 +285,7 @@
|
||||
else
|
||||
plant_overlay.icon_state = myseed.icon_harvest
|
||||
else
|
||||
var/t_growthstate = min(round((age / myseed.maturation) * myseed.growthstages), myseed.growthstages)
|
||||
var/t_growthstate = clamp(round((age / myseed.maturation) * myseed.growthstages), 1, myseed.growthstages)
|
||||
plant_overlay.icon_state = "[myseed.icon_grow][t_growthstate]"
|
||||
add_overlay(plant_overlay)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
/obj/item/survivalcapsule
|
||||
name = "bluespace shelter capsule"
|
||||
desc = "An emergency shelter stored within a pocket of bluespace."
|
||||
icon_state = "capsule"
|
||||
icon = 'icons/obj/mining.dmi'
|
||||
icon_state = "capsule"
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
var/template_id = "shelter_alpha"
|
||||
var/datum/map_template/shelter/template
|
||||
@@ -72,9 +72,10 @@
|
||||
/obj/item/survivalcapsule/luxury
|
||||
name = "luxury bluespace shelter capsule"
|
||||
desc = "An exorbitantly expensive luxury suite stored within a pocket of bluespace."
|
||||
icon_state = "capsule-lux"
|
||||
template_id = "shelter_beta"
|
||||
|
||||
/obj/item/survivalcapsule/luxuryelite
|
||||
/obj/item/survivalcapsule/luxury/elitebar
|
||||
name = "luxury elite bar capsule"
|
||||
desc = "A luxury bar in a capsule. Bartender required and not included."
|
||||
template_id = "shelter_charlie"
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
new /datum/data/mining_equipment("Fulton Pack", /obj/item/extraction_pack, 1000),
|
||||
new /datum/data/mining_equipment("Lazarus Injector", /obj/item/lazarus_injector, 1000),
|
||||
new /datum/data/mining_equipment("Silver Pickaxe", /obj/item/pickaxe/silver, 1000),
|
||||
new /datum/data/mining_equipment("Mining Conscription Kit", /obj/item/storage/backpack/duffelbag/mining_conscript, 1000),
|
||||
new /datum/data/mining_equipment("Mining Conscription Kit", /obj/item/storage/backpack/duffelbag/mining/conscript, 1000),
|
||||
new /datum/data/mining_equipment("1000 Point Transfer Card", /obj/item/card/mining_point_card/mp1000, 1000),
|
||||
new /datum/data/mining_equipment("1500 Point Transfer Card", /obj/item/card/mining_point_card/mp1500, 1500),
|
||||
new /datum/data/mining_equipment("2000 Point Transfer Card", /obj/item/card/mining_point_card/mp2000, 2000),
|
||||
@@ -55,7 +55,7 @@
|
||||
new /datum/data/mining_equipment("Jump Boots", /obj/item/clothing/shoes/bhop, 2500),
|
||||
new /datum/data/mining_equipment("Ice hiking boots", /obj/item/clothing/shoes/winterboots/ice_boots, 2500),
|
||||
new /datum/data/mining_equipment("Luxury Shelter Capsule", /obj/item/survivalcapsule/luxury, 3000),
|
||||
new /datum/data/mining_equipment("Luxury Bar Capsule", /obj/item/survivalcapsule/luxuryelite, 10000),
|
||||
new /datum/data/mining_equipment("Luxury Bar Capsule", /obj/item/survivalcapsule/luxury/elitebar, 10000),
|
||||
new /datum/data/mining_equipment("Nanotrasen Minebot", /mob/living/simple_animal/hostile/mining_drone, 800),
|
||||
new /datum/data/mining_equipment("Minebot Melee Upgrade", /obj/item/mine_bot_upgrade, 400),
|
||||
new /datum/data/mining_equipment("Minebot Armor Upgrade", /obj/item/mine_bot_upgrade/health, 400),
|
||||
@@ -70,9 +70,10 @@
|
||||
new /datum/data/mining_equipment("KA Damage Increase", /obj/item/borg/upgrade/modkit/damage, 1000),
|
||||
new /datum/data/mining_equipment("KA Cooldown Decrease", /obj/item/borg/upgrade/modkit/cooldown, 1000),
|
||||
new /datum/data/mining_equipment("KA AoE Damage", /obj/item/borg/upgrade/modkit/aoe/mobs, 2000),
|
||||
new /datum/data/mining_equipment("Miner Full Replacement", /obj/item/storage/backpack/duffelbag/mining_cloned, 3000),
|
||||
new /datum/data/mining_equipment("Miner Full Replacement", /obj/item/storage/backpack/duffelbag/mining/cloned, 3000),
|
||||
new /datum/data/mining_equipment("Premium Accelerator", /obj/item/gun/energy/kinetic_accelerator/premiumka, 8000),
|
||||
new /datum/data/mining_equipment("Kinetic Glaive", /obj/item/kinetic_crusher/glaive, 2250),
|
||||
new /datum/data/mining_equipment("Kinetic Glaive Kit", /obj/item/storage/backpack/duffelbag/mining/glaivekit, 2250),
|
||||
new /datum/data/mining_equipment("Survival Dagger", /obj/item/kitchen/knife/combat/survival/knuckledagger, 550),
|
||||
)
|
||||
|
||||
/datum/data/mining_equipment
|
||||
@@ -222,7 +223,7 @@
|
||||
new /obj/item/extinguisher/mini(drop_location)
|
||||
new /obj/item/kinetic_crusher(drop_location)
|
||||
if("Mining Conscription Kit")
|
||||
new /obj/item/storage/backpack/duffelbag/mining_conscript(drop_location)
|
||||
new /obj/item/storage/backpack/duffelbag/mining/conscript(drop_location)
|
||||
|
||||
SSblackbox.record_feedback("tally", "mining_voucher_redeemed", 1, selection)
|
||||
qdel(voucher)
|
||||
@@ -328,11 +329,11 @@
|
||||
to_chat(user, "You upgrade [I] with mining access.")
|
||||
qdel(src)
|
||||
|
||||
/obj/item/storage/backpack/duffelbag/mining_conscript
|
||||
/obj/item/storage/backpack/duffelbag/mining/conscript
|
||||
name = "mining conscription kit"
|
||||
desc = "A kit containing everything a crewmember needs to support a shaft miner in the field."
|
||||
|
||||
/obj/item/storage/backpack/duffelbag/mining_conscript/PopulateContents()
|
||||
/obj/item/storage/backpack/duffelbag/mining/conscript/PopulateContents()
|
||||
new /obj/item/pickaxe/mini(src)
|
||||
new /obj/item/clothing/glasses/meson(src)
|
||||
new /obj/item/t_scanner/adv_mining_scanner/lesser(src)
|
||||
@@ -347,11 +348,11 @@
|
||||
|
||||
//CITADEL ADDITIONS BELOW
|
||||
|
||||
/obj/item/storage/backpack/duffelbag/mining_cloned
|
||||
/obj/item/storage/backpack/duffelbag/mining/cloned
|
||||
name = "mining replacement kit"
|
||||
desc = "A large bag that has advance tools and a spare jumpsuit, boots, and gloves for a newly cloned miner to get back in the field. Even has a new ID!"
|
||||
desc = "A large bag that has advanced tools and a spare jumpsuit, boots, and gloves for a newly cloned miner to get back in the field. Even has a new ID!"
|
||||
|
||||
/obj/item/storage/backpack/duffelbag/mining_cloned/PopulateContents()
|
||||
/obj/item/storage/backpack/duffelbag/mining/cloned/PopulateContents()
|
||||
new /obj/item/pickaxe/mini(src)
|
||||
new /obj/item/clothing/under/rank/cargo/miner/lavaland(src)
|
||||
new /obj/item/clothing/shoes/workboots/mining(src)
|
||||
@@ -369,6 +370,12 @@
|
||||
new /obj/item/storage/bag/ore(src)
|
||||
new /obj/item/clothing/glasses/meson/prescription(src)
|
||||
|
||||
/obj/item/storage/backpack/duffelbag/mining/glaivekit
|
||||
|
||||
/obj/item/storage/backpack/duffelbag/mining/glaivekit/PopulateContents()
|
||||
new /obj/item/kinetic_crusher/glaive(src)
|
||||
new /obj/item/kitchen/knife/combat/survival/knuckledagger(src)
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/proc/RedeemSVoucher(obj/item/suit_voucher/voucher, mob/redeemer)
|
||||
var/items = list( "Exo-suit" = image(icon = 'icons/obj/clothing/suits.dmi', icon_state = "exo"),
|
||||
"SEVA suit" = image(icon = 'icons/obj/clothing/suits.dmi', icon_state = "seva"))
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
features = random_features(pref_species?.id, gender)
|
||||
age = rand(AGE_MIN,AGE_MAX)
|
||||
|
||||
/datum/preferences/proc/update_preview_icon(equip_job = TRUE)
|
||||
/datum/preferences/proc/update_preview_icon(current_tab)
|
||||
var/equip_job = (current_tab != 2)
|
||||
// Determine what job is marked as 'High' priority, and dress them up as such.
|
||||
var/datum/job/previewJob = get_highest_job()
|
||||
|
||||
@@ -45,9 +46,13 @@
|
||||
mannequin.add_overlay(mutable_appearance('modular_citadel/icons/ui/backgrounds.dmi', bgstate, layer = SPACE_LAYER))
|
||||
copy_to(mannequin, initial_spawn = TRUE)
|
||||
|
||||
if(previewJob && equip_job)
|
||||
mannequin.job = previewJob.title
|
||||
previewJob.equip(mannequin, TRUE, preference_source = parent)
|
||||
if(current_tab == 3)
|
||||
//give it its loadout if not on the appearance tab
|
||||
SSjob.equip_loadout(parent.mob, mannequin, FALSE, bypass_prereqs = TRUE, can_drop = FALSE)
|
||||
else
|
||||
if(previewJob && equip_job)
|
||||
mannequin.job = previewJob.title
|
||||
previewJob.equip(mannequin, TRUE, preference_source = parent)
|
||||
|
||||
COMPILE_OVERLAYS(mannequin)
|
||||
parent.show_character_previews(new /mutable_appearance(mannequin))
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Facial Hair Definitions //
|
||||
/////////////////////////////
|
||||
/datum/sprite_accessory/facial_hair
|
||||
icon = 'icons/mob/human_face.dmi'
|
||||
icon = 'icons/mob/hair.dmi'
|
||||
gender = MALE // barf (unless you're a dorf, dorfs dig chix w/ beards :P)
|
||||
|
||||
// please make sure they're sorted alphabetically and categorized
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Hair Definitions //
|
||||
//////////////////////
|
||||
/datum/sprite_accessory/hair
|
||||
icon = 'icons/mob/human_face.dmi' // default icon for all hairs
|
||||
icon = 'icons/mob/hair.dmi' // default icon for all hairs
|
||||
|
||||
// please make sure they're sorted alphabetically and, where needed, categorized
|
||||
// try to capitalize the names please~
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -979,30 +979,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()
|
||||
. = ..()
|
||||
|
||||
@@ -1046,7 +1046,8 @@
|
||||
return
|
||||
var/stambufferinfluence = (bufferedstam*(100/stambuffer))*0.2 //CIT CHANGE - makes stamina buffer influence movedelay
|
||||
if(!HAS_TRAIT(src, TRAIT_IGNOREDAMAGESLOWDOWN)) //if we want to ignore slowdown from damage, but not from equipment
|
||||
var/health_deficiency = ((maxHealth + stambufferinfluence) - 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) + stambufferinfluence) - (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)
|
||||
@@ -1057,11 +1058,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
|
||||
|
||||
@@ -101,3 +101,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
|
||||
|
||||
|
||||
@@ -117,6 +117,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
//the ids you can use for your species, if empty, it means default only and not changeable
|
||||
var/list/allowed_limb_ids
|
||||
|
||||
//the type of eyes this species has
|
||||
var/eye_type = "normal"
|
||||
|
||||
///////////
|
||||
// PROCS //
|
||||
///////////
|
||||
@@ -510,7 +513,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
var/mutable_appearance/hair_overlay = mutable_appearance(layer = -HAIR_LAYER)
|
||||
if(!hair_hidden && !H.getorgan(/obj/item/organ/brain)) //Applies the debrained overlay if there is no brain
|
||||
if(!(NOBLOOD in species_traits))
|
||||
hair_overlay.icon = 'icons/mob/human_face.dmi'
|
||||
hair_overlay.icon = 'icons/mob/hair.dmi'
|
||||
hair_overlay.icon_state = "debrained"
|
||||
|
||||
else if(H.hair_style && (HAIR in species_traits))
|
||||
@@ -569,7 +572,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
if(HD && !(HAS_TRAIT(H, TRAIT_HUSK)))
|
||||
// lipstick
|
||||
if(H.lip_style && (LIPS in species_traits))
|
||||
var/mutable_appearance/lip_overlay = mutable_appearance('icons/mob/human_face.dmi', "lips_[H.lip_style]", -BODY_LAYER)
|
||||
var/mutable_appearance/lip_overlay = mutable_appearance('icons/mob/lips.dmi', "lips_[H.lip_style]", -BODY_LAYER)
|
||||
lip_overlay.color = H.lip_color
|
||||
|
||||
if(OFFSET_LIPS in H.dna.species.offset_features)
|
||||
@@ -582,10 +585,15 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
if(!(NOEYES in species_traits))
|
||||
var/has_eyes = H.getorganslot(ORGAN_SLOT_EYES)
|
||||
if(!has_eyes)
|
||||
standing += mutable_appearance('icons/mob/human_face.dmi', "eyes_missing", -BODY_LAYER)
|
||||
standing += mutable_appearance('icons/mob/eyes.dmi', "eyes_missing", -BODY_LAYER)
|
||||
else
|
||||
var/mutable_appearance/left_eye = mutable_appearance('icons/mob/human_face.dmi', "left_eye", -BODY_LAYER)
|
||||
var/mutable_appearance/right_eye = mutable_appearance('icons/mob/human_face.dmi', "right_eye", -BODY_LAYER)
|
||||
var/left_state = DEFAULT_LEFT_EYE_STATE
|
||||
var/right_state = DEFAULT_RIGHT_EYE_STATE
|
||||
if(eye_type in GLOB.eye_types)
|
||||
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)
|
||||
var/mutable_appearance/right_eye = mutable_appearance('icons/mob/eyes.dmi', right_state, -BODY_LAYER)
|
||||
if((EYECOLOR in species_traits) && has_eyes)
|
||||
left_eye.color = "#" + H.left_eye_color
|
||||
right_eye.color = "#" + H.right_eye_color
|
||||
@@ -1937,6 +1945,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//
|
||||
/////////////
|
||||
|
||||
@@ -21,3 +21,5 @@
|
||||
species_type = "insect"
|
||||
|
||||
allowed_limb_ids = list("insect","apid","moth","moth_not_greyscale")
|
||||
|
||||
eye_type = "insect"
|
||||
|
||||
@@ -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
|
||||
@@ -35,12 +36,15 @@
|
||||
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
|
||||
@@ -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//////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -748,7 +756,7 @@ use_mob_overlay_icon: if FALSE, it will always use the default_icon_file even if
|
||||
if(HD && !(HAS_TRAIT(src, TRAIT_HUSK)))
|
||||
// lipstick
|
||||
if(lip_style && (LIPS in dna.species.species_traits))
|
||||
var/mutable_appearance/lip_overlay = mutable_appearance('icons/mob/human_face.dmi', "lips_[lip_style]", -BODY_LAYER)
|
||||
var/mutable_appearance/lip_overlay = mutable_appearance('icons/mob/lips.dmi', "lips_[lip_style]", -BODY_LAYER)
|
||||
lip_overlay.color = lip_color
|
||||
if(OFFSET_LIPS in dna.species.offset_features)
|
||||
lip_overlay.pixel_x += dna.species.offset_features[OFFSET_LIPS][1]
|
||||
@@ -759,10 +767,17 @@ use_mob_overlay_icon: if FALSE, it will always use the default_icon_file even if
|
||||
if(!(NOEYES in dna.species.species_traits))
|
||||
var/has_eyes = getorganslot(ORGAN_SLOT_EYES)
|
||||
if(!has_eyes)
|
||||
add_overlay(mutable_appearance('icons/mob/human_face.dmi', "eyes_missing", -BODY_LAYER))
|
||||
add_overlay(mutable_appearance('icons/mob/eyes.dmi', "eyes_missing", -BODY_LAYER))
|
||||
else
|
||||
var/mutable_appearance/left_eye = mutable_appearance('icons/mob/human_face.dmi', "left_eye", -BODY_LAYER)
|
||||
var/mutable_appearance/right_eye = mutable_appearance('icons/mob/human_face.dmi', "right_eye", -BODY_LAYER)
|
||||
var/left_state = DEFAULT_LEFT_EYE_STATE
|
||||
var/right_state = DEFAULT_RIGHT_EYE_STATE
|
||||
if(dna.species)
|
||||
var/eye_type = dna.species.eye_type
|
||||
if(GLOB.eye_types[eye_type])
|
||||
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)
|
||||
var/mutable_appearance/right_eye = mutable_appearance('icons/mob/eyes.dmi', right_state, -BODY_LAYER)
|
||||
if((EYECOLOR in dna.species.species_traits) && has_eyes)
|
||||
left_eye.color = "#" + left_eye_color
|
||||
right_eye.color = "#" + right_eye_color
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
hair_hidden = 1
|
||||
if(!hair_hidden)
|
||||
if(!getorgan(/obj/item/organ/brain)) //Applies the debrained overlay if there is no brain
|
||||
overlays_standing[HAIR_LAYER] = mutable_appearance('icons/mob/human_face.dmi', "debrained", -HAIR_LAYER)
|
||||
overlays_standing[HAIR_LAYER] = mutable_appearance('icons/mob/human_parts.dmi', "debrained", -HAIR_LAYER)
|
||||
apply_overlay(HAIR_LAYER)
|
||||
|
||||
|
||||
|
||||
@@ -231,6 +231,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -22,7 +22,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
|
||||
icon_state = "magicbase"
|
||||
icon_living = "magicbase"
|
||||
icon_dead = "magicbase"
|
||||
speed = 0
|
||||
speed = -0.5
|
||||
blood_volume = 0
|
||||
a_intent = INTENT_HARM
|
||||
stop_automated_movement = 1
|
||||
@@ -511,7 +511,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
|
||||
var/used_message = "<span class='holoparasite'>All the cards seem to be blank now.</span>"
|
||||
var/failure_message = "<span class='holoparasite bold'>..And draw a card! It's...blank? Maybe you should try again later.</span>"
|
||||
var/ling_failure = "<span class='holoparasite bold'>The deck refuses to respond to a souless creature such as you.</span>"
|
||||
var/list/possible_guardians = list("Assassin", "Chaos", "Charger", "Explosive", "Lightning", "Protector", "Ranged", "Standard", "Support")
|
||||
var/list/possible_guardians = list("Assassin", "Chaos", "Gravitokinetic", "Charger", "Explosive", "Lightning", "Protector", "Ranged", "Standard", "Support")
|
||||
var/random = TRUE
|
||||
var/allowmultiple = FALSE
|
||||
var/allowling = TRUE
|
||||
@@ -559,6 +559,9 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
|
||||
if("Chaos")
|
||||
pickedtype = /mob/living/simple_animal/hostile/guardian/fire
|
||||
|
||||
if("Gravitokinetic")
|
||||
pickedtype = /mob/living/simple_animal/hostile/guardian/gravitokinetic
|
||||
|
||||
if("Standard")
|
||||
pickedtype = /mob/living/simple_animal/hostile/guardian/punch
|
||||
|
||||
@@ -615,10 +618,10 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
|
||||
random = FALSE
|
||||
|
||||
/obj/item/guardiancreator/choose/dextrous
|
||||
possible_guardians = list("Assassin", "Chaos", "Charger", "Dextrous", "Explosive", "Lightning", "Protector", "Ranged", "Standard", "Support")
|
||||
possible_guardians = list("Assassin", "Chaos", "Gravitokinetic", "Charger", "Dextrous", "Explosive", "Lightning", "Protector", "Ranged", "Standard", "Support")
|
||||
|
||||
/obj/item/guardiancreator/choose/wizard
|
||||
possible_guardians = list("Assassin", "Chaos", "Charger", "Dextrous", "Explosive", "Lightning", "Protector", "Ranged", "Standard")
|
||||
possible_guardians = list("Assassin", "Chaos", "Gravitokinetic", "Charger", "Dextrous", "Explosive", "Lightning", "Protector", "Ranged", "Standard")
|
||||
allowmultiple = TRUE
|
||||
|
||||
/obj/item/guardiancreator/tech
|
||||
@@ -634,7 +637,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
|
||||
ling_failure = "<span class='holoparasite bold'>The holoparasites recoil in horror. They want nothing to do with a creature like you.</span>"
|
||||
|
||||
/obj/item/guardiancreator/tech/choose/traitor
|
||||
possible_guardians = list("Assassin", "Chaos", "Charger", "Explosive", "Lightning", "Protector", "Ranged", "Standard", "Support")
|
||||
possible_guardians = list("Assassin", "Chaos", "Gravitokinetic", "Charger", "Explosive", "Lightning", "Protector", "Ranged", "Standard", "Support")
|
||||
allowling = FALSE
|
||||
|
||||
/obj/item/guardiancreator/tech/choose/traitor/check_uplink_validity()
|
||||
@@ -644,10 +647,10 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
|
||||
random = FALSE
|
||||
|
||||
/obj/item/guardiancreator/tech/choose/dextrous
|
||||
possible_guardians = list("Assassin", "Chaos", "Charger", "Dextrous", "Explosive", "Lightning", "Protector", "Ranged", "Standard", "Support")
|
||||
possible_guardians = list("Assassin", "Chaos", "Gravitokinetic", "Charger", "Dextrous", "Explosive", "Lightning", "Protector", "Ranged", "Standard", "Support")
|
||||
|
||||
/obj/item/guardiancreator/tech/choose/nukie // lacks support and protector as encouraging nukies to play turtle isnt fun and dextrous is epic
|
||||
possible_guardians = list("Assassin", "Chaos", "Charger", "Dextrous", "Explosive", "Lightning", "Ranged", "Standard")
|
||||
possible_guardians = list("Assassin", "Chaos", "Gravitokinetic", "Charger", "Dextrous", "Explosive", "Lightning", "Ranged", "Standard")
|
||||
|
||||
/obj/item/guardiancreator/tech/choose/nukie/check_uplink_validity()
|
||||
return !used
|
||||
@@ -666,6 +669,8 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
|
||||
<br>
|
||||
<b>Explosive</b>: High damage resist and medium power attack that may explosively teleport targets. Can turn any object, including objects too large to pick up, into a bomb, dealing explosive damage to the next person to touch it. The object will return to normal after the trap is triggered or after a delay.<br>
|
||||
<br>
|
||||
<b>Gravitokinetic</b>: Attacks will apply crushing gravity to the target. Can target the ground as well to slow targets advancing on you, but this will affect the user.<br>
|
||||
<br>
|
||||
<b>Lightning</b>: Attacks apply lightning chains to targets. Has a lightning chain to the user. Lightning chains shock everything near them, doing constant damage.<br>
|
||||
<br>
|
||||
<b>Protector</b>: Causes you to teleport to it when out of range, unlike other parasites. Has two modes; Combat, where it does and takes medium damage, and Protection, where it does and takes almost no damage but moves slightly slower.<br>
|
||||
@@ -695,6 +700,8 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
|
||||
<br>
|
||||
<b>Explosive</b>: High damage resist and medium power attack that may explosively teleport targets. Can turn any object, including objects too large to pick up, into a bomb, dealing explosive damage to the next person to touch it. The object will return to normal after the trap is triggered or after a delay.<br>
|
||||
<br>
|
||||
<b>Gravitokinetic</b>: Attacks will apply crushing gravity to the target. Can target the ground as well to slow targets advancing on you, but this will affect the user.<br>
|
||||
<br>
|
||||
<b>Lightning</b>: Attacks apply lightning chains to targets. Has a lightning chain to the user. Lightning chains shock everything near them, doing constant damage.<br>
|
||||
<br>
|
||||
<b>Protector</b>: Causes you to teleport to it when out of range, unlike other parasites. Has two modes; Combat, where it does and takes medium damage, and Protection, where it does and takes almost no damage but moves slightly slower.<br>
|
||||
@@ -720,6 +727,8 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
|
||||
<br>
|
||||
<b>Explosive</b>: High damage resist and medium power attack that may explosively teleport targets. Can turn any object, including objects too large to pick up, into a bomb, dealing explosive damage to the next person to touch it. The object will return to normal after the trap is triggered or after a delay.<br>
|
||||
<br>
|
||||
<b>Gravitokinetic</b>: Attacks will apply crushing gravity to the target. Can target the ground as well to slow targets advancing on you, but this will affect the user.<br>
|
||||
<br>
|
||||
<b>Lightning</b>: Attacks apply lightning chains to targets. Has a lightning chain to the user. Lightning chains shock everything near them, doing constant damage.<br>
|
||||
<br>
|
||||
<b>Ranged</b>: Has two modes. Ranged; which fires a constant stream of weak, armor-ignoring projectiles. Scout; Cannot attack, but can move through walls and is quite hard to see. Can lay surveillance snares, which alert it when crossed, in either mode.<br>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//Assassin
|
||||
/mob/living/simple_animal/hostile/guardian/assassin
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 15
|
||||
attack_verb_continuous = "slashes"
|
||||
attack_verb_simple = "slash"
|
||||
attack_sound = 'sound/weapons/bladeslice.ogg'
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
//Charger
|
||||
/mob/living/simple_animal/hostile/guardian/charger
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 15
|
||||
ranged = 1 //technically
|
||||
ranged_message = "charges"
|
||||
ranged_cooldown_time = 20
|
||||
speed = -1
|
||||
damage_coeff = list(BRUTE = 0.2, BURN = 0.5, TOX = 0.5, CLONE = 0.5, STAMINA = 0, OXY = 0.5)
|
||||
playstyle_string = "<span class='holoparasite'>As a <b>charger</b> type you do medium damage, take half damage, have near immunity to brute damage, move very fast, and can charge at a location, damaging any target hit and forcing them to drop any items they are holding.</span>"
|
||||
magic_fluff_string = "<span class='holoparasite'>..And draw the Hunter, an alien master of rapid assault.</span>"
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//Bomb
|
||||
/mob/living/simple_animal/hostile/guardian/bomb
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 15
|
||||
damage_coeff = list(BRUTE = 0.6, BURN = 0.6, TOX = 0.6, CLONE = 0.6, STAMINA = 0, OXY = 0.6)
|
||||
playstyle_string = "<span class='holoparasite'>As an <b>explosive</b> type, you have moderate close combat abilities, take half damage, may explosively teleport targets on attack, and are capable of converting nearby items and objects into disguised bombs via alt click.</span>"
|
||||
magic_fluff_string = "<span class='holoparasite'>..And draw the Scientist, master of explosive death.</span>"
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
//gravitokinetic
|
||||
/mob/living/simple_animal/hostile/guardian/gravitokinetic
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 15
|
||||
damage_coeff = list(BRUTE = 0.75, BURN = 0.75, TOX = 0.75, CLONE = 0.75, STAMINA = 0, OXY = 0.75)
|
||||
playstyle_string = "<span class='holoparasite'>As a <b>gravitokinetic</b> type, you can alt click to make the gravity on the ground stronger, and punching applies this effect to a target.</span>"
|
||||
magic_fluff_string = "<span class='holoparasite'>..And draw the Singularity, an anomalous force of terror.</span>"
|
||||
tech_fluff_string = "<span class='holoparasite'>Boot sequence complete. Gravitokinetic modules loaded. Holoparasite swarm online.</span>"
|
||||
carp_fluff_string = "<span class='holoparasite'>CARP CARP CARP! Caught one! It's a gravitokinetic carp! Now do you understand the gravity of the situation?</span>"
|
||||
var/list/gravito_targets = list()
|
||||
var/gravity_power_range = 10 //how close the stand must stay to the target to keep the heavy gravity
|
||||
|
||||
///Removes gravity from affected mobs upon guardian death to prevent permanent effects
|
||||
/mob/living/simple_animal/hostile/guardian/gravitokinetic/death()
|
||||
. = ..()
|
||||
for(var/i in gravito_targets)
|
||||
remove_gravity(i)
|
||||
|
||||
/mob/living/simple_animal/hostile/guardian/gravitokinetic/AttackingTarget()
|
||||
. = ..()
|
||||
if(isliving(target) && target != src && target != summoner)
|
||||
to_chat(src, "<span class='danger'><B>Your punch has applied heavy gravity to [target]!</span></B>")
|
||||
add_gravity(target, 5)
|
||||
to_chat(target, "<span class='userdanger'>Everything feels really heavy!</span>")
|
||||
|
||||
/mob/living/simple_animal/hostile/guardian/gravitokinetic/AltClickOn(atom/A)
|
||||
if(isopenturf(A) && is_deployed() && stat != DEAD && in_range(src, A) && !incapacitated())
|
||||
var/turf/T = A
|
||||
if(isspaceturf(T))
|
||||
to_chat(src, "<span class='warning'>You cannot add gravity to space!</span>")
|
||||
return
|
||||
visible_message("<span class='danger'>[src] slams their fist into the [T]!</span>", "<span class='notice'>You modify the gravity of the [T].</span>")
|
||||
do_attack_animation(T)
|
||||
add_gravity(T, 3)
|
||||
return
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/guardian/gravitokinetic/Recall(forced)
|
||||
. = ..()
|
||||
to_chat(src, "<span class='danger'><B>You have released your gravitokinetic powers!</span></B>")
|
||||
for(var/i in gravito_targets)
|
||||
remove_gravity(i)
|
||||
|
||||
/mob/living/simple_animal/hostile/guardian/gravitokinetic/Manifest(forced)
|
||||
. = ..()
|
||||
//just make sure to reapply a gravity immunity wherever you summon. it can be overridden but not by you at least
|
||||
summoner.AddElement(/datum/element/forced_gravity, 1)
|
||||
AddElement(/datum/element/forced_gravity, 1)
|
||||
|
||||
/mob/living/simple_animal/hostile/guardian/gravitokinetic/Moved(oldLoc, dir)
|
||||
. = ..()
|
||||
for(var/i in gravito_targets)
|
||||
if(get_dist(src, i) > gravity_power_range)
|
||||
remove_gravity(i)
|
||||
|
||||
/mob/living/simple_animal/hostile/guardian/gravitokinetic/proc/add_gravity(atom/A, new_gravity = 3)
|
||||
if(gravito_targets[A])
|
||||
return
|
||||
A.AddElement(/datum/element/forced_gravity, new_gravity)
|
||||
gravito_targets[A] = new_gravity
|
||||
RegisterSignal(A, COMSIG_MOVABLE_MOVED, .proc/__distance_check)
|
||||
playsound(src, 'sound/effects/gravhit.ogg', 100, TRUE)
|
||||
|
||||
/mob/living/simple_animal/hostile/guardian/gravitokinetic/proc/remove_gravity(atom/target)
|
||||
if(isnull(gravito_targets[target]))
|
||||
return
|
||||
UnregisterSignal(target, COMSIG_MOVABLE_MOVED)
|
||||
target.RemoveElement(/datum/element/forced_gravity, gravito_targets[target])
|
||||
gravito_targets -= target
|
||||
|
||||
/mob/living/simple_animal/hostile/guardian/gravitokinetic/proc/__distance_check(atom/movable/AM, OldLoc, Dir, Forced)
|
||||
if(get_dist(src, AM) > gravity_power_range)
|
||||
remove_gravity(AM)
|
||||
@@ -1,5 +1,7 @@
|
||||
//Protector
|
||||
/mob/living/simple_animal/hostile/guardian/protector
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 15
|
||||
range = 15 //worse for it due to how it leashes
|
||||
damage_coeff = list(BRUTE = 0.4, BURN = 0.4, TOX = 0.4, CLONE = 0.4, STAMINA = 0, OXY = 0.4)
|
||||
playstyle_string = "<span class='holoparasite'>As a <b>protector</b> type you cause your summoner to leash to you instead of you leashing to them and have two modes; Combat Mode, where you do and take medium damage, and Protection Mode, where you do and take almost no damage, but move slightly slower.</span>"
|
||||
@@ -31,9 +33,10 @@
|
||||
cooldown = world.time + 10
|
||||
if(toggle)
|
||||
cut_overlays()
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 15
|
||||
speed = 0
|
||||
add_overlay(cooloverlay) //readd the guardian's colors
|
||||
melee_damage_lower = initial(melee_damage_lower)
|
||||
melee_damage_upper = initial(melee_damage_upper)
|
||||
speed = initial(speed)
|
||||
damage_coeff = list(BRUTE = 0.4, BURN = 0.4, TOX = 0.4, CLONE = 0.4, STAMINA = 0, OXY = 0.4)
|
||||
to_chat(src, "<span class='danger'><B>You switch to combat mode.</span></B>")
|
||||
toggle = FALSE
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
friendly_verb_continuous = "heals"
|
||||
friendly_verb_simple = "heal"
|
||||
damage_coeff = list(BRUTE = 0.7, BURN = 0.7, TOX = 0.7, CLONE = 0.7, STAMINA = 0, OXY = 0.7)
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 15
|
||||
playstyle_string = "<span class='holoparasite'>As a <b>support</b> type, you have 30% damage reduction and may toggle your basic attacks to a healing mode. In addition, Alt-Clicking on an adjacent object or mob will warp them to your bluespace beacon after a short delay.</span>"
|
||||
magic_fluff_string = "<span class='holoparasite'>..And draw the CMO, a potent force of life... and death.</span>"
|
||||
carp_fluff_string = "<span class='holoparasite'>CARP CARP CARP! You caught a support carp. It's a kleptocarp!</span>"
|
||||
@@ -43,15 +45,15 @@
|
||||
if(src.loc == summoner)
|
||||
if(toggle)
|
||||
a_intent = INTENT_HARM
|
||||
speed = 0
|
||||
speed = initial(speed)
|
||||
damage_coeff = list(BRUTE = 0.7, BURN = 0.7, TOX = 0.7, CLONE = 0.7, STAMINA = 0, OXY = 0.7)
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 15
|
||||
melee_damage_lower = initial(melee_damage_lower)
|
||||
melee_damage_upper = initial(melee_damage_upper)
|
||||
to_chat(src, "<span class='danger'><B>You switch to combat mode.</span></B>")
|
||||
toggle = FALSE
|
||||
else
|
||||
a_intent = INTENT_HELP
|
||||
speed = 1
|
||||
speed = initial(speed)
|
||||
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1)
|
||||
melee_damage_lower = 0
|
||||
melee_damage_upper = 0
|
||||
|
||||
@@ -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"
|
||||
@@ -59,12 +59,12 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/retaliate/ghost/proc/give_hair()
|
||||
if(ghost_hair_style != null)
|
||||
ghost_hair = mutable_appearance('icons/mob/human_face.dmi', "hair_[ghost_hair_style]", -HAIR_LAYER)
|
||||
ghost_hair = mutable_appearance('icons/mob/hair.dmi', "hair_[ghost_hair_style]", -HAIR_LAYER)
|
||||
ghost_hair.alpha = 200
|
||||
ghost_hair.color = ghost_hair_color
|
||||
add_overlay(ghost_hair)
|
||||
if(ghost_facial_hair_style != null)
|
||||
ghost_facial_hair = mutable_appearance('icons/mob/human_face.dmi', "facial_[ghost_facial_hair_style]", -HAIR_LAYER)
|
||||
ghost_facial_hair = mutable_appearance('icons/mob/hair.dmi', "facial_[ghost_facial_hair_style]", -HAIR_LAYER)
|
||||
ghost_facial_hair.alpha = 200
|
||||
ghost_facial_hair.color = ghost_facial_hair_color
|
||||
add_overlay(ghost_facial_hair)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
. = ..()
|
||||
update_config_movespeed()
|
||||
update_movespeed(TRUE)
|
||||
initialize_actionspeed()
|
||||
hook_vr("mob_new",list(src))
|
||||
|
||||
/mob/GenerateTag()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -85,9 +85,14 @@
|
||||
. += "[get_ammo(0,0)] of those are live rounds."
|
||||
|
||||
/obj/item/gun/ballistic/revolver/syndicate
|
||||
obj_flags = UNIQUE_RENAME
|
||||
unique_reskin = list("Default" = "revolver",
|
||||
"Silver" = "russianrevolver",
|
||||
"Robust" = "revolvercit")
|
||||
"Robust" = "revolvercit",
|
||||
"Bulky" = "revolverhakita",
|
||||
"Polished" = "revolvertoriate",
|
||||
"Soulless" = "revolveroldflip",
|
||||
"Soul" = "revolverold")
|
||||
|
||||
/obj/item/gun/ballistic/revolver/detective
|
||||
name = "\improper .38 Mars Special"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -484,12 +484,12 @@
|
||||
return FALSE
|
||||
var/list/D = holder.get_data("blood")
|
||||
if(D && D["changeling_loudness"])
|
||||
return (D["changeling_loudness"] >= 4 ? D["changeling_loudness"] : FALSE)
|
||||
return (D["changeling_loudness"] >= LINGBLOOD_DETECTION_THRESHOLD ? D["changeling_loudness"] : FALSE)
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/datum/chemical_reaction/reagent_explosion/lingblood/on_reaction(datum/reagents/holder, multiplier, specialreact)
|
||||
if(specialreact >= 10)
|
||||
if(specialreact > LINGBLOOD_EXPLOSION_THRESHOLD)
|
||||
return ..()
|
||||
else
|
||||
return FALSE
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/////////// skelter items
|
||||
|
||||
/obj/item/paper/fluff/ruins/skelter/cloner
|
||||
info = "You may be wondering why your pay has been cut, but rest assured - it's because we're investing in YOU! Should you meet your untimely demise, this machine can bring you back to life with as little as your brain! It also means death is no longer an acceptable excuse for neglecting your duties, so get back to work! <BR>\n<BR>\n-HQ"
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -488,12 +488,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))
|
||||
@@ -505,7 +505,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(FALSE) || stamina_dam >= max_damage)
|
||||
|
||||
@@ -161,7 +161,7 @@
|
||||
debrain_overlay.icon = 'icons/mob/animal_parts.dmi'
|
||||
debrain_overlay.icon_state = "debrained_larva"
|
||||
else if(!(NOBLOOD in species_flags_list))
|
||||
debrain_overlay.icon = 'icons/mob/human_face.dmi'
|
||||
debrain_overlay.icon = 'icons/mob/human_parts.dmi'
|
||||
debrain_overlay.icon_state = "debrained"
|
||||
. += debrain_overlay
|
||||
else
|
||||
@@ -175,21 +175,31 @@
|
||||
|
||||
// lipstick
|
||||
if(lip_style)
|
||||
var/image/lips_overlay = image('icons/mob/human_face.dmi', "lips_[lip_style]", -BODY_LAYER, SOUTH)
|
||||
var/image/lips_overlay = image('icons/mob/lips.dmi', "lips_[lip_style]", -BODY_LAYER, SOUTH)
|
||||
lips_overlay.color = lip_color
|
||||
. += lips_overlay
|
||||
|
||||
// eyes
|
||||
if(eyes)
|
||||
var/image/left_eye = image('icons/mob/human_face.dmi', "left_eye", -BODY_LAYER, SOUTH)
|
||||
var/image/right_eye = image('icons/mob/human_face.dmi', "right_eye", -BODY_LAYER, SOUTH)
|
||||
if(eyes.left_eye_color && eyes.right_eye_color)
|
||||
left_eye.color = "#" + eyes.left_eye_color
|
||||
right_eye.color = "#" + eyes.right_eye_color
|
||||
. += left_eye
|
||||
. += right_eye
|
||||
var/left_state = DEFAULT_LEFT_EYE_STATE
|
||||
var/right_state = DEFAULT_RIGHT_EYE_STATE
|
||||
if(owner && owner.dna.species)
|
||||
var/eye_type = owner.dna.species.eye_type
|
||||
if(GLOB.eye_types[eye_type])
|
||||
left_state = eye_type + "_left_eye"
|
||||
right_state = eye_type + "_right_eye"
|
||||
if(left_state != DEFAULT_NO_EYE_STATE)
|
||||
var/image/left_eye = image('icons/mob/hair.dmi', left_state, -BODY_LAYER, SOUTH)
|
||||
if(eyes.left_eye_color)
|
||||
left_eye.color = "#" + eyes.left_eye_color
|
||||
. += left_eye
|
||||
if(right_state != DEFAULT_NO_EYE_STATE)
|
||||
var/image/right_eye = image('icons/mob/hair.dmi', right_state, -BODY_LAYER, SOUTH)
|
||||
if(eyes.right_eye_color)
|
||||
right_eye.color = "#" + eyes.right_eye_color
|
||||
. += right_eye
|
||||
else
|
||||
var/eyes_overlay = image('icons/mob/human_face.dmi', "eyes_missing", -BODY_LAYER, SOUTH)
|
||||
var/eyes_overlay = image('icons/mob/hair.dmi', "eyes_missing", -BODY_LAYER, SOUTH)
|
||||
. += eyes_overlay
|
||||
|
||||
/obj/item/bodypart/head/monkey
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -248,7 +248,7 @@
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/Initialize()
|
||||
. = ..()
|
||||
mob_overlay = image('icons/mob/human_face.dmi', "eyes_glow_gs")
|
||||
mob_overlay = image('icons/mob/eyes.dmi', "eyes_glow_gs")
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/Destroy()
|
||||
terminate_effects()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -342,21 +342,23 @@
|
||||
to_chat(src, "<span class='warning'>You can't do that so fast, slow down.</span>")
|
||||
return
|
||||
|
||||
var/list/choices
|
||||
DelayNextAction(CLICK_CD_MELEE, flush = TRUE)
|
||||
|
||||
var/list/lickable = list()
|
||||
for(var/mob/living/L in view(1))
|
||||
if(L != src && (!L.ckey || L.client?.prefs.vore_flags & LICKABLE) && Adjacent(L))
|
||||
LAZYADD(choices, L)
|
||||
LAZYADD(lickable, L)
|
||||
for(var/mob/living/listed in lickable)
|
||||
lickable[listed] = new /mutable_appearance(listed)
|
||||
|
||||
if(!choices)
|
||||
if(!lickable)
|
||||
return
|
||||
|
||||
var/mob/living/tasted = input(src, "Who would you like to lick? (Excluding yourself and those with the preference disabled)", "Licking") as null|anything in choices
|
||||
var/mob/living/tasted = show_radial_menu(src, src, lickable, radius = 40, require_near = TRUE)
|
||||
|
||||
if(QDELETED(tasted) || (tasted.ckey && !(tasted.client?.prefs.vore_flags & LICKABLE)) || !Adjacent(tasted) || incapacitated(ignore_restraints = TRUE))
|
||||
return
|
||||
|
||||
DelayNextAction(CLICK_CD_MELEE)
|
||||
|
||||
visible_message("<span class='warning'>[src] licks [tasted]!</span>","<span class='notice'>You lick [tasted]. They taste rather like [tasted.get_taste_message()].</span>","<b>Slurp!</b>")
|
||||
|
||||
/mob/living/proc/get_taste_message(allow_generic = TRUE, datum/species/mrace)
|
||||
|
||||
@@ -661,7 +661,7 @@
|
||||
if(href_list["saveprefs"])
|
||||
if(!(user.client?.prefs))
|
||||
return FALSE
|
||||
if(!user.client.prefs.save_character())
|
||||
if(!user.copy_to_prefs_vr() || !user.client.prefs.save_character())
|
||||
to_chat(user, "<span class='warning'>Belly Preferences not saved!</span>")
|
||||
log_admin("Could not save vore prefs on USER: [user].")
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user