Merge branch 'mechdrillfix' of https://github.com/KeplerWasTaken/S.P.L.U.R.T-Station-13 into mechdrillfix
@@ -41,7 +41,6 @@ require only minor tweaks.
|
||||
//boolean - weather types that occur on the level
|
||||
#define ZTRAIT_SNOWSTORM "Weather_Snowstorm"
|
||||
#define ZTRAIT_ASHSTORM "Weather_Ashstorm"
|
||||
#define ZTRAIT_ACIDRAIN "Weather_Acidrain"
|
||||
#define ZTRAIT_VOIDSTORM "Weather_Voidstorm"
|
||||
#define ZTRAIT_ICESTORM "Weather_Icestorm"
|
||||
#define ZTRAIT_LONGRAIN "Weather_Longrain"
|
||||
|
||||
@@ -263,6 +263,14 @@
|
||||
/// Prevents sprinting from being active.
|
||||
#define TRAIT_SPRINT_LOCKED "sprint_locked"
|
||||
|
||||
/// Weather immunities, also protect mobs inside them.
|
||||
#define TRAIT_LAVA_IMMUNE "lava_immune" //Used by lava turfs and The Floor Is Lava.
|
||||
#define TRAIT_ASHSTORM_IMMUNE "ashstorm_immune"
|
||||
#define TRAIT_SNOWSTORM_IMMUNE "snowstorm_immune"
|
||||
#define TRAIT_RADSTORM_IMMUNE "radstorm_immune"
|
||||
#define TRAIT_VOIDSTORM_IMMUNE "voidstorm_immune"
|
||||
#define TRAIT_WEATHER_IMMUNE "weather_immune" //Immune to ALL weather effects.
|
||||
|
||||
//non-mob traits
|
||||
#define TRAIT_PARALYSIS "paralysis" //Used for limb-based paralysis, where replacing the limb will fix it
|
||||
#define VEHICLE_TRAIT "vehicle" // inherited from riding vehicles
|
||||
@@ -298,8 +306,8 @@
|
||||
#define GHOSTROLE_TRAIT "ghostrole"
|
||||
#define APHRO_TRAIT "aphro"
|
||||
#define BLOODSUCKER_TRAIT "bloodsucker"
|
||||
#define SHOES_TRAIT "shoes" //inherited from your sweet kicks
|
||||
#define GLOVE_TRAIT "glove" //inherited by your cool gloves
|
||||
#define SHOES_TRAIT "shoes" //inherited from your sweet kicks
|
||||
#define BOOK_TRAIT "granter (book)" // knowledge is power
|
||||
#define TURF_TRAIT "turf"
|
||||
#define STATION_TRAIT "station-trait"
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Returns used by production machinery
|
||||
// Based on access type that passed the check
|
||||
#define PROTOLOCK_ACCESS_NORMAL 1
|
||||
#define PROTOLOCK_ACCESS_LOWPOP 2
|
||||
#define PROTOLOCK_ACCESS_CAPTAIN 3
|
||||
#define PROTOLOCK_ACCESS_MINERAL 4
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#define isqareen(A) (istype(A, /mob/living/simple_animal/qareen))
|
||||
|
||||
#define isbloodfledge(A) (HAS_TRAIT(A, TRAIT_BLOODFLEDGE))
|
||||
|
||||
// Hyperstation Stuff
|
||||
#define iswendigo(A) (istype(A, /mob/living/carbon/wendigo))
|
||||
|
||||
|
||||
@@ -126,10 +126,15 @@ GLOBAL_LIST_INIT(traits_by_type, list(
|
||||
"TRAIT_EMPATH" = TRAIT_EMPATH,
|
||||
"TRAIT_FRIENDLY" = TRAIT_FRIENDLY,
|
||||
"TRAIT_IWASBATONED" = TRAIT_IWASBATONED,
|
||||
"TRAIT_SALT_SENSITIVE" = TRAIT_SALT_SENSITIVE,
|
||||
"TRAIT_LAVA_IMMUNE" = TRAIT_LAVA_IMMUNE,
|
||||
"TRAIT_ASHSTORM_IMMUNE" = TRAIT_ASHSTORM_IMMUNE,
|
||||
"TRAIT_SNOWSTORM_IMMUNE" = TRAIT_SNOWSTORM_IMMUNE,
|
||||
"TRAIT_VOIDSTORM_IMMUNE" = TRAIT_VOIDSTORM_IMMUNE,
|
||||
"TRAIT_WEATHER_IMMUNE" = TRAIT_WEATHER_IMMUNE,
|
||||
"TRAIT_SPACEWALK" = TRAIT_SPACEWALK,
|
||||
"TRAIT_PRIMITIVE" = TRAIT_PRIMITIVE, //unable to use mechs. Given to Ash Walkers
|
||||
"TRAIT_SALT_SENSITIVE" = TRAIT_SALT_SENSITIVE
|
||||
),
|
||||
),
|
||||
/obj/item/bodypart = list(
|
||||
"TRAIT_PARALYSIS" = TRAIT_PARALYSIS
|
||||
),
|
||||
|
||||
@@ -723,6 +723,9 @@
|
||||
button.maptext_height = 12
|
||||
|
||||
/datum/action/cooldown/IsAvailable(silent = FALSE)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
return next_use_time <= world.time
|
||||
|
||||
/datum/action/cooldown/proc/StartCooldown()
|
||||
|
||||
@@ -64,8 +64,8 @@
|
||||
var/overlay_plane = BLACKNESS_PLANE
|
||||
/// If the weather has no purpose but aesthetics.
|
||||
var/aesthetic = FALSE
|
||||
/// Used by mobs to prevent them from being affected by the weather
|
||||
var/immunity_type = "storm"
|
||||
/// Used by mobs (or movables containing mobs, such as enviro bags) to prevent them from being affected by the weather.
|
||||
var/immunity_type
|
||||
|
||||
/// The stage of the weather, from 1-4
|
||||
var/stage = END_STAGE
|
||||
@@ -133,15 +133,18 @@
|
||||
/datum/weather/proc/start()
|
||||
if(stage >= MAIN_STAGE)
|
||||
return
|
||||
SEND_GLOBAL_SIGNAL(COMSIG_WEATHER_START(type))
|
||||
stage = MAIN_STAGE
|
||||
update_areas()
|
||||
for(var/M in GLOB.player_list)
|
||||
var/turf/mob_turf = get_turf(M)
|
||||
if(mob_turf && (mob_turf.z in impacted_z_levels))
|
||||
for(var/z_level in impacted_z_levels)
|
||||
for(var/mob/player as anything in SSmobs.clients_by_zlevel[z_level])
|
||||
var/turf/mob_turf = get_turf(player)
|
||||
if(!mob_turf)
|
||||
continue
|
||||
if(weather_message)
|
||||
to_chat(M, weather_message)
|
||||
to_chat(player, weather_message)
|
||||
if(weather_sound)
|
||||
SEND_SOUND(M, sound(weather_sound))
|
||||
SEND_SOUND(player, sound(weather_sound))
|
||||
if(!perpetual)
|
||||
addtimer(CALLBACK(src, .proc/wind_down), weather_duration)
|
||||
|
||||
@@ -192,14 +195,27 @@
|
||||
* Returns TRUE if the living mob can be affected by the weather
|
||||
*
|
||||
*/
|
||||
/datum/weather/proc/can_weather_act(mob/living/L)
|
||||
var/turf/mob_turf = get_turf(L)
|
||||
if(mob_turf && !(mob_turf.z in impacted_z_levels))
|
||||
/datum/weather/proc/can_weather_act(mob/living/mob_to_check)
|
||||
var/turf/mob_turf = get_turf(mob_to_check)
|
||||
|
||||
if(!mob_turf)
|
||||
return
|
||||
if(immunity_type in L.weather_immunities)
|
||||
|
||||
if(!(mob_turf.z in impacted_z_levels))
|
||||
return
|
||||
if(!(get_area(L) in impacted_areas))
|
||||
|
||||
if((immunity_type && HAS_TRAIT(mob_to_check, immunity_type)) || HAS_TRAIT(mob_to_check, TRAIT_WEATHER_IMMUNE))
|
||||
return
|
||||
|
||||
var/atom/loc_to_check = mob_to_check.loc
|
||||
while(loc_to_check != mob_turf)
|
||||
if((immunity_type && HAS_TRAIT(loc_to_check, immunity_type)) || HAS_TRAIT(loc_to_check, TRAIT_WEATHER_IMMUNE))
|
||||
return
|
||||
loc_to_check = loc_to_check.loc
|
||||
|
||||
if(!(get_area(mob_to_check) in impacted_areas))
|
||||
return
|
||||
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
//Acid rain is part of the natural weather cycle in the humid forests of Planetstation, and cause acid damage to anyone unprotected.
|
||||
/datum/weather/acid_rain
|
||||
name = "acid rain"
|
||||
desc = "The planet's thunderstorms are by nature acidic, and will incinerate anyone standing beneath them without protection."
|
||||
|
||||
telegraph_duration = 400
|
||||
telegraph_message = "<span class='boldwarning'>Thunder rumbles far above. You hear droplets drumming against the canopy. Seek shelter.</span>"
|
||||
telegraph_sound = 'sound/ambience/acidrain_start.ogg'
|
||||
|
||||
weather_message = "<span class='userdanger'><i>Acidic rain pours down around you! Get inside!</i></span>"
|
||||
weather_overlay = "acid_rain"
|
||||
weather_duration_lower = 600
|
||||
weather_duration_upper = 1500
|
||||
weather_sound = 'sound/ambience/acidrain_mid.ogg'
|
||||
|
||||
end_duration = 100
|
||||
end_message = "<span class='boldannounce'>The downpour gradually slows to a light shower. It should be safe outside now.</span>"
|
||||
end_sound = 'sound/ambience/acidrain_end.ogg'
|
||||
|
||||
area_type = /area
|
||||
protect_indoors = TRUE
|
||||
target_trait = ZTRAIT_ACIDRAIN
|
||||
|
||||
immunity_type = "acid" // temp
|
||||
|
||||
barometer_predictable = TRUE
|
||||
|
||||
|
||||
/datum/weather/acid_rain/weather_act(mob/living/L)
|
||||
var/resist = L.getarmor(null, ACID)
|
||||
if(prob(max(0,100-resist)))
|
||||
L.acid_act(20,20)
|
||||
@@ -1,6 +1,5 @@
|
||||
//A reference to this list is passed into area sound managers, and it's modified in a manner that preserves that reference in ash_storm.dm
|
||||
GLOBAL_LIST_EMPTY(ash_storm_sounds)
|
||||
//Ash storms happen frequently on lavaland. They heavily obscure vision, and cause high fire damage to anyone caught outside.
|
||||
/datum/weather/ash_storm
|
||||
name = "ash storm"
|
||||
desc = "An intense atmospheric storm lifts ash off of the planet's surface and billows it down across the area, dealing intense fire damage to the unprotected."
|
||||
@@ -22,7 +21,7 @@ GLOBAL_LIST_EMPTY(ash_storm_sounds)
|
||||
protect_indoors = TRUE
|
||||
target_trait = ZTRAIT_ASHSTORM
|
||||
|
||||
immunity_type = "ash"
|
||||
immunity_type = TRAIT_ASHSTORM_IMMUNE
|
||||
|
||||
probability = 90
|
||||
|
||||
@@ -72,10 +71,6 @@ GLOBAL_LIST_EMPTY(ash_storm_sounds)
|
||||
var/thermal_protection = H.easy_thermal_protection()
|
||||
if(thermal_protection >= FIRE_IMMUNITY_MAX_TEMP_PROTECT)
|
||||
return TRUE
|
||||
if(isliving(L))// if we're a non immune mob inside an immune mob we have to reconsider if that mob is immune to protect ourselves
|
||||
var/mob/living/the_mob = L
|
||||
if("ash" in the_mob.weather_immunities)
|
||||
return TRUE
|
||||
// if(istype(L, /obj/structure/closet))
|
||||
// var/obj/structure/closet/the_locker = L
|
||||
// if(the_locker.weather_protection)
|
||||
@@ -93,7 +88,6 @@ GLOBAL_LIST_EMPTY(ash_storm_sounds)
|
||||
return
|
||||
L.adjustFireLoss(4)
|
||||
|
||||
|
||||
//Emberfalls are the result of an ash storm passing by close to the playable area of lavaland. They have a 10% chance to trigger in place of an ash storm.
|
||||
/datum/weather/ash_storm/emberfall
|
||||
name = "emberfall"
|
||||
|
||||
@@ -19,19 +19,23 @@
|
||||
target_trait = ZTRAIT_STATION
|
||||
|
||||
overlay_layer = ABOVE_OPEN_TURF_LAYER //Covers floors only
|
||||
immunity_type = "lava"
|
||||
immunity_type = TRAIT_LAVA_IMMUNE
|
||||
|
||||
|
||||
/datum/weather/floor_is_lava/weather_act(mob/living/L)
|
||||
if(issilicon(L))
|
||||
return
|
||||
if(istype(L.buckled, /obj/structure/bed))
|
||||
return
|
||||
for(var/obj/structure/O in L.loc)
|
||||
if(O.density)
|
||||
return
|
||||
if(L.loc.density)
|
||||
return
|
||||
if(!L.client) //Only sentient people are going along with it!
|
||||
return
|
||||
L.adjustFireLoss(3)
|
||||
/datum/weather/floor_is_lava/can_weather_act(mob/living/mob_to_check)
|
||||
if(!mob_to_check.client) //Only sentient people are going along with it!
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(!. || issilicon(mob_to_check) || istype(mob_to_check.buckled, /obj/structure/bed))
|
||||
return FALSE
|
||||
var/turf/mob_turf = get_turf(mob_to_check)
|
||||
if(mob_turf.density) //Walls are not floors.
|
||||
return FALSE
|
||||
for(var/obj/structure/structure_to_check in mob_turf)
|
||||
if(structure_to_check.density)
|
||||
return FALSE
|
||||
if(mob_to_check.movement_type & FLYING)
|
||||
return FALSE
|
||||
|
||||
/datum/weather/floor_is_lava/weather_act(mob/living/victim)
|
||||
victim.adjustFireLoss(3)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
protected_areas = list(/area/edina/protected)
|
||||
target_trait = ZTRAIT_ICESTORM
|
||||
|
||||
immunity_type = "rad"
|
||||
immunity_type = TRAIT_SNOWSTORM_IMMUNE
|
||||
|
||||
/datum/weather/ice_storm/weather_act(mob/living/L)
|
||||
//L.adjust_bodytemperature(-rand(10,20))
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
/area/ai_monitored/turret_protected/ai, /area/commons/storage/emergency/starboard, /area/commons/storage/emergency/port, /area/shuttle, /area/ruin/lavaland, /area/commons/dorms)
|
||||
target_trait = ZTRAIT_STATION
|
||||
|
||||
immunity_type = "rad"
|
||||
immunity_type = TRAIT_RADSTORM_IMMUNE
|
||||
|
||||
var/radiation_intensity = 100
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
protect_indoors = TRUE
|
||||
target_trait = ZTRAIT_SNOWSTORM
|
||||
|
||||
immunity_type = "snow"
|
||||
immunity_type = TRAIT_SNOWSTORM_IMMUNE
|
||||
|
||||
barometer_predictable = TRUE
|
||||
|
||||
|
||||
@@ -17,15 +17,18 @@
|
||||
protect_indoors = FALSE
|
||||
target_trait = ZTRAIT_VOIDSTORM
|
||||
|
||||
immunity_type = "void"
|
||||
immunity_type = TRAIT_VOIDSTORM_IMMUNE
|
||||
|
||||
barometer_predictable = FALSE
|
||||
perpetual = TRUE
|
||||
|
||||
/datum/weather/void_storm/weather_act(mob/living/L)
|
||||
if(IS_HERETIC(L) || IS_HERETIC_MONSTER(L))
|
||||
return
|
||||
L.adjustOxyLoss(rand(1,3))
|
||||
L.adjustFireLoss(rand(1,3))
|
||||
L.adjust_blurriness(rand(0,1))
|
||||
L.adjust_bodytemperature(-rand(5,15))
|
||||
/datum/weather/void_storm/can_weather_act(mob/living/mob_to_check)
|
||||
. = ..()
|
||||
if(IS_HERETIC(mob_to_check) || IS_HERETIC_MONSTER(mob_to_check))
|
||||
return FALSE
|
||||
|
||||
/datum/weather/void_storm/weather_act(mob/living/victim)
|
||||
victim.adjustOxyLoss(rand(1,3))
|
||||
victim.adjustFireLoss(rand(1,3))
|
||||
victim.adjust_blurriness(rand(0,1))
|
||||
victim.adjust_bodytemperature(-rand(5,15))
|
||||
|
||||
@@ -325,12 +325,12 @@ as performing this in action() will cause the upgrade to end up in the borg inst
|
||||
/obj/item/borg/upgrade/lavaproof/action(mob/living/silicon/robot/R, user = usr)
|
||||
. = ..()
|
||||
if(.)
|
||||
R.weather_immunities += "lava"
|
||||
ADD_TRAIT(src, TRAIT_LAVA_IMMUNE, type)
|
||||
|
||||
/obj/item/borg/upgrade/lavaproof/deactivate(mob/living/silicon/robot/R, user = usr)
|
||||
. = ..()
|
||||
if (.)
|
||||
R.weather_immunities -= "lava"
|
||||
REMOVE_TRAIT(src, TRAIT_LAVA_IMMUNE, type)
|
||||
|
||||
/obj/item/borg/upgrade/selfrepair
|
||||
name = "self-repair module"
|
||||
|
||||
@@ -17,6 +17,16 @@
|
||||
barefootstep = FOOTSTEP_LAVA
|
||||
clawfootstep = FOOTSTEP_LAVA
|
||||
heavyfootstep = FOOTSTEP_LAVA
|
||||
/// How much fire damage we deal to living mobs stepping on us
|
||||
var/lava_damage = 20
|
||||
/// How many firestacks we add to living mobs stepping on us
|
||||
var/lava_firestacks = 20
|
||||
/// How much temperature we expose objects with
|
||||
var/temperature_damage = 10000
|
||||
/// mobs with this trait won't burn.
|
||||
var/immunity_trait = TRAIT_LAVA_IMMUNE
|
||||
/// objects with these flags won't burn.
|
||||
var/immunity_resistance_flags = LAVA_PROOF
|
||||
|
||||
/turf/open/lava/ex_act(severity, target, origin)
|
||||
contents_explosion(severity, target, origin)
|
||||
@@ -107,62 +117,98 @@
|
||||
LAZYREMOVE(found_safeties, S)
|
||||
return LAZYLEN(found_safeties)
|
||||
|
||||
///Generic return value of the can_burn_stuff() proc. Does nothing.
|
||||
#define LAVA_BE_IGNORING 0
|
||||
/// Another. Won't burn the target but will make the turf start processing.
|
||||
#define LAVA_BE_PROCESSING 1
|
||||
/// Burns the target and makes the turf process (depending on the return value of do_burn()).
|
||||
#define LAVA_BE_BURNING 2
|
||||
|
||||
/turf/open/lava/proc/burn_stuff(AM)
|
||||
. = 0
|
||||
///Proc that sets on fire something or everything on the turf that's not immune to lava. Returns TRUE to make the turf start processing.
|
||||
/turf/open/lava/proc/burn_stuff(atom/movable/to_burn, delta_time = 1)
|
||||
|
||||
if(is_safe())
|
||||
return FALSE
|
||||
|
||||
var/thing_to_check = src
|
||||
if (AM)
|
||||
thing_to_check = list(AM)
|
||||
for(var/thing in thing_to_check)
|
||||
if(isobj(thing))
|
||||
var/obj/O = thing
|
||||
if((O.resistance_flags & (LAVA_PROOF|INDESTRUCTIBLE)) || O.throwing)
|
||||
if (to_burn)
|
||||
thing_to_check = list(to_burn)
|
||||
for(var/atom/movable/burn_target as anything in thing_to_check)
|
||||
switch(can_burn_stuff(burn_target))
|
||||
if(LAVA_BE_IGNORING)
|
||||
continue
|
||||
. = 1
|
||||
if((O.resistance_flags & (ON_FIRE)))
|
||||
continue
|
||||
if(!(O.resistance_flags & FLAMMABLE))
|
||||
O.resistance_flags |= FLAMMABLE //Even fireproof things burn up in lava
|
||||
if(O.resistance_flags & FIRE_PROOF)
|
||||
O.resistance_flags &= ~FIRE_PROOF
|
||||
if(O.armor.fire > 50) //obj with 100% fire armor still get slowly burned away.
|
||||
O.armor = O.armor.setRating(fire = 50)
|
||||
O.fire_act(10000, 1000)
|
||||
|
||||
else if (isliving(thing))
|
||||
. = 1
|
||||
var/mob/living/L = thing
|
||||
if(L.movement_type & FLYING)
|
||||
continue //YOU'RE FLYING OVER IT
|
||||
if("lava" in L.weather_immunities)
|
||||
continue
|
||||
var/buckle_check = L.buckling
|
||||
if(!buckle_check)
|
||||
buckle_check = L.buckled
|
||||
if(isobj(buckle_check))
|
||||
var/obj/O = buckle_check
|
||||
if(O.resistance_flags & LAVA_PROOF)
|
||||
if(LAVA_BE_BURNING)
|
||||
if(!do_burn(burn_target, delta_time))
|
||||
continue
|
||||
else if(isliving(buckle_check))
|
||||
var/mob/living/live = buckle_check
|
||||
if("lava" in live.weather_immunities)
|
||||
continue
|
||||
if(iscarbon(L))
|
||||
var/mob/living/carbon/C = L
|
||||
var/obj/item/clothing/S = C.get_item_by_slot(ITEM_SLOT_OCLOTHING)
|
||||
var/obj/item/clothing/H = C.get_item_by_slot(ITEM_SLOT_HEAD)
|
||||
. = TRUE
|
||||
|
||||
if(S && H && S.clothing_flags & LAVAPROTECT && H.clothing_flags & LAVAPROTECT)
|
||||
return
|
||||
/turf/open/lava/proc/can_burn_stuff(atom/movable/burn_target)
|
||||
if(burn_target.movement_type & (FLYING|FLOATING)) //you're flying over it.
|
||||
return isliving(burn_target) ? LAVA_BE_PROCESSING : LAVA_BE_IGNORING
|
||||
|
||||
L.adjustFireLoss(20)
|
||||
if(L) //mobs turning into object corpses could get deleted here.
|
||||
L.adjust_fire_stacks(20)
|
||||
L.IgniteMob()
|
||||
if(isobj(burn_target))
|
||||
if(burn_target.throwing) // to avoid gulag prisoners easily escaping, throwing only works for objects.
|
||||
return LAVA_BE_IGNORING
|
||||
var/obj/burn_obj = burn_target
|
||||
if((burn_obj.resistance_flags & immunity_resistance_flags))
|
||||
return LAVA_BE_PROCESSING
|
||||
return LAVA_BE_BURNING
|
||||
|
||||
if (!isliving(burn_target))
|
||||
return LAVA_BE_IGNORING
|
||||
|
||||
if(HAS_TRAIT(burn_target, immunity_trait))
|
||||
return LAVA_BE_PROCESSING
|
||||
var/mob/living/burn_living = burn_target
|
||||
var/atom/movable/burn_buckled = burn_living.buckled
|
||||
if(burn_buckled)
|
||||
if(burn_buckled.movement_type & (FLYING|FLOATING))
|
||||
return LAVA_BE_PROCESSING
|
||||
if(isobj(burn_buckled))
|
||||
var/obj/burn_buckled_obj = burn_buckled
|
||||
if(burn_buckled_obj.resistance_flags & immunity_resistance_flags)
|
||||
return LAVA_BE_PROCESSING
|
||||
else if(HAS_TRAIT(burn_buckled, immunity_trait))
|
||||
return LAVA_BE_PROCESSING
|
||||
|
||||
if(iscarbon(burn_living))
|
||||
var/mob/living/carbon/burn_carbon = burn_living
|
||||
var/obj/item/clothing/burn_suit = burn_carbon.get_item_by_slot(ITEM_SLOT_OCLOTHING)
|
||||
var/obj/item/clothing/burn_helmet = burn_carbon.get_item_by_slot(ITEM_SLOT_HEAD)
|
||||
if(burn_suit?.clothing_flags & LAVAPROTECT && burn_helmet?.clothing_flags & LAVAPROTECT)
|
||||
return LAVA_BE_PROCESSING
|
||||
|
||||
return LAVA_BE_BURNING
|
||||
|
||||
#undef LAVA_BE_IGNORING
|
||||
#undef LAVA_BE_PROCESSING
|
||||
#undef LAVA_BE_BURNING
|
||||
|
||||
/turf/open/lava/proc/do_burn(atom/movable/burn_target, delta_time = 1)
|
||||
. = TRUE
|
||||
if(isobj(burn_target))
|
||||
var/obj/burn_obj = burn_target
|
||||
if(burn_obj.resistance_flags & ON_FIRE) // already on fire; skip it.
|
||||
return
|
||||
if(!(burn_obj.resistance_flags & FLAMMABLE))
|
||||
burn_obj.resistance_flags |= FLAMMABLE //Even fireproof things burn up in lava
|
||||
if(burn_obj.resistance_flags & FIRE_PROOF)
|
||||
burn_obj.resistance_flags &= ~FIRE_PROOF
|
||||
if(burn_obj.armor.fire > 50) //obj with 100% fire armor still get slowly burned away.
|
||||
burn_obj.armor = burn_obj.armor.setRating(fire = 50)
|
||||
burn_obj.fire_act(temperature_damage, 1000 * delta_time)
|
||||
if(istype(burn_obj, /obj/structure/closet))
|
||||
var/obj/structure/closet/burn_closet = burn_obj
|
||||
for(var/burn_content in burn_closet.contents)
|
||||
burn_stuff(burn_content)
|
||||
|
||||
var/mob/living/burn_living = burn_target
|
||||
burn_living.update_fire()
|
||||
|
||||
burn_living.adjustFireLoss(lava_damage * delta_time)
|
||||
if(!QDELETED(burn_living)) //mobs turning into object corpses could get deleted here.
|
||||
burn_living.adjust_fire_stacks(lava_firestacks * delta_time)
|
||||
burn_living.IgniteMob()
|
||||
|
||||
/turf/open/lava/smooth
|
||||
name = "lava"
|
||||
|
||||
@@ -164,11 +164,12 @@
|
||||
icon_state = "liquidplasma"
|
||||
initial_gas_mix = "n2=82;plasma=24;TEMP=120"
|
||||
baseturfs = /turf/open/lava/plasma
|
||||
slowdown = 2
|
||||
|
||||
light_range = 3
|
||||
light_power = 0.75
|
||||
light_color = LIGHT_COLOR_PURPLE
|
||||
immunity_trait = TRAIT_SNOWSTORM_IMMUNE
|
||||
immunity_resistance_flags = FREEZE_PROOF
|
||||
|
||||
/turf/open/lava/plasma/attackby(obj/item/I, mob/user, params)
|
||||
var/obj/item/reagent_containers/glass/C = I
|
||||
@@ -178,78 +179,45 @@
|
||||
C.reagents.add_reagent(/datum/reagent/toxin/plasma, rand(5, 10))
|
||||
user.visible_message("[user] scoops some plasma from the [src] with \the [C].", "<span class='notice'>You scoop out some plasma from the [src] using \the [C].</span>")
|
||||
|
||||
/turf/open/lava/plasma/burn_stuff(AM)
|
||||
. = 0
|
||||
/turf/open/lava/plasma/do_burn(atom/movable/burn_target, delta_time = 1)
|
||||
. = TRUE
|
||||
if(isobj(burn_target))
|
||||
return FALSE // Does nothing against objects. Old code.
|
||||
|
||||
if(is_safe())
|
||||
return FALSE
|
||||
var/mob/living/burn_living = burn_target
|
||||
burn_living.adjustFireLoss(2)
|
||||
if(QDELETED(burn_living))
|
||||
return
|
||||
burn_living.adjust_fire_stacks(20) //dipping into a stream of plasma would probably make you more flammable than usual
|
||||
burn_living.adjust_bodytemperature(-rand(50,65)) //its cold, man
|
||||
if(!ishuman(burn_living) || DT_PROB(65, delta_time))
|
||||
return
|
||||
var/mob/living/carbon/human/burn_human = burn_living
|
||||
var/datum/species/burn_species = burn_human.dna.species
|
||||
if(istype(burn_species, /datum/species/plasmaman) || istype(burn_species, /datum/species/android) || istype(burn_species, /datum/species/synth)) //ignore plasmamen/robotic species
|
||||
return
|
||||
|
||||
var/thing_to_check = src
|
||||
if (AM)
|
||||
thing_to_check = list(AM)
|
||||
for(var/thing in thing_to_check)
|
||||
if(isobj(thing))
|
||||
var/obj/O = thing
|
||||
if((O.resistance_flags & (FREEZE_PROOF)) || O.throwing)
|
||||
continue
|
||||
|
||||
else if (isliving(thing))
|
||||
. = 1
|
||||
var/mob/living/L = thing
|
||||
if(L.movement_type & FLYING)
|
||||
continue //YOU'RE FLYING OVER IT
|
||||
if("snow" in L.weather_immunities)
|
||||
continue
|
||||
|
||||
var/buckle_check = L.buckling
|
||||
if(!buckle_check)
|
||||
buckle_check = L.buckled
|
||||
if(isobj(buckle_check))
|
||||
var/obj/O = buckle_check
|
||||
if(O.resistance_flags & FREEZE_PROOF)
|
||||
continue
|
||||
|
||||
else if(isliving(buckle_check))
|
||||
var/mob/living/live = buckle_check
|
||||
if("snow" in live.weather_immunities)
|
||||
continue
|
||||
|
||||
L.adjustFireLoss(2)
|
||||
if(L)
|
||||
L.adjust_fire_stacks(20) //dipping into a stream of plasma would probably make you more flammable than usual
|
||||
L.adjust_bodytemperature(-rand(50,65)) //its cold, man
|
||||
if(ishuman(L))//are they a carbon?
|
||||
var/list/plasma_parts = list()//a list of the organic parts to be turned into plasma limbs
|
||||
var/list/robo_parts = list()//keep a reference of robotic parts so we know if we can turn them into a plasmaman
|
||||
var/mob/living/carbon/human/PP = L
|
||||
var/S = PP.dna.species
|
||||
if(istype(S, /datum/species/plasmaman) || istype(S, /datum/species/android) || istype(S, /datum/species/synth)) //ignore plasmamen/robotic species
|
||||
continue
|
||||
|
||||
for(var/BP in PP.bodyparts)
|
||||
var/obj/item/bodypart/NN = BP
|
||||
if(NN.is_organic_limb() && NN.species_id != "plasmaman") //getting every organic, non-plasmaman limb (augments/androids are immune to this)
|
||||
plasma_parts += NN
|
||||
if(NN.is_robotic_limb(FALSE))
|
||||
robo_parts += NN
|
||||
|
||||
if(prob(35)) //checking if the delay is over & if the victim actually has any parts to nom
|
||||
PP.adjustToxLoss(15)
|
||||
PP.adjustFireLoss(25)
|
||||
if(plasma_parts.len)
|
||||
var/obj/item/bodypart/NB = pick(plasma_parts) //using the above-mentioned list to get a choice of limbs for dismember() to use
|
||||
PP.emote("scream")
|
||||
NB.species_id = "plasmaman"//change the species_id of the limb to that of a plasmaman
|
||||
NB.no_update = TRUE
|
||||
NB.change_bodypart_status()
|
||||
PP.visible_message("<span class='warning'>[L] screams in pain as [L.p_their()] [NB] melts down to the bone!</span>", \
|
||||
"<span class='userdanger'>You scream out in pain as your [NB] melts down to the bone, leaving an eerie plasma-like glow where flesh used to be!</span>")
|
||||
if(!plasma_parts.len && !robo_parts.len) //a person with no potential organic limbs left AND no robotic limbs, time to turn them into a plasmaman
|
||||
PP.IgniteMob()
|
||||
PP.set_species(/datum/species/plasmaman)
|
||||
PP.visible_message("<span class='warning'>[L] bursts into a brilliant purple flame as [L.p_their()] entire body is that of a skeleton!</span>", \
|
||||
"<span class='userdanger'>Your senses numb as all of your remaining flesh is turned into a purple slurry, sloshing off your body and leaving only your bones to show in a vibrant purple!</span>")
|
||||
var/list/plasma_parts = list()//a list of the organic parts to be turned into plasma limbs
|
||||
var/list/robo_parts = list()//keep a reference of robotic parts so we know if we can turn them into a plasmaman
|
||||
for(var/obj/item/bodypart/burn_limb as anything in burn_human.bodyparts)
|
||||
if(burn_limb.status == BODYPART_ORGANIC && burn_limb.species_id != SPECIES_PLASMAMAN) //getting every organic, non-plasmaman limb (augments/androids are immune to this)
|
||||
plasma_parts += burn_limb
|
||||
if(burn_limb.status == BODYPART_ROBOTIC)
|
||||
robo_parts += burn_limb
|
||||
|
||||
burn_human.adjustToxLoss(15)
|
||||
burn_human.adjustFireLoss(25)
|
||||
if(plasma_parts.len)
|
||||
var/obj/item/bodypart/burn_limb = pick(plasma_parts) //using the above-mentioned list to get a choice of limbs
|
||||
burn_human.emote("scream")
|
||||
burn_human.update_body_parts()
|
||||
burn_human.visible_message(span_warning("[burn_human] screams in pain as [burn_human.p_their()] [burn_limb] melts down to the bone!"), \
|
||||
span_userdanger("You scream out in pain as your [burn_limb] melts down to the bone, leaving an eerie plasma-like glow where flesh used to be!"))
|
||||
if(!plasma_parts.len && !robo_parts.len) //a person with no potential organic limbs left AND no robotic limbs, time to turn them into a plasmaman
|
||||
burn_human.IgniteMob()
|
||||
burn_human.set_species(/datum/species/plasmaman)
|
||||
burn_human.visible_message(span_warning("[burn_human] bursts into a brilliant purple flame as [burn_human.p_their()] entire body is that of a skeleton!"), \
|
||||
span_userdanger("Your senses numb as all of your remaining flesh is turned into a purple slurry, sloshing off your body and leaving only your bones to show in a vibrant purple!"))
|
||||
|
||||
/obj/vehicle/ridden/lavaboat/plasma
|
||||
name = "plasma boat"
|
||||
|
||||
@@ -156,38 +156,48 @@
|
||||
M.appearance_flags = RESET_COLOR
|
||||
. += M
|
||||
|
||||
/****************HEVA Suit and Mask****************/
|
||||
|
||||
// CITADEL ADDITIONS BELOW
|
||||
|
||||
/****************SEVA Suit and Mask****************/
|
||||
|
||||
/obj/item/clothing/suit/hooded/explorer/seva
|
||||
name = "SEVA Suit"
|
||||
desc = "A fire-proof suit for exploring hot environments. Its design and material make it easier for a Goliath to keep their grip on the wearer."
|
||||
icon_state = "seva"
|
||||
item_state = "seva"
|
||||
/obj/item/clothing/suit/hooded/explorer/heva
|
||||
name = "HEVA suit"
|
||||
desc = "The Hazardous Environments extra-Vehicular Activity suit, developed by WanTon & Sons Perilous Mining and sold to Nanotrasen for missions within inhospitable, mineral-rich zones. \
|
||||
Its sleek plating deflects most biological - radioactive - and chemical substances and materials. Most notably, this will negate the effects of ash storms and give goliaths better grip against you."
|
||||
icon_state = "heva"
|
||||
item_state = "heva"
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
|
||||
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
hoodtype = /obj/item/clothing/head/hooded/explorer/seva
|
||||
armor = list(MELEE = 15, BULLET = 10, LASER = 10, ENERGY = 10, BOMB = 35, BIO = 50, RAD = 25, FIRE = 100, ACID = 25)
|
||||
hoodtype = /obj/item/clothing/head/hooded/explorer/heva
|
||||
armor = list(MELEE = 20, BULLET = 20, LASER = 20, ENERGY = 20, BOMB = 20, BIO = 100, RAD = 80, FIRE = 100, ACID = 80)
|
||||
resistance_flags = FIRE_PROOF | GOLIATH_WEAKNESS
|
||||
|
||||
/obj/item/clothing/head/hooded/explorer/seva
|
||||
name = "SEVA Hood"
|
||||
desc = "A fire-proof hood for exploring hot environments. Its design and material make it easier for a Goliath to keep their grip on the wearer."
|
||||
icon_state = "seva"
|
||||
item_state = "seva"
|
||||
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
|
||||
armor = list(MELEE = 10, BULLET = 10, LASER = 10, ENERGY = 10, BOMB = 35, BIO = 50, RAD = 25, FIRE = 100, ACID = 25)
|
||||
/obj/item/clothing/head/hooded/explorer/heva
|
||||
name = "HEVA hood"
|
||||
desc = "The Hazardous Environments extra-Vehiclar Activity hood, developed by WanTon & Sons Perilous Mining. \
|
||||
Its sleek plating deflects most biological - radioactive - and chemical substances and materials. An instructive tag dictates that the provided mask is required for full protection."
|
||||
icon_state = "heva"
|
||||
item_state = "heva"
|
||||
flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH
|
||||
armor = list(MELEE = 20, BULLET = 20, LASER = 20, ENERGY = 20, BOMB = 20, BIO = 100, RAD = 20, FIRE = 60, ACID = 20)
|
||||
resistance_flags = FIRE_PROOF | GOLIATH_WEAKNESS
|
||||
|
||||
/obj/item/clothing/mask/gas/seva
|
||||
name = "SEVA Mask"
|
||||
desc = "A face-covering plate that can be connected to an air supply. Intended for use with the SEVA Suit."
|
||||
icon_state = "seva"
|
||||
item_state = "seva"
|
||||
resistance_flags = FIRE_PROOF
|
||||
/obj/item/clothing/head/hooded/explorer/heva/equipped(mob/living/carbon/human/user, slot)
|
||||
..()
|
||||
if (slot == ITEM_SLOT_HEAD)
|
||||
ADD_TRAIT(user, TRAIT_ASHSTORM_IMMUNE, "heva_suit")
|
||||
|
||||
/obj/item/clothing/head/hooded/explorer/heva/dropped(mob/living/carbon/human/user)
|
||||
..()
|
||||
if (HAS_TRAIT_FROM(user, TRAIT_ASHSTORM_IMMUNE, "heva_suit"))
|
||||
REMOVE_TRAIT(user, TRAIT_ASHSTORM_IMMUNE, "heva_suit")
|
||||
|
||||
/obj/item/clothing/mask/gas/heva
|
||||
name = "HEVA mask"
|
||||
desc = "The Hazardous Environments extra-Vehiclar Activity mask, developed by WanTon & Sons Perilous Mining. \
|
||||
Its sleek plating deflects most biological - radioactive - and chemical substances and materials. An instructive tag dictates that the provided protective attire is required for full protection."
|
||||
icon_state = "heva"
|
||||
item_state = "heva"
|
||||
flags_inv = HIDEFACIALHAIR|HIDEFACE|HIDEEYES|HIDEEARS|HIDEHAIR
|
||||
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 60, FIRE = 40, ACID = 50)
|
||||
|
||||
/****************Exo-Suit and Mask****************/
|
||||
|
||||
|
||||
@@ -1043,7 +1043,7 @@
|
||||
user.mind.AddSpell(D)
|
||||
if(4)
|
||||
to_chat(user, "<span class='danger'>You feel like you could walk straight through lava now.</span>")
|
||||
H.weather_immunities |= "lava"
|
||||
ADD_TRAIT(user, TRAIT_LAVA_IMMUNE, type)
|
||||
|
||||
playsound(user.loc,'sound/items/drink.ogg', rand(10,50), 1)
|
||||
qdel(src)
|
||||
|
||||
@@ -236,7 +236,7 @@
|
||||
|
||||
/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"))
|
||||
"HEVA suit" = image(icon = 'icons/obj/clothing/suits.dmi', icon_state = "heva"))
|
||||
|
||||
var/selection = show_radial_menu(redeemer, src, items, require_near = TRUE, tooltips = TRUE)
|
||||
if(!selection || !Adjacent(redeemer) || QDELETED(voucher) || voucher.loc != redeemer)
|
||||
@@ -246,9 +246,9 @@
|
||||
if("Exo-suit")
|
||||
new /obj/item/clothing/suit/hooded/explorer/exo(drop_location)
|
||||
new /obj/item/clothing/mask/gas/exo(drop_location)
|
||||
if("SEVA suit")
|
||||
new /obj/item/clothing/suit/hooded/explorer/seva(drop_location)
|
||||
new /obj/item/clothing/mask/gas/seva(drop_location)
|
||||
if("HEVA suit")
|
||||
new /obj/item/clothing/suit/hooded/explorer/heva(drop_location)
|
||||
new /obj/item/clothing/mask/gas/heva(drop_location)
|
||||
playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3)
|
||||
SSblackbox.record_feedback("tally", "suit_voucher_redeemed", 1, selection)
|
||||
qdel(voucher)
|
||||
|
||||
@@ -48,13 +48,11 @@
|
||||
|
||||
else //Maybe uses plasma in the future, although that wouldn't make any sense...
|
||||
leaping = 1
|
||||
weather_immunities += "lava"
|
||||
update_icons()
|
||||
throw_at(A, MAX_ALIEN_LEAP_DIST, 1, src, FALSE, TRUE, callback = CALLBACK(src, .proc/leap_end))
|
||||
|
||||
/mob/living/carbon/alien/humanoid/hunter/proc/leap_end()
|
||||
leaping = 0
|
||||
weather_immunities -= "lava"
|
||||
update_icons()
|
||||
|
||||
/mob/living/carbon/alien/humanoid/hunter/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
|
||||
|
||||
@@ -214,11 +214,11 @@
|
||||
|
||||
/datum/species/golem/titanium/on_species_gain(mob/living/carbon/C, datum/species/old_species)
|
||||
. = ..()
|
||||
C.weather_immunities |= "ash"
|
||||
ADD_TRAIT(C, TRAIT_ASHSTORM_IMMUNE, SPECIES_TRAIT)
|
||||
|
||||
/datum/species/golem/titanium/on_species_loss(mob/living/carbon/C)
|
||||
. = ..()
|
||||
C.weather_immunities -= "ash"
|
||||
REMOVE_TRAIT(C, TRAIT_ASHSTORM_IMMUNE, SPECIES_TRAIT)
|
||||
|
||||
//Immune to ash storms and lava
|
||||
/datum/species/golem/plastitanium
|
||||
@@ -233,13 +233,13 @@
|
||||
|
||||
/datum/species/golem/plastitanium/on_species_gain(mob/living/carbon/C, datum/species/old_species)
|
||||
. = ..()
|
||||
C.weather_immunities |= "lava"
|
||||
C.weather_immunities |= "ash"
|
||||
ADD_TRAIT(C, TRAIT_LAVA_IMMUNE, SPECIES_TRAIT)
|
||||
ADD_TRAIT(C, TRAIT_ASHSTORM_IMMUNE, SPECIES_TRAIT)
|
||||
|
||||
/datum/species/golem/plastitanium/on_species_loss(mob/living/carbon/C)
|
||||
. = ..()
|
||||
C.weather_immunities -= "ash"
|
||||
C.weather_immunities -= "lava"
|
||||
REMOVE_TRAIT(C, TRAIT_LAVA_IMMUNE, SPECIES_TRAIT)
|
||||
REMOVE_TRAIT(C, TRAIT_ASHSTORM_IMMUNE, SPECIES_TRAIT)
|
||||
|
||||
//Fast and regenerates... but can only speak like an abductor
|
||||
/datum/species/golem/alloy
|
||||
|
||||
@@ -107,8 +107,6 @@
|
||||
|
||||
var/hellbound = 0 //People who've signed infernal contracts are unrevivable.
|
||||
|
||||
var/list/weather_immunities = list()
|
||||
|
||||
var/stun_absorption = null //converted to a list of stun absorption sources this mob has when one is added
|
||||
|
||||
var/blood_volume = 0 //how much blood the mob has
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
pass_flags = PASSTABLE | PASSMOB
|
||||
mob_size = MOB_SIZE_TINY
|
||||
desc = "A generic pAI mobile hard-light holographics emitter. It seems to be deactivated."
|
||||
weather_immunities = list("ash")
|
||||
health = 500
|
||||
maxHealth = 500
|
||||
layer = BELOW_MOB_LAYER
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
initial_language_holder = /datum/language_holder/synthetic
|
||||
see_in_dark = 8
|
||||
bubble_icon = "machine"
|
||||
weather_immunities = list("ash")
|
||||
possible_a_intents = list(INTENT_HELP, INTENT_HARM)
|
||||
mob_biotypes = MOB_ROBOTIC
|
||||
rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE
|
||||
@@ -60,6 +59,7 @@
|
||||
diag_hud.add_to_hud(src)
|
||||
diag_hud_set_status()
|
||||
diag_hud_set_health()
|
||||
ADD_TRAIT(src, TRAIT_ASHSTORM_IMMUNE, ROUNDSTART_TRAIT)
|
||||
|
||||
/mob/living/silicon/ComponentInitialize()
|
||||
. = ..()
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
icon_living = "snowbear"
|
||||
icon_dead = "snowbear_dead"
|
||||
desc = "It's a polar bear, in space, but not actually in space."
|
||||
weather_immunities = list("snow")
|
||||
weather_immunities = list(TRAIT_SNOWSTORM_IMMUNE)
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/russian
|
||||
name = "combat bear"
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
gold_core_spawnable = HOSTILE_SPAWN
|
||||
faction = list(ROLE_WIZARD)
|
||||
footstep_type = FOOTSTEP_MOB_SHOE
|
||||
weather_immunities = list("lava","ash")
|
||||
weather_immunities = list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE)
|
||||
minbodytemp = 0
|
||||
maxbodytemp = INFINITY
|
||||
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
||||
|
||||
@@ -16,7 +16,7 @@ Difficulty: Extremely Hard
|
||||
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
|
||||
light_color = "#E4C7C5"
|
||||
movement_type = GROUND
|
||||
weather_immunities = list("snow")
|
||||
weather_immunities = list(TRAIT_SNOWSTORM_IMMUNE)
|
||||
speak_emote = list("roars")
|
||||
armour_penetration = 100
|
||||
melee_damage_lower = 10
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
obj_damage = 400
|
||||
light_range = 3
|
||||
faction = list("mining", "boss")
|
||||
weather_immunities = list("lava","ash")
|
||||
weather_immunities = list(TRAIT_LAVA_IMMUNE,TRAIT_ASHSTORM_IMMUNE)
|
||||
movement_type = FLYING
|
||||
robust_searching = 1
|
||||
ranged_ignores_vision = TRUE
|
||||
|
||||
@@ -52,7 +52,7 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa
|
||||
crusher_achievement_type = /datum/award/achievement/boss/swarmer_beacon_crusher
|
||||
score_achievement_type = /datum/award/score/swarmer_beacon_score
|
||||
faction = list("mining", "boss", "swarmer")
|
||||
weather_immunities = list("lava","ash")
|
||||
weather_immunities = list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE)
|
||||
stop_automated_movement = TRUE
|
||||
wander = FALSE
|
||||
layer = BELOW_MOB_LAYER
|
||||
@@ -101,7 +101,7 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa
|
||||
/mob/living/simple_animal/hostile/swarmer/ai
|
||||
wander = 1
|
||||
faction = list("swarmer", "mining")
|
||||
weather_immunities = list("ash") //wouldn't be fun otherwise
|
||||
weather_immunities = list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE) //wouldn't be fun otherwise
|
||||
AIStatus = AI_ON
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/Initialize(mapload)
|
||||
|
||||
@@ -14,7 +14,7 @@ Difficulty: Hard
|
||||
attack_verb_continuous = "claws"
|
||||
attack_verb_simple = "claw"
|
||||
attack_sound = 'sound/magic/demon_attack1.ogg'
|
||||
weather_immunities = list("snow")
|
||||
weather_immunities = list(TRAIT_SNOWSTORM_IMMUNE)
|
||||
speak_emote = list("roars")
|
||||
armour_penetration = 40
|
||||
melee_damage_lower = 40
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
speak_emote = list("warbles", "quavers")
|
||||
emote_hear = list("trills.")
|
||||
emote_see = list("sniffs.", "burps.")
|
||||
weather_immunities = list("lava","ash")
|
||||
weather_immunities = list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE)
|
||||
faction = list("mining", "ashwalker")
|
||||
density = FALSE
|
||||
speak_chance = 1
|
||||
|
||||
@@ -272,7 +272,7 @@
|
||||
aggro_vision_range = 9
|
||||
speed = 3
|
||||
faction = list("mining")
|
||||
weather_immunities = list("lava","ash")
|
||||
weather_immunities = list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE)
|
||||
obj_damage = 30
|
||||
environment_smash = ENVIRONMENT_SMASH_STRUCTURES
|
||||
see_in_dark = 8
|
||||
@@ -321,7 +321,7 @@
|
||||
gloves = /obj/item/clothing/gloves/color/black
|
||||
mask = /obj/item/clothing/mask/gas/explorer
|
||||
if(prob(20))
|
||||
suit = pickweight(list(/obj/item/clothing/suit/hooded/explorer/standard = 6, /obj/item/clothing/suit/hooded/cloak/goliath = 2, /obj/item/clothing/suit/hooded/explorer/exo = 6, /obj/item/clothing/suit/hooded/explorer/seva = 6))
|
||||
suit = pickweight(list(/obj/item/clothing/suit/hooded/explorer/standard = 6, /obj/item/clothing/suit/hooded/cloak/goliath = 2, /obj/item/clothing/suit/hooded/explorer/exo = 6, /obj/item/clothing/suit/hooded/explorer/heva = 6))
|
||||
if(prob(30))
|
||||
r_pocket = pickweight(list(/obj/item/stack/marker_beacon = 20, /obj/item/stack/spacecash/c1000 = 7, /obj/item/reagent_containers/hypospray/medipen/survival = 2, /obj/item/borg/upgrade/modkit/damage = 1 ))
|
||||
if(prob(10))
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
vision_range = 2
|
||||
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
||||
faction = list("mining")
|
||||
weather_immunities = list("lava","ash")
|
||||
weather_immunities = list(TRAIT_LAVA_IMMUNE,TRAIT_ASHSTORM_IMMUNE)
|
||||
obj_damage = 30
|
||||
environment_smash = ENVIRONMENT_SMASH_WALLS
|
||||
minbodytemp = 0
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
icon_dead = "eskimo_dead"
|
||||
maxHealth = 55
|
||||
health = 55
|
||||
weather_immunities = list("snow")
|
||||
weather_immunities = list(TRAIT_SNOWSTORM_IMMUNE)
|
||||
gold_core_spawnable = NO_SPAWN
|
||||
melee_damage_lower = 17
|
||||
melee_damage_upper = 20
|
||||
@@ -65,7 +65,7 @@
|
||||
icon_dead = "templar_dead"
|
||||
maxHealth = 150
|
||||
health = 150
|
||||
weather_immunities = list("snow")
|
||||
weather_immunities = list(TRAIT_SNOWSTORM_IMMUNE)
|
||||
speed = 2
|
||||
gold_core_spawnable = NO_SPAWN
|
||||
speak_chance = 1
|
||||
@@ -86,7 +86,7 @@
|
||||
speed = 5
|
||||
maxHealth = 75
|
||||
health = 75
|
||||
weather_immunities = list("snow")
|
||||
weather_immunities = list(TRAIT_SNOWSTORM_IMMUNE)
|
||||
color = rgb(114,228,250)
|
||||
loot = list(/obj/effect/decal/remains/human{color = rgb(114,228,250)})
|
||||
|
||||
|
||||
@@ -56,6 +56,9 @@
|
||||
var/minbodytemp = 250
|
||||
var/maxbodytemp = 350
|
||||
|
||||
/// List of weather immunity traits that are then added on Initialize(), see traits.dm.
|
||||
var/list/weather_immunities
|
||||
|
||||
///Healable by medical stacks? Defaults to yes.
|
||||
var/healable = 1
|
||||
|
||||
@@ -165,6 +168,8 @@
|
||||
AddComponent(/datum/component/personal_crafting)
|
||||
if(footstep_type)
|
||||
AddComponent(/datum/component/footstep, footstep_type)
|
||||
for(var/trait in weather_immunities)
|
||||
ADD_TRAIT(src, trait, ROUNDSTART_TRAIT)
|
||||
|
||||
/mob/living/simple_animal/Destroy()
|
||||
GLOB.simple_animals[AIStatus] -= src
|
||||
|
||||
@@ -85,7 +85,8 @@
|
||||
|
||||
/datum/uplink_item/suits/wallwalkers
|
||||
name = "Wall Walking Boots"
|
||||
desc = "Through bluespace magic stolen from an organisation that hoards technology, these boots simply allow you to slip through the atoms that make up anything, but only while walking, for safety reasons. As well as this, they unfortunately cause minor breath loss as the majority of atoms in your lungs are sucked out into any solid object you walk through."
|
||||
desc = "Through bluespace magic stolen from an organisation that hoards technology, these boots simply allow you to slip through the atoms that make up anything,but only while walking, \
|
||||
for safety reasons.As well as this, they unfortunately cause minor breath loss as the majority of atoms in your lungs are sucked out into any solid object you walk through."
|
||||
item = /obj/item/clothing/shoes/wallwalkers
|
||||
cost = 6
|
||||
purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
|
||||
|
||||
@@ -15,6 +15,55 @@
|
||||
- rscadd: Added mime messages for all SPLURT audio emotes
|
||||
- tweak: All SPLURT audio emotes now use length-based cooldowns
|
||||
- refactor: Refactored SPLURT audio emote code
|
||||
- bugfix: Fixed Cargo protolathe access (MAILSORTING instead of CARGO)
|
||||
2023-03-05:
|
||||
thux-tk:
|
||||
- rscadd: new arousal meter to humanoid mob's interface
|
||||
2023-03-06:
|
||||
Anonymous:
|
||||
- rscadd: 'Emotes: \*bark2; \*yap; \*howl; \*coyhowl.'
|
||||
2023-03-07:
|
||||
Anonymous:
|
||||
- rscadd: 'I am gonna sugarcoat it: \*tt.'
|
||||
LeDrascol:
|
||||
- rscadd: Added Bloodfledge quirk examine text
|
||||
- rscadd: Added Bloodfledge integration with Dumb trait
|
||||
- rscadd: Added Bloodfledge body part region targeting support
|
||||
- rscadd: Added Bloodfledge coffin examine text and usage message
|
||||
- rscadd: Added warning to flight potion for bloodfledge users
|
||||
- tweak: Bloodfledge quirk actions use proper cooldown buttons
|
||||
- tweak: Bloodfledge Bite failure splatters blood onto participants
|
||||
- tweak: Bloodfledge Bite can transfer some of the target's reagents
|
||||
- tweak: Bloodfledge Bite interaction time is skipped for targets with no blood
|
||||
- tweak: Drinking blood now grants Strange Nutriment instead of nourishing directly
|
||||
- balance: Bloodfledge robots will not gain the Revive ability (technical issues)
|
||||
- balance: Bloodfledge Revive has a five-minute cooldown
|
||||
- balance: Bloodfledge Revive will always work regardless of health
|
||||
- balance: Bloodfledge Bite cannot drink from mechanical limbs
|
||||
- balance: Bloodfledge Bite will not work directly on Synthetic Lizard faces
|
||||
- balance: Bloodfledge Bite failure triggers cooldown to prevent abuse
|
||||
- balance: Bloodfledge Bite failure causes minor damage and bleeding to compatible
|
||||
regions
|
||||
- balance: Bloodfledge Bite failure only drops 20% of bite amount (from 100%)
|
||||
- balance: Bloodfledge abilities do not function under anti-magic, garlic, or a
|
||||
staked heart
|
||||
- balance: Blood must be ingested to gain nutrition
|
||||
- balance: Blood ingestion will NOT grant nutrition if used by it's original donor
|
||||
- balance: Blood ingestion will not clear disgust or stamina loss
|
||||
2023-03-08:
|
||||
LeDrascol:
|
||||
- rscadd: Added better feedback for production machinery topic access denial
|
||||
- tweak: Production machinery access checks now work for all mobs
|
||||
- balance: Production machinery can now be used in 'mineral mode' by crew with ORM
|
||||
access
|
||||
2023-03-10:
|
||||
Anonymous:
|
||||
- rscadd: Prison jumpsuits can be re-skinned into other color-coded security level,
|
||||
including protective custody and "vampire" variant.
|
||||
2023-03-11:
|
||||
LeDrascol:
|
||||
- rscadd: Added genital fluid blacklist entries for missing effect reagents
|
||||
- code_imp: Added comments explaining the function of every functional consumable
|
||||
reagent
|
||||
- refactor: Refactored genital fluid list generation
|
||||
- refactor: Refactored getter function for allowed genital fluids list
|
||||
|
||||
|
Before Width: | Height: | Size: 237 KiB After Width: | Height: | Size: 239 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 513 KiB After Width: | Height: | Size: 508 KiB |
|
Before Width: | Height: | Size: 297 KiB After Width: | Height: | Size: 296 KiB |
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 186 KiB After Width: | Height: | Size: 185 KiB |
@@ -666,43 +666,58 @@
|
||||
name = "lava walking medal"
|
||||
desc = "A golden medal. Capable of making any jumpsuit completely lava proof for a brief window of time."
|
||||
icon_state = "gold"
|
||||
actions_types = list(/datum/action/item_action/lavawalk)
|
||||
var/cool_down = 0
|
||||
var/cooldown_time = 1200 //two full minutes
|
||||
var/effectduration = 100 //10 seconds of lava walking
|
||||
var/storedimmunities = list()
|
||||
var/datum/action/cooldown/lavawalk/lavawalk
|
||||
var/effectduration = 10 SECONDS
|
||||
var/timer
|
||||
|
||||
/obj/item/clothing/accessory/lavawalk/on_uniform_equip(obj/item/clothing/under/U, user)
|
||||
/obj/item/clothing/accessory/lavawalk/ComponentInitialize()
|
||||
. = ..()
|
||||
var/mob/living/L = U.loc
|
||||
if(L && istype(L))
|
||||
for(var/datum/action/A in actions_types)
|
||||
A.Grant(L)
|
||||
lavawalk = new(src)
|
||||
RegisterSignal(lavawalk, COMSIG_ACTION_TRIGGER, .proc/activate)
|
||||
|
||||
/obj/item/clothing/accessory/lavawalk/on_uniform_dropped(obj/item/clothing/under/U, user)
|
||||
/obj/item/clothing/accessory/lavawalk/Destroy()
|
||||
. = ..()
|
||||
var/mob/living/L = U.loc
|
||||
if(L && istype(L))
|
||||
for(var/datum/action/A in actions_types)
|
||||
A.Remove(L)
|
||||
var/mob/living/user = get_atom_on_turf(src, /mob/living)
|
||||
if(user && timer)
|
||||
reset_user(user)
|
||||
UnregisterSignal(lavawalk, COMSIG_ACTION_TRIGGER)
|
||||
QDEL_NULL(lavawalk)
|
||||
|
||||
/datum/action/item_action/lavawalk
|
||||
/obj/item/clothing/accessory/lavawalk/on_uniform_equip(obj/item/clothing/under/U, mob/living/user)
|
||||
. = ..()
|
||||
if(istype(user))
|
||||
lavawalk.Grant(user)
|
||||
|
||||
/obj/item/clothing/accessory/lavawalk/on_uniform_dropped(obj/item/clothing/under/U, mob/living/user)
|
||||
. = ..()
|
||||
if(istype(user))
|
||||
if(timer)
|
||||
reset_user(user)
|
||||
lavawalk.Remove(user)
|
||||
|
||||
/datum/action/cooldown/lavawalk
|
||||
name = "Lava Walk"
|
||||
desc = "Become immune to lava for a brief period of time."
|
||||
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUN|AB_CHECK_CONSCIOUS
|
||||
cooldown_time = 2 MINUTES //two full minutes
|
||||
use_target_appearance = TRUE
|
||||
|
||||
/obj/item/clothing/accessory/lavawalk/ui_action_click(mob/user, actiontype)
|
||||
if(istype(actiontype, /datum/action/item_action/lavawalk))
|
||||
if(world.time >= cool_down)
|
||||
var/mob/living/L = user
|
||||
if(istype(L))
|
||||
storedimmunities = L.weather_immunities.Copy()
|
||||
L.weather_immunities |= list("ash", "lava")
|
||||
cool_down = world.time + cooldown_time
|
||||
addtimer(CALLBACK(src, .proc/reset_user, L), effectduration)
|
||||
/obj/item/clothing/accessory/lavawalk/proc/activate(datum/action/cooldown/lavawalk/action, obj/item/clothing/accessory/lavawalk/item)
|
||||
var/mob/living/L = usr
|
||||
if(istype(L))
|
||||
to_chat(L, span_notice("\The [src] begins glowing!"))
|
||||
L.balloon_alert(L, "activated")
|
||||
ADD_TRAIT(L, TRAIT_ASHSTORM_IMMUNE, src)
|
||||
ADD_TRAIT(L, TRAIT_LAVA_IMMUNE, src)
|
||||
timer = addtimer(CALLBACK(src, .proc/reset_user, L), effectduration)
|
||||
action.StartCooldown()
|
||||
|
||||
/obj/item/clothing/accessory/lavawalk/proc/reset_user(mob/living/user)
|
||||
user.weather_immunities = storedimmunities
|
||||
storedimmunities = list()
|
||||
REMOVE_TRAIT(user, TRAIT_ASHSTORM_IMMUNE, src)
|
||||
REMOVE_TRAIT(user, TRAIT_LAVA_IMMUNE, src)
|
||||
to_chat(user, span_boldwarning("\The [src]'s glow dims."))
|
||||
user.balloon_alert(user, "wore off")
|
||||
QDEL_NULL(timer)
|
||||
|
||||
//Nerfing those on the chest because too OP yada yada
|
||||
/obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor/damaged
|
||||
|
||||
@@ -33,37 +33,68 @@
|
||||
// This is intended for low populations
|
||||
if((!PROTOLOCK_DURING_LOWPOP) && (!JOB_MINIMAL_ACCESS))
|
||||
// Allow unrestricted use
|
||||
return TRUE
|
||||
return PROTOLOCK_ACCESS_LOWPOP
|
||||
|
||||
// Define machine user
|
||||
var/mob/living/carbon/human/machine_user = src
|
||||
|
||||
// Check if user exists
|
||||
if(!istype(machine_user))
|
||||
return TRUE
|
||||
// Check if user has access to this machine
|
||||
if(machine_target.allowed(src))
|
||||
return PROTOLOCK_ACCESS_NORMAL
|
||||
|
||||
// Define user ID card
|
||||
var/obj/item/card/id/user_id = machine_user.get_idcard()
|
||||
var/obj/item/card/id/user_id = get_idcard()
|
||||
|
||||
// Check if ID card was found
|
||||
if(!istype(user_id))
|
||||
// Warn in local chat, then return
|
||||
machine_target.say("Access denied: Unable to scan user ID card.")
|
||||
return FALSE
|
||||
|
||||
// Check for Captain
|
||||
if(ACCESS_CAPTAIN in user_id.access)
|
||||
// Allow usage
|
||||
return TRUE
|
||||
return PROTOLOCK_ACCESS_CAPTAIN
|
||||
|
||||
// Check if access requirements are met
|
||||
if(machine_target.check_access(user_id))
|
||||
// Check for ORM access
|
||||
if(ACCESS_MINERAL_STOREROOM in user_id.access)
|
||||
// Allow use
|
||||
return PROTOLOCK_ACCESS_MINERAL
|
||||
|
||||
// User has no access
|
||||
return FALSE
|
||||
|
||||
/mob/proc/can_use_production_topic(obj/machinery/rnd/production/machine_target, raw, ls)
|
||||
// Basic actions that are always permitted
|
||||
// This includes syncing research and switching screens
|
||||
if(ls["sync_research"] || ls["switch_screen"])
|
||||
return TRUE
|
||||
|
||||
// User does not have access
|
||||
// Warn in local chat, then return
|
||||
machine_target.say("Access denied: No valid departmental credentials detected.")
|
||||
// Define user's access type
|
||||
var/user_access = usr.can_use_production(machine_target)
|
||||
|
||||
// Switch result based on access type
|
||||
// This currently doesn't do anything special
|
||||
switch(user_access)
|
||||
// Type: Low population
|
||||
if(PROTOLOCK_ACCESS_LOWPOP)
|
||||
return TRUE
|
||||
|
||||
// Type: Standard
|
||||
if(PROTOLOCK_ACCESS_NORMAL)
|
||||
return TRUE
|
||||
|
||||
// Type: Captain
|
||||
if(PROTOLOCK_ACCESS_CAPTAIN)
|
||||
return TRUE
|
||||
|
||||
// Type: Mineral / ORM
|
||||
if(PROTOLOCK_ACCESS_MINERAL)
|
||||
// Check if permitted topic
|
||||
if(ls["ejectsheet"])
|
||||
return TRUE
|
||||
|
||||
// Topic prohibited
|
||||
// Deny usage
|
||||
else
|
||||
return FALSE
|
||||
|
||||
// Default to false
|
||||
return FALSE
|
||||
|
||||
#undef JOB_MINIMAL_ACCESS
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
// Check if user can use machine
|
||||
if(!user.can_use_production(src))
|
||||
// Warn in local chat and return
|
||||
say("Access denied: No valid departmental or mineral credentials detected.")
|
||||
return
|
||||
|
||||
// Return normally
|
||||
@@ -22,7 +24,14 @@
|
||||
return ..()
|
||||
|
||||
// Check if user can use machine
|
||||
if(!usr.can_use_production(src))
|
||||
if(!usr.can_use_production_topic(src, raw, ls))
|
||||
// Alert in local chat
|
||||
usr.visible_message(span_warning("[usr] pushes a button on [src], causing it to chime with the familiar sound of rejection."), span_warning("The machine buzzes with a soft chime. It seems you don't have access to that button."))
|
||||
|
||||
// Play sound
|
||||
playsound(loc, 'sound/machines/uplinkerror.ogg', 70, 0)
|
||||
|
||||
// Return
|
||||
return
|
||||
|
||||
// Return normally
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
req_access = list(ACCESS_MEDICAL)
|
||||
|
||||
/obj/machinery/rnd/production/protolathe/department/cargo
|
||||
req_access = list(ACCESS_CARGO)
|
||||
req_one_access = list(ACCESS_CARGO, ACCESS_MINING)
|
||||
|
||||
/obj/machinery/rnd/production/protolathe/department/science
|
||||
req_access = list(ACCESS_RESEARCH)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
req_access = list(ACCESS_MEDICAL)
|
||||
|
||||
/obj/machinery/rnd/production/techfab/department/cargo
|
||||
req_access = list(ACCESS_CARGO)
|
||||
req_one_access = list(ACCESS_CARGO, ACCESS_MINING)
|
||||
|
||||
/obj/machinery/rnd/production/techfab/department/science
|
||||
req_access = list(ACCESS_RESEARCH)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//Genitals and arousals lists
|
||||
GLOBAL_LIST(genital_fluids_list)
|
||||
GLOBAL_LIST(genital_fluids_paths)
|
||||
|
||||
GLOBAL_LIST_INIT(default_genital_fluids, list(
|
||||
find_reagent_object_from_type(/datum/reagent/consumable/milk),
|
||||
|
||||
@@ -34,3 +34,14 @@
|
||||
description = span_warning("I can feel a pale curse from the blood I drank.\n")
|
||||
mood_change = -1
|
||||
timeout = 2 MINUTES
|
||||
|
||||
// Matches drinking shared exotic blood
|
||||
/datum/mood_event/drankblood_insect
|
||||
description = span_boldwarning("I drank an insect's hemolymph. What is wrong with me?\n")
|
||||
mood_change = -2
|
||||
timeout = 2 MINUTES
|
||||
|
||||
/datum/mood_event/drankblood_xeno
|
||||
description = span_boldwarning("I drank xenobiological blood. What is wrong with me?\n")
|
||||
mood_change = -2
|
||||
timeout = 2 MINUTES
|
||||
|
||||
@@ -33,3 +33,8 @@
|
||||
description = span_nicegreen("I\'ve tasted sympathy from a fellow curse bearer.\n")
|
||||
mood_change = 1
|
||||
timeout = 2 MINUTES
|
||||
|
||||
/datum/mood_event/drank_exotic_matched
|
||||
description = span_nicegreen("I tasted familiarity from the blood I drank!\n")
|
||||
mood_change = 2
|
||||
timeout = 2 MINUTES
|
||||
|
||||
@@ -273,7 +273,7 @@
|
||||
/datum/quirk/storage_concealment
|
||||
name = "Dorsualiphobic Augmentation"
|
||||
desc = "You despise the idea of being seen wearing any type of back-mounted storage apparatus! A new technology shields you from the immense shame you may experience, by hiding your equipped backpack."
|
||||
|
||||
|
||||
// UNUSED: Enable by setting these values to TRUE
|
||||
// The shame is unbearable
|
||||
mood_quirk = FALSE
|
||||
@@ -281,10 +281,10 @@
|
||||
|
||||
/datum/quirk/storage_concealment/on_spawn()
|
||||
. = ..()
|
||||
|
||||
|
||||
// Create a new augment item
|
||||
var/obj/item/implant/hide_backpack/put_in = new
|
||||
|
||||
|
||||
// Apply the augment to the quirk holder
|
||||
put_in.implant(quirk_holder, null, TRUE, TRUE)
|
||||
|
||||
@@ -355,11 +355,9 @@
|
||||
mob_trait = TRAIT_BLOODFLEDGE
|
||||
gain_text = span_notice("You feel a sanguine thirst.")
|
||||
lose_text = span_notice("You feel the sanguine thirst fade away.")
|
||||
processing_quirk = TRUE
|
||||
processing_quirk = FALSE // Handled by crates.dm
|
||||
|
||||
/datum/quirk/bloodfledge/add()
|
||||
. = ..()
|
||||
|
||||
// Define quirk mob
|
||||
var/mob/living/carbon/human/quirk_mob = quirk_holder
|
||||
|
||||
@@ -371,44 +369,60 @@
|
||||
if(!quirk_mob.dna.skin_tone_override)
|
||||
quirk_mob.skin_tone = "albino"
|
||||
|
||||
// Add quirk ability action datums
|
||||
var/datum/action/bloodfledge/bite/act_bite = new
|
||||
var/datum/action/bloodfledge/revive/act_revive = new
|
||||
act_bite.Grant(quirk_mob)
|
||||
act_revive.Grant(quirk_mob)
|
||||
|
||||
// Add quirk language
|
||||
quirk_mob.grant_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_BLOODSUCKER)
|
||||
|
||||
// Register examine text
|
||||
RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, .proc/quirk_examine_bloodfledge)
|
||||
|
||||
/datum/quirk/bloodfledge/post_add()
|
||||
// Define quirk mob
|
||||
var/mob/living/carbon/human/quirk_mob = quirk_holder
|
||||
|
||||
// Define and grant ability Bite
|
||||
var/datum/action/cooldown/bloodfledge/bite/act_bite = new
|
||||
act_bite.Grant(quirk_mob)
|
||||
|
||||
// Check for synthetic
|
||||
// Robotic mobs have technical issues with adjusting damage
|
||||
if(quirk_mob.mob_biotypes & MOB_ROBOTIC)
|
||||
// Warn user
|
||||
to_chat(quirk_mob, span_warning("As a synthetic lifeform, your components are only able to grant limited sanguine abilities! Regeneration and revival are not possible."))
|
||||
|
||||
// User is not synthetic
|
||||
else
|
||||
// Define and grant ability Revive
|
||||
var/datum/action/cooldown/bloodfledge/revive/act_revive = new
|
||||
act_revive.Grant(quirk_mob)
|
||||
|
||||
/datum/quirk/bloodfledge/on_process()
|
||||
. = ..()
|
||||
// Processing is currently only used for coffin healing
|
||||
// This is started and stopped by a proc in crates.dm
|
||||
|
||||
// Define potential coffin
|
||||
var/quirk_coffin = quirk_holder.loc
|
||||
|
||||
// Check if the current area is a coffin
|
||||
if(istype(quirk_holder.loc, /obj/structure/closet/crate/coffin))
|
||||
if(istype(quirk_coffin, /obj/structure/closet/crate/coffin))
|
||||
// Define quirk mob
|
||||
var/mob/living/carbon/human/quirk_mob = quirk_holder
|
||||
|
||||
// Quirk mob must be injured
|
||||
if(quirk_mob.health >= quirk_mob.maxHealth)
|
||||
return
|
||||
// Warn user
|
||||
to_chat(quirk_mob, span_notice("[quirk_coffin] does nothing more to help you, as your body is fully mended."))
|
||||
|
||||
// Prevent healing for robots
|
||||
// This caused numerous technical issues
|
||||
if(quirk_mob.mob_biotypes & MOB_ROBOTIC)
|
||||
// Display a warning chat message (10% chance)
|
||||
if(prob(20))
|
||||
to_chat(quirk_mob, span_warning("Your mechanical body rejects the curse's healing properties!"))
|
||||
|
||||
// Return without healing, due robotic nature
|
||||
// Stop processing and return
|
||||
STOP_PROCESSING(SSquirks, src)
|
||||
return
|
||||
|
||||
// Nutrition (blood) level must be above STARVING
|
||||
if(quirk_mob.nutrition <= NUTRITION_LEVEL_STARVING)
|
||||
// Display a warning chat message (10% chance)
|
||||
if(prob(20))
|
||||
to_chat(quirk_mob, span_warning("You need more blood before you can regenerate!"))
|
||||
// Warn user
|
||||
to_chat(quirk_mob, span_warning("[quirk_coffin] requires blood to operate, which you are currently lacking. Your connection to the other-world fades once again."))
|
||||
|
||||
// Return without healing, due to lack of blood
|
||||
// Stop processing and return
|
||||
STOP_PROCESSING(SSquirks, src)
|
||||
return
|
||||
|
||||
// Define initial health
|
||||
@@ -417,12 +431,6 @@
|
||||
// Heal brute and burn
|
||||
// Accounts for robotic limbs
|
||||
quirk_mob.heal_overall_damage(2,2)
|
||||
/*
|
||||
// Heal brute
|
||||
quirk_mob.adjustBruteLoss(-2)
|
||||
// Heal burn
|
||||
quirk_mob.adjustFireLoss(-2)
|
||||
*/
|
||||
// Heal oxygen
|
||||
quirk_mob.adjustOxyLoss(-2)
|
||||
// Heal clone
|
||||
@@ -448,9 +456,16 @@
|
||||
// Amount is equal to 50% of healing done
|
||||
quirk_mob.adjust_nutrition(health_restored*-1)
|
||||
|
||||
// User is not in a coffin
|
||||
// This should not occur without teleportation
|
||||
else
|
||||
// Warn user
|
||||
to_chat(quirk_holder, span_warning("Your connection to the other-world is broken upon leaving the [quirk_coffin]!"))
|
||||
|
||||
// Stop processing
|
||||
STOP_PROCESSING(SSquirks, src)
|
||||
|
||||
/datum/quirk/bloodfledge/remove()
|
||||
. = ..()
|
||||
|
||||
// Define quirk mob
|
||||
var/mob/living/carbon/human/quirk_mob = quirk_holder
|
||||
|
||||
@@ -459,17 +474,18 @@
|
||||
REMOVE_TRAIT(quirk_mob, TRAIT_NOTHIRST, ROUNDSTART_TRAIT)
|
||||
|
||||
// Remove quirk ability action datums
|
||||
var/datum/action/bloodfledge/bite/act_bite = locate() in quirk_mob.actions
|
||||
var/datum/action/bloodfledge/revive/act_revive = locate() in quirk_mob.actions
|
||||
var/datum/action/cooldown/bloodfledge/bite/act_bite = locate() in quirk_mob.actions
|
||||
var/datum/action/cooldown/bloodfledge/revive/act_revive = locate() in quirk_mob.actions
|
||||
act_bite.Remove(quirk_mob)
|
||||
act_revive.Remove(quirk_mob)
|
||||
|
||||
// Remove quirk language
|
||||
quirk_mob.remove_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_BLOODSUCKER)
|
||||
|
||||
/datum/quirk/bloodfledge/on_spawn()
|
||||
. = ..()
|
||||
// Unregister examine text
|
||||
UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
|
||||
|
||||
/datum/quirk/bloodfledge/on_spawn()
|
||||
// Define quirk mob
|
||||
var/mob/living/carbon/human/quirk_mob = quirk_holder
|
||||
|
||||
@@ -506,6 +522,55 @@
|
||||
// This should not post_add, because the ID is added by on_spawn
|
||||
to_chat(quirk_holder, span_boldnotice("There is a bloodfledge's ID card [id_location], linked to your station account. It functions as a spare ID, but lacks job access."))
|
||||
|
||||
/datum/quirk/bloodfledge/proc/quirk_examine_bloodfledge(atom/examine_target, mob/living/carbon/human/examiner, list/examine_list)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
// Check if human examiner exists
|
||||
if(!istype(examiner))
|
||||
return
|
||||
|
||||
// Check if examiner is dumb
|
||||
if(HAS_TRAIT(examiner, TRAIT_DUMB))
|
||||
// Return with no effects
|
||||
return
|
||||
|
||||
// Define quirk mob
|
||||
var/mob/living/carbon/human/quirk_mob = quirk_holder
|
||||
|
||||
// Define hunger texts
|
||||
var/examine_hunger_public
|
||||
var/examine_hunger_secret
|
||||
|
||||
// Check hunger levels
|
||||
switch(quirk_mob.nutrition)
|
||||
// Hungry
|
||||
if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY)
|
||||
examine_hunger_secret = "[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] blood starved!"
|
||||
examine_hunger_public = "[quirk_holder.p_they(TRUE)] seem[quirk_holder.p_s()] on edge from something."
|
||||
|
||||
// Starving
|
||||
if(0 to NUTRITION_LEVEL_STARVING)
|
||||
examine_hunger_secret = "[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] in dire need of blood!"
|
||||
examine_hunger_public = "[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] radiating an aura of frenzied hunger!"
|
||||
|
||||
// Invalid hunger
|
||||
else
|
||||
// Return with no message
|
||||
return
|
||||
|
||||
// Check if examiner shares the quirk
|
||||
if(isbloodfledge(examiner))
|
||||
// Add detection text
|
||||
examine_list += span_info("[quirk_holder.p_their(TRUE)] hunger makes it easy to identify [quirk_holder.p_them()] as a fellow Bloodsucker Fledgling!")
|
||||
|
||||
// Add hunger text
|
||||
examine_list += span_warning(examine_hunger_secret)
|
||||
|
||||
// Check if public hunger text exists
|
||||
else
|
||||
// Add hunger text
|
||||
examine_list += span_warning(examine_hunger_public)
|
||||
|
||||
/datum/quirk/werewolf //adds the werewolf quirk
|
||||
name = "Werewolf"
|
||||
desc = "A beastly affliction allows you to shape-shift into a large anthropomorphic canine at will."
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#define BLOODFLEDGE_DRAIN_NUM 50
|
||||
#define BLOODFLEDGE_COOLDOWN_BITE 60
|
||||
#define BLOODFLEDGE_COOLDOWN_BITE 60 // Six seconds
|
||||
#define BLOODFLEDGE_COOLDOWN_REVIVE 3000 // Five minutes
|
||||
#define BLOODFLEDGE_BANK_CAPACITY (BLOODFLEDGE_DRAIN_NUM * 2)
|
||||
|
||||
//
|
||||
// Quirk: Hypnotic Gaze
|
||||
@@ -115,7 +117,7 @@
|
||||
to_chat(action_owner, span_warning("You sense that [action_target] would rather not be hypnotized, and decide to respect their wishes."))
|
||||
to_chat(action_target, span_notice("[action_owner] stares into your eyes with a strange conviction, but turns away after a moment."))
|
||||
return
|
||||
|
||||
|
||||
// Check for mindshield implant
|
||||
if(HAS_TRAIT(action_target, TRAIT_MINDSHIELD))
|
||||
// Warn the users, then return
|
||||
@@ -183,7 +185,7 @@
|
||||
|
||||
// Prompt action owner for response
|
||||
var/input_suggestion = input("What would you like to suggest [action_target] do? Leave blank to release [action_target.p_them()] instead.", "Hypnotic suggestion", null, null)
|
||||
|
||||
|
||||
// Check if input text exists
|
||||
if(!input_suggestion)
|
||||
// Alert user of no input
|
||||
@@ -205,7 +207,7 @@
|
||||
|
||||
// Display local message
|
||||
action_target.visible_message(span_warning("[action_target] wakes up from their deep slumber!"), span_danger("Your eyelids gently open as you see [action_owner]'s face staring back at you."))
|
||||
|
||||
|
||||
// Remove sleep, then return
|
||||
action_target.SetSleeping(0)
|
||||
return
|
||||
@@ -245,7 +247,7 @@
|
||||
//
|
||||
|
||||
// Basic action preset
|
||||
/datum/action/bloodfledge
|
||||
/datum/action/cooldown/bloodfledge
|
||||
name = "Broken Bloodfledge Ability"
|
||||
desc = "You shouldn't be seeing this!"
|
||||
button_icon_state = "power_torpor"
|
||||
@@ -253,30 +255,62 @@
|
||||
buttontooltipstyle = "cult"
|
||||
icon_icon = 'icons/mob/actions/bloodsucker.dmi'
|
||||
button_icon = 'icons/mob/actions/bloodsucker.dmi'
|
||||
transparent_when_unavailable = TRUE
|
||||
|
||||
// Action: Bite
|
||||
/datum/action/bloodfledge/bite
|
||||
name = "Fledgling Bite"
|
||||
desc = "Sink your vampiric fangs into the person you are grabbing, and attempt to drink their blood."
|
||||
button_icon_state = "power_feed"
|
||||
var/drain_cooldown = 0
|
||||
|
||||
/datum/action/bloodfledge/bite/Trigger()
|
||||
// Basic can-use check
|
||||
/datum/action/cooldown/bloodfledge/IsAvailable(silent = FALSE)
|
||||
. = ..()
|
||||
|
||||
// Check parent return
|
||||
if(!.)
|
||||
return FALSE
|
||||
|
||||
// Check for carbon owner
|
||||
if(!iscarbon(owner))
|
||||
// Warn user and return
|
||||
to_chat(owner, span_warning("You shouldn't have this ability!"))
|
||||
return FALSE
|
||||
|
||||
// Check vampire ability mob proc
|
||||
if(!owner.allow_vampiric_ability(silent = FALSE))
|
||||
return FALSE
|
||||
|
||||
// Action can be used
|
||||
return TRUE
|
||||
|
||||
// Action: Bite
|
||||
/datum/action/cooldown/bloodfledge/bite
|
||||
name = "Fledgling Bite"
|
||||
desc = "Sink your vampiric fangs into the person you are grabbing, and attempt to drink their blood."
|
||||
button_icon_state = "power_feed"
|
||||
cooldown_time = BLOODFLEDGE_COOLDOWN_BITE
|
||||
var/time_interact = 30
|
||||
|
||||
// Reagent holder, used to change reaction type
|
||||
var/datum/reagents/blood_bank
|
||||
|
||||
/datum/action/cooldown/bloodfledge/bite/Grant()
|
||||
. = ..()
|
||||
|
||||
// Check for voracious
|
||||
if(HAS_TRAIT(owner, TRAIT_VORACIOUS))
|
||||
// Make times twice as fast
|
||||
cooldown_time *= 0.5
|
||||
time_interact*= 0.5
|
||||
|
||||
// Create reagent holder
|
||||
blood_bank = new(BLOODFLEDGE_BANK_CAPACITY)
|
||||
|
||||
/datum/action/cooldown/bloodfledge/bite/Trigger()
|
||||
. = ..()
|
||||
|
||||
// Check parent return
|
||||
if(!.)
|
||||
return
|
||||
|
||||
// Define action owner
|
||||
var/mob/living/carbon/action_owner = owner
|
||||
|
||||
// Check for cooldown
|
||||
if(drain_cooldown >= world.time)
|
||||
// Warn the user, then return
|
||||
to_chat(action_owner, span_notice("That ability isn't ready yet."))
|
||||
return
|
||||
|
||||
// Check for any grabbed target
|
||||
if(!action_owner.pulling)
|
||||
// Warn the user, then return
|
||||
@@ -301,11 +335,20 @@
|
||||
to_chat(action_owner, span_notice("You can't bite things while muzzled!"))
|
||||
return
|
||||
|
||||
// Check for covered mouth
|
||||
if(action_owner.is_mouth_covered())
|
||||
// Warn the user, then return
|
||||
to_chat(action_owner, span_notice("You can't bite things with your mouth covered!"))
|
||||
return
|
||||
|
||||
// Define pulled target
|
||||
var/pull_target = action_owner.pulling
|
||||
|
||||
// Define bite target
|
||||
var/mob/living/carbon/bite_target
|
||||
var/mob/living/carbon/human/bite_target
|
||||
|
||||
// Define if action owner is dumb
|
||||
var/action_owner_dumb = HAS_TRAIT(action_owner, TRAIT_DUMB)
|
||||
|
||||
// Check if the target is carbon
|
||||
if(iscarbon(pull_target))
|
||||
@@ -316,7 +359,7 @@
|
||||
else if(istype(pull_target,/obj/structure/arachnid/cocoon))
|
||||
// Define if cocoon has a valid target
|
||||
// This cannot use pull_target
|
||||
var/possible_cocoon_target = locate(/mob/living/carbon) in action_owner.pulling.contents
|
||||
var/possible_cocoon_target = locate(/mob/living/carbon/human) in action_owner.pulling.contents
|
||||
|
||||
// Check defined cocoon target
|
||||
if(possible_cocoon_target)
|
||||
@@ -325,86 +368,294 @@
|
||||
|
||||
// Or a blood tomato
|
||||
else if(istype(pull_target,/obj/item/reagent_containers/food/snacks/grown/tomato/blood))
|
||||
// Set message based on dumbness
|
||||
var/message_tomato_suffix = (action_owner_dumb ? ", and absorb it\'s delicious vegan-friendly blood!" : "! It's not very nutritious.")
|
||||
// Warn the user, then return
|
||||
to_chat(action_owner, span_danger("You plunge your fangs into [pull_target]! It's not very nutritious."))
|
||||
to_chat(action_owner, span_danger("You plunge your fangs into [pull_target][message_tomato_suffix]"))
|
||||
return
|
||||
|
||||
// This doesn't actually interact with the item
|
||||
|
||||
// Or none of the above
|
||||
else
|
||||
// Set message based on dumbness
|
||||
var/message_invalid_target = (action_owner_dumb ? "You bite at [pull_target], but nothing seems to happen" : "You can't drain blood from [pull_target]!")
|
||||
// Warn the user, then return
|
||||
to_chat(action_owner, span_warning("You can't drain blood from [pull_target]!"))
|
||||
to_chat(action_owner, span_warning(message_invalid_target))
|
||||
return
|
||||
|
||||
// Define selected zone
|
||||
var/target_zone = action_owner.zone_selected
|
||||
|
||||
// Check if target can be penetrated
|
||||
// Bypass pierce immunity so feedback can be provided later
|
||||
if(!bite_target.can_inject(action_owner, FALSE, target_zone, FALSE, TRUE))
|
||||
// Warn the user, then return
|
||||
to_chat(action_owner, span_warning("There\'s no exposed flesh or thin material in that region of [bite_target]'s body. You're unable to bite them!"))
|
||||
return
|
||||
|
||||
// Check targeted body part
|
||||
var/obj/item/bodypart/bite_bodypart = bite_target.get_bodypart(target_zone)
|
||||
|
||||
// Define zone name
|
||||
var/target_zone_name = "flesh"
|
||||
|
||||
// Define if target zone has special effects
|
||||
var/target_zone_effects = FALSE
|
||||
|
||||
// Define if zone should be checked
|
||||
// Uses dismember check to determine if it can be missing
|
||||
// Missing limbs are assumed to be dismembered
|
||||
var/target_zone_check = bite_bodypart?.can_dismember() || TRUE
|
||||
|
||||
// Set zone name based on region
|
||||
// Also checks for some protections
|
||||
switch(target_zone)
|
||||
if(BODY_ZONE_HEAD)
|
||||
target_zone_name = "neck"
|
||||
|
||||
if(BODY_ZONE_CHEST)
|
||||
target_zone_name = "shoulder"
|
||||
|
||||
if(BODY_ZONE_L_ARM)
|
||||
target_zone_name = "left arm"
|
||||
|
||||
if(BODY_ZONE_R_ARM)
|
||||
target_zone_name = "right arm"
|
||||
|
||||
if(BODY_ZONE_L_LEG)
|
||||
target_zone_name = "left thigh"
|
||||
|
||||
if(BODY_ZONE_R_LEG)
|
||||
target_zone_name = "right thigh"
|
||||
|
||||
if(BODY_ZONE_PRECISE_EYES)
|
||||
// Check if eyes exist and are exposed
|
||||
if(!bite_target.has_eyes(REQUIRE_EXPOSED))
|
||||
// Warn user and return
|
||||
to_chat(action_owner, span_warning("You can't find [bite_target]'s eyes to bite them!"))
|
||||
return
|
||||
|
||||
// Set region data normally
|
||||
target_zone_name = "eyes"
|
||||
target_zone_check = FALSE
|
||||
target_zone_effects = TRUE
|
||||
|
||||
if(BODY_ZONE_PRECISE_MOUTH)
|
||||
// Check if mouth exists and is exposed
|
||||
if(!(bite_target.has_mouth() && bite_target.mouth_is_free()))
|
||||
to_chat(action_owner, span_warning("You can't find [bite_target]'s lips to bite them!"))
|
||||
return
|
||||
|
||||
// Set region data normally
|
||||
target_zone_name = "lips"
|
||||
target_zone_check = FALSE
|
||||
target_zone_effects = TRUE
|
||||
|
||||
if(BODY_ZONE_PRECISE_GROIN)
|
||||
target_zone_name = "groin"
|
||||
target_zone_check = FALSE
|
||||
|
||||
if(BODY_ZONE_PRECISE_L_HAND)
|
||||
target_zone_name = "left wrist"
|
||||
|
||||
if(BODY_ZONE_PRECISE_R_HAND)
|
||||
target_zone_name = "right wrist"
|
||||
|
||||
if(BODY_ZONE_PRECISE_L_FOOT)
|
||||
target_zone_name = "left ankle"
|
||||
|
||||
if(BODY_ZONE_PRECISE_R_FOOT)
|
||||
target_zone_name = "right ankle"
|
||||
|
||||
// Check if target should be checked
|
||||
if(target_zone_check)
|
||||
// Check if bodypart exists
|
||||
if(!bite_bodypart)
|
||||
// Warn user and return
|
||||
to_chat(action_owner, span_warning("[bite_target] doesn't have a [target_zone_name] for you to bite!"))
|
||||
return
|
||||
|
||||
// Check if bodypart is organic
|
||||
if(!bite_bodypart.is_organic_limb())
|
||||
// Display local message
|
||||
action_owner.visible_message(span_danger("[action_owner] tries to bite [bite_target]'s [target_zone_name], but is unable to penetrate the mechanical prosthetic!"), span_warning("You attempt to bite [bite_target]'s [target_zone_name], but can't penetrate the mechanical prosthetic!"))
|
||||
|
||||
// Warn user
|
||||
to_chat(bite_target, span_warning("[action_owner] tries to bite your [target_zone_name], but is unable to penetrate the mechanical prosthetic!"))
|
||||
|
||||
// Play metal hit sound
|
||||
playsound(bite_target, "sound/effects/clang[pick(1,2)].ogg", 30, 1, -2)
|
||||
|
||||
// Start cooldown early to prevent spam
|
||||
StartCooldown()
|
||||
|
||||
// Return without further effects
|
||||
return
|
||||
|
||||
// Check for anti-magic
|
||||
if(bite_target.anti_magic_check(FALSE, TRUE, FALSE, 0))
|
||||
// Check for a dumb user
|
||||
if(action_owner_dumb)
|
||||
// Display local message
|
||||
action_owner.visible_message(span_danger("[action_owner] tries to bite [bite_target]'s [target_zone_name], but bursts into flames just as [action_owner.p_they()] come[action_owner.p_s()] into contact with [bite_target.p_them()]!"), span_userdanger("Surges of pain course through your body as you attempt to bite [bite_target]! What were you thinking?"))
|
||||
|
||||
// Warn target
|
||||
to_chat(bite_target, span_warning("[action_owner] tries to bite you, but bursts into flames just as [action_owner.p_they()] come[action_owner.p_s()] into contact with you!"))
|
||||
|
||||
// Stop grabbing
|
||||
action_owner.stop_pulling()
|
||||
|
||||
// Ignite action owner
|
||||
action_owner.adjust_fire_stacks(2)
|
||||
action_owner.IgniteMob()
|
||||
|
||||
// Return with no further effects
|
||||
return
|
||||
|
||||
// Warn the user and target, then return
|
||||
to_chat(bite_target, span_warning("[action_owner] tries to bite you, but stops before touching you!"))
|
||||
to_chat(bite_target, span_warning("[action_owner] tries to bite your [target_zone_name], but stops before touching you!"))
|
||||
to_chat(action_owner, span_warning("[bite_target] is blessed! You stop just in time to avoid catching fire."))
|
||||
return
|
||||
|
||||
// Check for garlic necklace or garlic in the bloodstream
|
||||
if(!blood_sucking_checks(bite_target, TRUE, TRUE))
|
||||
// Check for a dumb user
|
||||
if(action_owner_dumb)
|
||||
// Display local message
|
||||
action_owner.visible_message(span_danger("[action_owner] tries to bite [bite_target]'s [target_zone_name], but immediately recoils in disgust upon touching [bite_target.p_them()]!"), span_userdanger("An intense wave of disgust washes over your body as you attempt to bite [bite_target]! What were you thinking?"))
|
||||
|
||||
// Warn target
|
||||
to_chat(bite_target, span_warning("[action_owner] tries to bite your [target_zone_name], but recoils in disgust just as [action_owner.p_they()] come[action_owner.p_s()] into contact with you!"))
|
||||
|
||||
// Stop grabbing
|
||||
action_owner.stop_pulling()
|
||||
|
||||
// Add disgust
|
||||
action_owner.adjust_disgust(10)
|
||||
|
||||
// Vomit
|
||||
action_owner.vomit()
|
||||
|
||||
// Return with no further effects
|
||||
return
|
||||
|
||||
// Warn the user and target, then return
|
||||
to_chat(bite_target, span_warning("[action_owner] tries to bite you, but is warded off by your Allium Sativum!"))
|
||||
to_chat(action_owner, span_warning("You sense that [bite_target] is protected by Allium Sativum, and refrain from biting them."))
|
||||
to_chat(bite_target, span_warning("[action_owner] leans in to bite your [target_zone_name], but is warded off by your Allium Sativum!"))
|
||||
to_chat(action_owner, span_warning("You sense that [bite_target] is protected by Allium Sativum, and refrain from biting [bite_target.p_them()]."))
|
||||
return
|
||||
|
||||
// Define bite target's blood volume
|
||||
var/target_blood_volume = bite_target.blood_volume
|
||||
|
||||
// Check for sufficient blood volume
|
||||
if(!target_blood_volume)
|
||||
if(target_blood_volume < BLOODFLEDGE_DRAIN_NUM)
|
||||
// Warn the user, then return
|
||||
to_chat(action_owner, span_warning("There's not enough blood in [bite_target]!"))
|
||||
return
|
||||
|
||||
// Check if total blood would become too low
|
||||
if((target_blood_volume - BLOODFLEDGE_DRAIN_NUM) <= BLOOD_VOLUME_OKAY)
|
||||
// Check for a dumb user
|
||||
if(action_owner_dumb)
|
||||
// Warn the user, but allow
|
||||
to_chat(action_owner, span_warning("You pay no attention to [bite_target]'s blood volume, and bite [bite_target.p_their()] [target_zone_name] without hesitation."))
|
||||
|
||||
// Check for aggressive grab
|
||||
if(action_owner.grab_state < GRAB_AGGRESSIVE)
|
||||
else if(action_owner.grab_state < GRAB_AGGRESSIVE)
|
||||
// Warn the user, then return
|
||||
to_chat(action_owner, span_warning("You sense that [bite_target] is running low on blood. You'll need a tighter grip on [bite_target.p_them()] to continue."))
|
||||
return
|
||||
|
||||
// Check for pacifist
|
||||
if(HAS_TRAIT(action_owner, TRAIT_PACIFISM))
|
||||
else if(HAS_TRAIT(action_owner, TRAIT_PACIFISM))
|
||||
// Warn the user, then return
|
||||
to_chat(action_owner, span_warning("You can't drain any more blood from [bite_target] without hurting [bite_target.p_them()]!"))
|
||||
return
|
||||
|
||||
// Set cooldown and action times
|
||||
var/time_cooldown = BLOODFLEDGE_COOLDOWN_BITE
|
||||
var/time_interact = 30
|
||||
// Check for pierce immunity
|
||||
if(HAS_TRAIT(bite_target, TRAIT_PIERCEIMMUNE))
|
||||
// Display local chat message
|
||||
action_owner.visible_message(span_danger("[action_owner] tries to bite down on [bite_target]'s [target_zone_name], but can't seem to pierce [bite_target.p_them()]!"), span_danger("You try to bite down on [bite_target]'s [target_zone_name], but are completely unable to pierce [bite_target.p_them()]!"))
|
||||
|
||||
// Check for voracious
|
||||
if(HAS_TRAIT(action_owner, TRAIT_VORACIOUS))
|
||||
// Make times twice as fast
|
||||
time_cooldown *= 0.5
|
||||
time_interact*= 0.5
|
||||
// Warn bite target
|
||||
to_chat(bite_target, span_userdanger("[action_owner] tries to bite your [target_zone_name], but is unable to piece you!"))
|
||||
|
||||
// Set cooldown
|
||||
drain_cooldown = world.time + time_cooldown
|
||||
// Return without further effects
|
||||
return
|
||||
|
||||
// Check for target zone special effects
|
||||
if(target_zone_effects)
|
||||
// Check if biting eyes or mouth
|
||||
if((target_zone == BODY_ZONE_PRECISE_EYES) || (target_zone == BODY_ZONE_PRECISE_MOUTH))
|
||||
// Check if biting target with proto-type face
|
||||
// Snout type is a string that cannot use subtype search
|
||||
if(findtext(bite_target.dna?.features["mam_snouts"], "Synthetic Lizard"))
|
||||
// Display local chat message
|
||||
action_owner.visible_message(span_notice("[action_owner]'s fangs clank harmlessly against [bite_target]'s face screen!"), span_notice("Your fangs clank harmlessly against [bite_target]'s face screen!"))
|
||||
|
||||
// Play glass tap sound
|
||||
playsound(bite_target, 'sound/effects/Glasshit.ogg', 30, 1, -2)
|
||||
|
||||
// Start cooldown early to prevent spam
|
||||
StartCooldown()
|
||||
|
||||
// Return without further effects
|
||||
return
|
||||
|
||||
// Check for strange bite regions
|
||||
switch(target_zone)
|
||||
// Zone is eyes
|
||||
if(BODY_ZONE_PRECISE_EYES)
|
||||
// Define target's eyes
|
||||
var/obj/item/organ/eyes/target_eyes = bite_target.getorganslot(ORGAN_SLOT_EYES)
|
||||
|
||||
// Check if eyes exist
|
||||
if(target_eyes)
|
||||
// Display warning
|
||||
to_chat(bite_target, span_userdanger("Your [target_eyes] rupture in pain as [action_owner]'s fangs pierce their surface!"))
|
||||
|
||||
// Blur vision
|
||||
bite_target.blur_eyes(10)
|
||||
|
||||
// Add organ damage
|
||||
target_eyes.applyOrganDamage(20)
|
||||
|
||||
// Zone is mouth
|
||||
if(BODY_ZONE_PRECISE_MOUTH)
|
||||
// Cause temporary stuttering
|
||||
bite_target.stuttering = 10
|
||||
|
||||
// Display local chat message
|
||||
action_owner.visible_message(span_danger("[action_owner] begins to bite down on [bite_target]'s neck!"))
|
||||
|
||||
// Warn bite target
|
||||
to_chat(bite_target, span_userdanger("[action_owner] has bitten your neck, and is trying to drain your blood!"))
|
||||
action_owner.visible_message(span_danger("[action_owner] bites down on [bite_target]'s [target_zone_name]!"), span_danger("You bite down on [bite_target]'s [target_zone_name]!"))
|
||||
|
||||
// Play a bite sound effect
|
||||
playsound(action_owner, 'sound/weapons/bite.ogg', 30, 1, -2)
|
||||
|
||||
// Check if bite target species has blood
|
||||
if(NOBLOOD in bite_target.dna?.species?.species_traits)
|
||||
// Warn the user and target
|
||||
to_chat(bite_target, span_warning("[action_owner] bit your [target_zone_name] in an attempt to drain your blood, but couldn't find any!"))
|
||||
to_chat(action_owner, span_warning("[bite_target] doesn't have any blood to drink!"))
|
||||
|
||||
// Start cooldown early to prevent sound spam
|
||||
StartCooldown()
|
||||
|
||||
// Return without effects
|
||||
return
|
||||
|
||||
// Warn bite target
|
||||
to_chat(bite_target, span_userdanger("[action_owner] has bitten your [target_zone_name], and is trying to drain your blood!"))
|
||||
|
||||
// Try to perform action timer
|
||||
if(!do_after(action_owner, time_interact, target = bite_target))
|
||||
// When failing
|
||||
// Display a local chat message
|
||||
action_owner.visible_message(span_danger("[action_owner]'s fangs are prematurely torn from [bite_target]'s neck, spilling [bite_target.p_their()] blood!"))
|
||||
action_owner.visible_message(span_danger("[action_owner]'s fangs are prematurely torn from [bite_target]'s [target_zone_name], spilling some of [bite_target.p_their()] blood!"), span_danger("Your fangs are prematurely torn from [bite_target]'s [target_zone_name], spilling some of [bite_target.p_their()] blood!"))
|
||||
|
||||
// Bite target "drops" the blood
|
||||
// Bite target "drops" 20% of the blood
|
||||
// This creates large blood splatter
|
||||
bite_target.bleed(BLOODFLEDGE_DRAIN_NUM, FALSE)
|
||||
bite_target.bleed((BLOODFLEDGE_DRAIN_NUM*0.2), FALSE)
|
||||
|
||||
// Play splatter sound
|
||||
playsound(get_turf(target), 'sound/effects/splat.ogg', 40, 1)
|
||||
@@ -417,21 +668,26 @@
|
||||
// Log the biting action failure
|
||||
log_combat(action_owner,bite_target,"bloodfledge bitten (interrupted)")
|
||||
|
||||
// Add target's blood to quirk holder and themselves
|
||||
bite_target.add_mob_blood(bite_target)
|
||||
action_owner.add_mob_blood(bite_target)
|
||||
|
||||
// Check if body part is valid for bleeding
|
||||
// This reuses the dismember-able check
|
||||
if(target_zone_check)
|
||||
// Cause minor bleeding
|
||||
bite_bodypart.generic_bleedstacks += 2
|
||||
|
||||
// Apply minor damage
|
||||
bite_bodypart.receive_damage(brute = rand(4,8), sharpness = SHARP_POINTY)
|
||||
|
||||
// Start cooldown early
|
||||
// This is to prevent bite interrupt spam
|
||||
StartCooldown()
|
||||
|
||||
// Return
|
||||
return
|
||||
|
||||
// Check if bite target species has blood
|
||||
if(NOBLOOD in bite_target.dna.species.species_traits)
|
||||
// Warn the user and target, then return
|
||||
to_chat(bite_target, span_warning("[action_owner] tried to drain you, but didn't find any blood!"))
|
||||
to_chat(action_owner, span_warning("[bite_target] doesn't have any blood to drink!"))
|
||||
return
|
||||
|
||||
// Create blood splatter
|
||||
bite_target.add_splatter_floor(get_turf(bite_target), TRUE)
|
||||
|
||||
// Checks for exotic species blood below
|
||||
|
||||
// Variable for species with non-blood blood volumes
|
||||
var/blood_valid = TRUE
|
||||
|
||||
@@ -442,81 +698,149 @@
|
||||
// Action owner assumes blood until after drinking
|
||||
var/blood_name = "blood"
|
||||
|
||||
// Check bite target for synth blood
|
||||
if(bite_target.mob_biotypes & MOB_ROBOTIC)
|
||||
// Mark blood as invalid
|
||||
blood_valid = FALSE
|
||||
// Check if target has exotic blood
|
||||
if(bite_target.dna?.species?.exotic_bloodtype)
|
||||
// Define blood types for owner and target
|
||||
var/blood_type_owner = action_owner.dna?.species?.exotic_bloodtype
|
||||
var/blood_type_target = bite_target.dna?.species?.exotic_bloodtype
|
||||
|
||||
// Set blood type name
|
||||
blood_name = "coolant"
|
||||
// Define if blood types match
|
||||
var/blood_type_match = (blood_type_owner == blood_type_target ? TRUE : FALSE)
|
||||
|
||||
// Check if the action owner is also a synth
|
||||
if (action_owner.mob_biotypes & MOB_ROBOTIC)
|
||||
// Allow gaining blood from this
|
||||
blood_transfer = TRUE
|
||||
// Check if types matched
|
||||
if(blood_type_match)
|
||||
// Add positive mood
|
||||
SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_exotic_match", /datum/mood_event/drank_exotic_matched)
|
||||
|
||||
// Action owner is not a synth
|
||||
else
|
||||
// Warn the user
|
||||
to_chat(action_owner, span_warning("That didn't taste like blood at all..."))
|
||||
// Switch for target's blood type
|
||||
switch(blood_type_target)
|
||||
// Synth blood
|
||||
if("S")
|
||||
// Mark blood as invalid
|
||||
blood_valid = FALSE
|
||||
|
||||
// Add disgust
|
||||
action_owner.adjust_disgust(2)
|
||||
// Set blood type name
|
||||
blood_name = "coolant"
|
||||
|
||||
// Cause negative mood
|
||||
SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_synth", /datum/mood_event/drankblood_synth)
|
||||
// Check if blood types match
|
||||
if(blood_type_match)
|
||||
// Allow transferring blood from this
|
||||
blood_transfer = TRUE
|
||||
|
||||
// Check if bite target is a slime
|
||||
if (isslimeperson(bite_target))
|
||||
// Mark blood as invalid
|
||||
blood_valid = FALSE
|
||||
// Blood types do not match
|
||||
else
|
||||
// Warn the user
|
||||
to_chat(action_owner, span_warning("That didn't taste like blood at all..."))
|
||||
|
||||
// Set blood type name
|
||||
blood_name = "slime"
|
||||
// Add disgust
|
||||
action_owner.adjust_disgust(2)
|
||||
|
||||
// Check if the action owner is also a slime
|
||||
if(isslimeperson(action_owner))
|
||||
// Allow gaining blood from this
|
||||
blood_transfer = TRUE
|
||||
// Cause negative mood
|
||||
SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_synth", /datum/mood_event/drankblood_synth)
|
||||
|
||||
// Action owner is not a slime
|
||||
else
|
||||
// Warn the user
|
||||
to_chat(action_owner, span_warning("You feel a sloshing presence inside you, but it dies out after a few moments."))
|
||||
// Slime blood
|
||||
if("GEL")
|
||||
// Mark blood as invalid
|
||||
blood_valid = FALSE
|
||||
|
||||
// Add disgust
|
||||
action_owner.adjust_disgust(2)
|
||||
// Allow transferring blood from this
|
||||
blood_transfer = TRUE
|
||||
|
||||
// Cause negative mood
|
||||
SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_slime", /datum/mood_event/drankblood_slime)
|
||||
// Set blood type name
|
||||
blood_name = "slime"
|
||||
|
||||
// End of species blood checks
|
||||
// Check if blood types match
|
||||
if(!blood_type_match)
|
||||
// Cause negative mood
|
||||
SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_slime", /datum/mood_event/drankblood_slime)
|
||||
|
||||
// Bug blood
|
||||
if("BUG")
|
||||
// Set blood type name
|
||||
blood_name = "hemolymph"
|
||||
|
||||
// Check if blood types match
|
||||
if(!blood_type_match)
|
||||
// Mark blood as invalid
|
||||
blood_valid = FALSE
|
||||
|
||||
// Cause negative mood
|
||||
SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_insect", /datum/mood_event/drankblood_insect)
|
||||
|
||||
// Xenomorph blood
|
||||
if("X*")
|
||||
// Set blood type name
|
||||
blood_name = "xeno blood"
|
||||
|
||||
// Check if blood types match
|
||||
if(!blood_type_match)
|
||||
// Mark blood as invalid
|
||||
blood_valid = FALSE
|
||||
|
||||
// Cause negative mood
|
||||
SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_xeno", /datum/mood_event/drankblood_xeno)
|
||||
|
||||
// Lizard blood
|
||||
if("L")
|
||||
// Set blood type name
|
||||
blood_name = "reptilian blood"
|
||||
|
||||
// End of exotic blood checks
|
||||
|
||||
// Define user's remaining capacity to absorb blood
|
||||
var/blood_volume_difference = BLOOD_VOLUME_MAXIMUM - action_owner.blood_volume
|
||||
var/drained_blood = min(target_blood_volume, BLOODFLEDGE_DRAIN_NUM, blood_volume_difference)
|
||||
|
||||
// Remove blood from bite target
|
||||
bite_target.blood_volume = clamp(target_blood_volume - drained_blood, 0, BLOOD_VOLUME_MAXIMUM)
|
||||
|
||||
// Perform a blood transfer
|
||||
// This is done to transfer compatible diseases
|
||||
// Grants nothing, unless blood transfer variable is set
|
||||
bite_target.transfer_blood_to(action_owner, (blood_transfer ? drained_blood : 0), TRUE)
|
||||
|
||||
// Check if action owner received valid (nourishing) blood
|
||||
if(blood_valid)
|
||||
// Add blood reagent to the user
|
||||
action_owner.reagents.add_reagent(/datum/reagent/blood/, drained_blood)
|
||||
// Transfer reagents from target to action owner
|
||||
// Limited to a maximum 10% of bite amount (default 10u)
|
||||
bite_target.reagents.trans_to(action_owner, (drained_blood*0.1))
|
||||
|
||||
// Alert the bite target and local user of success
|
||||
// Yes, this is AFTER the message for non-valid blood
|
||||
to_chat(bite_target, span_danger("[action_owner] has taken some of your [blood_name]!"))
|
||||
to_chat(action_owner, span_notice("You've drained some of [bite_target]'s [blood_name]!"))
|
||||
|
||||
// Alert the action holder if blood volume limit was exceeded
|
||||
if(blood_transfer && (action_owner.blood_volume >= BLOOD_VOLUME_MAXIMUM))
|
||||
to_chat(action_owner, span_warning("You body fails to absorb any more [blood_name]. The remainder has been lost."))
|
||||
// Check if action owner received valid (nourishing) blood
|
||||
if(blood_valid)
|
||||
// Add blood reagent to reagent holder
|
||||
blood_bank.add_reagent(/datum/reagent/blood/, drained_blood, bite_target.get_blood_data())
|
||||
|
||||
// Set reaction type to INGEST
|
||||
blood_bank.reaction(action_owner, INGEST)
|
||||
|
||||
// Transfer reagent to action owner
|
||||
blood_bank.trans_to(action_owner, drained_blood)
|
||||
|
||||
// Remove all reagents
|
||||
blood_bank.remove_all()
|
||||
|
||||
// Check if blood transfer should occur
|
||||
else if(blood_transfer)
|
||||
// Check if action holder's blood volume limit was exceeded
|
||||
if(action_owner.blood_volume >= BLOOD_VOLUME_MAXIMUM)
|
||||
// Warn user
|
||||
to_chat(action_owner, span_warning("You body cannot integrate any more [blood_name]. The remainder will be lost."))
|
||||
|
||||
// Blood volume limit was not exceeded
|
||||
else
|
||||
// Alert user
|
||||
to_chat(action_owner, span_notice("You body integrates the [blood_name] directly, instead of processing it into nutrition."))
|
||||
|
||||
// Transfer blood directly
|
||||
bite_target.transfer_blood_to(action_owner, drained_blood, TRUE)
|
||||
|
||||
// Set drain amount to none
|
||||
// This prevents double removal
|
||||
drained_blood = 0
|
||||
|
||||
// Valid blood was not received
|
||||
// No direct blood transfer occurred
|
||||
else
|
||||
// Warn user of failure
|
||||
to_chat(action_owner, span_warning("Your body cannot process the [blood_name] into nourishment!"))
|
||||
|
||||
// Remove blood from bite target
|
||||
bite_target.blood_volume = clamp(target_blood_volume - drained_blood, 0, BLOOD_VOLUME_MAXIMUM)
|
||||
|
||||
// Play a heartbeat sound effect
|
||||
// This was changed to match bloodsucker
|
||||
@@ -562,15 +886,23 @@
|
||||
// Cause mood event
|
||||
SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_cursed_blood", mood_type)
|
||||
|
||||
// Start cooldown
|
||||
StartCooldown()
|
||||
|
||||
// Action: Revive
|
||||
/datum/action/bloodfledge/revive
|
||||
/datum/action/cooldown/bloodfledge/revive
|
||||
name = "Fledgling Revive"
|
||||
desc = "Expend all of your remaining energy to escape death."
|
||||
button_icon_state = "power_strength"
|
||||
cooldown_time = BLOODFLEDGE_COOLDOWN_REVIVE
|
||||
|
||||
/datum/action/bloodfledge/revive/Trigger()
|
||||
/datum/action/cooldown/bloodfledge/revive/Trigger()
|
||||
. = ..()
|
||||
|
||||
// Check parent return
|
||||
if(!.)
|
||||
return
|
||||
|
||||
// Define mob
|
||||
var/mob/living/carbon/human/action_owner = owner
|
||||
|
||||
@@ -579,7 +911,7 @@
|
||||
if(action_owner.stat != DEAD)
|
||||
// Warn user in chat
|
||||
to_chat(action_owner, "You can't use this ability while alive!")
|
||||
|
||||
|
||||
// Return
|
||||
return
|
||||
|
||||
@@ -594,6 +926,9 @@
|
||||
if(action_owner.nutrition <= NUTRITION_LEVEL_STARVING)
|
||||
revive_failed += "\n- You don't have enough blood left!"
|
||||
|
||||
/*
|
||||
* Removed to buff revivals
|
||||
*
|
||||
// Condition: Can be revived
|
||||
// This is used by revive(), and must be checked here to prevent false feedback
|
||||
if(!action_owner.can_be_revived())
|
||||
@@ -602,22 +937,23 @@
|
||||
// Condition: Damage limit, brute
|
||||
if(action_owner.getBruteLoss() >= MAX_REVIVE_BRUTE_DAMAGE)
|
||||
revive_failed += "\n- Your body is too battered!"
|
||||
|
||||
|
||||
// Condition: Damage limit, burn
|
||||
if(action_owner.getFireLoss() >= MAX_REVIVE_FIRE_DAMAGE)
|
||||
revive_failed += "\n- Your body is too badly burned!"
|
||||
*/
|
||||
|
||||
// Condition: Suicide
|
||||
if(action_owner.suiciding)
|
||||
revive_failed += "\n- You chose this path."
|
||||
revive_failed += "\n- You chose this path."
|
||||
|
||||
// Condition: No revivals
|
||||
if(HAS_TRAIT(action_owner, TRAIT_NOCLONE))
|
||||
revive_failed += "\n- You only had one chance."
|
||||
revive_failed += "\n- You only had one chance."
|
||||
|
||||
// Condition: Demonic contract
|
||||
if(action_owner.hellbound)
|
||||
revive_failed += "\n- The soul pact must be honored."
|
||||
revive_failed += "\n- The soul pact must be honored."
|
||||
|
||||
// Check for failure
|
||||
if(revive_failed)
|
||||
@@ -630,6 +966,48 @@
|
||||
// Return
|
||||
return
|
||||
|
||||
// Check if health is too low to use revive()
|
||||
if(action_owner.health <= HEALTH_THRESHOLD_DEAD)
|
||||
// Set health high enough to revive
|
||||
// Based on defib.dm
|
||||
|
||||
// Define damage values
|
||||
var/damage_brute = action_owner.getBruteLoss()
|
||||
var/damage_burn = action_owner.getFireLoss()
|
||||
var/damage_tox = action_owner.getToxLoss()
|
||||
var/damage_oxy = action_owner.getOxyLoss()
|
||||
var/damage_clone = action_owner.getCloneLoss()
|
||||
var/damage_brain = action_owner.getOrganLoss(ORGAN_SLOT_BRAIN)
|
||||
|
||||
// Define total damage
|
||||
var/damage_total = damage_brute + damage_burn + damage_tox + damage_oxy + damage_brain + damage_clone
|
||||
|
||||
// Define to prevent redundant math
|
||||
var/health_half_crit = action_owner.health - HALFWAYCRITDEATH
|
||||
|
||||
// Adjust damage types
|
||||
action_owner.adjustOxyLoss(health_half_crit * (damage_oxy / damage_total), 0)
|
||||
action_owner.adjustToxLoss(health_half_crit * (damage_tox / damage_total), 0)
|
||||
action_owner.adjustFireLoss(health_half_crit * (damage_burn / damage_total), 0)
|
||||
action_owner.adjustBruteLoss(health_half_crit * (damage_brute / damage_total), 0)
|
||||
action_owner.adjustCloneLoss(health_half_crit * (damage_clone / damage_total), 0)
|
||||
action_owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, health_half_crit * (damage_brain / damage_total))
|
||||
|
||||
// Update health
|
||||
action_owner.updatehealth()
|
||||
|
||||
// Check if revival is possible
|
||||
// This is used by revive(), and must be checked here to prevent false feedback
|
||||
if(!action_owner.can_be_revived())
|
||||
// Warn user
|
||||
to_chat(action_owner, span_warning("Despite your body's best attempts at mending, it remains too weak to revive! Something this terrible shouldn't be possible!"))
|
||||
|
||||
// Start cooldown anyway, since healing was performed
|
||||
StartCooldown()
|
||||
|
||||
// Return without revival
|
||||
return
|
||||
|
||||
// Define time dead
|
||||
// Used for revive policy
|
||||
var/time_dead = world.time - action_owner.timeofdeath
|
||||
@@ -669,6 +1047,9 @@
|
||||
// Log the revival and effective policy
|
||||
action_owner.log_message("revived using a vampire quirk ability after being dead for [time_dead] deciseconds. Considered [time_late? "late" : "memory-intact"] revival under configured policy limits.", LOG_GAME)
|
||||
|
||||
// Start cooldown
|
||||
StartCooldown()
|
||||
|
||||
//
|
||||
// Quirk: Werewolf
|
||||
//
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/obj/structure/closet/crate/coffin/examine(mob/user)
|
||||
. = ..()
|
||||
|
||||
// Define carbon user
|
||||
var/mob/living/carbon/coffin_examinee
|
||||
|
||||
// Check if carbon user exists
|
||||
if(!istype(coffin_examinee))
|
||||
return
|
||||
|
||||
// Check for bloodfledge
|
||||
if(isbloodfledge(coffin_examinee))
|
||||
. += span_cult("As a Bloodsucker Fledgling; You can use coffins like this one to heal your wounds and escape from death.")
|
||||
|
||||
/obj/structure/closet/crate/coffin/after_close(mob/living/coffin_toucher)
|
||||
. = ..()
|
||||
|
||||
// Iterate over carbon mobs inside
|
||||
for(var/mob/living/carbon/coffin_user in contents)
|
||||
// Check for bloodfledge
|
||||
if(isbloodfledge(coffin_user))
|
||||
// Check for synthetic
|
||||
if(coffin_user.mob_biotypes && (coffin_user.mob_biotypes & MOB_ROBOTIC))
|
||||
// Warn user and continue
|
||||
to_chat(coffin_user, span_warning("Your components don't respond to [src]'s sanguine connection! Regeneration will not be possible."))
|
||||
continue
|
||||
|
||||
// Check if vampire ability is allowed
|
||||
if(!coffin_user.allow_vampiric_ability())
|
||||
to_chat(coffin_user, span_warning("[src] fails to form a connection with your body amidst the strong magical interference! Something is blocking your connection to the other-world!"))
|
||||
// Function already contains message
|
||||
continue
|
||||
|
||||
// Define quirk entry
|
||||
var/datum/quirk/bloodfledge/quirk_target = locate() in coffin_user.roundstart_quirks
|
||||
|
||||
// Start processing
|
||||
START_PROCESSING(SSquirks, quirk_target)
|
||||
|
||||
// Alert user
|
||||
to_chat(coffin_user, span_nicegreen("[src] empowers your connection to the other-world, allowing your body to mend."))
|
||||
|
||||
/obj/structure/closet/crate/coffin/after_open(mob/living/coffin_toucher)
|
||||
. = ..()
|
||||
|
||||
// Define turf
|
||||
var/turf/coffin_turf = get_turf(src)
|
||||
|
||||
// Iterate over carbon mobs inside
|
||||
for(var/mob/living/carbon/coffin_user in coffin_turf.contents)
|
||||
// Check for bloodfledge
|
||||
if(isbloodfledge(coffin_user))
|
||||
// Define quirk entry
|
||||
var/datum/quirk/bloodfledge/quirk_target = locate() in coffin_user.roundstart_quirks
|
||||
|
||||
// Stop processing
|
||||
STOP_PROCESSING(SSquirks, quirk_target)
|
||||
|
||||
// Alert user
|
||||
to_chat(coffin_user, span_notice("[src] is no longer empowering you."))
|
||||
@@ -0,0 +1,5 @@
|
||||
/datum/antagonist/bloodsucker/New()
|
||||
. = ..()
|
||||
|
||||
// Add antagonist incompatible quirks
|
||||
LAZYADD(blacklisted_quirks, list(/datum/quirk/bloodfledge))
|
||||
@@ -1,5 +1,7 @@
|
||||
#define APPEARANCE_CATEGORY_COLUMN "<td valign='top' width='17%'>"
|
||||
#define MAX_MUTANT_ROWS 5
|
||||
#define GFLUID_ETHANOL_POWER_LIMIT 80
|
||||
#define GFLUID_RARITY_LIMIT REAGENT_VALUE_RARE
|
||||
|
||||
/datum/preferences
|
||||
max_save_slots = DEFAULT_SAVE_SLOTS
|
||||
@@ -8,8 +10,11 @@
|
||||
var/new_character_creator = TRUE // old/new character creator
|
||||
|
||||
/datum/preferences/New(client/C)
|
||||
// Check if readable fluids list exists
|
||||
// Please move this check a better location if possible
|
||||
if(!GLOB.genital_fluids_list)
|
||||
build_genital_fluids_list() //I DON'T KNOW where else to put it, ok??
|
||||
// Build list
|
||||
build_genital_fluids_list()
|
||||
|
||||
//Extra saves for donators
|
||||
max_save_slots = CONFIG_GET(number/base_save_slots)
|
||||
@@ -1201,123 +1206,545 @@
|
||||
. = ..()
|
||||
|
||||
/proc/build_genital_fluids_list()
|
||||
|
||||
var/list/blacklisted = list( //Nonos
|
||||
//Ethanol
|
||||
// Define disallowed reagents
|
||||
var/list/blacklisted = list(
|
||||
// Base ethanol
|
||||
/datum/reagent/consumable/ethanol,
|
||||
|
||||
//
|
||||
// Effect drinks
|
||||
//
|
||||
|
||||
// Removes dizziness, drowsiness, and sleeping
|
||||
/datum/reagent/consumable/ethanol/kahlua,
|
||||
|
||||
// Can cause organ loss and death
|
||||
/datum/reagent/consumable/ethanol/thirteenloko,
|
||||
|
||||
// Drugs the user
|
||||
/datum/reagent/consumable/ethanol/threemileisland,
|
||||
|
||||
// Causes hallucinations
|
||||
/datum/reagent/consumable/ethanol/absinthe,
|
||||
|
||||
// Heals body parts for assistants
|
||||
/datum/reagent/consumable/ethanol/hooch,
|
||||
|
||||
// Heals revolutionary antagonists
|
||||
/datum/reagent/consumable/ethanol/cuba_libre,
|
||||
|
||||
// Heals radiation for engineers
|
||||
/datum/reagent/consumable/ethanol/screwdrivercocktail,
|
||||
|
||||
// Restores blood volume
|
||||
/datum/reagent/consumable/ethanol/bloody_mary,
|
||||
/datum/reagent/consumable/ethanol/brave_bull,
|
||||
|
||||
// Causes the user to emit light
|
||||
/datum/reagent/consumable/ethanol/tequila_sunrise,
|
||||
|
||||
// Increases body temperature
|
||||
/datum/reagent/consumable/ethanol/toxins_special,
|
||||
|
||||
// Causes hallucinations
|
||||
/datum/reagent/consumable/ethanol/beepsky_smash,
|
||||
|
||||
// Heals brute and burn damage for dwarfs
|
||||
/datum/reagent/consumable/ethanol/manly_dorf,
|
||||
|
||||
// Drugs the user
|
||||
/datum/reagent/consumable/ethanol/manhattan_proj,
|
||||
|
||||
// Increases body temperature
|
||||
/datum/reagent/consumable/ethanol/antifreeze,
|
||||
|
||||
// Heals brute damage
|
||||
/datum/reagent/consumable/ethanol/barefoot,
|
||||
/datum/reagent/consumable/ethanol/barefoot,
|
||||
|
||||
// Increases body temperature
|
||||
/datum/reagent/consumable/ethanol/sbiten,
|
||||
|
||||
// Reduces body temperature
|
||||
/datum/reagent/consumable/ethanol/iced_beer,
|
||||
|
||||
// Grants points for changeling antagonist
|
||||
/datum/reagent/consumable/ethanol/changelingsting,
|
||||
|
||||
// Plays an explosion sound effect
|
||||
/datum/reagent/consumable/ethanol/syndicatebomb,
|
||||
|
||||
// Heals body parts for clowns
|
||||
/datum/reagent/consumable/ethanol/bananahonk,
|
||||
|
||||
// Heals body parts for mimes
|
||||
/datum/reagent/consumable/ethanol/silencer,
|
||||
|
||||
// Attracts nearby ores
|
||||
/datum/reagent/consumable/ethanol/fetching_fizz,
|
||||
|
||||
// Heals critical health users 'extremely quickly'
|
||||
/datum/reagent/consumable/ethanol/hearty_punch,
|
||||
|
||||
// Causes confusion, dizziness, slurring, sleep, and toxin damage
|
||||
/datum/reagent/consumable/ethanol/atomicbomb,
|
||||
|
||||
// Causes dizziness, slurring, confusion, drugging, and toxin damage
|
||||
/datum/reagent/consumable/ethanol/gargle_blaster,
|
||||
|
||||
// Causes brain damage, drugging, and dizziness
|
||||
/datum/reagent/consumable/ethanol/neurotoxin,
|
||||
|
||||
// Causes brain damage
|
||||
/datum/reagent/consumable/ethanol/neuroweak,
|
||||
|
||||
// Causes slurring, dizziness, drugging, jittering, and toxin damage
|
||||
/datum/reagent/consumable/ethanol/hippies_delight,
|
||||
|
||||
// Causes cult sluttering and stuttering
|
||||
/datum/reagent/consumable/ethanol/narsour,
|
||||
|
||||
// Causes clock cult slurring and stuttering
|
||||
/datum/reagent/consumable/ethanol/cogchamp,
|
||||
|
||||
// Heals body part and brute damage for some mobs
|
||||
/datum/reagent/consumable/ethanol/pinotmort,
|
||||
|
||||
// Heals body part and brute damage for security
|
||||
/datum/reagent/consumable/ethanol/quadruple_sec,
|
||||
|
||||
// Heals body part, brute, suffocation, burn, and toxin damage for security
|
||||
/datum/reagent/consumable/ethanol/quintuple_sec,
|
||||
|
||||
// Heals brute, burn, toxin, suffocation, and stamina damage
|
||||
/datum/reagent/consumable/ethanol/bastion_bourbon,
|
||||
|
||||
// Grants nutrition
|
||||
/datum/reagent/consumable/ethanol/squirt_cider,
|
||||
|
||||
// Grants nutrition
|
||||
/datum/reagent/consumable/ethanol/sugar_rush,
|
||||
/datum/reagent/consumable/ethanol/crevice_spike,
|
||||
|
||||
// Grants soothed throat effect and increases temperature
|
||||
/datum/reagent/consumable/ethanol/peppermint_patty,
|
||||
|
||||
// Removes mighty shield reagent
|
||||
/datum/reagent/consumable/ethanol/alexander,
|
||||
|
||||
// Heals brute and burn damage for sleeping users
|
||||
/datum/reagent/consumable/ethanol/between_the_sheets,
|
||||
|
||||
// Removes nutrition and causes toxin damage
|
||||
/datum/reagent/consumable/ethanol/fernet,
|
||||
|
||||
// Removes nutrition and causes toxin damage
|
||||
/datum/reagent/consumable/ethanol/fernet_cola,
|
||||
|
||||
// Removes nutrition and clears overeating duration
|
||||
/datum/reagent/consumable/ethanol/fanciulli,
|
||||
|
||||
// Reduces body temperature
|
||||
/datum/reagent/consumable/ethanol/branca_menta,
|
||||
|
||||
// Heals body part damage for mimes
|
||||
/datum/reagent/consumable/ethanol/blank_paper,
|
||||
|
||||
// Heals body part, suffocation, and toxin damage for wizards
|
||||
/datum/reagent/consumable/ethanol/wizz_fizz,
|
||||
|
||||
// Causes toxin damage to insects
|
||||
/datum/reagent/consumable/ethanol/bug_spray,
|
||||
|
||||
// Reduces stamina
|
||||
/datum/reagent/consumable/ethanol/turbo,
|
||||
|
||||
// Increases age, changes hair color, causes nearsightedness, causes a beard
|
||||
/datum/reagent/consumable/ethanol/old_timer,
|
||||
|
||||
// Heals burn damage, removes jittering, and removes stuttering for Chaplain
|
||||
/datum/reagent/consumable/ethanol/trappist,
|
||||
|
||||
// Teleports the user
|
||||
/datum/reagent/consumable/ethanol/blazaam,
|
||||
|
||||
// Increases temperature, and can cause ignition
|
||||
/datum/reagent/consumable/ethanol/mauna_loa,
|
||||
|
||||
// Heals body part, brute, suffocation, fire, foxin, and radiation for the Captain
|
||||
/datum/reagent/consumable/ethanol/commander_and_chief,
|
||||
|
||||
// Increases temperature
|
||||
/datum/reagent/consumable/ethanol/hellfire,
|
||||
//Drink reagents
|
||||
|
||||
// Causes drugging and stamina loss
|
||||
/datum/reagent/consumable/ethanol/hotlime_miami,
|
||||
|
||||
// Causes brute damage
|
||||
/datum/reagent/consumable/ethanol/crevice_spike,
|
||||
|
||||
/*
|
||||
* The following reagents have effects
|
||||
* But are too mild to warrant blacklisting
|
||||
*
|
||||
// Tints the user green
|
||||
/datum/reagent/consumable/ethanol/beer/green,
|
||||
|
||||
// Heals radiation
|
||||
/datum/reagent/consumable/ethanol/vodka,
|
||||
|
||||
// Heals brute loss
|
||||
/datum/reagent/consumable/ethanol/bilk,
|
||||
|
||||
// Plays an explosion sound effect
|
||||
/datum/reagent/consumable/ethanol/b52,
|
||||
|
||||
// Displays a chat message
|
||||
/datum/reagent/consumable/ethanol/gunfire,
|
||||
*/
|
||||
|
||||
//
|
||||
// SPLURT effect drinks
|
||||
//
|
||||
|
||||
/*
|
||||
* The following reagents have effects
|
||||
* But are allowed for humor purposes
|
||||
*
|
||||
// Causes clothing loss
|
||||
/datum/reagent/consumable/ethanol/panty_dropper,
|
||||
|
||||
// Causes brain damage
|
||||
/datum/reagent/consumable/ethanol/lean,
|
||||
*/
|
||||
|
||||
// Contains morphine
|
||||
/datum/reagent/consumable/ethanol/isloation_cell/morphine,
|
||||
|
||||
// Contains hexacrocin, morphine, and enthrall
|
||||
/datum/reagent/consumable/ethanol/chemical_ex,
|
||||
|
||||
// Captain drink
|
||||
/datum/reagent/consumable/ethanol/heart_of_gold,
|
||||
|
||||
// Captain drink
|
||||
/datum/reagent/consumable/ethanol/moth_in_chief,
|
||||
|
||||
// Replaces the tongue
|
||||
/datum/reagent/consumable/ethanol/skullfucker_deluxe,
|
||||
|
||||
// Heals brute, burn, and toxin or suffocation damage
|
||||
/datum/reagent/consumable/ethanol/ionstorm,
|
||||
|
||||
//
|
||||
// Effect drink reagents
|
||||
//
|
||||
|
||||
// Causes toxin damage
|
||||
/datum/reagent/consumable/poisonberryjuice,
|
||||
|
||||
// Heals body parts for clown
|
||||
/datum/reagent/consumable/banana,
|
||||
|
||||
// Heals body parts for mime
|
||||
/datum/reagent/consumable/nothing,
|
||||
|
||||
// Causes forced laughter emote and mood event
|
||||
/datum/reagent/consumable/laughter,
|
||||
|
||||
// Causes stun and mood event
|
||||
/datum/reagent/consumable/superlaughter,
|
||||
/datum/reagent/consumable/soymilk, //No soy shall come from any titty
|
||||
|
||||
// Heals brute, fire, toxin, and suffocation damage, and reduces nutrition for non-doctors
|
||||
/datum/reagent/consumable/doctor_delight,
|
||||
|
||||
// Reduces size
|
||||
/datum/reagent/consumable/red_queen,
|
||||
|
||||
// Causes stamina loss, forced emote, chat messages, and arousal
|
||||
/datum/reagent/consumable/catnip_tea,
|
||||
|
||||
// Heals toxin damage
|
||||
/datum/reagent/consumable/aloejuice,
|
||||
|
||||
/*
|
||||
* The following reagents have effects
|
||||
* But are allowed for humor purposes
|
||||
*
|
||||
|
||||
// Heals body part and brute damage, removes capsaicin, and heals body parts for calcium healers
|
||||
/datum/reagent/consumable/milk,
|
||||
|
||||
// Heals body parts
|
||||
/datum/reagent/consumable/soymilk,
|
||||
|
||||
// Heals body parts
|
||||
/datum/reagent/consumable/coconutmilk,
|
||||
|
||||
// Heals body parts
|
||||
/datum/reagent/consumable/cream,
|
||||
*/
|
||||
|
||||
/*
|
||||
* The following reagents have effects
|
||||
* But are too mild to warrant blacklisting
|
||||
*
|
||||
|
||||
// Heals suffocation damage
|
||||
/datum/reagent/consumable/orangejuice,
|
||||
|
||||
// Heals burn damage
|
||||
/datum/reagent/consumable/tomatojuice,
|
||||
|
||||
// Heals body parts
|
||||
/datum/reagent/consumable/tomatojuice,
|
||||
|
||||
// Heals blurred vision, blindness, and nearsightedness
|
||||
/datum/reagent/consumable/carrotjuice,
|
||||
|
||||
// Removes dizziness, drowsiness, and sleeping, increases temperature, and removes frost oil
|
||||
/datum/reagent/consumable/coffee,
|
||||
|
||||
// Reduces dizziness, drowsiness, jittering, and sleeping, heals toxin damage, and increases temperature
|
||||
/datum/reagent/consumable/tea,
|
||||
|
||||
// Reduces nutrition, dizziness, drowsiness, and jittering, and increases temperature
|
||||
/datum/reagent/consumable/tea/red,
|
||||
|
||||
// Heals liver damage, reduces dizziness, drowsiness, and jittering, and increases temperature
|
||||
/datum/reagent/consumable/tea/green,
|
||||
|
||||
// Heals toxin damage, reduces dizziness, drowsiness, and jittering, and increases temperature
|
||||
/datum/reagent/consumable/tea/forest,
|
||||
|
||||
// Causes drugging and dizziness, removes all disgust
|
||||
/datum/reagent/consumable/tea/mush,
|
||||
|
||||
// Displays chat messages
|
||||
/datum/reagent/consumable/tea/arnold_palmer,
|
||||
|
||||
// Reduces dizziness, drowsiness, jittering, and sleeping, and reduces temperature
|
||||
/datum/reagent/consumable/icecoffee,
|
||||
|
||||
// Reduces dizziness, drowsiness, and sleeping, heals toxin damage, and reduces temperature
|
||||
/datum/reagent/consumable/icetea,
|
||||
|
||||
// Reduces drowsiness and temperature
|
||||
/datum/reagent/consumable/space_cola,
|
||||
|
||||
// Causes jittering, drugging, and dizziness, removes drowsiness, reduces sleeping and temperature
|
||||
/datum/reagent/consumable/nuka_cola,
|
||||
|
||||
// Reduces drowsiness, sleeping, and temperature, and causes jittering
|
||||
/datum/reagent/consumable/spacemountainwind,
|
||||
|
||||
// Reduces temperature
|
||||
/datum/reagent/consumable/space_up,
|
||||
|
||||
// Reduces temperature
|
||||
/datum/reagent/consumable/lemon_lime,
|
||||
|
||||
// Reduces temperature
|
||||
/datum/reagent/consumable/pwr_game,
|
||||
|
||||
// Reduces temperature
|
||||
/datum/reagent/consumable/shamblers,
|
||||
|
||||
// Adds sugar or honey
|
||||
/datum/reagent/consumable/buzz_fuzz,
|
||||
|
||||
// Causes jittering, and dizziness, removes drowsiness, reduces sleeping and temperature
|
||||
/datum/reagent/consumable/grey_bull,
|
||||
|
||||
// Reduces dizziness, and drowsiness, and temperature
|
||||
/datum/reagent/consumable/sodawater,
|
||||
|
||||
// Reduces dizziness, and drowsiness, and temperature
|
||||
/datum/reagent/consumable/tonic,
|
||||
|
||||
// Reduces dizziness and drowsiness, removes sleeping, increases temperature and jittering, heals body parts
|
||||
/datum/reagent/consumable/soy_latte,
|
||||
//Normal reagents
|
||||
|
||||
// Reduces dizziness and drowsiness, removes sleeping, increases temperature and jittering, heals body parts
|
||||
/datum/reagent/consumable/cafe_latte,
|
||||
|
||||
// Reduces temperature
|
||||
/datum/reagent/consumable/grape_soda,
|
||||
|
||||
// Causes throat soothed effect
|
||||
/datum/reagent/consumable/menthol,
|
||||
|
||||
// Reduces temperature
|
||||
/datum/reagent/consumable/cream_soda,
|
||||
|
||||
// Reduces disgust
|
||||
/datum/reagent/consumable/sol_dry,
|
||||
|
||||
// Causes chat messages
|
||||
/datum/reagent/consumable/milk/pinkmilk,
|
||||
|
||||
// Causes chat messages
|
||||
/datum/reagent/consumable/tea/pinktea,
|
||||
|
||||
// Causes jittering, and dizziness, removes drowsiness, reduces sleeping and temperature
|
||||
/datum/reagent/consumable/monkey_energy,
|
||||
*/
|
||||
|
||||
//
|
||||
// Effect standard reagents
|
||||
//
|
||||
|
||||
// Grants nutrition
|
||||
/datum/reagent/consumable/nutriment/vitamin,
|
||||
|
||||
// Can cause hyperglycemic shock (sleeping)
|
||||
/datum/reagent/consumable/sugar,
|
||||
|
||||
// Increases temperature
|
||||
/datum/reagent/consumable/capsaicin,
|
||||
|
||||
// Reduces temperature
|
||||
/datum/reagent/consumable/frostoil,
|
||||
|
||||
// Causes coughing, and can be used for stuns
|
||||
/datum/reagent/consumable/condensedcapsaicin,
|
||||
|
||||
// Heals the cook, but damages vampires
|
||||
/datum/reagent/consumable/garlic,
|
||||
|
||||
// Heals body part damage
|
||||
/datum/reagent/consumable/sprinkles,
|
||||
/datum/reagent/consumable/enzyme,
|
||||
|
||||
// Increases temperature
|
||||
/datum/reagent/consumable/hot_ramen,
|
||||
|
||||
// Increases temperature
|
||||
/datum/reagent/consumable/hell_ramen,
|
||||
|
||||
// Adds sugar reagent
|
||||
/datum/reagent/consumable/corn_syrup,
|
||||
|
||||
// Adds sugar, heals brute, burn, suffocation, and toxin
|
||||
/datum/reagent/consumable/honey,
|
||||
|
||||
// Causes temporary blindness and blurred vision
|
||||
/datum/reagent/consumable/tearjuice,
|
||||
|
||||
// Causes unconsciousness, breath loss, brain damage, toxin damage, stamina loss, and blurred vision
|
||||
/datum/reagent/consumable/entpoly,
|
||||
|
||||
// Heals brute and burn damage
|
||||
/datum/reagent/consumable/vitfro,
|
||||
|
||||
// Causes electrocution
|
||||
/datum/reagent/consumable/liquidelectricity,
|
||||
|
||||
// Causes forced speech
|
||||
/datum/reagent/consumable/char,
|
||||
/datum/reagent/consumable/laughsyrup,
|
||||
/datum/reagent/consumable/honey, //zad
|
||||
|
||||
// Secret reagent, makes all food max quality
|
||||
/datum/reagent/consumable/secretsauce,
|
||||
|
||||
// Used for making most food
|
||||
/datum/reagent/consumable/enzyme,
|
||||
|
||||
/*
|
||||
* The following reagents have effects
|
||||
* But are too mild to warrant blacklisting
|
||||
*
|
||||
// Increases temperature
|
||||
/datum/reagent/consumable/hot_coco,
|
||||
*/
|
||||
)
|
||||
|
||||
GLOB.genital_fluids_list = list()
|
||||
// Define base list
|
||||
var/list/consumable_list = subtypesof(/datum/reagent/consumable)
|
||||
|
||||
var/list/paths = subtypesof(/datum/reagent/consumable)
|
||||
LAZYADD(paths, list(
|
||||
// Define additional allowed reagents
|
||||
var/list/whitelist_list = list(
|
||||
// Just water
|
||||
/datum/reagent/water,
|
||||
|
||||
// Causes arousal
|
||||
// Allowed for ERP reasons
|
||||
/datum/reagent/drug/aphrodisiac,
|
||||
/datum/reagent/drug/copium,
|
||||
/datum/reagent/blood
|
||||
))
|
||||
|
||||
for(var/path in paths)
|
||||
var/datum/reagent/instance = find_reagent_object_from_type(path)
|
||||
// Causes positive mood bonus
|
||||
// On overdose: Causes negative mood penalty and disgust
|
||||
/datum/reagent/drug/copium/gfluid,
|
||||
|
||||
// Restores blood volume
|
||||
/datum/reagent/blood,
|
||||
)
|
||||
|
||||
// Add whitelisted entries to main list
|
||||
LAZYADD(consumable_list, whitelist_list)
|
||||
|
||||
// Define final list
|
||||
var/list/reagent_list
|
||||
|
||||
// Define final type-based list
|
||||
var/list/reagent_list_paths
|
||||
|
||||
for(var/reagent in consumable_list)
|
||||
// Define reagent
|
||||
var/datum/reagent/instance = find_reagent_object_from_type(reagent)
|
||||
|
||||
// Check if reagent exists
|
||||
if(!instance)
|
||||
continue
|
||||
if(path in blacklisted)
|
||||
continue
|
||||
if(istype(instance, /datum/reagent/consumable/ethanol))
|
||||
var/datum/reagent/consumable/ethanol/drink = instance
|
||||
if(drink.boozepwr > 80)
|
||||
continue
|
||||
|
||||
// Check if reagent is non-liquid
|
||||
if(instance.reagent_state != LIQUID)
|
||||
// Ignore reagent
|
||||
continue
|
||||
|
||||
LAZYADD(GLOB.genital_fluids_list, instance)
|
||||
// Check if reagent is blacklisted
|
||||
if(reagent in blacklisted)
|
||||
// Ignore reagent
|
||||
continue
|
||||
|
||||
// Check if reagent is manually whitelisted
|
||||
if(reagent in whitelist_list)
|
||||
// Add immediately
|
||||
LAZYADD(reagent_list, instance)
|
||||
LAZYADD(reagent_list_paths, reagent)
|
||||
|
||||
// Skip further processing
|
||||
continue
|
||||
|
||||
// Check if reagent exceeds rarity limit
|
||||
if(instance.value >= GFLUID_RARITY_LIMIT)
|
||||
// Ignore reagent
|
||||
continue
|
||||
|
||||
// Check if reagent is an ethanol sub-type
|
||||
if(istype(instance, /datum/reagent/consumable/ethanol))
|
||||
// Define ethanol reagent
|
||||
var/datum/reagent/consumable/ethanol/drink = instance
|
||||
|
||||
// Check if booze power exceeds the defined limit
|
||||
if(drink.boozepwr > GFLUID_ETHANOL_POWER_LIMIT)
|
||||
// Ignore reagent
|
||||
continue
|
||||
|
||||
// Add reagent to final list
|
||||
LAZYADD(reagent_list, instance)
|
||||
|
||||
// Add reagent to type list
|
||||
LAZYADD(reagent_list_paths, reagent)
|
||||
|
||||
// Define readable GLOB
|
||||
GLOB.genital_fluids_list = reagent_list
|
||||
|
||||
// Define type-path GLOB
|
||||
GLOB.genital_fluids_paths = reagent_list_paths
|
||||
|
||||
/proc/allowed_gfluid_paths()
|
||||
if(!GLOB.genital_fluids_list)
|
||||
// Check if paths list exists
|
||||
if(!GLOB.genital_fluids_paths)
|
||||
// Build list
|
||||
build_genital_fluids_list()
|
||||
|
||||
var/list/allowed
|
||||
for(var/datum/reagent/fluid in GLOB.genital_fluids_list)
|
||||
LAZYADD(allowed, fluid.type)
|
||||
|
||||
return allowed
|
||||
// Return list of valid types
|
||||
return GLOB.genital_fluids_paths
|
||||
|
||||
#undef APPEARANCE_CATEGORY_COLUMN
|
||||
#undef MAX_MUTANT_ROWS
|
||||
|
||||
@@ -341,3 +341,33 @@
|
||||
icon_state = "explorerstripper"
|
||||
mob_overlay_icon = 'modular_splurt/icons/mob/clothing/uniform.dmi'
|
||||
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
|
||||
|
||||
// Prison Jumpsuit/Jumpskirt override to allow reskins
|
||||
/obj/item/clothing/under/rank/prisoner
|
||||
reskin_binding = COMSIG_CLICK_CTRL
|
||||
unique_reskin = list(
|
||||
"max-sec" = list("icon_state" = "maxprisoner", "icon" = 'modular_splurt/icons/obj/clothing/uniforms.dmi', "mob_overlay_icon" = 'modular_splurt/icons/mobs/clothing/uniform.dmi', "anthro_mob_worn_overlay" = 'modular_splurt/icons/mobs/clothing/uniform_digi.dmi'),
|
||||
"high-sec" = list("icon_state" = "highprisoner", "icon" = 'modular_splurt/icons/obj/clothing/uniforms.dmi', "mob_overlay_icon" = 'modular_splurt/icons/mobs/clothing/uniform.dmi', "anthro_mob_worn_overlay" = 'modular_splurt/icons/mobs/clothing/uniform_digi.dmi'),
|
||||
"med-sec" = list("icon_state" = "prisoner", "icon" = 'icons/obj/clothing/uniforms.dmi', "mob_overlay_icon" = null, "anthro_mob_worn_overlay" = null),
|
||||
"low-sec" = list("icon_state" = "lowprisoner", "icon" = 'modular_splurt/icons/obj/clothing/uniforms.dmi', "mob_overlay_icon" = 'modular_splurt/icons/mobs/clothing/uniform.dmi', "anthro_mob_worn_overlay" = 'modular_splurt/icons/mobs/clothing/uniform_digi.dmi'),
|
||||
"prot-sec" = list("icon_state" = "protprisoner", "icon" = 'modular_splurt/icons/obj/clothing/uniforms.dmi', "mob_overlay_icon" = 'modular_splurt/icons/mobs/clothing/uniform.dmi', "anthro_mob_worn_overlay" = 'modular_splurt/icons/mobs/clothing/uniform_digi.dmi'),
|
||||
"vampire" = list("icon_state" = "blackprisoner", "icon" = 'modular_splurt/icons/obj/clothing/uniforms.dmi', "mob_overlay_icon" = 'modular_splurt/icons/mobs/clothing/uniform.dmi', "anthro_mob_worn_overlay" = 'modular_splurt/icons/mobs/clothing/uniform_digi.dmi')
|
||||
)
|
||||
|
||||
/obj/item/clothing/under/rank/prisoner/reskin_obj(mob/M)
|
||||
. = ..()
|
||||
name = "prison [current_skin] jumpsuit"
|
||||
|
||||
/obj/item/clothing/under/rank/prisoner/skirt
|
||||
unique_reskin = list(
|
||||
"max-sec" = list("icon_state" = "maxprisoner_skirt", "icon" = 'modular_splurt/icons/obj/clothing/uniforms.dmi', "mob_overlay_icon" = 'modular_splurt/icons/mobs/clothing/uniform.dmi'),
|
||||
"high-sec" = list("icon_state" = "highprisoner_skirt", "icon" = 'modular_splurt/icons/obj/clothing/uniforms.dmi', "mob_overlay_icon" = 'modular_splurt/icons/mobs/clothing/uniform.dmi'),
|
||||
"med-sec" = list("icon_state" = "prisoner_skirt", "icon" = 'icons/obj/clothing/uniforms.dmi', "mob_overlay_icon" = null),
|
||||
"low-sec" = list("icon_state" = "lowprisoner_skirt", "icon" = 'modular_splurt/icons/obj/clothing/uniforms.dmi', "mob_overlay_icon" = 'modular_splurt/icons/mobs/clothing/uniform.dmi'),
|
||||
"prot-sec" = list("icon_state" = "protprisoner_skirt", "icon" = 'modular_splurt/icons/obj/clothing/uniforms.dmi', "mob_overlay_icon" = 'modular_splurt/icons/mobs/clothing/uniform.dmi'),
|
||||
"vampire" = list("icon_state" = "blackprisoner_skirt", "icon" = 'modular_splurt/icons/obj/clothing/uniforms.dmi', "mob_overlay_icon" = 'modular_splurt/icons/mobs/clothing/uniform.dmi')
|
||||
)
|
||||
|
||||
/obj/item/clothing/under/rank/prisoner/skirt/reskin_obj(mob/M)
|
||||
. = ..()
|
||||
name = "prison [current_skin] jumpskirt"
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// Potion of flight
|
||||
/obj/item/reagent_containers/glass/bottle/potion/flight/attack(mob/living/target, mob/living/user)
|
||||
// Check for self attack
|
||||
// Check for carbon target
|
||||
if((target != user) || (!iscarbon(user)))
|
||||
// Return normally
|
||||
return ..()
|
||||
|
||||
// Check for bloodfledge
|
||||
if(isbloodfledge(user))
|
||||
// Define list of options
|
||||
var/list/prompt_options = list("Confirm", "Cancel")
|
||||
|
||||
// Prompt user for input
|
||||
var/input_warning = tgui_alert(user, "You sense that drinking this may be a bad idea. Are you sure you'd like to continue?",src,prompt_options)
|
||||
|
||||
// Sanitize input
|
||||
sanitize_inlist(input_warning, prompt_options)
|
||||
|
||||
// Check if input was NOT confirmation
|
||||
if(input_warning != "Confirm")
|
||||
// Return without results
|
||||
return
|
||||
|
||||
// Return normally
|
||||
. = ..()
|
||||
@@ -3,3 +3,9 @@
|
||||
if(id in (CONFIG_GET(keyed_list/roundstart_races)))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/species/vampire/roundstart/New()
|
||||
. = ..()
|
||||
|
||||
// Add species incompatible quirks
|
||||
LAZYADD(blacklisted_quirks, list(/datum/quirk/bloodfledge))
|
||||
|
||||
@@ -647,6 +647,18 @@
|
||||
emote_sound = 'modular_splurt/sound/voice/waterphone.ogg'
|
||||
emote_cooldown = 3.4 SECONDS
|
||||
|
||||
/datum/emote/living/audio/taunt
|
||||
key = "tt"
|
||||
key_third_person = "taunts"
|
||||
message = "strikes a pose!"
|
||||
message_param = "taunts %t!"
|
||||
emote_sound = 'modular_splurt/sound/voice/phillyhit.ogg'
|
||||
|
||||
/datum/emote/living/audio/taunt/alt
|
||||
key = "tt2"
|
||||
key_third_person = "taunts2"
|
||||
emote_sound = 'modular_splurt/sound/voice/orchestrahit.ogg'
|
||||
|
||||
/datum/emote/living/audio/weh2
|
||||
key = "weh2"
|
||||
key_third_person = "wehs2"
|
||||
@@ -679,6 +691,42 @@
|
||||
emote_sound = 'modular_splurt/sound/voice/waa.ogg'
|
||||
emote_cooldown = 3.5 SECONDS
|
||||
|
||||
/datum/emote/living/audio/bark2
|
||||
key = "bark2"
|
||||
key_third_person = "barks2"
|
||||
message = "barks!"
|
||||
message_mime = "acts out a bark!"
|
||||
emote_sound = 'modular_splurt/sound/voice/bark_alt.ogg'
|
||||
emote_cooldown = 0.35 SECONDS
|
||||
|
||||
/datum/emote/living/audio/yap
|
||||
key = "yap"
|
||||
key_third_person = "yaps"
|
||||
message = "yaps!"
|
||||
message_mime = "acts out a yap!"
|
||||
emote_sound = 'modular_splurt/sound/voice/yap.ogg'
|
||||
emote_cooldown = 0.28 SECONDS
|
||||
|
||||
/datum/emote/living/audio/howl
|
||||
key = "howl"
|
||||
key_third_person = "howls"
|
||||
message = "howls!"
|
||||
message_mime = "acts out a howl!"
|
||||
emote_sound = 'modular_splurt/sound/voice/wolfhowl.ogg'
|
||||
emote_cooldown = 2.04 SECONDS
|
||||
|
||||
/datum/emote/living/audio/coyhowl
|
||||
key = "coyhowl"
|
||||
key_third_person = "coyhowls"
|
||||
message = "howls like coyote!"
|
||||
message_mime = "acts out a coyote's howl!"
|
||||
emote_sound = 'modular_splurt/sound/voice/coyotehowl.ogg'
|
||||
emote_cooldown = 2.94 SECONDS // Uses longest sound's time
|
||||
|
||||
/datum/emote/living/audio/coyhowl/run_emote(mob/user, params)
|
||||
emote_sound = pick('modular_splurt/sound/voice/coyotehowl.ogg', 'modular_splurt/sound/voice/coyotehowl2.ogg', 'modular_splurt/sound/voice/coyotehowl3.ogg', 'modular_splurt/sound/voice/coyotehowl4.ogg', 'modular_splurt/sound/voice/coyotehowl5.ogg')
|
||||
. = ..()
|
||||
|
||||
/datum/emote/living/mlem
|
||||
key = "mlem"
|
||||
key_third_person = "mlems"
|
||||
|
||||
@@ -340,12 +340,12 @@ SLEEPER CODE IS IN game/objects/items/devices/dogborg_sleeper.dm !
|
||||
|
||||
else
|
||||
leaping = 1
|
||||
weather_immunities += "lava"
|
||||
//weather_immunities += "lava"
|
||||
pixel_y = 10
|
||||
update_icons()
|
||||
throw_at(A, MAX_K9_LEAP_DIST, 1, spin=0, diagonals_first = 1)
|
||||
cell.use(750) //Less than a stunbaton since stunbatons hit everytime.
|
||||
weather_immunities -= "lava"
|
||||
//weather_immunities -= "lava"
|
||||
|
||||
/mob/living/silicon/robot/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
|
||||
|
||||
|
||||
@@ -41,3 +41,41 @@
|
||||
if (.)
|
||||
return
|
||||
to_chat(A, span_notice("[src] seems to be checking you out."))
|
||||
|
||||
/mob/proc/allow_vampiric_ability(check_anti_magic = TRUE, check_holy = TRUE, check_garlic_neck = TRUE, check_garlic_blood = TRUE, check_stake = TRUE, silent = TRUE)
|
||||
// Check if carbon
|
||||
if(!iscarbon(src))
|
||||
// Warn user and return false
|
||||
if(!silent)
|
||||
to_chat(src, span_warning("Your body cannot form connections to the other-world!"))
|
||||
return FALSE
|
||||
|
||||
// Check for anti-magic variables
|
||||
if(check_anti_magic || check_holy)
|
||||
// Check for anti-magic
|
||||
if(src.anti_magic_check(check_anti_magic, check_holy, FALSE, 0, TRUE))
|
||||
// Warn user and return false
|
||||
if(!silent)
|
||||
to_chat(src, span_warning("A powerful anti-magic force is blocking your connection to the other-world!"))
|
||||
return FALSE
|
||||
|
||||
// Check for anti-garlic variables
|
||||
if(check_garlic_neck || check_garlic_blood)
|
||||
// Check bloodsucker checks
|
||||
if(!blood_sucking_checks(src, check_garlic_neck, check_garlic_blood))
|
||||
// Warn user and return false
|
||||
if(!silent)
|
||||
to_chat(src, span_warning("The warding power of Allium Sativum prevents you from using any sanguine powers!"))
|
||||
return FALSE
|
||||
|
||||
// Check for stake variable
|
||||
if(check_stake)
|
||||
// Check for stake
|
||||
if(src.AmStaked())
|
||||
// Warn user and return false
|
||||
if(!silent)
|
||||
to_chat(src, span_warning("You are staked! You must remove the offending weapon from your heart before using any sanguine powers!"))
|
||||
return FALSE
|
||||
|
||||
// All checks passed
|
||||
return TRUE
|
||||
|
||||
@@ -90,6 +90,10 @@
|
||||
gas = GAS_COPIUM
|
||||
value = REAGENT_VALUE_GLORIOUS
|
||||
|
||||
// Variant of Copium created by genital fluids
|
||||
/datum/reagent/drug/copium/gfluid
|
||||
value = REAGENT_VALUE_COMMON
|
||||
|
||||
/datum/reagent/drug/copium/on_mob_life(mob/living/carbon/M)
|
||||
. = ..()
|
||||
|
||||
|
||||
@@ -36,12 +36,29 @@
|
||||
if(HAS_TRAIT(M,TRAIT_INCUBUS))
|
||||
M.adjust_nutrition(1.5)
|
||||
|
||||
/datum/reagent/blood/on_mob_life(mob/living/carbon/C)
|
||||
/datum/reagent/blood/reaction_mob(mob/living/carbon/M, method=TOUCH, reac_volume)
|
||||
. = ..()
|
||||
if(HAS_TRAIT(C,TRAIT_BLOODFLEDGE))
|
||||
C.adjust_nutrition(6)
|
||||
C.adjust_disgust(-2) // Negates the chapel's disgust effect
|
||||
C.adjustStaminaLoss(1) // Mitigates the chapel's stamina effect
|
||||
// Check if ingested
|
||||
if(method != INGEST)
|
||||
return
|
||||
|
||||
// Check if blood data exists
|
||||
if(!data)
|
||||
// Log warning and return
|
||||
log_game("[M] attempted to ingest blood that had no data!")
|
||||
return
|
||||
|
||||
// Check for Bloodfledge quirk
|
||||
if(HAS_TRAIT(M,TRAIT_BLOODFLEDGE))
|
||||
// Check for own blood
|
||||
if(data["donor"] == M)
|
||||
// Warn user and return
|
||||
to_chat(M, span_warning("You gain no nourishment from the familiar blood..."))
|
||||
return
|
||||
|
||||
// Add nutrition reagent
|
||||
// Reduced to 50%
|
||||
M.reagents.add_reagent(/datum/reagent/consumable/notriment, reac_volume*0.5)
|
||||
|
||||
/datum/reagent/water/holywater/on_mob_life(mob/living/carbon/M)
|
||||
. = ..()
|
||||
@@ -60,27 +77,59 @@
|
||||
// Escape clause: 12% chance to continue
|
||||
if(!prob(12))
|
||||
return
|
||||
|
||||
|
||||
// Character speaks nonsense
|
||||
M.say(pick("Somebody help me...","Unshackle me please...","Anybody... I've had enough of this dream...","The night blocks all sight...","Oh, somebody, please..."), forced = "holy water")
|
||||
|
||||
|
||||
// Escape clause: 10% chance to continue
|
||||
if(!prob(10))
|
||||
return
|
||||
|
||||
|
||||
// Character has a seisure
|
||||
M.visible_message(span_danger("[M] starts having a seizure!"), span_userdanger("You have a seizure!"))
|
||||
M.Unconscious(120)
|
||||
to_chat(M, "<span class='cultlarge'>[pick("The moon is close. It will be a long hunt tonight.", "Ludwig, why have you forsaken me?", \
|
||||
"The night is near its end...", "Fear the blood...")]</span>")
|
||||
|
||||
|
||||
// Apply damage
|
||||
M.adjustToxLoss(1, 0)
|
||||
M.adjustFireLoss(1, 0)
|
||||
|
||||
|
||||
// Escape clause: 25% chance to continue
|
||||
if(!prob(25))
|
||||
return
|
||||
|
||||
|
||||
// Spontaneous combustion
|
||||
M.IgniteMob()
|
||||
|
||||
// This is used by 'alternative food' quirks
|
||||
// It should not be used for any other purpose
|
||||
/datum/reagent/consumable/notriment
|
||||
name = "Strange Nutriment"
|
||||
description = "An exotic form of nutriment produced by unusual digestive systems."
|
||||
reagent_state = SOLID
|
||||
nutriment_factor = 5 // From 4
|
||||
metabolization_rate = 1 // From 0.4
|
||||
max_nutrition = NUTRITION_LEVEL_FAT // From INFINITY
|
||||
color = "#66552f" // rgb: 102, 85, 47
|
||||
|
||||
/datum/reagent/consumable/notriment/reaction_mob(mob/living/carbon/M, method=TOUCH, reac_volume)
|
||||
// Check if mob can process food
|
||||
if(!HAS_TRAIT(M, TRAIT_NO_PROCESS_FOOD))
|
||||
// Warn user
|
||||
to_chat(M, span_warning("Your body is incapable of processing the Strange Nutriment!"))
|
||||
|
||||
// Remove reagent
|
||||
M.reagents.remove_reagent(/datum/reagent/consumable/notriment/, reac_volume)
|
||||
|
||||
// Ignore this mob
|
||||
return
|
||||
|
||||
// Return normally
|
||||
. = ..()
|
||||
|
||||
/datum/reagent/consumable/notriment/on_mob_life(mob/living/carbon/M)
|
||||
. = ..()
|
||||
|
||||
// Add nutrition
|
||||
M.adjust_nutrition(nutriment_factor, max_nutrition)
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
var/mob/living/carbon/human/cord_user = user
|
||||
|
||||
// Check for bloodfledge
|
||||
if(HAS_TRAIT(cord_user, TRAIT_BLOODFLEDGE))
|
||||
if(isbloodfledge(cord_user))
|
||||
// Warn user and return
|
||||
to_chat(cord_user, span_warning("You try to siphon energy from [target], but a sanguine force prevents you from absorbing any charge!"))
|
||||
return
|
||||
|
||||
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 32 KiB |
@@ -235,6 +235,7 @@
|
||||
#include "code\__HELPERS\sorts\InsertSort.dm"
|
||||
#include "code\__HELPERS\sorts\MergeSort.dm"
|
||||
#include "code\__HELPERS\sorts\TimSort.dm"
|
||||
#include "code\__SANDCODE\DEFINES\access.dm"
|
||||
#include "code\__SANDCODE\DEFINES\chat.dm"
|
||||
#include "code\__SANDCODE\DEFINES\DNA.dm"
|
||||
#include "code\__SANDCODE\DEFINES\keybindings.dm"
|
||||
@@ -856,7 +857,6 @@
|
||||
#include "code\datums\traits\negative.dm"
|
||||
#include "code\datums\traits\neutral.dm"
|
||||
#include "code\datums\weather\weather.dm"
|
||||
#include "code\datums\weather\weather_types\acid_rain.dm"
|
||||
#include "code\datums\weather\weather_types\ash_storm.dm"
|
||||
#include "code\datums\weather\weather_types\floor_is_lava.dm"
|
||||
#include "code\datums\weather\weather_types\ice_storm.dm"
|
||||
@@ -4475,6 +4475,7 @@
|
||||
#include "modular_splurt\code\game\objects\structures\bed_chairs\sofa.dm"
|
||||
#include "modular_splurt\code\game\objects\structures\cannons\cannon.dm"
|
||||
#include "modular_splurt\code\game\objects\structures\cannons\cannonballs.dm"
|
||||
#include "modular_splurt\code\game\objects\structures\crates_lockers\crates.dm"
|
||||
#include "modular_splurt\code\game\objects\structures\crates_lockers\closets\fitness.dm"
|
||||
#include "modular_splurt\code\game\objects\structures\crates_lockers\closets\slaver.dm"
|
||||
#include "modular_splurt\code\game\objects\structures\crates_lockers\closets\secure\psychology.dm"
|
||||
@@ -4501,6 +4502,7 @@
|
||||
#include "modular_splurt\code\modules\admin\verbs\randomverbs.dm"
|
||||
#include "modular_splurt\code\modules\admin\verbs\vpnbunker.dm"
|
||||
#include "modular_splurt\code\modules\antagonists\_common\antag_spawner.dm"
|
||||
#include "modular_splurt\code\modules\antagonists\bloodsucker\datum_bloodsucker.dm"
|
||||
#include "modular_splurt\code\modules\antagonists\bloodsucker\levelup.dm"
|
||||
#include "modular_splurt\code\modules\antagonists\ert_cleanup\ert_cleanup.dm"
|
||||
#include "modular_splurt\code\modules\antagonists\qareen\qareen.dm"
|
||||
@@ -4688,6 +4690,7 @@
|
||||
#include "modular_splurt\code\modules\mentor\mentor_verbs.dm"
|
||||
#include "modular_splurt\code\modules\mining\equipment\kinetic_crusher.dm"
|
||||
#include "modular_splurt\code\modules\mining\equipment\machine_vending.dm"
|
||||
#include "modular_splurt\code\modules\mining\lavaland\necropolis_chests.dm"
|
||||
#include "modular_splurt\code\modules\mob\emote.dm"
|
||||
#include "modular_splurt\code\modules\mob\mob.dm"
|
||||
#include "modular_splurt\code\modules\mob\mob_defines.dm"
|
||||
|
||||
@@ -2,23 +2,18 @@ import { map, sortBy } from 'common/collections';
|
||||
import { flow } from 'common/fp';
|
||||
import { pureComponentHooks } from 'common/react';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Box, Button, Dimmer, Flex, Icon, Table, Tabs } from '../components';
|
||||
import { Box, Button, Dimmer, Icon, Table, Tabs, Stack, Section } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
import { AreaCharge, powerRank } from './PowerMonitor';
|
||||
|
||||
export const ApcControl = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
return (
|
||||
<Window
|
||||
title="APC Controller"
|
||||
width={550}
|
||||
height={500}>
|
||||
{data.authenticated === 1 && (
|
||||
<ApcLoggedIn />
|
||||
)}
|
||||
{data.authenticated === 0 && (
|
||||
<ApcLoggedOut />
|
||||
)}
|
||||
<Window title="APC Controller" width={550} height={500}>
|
||||
<Window.Content>
|
||||
{data.authenticated === 1 && <ApcLoggedIn />}
|
||||
{data.authenticated === 0 && <ApcLoggedOut />}
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
@@ -28,25 +23,24 @@ const ApcLoggedOut = (props, context) => {
|
||||
const { emagged } = data;
|
||||
const text = emagged === 1 ? 'Open' : 'Log In';
|
||||
return (
|
||||
<Window.Content>
|
||||
<Section>
|
||||
<Button
|
||||
fluid
|
||||
icon="sign-in-alt"
|
||||
color={emagged === 1 ? '' : 'good'}
|
||||
content={text}
|
||||
onClick={() => act('log-in')} />
|
||||
</Window.Content>
|
||||
fluid
|
||||
onClick={() => act('log-in')}
|
||||
/>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const ApcLoggedIn = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { restoring } = data;
|
||||
const [
|
||||
tabIndex,
|
||||
setTabIndex,
|
||||
] = useLocalState(context, 'tab-index', 1);
|
||||
const [tabIndex, setTabIndex] = useLocalState(context, 'tab-index', 1);
|
||||
return (
|
||||
<>
|
||||
<Box>
|
||||
<Tabs>
|
||||
<Tabs.Tab
|
||||
selected={tabIndex === 1}
|
||||
@@ -72,59 +66,62 @@ const ApcLoggedIn = (props, context) => {
|
||||
</Dimmer>
|
||||
)}
|
||||
{tabIndex === 1 && (
|
||||
<>
|
||||
<ControlPanel />
|
||||
<Box fillPositionedParent top="53px">
|
||||
<Window.Content overflow="auto">
|
||||
<Stack vertical>
|
||||
<Stack.Item>
|
||||
<Section>
|
||||
<ControlPanel />
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Section scrollable>
|
||||
<ApcControlScene />
|
||||
</Window.Content>
|
||||
</Box>
|
||||
</>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
)}
|
||||
{tabIndex === 2 && (
|
||||
<Box fillPositionedParent top="20px">
|
||||
<Window.Content overflow="auto">
|
||||
<Section scrollable>
|
||||
<Box height={34}>
|
||||
<LogPanel />
|
||||
</Window.Content>
|
||||
</Box>
|
||||
</Box>
|
||||
</Section>
|
||||
)}
|
||||
</>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const ControlPanel = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
emagged,
|
||||
logging,
|
||||
} = data;
|
||||
const [
|
||||
sortByField,
|
||||
setSortByField,
|
||||
] = useLocalState(context, 'sortByField', null);
|
||||
const { emagged, logging } = data;
|
||||
const [sortByField, setSortByField] = useLocalState(
|
||||
context,
|
||||
'sortByField',
|
||||
'name'
|
||||
);
|
||||
return (
|
||||
<Flex>
|
||||
<Flex.Item>
|
||||
<Stack justify="space-between">
|
||||
<Stack.Item>
|
||||
<Box inline mr={2} color="label">
|
||||
Sort by:
|
||||
</Box>
|
||||
<Button.Checkbox
|
||||
checked={sortByField === 'name'}
|
||||
content="Name"
|
||||
onClick={() => setSortByField(sortByField !== 'name' && 'name')} />
|
||||
onClick={() => setSortByField(sortByField !== 'name' && 'name')}
|
||||
/>
|
||||
<Button.Checkbox
|
||||
checked={sortByField === 'charge'}
|
||||
content="Charge"
|
||||
onClick={() => setSortByField(
|
||||
sortByField !== 'charge' && 'charge'
|
||||
)} />
|
||||
onClick={() => setSortByField(sortByField !== 'charge' && 'charge')}
|
||||
/>
|
||||
<Button.Checkbox
|
||||
checked={sortByField === 'draw'}
|
||||
content="Draw"
|
||||
onClick={() => setSortByField(sortByField !== 'draw' && 'draw')} />
|
||||
</Flex.Item>
|
||||
<Flex.Item grow={1} />
|
||||
<Flex.Item>
|
||||
onClick={() => setSortByField(sortByField !== 'draw' && 'draw')}
|
||||
/>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow={1} />
|
||||
<Stack.Item>
|
||||
{emagged === 1 && (
|
||||
<>
|
||||
<Button
|
||||
@@ -139,21 +136,20 @@ const ControlPanel = (props, context) => {
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
icon="sign-out-alt"
|
||||
color="bad"
|
||||
content="Log Out"
|
||||
onClick={() => act('log-out')}
|
||||
/>
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const ApcControlScene = (props, context) => {
|
||||
const { data, act } = useBackend(context);
|
||||
|
||||
const [
|
||||
sortByField,
|
||||
] = useLocalState(context, 'sortByField', null);
|
||||
const [sortByField] = useLocalState(context, 'sortByField', 'name');
|
||||
|
||||
const apcs = flow([
|
||||
map((apc, i) => ({
|
||||
@@ -161,94 +157,87 @@ const ApcControlScene = (props, context) => {
|
||||
// Generate a unique id
|
||||
id: apc.name + i,
|
||||
})),
|
||||
sortByField === 'name' && sortBy(apc => apc.name),
|
||||
sortByField === 'charge' && sortBy(apc => -apc.charge),
|
||||
sortByField === 'draw' && sortBy(
|
||||
apc => -powerRank(apc.load),
|
||||
apc => -parseFloat(apc.load)),
|
||||
sortByField === 'name' && sortBy((apc) => apc.name),
|
||||
sortByField === 'charge' && sortBy((apc) => -apc.charge),
|
||||
sortByField === 'draw'
|
||||
&& sortBy(
|
||||
(apc) => -powerRank(apc.load),
|
||||
(apc) => -parseFloat(apc.load)
|
||||
),
|
||||
])(data.apcs);
|
||||
return (
|
||||
<Table>
|
||||
<Table.Row header>
|
||||
<Table.Cell>
|
||||
On/Off
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
Area
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing>
|
||||
Charge
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing textAlign="right">
|
||||
Draw
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing title="Equipment">
|
||||
Eqp
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing title="Lighting">
|
||||
Lgt
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing title="Environment">
|
||||
Env
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{apcs.map((apc, i) => (
|
||||
<tr
|
||||
key={apc.id}
|
||||
className="Table__row candystripe">
|
||||
<td>
|
||||
<Button
|
||||
icon={apc.operating ? 'power-off' : 'times'}
|
||||
color={apc.operating ? 'good' : 'bad'}
|
||||
onClick={() => act('breaker', {
|
||||
ref: apc.ref,
|
||||
})}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<Button
|
||||
onClick={() => act('access-apc', {
|
||||
ref: apc.ref,
|
||||
})}>
|
||||
{apc.name}
|
||||
</Button>
|
||||
</td>
|
||||
<td className="Table__cell text-right text-nowrap">
|
||||
<AreaCharge
|
||||
charging={apc.charging}
|
||||
charge={apc.charge}
|
||||
/>
|
||||
</td>
|
||||
<td className="Table__cell text-right text-nowrap">
|
||||
{apc.load}
|
||||
</td>
|
||||
<td className="Table__cell text-center text-nowrap">
|
||||
<AreaStatusColorButton
|
||||
target="equipment"
|
||||
status={apc.eqp}
|
||||
apc={apc}
|
||||
act={act}
|
||||
/>
|
||||
</td>
|
||||
<td className="Table__cell text-center text-nowrap">
|
||||
<AreaStatusColorButton
|
||||
target="lighting"
|
||||
status={apc.lgt}
|
||||
apc={apc}
|
||||
act={act}
|
||||
/>
|
||||
</td>
|
||||
<td className="Table__cell text-center text-nowrap">
|
||||
<AreaStatusColorButton
|
||||
target="environ"
|
||||
status={apc.env}
|
||||
apc={apc}
|
||||
act={act}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</Table>
|
||||
<Box height={30}>
|
||||
<Table>
|
||||
<Table.Row header>
|
||||
<Table.Cell>On/Off</Table.Cell>
|
||||
<Table.Cell>Area</Table.Cell>
|
||||
<Table.Cell collapsing>Charge</Table.Cell>
|
||||
<Table.Cell collapsing textAlign="right">
|
||||
Draw
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing title="Equipment">
|
||||
Eqp
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing title="Lighting">
|
||||
Lgt
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing title="Environment">
|
||||
Env
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{apcs.map((apc, i) => (
|
||||
<tr key={apc.id} className="Table__row candystripe">
|
||||
<td>
|
||||
<Button
|
||||
icon={apc.operating ? 'power-off' : 'times'}
|
||||
color={apc.operating ? 'good' : 'bad'}
|
||||
onClick={() =>
|
||||
act('breaker', {
|
||||
ref: apc.ref,
|
||||
})}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<Button
|
||||
onClick={() =>
|
||||
act('access-apc', {
|
||||
ref: apc.ref,
|
||||
})}>
|
||||
{apc.name}
|
||||
</Button>
|
||||
</td>
|
||||
<td className="Table__cell text-right text-nowrap">
|
||||
<AreaCharge charging={apc.charging} charge={apc.charge} />
|
||||
</td>
|
||||
<td className="Table__cell text-right text-nowrap">{apc.load}</td>
|
||||
<td className="Table__cell text-center text-nowrap">
|
||||
<AreaStatusColorButton
|
||||
target="equipment"
|
||||
status={apc.eqp}
|
||||
apc={apc}
|
||||
act={act}
|
||||
/>
|
||||
</td>
|
||||
<td className="Table__cell text-center text-nowrap">
|
||||
<AreaStatusColorButton
|
||||
target="lighting"
|
||||
status={apc.lgt}
|
||||
apc={apc}
|
||||
act={act}
|
||||
/>
|
||||
</td>
|
||||
<td className="Table__cell text-center text-nowrap">
|
||||
<AreaStatusColorButton
|
||||
target="environ"
|
||||
status={apc.env}
|
||||
apc={apc}
|
||||
act={act}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</Table>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -261,16 +250,12 @@ const LogPanel = (props, context) => {
|
||||
// Generate a unique id
|
||||
id: line.entry + i,
|
||||
})),
|
||||
logs => logs.reverse(),
|
||||
(logs) => logs.reverse(),
|
||||
])(data.logs);
|
||||
return (
|
||||
<Box m={-0.5}>
|
||||
{logs.map(line => (
|
||||
<Box
|
||||
p={0.5}
|
||||
key={line.id}
|
||||
className="candystripe"
|
||||
bold>
|
||||
{logs.map((line) => (
|
||||
<Box p={0.5} key={line.id} className="candystripe" bold>
|
||||
{line.entry}
|
||||
</Box>
|
||||
))}
|
||||
@@ -278,7 +263,7 @@ const LogPanel = (props, context) => {
|
||||
);
|
||||
};
|
||||
|
||||
const AreaStatusColorButton = props => {
|
||||
const AreaStatusColorButton = (props) => {
|
||||
const { target, status, apc, act } = props;
|
||||
const power = Boolean(status & 2);
|
||||
const mode = Boolean(status & 1);
|
||||
@@ -286,20 +271,20 @@ const AreaStatusColorButton = props => {
|
||||
<Button
|
||||
icon={mode ? 'sync' : 'power-off'}
|
||||
color={power ? 'good' : 'bad'}
|
||||
onClick={() => act('toggle-minor', {
|
||||
type: target,
|
||||
value: statusChange(status),
|
||||
ref: apc.ref,
|
||||
})}
|
||||
onClick={() =>
|
||||
act('toggle-minor', {
|
||||
type: target,
|
||||
value: statusChange(status),
|
||||
ref: apc.ref,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const statusChange = status => {
|
||||
const statusChange = (status) => {
|
||||
// mode flip power flip both flip
|
||||
// 0, 2, 3
|
||||
return status === 0 ? 2 : status === 2 ? 3 : 0;
|
||||
};
|
||||
|
||||
AreaStatusColorButton.defaultHooks = pureComponentHooks;
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
import { useBackend } from '../backend';
|
||||
import { Box, Button, LabeledList, Section } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const CellularEmporium = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { abilities } = data;
|
||||
return (
|
||||
<Window
|
||||
width={900}
|
||||
height={480}>
|
||||
<Window.Content overflow="auto">
|
||||
<Section>
|
||||
<LabeledList>
|
||||
<LabeledList.Item
|
||||
label="Genetic Points"
|
||||
buttons={(
|
||||
<Button
|
||||
icon="undo"
|
||||
content="Readapt"
|
||||
disabled={!data.can_readapt}
|
||||
onClick={() => act('readapt')} />
|
||||
)}>
|
||||
{data.genetic_points_remaining}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
<Section>
|
||||
<LabeledList>
|
||||
{abilities.map(ability => (
|
||||
<LabeledList.Item
|
||||
key={ability.name}
|
||||
className="candystripe"
|
||||
label={ability.name}
|
||||
buttons={(
|
||||
<>
|
||||
{ability.dna_cost}
|
||||
{' '}
|
||||
<Button
|
||||
content={ability.owned ? 'Evolved' : 'Evolve'}
|
||||
selected={ability.owned}
|
||||
onClick={() => act('evolve', {
|
||||
name: ability.name,
|
||||
})} />
|
||||
</>
|
||||
)}>
|
||||
{ability.desc}
|
||||
<Box color="good">
|
||||
{ability.helptext}
|
||||
</Box>
|
||||
</LabeledList.Item>
|
||||
))}
|
||||
</LabeledList>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,97 @@
|
||||
import { useBackend } from '../backend';
|
||||
import { Button, Section, Icon, Stack, LabeledList, Box, NoticeBox } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
type CellularEmporiumContext = {
|
||||
abilities: Ability[];
|
||||
can_readapt: boolean;
|
||||
genetic_points_remaining: number;
|
||||
};
|
||||
|
||||
type Ability = {
|
||||
name: string;
|
||||
desc: string;
|
||||
path: string;
|
||||
dna_cost: number;
|
||||
helptext: string;
|
||||
owned: boolean;
|
||||
can_purchase: boolean;
|
||||
};
|
||||
|
||||
export const CellularEmporium = (props, context) => {
|
||||
const { act, data } = useBackend<CellularEmporiumContext>(context);
|
||||
const { can_readapt, genetic_points_remaining } = data;
|
||||
return (
|
||||
<Window width={900} height={480}>
|
||||
<Window.Content>
|
||||
<Section
|
||||
fill
|
||||
scrollable
|
||||
title={'Genetic Points'}
|
||||
buttons={
|
||||
<Stack>
|
||||
<Stack.Item fontSize="16px">
|
||||
{genetic_points_remaining && genetic_points_remaining}{' '}
|
||||
<Icon name="dna" color="#DD66DD" />
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button
|
||||
icon="undo"
|
||||
content="Readapt"
|
||||
disabled={!can_readapt}
|
||||
onClick={() => act('readapt')}
|
||||
/>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
}>
|
||||
<AbilityList />
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const AbilityList = (props, context) => {
|
||||
const { act, data } = useBackend<CellularEmporiumContext>(context);
|
||||
const { abilities, genetic_points_remaining } = data;
|
||||
|
||||
if (!abilities) {
|
||||
return <NoticeBox>None</NoticeBox>;
|
||||
} else {
|
||||
return (
|
||||
<LabeledList>
|
||||
{abilities.map((ability) => (
|
||||
<LabeledList.Item
|
||||
key={ability.name}
|
||||
className="candystripe"
|
||||
label={ability.name}
|
||||
buttons={
|
||||
<Stack>
|
||||
<Stack.Item>{ability.dna_cost}</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Icon name="dna" color={ability.owned ? '#DD66DD' : 'gray'} />
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button
|
||||
content={'Evolve'}
|
||||
disabled={
|
||||
ability.owned
|
||||
|| ability.dna_cost > genetic_points_remaining
|
||||
|| !ability.can_purchase
|
||||
}
|
||||
onClick={() =>
|
||||
act('evolve', {
|
||||
name: ability.name,
|
||||
})}
|
||||
/>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
}>
|
||||
{ability.desc}
|
||||
<Box color="good">{ability.helptext}</Box>
|
||||
</LabeledList.Item>
|
||||
))}
|
||||
</LabeledList>
|
||||
);
|
||||
}
|
||||
};
|
||||