Merge pull request #10541 from Detective-Google/the-p-o-o-l
adds the POOL
@@ -17,4 +17,4 @@
|
||||
#define MOVE_FORCE_NORMAL MOVE_FORCE_DEFAULT
|
||||
#define MOVE_FORCE_WEAK (MOVE_FORCE_DEFAULT / 2)
|
||||
#define MOVE_FORCE_VERY_WEAK ((MOVE_FORCE_DEFAULT / MOVE_FORCE_CRUSH_RATIO) + 1)
|
||||
#define MOVE_FORCE_EXTREMELY_WEAK (MOVE_FORCE_DEFAULT / (MOVE_FORCE_CRUSH_RATIO * 3))
|
||||
#define MOVE_FORCE_EXTREMELY_WEAK (MOVE_FORCE_DEFAULT / (MOVE_FORCE_CRUSH_RATIO * 3))
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
//TODO: move these to their own file
|
||||
#define POOL_FRIGID 1
|
||||
#define POOL_COOL 2
|
||||
#define POOL_NORMAL 3
|
||||
#define POOL_WARM 4
|
||||
#define POOL_SCALDING 5
|
||||
|
||||
GLOBAL_LIST_INIT(blacklisted_pool_reagents, list(
|
||||
/datum/reagent/toxin/plasma, /datum/reagent/oxygen, /datum/reagent/nitrous_oxide, /datum/reagent/nitrogen, //gases
|
||||
/datum/reagent/fermi, //blanket fermichem ban sorry. this also covers mkultra, genital enlargers, etc etc.
|
||||
/datum/reagent/drug/aphrodisiac, /datum/reagent/drug/anaphrodisiac, /datum/reagent/drug/aphrodisiacplus, /datum/reagent/drug/anaphrodisiacplus, //literally asking for prefbreaks
|
||||
/datum/reagent/consumable/femcum, /datum/reagent/consumable/semen //NO.
|
||||
))
|
||||
@@ -124,6 +124,8 @@
|
||||
#define TRAIT_ALCOHOL_LIGHTWEIGHT "alcohol_lightweight" //Skyrat port
|
||||
#define TRAIT_CURSED_BLOOD "cursed_blood" //Yo dawg I heard you like bloodborne references so I put a
|
||||
|
||||
#define TRAIT_SWIMMING "swimming" //only applied by /datum/element/swimming, for checking
|
||||
|
||||
//non-mob traits
|
||||
#define TRAIT_PARALYSIS "paralysis" //Used for limb-based paralysis, where replacing the limb will fix it
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/// Just for marking when someone's swimming.
|
||||
/datum/element/swimming
|
||||
element_flags = ELEMENT_DETACH
|
||||
|
||||
/datum/element/swimming/Attach(datum/target)
|
||||
if((. = ..()) == ELEMENT_INCOMPATIBLE)
|
||||
return
|
||||
if(!isliving(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/check_valid)
|
||||
ADD_TRAIT(target, TRAIT_SWIMMING, TRAIT_SWIMMING) //seriously there's only one way to get this
|
||||
|
||||
/datum/element/swimming/Detach(datum/target)
|
||||
. = ..()
|
||||
UnregisterSignal(target, COMSIG_MOVABLE_MOVED)
|
||||
REMOVE_TRAIT(target, TRAIT_SWIMMING, TRAIT_SWIMMING)
|
||||
|
||||
/datum/element/swimming/proc/check_valid(datum/source)
|
||||
var/mob/living/L = source
|
||||
if(!istype(L.loc, /turf/open/pool))
|
||||
source.RemoveElement(/datum/element/swimming)
|
||||
@@ -1366,3 +1366,8 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
|
||||
/area/xenoarch/med
|
||||
name = "Xenoarchaeology Medical"
|
||||
icon_state = "xenomed"
|
||||
|
||||
//Pool
|
||||
/area/crew_quarters/fitness/pool
|
||||
name = "Pool Area"
|
||||
icon_state = "pool"
|
||||
|
||||
@@ -184,6 +184,8 @@
|
||||
soundin = pick('sound/items/bikehorn.ogg', 'sound/items/AirHorn2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/items/AirHorn.ogg', 'sound/effects/reee.ogg', 'sound/items/WEEOO1.ogg', 'sound/voice/beepsky/iamthelaw.ogg', 'sound/voice/beepsky/creep.ogg','sound/magic/Fireball.ogg' ,'sound/effects/pray.ogg', 'sound/voice/hiss1.ogg','sound/machines/buzz-sigh.ogg', 'sound/machines/ping.ogg', 'sound/weapons/flashbang.ogg', 'sound/weapons/bladeslice.ogg')
|
||||
if("goose")
|
||||
soundin = pick('sound/creatures/goose1.ogg', 'sound/creatures/goose2.ogg', 'sound/creatures/goose3.ogg', 'sound/creatures/goose4.ogg')
|
||||
if("water_wade")
|
||||
soundin = pick('sound/effects/water_wade1.ogg', 'sound/effects/water_wade2.ogg', 'sound/effects/water_wade3.ogg', 'sound/effects/water_wade4.ogg')
|
||||
//START OF CIT CHANGES - adds random vore sounds
|
||||
if ("struggle_sound")
|
||||
soundin = pick( 'sound/vore/pred/struggle_01.ogg','sound/vore/pred/struggle_02.ogg','sound/vore/pred/struggle_03.ogg',
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
//shameless copies of carps.
|
||||
|
||||
/mob/living/simple_animal/hostile/shark
|
||||
name = "Space Shark"
|
||||
desc = "The best terror of the seas, next to the kraken."
|
||||
icon_state = "shark"
|
||||
icon_living = "shark"
|
||||
icon = 'icons/mob/sharks.dmi'
|
||||
icon_dead = "shark_dead"
|
||||
icon_gib = "carp_gib"
|
||||
environment_smash = 0
|
||||
speak_chance = 0
|
||||
turns_per_move = 3
|
||||
butcher_results = list(/obj/item/reagent_containers/food/snacks/carpmeat = 3)
|
||||
response_help = "pets"
|
||||
response_disarm = "gently pushes aside"
|
||||
response_harm = "hits"
|
||||
speed = 0
|
||||
maxHealth = 75
|
||||
health = 75
|
||||
harm_intent_damage = 18
|
||||
melee_damage_lower = 18
|
||||
melee_damage_upper = 18
|
||||
attacktext = "maims"
|
||||
attack_sound = 'sound/weapons/bite.ogg'
|
||||
gold_core_spawnable = 1
|
||||
//Space shark aren't affected by cold.
|
||||
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)
|
||||
minbodytemp = 0
|
||||
maxbodytemp = 1500
|
||||
|
||||
faction = list("shark")
|
||||
|
||||
/mob/living/simple_animal/hostile/shark/Process_Spacemove(var/movement_dir = 0)
|
||||
return 1 //No drifting in space for space sharks....either!
|
||||
|
||||
/mob/living/simple_animal/hostile/shark/FindTarget()
|
||||
. = ..()
|
||||
if(.)
|
||||
emote("me", 1, "growls at [.]!")
|
||||
|
||||
/mob/living/simple_animal/hostile/shark/AttackingTarget()
|
||||
. =..()
|
||||
var/mob/living/carbon/L = .
|
||||
if(istype(L))
|
||||
if(prob(25))
|
||||
L.Knockdown(20)
|
||||
L.visible_message("<span class='danger'>\the [src] knocks down \the [L]!</span>")
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/shark/laser
|
||||
name = "Laser-Shark"
|
||||
desc = "NOW we've jumped the shark."
|
||||
icon_state = "lasershark"
|
||||
icon_living = "lasershark"
|
||||
icon_dead = "lasershark_dead"
|
||||
icon_gib = "carp_gib"
|
||||
ranged = 1
|
||||
retreat_distance = 3
|
||||
minimum_distance = 0 //Between shots they can and will close in to nash
|
||||
projectiletype = /obj/item/projectile/beam/laser/heavylaser
|
||||
projectilesound = 'sound/weapons/lasercannonfire.ogg'
|
||||
maxHealth = 50
|
||||
health = 50
|
||||
|
||||
/mob/living/simple_animal/hostile/shark/kawaii
|
||||
name = "Kawaii Shark"
|
||||
desc = "Senpai~ Notice me.."
|
||||
icon_state = "kawaiishark"
|
||||
icon_living = "kawaiishark"
|
||||
icon_dead = "kawaiishark_dead"
|
||||
speak = list("Oh Senpai","Notice me senpai!","Oh my...","Kawaii~")
|
||||
speak_emote = list("lovingly says","says")
|
||||
speak_chance = 2
|
||||
turns_per_move = 3
|
||||
butcher_results = list(/mob/living/simple_animal/butterfly = 3)
|
||||
maxHealth = 50
|
||||
health = 50
|
||||
maxbodytemp = INFINITY
|
||||
|
||||
harm_intent_damage = 0
|
||||
melee_damage_lower = 0
|
||||
melee_damage_upper = 0
|
||||
attacktext = "violently hugs"
|
||||
vision_range = 0
|
||||
|
||||
/mob/living/simple_animal/hostile/shark/kawaii/death()
|
||||
say("Senpai, you noticed~!")
|
||||
LoseAggro()
|
||||
..()
|
||||
walk(src, 0)
|
||||
@@ -0,0 +1,422 @@
|
||||
#define POOL_NO_OVERDOSE_MEDICINE_MAX 5 //max units of no-overdose medicine to allow mobs to have through duplication
|
||||
|
||||
//Originally stolen from paradise. Credits to tigercat2000.
|
||||
//Modified a lot by Kokojo and Tortellini Tony for hippiestation.
|
||||
//Heavily refactored by tgstation
|
||||
/obj/machinery/pool
|
||||
icon = 'icons/obj/machines/pool.dmi'
|
||||
anchored = TRUE
|
||||
resistance_flags = INDESTRUCTIBLE
|
||||
|
||||
/obj/machinery/pool/controller
|
||||
name = "\improper Pool Controller"
|
||||
desc = "An advanced substance generation and fluid tank management system that can refill the contents of a pool to a completely different substance in minutes."
|
||||
icon_state = "poolc_3"
|
||||
density = TRUE
|
||||
use_power = TRUE
|
||||
idle_power_usage = 75
|
||||
/// How far it scans for pool objects
|
||||
var/scan_range = 6
|
||||
/// Is pool mist currently on?
|
||||
var/mist_state = FALSE
|
||||
/// Linked mist effects
|
||||
var/list/obj/effect/mist/linked_mist = list()
|
||||
/// Pool turfs
|
||||
var/list/turf/open/pool/linked_turfs = list()
|
||||
/// All mobs in pool
|
||||
var/list/mob/living/mobs_in_pool = list()
|
||||
/// Is the pool bloody?
|
||||
var/bloody = 0
|
||||
/// Last time we process_reagents()'d
|
||||
var/last_reagent_process = 0
|
||||
/// Maximum amount we will take from a beaker
|
||||
var/max_beaker_transfer = 100
|
||||
/// Minimum amount of a reagent for it to work on us
|
||||
var/min_reagent_amount = 10
|
||||
/// ADMINBUS ONLY - WHETHER OR NOT WE HAVE NOREACT ;)
|
||||
var/noreact_reagents = FALSE
|
||||
/// how fast in deciseconds between reagent processes
|
||||
var/reagent_tick_interval = 5
|
||||
/// Can we use unsafe temperatures
|
||||
var/temperature_unlocked = FALSE
|
||||
/// See __DEFINES/pool.dm, temperature defines
|
||||
var/temperature = POOL_NORMAL
|
||||
/// Whether or not the pool can be drained
|
||||
var/drainable = FALSE
|
||||
// Whether or not the pool is drained
|
||||
var/drained = FALSE
|
||||
/// Pool drain
|
||||
var/obj/machinery/pool/drain/linked_drain
|
||||
/// Pool filter
|
||||
var/obj/machinery/pool/filter/linked_filter
|
||||
/// Next world.time you can interact with settings
|
||||
var/interact_delay = 0
|
||||
/// Airlock style shocks
|
||||
var/shocked = FALSE
|
||||
/// Old reagent color, used to determine if update_color needs to reset colors.
|
||||
var/old_rcolor
|
||||
/// Just to prevent spam
|
||||
var/draining = FALSE
|
||||
/// Reagent blacklisting
|
||||
var/respect_reagent_blacklist = TRUE
|
||||
|
||||
/obj/machinery/pool/controller/examine(mob/user)
|
||||
. = ..()
|
||||
. += "<span class='boldnotice'>Alt click to drain reagents.</span>"
|
||||
|
||||
/obj/machinery/pool/controller/Initialize()
|
||||
. = ..()
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
create_reagents(1000)
|
||||
if(noreact_reagents)
|
||||
reagents.reagents_holder_flags |= NO_REACT
|
||||
wires = new /datum/wires/poolcontroller(src)
|
||||
scan_things()
|
||||
|
||||
/obj/machinery/pool/controller/Destroy()
|
||||
STOP_PROCESSING(SSprocessing, src)
|
||||
linked_drain = null
|
||||
linked_filter = null
|
||||
linked_turfs.Cut()
|
||||
mobs_in_pool.Cut()
|
||||
return ..()
|
||||
|
||||
/obj/machinery/pool/controller/proc/scan_things()
|
||||
var/list/cached = range(scan_range, src)
|
||||
for(var/turf/open/pool/W in cached)
|
||||
linked_turfs += W
|
||||
W.controller = src
|
||||
for(var/obj/machinery/pool/drain/pooldrain in cached)
|
||||
linked_drain = pooldrain
|
||||
linked_drain.controller = src
|
||||
break
|
||||
for(var/obj/machinery/pool/filter/F in cached)
|
||||
linked_filter = F
|
||||
linked_filter.controller = src
|
||||
break
|
||||
|
||||
/obj/machinery/pool/controller/emag_act(mob/user)
|
||||
. = ..()
|
||||
if(!(obj_flags & EMAGGED)) //If it is not already emagged, emag it.
|
||||
to_chat(user, "<span class='warning'>You disable the [src]'s safety features.</span>")
|
||||
do_sparks(5, TRUE, src)
|
||||
obj_flags |= EMAGGED
|
||||
temperature_unlocked = TRUE
|
||||
drainable = TRUE
|
||||
log_game("[key_name(user)] emagged [src]")
|
||||
message_admins("[key_name_admin(user)] emagged [src]")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>The interface on [src] is already too damaged to short it again.</span>")
|
||||
return
|
||||
|
||||
/obj/machinery/pool/controller/AltClick(mob/user)
|
||||
. = ..()
|
||||
if(!isliving(user) || !user.Adjacent(src) || !user.CanReach(src) || user.IsStun() || user.IsKnockdown() || user.incapacitated())
|
||||
return FALSE
|
||||
visible_message("<span class='boldwarning'>[user] starts to drain [src]!</span>")
|
||||
draining = TRUE
|
||||
if(!do_after(user, 50, target = src))
|
||||
draining = FALSE
|
||||
return TRUE
|
||||
reagents.remove_all(INFINITY)
|
||||
visible_message("<span class='boldnotice'>[user] drains [src].</span>")
|
||||
say("Reagents cleared.")
|
||||
update_color()
|
||||
draining = FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/pool/controller/attackby(obj/item/W, mob/user)
|
||||
if(shocked && !(stat & NOPOWER))
|
||||
shock(user,50)
|
||||
if(stat & (BROKEN))
|
||||
return
|
||||
if(istype(W,/obj/item/reagent_containers))
|
||||
if(W.reagents.total_volume) //check if there's reagent
|
||||
user.visible_message("<span class='boldwarning'>[user] is feeding [src] some chemicals from [W].</span>")
|
||||
if(do_after(user, 50, target = src))
|
||||
for(var/datum/reagent/R in W.reagents.reagent_list)
|
||||
if(R.type in GLOB.blacklisted_pool_reagents)
|
||||
to_chat(user, "[src] cannot accept [R.name].")
|
||||
return
|
||||
if(R.reagent_state == SOLID)
|
||||
to_chat(user, "The pool cannot accept reagents in solid form!.")
|
||||
return
|
||||
reagents.clear_reagents()
|
||||
// This also reacts them. No nitroglycerin deathpools, sorry gamers :(
|
||||
W.reagents.trans_to(reagents, max_beaker_transfer)
|
||||
user.visible_message("<span class='notice'>[src] makes a slurping noise.</span>", "<span class='notice'>All of the contents of [W] are quickly suctioned out by the machine!</span")
|
||||
updateUsrDialog()
|
||||
var/list/reagent_names = list()
|
||||
var/list/rejected = list()
|
||||
for(var/datum/reagent/R in reagents.reagent_list)
|
||||
if((R.volume >= min_reagent_amount) && (!respect_reagent_blacklist || R.can_synth))
|
||||
reagent_names += R.name
|
||||
else
|
||||
reagents.remove_reagent(R.type, INFINITY)
|
||||
rejected += R.name
|
||||
if(length(reagent_names))
|
||||
reagent_names = english_list(reagent_names)
|
||||
var/msg = "POOL: [key_name(user)] has changed [src]'s chems to [reagent_names]"
|
||||
log_game(msg)
|
||||
message_admins(msg)
|
||||
if(length(rejected))
|
||||
rejected = english_list(rejected)
|
||||
to_chat(user, "<span class='warning'>[src] rejects the following chemicals as they do not have at least [min_reagent_amount] units of volume: [rejected]</span>")
|
||||
update_color()
|
||||
else
|
||||
to_chat(user, "<span class='notice'>[src] beeps unpleasantly as it rejects the beaker. Why are you trying to feed it an empty beaker?</span>")
|
||||
return
|
||||
else if(panel_open && is_wire_tool(W))
|
||||
wires.interact(user)
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/machinery/pool/controller/screwdriver_act(mob/living/user, obj/item/W)
|
||||
. = ..()
|
||||
if(.)
|
||||
return TRUE
|
||||
cut_overlays()
|
||||
panel_open = !panel_open
|
||||
to_chat(user, "You [panel_open ? "open" : "close"] the maintenance panel.")
|
||||
W.play_tool_sound(src)
|
||||
if(panel_open)
|
||||
add_overlay("wires")
|
||||
return TRUE
|
||||
|
||||
//procs
|
||||
/obj/machinery/pool/controller/proc/shock(mob/user, prb)
|
||||
if(stat & (BROKEN|NOPOWER)) // unpowered, no shock
|
||||
return FALSE
|
||||
if(!prob(prb))
|
||||
return FALSE
|
||||
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
|
||||
s.set_up(5, 1, src)
|
||||
s.start()
|
||||
if(electrocute_mob(user, get_area(src), src, 0.7))
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/obj/machinery/pool/controller/proc/process_reagents()
|
||||
if(last_reagent_process > world.time + reagent_tick_interval)
|
||||
return
|
||||
if(!length(reagents.reagent_list))
|
||||
return
|
||||
for(var/turf/open/pool/W in linked_turfs)
|
||||
for(var/mob/living/carbon/human/swimee in W)
|
||||
for(var/datum/reagent/R in reagents.reagent_list)
|
||||
if(R.reagent_state == SOLID)
|
||||
R.reagent_state = LIQUID
|
||||
if(!swimee.reagents.has_reagent(POOL_NO_OVERDOSE_MEDICINE_MAX))
|
||||
swimee.reagents.add_reagent(R.type, 0.5) //osmosis
|
||||
reagents.reaction(swimee, VAPOR, 0.03) //3 percent. Need to find a way to prevent this from stacking chems at some point like the above.
|
||||
for(var/obj/objects in W)
|
||||
if(W.reagents)
|
||||
W.reagents.reaction(objects, VAPOR, 1)
|
||||
last_reagent_process = world.time
|
||||
|
||||
/obj/machinery/pool/controller/process()
|
||||
updateUsrDialog()
|
||||
if(stat & (NOPOWER|BROKEN))
|
||||
return
|
||||
if(drained)
|
||||
return
|
||||
process_pool()
|
||||
process_reagents()
|
||||
|
||||
/obj/machinery/pool/controller/proc/process_pool()
|
||||
if(drained)
|
||||
return
|
||||
for(var/mob/living/M in mobs_in_pool)
|
||||
switch(temperature) //Apply different effects based on what the temperature is set to.
|
||||
if(POOL_SCALDING) //Scalding
|
||||
M.adjust_bodytemperature(50,0,500)
|
||||
if(POOL_WARM) //Warm
|
||||
M.adjust_bodytemperature(20,0,360) //Heats up mobs till the termometer shows up
|
||||
//Normal temp does nothing, because it's just room temperature water.
|
||||
if(POOL_COOL)
|
||||
M.adjust_bodytemperature(-20,250) //Cools mobs till the termometer shows up
|
||||
if(POOL_FRIGID) //Freezing
|
||||
M.adjust_bodytemperature(-60) //cool mob at -35k per cycle, less would not affect the mob enough.
|
||||
if(M.bodytemperature <= 50 && !M.stat)
|
||||
M.apply_status_effect(/datum/status_effect/freon)
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/drownee = M
|
||||
if(!drownee || drownee.stat == DEAD)
|
||||
return
|
||||
if(drownee.resting && !drownee.internal)
|
||||
if(drownee.stat != CONSCIOUS)
|
||||
drownee.adjustOxyLoss(9)
|
||||
else
|
||||
drownee.adjustOxyLoss(4)
|
||||
if(prob(35))
|
||||
to_chat(drownee, "<span class='danger'>You're drowning!</span>")
|
||||
|
||||
/obj/machinery/pool/controller/proc/set_bloody(state)
|
||||
if(bloody == state)
|
||||
return
|
||||
bloody = state
|
||||
update_color()
|
||||
|
||||
/obj/machinery/pool/controller/proc/update_color()
|
||||
if(drained)
|
||||
return
|
||||
var/rcolor
|
||||
if(reagents.reagent_list.len)
|
||||
rcolor = mix_color_from_reagents(reagents.reagent_list)
|
||||
if(rcolor == old_rcolor)
|
||||
return // small performance upgrade hopefully?
|
||||
old_rcolor = rcolor
|
||||
for(var/X in linked_turfs)
|
||||
var/turf/open/pool/color1 = X
|
||||
if(bloody)
|
||||
if(rcolor)
|
||||
var/thecolor = BlendRGB(rgb(150, 20, 20), rcolor, 0.5)
|
||||
color1.watereffect.add_atom_colour(thecolor, FIXED_COLOUR_PRIORITY)
|
||||
color1.watertop.add_atom_colour(thecolor, FIXED_COLOUR_PRIORITY)
|
||||
else
|
||||
var/thecolor = rgb(150, 20, 20)
|
||||
color1.watereffect.add_atom_colour(thecolor, FIXED_COLOUR_PRIORITY)
|
||||
color1.watertop.add_atom_colour(thecolor, FIXED_COLOUR_PRIORITY)
|
||||
else if(!bloody && rcolor)
|
||||
color1.watereffect.add_atom_colour(rcolor, FIXED_COLOUR_PRIORITY)
|
||||
color1.watertop.add_atom_colour(rcolor, FIXED_COLOUR_PRIORITY)
|
||||
else
|
||||
color1.watereffect.remove_atom_colour(FIXED_COLOUR_PRIORITY)
|
||||
color1.watertop.remove_atom_colour(FIXED_COLOUR_PRIORITY)
|
||||
|
||||
/obj/machinery/pool/controller/proc/update_temp()
|
||||
if(mist_state)
|
||||
if(temperature < POOL_SCALDING)
|
||||
mist_off()
|
||||
else
|
||||
if(temperature == POOL_SCALDING)
|
||||
mist_on()
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/pool/controller/update_icon()
|
||||
. = ..()
|
||||
icon_state = "poolc_[temperature]"
|
||||
|
||||
/obj/machinery/pool/controller/proc/CanUpTemp(mob/user)
|
||||
if(temperature == POOL_WARM && (temperature_unlocked || issilicon(user) || IsAdminGhost(user)) || temperature < POOL_WARM)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/machinery/pool/controller/proc/CanDownTemp(mob/user)
|
||||
if(temperature == POOL_COOL && (temperature_unlocked || issilicon(user) || IsAdminGhost(user)) || temperature > POOL_COOL)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/machinery/pool/controller/Topic(href, href_list)
|
||||
if(..())
|
||||
return
|
||||
if(interact_delay > world.time)
|
||||
return
|
||||
if(href_list["IncreaseTemp"])
|
||||
if(CanUpTemp(usr))
|
||||
visible_message("<span class='warning'>[usr] presses a button on [src].</span>")
|
||||
temperature++
|
||||
update_temp()
|
||||
var/msg = "POOL: [key_name(usr)] increased [src]'s pool temperature at [COORD(src)] to [temperature]"
|
||||
log_game(msg)
|
||||
message_admins(msg)
|
||||
interact_delay = world.time + 15
|
||||
if(href_list["DecreaseTemp"])
|
||||
if(CanDownTemp(usr))
|
||||
visible_message("<span class='warning'>[usr] presses a button on [src].</span>")
|
||||
temperature--
|
||||
update_temp()
|
||||
var/msg = "POOL: [key_name(usr)] decreased [src]'s pool temperature at [COORD(src)] to [temperature]"
|
||||
log_game(msg)
|
||||
message_admins(msg)
|
||||
interact_delay = world.time + 15
|
||||
if(href_list["Activate Drain"])
|
||||
if((drainable || issilicon(usr) || IsAdminGhost(usr)) && !linked_drain.active)
|
||||
var/msg = "POOL: [key_name(usr)] activated [src]'s pool drain in [linked_drain.filling? "FILLING" : "DRAINING"] mode at [COORD(src)]"
|
||||
log_game(msg)
|
||||
message_admins(msg)
|
||||
visible_message("<span class='warning'>[usr] presses a button on [src].</span>")
|
||||
mist_off()
|
||||
interact_delay = world.time + 60
|
||||
linked_drain.active = TRUE
|
||||
linked_drain.cycles_left = 75
|
||||
if(!linked_drain.filling)
|
||||
new /obj/effect/whirlpool(linked_drain.loc)
|
||||
temperature = POOL_NORMAL
|
||||
else
|
||||
new /obj/effect/waterspout(linked_drain.loc)
|
||||
temperature = POOL_NORMAL
|
||||
update_temp()
|
||||
bloody = FALSE
|
||||
updateUsrDialog()
|
||||
|
||||
/obj/machinery/pool/controller/proc/temp2text()
|
||||
switch(temperature)
|
||||
if(POOL_FRIGID)
|
||||
return "<span class='boldwarning'>Frigid</span>"
|
||||
if(POOL_COOL)
|
||||
return "<span class='boldnotice'>Cool</span>"
|
||||
if(POOL_NORMAL)
|
||||
return "<span class='notice'>Normal</span>"
|
||||
if(POOL_WARM)
|
||||
return "<span class='boldnotice'>Warm</span>"
|
||||
if(POOL_SCALDING)
|
||||
return "<span class='boldwarning'>Scalding</span>"
|
||||
else
|
||||
return "Outside of possible range."
|
||||
|
||||
/obj/machinery/pool/controller/ui_interact(mob/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if(shocked && !(stat & NOPOWER))
|
||||
shock(user,50)
|
||||
if(panel_open && !isAI(user))
|
||||
return wires.interact(user)
|
||||
if(stat & (NOPOWER|BROKEN))
|
||||
return
|
||||
var/datum/browser/popup = new(user, "Pool Controller", name, 300, 450)
|
||||
var/dat = ""
|
||||
if(interact_delay > world.time)
|
||||
dat += "<span class='notice'>[round((interact_delay - world.time)/10, 0.1)] seconds left until [src] can operate again.</span><BR>"
|
||||
dat += text({"
|
||||
<h3>Temperature</h3>
|
||||
<div class='statusDisplay'>
|
||||
<B>Current temperature:</B> [temp2text()]<BR>
|
||||
[CanUpTemp(user) ? "<a href='?src=\ref[src];IncreaseTemp=1'>Increase Temperature</a><br>" : "<span class='linkOff'>Increase Temperature</span><br>"]
|
||||
[CanDownTemp(user) ? "<a href='?src=\ref[src];DecreaseTemp=1'>Decrease Temperature</a><br>" : "<span class='linkOff'>Decrease Temperature</span><br>"]
|
||||
</div>
|
||||
<h3>Drain</h3>
|
||||
<div class='statusDisplay'>
|
||||
<B>Drain status:</B> [(issilicon(user) || IsAdminGhost(user) || drainable) ? "<span class='bad'>Enabled</span>" : "<span class='good'>Disabled</span>"]
|
||||
<br><b>Pool status:</b> "})
|
||||
if(!drained)
|
||||
dat += "<span class='good'>Full</span><BR>"
|
||||
else
|
||||
dat += "<span class='bad'>Drained</span><BR>"
|
||||
if((issilicon(user) || IsAdminGhost(user) || drainable) && !linked_drain.active)
|
||||
dat += "<a href='?src=\ref[src];Activate Drain=1'>[drained ? "Fill" : "Drain"] Pool</a><br>"
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/pool/controller/proc/reset(wire)
|
||||
switch(wire)
|
||||
if(WIRE_SHOCK)
|
||||
if(!wires.is_cut(wire))
|
||||
shocked = FALSE
|
||||
|
||||
/obj/machinery/pool/controller/proc/mist_on() //Spawn /obj/effect/mist (from the shower) on all linked pool tiles
|
||||
if(mist_state)
|
||||
return
|
||||
mist_state = TRUE
|
||||
for(var/X in linked_turfs)
|
||||
var/turf/open/pool/W = X
|
||||
if(W.filled)
|
||||
var/M = new /obj/effect/mist(W)
|
||||
linked_mist += M
|
||||
|
||||
/obj/machinery/pool/controller/proc/mist_off() //Delete all /obj/effect/mist from all linked pool tiles.
|
||||
for(var/M in linked_mist)
|
||||
qdel(M)
|
||||
mist_state = FALSE
|
||||
@@ -0,0 +1,160 @@
|
||||
/obj/machinery/pool/drain
|
||||
name = "drain"
|
||||
icon_state = "drain"
|
||||
desc = "A suction system to remove the contents of the pool, and sometimes small objects. Do not insert fingers."
|
||||
anchored = TRUE
|
||||
/// Active/on?
|
||||
var/active = FALSE
|
||||
/// Filling or draining
|
||||
var/filling = FALSE
|
||||
/// Drain item suction range
|
||||
var/item_suction_range = 2
|
||||
/// Fill mode knock away range
|
||||
var/fill_push_range = 6
|
||||
/// Drain mode suction range
|
||||
var/drain_suck_range = 6
|
||||
/// Parent controller
|
||||
var/obj/machinery/pool/controller/controller
|
||||
/// Cycles left for fill/drain while active
|
||||
var/cycles_left = 0
|
||||
/// Mobs we are swirling around
|
||||
var/list/whirling_mobs
|
||||
/// Suck in once per x ticks
|
||||
var/suck_in_once_per = 3
|
||||
|
||||
var/cooldown
|
||||
|
||||
/obj/machinery/pool/drain/Initialize()
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
whirling_mobs = list()
|
||||
return ..()
|
||||
|
||||
/obj/machinery/pool/drain/Destroy()
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
controller.linked_drain = null
|
||||
controller = null
|
||||
whirling_mobs = null
|
||||
return ..()
|
||||
|
||||
/obj/machinery/pool/drain/proc/is_in_our_pool(atom/A)
|
||||
. = FALSE
|
||||
if(istype(A.loc, /turf/open/pool))
|
||||
var/turf/open/pool/P = A.loc
|
||||
if(P.controller == controller)
|
||||
. = TRUE
|
||||
|
||||
// This should probably start using move force sometime in the future but I'm lazy.
|
||||
/obj/machinery/pool/drain/process()
|
||||
if(!filling)
|
||||
for(var/obj/item/I in range(min(item_suction_range, 10), src))
|
||||
if(!I.anchored && (I.w_class <= WEIGHT_CLASS_SMALL))
|
||||
step_towards(I, src)
|
||||
if((I.w_class <= WEIGHT_CLASS_TINY) && (get_dist(I, src) == 0))
|
||||
I.forceMove(controller.linked_filter)
|
||||
if(active)
|
||||
if(filling)
|
||||
if(cycles_left-- > 0)
|
||||
playsound(src, 'sound/effects/fillingwatter.ogg', 100, TRUE)
|
||||
for(var/obj/O in orange(min(fill_push_range, 10), src))
|
||||
if(!O.anchored && is_in_our_pool(O))
|
||||
step_away(O, src)
|
||||
for(var/mob/M in orange(min(fill_push_range, 10), src)) //compiler fastpath apparently?
|
||||
if(!M.anchored && isliving(M) && is_in_our_pool(M))
|
||||
step_away(M, src)
|
||||
else
|
||||
for(var/turf/open/pool/P in controller.linked_turfs)
|
||||
P.filled = TRUE
|
||||
P.update_icon_state()
|
||||
for(var/obj/effect/waterspout/S in range(1, src))
|
||||
qdel(S)
|
||||
controller.drained = FALSE
|
||||
if(controller.bloody < 1000)
|
||||
controller.bloody /= 2
|
||||
else
|
||||
controller.bloody /= 4
|
||||
controller.update_color()
|
||||
filling = FALSE
|
||||
active = FALSE
|
||||
else
|
||||
if(cycles_left-- > 0)
|
||||
playsound(src, 'sound/effects/pooldrain.ogg', 100, TRUE)
|
||||
playsound(src, "water_wade", 60, TRUE)
|
||||
for(var/obj/O in orange(min(drain_suck_range, 10), src))
|
||||
if(!O.anchored && is_in_our_pool(O))
|
||||
step_towards(O, src)
|
||||
for(var/mob/M in orange(min(drain_suck_range, 10), src))
|
||||
if(isliving(M) && !M.anchored && is_in_our_pool(M))
|
||||
if(!(cycles_left % suck_in_once_per))
|
||||
step_towards(M, src)
|
||||
whirl_mob(M)
|
||||
if(ishuman(M) && (get_dist(M, src) <= 1))
|
||||
var/mob/living/carbon/human/H = M
|
||||
playsound(src, pick('sound/misc/crack.ogg','sound/misc/crunch.ogg'), 50, TRUE)
|
||||
if(H.lying) //down for any reason
|
||||
H.adjustBruteLoss(2)
|
||||
to_chat(H, "<span class='danger'>You're caught in the drain!</span>")
|
||||
else
|
||||
H.apply_damage(2.5, BRUTE, pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) //drain should only target the legs
|
||||
to_chat(H, "<span class='danger'>Your legs are caught in the drain!</span>")
|
||||
else
|
||||
for(var/turf/open/pool/P in controller.linked_turfs)
|
||||
P.filled = FALSE
|
||||
P.update_icon_state()
|
||||
for(var/obj/effect/whirlpool/W in range(1, src))
|
||||
qdel(W)
|
||||
controller.drained = TRUE
|
||||
controller.mist_off()
|
||||
active = FALSE
|
||||
filling = TRUE
|
||||
|
||||
/// dangerous proc don't fuck with, admins
|
||||
/obj/machinery/pool/drain/proc/whirl_mob(mob/living/L, duration = 8, delay = 1)
|
||||
set waitfor = FALSE
|
||||
if(whirling_mobs[L])
|
||||
return
|
||||
whirling_mobs[L] = TRUE
|
||||
for(var/i in 1 to min(duration, 100))
|
||||
L.setDir(turn(L.dir, 90))
|
||||
sleep(delay)
|
||||
if(QDELETED(L))
|
||||
break
|
||||
if(QDELETED(src))
|
||||
return
|
||||
whirling_mobs -= L
|
||||
|
||||
/obj/machinery/pool/filter
|
||||
name = "Filter"
|
||||
icon_state = "filter"
|
||||
desc = "The part of the pool where all the IDs, ATV keys, and pens, and other dangerous things get trapped."
|
||||
var/obj/machinery/pool/controller/controller
|
||||
|
||||
/obj/machinery/pool/filter/Destroy()
|
||||
controller.linked_filter = null
|
||||
controller = null
|
||||
return ..()
|
||||
|
||||
/obj/machinery/pool/filter/emag_act(mob/living/user)
|
||||
. = ..()
|
||||
if(!(obj_flags & EMAGGED))
|
||||
to_chat(user, "<span class='warning'>You disable the [src]'s shark filter! Run!</span>")
|
||||
obj_flags |= EMAGGED
|
||||
do_sparks(5, TRUE, src)
|
||||
icon_state = "filter_b"
|
||||
addtimer(CALLBACK(src, /obj/machinery/pool/filter/proc/spawn_shark), 50)
|
||||
var/msg = "[key_name(user)] emagged the pool filter and spawned a shark"
|
||||
log_game(msg)
|
||||
message_admins(msg)
|
||||
|
||||
/obj/machinery/pool/filter/proc/spawn_shark()
|
||||
if(prob(50))
|
||||
new /mob/living/simple_animal/hostile/shark(loc)
|
||||
else
|
||||
if(prob(50))
|
||||
new /mob/living/simple_animal/hostile/shark/kawaii(loc)
|
||||
else
|
||||
new /mob/living/simple_animal/hostile/shark/laser(loc)
|
||||
|
||||
/obj/machinery/pool/filter/attack_hand(mob/user)
|
||||
to_chat(user, "You search the filter.")
|
||||
for(var/obj/O in contents)
|
||||
O.forceMove(loc)
|
||||
@@ -0,0 +1,29 @@
|
||||
/obj/effect/splash
|
||||
name = "splash"
|
||||
desc = "Wataaa!."
|
||||
icon = 'icons/turf/pool.dmi'
|
||||
icon_state = "splash"
|
||||
layer = ABOVE_ALL_MOB_LAYER
|
||||
|
||||
/obj/effect/whirlpool
|
||||
name = "Whirlpool"
|
||||
icon = 'icons/effects/96x96.dmi'
|
||||
icon_state = "whirlpool"
|
||||
layer = 5
|
||||
anchored = TRUE
|
||||
mouse_opacity = 0
|
||||
pixel_x = -32
|
||||
pixel_y = -32
|
||||
alpha = 90
|
||||
|
||||
/obj/effect/waterspout
|
||||
name = "Waterspout"
|
||||
icon = 'icons/effects/96x96.dmi'
|
||||
icon_state = "waterspout"
|
||||
color = "#3399AA"
|
||||
layer = 5
|
||||
anchored = TRUE
|
||||
mouse_opacity = 0
|
||||
pixel_x = -32
|
||||
pixel_y = -32
|
||||
alpha = 120
|
||||
@@ -0,0 +1,191 @@
|
||||
/turf/open/pool
|
||||
icon = 'icons/turf/pool.dmi'
|
||||
name = "poolwater"
|
||||
desc = "You're safer here than in the deep."
|
||||
icon_state = "pool_tile"
|
||||
heat_capacity = INFINITY
|
||||
var/filled = TRUE
|
||||
var/next_splash = 0
|
||||
var/obj/machinery/pool/controller/controller
|
||||
var/obj/effect/overlay/water/watereffect
|
||||
var/obj/effect/overlay/water/top/watertop
|
||||
|
||||
/turf/open/pool/Initialize(mapload)
|
||||
. = ..()
|
||||
update_icon_state()
|
||||
|
||||
/turf/open/pool/Destroy()
|
||||
if(controller)
|
||||
controller.linked_turfs -= src
|
||||
controller = null
|
||||
QDEL_NULL(watereffect)
|
||||
QDEL_NULL(watertop)
|
||||
return ..()
|
||||
|
||||
/turf/open/pool/update_icon_state()
|
||||
. = ..()
|
||||
if(!filled)
|
||||
name = "drained pool"
|
||||
desc = "No diving!"
|
||||
QDEL_NULL(watereffect)
|
||||
QDEL_NULL(watertop)
|
||||
else
|
||||
name = "poolwater"
|
||||
desc = "You're safer here than in the deep."
|
||||
watereffect = new /obj/effect/overlay/water(src)
|
||||
watertop = new /obj/effect/overlay/water/top(src)
|
||||
|
||||
/obj/effect/overlay/water
|
||||
name = "water"
|
||||
icon = 'icons/turf/pool.dmi'
|
||||
icon_state = "bottom"
|
||||
density = FALSE
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
layer = ABOVE_MOB_LAYER
|
||||
anchored = TRUE
|
||||
resistance_flags = INDESTRUCTIBLE
|
||||
|
||||
/obj/effect/overlay/water/top
|
||||
icon_state = "top"
|
||||
layer = BELOW_MOB_LAYER
|
||||
|
||||
// Mousedrop hook to normal turfs to get out of pools.
|
||||
/turf/open/MouseDrop_T(atom/from, mob/user)
|
||||
// I could make this /open/floor and not have the !istype but ehh - kev
|
||||
if(isliving(from) && HAS_TRAIT(from, TRAIT_SWIMMING) && isliving(user) && ((user == from) || user.CanReach(from)) && !user.IsStun() && !user.IsKnockdown() && !user.incapacitated() && !istype(src, /turf/open/pool))
|
||||
var/mob/living/L = from
|
||||
//The element only exists if you're on water and a living mob, so let's skip those checks.
|
||||
var/pre_msg
|
||||
var/post_msg
|
||||
if(user == from)
|
||||
pre_msg = "<span class='notice'>[L] is getting out of the pool.</span>"
|
||||
post_msg = "<span class='notice'>[L] gets out of the pool.</span>"
|
||||
else
|
||||
pre_msg = "<span class='notice'>[L] is being pulled out of the pool by [user].</span>"
|
||||
post_msg = "<span class='notice'>[user] pulls [L] out of the pool.</span>"
|
||||
L.visible_message(pre_msg)
|
||||
if(do_mob(user, L, 20))
|
||||
L.visible_message(post_msg)
|
||||
L.forceMove(src)
|
||||
else
|
||||
return ..()
|
||||
|
||||
// Exit check
|
||||
/turf/open/pool/Exit(atom/movable/AM, atom/newloc)
|
||||
if(!AM.has_gravity(src))
|
||||
return ..()
|
||||
if(isliving(AM) || isstructure(AM))
|
||||
if(AM.throwing)
|
||||
return ..() //WHEEEEEEEEEEE
|
||||
if(istype(AM, /obj/structure) && isliving(AM.pulledby))
|
||||
return ..() //people pulling stuff out of pool
|
||||
if(!ishuman(AM))
|
||||
return ..() //human weak, monkey (and anyone else) ook ook eek eek strong
|
||||
if(isliving(AM) && (locate(/obj/structure/pool/ladder) in src))
|
||||
return ..() //climbing out
|
||||
return istype(newloc, /turf/open/pool)
|
||||
return ..()
|
||||
|
||||
// Exited logic
|
||||
/turf/open/pool/Exited(atom/A, atom/newLoc)
|
||||
. = ..()
|
||||
if(isliving(A))
|
||||
var/turf/open/pool/P = newLoc
|
||||
if(!istype(P) || (P.controller != controller))
|
||||
controller?.mobs_in_pool -= A
|
||||
|
||||
// Entered logic
|
||||
/turf/open/pool/Entered(atom/movable/AM, atom/oldloc)
|
||||
if(istype(AM, /obj/effect/decal/cleanable))
|
||||
var/obj/effect/decal/cleanable/C = AM
|
||||
if(prob(C.bloodiness))
|
||||
controller.set_bloody(TRUE)
|
||||
QDEL_IN(AM, 25)
|
||||
animate(AM, alpha = 10, time = 20)
|
||||
return ..()
|
||||
if(!AM.has_gravity(src))
|
||||
return ..()
|
||||
if(isliving(AM))
|
||||
var/mob/living/victim = AM
|
||||
if(!HAS_TRAIT(victim, TRAIT_SWIMMING)) //poor guy not swimming time to dunk them!
|
||||
victim.AddElement(/datum/element/swimming)
|
||||
controller.mobs_in_pool += victim
|
||||
if(locate(/obj/structure/pool/ladder) in src) //safe climbing
|
||||
return
|
||||
if(iscarbon(AM)) //FUN TIME!
|
||||
var/mob/living/carbon/H = victim
|
||||
if(filled)
|
||||
if (H.wear_mask && H.wear_mask.flags_cover & MASKCOVERSMOUTH)
|
||||
H.visible_message("<span class='danger'>[H] falls in the water!</span>",
|
||||
"<span class='userdanger'>You fall in the water!</span>")
|
||||
playsound(src, 'sound/effects/splash.ogg', 60, TRUE, 1)
|
||||
H.Knockdown(20)
|
||||
return
|
||||
else
|
||||
H.Knockdown(60)
|
||||
H.adjustOxyLoss(5)
|
||||
H.emote("cough")
|
||||
H.visible_message("<span class='danger'>[H] falls in and takes a drink!</span>",
|
||||
"<span class='userdanger'>You fall in and swallow some water!</span>")
|
||||
playsound(src, 'sound/effects/splash.ogg', 60, TRUE, 1)
|
||||
else if(!H.head || !(H.head.armor.getRating("melee") > 20))
|
||||
if(prob(75))
|
||||
H.visible_message("<span class='danger'>[H] falls in the drained pool!</span>",
|
||||
"<span class='userdanger'>You fall in the drained pool!</span>")
|
||||
H.adjustBruteLoss(7)
|
||||
H.Knockdown(80)
|
||||
playsound(src, 'sound/effects/woodhit.ogg', 60, TRUE, 1)
|
||||
else
|
||||
H.visible_message("<span class='danger'>[H] falls in the drained pool, and cracks his skull!</span>",
|
||||
"<span class='userdanger'>You fall in the drained pool, and crack your skull!</span>")
|
||||
H.apply_damage(15, BRUTE, "head")
|
||||
H.Knockdown(200) // This should hurt. And it does.
|
||||
playsound(src, 'sound/effects/woodhit.ogg', 60, TRUE, 1)
|
||||
playsound(src, 'sound/misc/crack.ogg', 100, TRUE)
|
||||
else
|
||||
H.visible_message("<span class='danger'>[H] falls in the drained pool, but had an helmet!</span>",
|
||||
"<span class='userdanger'>You fall in the drained pool, but you had an helmet!</span>")
|
||||
H.Knockdown(40)
|
||||
playsound(src, 'sound/effects/woodhit.ogg', 60, TRUE, 1)
|
||||
else if(filled)
|
||||
victim.adjustStaminaLoss(1)
|
||||
playsound(src, "water_wade", 20, TRUE)
|
||||
return ..()
|
||||
|
||||
/turf/open/pool/MouseDrop_T(atom/from, mob/user)
|
||||
. = ..()
|
||||
if(!isliving(from))
|
||||
return
|
||||
var/mob/living/victim = from
|
||||
if(user.stat || user.lying || !Adjacent(user) || !from.Adjacent(user) || !iscarbon(user) || !victim.has_gravity(src) || HAS_TRAIT(victim, TRAIT_SWIMMING))
|
||||
return
|
||||
var/victimname = victim == user? "themselves" : "[victim]"
|
||||
var/starttext = victim == user? "[user] is descending into [src]." : "[user] is lowering [victim] into [src]."
|
||||
user.visible_message("<span class='notice'>[starttext]</span>")
|
||||
if(do_mob(user, victim, 20))
|
||||
user.visible_message("<span class='notice'>[user] lowers [victimname] into [src].</span>")
|
||||
victim.AddElement(/datum/element/swimming) //make sure they have it so they don't fall/whatever
|
||||
victim.forceMove(src)
|
||||
|
||||
/turf/open/pool/attackby(obj/item/W, mob/living/user)
|
||||
if(istype(W, /obj/item/mop) && filled)
|
||||
W.reagents.add_reagent("water", 5)
|
||||
to_chat(user, "<span class='notice'>You wet [W] in [src].</span>")
|
||||
playsound(loc, 'sound/effects/slosh.ogg', 25, TRUE)
|
||||
else
|
||||
return ..()
|
||||
|
||||
/turf/open/pool/attack_hand(mob/living/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if((user.loc != src) && !user.IsStun() && !user.IsKnockdown() && !user.incapacitated() && Adjacent(user) && HAS_TRAIT(user, TRAIT_SWIMMING) && filled && (next_splash < world.time))
|
||||
playsound(src, 'sound/effects/watersplash.ogg', 8, TRUE, 1)
|
||||
next_splash = world.time + 25
|
||||
var/obj/effect/splash/S = new(src)
|
||||
animate(S, alpha = 0, time = 8)
|
||||
QDEL_IN(S, 10)
|
||||
for(var/mob/living/carbon/human/H in src)
|
||||
if(!H.wear_mask && (H.stat == CONSCIOUS))
|
||||
H.emote("cough")
|
||||
H.adjustStaminaLoss(4)
|
||||
@@ -0,0 +1,32 @@
|
||||
//Pool noodles
|
||||
|
||||
/obj/item/toy/poolnoodle
|
||||
icon = 'icons/obj/toy.dmi'
|
||||
icon_state = "noodle"
|
||||
name = "pool noodle"
|
||||
desc = "A strange, bulky, bendable toy that can annoy people."
|
||||
force = 0
|
||||
color = "#000000"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
throwforce = 1
|
||||
throw_speed = 10 //weeee
|
||||
hitsound = 'sound/weapons/tap.ogg'
|
||||
attack_verb = list("flogged", "poked", "jabbed", "slapped", "annoyed")
|
||||
lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/items_righthand.dmi'
|
||||
|
||||
/obj/item/toy/poolnoodle/attack(target as mob, mob/living/user as mob)
|
||||
. = ..()
|
||||
user.spin(prob(20)? 16 : 8, 1)
|
||||
|
||||
/obj/item/toy/poolnoodle/red
|
||||
item_state = "noodlered"
|
||||
color = "#ff4c4c"
|
||||
|
||||
/obj/item/toy/poolnoodle/blue
|
||||
item_state = "noodleblue"
|
||||
color = "#3232ff"
|
||||
|
||||
/obj/item/toy/poolnoodle/yellow
|
||||
item_state = "noodleyellow"
|
||||
color = "#ffff66"
|
||||
@@ -0,0 +1,159 @@
|
||||
/obj/structure/pool
|
||||
name = "pool"
|
||||
icon = 'icons/obj/machines/pool.dmi'
|
||||
anchored = TRUE
|
||||
resistance_flags = UNACIDABLE|INDESTRUCTIBLE
|
||||
|
||||
/obj/structure/pool/ladder
|
||||
name = "Ladder"
|
||||
icon_state = "ladder"
|
||||
desc = "Are you getting in or are you getting out?."
|
||||
layer = ABOVE_MOB_LAYER
|
||||
dir = EAST
|
||||
|
||||
/obj/structure/pool/ladder/attack_hand(mob/living/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if(!HAS_TRAIT(user, TRAIT_SWIMMING))
|
||||
if(user.CanReach(src))
|
||||
user.AddElement(/datum/element/swimming)
|
||||
user.forceMove(get_step(src, dir))
|
||||
else
|
||||
if(user.loc == loc)
|
||||
user.forceMove(get_step(src, turn(dir, 180))) //If this moves them out the element cleans up after itself.
|
||||
|
||||
/obj/structure/pool/Rboard
|
||||
name = "JumpBoard"
|
||||
density = FALSE
|
||||
icon_state = "boardright"
|
||||
desc = "The less-loved portion of the jumping board."
|
||||
dir = EAST
|
||||
|
||||
/obj/structure/pool/Lboard
|
||||
name = "JumpBoard"
|
||||
icon_state = "boardleft"
|
||||
desc = "Get on there to jump!"
|
||||
layer = FLY_LAYER
|
||||
dir = WEST
|
||||
var/jumping = FALSE
|
||||
var/timer
|
||||
|
||||
/obj/structure/pool/Lboard/proc/backswim()
|
||||
if(jumping)
|
||||
for(var/mob/living/jumpee in loc) //hackzors.
|
||||
playsound(jumpee, 'sound/effects/splash.ogg', 60, TRUE, 1)
|
||||
if(!HAS_TRAIT(jumpee, TRAIT_SWIMMING))
|
||||
jumpee.AddElement(/datum/element/swimming)
|
||||
jumpee.Stun(2)
|
||||
|
||||
/obj/structure/pool/Lboard/proc/reset_position(mob/user, initial_layer, initial_px, initial_py)
|
||||
user.layer = initial_layer
|
||||
user.pixel_x = initial_px
|
||||
user.pixel_y = initial_py
|
||||
|
||||
/obj/structure/pool/Lboard/attack_hand(mob/living/user)
|
||||
if(iscarbon(user))
|
||||
var/mob/living/carbon/jumper = user
|
||||
if(jumping)
|
||||
to_chat(user, "<span class='notice'>Someone else is already making a jump!</span>")
|
||||
return
|
||||
var/turf/T = get_turf(src)
|
||||
if(HAS_TRAIT(user, TRAIT_SWIMMING))
|
||||
return
|
||||
else
|
||||
if(Adjacent(jumper))
|
||||
jumper.visible_message("<span class='notice'>[user] climbs up \the [src]!</span>", \
|
||||
"<span class='notice'>You climb up \the [src] and prepares to jump!</span>")
|
||||
jumper.Stun(40)
|
||||
jumping = TRUE
|
||||
var/original_layer = jumper.layer
|
||||
var/original_px = jumper.pixel_x
|
||||
var/original_py = jumper.pixel_y
|
||||
jumper.layer = RIPPLE_LAYER
|
||||
jumper.pixel_x = 3
|
||||
jumper.pixel_y = 7
|
||||
jumper.dir = WEST
|
||||
jumper.AddElement(/datum/element/swimming)
|
||||
sleep(1)
|
||||
jumper.forceMove(T)
|
||||
addtimer(CALLBACK(src, .proc/dive, jumper, original_layer, original_px, original_py), 10)
|
||||
|
||||
/obj/structure/pool/Lboard/proc/dive(mob/living/carbon/jumper, original_layer, original_px, original_py)
|
||||
switch(rand(1, 100))
|
||||
if(1 to 20)
|
||||
jumper.visible_message("<span class='notice'>[jumper] goes for a small dive!</span>", \
|
||||
"<span class='notice'>You go for a small dive.</span>")
|
||||
sleep(15)
|
||||
backswim()
|
||||
var/atom/throw_target = get_edge_target_turf(src, dir)
|
||||
jumper.throw_at(throw_target, 1, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
|
||||
|
||||
if(21 to 40)
|
||||
jumper.visible_message("<span class='notice'>[jumper] goes for a dive!</span>", \
|
||||
"<span class='notice'>You're going for a dive!</span>")
|
||||
sleep(20)
|
||||
backswim()
|
||||
var/atom/throw_target = get_edge_target_turf(src, dir)
|
||||
jumper.throw_at(throw_target, 2, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
|
||||
|
||||
if(41 to 60)
|
||||
jumper.visible_message("<span class='notice'>[jumper] goes for a long dive! Stay far away!</span>", \
|
||||
"<span class='notice'>You're going for a long dive!!</span>")
|
||||
sleep(25)
|
||||
backswim()
|
||||
var/atom/throw_target = get_edge_target_turf(src, dir)
|
||||
jumper.throw_at(throw_target, 3, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
|
||||
|
||||
if(61 to 80)
|
||||
jumper.visible_message("<span class='notice'>[jumper] goes for an awesome dive! Don't stand in [jumper.p_their()] way!</span>", \
|
||||
"<span class='notice'>You feel like this dive will be awesome</span>")
|
||||
sleep(30)
|
||||
backswim()
|
||||
var/atom/throw_target = get_edge_target_turf(src, dir)
|
||||
jumper.throw_at(throw_target, 4, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
|
||||
if(81 to 91)
|
||||
sleep(20)
|
||||
backswim()
|
||||
jumper.visible_message("<span class='danger'>[jumper] misses [jumper.p_their()] step!</span>", \
|
||||
"<span class='userdanger'>You misstep!</span>")
|
||||
var/atom/throw_target = get_edge_target_turf(src, dir)
|
||||
jumper.throw_at(throw_target, 0, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
|
||||
jumper.Knockdown(100)
|
||||
jumper.adjustBruteLoss(10)
|
||||
|
||||
if(91 to 100)
|
||||
jumper.visible_message("<span class='notice'>[jumper] is preparing for the legendary dive! Can [jumper.p_they()] make it?</span>", \
|
||||
"<span class='userdanger'>You start preparing for a legendary dive!</span>")
|
||||
jumper.SpinAnimation(7,1)
|
||||
|
||||
sleep(30)
|
||||
if(prob(75))
|
||||
backswim()
|
||||
jumper.visible_message("<span class='notice'>[jumper] fails!</span>", \
|
||||
"<span class='userdanger'>You can't quite do it!</span>")
|
||||
var/atom/throw_target = get_edge_target_turf(src, dir)
|
||||
jumper.throw_at(throw_target, 1, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
|
||||
else
|
||||
jumper.fire_stacks = min(1,jumper.fire_stacks + 1)
|
||||
jumper.IgniteMob()
|
||||
sleep(5)
|
||||
backswim()
|
||||
jumper.visible_message("<span class='danger'>[jumper] bursts into flames of pure awesomness!</span>", \
|
||||
"<span class='userdanger'>No one can stop you now!</span>")
|
||||
var/atom/throw_target = get_edge_target_turf(src, dir)
|
||||
jumper.throw_at(throw_target, 6, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
|
||||
addtimer(CALLBACK(src, .proc/togglejumping), 35)
|
||||
reset_position(jumper, original_layer, original_px, original_py)
|
||||
|
||||
/obj/structure/pool/Lboard/proc/togglejumping()
|
||||
jumping = FALSE
|
||||
|
||||
/obj/structure/pool/Lboard/proc/on_finish_jump(mob/living/victim)
|
||||
if(istype(victim.loc, /turf/open/pool))
|
||||
var/turf/open/pool/P = victim.loc
|
||||
if(!P.filled) //you dun fucked up now
|
||||
to_chat(victim, "<span class='warning'>That was stupid of you..</span>")
|
||||
victim.visible_message("<span class='danger'>[victim] smashes into the ground!</span>")
|
||||
victim.apply_damage(50)
|
||||
victim.Knockdown(200)
|
||||
@@ -0,0 +1,59 @@
|
||||
#define POOL_WIRE_DRAIN "drain"
|
||||
#define POOL_WIRE_TEMP "temp"
|
||||
|
||||
|
||||
/datum/wires/poolcontroller
|
||||
holder_type = /obj/machinery/pool/controller
|
||||
proper_name = "Pool"
|
||||
|
||||
/datum/wires/poolcontroller/New(atom/holder)
|
||||
wires = list(
|
||||
POOL_WIRE_DRAIN, WIRE_SHOCK, WIRE_ZAP, POOL_WIRE_TEMP
|
||||
)
|
||||
add_duds(3)
|
||||
..()
|
||||
|
||||
/datum/wires/poolcontroller/interactable(mob/user)
|
||||
var/obj/machinery/pool/controller/P = holder
|
||||
if(P.panel_open)
|
||||
return TRUE
|
||||
|
||||
/datum/wires/poolcontroller/get_status()
|
||||
var/obj/machinery/pool/controller/P = holder
|
||||
var/list/status = list()
|
||||
status += "The blue light is [P.drainable ? "on" : "off"]."
|
||||
status += "The red light is [P.temperature_unlocked ? "on" : "off"]."
|
||||
status += "The yellow light is [P.shocked ? "on" : "off"]."
|
||||
return status
|
||||
|
||||
/datum/wires/poolcontroller/on_pulse(wire)
|
||||
var/obj/machinery/pool/controller/P = holder
|
||||
switch(wire)
|
||||
if(POOL_WIRE_DRAIN)
|
||||
P.drainable = FALSE
|
||||
if(POOL_WIRE_TEMP)
|
||||
P.temperature_unlocked = FALSE
|
||||
if(WIRE_SHOCK)
|
||||
P.shocked = !P.shocked
|
||||
addtimer(CALLBACK(P, /obj/machinery/autolathe.proc/reset, wire), 60)
|
||||
|
||||
/datum/wires/poolcontroller/on_cut(wire, mend)
|
||||
var/obj/machinery/pool/controller/P = holder
|
||||
switch(wire)
|
||||
if(POOL_WIRE_DRAIN)
|
||||
if(mend)
|
||||
P.drainable = FALSE
|
||||
else
|
||||
P.drainable = TRUE
|
||||
if(POOL_WIRE_TEMP)
|
||||
if(mend)
|
||||
P.temperature_unlocked = FALSE
|
||||
else
|
||||
P.temperature_unlocked = TRUE
|
||||
if(WIRE_ZAP)
|
||||
P.shock(usr, 50)
|
||||
if(WIRE_SHOCK)
|
||||
if(mend)
|
||||
P.stat &= ~NOPOWER
|
||||
else
|
||||
P.stat |= NOPOWER
|
||||
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 28 KiB |
@@ -1,3 +1,4 @@
|
||||
|
||||
// DM Environment file for tgstation.dme.
|
||||
// All manual changes should be made outside the BEGIN_ and END_ blocks.
|
||||
// New source code should be placed in .dm files: choose File/New --> Code File.
|
||||
@@ -76,6 +77,7 @@
|
||||
#include "code\__DEFINES\obj_flags.dm"
|
||||
#include "code\__DEFINES\pinpointers.dm"
|
||||
#include "code\__DEFINES\pipe_construction.dm"
|
||||
#include "code\__DEFINES\pool.dm"
|
||||
#include "code\__DEFINES\preferences.dm"
|
||||
#include "code\__DEFINES\profile.dm"
|
||||
#include "code\__DEFINES\qdel.dm"
|
||||
@@ -461,6 +463,7 @@
|
||||
#include "code\datums\elements\earhealing.dm"
|
||||
#include "code\datums\elements\ghost_role_eligibility.dm"
|
||||
#include "code\datums\elements\mob_holder.dm"
|
||||
#include "code\datums\elements\swimming.dm"
|
||||
#include "code\datums\elements\wuv.dm"
|
||||
#include "code\datums\helper_datums\events.dm"
|
||||
#include "code\datums\helper_datums\getrev.dm"
|
||||
@@ -2225,6 +2228,7 @@
|
||||
#include "code\modules\mob\living\simple_animal\hostile\netherworld.dm"
|
||||
#include "code\modules\mob\living\simple_animal\hostile\pirate.dm"
|
||||
#include "code\modules\mob\living\simple_animal\hostile\russian.dm"
|
||||
#include "code\modules\mob\living\simple_animal\hostile\sharks.dm"
|
||||
#include "code\modules\mob\living\simple_animal\hostile\skeleton.dm"
|
||||
#include "code\modules\mob\living\simple_animal\hostile\statue.dm"
|
||||
#include "code\modules\mob\living\simple_animal\hostile\stickman.dm"
|
||||
@@ -2363,6 +2367,13 @@
|
||||
#include "code\modules\photography\photos\album.dm"
|
||||
#include "code\modules\photography\photos\frame.dm"
|
||||
#include "code\modules\photography\photos\photo.dm"
|
||||
#include "code\modules\pool\pool_controller.dm"
|
||||
#include "code\modules\pool\pool_drain.dm"
|
||||
#include "code\modules\pool\pool_effects.dm"
|
||||
#include "code\modules\pool\pool_main.dm"
|
||||
#include "code\modules\pool\pool_noodles.dm"
|
||||
#include "code\modules\pool\pool_structures.dm"
|
||||
#include "code\modules\pool\pool_wires.dm"
|
||||
#include "code\modules\power\apc.dm"
|
||||
#include "code\modules\power\cable.dm"
|
||||
#include "code\modules\power\cell.dm"
|
||||
|
||||