Files
Aurora.3/code/game/objects/items/airbubble.dm
Fluffy b8902e2e16 Runtime map now loads in ~11 seconds instead of ~40, sped up various other things (#19957)
Runtime map now has a bunch of new areas / items with often-tested
stuffs, and some hard-to-put-at-runtime stuffs.
Runtime map jobs now are positioned to make it faster to reach the
aforementioned often-tested stuffs.
Runtime map doesn't generate an overmap anymore by default, which speeds
up the process.
Runtime map now loads in ~11 seconds instead of ~40 seconds as it was
before.
Updated the maploader to be faster in parsing maps.
Bapi is not engaged anymore if we're only measuring the map size, which
speeds up the process.
In fastboot we do not generate the codexes anymore, which speeds up the
process.
In fastboot and if exoplanets and away sites are not enabled, we do not
parse the map templates anymore, which speeds up the process.
Updated the icon smoothing to be faster.
Optimized cargo area code.
Other optimizations.
2024-10-06 21:31:01 +00:00

635 lines
20 KiB
Plaintext

// Regular airbubble - folded
/obj/item/airbubble
name = "air bubble"
desc = "Special air bubble designed to protect people inside of it from decompressed environments. Has an integrated cooling unit to preserve a stable temperature inside. Requires a power cell to operate."
icon = 'icons/obj/airbubble.dmi'
icon_state = "airbubble_fact_folded"
w_class = WEIGHT_CLASS_NORMAL
var/used = FALSE
var/ripped = FALSE
var/zipped = FALSE
var/obj/item/tank/internal_tank
var/syndie = FALSE
/obj/item/airbubble/Destroy()
qdel(internal_tank)
return ..()
// Deploy the bubble and transfer all properties of it
/obj/item/airbubble/attack_self(mob/user)
if (!isturf(user.loc))
to_chat(user, "You're fucking stupid, the air bubble can't deploy in an enclosed space.")
return
user.visible_message(
SPAN_WARNING("[user] begins deploying \the [src]."),
SPAN_NOTICE("You begin deplyoing \the [src].")
)
if (!do_after(user, 0.45 SECONDS))
return
user.visible_message(
SPAN_WARNING("[user] deployed \the [src].") ,
SPAN_NOTICE("You deploy \the [src].")
)
var/obj/structure/closet/airbubble/R
if(syndie)
R = new /obj/structure/closet/airbubble/syndie(user.loc)
else
R = new /obj/structure/closet/airbubble(user.loc)
if(!used)
internal_tank = new /obj/item/tank/emergency_oxygen/engi(src)
internal_tank.air_contents.adjust_gas(GAS_OXYGEN, (42*ONE_ATMOSPHERE)/(R_IDEAL_GAS_EQUATION*T20C))
R.internal_tank = internal_tank
if(!isnull(internal_tank))
internal_tank.forceMove(R)
internal_tank = null
R.add_fingerprint(user)
R.ripped = ripped
R.zipped = zipped
R.update_icon()
R.desc = desc
user.drop_from_inventory(src)
qdel(src)
// Deployed bubble
/obj/structure/closet/airbubble
name = "air bubble"
desc = "Special air bubble designed to protect people inside of it from decompressed environments. Has an integrated cooling unit to preserve a stable temperature inside. Requires a power cell to operate."
icon = 'icons/obj/airbubble.dmi'
icon_state = "airbubble"
open_sound = 'sound/items/zip.ogg'
close_sound = 'sound/items/zip.ogg'
var/item_path = /obj/item/airbubble
var/zipped = FALSE
density = 0
storage_capacity = 20
var/contains_body = FALSE
var/used = TRUE // If we have deployed it once
var/ripped = FALSE // If it has a hole it in, vent all the air outside
var/breakout_time = 1 // How many minutes it takes to break out of it.
var/use_internal_tank = TRUE
var/datum/gas_mixture/inside_air
var/internal_tank_valve = 45 // arbitrary for now
var/obj/item/tank/internal_tank
var/syndie = FALSE
var/last_shake = 0
var/cooling = FALSE
var/obj/item/cell/cell = null
var/max_cooling = 12 //in degrees per second - probably don't need to mess with heat capacity here
var/charge_consumption = 8.3 //charge per second at max_cooling
var/thermostat = T20C
slowdown = 0
// Examine to see tank pressure
/obj/structure/closet/airbubble/get_examine_text(mob/user, distance, is_adjacent, infix, suffix)
. = ..()
if(!isnull(internal_tank))
. += SPAN_NOTICE("\The [src] has [internal_tank] attached, that displays [round(internal_tank.air_contents.return_pressure() ? internal_tank.air_contents.return_pressure() : 0)] KPa.")
else
. += SPAN_NOTICE("\The [src] has no tank attached.")
if (cell)
. += "\The [src] has [cell] attached, the charge meter reads [round(cell.percent())]%."
else
. += SPAN_WARNING("[src] has no power cell installed.")
/obj/structure/closet/airbubble/can_open()
if(zipped)
return 0
return 1
/obj/structure/closet/airbubble/can_close(mob/user as mob)
if(zipped)
return 0
var/turf/T = get_turf(src)
for (var/obj/O in T)
if (O.density && O != src)
return 0
var/mob_num = 0
for(var/mob/living/M in T)
mob_num += 1
if(mob_num > 1)
to_chat(user, SPAN_WARNING("[src] can only fit one person."))
return 0
return 1
/obj/structure/closet/airbubble/dump_contents()
for(var/obj/I in src)
if (I != internal_tank && I != cell)
I.forceMove(loc)
for(var/mob/M in src)
M.forceMove(loc)
if(M.client)
M.client.eye = M.client.mob
M.client.perspective = MOB_PERSPECTIVE
/obj/structure/closet/airbubble/Initialize()
. = ..()
add_inside()
START_PROCESSING(SSfast_process, src)
/obj/structure/closet/airbubble/Destroy()
STOP_PROCESSING(SSfast_process, src)
qdel(internal_tank)
if(parts)
new parts(loc)
if (smoothing_flags)
QUEUE_SMOOTH_NEIGHBORS(src)
return ..()
/obj/structure/closet/airbubble/toggle(mob/user as mob)
if(!(opened ? close(user) : open(user)))
to_chat(user, SPAN_NOTICE("It won't budge!"))
return
update_icon()
return 1
/obj/structure/closet/airbubble/open(mob/user as mob)
if(opened)
return 0
if(!can_open())
return 0
dump_contents()
update_icon()
opened = 1
playsound(loc, open_sound, 15, 1, -3)
density = 0
return 1
/obj/structure/closet/airbubble/close(mob/user as mob)
if(!opened)
return 0
if(!can_close(user))
return 0
var/stored_units = 0
if(store_misc)
stored_units += store_misc(stored_units)
if(store_items)
stored_units += store_items(stored_units)
if(store_mobs)
stored_units += store_mobs(stored_units)
update_icon()
opened = 0
playsound(loc, close_sound, 25, 0, -3)
density = 1
add_inside()
return 1
// Fold the bubble, transfering properties.
/obj/structure/closet/airbubble/MouseDrop(over_object, src_location, over_location)
if((!zipped || ripped )&& (over_object == usr && (in_range(src, usr) || usr.contents.Find(src))))
if(!ishuman(usr)) return
if(opened) return 0
if(contents.len > 1) return 0
if(cell)
to_chat(usr, SPAN_WARNING("[src] can not be folded with [cell] attached to it."))
return
usr.visible_message(
SPAN_WARNING("[usr] begins folding up the [src.name]."),
SPAN_NOTICE("You begin folding up the [src.name].")
)
if (!do_after(usr, 0.45 SECONDS))
return
usr.visible_message(
SPAN_WARNING("[usr] folds up the [src.name].") ,
SPAN_NOTICE("You fold up the [src.name].")
)
var/obj/item/airbubble/bag
if(syndie)
bag = new /obj/item/airbubble/syndie(get_turf(src))
else
bag = new /obj/item/airbubble(get_turf(src))
bag.ripped = ripped
bag.zipped = zipped
bag.used = TRUE
bag.internal_tank = internal_tank
if(!isnull(internal_tank))
internal_tank.forceMove(bag)
internal_tank = null
bag.w_class = WEIGHT_CLASS_BULKY
bag.desc = "Special air bubble designed to protect people inside of it from decompressed environments. Has an integrated cooling unit to preserve a stable temperature inside. Requires a power cell to operate."
if(syndie)
bag.desc += " This does not seem like a regular color scheme"
bag.desc += SPAN_NOTICE(" It appears to be poorly hand folded.")
if(ripped)
bag.icon_state = "[icon_state]_man_folded_ripped"
bag.desc += SPAN_DANGER(" It has hole in it! Maybe you shouldn't use it!")
else
bag.icon_state = "[icon_state]_man_folded"
qdel(src)
return
return ..()
/obj/structure/closet/airbubble/req_breakout()
if(opened)
return 0 //Door's open... wait, why are you in it's contents then?
if(zipped)
return 1 // If it is zipped
if(breakout)
return -1 //Already breaking out.
return 0
/obj/structure/closet/airbubble/proc/breakout_callback(var/mob/living/escapee)
if (QDELETED(escapee))
return FALSE
if ((world.time - last_shake) > 5 SECONDS)
var/sound_to_play = pick(list('sound/items/rip1.ogg', 'sound/items/rip2.ogg'))
playsound(loc, sound_to_play, 100, 1)
shake_animation()
last_shake = world.time
if (!req_breakout())
return FALSE
return TRUE
// If we are stuck, and need to get out
/obj/structure/closet/airbubble/mob_breakout(var/mob/living/escapee)
if (req_breakout() < 1)
return
escapee.next_move = world.time + 100
escapee.last_special = world.time + 100
to_chat(escapee, SPAN_WARNING("You lean on the back of \the [src] and start punching internal wall with your legs. (this will take about [breakout_time] minutes)"))
visible_message(SPAN_DANGER("\The [src] begins to shake violently! Something is terring it from the inside!"))
var/time = 360 * breakout_time * 2
breakout = TRUE
if (!do_after(escapee, time, src, extra_checks = CALLBACK(src, PROC_REF(breakout_callback), escapee)))
breakout = FALSE
return
breakout = FALSE
to_chat(escapee, SPAN_WARNING("You successfully break out! Tearing the bubble's walls!")) // holy shit this is hilarious
visible_message(SPAN_DANGER("\the [escapee] successfully broke out of \the [src]! Tearing the bubble's walls!"))
var/sound_to_play = pick(list('sound/items/rip1.ogg', 'sound/items/rip2.ogg'))
playsound(loc, sound_to_play, 100, 1)
break_open()
shake_animation()
desc += SPAN_DANGER(" It has hole in it! Maybe you shouldn't use it!")
// We are out finally, the bubble is ripped. So dump everything out from it. Especially air and user.
/obj/structure/closet/airbubble/break_open()
ripped = TRUE
update_icon()
dump_contents()
var/datum/gas_mixture/t_air = get_turf_air()
t_air.merge(inside_air)
// When we shoot bubble, make it rip.
/obj/structure/closet/airbubble/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
. = ..()
if(. != BULLET_ACT_HIT)
return .
ripped = TRUE
update_icon()
// Change valve on internal tank
/obj/structure/closet/airbubble/verb/set_internals()
set src in oview(1)
set category = "Object"
set name = "Set Internals"
if(!usr.canmove || usr.stat || usr.restrained())
return
if(!ishuman(usr))
to_chat(usr, SPAN_WARNING("This mob type can't use this verb."))
return
if(!isnull(internal_tank))
usr.visible_message(
SPAN_WARNING("[usr] is setting [src] internals."),
SPAN_NOTICE("You are settting [src] internals.")
)
if (!do_after(usr, 2 SECONDS, src))
return
usr.visible_message(
SPAN_WARNING("[usr] has set [src] internals.") ,
SPAN_NOTICE("You set [src] internals.")
)
if(use_internal_tank)
STOP_PROCESSING(SSfast_process, src)
else
START_PROCESSING(SSfast_process, src)
use_internal_tank = !use_internal_tank
update_icon()
else
to_chat(usr, SPAN_NOTICE("[src] has no internal tank."))
// Remove tank from bubble
/obj/structure/closet/airbubble/verb/take_tank()
set src in oview(1)
set category = "Object"
set name = "Remove Air Tank"
if(!usr.canmove || usr.stat || usr.restrained())
return
if(!ishuman(usr))
to_chat(usr, SPAN_WARNING("This mob type can't use this verb."))
return
if(!isnull(internal_tank))
usr.visible_message(
SPAN_WARNING("[usr] is removing [internal_tank] from [src]."),
SPAN_NOTICE("You are removing [internal_tank] from [src].")
)
if (!do_after(usr, 2 SECONDS, src))
return
usr.visible_message(
SPAN_WARNING("[usr] has removed [internal_tank] from [src]."),
SPAN_NOTICE("You removed [internal_tank] from [src].")
)
for(var/obj/I in src)
I.forceMove(usr.loc)
use_internal_tank = 0
internal_tank = null
update_icon()
STOP_PROCESSING(SSfast_process, src)
else
to_chat(usr, SPAN_WARNING("[src] has no tank."))
// Remove tank from bubble
/obj/structure/closet/airbubble/verb/take_cell()
set src in oview(1)
set category = "Object"
set name = "Detach Power Cell"
if(!usr.canmove || usr.stat || usr.restrained())
return
if(!ishuman(usr))
to_chat(usr, SPAN_WARNING("This mob type can't use this verb."))
return
if(!isnull(cell))
usr.visible_message(
SPAN_WARNING("[usr] is removing [cell] from [src]."),
SPAN_NOTICE("You are removing [cell] from [src].")
)
if (!do_after(usr, 2 SECONDS, src))
return
usr.visible_message(
SPAN_WARNING("[usr] has removed [cell] from [src]."),
SPAN_NOTICE("You removed [cell] from [src].")
)
cell.forceMove(usr.loc)
cell = null
cooling = FALSE
update_icon()
else
to_chat(usr, SPAN_WARNING("[src] has no power cell."))
// Handle most of things: restraining, cutting restrains, attaching tank.
/obj/structure/closet/airbubble/attackby(obj/item/attacking_item, mob/user)
if(istype(attacking_item, /obj/item/tank))
if(!isnull(use_internal_tank))
user.visible_message(
SPAN_WARNING("[user] is attaching [attacking_item] to [src]."),
SPAN_NOTICE("You are attaching [attacking_item] to [src].")
)
if (!do_after(user, 2 SECONDS, src))
return
user.visible_message(
SPAN_WARNING("[user] has attached [attacking_item] to [src]."),
SPAN_NOTICE("You attached [attacking_item] to [src].")
)
internal_tank = attacking_item
user.drop_from_inventory(attacking_item, src)
use_internal_tank = 1
START_PROCESSING(SSfast_process, src)
else
to_chat(user, SPAN_WARNING("[src] already has a tank attached."))
return TRUE
if(opened)
if(istype(attacking_item, /obj/item/grab))
var/obj/item/grab/G = attacking_item
MouseDrop_T(G.affecting, user)
return FALSE
if(!attacking_item.dropsafety())
return FALSE
user.drop_item()
return TRUE
else if(istype(attacking_item, /obj/item/handcuffs/cable))
if(zipped)
to_chat(user, SPAN_WARNING("[src]'s zipper is already restrained."))
return TRUE
user.visible_message(
SPAN_WARNING("[user] begins putting cable restrains on zipper of [src]."),
SPAN_NOTICE("You begin putting cable restrains on zipper of [src].")
)
playsound(loc, 'sound/weapons/cablecuff.ogg', 50, 1)
if (!do_after(user, 3 SECONDS, src, extra_checks = CALLBACK(src, PROC_REF(is_closed))))
return TRUE
zipped = !zipped
update_icon()
user.visible_message(
SPAN_WARNING("[src]'s zipper have been zipped by [user]."),
SPAN_NOTICE("You put restrains on [src]'s zipper.")
)
qdel(attacking_item)
update_icon()
return TRUE
else if(attacking_item.iswirecutter())
if(!zipped)
to_chat(user, SPAN_WARNING("[src] has no cables to cut."))
attack_hand(user)
return TRUE
user.visible_message(
SPAN_WARNING("[user] begins cutting cable restrains on zipper of [src]."),
SPAN_NOTICE("You begin cutting cable restrains on zipper of [src].")
)
playsound(loc, 'sound/items/Wirecutter.ogg', 50, 1)
if (!do_after(user, 3 SECONDS, src, extra_checks = CALLBACK(src, PROC_REF(is_closed))))
return TRUE
zipped = !zipped
update_icon()
user.visible_message(
SPAN_WARNING("[src] zipper's cable restrains has been cut by [user]."),
SPAN_NOTICE("You cut cable restrains on [src]'s zipper.")
)
new/obj/item/handcuffs/cable(src.loc)
update_icon()
return TRUE
else if(istype(attacking_item, /obj/item/cell))
if(!isnull(cell))
to_chat(user, SPAN_WARNING("[src] already has [cell] attached to it."))
attack_hand(user)
return TRUE
user.visible_message(
SPAN_WARNING("[user] is attaching [attacking_item] to [src]."),
SPAN_NOTICE("You are attaching [attacking_item] to [src].")
)
if (!do_after(user, 2 SECONDS, src))
return TRUE
user.visible_message(
SPAN_WARNING("[user] has attached [attacking_item] to [src]."),
SPAN_NOTICE("You attached [attacking_item] to [src].")
)
cell = attacking_item
cooling = TRUE
user.drop_from_inventory(attacking_item, src)
return TRUE
else
return attack_hand(user)
/obj/structure/closet/airbubble/store_mobs(var/stored_units, var/mob_limit)
contains_body = ..(stored_units, mob_limit = TRUE)
return contains_body
/obj/structure/closet/airbubble/update_icon()
ClearOverlays()
if(ripped)
name = "ripped air bubble"
icon_state = "[icon_state]_ripped"
else
icon_state = "[initial(icon_state)][opened ? "_open" : ""]"
if(zipped)
AddOverlays("[icon_state]_restrained")
AddOverlays("pressure_[(use_internal_tank) ?("on") : ("off") ]")
// Process transfer of air from the tank. Handle if it is ripped open.
/obj/structure/closet/airbubble/proc/process_tank_give_air()
if(internal_tank)
var/datum/gas_mixture/tank_air = internal_tank.return_air()
var/release_pressure = internal_tank_valve
// If ripped, we are leaking
if(ripped)
// If we has no pressure in the tank, why bother?
if(tank_air.return_pressure() <= 1)
STOP_PROCESSING(SSfast_process, src)
use_internal_tank = !use_internal_tank
visible_message(SPAN_WARNING("You hear last bits of air coming out from [src]'s hole.Maybe the tank run out of air?"))
playsound(loc, 'sound/effects/wind/wind_2_1.ogg', 100, 1)
return
inside_air = get_turf_air()
visible_message(SPAN_WARNING("You hear air howling from [src]'s hole. Maybe it is good to shut off valve on the internals tank?"))
playsound(loc, 'sound/effects/wind/wind_2_2.ogg', 100, 1)
var/transfer_moles = inside_air.volume/(inside_air.temperature * R_IDEAL_GAS_EQUATION)
var/datum/gas_mixture/removed = tank_air.remove(transfer_moles)
if(inside_air)
inside_air.merge(removed)
else
qdel(removed)
return
var/inside_pressure = inside_air.return_pressure()
var/pressure_delta = min(release_pressure - inside_pressure, (tank_air.return_pressure() - inside_pressure)/2)
var/transfer_moles = 0
if(pressure_delta > 0) //inside pressure lower than release pressure
if(tank_air.temperature > 0)
transfer_moles = pressure_delta*inside_air.volume/(inside_air.temperature * R_IDEAL_GAS_EQUATION)
var/datum/gas_mixture/removed = tank_air.remove(transfer_moles)
inside_air.merge(removed)
else if(pressure_delta < 0) //inside pressure higher than release pressure
var/datum/gas_mixture/t_air = get_turf_air()
pressure_delta = inside_pressure - release_pressure
if(t_air)
pressure_delta = min(inside_pressure - t_air.return_pressure(), pressure_delta)
if(pressure_delta > 0) //if location pressure is lower than inside pressure
transfer_moles = pressure_delta*inside_air.volume/(inside_air.temperature * R_IDEAL_GAS_EQUATION)
var/datum/gas_mixture/removed = inside_air.remove(transfer_moles)
if(t_air)
t_air.merge(removed)
else //just delete the inside gas, we're in space or some shit
qdel(removed)
/obj/structure/closet/airbubble/proc/process_preserve_temp()
if (!cooling || !cell)
return
for(var/mob/living/carbon/human/H in src)
var/datum/gas_mixture/t_air = get_turf_air()
if(!t_air)
return
var/env_temp = t_air.temperature
var/temp_adj = min(H.bodytemperature - max(thermostat, env_temp), max_cooling)
if (temp_adj < 0.5) //only cools, doesn't heat, also we don't need extreme precision
return
var/charge_usage = (temp_adj/max_cooling)*charge_consumption
H.bodytemperature -= temp_adj
cell.use(charge_usage)
if(cell.charge <= 0)
cooling = FALSE
/obj/structure/closet/airbubble/return_air()
if(use_internal_tank)
return inside_air
return ..()
/obj/structure/closet/airbubble/proc/get_turf_air()
var/turf/T = get_turf(src)
if(T)
return T.return_air()
/obj/structure/closet/airbubble/proc/return_pressure()
. = 0
if(use_internal_tank)
. = inside_air.return_pressure()
else
var/datum/gas_mixture/t_air = get_turf_air()
if(t_air)
. = t_air.return_pressure()
return
/obj/structure/closet/airbubble/proc/return_temperature()
. = 0
if(use_internal_tank)
. = inside_air.temperature
else
var/datum/gas_mixture/t_air = get_turf_air()
if(t_air)
. = t_air.temperature
return
/obj/structure/closet/airbubble/process()
process_preserve_temp()
process_tank_give_air()
/obj/structure/closet/airbubble/proc/add_inside()
inside_air = new
inside_air.temperature = T20C
inside_air.volume = 2
inside_air.adjust_multi(GAS_OXYGEN, O2STANDARD*inside_air.volume/(R_IDEAL_GAS_EQUATION*inside_air.temperature), GAS_NITROGEN, N2STANDARD*inside_air.volume/(R_IDEAL_GAS_EQUATION*inside_air.temperature))
return inside_air
// Syndicate airbubble
/obj/item/airbubble/syndie
name = "air bubble"
desc = "Special air bubble designed to protect people inside of it from decompressed environments. Has an integrated cooling unit to preserve a stable temperature inside. Requires a power cell to operate. This does not seem like a regular color scheme."
icon_state = "airbubble_syndie_fact_folded"
syndie = TRUE
/obj/structure/closet/airbubble/syndie
name = "air bubble"
desc = "Special air bubble designed to protect people inside of it from decompressed environments. Has an integrated cooling unit to preserve a stable temperature inside. Requires a power cell to operate. This does not seem like a regular color scheme."
icon_state = "airbubble_syndie"
item_path = /obj/item/airbubble/syndie
syndie = TRUE