diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index 4411c991873dd..2dd680c073300 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -210,6 +210,8 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list(
#define ismecha(A) (istype(A, /obj/vehicle/sealed/mecha))
+#define ismedicalmecha(A) (istype(A, /obj/vehicle/sealed/mecha/medical))
+
#define ismopable(A) (A && (A.layer <= HIGH_SIGIL_LAYER)) //If something can be cleaned by floor-cleaning devices such as mops or clean bots
#define isorgan(A) (istype(A, /obj/item/organ))
diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm
index 3a746e3639194..dbd4f1aefcf54 100644
--- a/code/__DEFINES/say.dm
+++ b/code/__DEFINES/say.dm
@@ -63,6 +63,7 @@
#define SPAN_MEGAPHONE "megaphone"
#define SPAN_CLOWN "clowntext"
#define SPAN_SINGING "singing"
+#define SPAN_TAPE_RECORDER "tape_recorder"
//bitflag #defines for return value of the radio() proc.
#define ITALICS (1<<0)
diff --git a/code/datums/looping_sounds/item_sounds.dm b/code/datums/looping_sounds/item_sounds.dm
index 5b02eef9c4ce6..181e1d5a12b70 100644
--- a/code/datums/looping_sounds/item_sounds.dm
+++ b/code/datums/looping_sounds/item_sounds.dm
@@ -51,3 +51,8 @@
mid_sounds = list('sound/items/weeoo1.ogg' = 1)
mid_length = 15
volume = 20
+
+/datum/looping_sound/tape_recorder_hiss
+ mid_sounds = list('sound/items/taperecorder/taperecorder_hiss_mid.ogg')
+ start_sound = list('sound/items/taperecorder/taperecorder_hiss_start.ogg')
+ volume = 10
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 0b5be56586805..20e5b0d19fad2 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -695,11 +695,14 @@
/atom/movable/proc/newtonian_move(direction, instant = FALSE) // Accepts the direction to move, and if the push should be instant
- if(!loc || Process_Spacemove(0) || !direction)
+ if(!isturf(loc) || Process_Spacemove(0) || !direction)
return FALSE
+
if(SEND_SIGNAL(src, COMSIG_MOVABLE_NEWTONIAN_MOVE, direction) & COMPONENT_MOVABLE_NEWTONIAN_BLOCK)
return TRUE
+
AddComponent(/datum/component/drift, direction, instant)
+
return TRUE
/atom/movable/proc/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
diff --git a/code/game/gamemodes/clown_ops/clown_weapons.dm b/code/game/gamemodes/clown_ops/clown_weapons.dm
index a7eb976c49c1a..defcab2e4be36 100644
--- a/code/game/gamemodes/clown_ops/clown_weapons.dm
+++ b/code/game/gamemodes/clown_ops/clown_weapons.dm
@@ -252,73 +252,3 @@
/obj/item/clothing/mask/fakemoustache/sticky/proc/unstick()
REMOVE_TRAIT(src, TRAIT_NODROP, STICKY_MOUSTACHE_TRAIT)
-
-//DARK H.O.N.K. AND CLOWN MECH WEAPONS
-
-/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/banana_mortar/bombanana
- name = "bombanana mortar"
- desc = "Equipment for clown exosuits. Launches exploding banana peels."
- icon_state = "mecha_bananamrtr"
- projectile = /obj/item/grown/bananapeel/bombanana
- projectiles = 8
- projectile_energy_cost = 1000
-
-/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/banana_mortar/bombanana/can_attach(obj/vehicle/sealed/mecha/combat/honker/M)
- if(..())
- if(istype(M))
- return TRUE
- return FALSE
-
-/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang/tearstache
- name = "\improper HONKeR-6 grenade launcher"
- desc = "A weapon for combat exosuits. Launches primed tear-stache grenades."
- icon_state = "mecha_grenadelnchr"
- projectile = /obj/item/grenade/chem_grenade/teargas/moustache
- fire_sound = 'sound/weapons/grenadelaunch.ogg'
- projectiles = 6
- missile_speed = 1.5
- projectile_energy_cost = 800
- equip_cooldown = 60
- det_time = 20
-
-/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang/tearstache/can_attach(obj/vehicle/sealed/mecha/combat/honker/M)
- if(..())
- if(istype(M))
- return TRUE
- return FALSE
-
-/obj/vehicle/sealed/mecha/combat/honker/dark
- desc = "Produced by \"Tyranny of Honk, INC\", this exosuit is designed as heavy clown-support. This one has been painted black for maximum fun. HONK!"
- name = "\improper Dark H.O.N.K"
- icon_state = "darkhonker"
- base_icon_state = "darkhonker"
- max_integrity = 300
- deflect_chance = 15
- armor = list(MELEE = 40, BULLET = 40, LASER = 50, ENERGY = 35, BOMB = 20, BIO = 0, RAD = 0, FIRE = 100, ACID = 100, STAMINA = 0, BLEED = 0)
- max_temperature = 35000
- operation_req_access = list(ACCESS_SYNDICATE)
- internals_req_access = list(ACCESS_SYNDICATE)
- wreckage = /obj/structure/mecha_wreckage/honker/dark
- max_equip = 4
-
-/obj/vehicle/sealed/mecha/combat/honker/dark/add_cell(obj/item/stock_parts/cell/C)
- if(C)
- C.forceMove(src)
- cell = C
- return
- cell = new /obj/item/stock_parts/cell/hyper(src)
-
-/obj/vehicle/sealed/mecha/combat/honker/dark/loaded/Initialize(mapload)
- . = ..()
- var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/thrusters/ion(src)
- ME.attach(src)
- ME = new /obj/item/mecha_parts/mecha_equipment/weapon/honker()
- ME.attach(src)
- ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/banana_mortar/bombanana()//Needed more offensive weapons.
- ME.attach(src)
- ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang/tearstache()//The mousetrap mortar was not up-to-snuff.
- ME.attach(src)
-
-/obj/structure/mecha_wreckage/honker/dark
- name = "\improper Dark H.O.N.K wreckage"
- icon_state = "darkhonker-broken"
diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm
index ab5d8bad62876..98d6381ede1fb 100644
--- a/code/game/objects/items/devices/taperecorder.dm
+++ b/code/game/objects/items/devices/taperecorder.dm
@@ -11,53 +11,105 @@
slot_flags = ITEM_SLOT_BELT
custom_materials = list(/datum/material/iron=60, /datum/material/glass=30)
force = 2
- throwforce = 0
+ throwforce = 2
+ speech_span = SPAN_TAPE_RECORDER
drop_sound = 'sound/items/handling/taperecorder_drop.ogg'
pickup_sound = 'sound/items/handling/taperecorder_pickup.ogg'
- var/recording = 0
- var/playing = 0
+ var/recording = FALSE
+ var/playing = FALSE
var/playsleepseconds = 0
var/obj/item/tape/mytape
var/starting_tape_type = /obj/item/tape/random
- var/open_panel = 0
- var/canprint = 1
+ var/open_panel = FALSE
+ var/canprint = TRUE
+ var/list/icons_available = list()
+ var/radial_icon_file = 'icons/obj/radial_taperecorder.dmi'
+ ///Whether we've warned during this recording session that the tape is almost up.
+ var/time_warned = FALSE
+ ///Seconds under which to warn that the tape is almost up.
+ var/time_left_warning = 60 SECONDS
+ ///Sound loop that plays when recording or playing back.
+ var/datum/looping_sound/tape_recorder_hiss/soundloop
/obj/item/taperecorder/Initialize(mapload)
. = ..()
if(starting_tape_type)
mytape = new starting_tape_type(src)
+ soundloop = new(src, FALSE)
update_icon()
become_hearing_sensitive()
+/obj/item/taperecorder/proc/readout()
+ if(mytape)
+ if(playing)
+ return "PLAYING"
+ else
+ var/time = mytape.used_capacity / 10 //deciseconds / 10 = seconds
+ var/mins = round(time / 60)
+ var/secs = time - mins * 60
+ return "[mins]m [secs]s"
+ return "NO TAPE INSERTED"
+
+/obj/item/taperecorder/proc/update_available_icons()
+ icons_available = list()
+
+ if(!playing && !recording)
+ icons_available += list("Record" = image(radial_icon_file,"record"))
+ icons_available += list("Play" = image(radial_icon_file,"play"))
+ if(canprint && mytape?.storedinfo.len)
+ icons_available += list("Print Transcript" = image(radial_icon_file,"print"))
+ if(playing || recording)
+ icons_available += list("Stop" = image(radial_icon_file,"stop"))
+ if(mytape)
+ icons_available += list("Eject" = image(radial_icon_file,"eject"))
+
+/obj/item/taperecorder/proc/update_sound()
+ if(!playing && !recording)
+ soundloop.stop()
+ else
+ soundloop.start()
+
/obj/item/taperecorder/Destroy()
+ QDEL_NULL(soundloop)
QDEL_NULL(mytape)
return ..()
/obj/item/taperecorder/examine(mob/user)
. = ..()
- . += "The wire panel is [open_panel ? "opened" : "closed"]."
-
+ if(in_range(src, user) || isobserver(user))
+ . += "The wire panel is [open_panel ? "opened" : "closed"]. The display reads:"
+ . += "[readout()]"
/obj/item/taperecorder/attackby(obj/item/I, mob/user, params)
if(!mytape && istype(I, /obj/item/tape))
if(!user.transferItemToLoc(I,src))
return
mytape = I
- to_chat(user, "You insert [I] into [src].")
- update_icon()
+ balloon_alert(user, "inserted [mytape]")
+ playsound(src, 'sound/items/taperecorder/taperecorder_close.ogg', 50, FALSE)
+ update_appearance()
/obj/item/taperecorder/proc/eject(mob/user)
- if(mytape)
- to_chat(user, "You remove [mytape] from [src].")
- stop()
- user.put_in_hands(mytape)
- mytape = null
- update_icon()
+ if(!mytape)
+ balloon_alert(user, "no tape!")
+ return
+ if(playing)
+ balloon_alert(user, "stop the tape first!")
+ return
+ if(recording)
+ balloon_alert(user, "stop the recording first!")
+ return
+ playsound(src, 'sound/items/taperecorder/taperecorder_open.ogg', 50, FALSE)
+ balloon_alert(user, "ejected [mytape]")
+ stop()
+ user.put_in_hands(mytape)
+ mytape = null
+ update_icon()
/obj/item/taperecorder/fire_act(exposed_temperature, exposed_volume)
- mytape.ruin() //Fires destroy the tape
+ mytape.unspool() //Fires unspool the tape, which makes sense if you don't think about it
..()
//ATTACK HAND IGNORING PARENT RETURN VALUE
@@ -82,8 +134,10 @@
set category = "Object"
if(!can_use(usr))
+ balloon_alert(usr, "can't use!")
return
if(!mytape)
+ balloon_alert(usr, "no tape!")
return
eject(usr)
@@ -107,37 +161,48 @@
. = ..()
if(mytape && recording)
mytape.timestamp += mytape.used_capacity
- mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [message]"
+ mytape.storedinfo += "\[[time2text(mytape.used_capacity,"mm:ss")]\] [message]"
/obj/item/taperecorder/verb/record()
set name = "Start Recording"
set category = "Object"
if(!can_use(usr))
+ balloon_alert(usr, "can't use!")
return
- if(!mytape || mytape.ruined)
+ if(!mytape || mytape.unspooled)
+ balloon_alert(usr, "no spooled tape!")
return
if(recording)
+ balloon_alert(usr, "stop recording first!")
return
if(playing)
+ balloon_alert(usr, "already playing!")
return
+ playsound(src, 'sound/items/taperecorder/taperecorder_play.ogg', 50, FALSE)
+
if(mytape.used_capacity < mytape.max_capacity)
- to_chat(usr, "Recording started.")
- recording = 1
+ recording = TRUE
+ balloon_alert(usr, "started recording")
+ update_sound()
update_icon()
- mytape.timestamp += mytape.used_capacity
- mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] Recording started."
var/used = mytape.used_capacity //to stop runtimes when you eject the tape
var/max = mytape.max_capacity
while(recording && used < max)
- mytape.used_capacity++
- used++
- sleep(10)
- recording = 0
- update_icon()
+ mytape.used_capacity += 1 SECONDS
+ used += 1 SECONDS
+ if(max - used < time_left_warning && !time_warned)
+ time_warned = TRUE
+ balloon_alert(usr, "[(max - used) / 10] second\s left")
+ sleep(1 SECONDS)
+ if(used >= max)
+ balloon_alert(usr, "tape full!")
+ sleep(1 SECONDS) //prevent balloon alerts layering over the top of each other
+ stop()
else
- to_chat(usr, "The tape is full.")
+ balloon_alert(usr, "tape full!")
+ playsound(src, 'sound/items/taperecorder/taperecorder_stop.ogg', 50, FALSE)
/obj/item/taperecorder/verb/stop()
@@ -145,70 +210,93 @@
set category = "Object"
if(!can_use(usr))
+ balloon_alert(usr, "can't use!")
return
if(recording)
- recording = 0
- mytape.timestamp += mytape.used_capacity
- mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] Recording stopped."
- to_chat(usr, "Recording stopped.")
- return
+ playsound(src, 'sound/items/taperecorder/taperecorder_stop.ogg', 50, FALSE)
+ balloon_alert(usr, "stopped recording")
+ recording = FALSE
else if(playing)
- playing = 0
- var/turf/T = get_turf(src)
- T.visible_message("Tape Recorder: Playback stopped.")
+ playsound(src, 'sound/items/taperecorder/taperecorder_stop.ogg', 50, FALSE)
+ balloon_alert(usr, "stopped playing")
+ playing = FALSE
+ time_warned = FALSE
update_icon()
-
+ update_sound()
/obj/item/taperecorder/verb/play()
set name = "Play Tape"
set category = "Object"
if(!can_use(usr))
+ balloon_alert(usr, "can't use!")
return
- if(!mytape || mytape.ruined)
+ if(!mytape || mytape.unspooled)
+ balloon_alert(usr, "no spooled tape!")
return
if(recording)
+ balloon_alert(usr, "stop recording first!")
return
if(playing)
+ balloon_alert(usr, "already playing!")
+ return
+ if(mytape.storedinfo?.len <= 0)
+ balloon_alert(usr, "[mytape] is empty!")
return
- playing = 1
+ playing = TRUE
update_icon()
- to_chat(usr, "Playing started.")
+ update_sound()
+ balloon_alert(usr, "started playing")
+ playsound(src, 'sound/items/taperecorder/taperecorder_play.ogg', 50, FALSE)
var/used = mytape.used_capacity //to stop runtimes when you eject the tape
var/max = mytape.max_capacity
- for(var/i = 1, used < max, sleep(10 * playsleepseconds))
+ for(var/i = 1, used <= max, sleep(playsleepseconds))
if(!mytape)
break
- if(playing == 0)
+ if(playing == FALSE)
break
if(mytape.storedinfo.len < i)
+ balloon_alert(usr, "recording ended")
+ sleep(1 SECONDS)
break
- say(mytape.storedinfo[i])
+ say("[mytape.storedinfo[i]]")
if(mytape.storedinfo.len < i + 1)
playsleepseconds = 1
- sleep(10)
- say("End of recording.")
+ sleep(1 SECONDS)
else
playsleepseconds = mytape.timestamp[i + 1] - mytape.timestamp[i]
- if(playsleepseconds > 14)
- sleep(10)
- say("Skipping [playsleepseconds] seconds of silence")
- playsleepseconds = 1
+ if(playsleepseconds > 14 SECONDS)
+ sleep(1 SECONDS)
+ say("Skipping [playsleepseconds/10] seconds of silence.")
+ playsleepseconds = 1 SECONDS
i++
- playing = 0
- update_icon()
+ stop()
/obj/item/taperecorder/attack_self(mob/user)
- if(!mytape || mytape.ruined)
+ if(!mytape)
+ balloon_alert(user, "it's empty!")
return
- if(recording)
- stop()
- else
- record()
+
+ update_available_icons()
+ if(icons_available)
+ var/selection = show_radial_menu(user, src, icons_available, radius = 38, require_near = TRUE, tooltips = TRUE)
+ if(!selection)
+ return
+ switch(selection)
+ if("Stop")
+ stop()
+ if("Record")
+ record()
+ if("Play")
+ play()
+ if("Print Transcript")
+ print_transcript()
+ if("Eject")
+ eject(user)
/obj/item/taperecorder/verb/print_transcript()
@@ -217,15 +305,22 @@
var/list/transcribed_info = mytape.storedinfo
if(!length(transcribed_info))
- return
- if(!mytape)
+ balloon_alert(usr, "tape is empty!")
return
if(!canprint)
- to_chat(usr, "The recorder can't print that fast!")
- return
- if(recording || playing)
+ balloon_alert(usr, "can't print that fast!")
return
if(!can_use(usr))
+ balloon_alert(usr, "can't use!")
+ return
+ if(!mytape || mytape.unspooled)
+ balloon_alert(usr, "no spooled tape!")
+ return
+ if(recording)
+ balloon_alert(usr, "stop recording first!")
+ return
+ if(playing)
+ balloon_alert(usr, "already playing!")
return
var/transcribed_text = "Transcript:
"
@@ -240,7 +335,7 @@
// Very unexpected. Better abort non-gracefully.
if(excerpt_length > MAX_PAPER_LENGTH)
- say("Error: Data corruption detected. Cannot print.")
+ balloon_alert(usr, "data corrupted, can't print!")
CRASH("Transcript entry has more than [MAX_PAPER_LENGTH] chars: [excerpt_length] chars")
// If we're going to overflow the paper's length, print the current transcribed text out first and reset to prevent us
@@ -259,7 +354,10 @@
transcript_paper.add_raw_text(transcribed_text)
transcript_paper.name = "[paper_name] page [page_count]"
transcript_paper.update_appearance()
- to_chat(usr, "Transcript printed, [page_count] pages.")
+ balloon_alert(usr, "transcript printed\n[page_count] page\s")
+ playsound(src, 'sound/items/taperecorder/taperecorder_print.ogg', 50, FALSE)
+
+ // Can't put the entire stack into their hands if there's multple pages, but hey we can at least put one page in.
usr.put_in_hands(transcript_paper)
canprint = FALSE
addtimer(VARSET_CALLBACK(src, canprint, TRUE), 30 SECONDS)
@@ -272,7 +370,7 @@
/obj/item/tape
name = "tape"
- desc = "A magnetic tape that can hold up to ten minutes of content."
+ desc = "A magnetic tape that can hold up to ten minutes of content on either side."
icon_state = "tape_white"
icon = 'icons/obj/device.dmi'
item_state = "analyzer"
@@ -282,46 +380,123 @@
custom_materials = list(/datum/material/iron=20, /datum/material/glass=5)
force = 1
throwforce = 0
- var/max_capacity = 600
- var/used_capacity = 0
+ obj_flags = UNIQUE_RENAME //my mixtape
+ drop_sound = 'sound/items/handling/tape_drop.ogg'
+ pickup_sound = 'sound/items/handling/tape_pickup.ogg'
+ ///Because we can't expect God to do all the work.
+ var/initial_icon_state
+ var/max_capacity = 10 MINUTES
+ var/used_capacity = 0 SECONDS
+ ///Numbered list of chat messages the recorder has heard with spans and prepended timestamps. Used for playback and transcription.
var/list/storedinfo = list()
+ ///Numbered list of seconds the messages in the previous list appear at on the tape. Used by playback to get the timing right.
var/list/timestamp = list()
- var/ruined = 0
+ var/used_capacity_otherside = 0 SECONDS //Separate my side
+ var/list/storedinfo_otherside = list()
+ var/list/timestamp_otherside = list()
+ var/unspooled = FALSE
+ var/list/icons_available = list()
+ var/radial_icon_file = 'icons/obj/radial_tape.dmi'
/obj/item/tape/fire_act(exposed_temperature, exposed_volume)
- ruin()
+ unspool()
..()
-/obj/item/tape/attack_self(mob/user)
- if(!ruined)
- to_chat(user, "You pull out all the tape!")
- ruin()
+/obj/item/tape/Initialize(mapload)
+ . = ..()
+ initial_icon_state = icon_state //random tapes will set this after choosing their icon
+ var/mycolor = random_short_color()
+ name += " ([mycolor])" //multiple tapes can get confusing fast
+ if(icon_state == "tape_greyscale")
+ add_atom_colour("#[mycolor]", FIXED_COLOUR_PRIORITY)
+ if(prob(50))
+ tapeflip()
+
+/obj/item/tape/proc/update_available_icons()
+ icons_available = list()
+ if(!unspooled)
+ icons_available += list("Unwind tape" = image(radial_icon_file,"tape_unwind"))
+ icons_available += list("Flip tape" = image(radial_icon_file,"tape_flip"))
+/obj/item/tape/attack_self(mob/user)
+ update_available_icons()
+ if(icons_available)
+ var/selection = show_radial_menu(user, src, icons_available, radius = 38, require_near = TRUE, tooltips = TRUE)
+ if(!selection)
+ return
+ switch(selection)
+ if("Flip tape")
+ if(loc != user)
+ return
+ tapeflip()
+ balloon_alert(user, "flipped tape")
+ playsound(src, 'sound/items/taperecorder/tape_flip.ogg', 70, FALSE)
+ if("Unwind tape")
+ if(loc != user)
+ return
+ unspool()
+ balloon_alert(user, "unspooled tape")
+
+/obj/item/tape/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
+ if(prob(50))
+ tapeflip()
+ . = ..()
-/obj/item/tape/proc/ruin()
- //Lets not add infinite amounts of overlays when our fireact is called
- //repeatedly
- if(!ruined)
+/obj/item/tape/proc/unspool()
+ //Let's not add infinite amounts of overlays when our fire_act is called repeatedly
+ if(!unspooled)
add_overlay("ribbonoverlay")
- ruined = 1
-
+ unspooled = TRUE
-/obj/item/tape/proc/fix()
+/obj/item/tape/proc/respool()
cut_overlay("ribbonoverlay")
- ruined = 0
-
-
-/obj/item/tape/attackby(obj/item/I, mob/user, params)
- if(ruined && I.tool_behaviour == TOOL_SCREWDRIVER || istype(I, /obj/item/pen))
- to_chat(user, "You start winding the tape back in...")
- if(I.use_tool(src, user, 120))
- to_chat(user, "You wound the tape back in.")
- fix()
+ unspooled = FALSE
+
+/obj/item/tape/proc/tapeflip()
+ //first we save a copy of our current side
+ var/list/storedinfo_currentside = storedinfo.Copy()
+ var/list/timestamp_currentside = timestamp.Copy()
+ var/used_capacity_currentside = used_capacity
+ //then we overwite our current side with our other side
+ storedinfo = storedinfo_otherside.Copy()
+ timestamp = timestamp_otherside.Copy()
+ used_capacity = used_capacity_otherside
+ //then we overwrite our other side with the saved side
+ storedinfo_otherside = storedinfo_currentside.Copy()
+ timestamp_otherside = timestamp_currentside.Copy()
+ used_capacity_otherside = used_capacity_currentside
+
+ if(icon_state == initial_icon_state)
+ icon_state = "[initial_icon_state]_reverse"
+ else if(icon_state == "[initial_icon_state]_reverse") //so flipping doesn't overwrite an unexpected icon_state (e.g. an admin's)
+ icon_state = initial_icon_state
+
+/obj/item/tape/attackby(obj/item/tool, mob/user, params)
+ if(unspooled && (istype(tool, /obj/item/pen)))
+ balloon_alert(user, "respooling tape...")
+ if(!tool.use_tool(src, user, 12 SECONDS))
+ balloon_alert(user, "respooling failed!")
+ return FALSE
+ balloon_alert(user, "tape respooled")
+ respool()
+
+/obj/item/tape/screwdriver_act(mob/living/user, obj/item/tool)
+ if(!unspooled)
+ return FALSE
+ balloon_alert(user, "respooling tape...")
+ if(!tool.use_tool(src, user, 12 SECONDS))
+ balloon_alert(user, "respooling failed!")
+ return FALSE
+ balloon_alert(user, "tape respooled")
+ respool()
//Random colour tapes
/obj/item/tape/random
icon_state = "random_tape"
/obj/item/tape/random/Initialize(mapload)
+ icon_state = "tape_[pick("white", "blue", "red", "yellow", "purple", "tape_greyscale")]"
. = ..()
- icon_state = "tape_[pick("white", "blue", "red", "yellow", "purple")]"
+
+/obj/item/tape/dyed
+ icon_state = "tape_greyscale"
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index c970ae658ff1f..8a238b6a098c9 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -910,7 +910,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
// Ghosts have no momentum, being massless ectoplasm
/mob/dead/observer/Process_Spacemove(movement_dir)
- return 1
+ return TRUE
/mob/dead/observer/vv_edit_var(var_name, var_value)
. = ..()
diff --git a/code/modules/mob/living/simple_animal/hostile/mecha_pilot.dm b/code/modules/mob/living/simple_animal/hostile/mecha_pilot.dm
index e46b40ddf8ba6..fd6d4521dba22 100644
--- a/code/modules/mob/living/simple_animal/hostile/mecha_pilot.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mecha_pilot.dm
@@ -292,6 +292,6 @@
/mob/living/simple_animal/hostile/syndicate/mecha_pilot/Goto(target, delay, minimum_distance)
if(mecha)
- SSmove_manager.move_to(mecha, target, minimum_distance, mecha.movedelay * mecha.step_multiplier)
+ SSmove_manager.move_to(mecha, target, minimum_distance, mecha.movedelay)
else
..()
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index 33b6bd2d00535..27fe963ce5b4a 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -304,7 +304,8 @@
* You can move in space if you have a spacewalk ability
*/
/mob/Process_Spacemove(movement_dir = 0)
- if(spacewalk || ..())
+ . = ..()
+ if(. ||spacewalk)
return TRUE
var/atom/movable/backup = get_spacemove_backup(movement_dir)
if(backup)
diff --git a/code/modules/projectiles/ammunition/_ammunition.dm b/code/modules/projectiles/ammunition/_ammunition.dm
index eba49b5d66e7d..7d8ef8fb69c13 100644
--- a/code/modules/projectiles/ammunition/_ammunition.dm
+++ b/code/modules/projectiles/ammunition/_ammunition.dm
@@ -54,7 +54,7 @@
return ..()
/obj/item/ammo_casing/update_icon()
- ..()
+ . = ..()
icon_state = "[initial(icon_state)][BB ? "-live" : ""]"
desc = "[initial(desc)][BB ? "" : " This one is spent."]"
@@ -88,7 +88,7 @@
/obj/item/ammo_casing/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(heavy_metal)
bounce_away(FALSE, NONE)
- . = ..()
+ return ..()
/obj/item/ammo_casing/proc/bounce_away(still_warm = FALSE, bounce_delay = 3)
update_icon()
diff --git a/code/modules/projectiles/ammunition/special/syringe.dm b/code/modules/projectiles/ammunition/special/syringe.dm
index 188f80b51d1cd..1b2eb71f75dc3 100644
--- a/code/modules/projectiles/ammunition/special/syringe.dm
+++ b/code/modules/projectiles/ammunition/special/syringe.dm
@@ -19,7 +19,16 @@
SG.syringes.Remove(S)
S.forceMove(BB)
D.syringe = S
- ..()
+ else if(istype(loc, /obj/item/mecha_parts/mecha_equipment/medical/syringe_gun))
+ var/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/syringe_gun = loc
+ var/obj/item/reagent_containers/syringe/loaded_syringe = syringe_gun.syringes[1]
+ var/obj/projectile/bullet/dart/shot_dart = BB
+ syringe_gun.reagents.trans_to(shot_dart, min(loaded_syringe.volume, syringe_gun.reagents.total_volume), transfered_by = user)
+ shot_dart.name = loaded_syringe.name
+ shot_dart.piercing = loaded_syringe.proj_piercing
+ LAZYREMOVE(syringe_gun.syringes, loaded_syringe)
+ qdel(loaded_syringe)
+ return ..()
/obj/item/ammo_casing/chemgun
name = "dart synthesiser"
@@ -37,7 +46,7 @@
CG.reagents.trans_to(BB, 15, transfered_by = user)
BB.name = "chemical dart"
CG.syringes_left--
- ..()
+ return ..()
/obj/item/ammo_casing/bee
name = "bee synthesiser"
@@ -55,7 +64,7 @@
CG.reagents.trans_to(BB, 5, transfered_by = user)
BB.name = "bee"
CG.syringes_left--
- ..()
+ return ..()
/obj/item/ammo_casing/dnainjector
name = "rigged syringe gun spring"
@@ -75,5 +84,5 @@
var/obj/projectile/bullet/dnainjector/D = BB
S.forceMove(D)
D.injector = S
- ..()
+ return ..()
diff --git a/code/modules/vehicles/_vehicle.dm b/code/modules/vehicles/_vehicle.dm
index 29dc5b3dfaf7c..bacce6c3aee88 100644
--- a/code/modules/vehicles/_vehicle.dm
+++ b/code/modules/vehicles/_vehicle.dm
@@ -60,7 +60,7 @@
return occupants
/obj/vehicle/proc/occupant_amount()
- return length(occupants)
+ return LAZYLEN(occupants)
/obj/vehicle/proc/return_amount_of_controllers_with_flag(flag)
. = 0
@@ -85,12 +85,13 @@
return is_occupant(M) && occupants[M] & VEHICLE_CONTROL_DRIVE
/obj/vehicle/proc/is_occupant(mob/M)
- return !isnull(occupants[M])
+ return !isnull(LAZYACCESS(occupants, M))
/obj/vehicle/proc/add_occupant(mob/M, control_flags)
- if(!istype(M) || occupants[M])
+ if(!istype(M) || is_occupant(M))
return FALSE
- occupants[M] = NONE
+
+ LAZYSET(occupants, M, NONE)
add_control_flags(M, control_flags)
after_add_occupant(M)
grant_passenger_actions(M)
@@ -108,7 +109,7 @@
return FALSE
remove_control_flags(M, ALL)
remove_passenger_actions(M)
- occupants -= M
+ LAZYREMOVE(occupants, M)
cleanup_actions_for_mob(M)
after_remove_occupant(M)
return TRUE
@@ -149,7 +150,7 @@
return
/obj/vehicle/proc/add_control_flags(mob/controller, flags)
- if(!istype(controller) || !flags)
+ if(!is_occupant(controller) || !flags)
return FALSE
occupants[controller] |= flags
for(var/i in GLOB.bitflags)
@@ -158,7 +159,7 @@
return TRUE
/obj/vehicle/proc/remove_control_flags(mob/controller, flags)
- if(!istype(controller) || !flags)
+ if(!is_occupant(controller) || !flags)
return FALSE
occupants[controller] &= ~flags
for(var/i in GLOB.bitflags)
diff --git a/code/modules/vehicles/cars/car.dm b/code/modules/vehicles/cars/car.dm
index 68a332beee868..f7e4b33402b25 100644
--- a/code/modules/vehicles/cars/car.dm
+++ b/code/modules/vehicles/cars/car.dm
@@ -59,7 +59,7 @@
return ..()
/obj/vehicle/sealed/car/mob_try_exit(mob/M, mob/user, silent = FALSE)
- if(M == user && (occupants[M] & VEHICLE_CONTROL_KIDNAPPED))
+ if(M == user && (LAZYACCESS(occupants, M) & VEHICLE_CONTROL_KIDNAPPED))
to_chat(user, "You push against the back of [src] trunk to try and get out.")
if(!do_after(user, escape_time, target = src))
return FALSE
diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm
index 8cd113ee63972..07201cd298103 100644
--- a/code/modules/vehicles/mecha/_mecha.dm
+++ b/code/modules/vehicles/mecha/_mecha.dm
@@ -34,14 +34,15 @@
light_on = FALSE
light_power = 1
light_range = 4
+ generic_canpass = FALSE
///What direction will the mech face when entered/powered on? Defaults to South.
var/dir_in = SOUTH
///How much energy the mech will consume each time it moves. This variable is a backup for when leg actuators affect the energy drain.
var/normal_step_energy_drain = 10
//Beestation stuff
- var/step_multiplier = 1
- var/step_restricted = 0 //applied on_entered() by things which slow or restrict mech movement. Resets to zero at the end of every movement
+ ///applied on_entered() by things which slow or restrict mech movement. Resets to zero at the end of every movement
+ var/step_restricted = 0
///How much energy the mech will consume each time it moves. this is the current active energy consumed
var/step_energy_drain = 10
@@ -178,13 +179,12 @@
hud_possible = list (DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_TRACK_HUD)
/obj/item/radio/mech //this has to go somewhere
+ subspace_transmission = TRUE
/obj/vehicle/sealed/mecha/Initialize(mapload)
. = ..()
- add_radio()
- add_cabin()
if(enclosed)
- add_airtank()
+ internal_tank = new (src)
RegisterSignal(src, COMSIG_MOVABLE_PRE_MOVE , PROC_REF(disconnect_air))
RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(play_stepsound))
@@ -194,6 +194,15 @@
smoke_system.set_up(3, src)
smoke_system.attach(src)
+ radio = new(src)
+ radio.name = "[src] radio"
+
+ cabin_air = new
+ cabin_air.set_temperature(T20C)
+ cabin_air.set_volume(200)
+ cabin_air.set_moles(GAS_O2, O2STANDARD*cabin_air.return_volume()/(R_IDEAL_GAS_EQUATION*cabin_air.return_temperature()))
+ cabin_air.set_moles(GAS_N2, N2STANDARD*cabin_air.return_volume()/(R_IDEAL_GAS_EQUATION*cabin_air.return_temperature()))
+
add_cell()
add_scanmod()
add_capacitor()
@@ -210,12 +219,10 @@
update_appearance()
become_hearing_sensitive(trait_source = ROUNDSTART_TRAIT)
- update_step_speed()
//separate proc so that the ejection mechanism can be easily triggered by other things, such as admins
/obj/vehicle/sealed/mecha/proc/Eject()
- for(var/M in occupants)
- var/mob/living/occupant = M
+ for(var/mob/living/occupant as anything in occupants)
if(isAI(occupant))
occupant.gib() //No wreck, no AI to recover
else
@@ -231,24 +238,30 @@
/obj/vehicle/sealed/mecha/Destroy()
Eject()
if(LAZYLEN(equipment))
- for(var/E in equipment)
- var/obj/item/mecha_parts/mecha_equipment/equip = E
+ for(var/obj/item/mecha_parts/mecha_equipment/equip as anything in equipment)
equip.detach(loc)
qdel(equip)
+
+ STOP_PROCESSING(SSobj, src)
+ LAZYCLEARLIST(equipment)
+
QDEL_NULL(cell)
QDEL_NULL(scanmod)
QDEL_NULL(capacitor)
QDEL_NULL(internal_tank)
- STOP_PROCESSING(SSobj, src)
- LAZYCLEARLIST(equipment)
- assume_air(cabin_air)
- cabin_air = null
+ QDEL_NULL(cabin_air)
QDEL_NULL(spark_system)
QDEL_NULL(smoke_system)
GLOB.mechas_list -= src //global mech list
return ..()
+/obj/vehicle/sealed/mecha/obj_destruction()
+ loc.assume_air(cabin_air)
+ air_update_turf(FALSE, FALSE)
+ Eject()
+ return ..()
+
/obj/vehicle/sealed/mecha/update_icon_state()
if((mecha_flags & SILICON_PILOT) && silicon_icon_state)
icon_state = silicon_icon_state
@@ -259,6 +272,16 @@
icon_state = "[base_icon_state]-open"
return ..()
+/obj/vehicle/sealed/mecha/CanPassThrough(atom/blocker, movement_dir, blocker_opinion)
+ if(!phasing || get_charge() <= phasing_energy_drain || throwing)
+ return ..()
+ if(phase_state)
+ flick(phase_state, src)
+ var/turf/destination_turf = get_step(loc, movement_dir)
+ var/area/destination_area = destination_turf.loc
+ if(destination_area.teleport_restriction >= TELEPORT_ALLOW_NONE)
+ return FALSE
+ return TRUE
/obj/vehicle/sealed/mecha/get_cell()
return cell
@@ -270,7 +293,7 @@
/obj/vehicle/sealed/mecha/proc/restore_equipment()
equipment_disabled = FALSE
for(var/occupant in occupants)
- var/mob/mob_occupant
+ var/mob/mob_occupant = occupant
SEND_SOUND(mob_occupant, sound('sound/items/timer.ogg', volume=50))
to_chat(mob_occupant, "Equipment control unit has been rebooted successfully.")
mob_occupant.update_mouse_pointer()
@@ -299,10 +322,6 @@
////// Helpers /////////
////////////////////////
-/obj/vehicle/sealed/mecha/proc/add_airtank()
- internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src)
- return internal_tank
-
///Adds a cell, for use in Map-spawned mechs, Nuke Ops mechs, and admin-spawned mechs. Mechs built by hand will replace this.
/obj/vehicle/sealed/mecha/proc/add_cell(var/obj/item/stock_parts/cell/C=null)
QDEL_NULL(cell)
@@ -330,27 +349,6 @@
return
capacitor = new /obj/item/stock_parts/capacitor(src)
-/obj/vehicle/sealed/mecha/proc/add_cabin()
- cabin_air = new
- cabin_air.set_temperature(T20C)
- cabin_air.set_volume(200)
- cabin_air.set_moles(GAS_O2, O2STANDARD*cabin_air.return_volume()/(R_IDEAL_GAS_EQUATION*cabin_air.return_temperature()))
- cabin_air.set_moles(GAS_N2, N2STANDARD*cabin_air.return_volume()/(R_IDEAL_GAS_EQUATION*cabin_air.return_temperature()))
- return cabin_air
-
-/obj/vehicle/sealed/mecha/proc/add_radio()
- radio = new(src)
- radio.name = "[src] radio"
- radio.icon = icon
- radio.icon_state = icon_state
- radio.subspace_transmission = TRUE
-
-/obj/vehicle/sealed/mecha/proc/can_use(mob/user)
- if(istype(user) && is_occupant(user))
- if(!user.incapacitated())
- return TRUE
- return FALSE
-
////////////////////////////////////////////////////////////////////////////////
/obj/vehicle/sealed/mecha/examine(mob/user)
@@ -390,41 +388,36 @@
break //in case user is holding two guns
//processing internal damage, temperature, air regulation, alert updates, lights power use.
-/obj/vehicle/sealed/mecha/process()
- var/internal_temp_regulation = 1
-
+/obj/vehicle/sealed/mecha/process(delta_time)
if(internal_damage)
if(internal_damage & MECHA_INT_FIRE)
- if(!(internal_damage & MECHA_INT_TEMP_CONTROL) && prob(5))
- clearInternalDamage(MECHA_INT_FIRE)
+ if(!(internal_damage & MECHA_INT_TEMP_CONTROL) && DT_PROB(2.5, delta_time))
+ clear_internal_damage(MECHA_INT_FIRE)
if(internal_tank)
var/datum/gas_mixture/int_tank_air = internal_tank.return_air()
if(int_tank_air.return_pressure() > internal_tank.maximum_pressure && !(internal_damage & MECHA_INT_TANK_BREACH))
- setInternalDamage(MECHA_INT_TANK_BREACH)
+ set_internal_damage(MECHA_INT_TANK_BREACH)
if(int_tank_air && int_tank_air.return_volume() > 0) //heat the air_contents
- int_tank_air.set_temperature(min(6000+T0C, int_tank_air.return_temperature()+rand(10,15)))
+ int_tank_air.set_temperature(min(6000+T0C, int_tank_air.return_temperature()+rand(5,7.5)*delta_time))
if(cabin_air && cabin_air.return_volume()>0)
- cabin_air.set_temperature(min(6000+T0C, cabin_air.return_temperature()+rand(10,15)))
+ cabin_air.set_temperature(min(6000+T0C, cabin_air.return_temperature()+rand(5,7.5)*delta_time))
if(cabin_air.return_temperature() > max_temperature/2)
- take_damage(4/round(max_temperature/cabin_air.return_temperature(),0.1), BURN, 0, 0)
-
- if(internal_damage & MECHA_INT_TEMP_CONTROL)
- internal_temp_regulation = 0
+ take_damage(delta_time*2/round(max_temperature/cabin_air.return_temperature(),0.1), BURN, 0, 0)
if(internal_damage & MECHA_INT_TANK_BREACH) //remove some air from internal tank
if(internal_tank)
- assume_air_ratio(internal_tank.return_air(), 0.1)
+ assume_air_ratio(internal_tank.return_air(), DT_PROB_RATE(0.05, delta_time))
if(internal_damage & MECHA_INT_SHORT_CIRCUIT)
if(get_charge())
spark_system.start()
- cell.charge -= min(20,cell.charge)
- cell.maxcharge -= min(20,cell.maxcharge)
+ cell.charge -= min(10 * delta_time, cell.charge)
+ cell.maxcharge -= min(10 * delta_time, cell.maxcharge)
- if(internal_temp_regulation)
+ if(!(internal_damage & MECHA_INT_TEMP_CONTROL))
if(cabin_air && cabin_air.return_volume() > 0)
var/delta = cabin_air.return_temperature() - T20C
- cabin_air.set_temperature(cabin_air.return_temperature() - max(-10, min(10, round(delta/4,0.1))))
+ cabin_air.set_temperature(cabin_air.return_temperature() - clamp(round(delta / 8, 0.1), -5, 5) * delta_time)
if(internal_tank)
var/datum/gas_mixture/tank_air = internal_tank.return_air()
@@ -446,56 +439,53 @@
transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION)
cabin_air.transfer_to(t_air, transfer_moles)
- if(occupants)
- for(var/i in occupants)
- var/mob/living/occupant = i
- if(cell)
- var/cellcharge = cell.maxcharge ? cell.charge / cell.maxcharge : 0 //Division by 0 protection
- switch(cellcharge)
- if(0.75 to INFINITY)
- occupant.clear_alert("charge")
- if(0.5 to 0.75)
- occupant.throw_alert("charge", /atom/movable/screen/alert/lowcell, 1)
- if(0.25 to 0.5)
- occupant.throw_alert("charge", /atom/movable/screen/alert/lowcell, 2)
- if(0.01 to 0.25)
- occupant.throw_alert("charge", /atom/movable/screen/alert/lowcell, 3)
- else
- occupant.throw_alert("charge", /atom/movable/screen/alert/emptycell)
-
- var/integrity = obj_integrity/max_integrity*100
- switch(integrity)
- if(30 to 45)
- occupant.throw_alert("mech damage", /atom/movable/screen/alert/low_mech_integrity, 1)
- if(15 to 35)
- occupant.throw_alert("mech damage", /atom/movable/screen/alert/low_mech_integrity, 2)
- if(-INFINITY to 15)
- occupant.throw_alert("mech damage", /atom/movable/screen/alert/low_mech_integrity, 3)
- else
- occupant.clear_alert("mech damage")
- var/atom/checking = occupant.loc
- // recursive check to handle all cases regarding very nested occupants,
- // such as brainmob inside brainitem inside MMI inside mecha
- while (!isnull(checking))
- if (isturf(checking))
- // hit a turf before hitting the mecha, seems like they have
- // been moved out
+
+ for(var/mob/living/occupant as anything in occupants)
+ if(!enclosed && occupant?.incapacitated()) //no sides mean it's easy to just sorta fall out if you're incapacitated.
+ visible_message("[occupant] tumbles out of the cockpit!")
+ mob_exit(occupant) //bye bye
+ continue
+ if(cell)
+ var/cellcharge = cell.maxcharge ? cell.charge / cell.maxcharge : 0 //Division by 0 protection
+ switch(cellcharge)
+ if(0.75 to INFINITY)
occupant.clear_alert("charge")
- occupant.clear_alert("mech damage")
- occupant = null
- break
- else if (checking == src)
- break // all good
- checking = checking.loc
+ if(0.5 to 0.75)
+ occupant.throw_alert("charge", /atom/movable/screen/alert/lowcell, 1)
+ if(0.25 to 0.5)
+ occupant.throw_alert("charge", /atom/movable/screen/alert/lowcell, 2)
+ if(0.01 to 0.25)
+ occupant.throw_alert("charge", /atom/movable/screen/alert/lowcell, 3)
+ else
+ occupant.throw_alert("charge", /atom/movable/screen/alert/emptycell)
+
+ var/integrity = obj_integrity/max_integrity*100
+ switch(integrity)
+ if(30 to 45)
+ occupant.throw_alert("mech damage", /atom/movable/screen/alert/low_mech_integrity, 1)
+ if(15 to 35)
+ occupant.throw_alert("mech damage", /atom/movable/screen/alert/low_mech_integrity, 2)
+ if(-INFINITY to 15)
+ occupant.throw_alert("mech damage", /atom/movable/screen/alert/low_mech_integrity, 3)
+ else
+ occupant.clear_alert("mech damage")
+ var/atom/checking = occupant.loc
+ // recursive check to handle all cases regarding very nested occupants,
+ // such as brainmob inside brainitem inside MMI inside mecha
+ while (!isnull(checking))
+ if (isturf(checking))
+ // hit a turf before hitting the mecha, seems like they have
+ // been moved out
+ occupant.clear_alert("charge")
+ occupant.clear_alert("mech damage")
+ occupant = null
+ break
+ else if (checking == src)
+ break // all good
+ checking = checking.loc
if(mecha_flags & LIGHTS_ON)
- use_power(2)
-
- for(var/b in occupants)
- var/mob/living/occupant = b
- if(!enclosed && occupant?.incapacitated()) //no sides mean it's easy to just sorta fall out if you're incapacitated.
- visible_message("[occupant] tumbles out of the cockpit!")
- mob_exit(occupant) //bye bye
+ use_power(2*delta_time)
//Diagnostic HUD updates
diag_hud_set_mechhealth()
@@ -504,12 +494,12 @@
/obj/vehicle/sealed/mecha/fire_act() //Check if we should ignite the pilot of an open-canopy mech
. = ..()
- if(LAZYLEN(occupants) && !enclosed && !(mecha_flags & SILICON_PILOT))
- for(var/M in occupants)
- var/mob/living/cookedalive = M
- if(cookedalive.fire_stacks < 5)
- cookedalive.fire_stacks += 1
- cookedalive.IgniteMob()
+ if(enclosed || mecha_flags & SILICON_PILOT)
+ return
+ for(var/mob/living/cookedalive as anything in occupants)
+ if(cookedalive.fire_stacks < 5)
+ cookedalive.fire_stacks += 1
+ cookedalive.IgniteMob()
///Displays a special speech bubble when someone inside the mecha speaks
/obj/vehicle/sealed/mecha/proc/display_speech_bubble(datum/source, list/speech_args)
@@ -534,13 +524,12 @@
///// Action processing ////
////////////////////////////
+///Called when a driver clicks somewhere. Handles everything like equipment, punches, etc.
/obj/vehicle/sealed/mecha/proc/on_mouseclick(mob/user, atom/target, params)
SIGNAL_HANDLER
- if(!locate(/turf) in list(target,target.loc)) // Prevents inventory from being drilled
- return
- if(completely_disabled)
+ if(!isturf(target) && !isturf(target.loc)) // Prevents inventory from being drilled
return
- if(is_currently_ejecting)
+ if(completely_disabled || is_currently_ejecting)
return
var/list/mouse_control = params2list(params)
if(isAI(user) == !mouse_control["middle"])//BASICALLY if a human uses MMB, or an AI doesn't, then do nothing.
@@ -562,34 +551,30 @@
return
if(internal_damage & MECHA_INT_CONTROL_LOST)
target = pick(view(3,target))
- if(!target)
- return
-
- var/mob/living/L = user
+ var/mob/living/livinguser = user
if(selected)
if(!Adjacent(target) && (selected.range & MECHA_RANGED))
- if(HAS_TRAIT(L, TRAIT_PACIFISM) && selected.harmful)
- to_chat(L, "You don't want to harm other living beings!")
+ if(HAS_TRAIT(livinguser, TRAIT_PACIFISM) && selected.harmful)
+ to_chat(livinguser, "You don't want to harm other living beings!")
return
INVOKE_ASYNC(selected, TYPE_PROC_REF(/obj/item/mecha_parts/mecha_equipment, action), user, target, params)
return
if((selected.range & MECHA_MELEE) && Adjacent(target))
- if(isliving(target) && selected.harmful && HAS_TRAIT(L, TRAIT_PACIFISM))
- to_chat(L, "You don't want to harm other living beings!")
+ if(isliving(target) && selected.harmful && HAS_TRAIT(livinguser, TRAIT_PACIFISM))
+ to_chat(livinguser, "You don't want to harm other living beings!")
return
INVOKE_ASYNC(selected, TYPE_PROC_REF(/obj/item/mecha_parts/mecha_equipment, action), user, target, params)
return
- if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_MECHA_MELEE_ATTACK) || !istype(target, /atom) || !Adjacent(target))
+ if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_MECHA_MELEE_ATTACK) || !Adjacent(target))
return
if(internal_damage & MECHA_INT_CONTROL_LOST)
- var/list/possible_targets = oview(1,src)
- if(!length(possible_targets))
- return
- target = pick(possible_targets)
- target.mech_melee_attack(src, user)
- TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_MELEE_ATTACK, melee_cooldown)
+ target = pick(oview(1,src))
+ if(force)
+ target.mech_melee_attack(src, user)
+ TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_MELEE_ATTACK, melee_cooldown)
/obj/vehicle/sealed/mecha/proc/on_middlemouseclick(mob/user, atom/target, params)
+ SIGNAL_HANDLER
if(isAI(user))
on_mouseclick(user, target, params)
@@ -597,18 +582,13 @@
//////// Movement procs ////////
//////////////////////////////////
-/obj/vehicle/sealed/mecha/proc/update_step_speed()
- // Calculate the speed delta
- // Calculate the move multiplier speed, to be proportional to mob speed
- // 1.5 was the previous value, so calculate the multiplier in proportion to that
- step_multiplier = CONFIG_GET(number/movedelay/run_delay) / 1.5
-
/obj/vehicle/sealed/mecha/proc/play_stepsound()
SIGNAL_HANDLER
if(mecha_flags & QUIET_STEPS)
return
playsound(src, stepsound, 40, TRUE)
+///Disconnects air tank- air port connection on mecha move
/obj/vehicle/sealed/mecha/proc/disconnect_air()
SIGNAL_HANDLER
if(internal_tank.disconnect()) // Something moved us and broke connection
@@ -715,25 +695,18 @@
//set_glide_size(DELAY_TO_GLIDE_SIZE(movedelay))
//Otherwise just walk normally
. = step(src,direction, dir)
-
+ if(phasing)
+ use_power(phasing_energy_drain)
if(strafe)
setDir(olddir)
/obj/vehicle/sealed/mecha/Bump(atom/obstacle)
- if(phasing && get_charge() >= phasing_energy_drain && !throwing)
- if(phase_state)
- flick(phase_state, src)
- var/turf/target = get_step(src, dir)
- if(target.flags_1 & NOJAUNT_1)
- to_chat(src, "Phasing anomaly detected, emergency deactivation initiated.")
- sleep(movedelay*3*step_multiplier)
- phasing = FALSE
- return
- if(do_teleport(src, get_step(src, dir), no_effects = TRUE))
- use_power(phasing_energy_drain)
- addtimer(VARSET_CALLBACK(src, movedelay, TRUE), movedelay*3*step_multiplier)
. = ..()
+ if(phasing) //Theres only one cause for phasing canpass fails
+ to_chat(occupants, "[icon2html(src, occupants)]A dull, universal force is preventing you from phasing here!")
+ spark_system.start()
+ return
if(.) //mech was thrown/door/whatever
return
if(bumpsmash) //Need a pilot to push the PUNCH button.
@@ -769,20 +742,20 @@
possible_int_damage -= T
var/int_dam_flag = safepick(possible_int_damage)
if(int_dam_flag)
- setInternalDamage(int_dam_flag)
+ set_internal_damage(int_dam_flag)
if(prob(5))
if(ignore_threshold || obj_integrity*100/max_integrity < internal_damage_threshold)
if(LAZYLEN(equipment))
var/obj/item/mecha_parts/mecha_equipment/ME = safepick(equipment)
qdel(ME)
-/obj/vehicle/sealed/mecha/proc/setInternalDamage(int_dam_flag)
+/obj/vehicle/sealed/mecha/proc/set_internal_damage(int_dam_flag)
internal_damage |= int_dam_flag
log_message("Internal damage of type [int_dam_flag].", LOG_MECHA)
SEND_SOUND(occupants, sound('sound/machines/warning-buzzer.ogg',wait=0))
diag_hud_set_mechstat()
-/obj/vehicle/sealed/mecha/proc/clearInternalDamage(int_dam_flag)
+/obj/vehicle/sealed/mecha/proc/clear_internal_damage(int_dam_flag)
if(internal_damage & int_dam_flag)
switch(int_dam_flag)
if(MECHA_INT_TEMP_CONTROL)
@@ -810,20 +783,20 @@
break
//Nothing like a big, red link to make the player feel powerful!
to_chat(user, "ASSUME DIRECT CONTROL?
")
- else
- examine(user)
- if(length(return_drivers()) > 0)
- to_chat(user, "This exosuit has a pilot and cannot be controlled.")
- return
- var/can_control_mech = 0
- for(var/obj/item/mecha_parts/mecha_tracking/ai_control/A in trackers)
- can_control_mech = 1
- to_chat(user, "[icon2html(src, user)] Status of [name]:\n[A.get_mecha_info()].")
- break
- if(!can_control_mech)
- to_chat(user, "You cannot control exosuits without AI control beacons installed.")
- return
- to_chat(user, "Take control of exosuit?
")
+ return
+ examine(user)
+ if(length(return_drivers()) > 0)
+ to_chat(user, "This exosuit has a pilot and cannot be controlled.")
+ return
+ var/can_control_mech = FALSE
+ for(var/obj/item/mecha_parts/mecha_tracking/ai_control/A in trackers)
+ can_control_mech = TRUE
+ to_chat(user, "[icon2html(src, user)] Status of [name]:\n[A.get_mecha_info()]")
+ break
+ if(!can_control_mech)
+ to_chat(user, "You cannot control exosuits without AI control beacons installed.")
+ return
+ to_chat(user, "Take control of exosuit?
")
/obj/vehicle/sealed/mecha/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card)
. = ..()
@@ -870,7 +843,8 @@
if(AI.can_dominate_mechs && LAZYLEN(occupants)) //Oh, I am sorry, were you using that?
to_chat(AI, "Occupants detected! Forced ejection initiated!")
to_chat(occupants, "You have been forcibly ejected!")
- ejectall() //IT IS MINE, NOW. SUCK IT, RD!
+ for(var/ejectee in occupants)
+ mob_exit(ejectee, TRUE, TRUE) //IT IS MINE, NOW. SUCK IT, RD!
AI.can_shunt = FALSE //ONE AI ENTERS. NO AI LEAVES.
if(AI_TRANS_FROM_CARD) //Using an AI card to upload to a mech.
@@ -908,9 +882,9 @@
/obj/vehicle/sealed/mecha/proc/aimob_enter_mech(mob/living/simple_animal/hostile/syndicate/mecha_pilot/pilot_mob)
if(!pilot_mob?.Adjacent(src))
return
- if(occupants)
+ if(LAZYLEN(occupants))
return
- LAZYADD(occupants, src)
+ LAZYSET(occupants, pilot_mob, NONE)
pilot_mob.mecha = src
pilot_mob.forceMove(src)
update_appearance()
@@ -928,39 +902,6 @@
//////// Atmospheric stuff ////////
/////////////////////////////////////
-/obj/vehicle/sealed/mecha/remove_air(amount)
- if(use_internal_tank)
- return cabin_air.remove(amount)
- return ..()
-
-/obj/vehicle/sealed/mecha/remove_air_ratio(ratio)
- if(use_internal_tank)
- return cabin_air.remove_ratio(ratio)
- return ..()
-
-/obj/vehicle/sealed/mecha/return_air()
- if(use_internal_tank)
- return cabin_air
- return ..()
-
-/obj/vehicle/sealed/mecha/return_analyzable_air()
- return cabin_air
-
-/obj/vehicle/sealed/mecha/proc/return_pressure()
- var/datum/gas_mixture/t_air = return_air()
- if(t_air)
- return t_air.return_pressure()
- return
-
-/obj/vehicle/sealed/mecha/return_temperature()
- var/datum/gas_mixture/t_air = return_air()
- if(t_air)
- return t_air.return_temperature()
- return
-
-/obj/vehicle/sealed/mecha/portableConnectorReturnAir()
- return internal_tank.return_air()
-
/obj/vehicle/sealed/mecha/mob_try_enter(mob/M)
if(!ishuman(M)) // no silicons or drones in mechas.
return
@@ -1027,13 +968,12 @@
return FALSE
visible_message("[user] starts to insert an MMI into [name].")
- if(do_after(user, 40, target = src))
- if(LAZYLEN(occupants) < max_occupants)
- return mmi_moved_inside(M, user)
- else
- to_chat(user, "Maximum occupants detected!")
- else
+ if(!do_after(user, 4 SECONDS, target = src))
to_chat(user, "You stop inserting the MMI.")
+ return FALSE
+ if(LAZYLEN(occupants) < max_occupants)
+ return mmi_moved_inside(M, user)
+ to_chat(user, "Maximum occupants exceeded!")
return FALSE
/obj/vehicle/sealed/mecha/proc/mmi_moved_inside(obj/item/mmi/brain_obj, mob/user)
@@ -1076,13 +1016,9 @@
to_chat(user, "You stop exiting the mech. Weapons are enabled again.")
is_currently_ejecting = FALSE
-/obj/vehicle/sealed/mecha/proc/ejectall()
- for(var/ejectee in occupants)
- mob_exit(ejectee, TRUE, TRUE)
-
/obj/vehicle/sealed/mecha/mob_exit(mob/M, silent, forced)
- var/newloc = get_turf(src)
var/atom/movable/mob_container
+ var/turf/newloc = get_turf(src)
if(ishuman(M))
mob_container = M
else if(isbrain(M))
@@ -1097,10 +1033,12 @@
return
else
if(!AI.linked_core)
- to_chat(AI, "Inactive core destroyed. Unable to return.")
+ if(!silent)
+ to_chat(AI, "Inactive core destroyed. Unable to return.")
AI.linked_core = null
return
- to_chat(AI, "Returning to core...")
+ if(!silent)
+ to_chat(AI, "Returning to core...")
AI.controlled_mech = null
AI.remote_control = null
mob_container = AI
@@ -1108,21 +1046,21 @@
qdel(AI.linked_core)
else
return ..()
- var/mob/living/L = M
+ var/mob/living/ejector = M
mecha_flags &= ~SILICON_PILOT
- if(mob_container.forceMove(newloc))//ejecting mob container
- log_message("[mob_container] moved out.", LOG_MECHA)
- L << browse(null, "window=exosuit")
-
- if(istype(mob_container, /obj/item/mmi))
- var/obj/item/mmi/mmi = mob_container
- if(mmi.brainmob)
- L.forceMove(mmi)
- L.reset_perspective()
- remove_occupant(L)
- mmi.set_mecha(null)
- mmi.update_appearance()
- setDir(dir_in)
+ mob_container.forceMove(newloc)//ejecting mob container
+ log_message("[mob_container] moved out.", LOG_MECHA)
+ ejector << browse(null, "window=exosuit")
+
+ if(istype(mob_container, /obj/item/mmi))
+ var/obj/item/mmi/mmi = mob_container
+ if(mmi.brainmob)
+ ejector.forceMove(mmi)
+ ejector.reset_perspective()
+ remove_occupant(ejector)
+ mmi.set_mecha(null)
+ mmi.update_appearance()
+ setDir(dir_in)
return ..()
@@ -1144,7 +1082,7 @@
if(M.client)
M.update_mouse_pointer()
M.client.view_size.resetToDefault()
- zoom_mode = 0
+ zoom_mode = FALSE
. = ..()
update_appearance()
@@ -1188,6 +1126,39 @@
return TRUE
return FALSE
+/obj/vehicle/sealed/mecha/remove_air(amount)
+ if(use_internal_tank)
+ return cabin_air.remove(amount)
+ return ..()
+
+/obj/vehicle/sealed/mecha/remove_air_ratio(ratio)
+ if(use_internal_tank)
+ return cabin_air.remove_ratio(ratio)
+ return ..()
+
+/obj/vehicle/sealed/mecha/return_air()
+ if(use_internal_tank)
+ return cabin_air
+ return ..()
+
+/obj/vehicle/sealed/mecha/return_analyzable_air()
+ return cabin_air
+
+/obj/vehicle/sealed/mecha/proc/return_pressure()
+ var/datum/gas_mixture/t_air = return_air()
+ if(t_air)
+ return t_air.return_pressure()
+ return
+
+/obj/vehicle/sealed/mecha/return_temperature()
+ var/datum/gas_mixture/t_air = return_air()
+ if(t_air)
+ return t_air.return_temperature()
+ return
+
+/obj/vehicle/sealed/mecha/portableConnectorReturnAir()
+ return internal_tank.return_air()
+
/obj/vehicle/sealed/mecha/lighteater_act(obj/item/light_eater/light_eater, atom/parent)
..()
if(mecha_flags & HAS_LIGHTS)
diff --git a/code/modules/vehicles/mecha/combat/durand.dm b/code/modules/vehicles/mecha/combat/durand.dm
index 242a4be15d74c..14e478a16c73d 100644
--- a/code/modules/vehicles/mecha/combat/durand.dm
+++ b/code/modules/vehicles/mecha/combat/durand.dm
@@ -181,7 +181,7 @@ CREATION_TEST_IGNORE_SUBTYPES(/obj/durand_shield)
/obj/durand_shield/proc/activate(datum/source, mob/owner, list/signal_args)
SIGNAL_HANDLER
currentuser = owner
- if(!chassis?.occupants)
+ if(!LAZYLEN(chassis?.occupants))
return
if(switching && !signal_args[1])
return
diff --git a/code/modules/vehicles/mecha/combat/honker.dm b/code/modules/vehicles/mecha/combat/honker.dm
index 69c4dc7a967b2..07ae4e6faaf0b 100644
--- a/code/modules/vehicles/mecha/combat/honker.dm
+++ b/code/modules/vehicles/mecha/combat/honker.dm
@@ -23,7 +23,7 @@
var/tank_pressure = internal_tank ? round(int_tank_air.return_pressure(),0.01) : "None"
var/tank_temperature = internal_tank ? int_tank_air.return_temperature() : "Unknown"
var/cabin_pressure = round(return_pressure(),0.01)
- var/output = {"[report_internal_damage()]
+ return {"[report_internal_damage()]
[integrity<30?"DAMAGE LEVEL CRITICAL
":null]
[internal_damage&MECHA_INT_TEMP_CONTROL?"CLOWN SUPPORT SYSTEM MALFUNCTION
":null]
[internal_damage&MECHA_INT_TANK_BREACH?"GAS TANK HONK
":null]
@@ -37,10 +37,9 @@
HONK temperature: [return_temperature()]°K|[return_temperature() - T0C]°C
Lights: [(mecha_flags & LIGHTS_ON)?"on":"off"]
"}
- return output
/obj/vehicle/sealed/mecha/combat/honker/get_stats_html(mob/user)
- var/output = {"
+ return {"
[src.name] data
@@ -97,7 +96,6 @@
"}
- return output
/obj/vehicle/sealed/mecha/combat/honker/get_commands()
var/output = {"
@@ -180,3 +178,40 @@
if("explosionfar")
playsound(src, 'sound/effects/explosionfar.ogg', 50)
return
+
+//DARK H.O.N.K.
+
+/obj/vehicle/sealed/mecha/combat/honker/dark
+ desc = "Produced by \"Tyranny of Honk, INC\", this exosuit is designed as heavy clown-support. This one has been painted black for maximum fun. HONK!"
+ name = "\improper Dark H.O.N.K"
+ icon_state = "darkhonker"
+ max_integrity = 300
+ deflect_chance = 15
+ armor = list(MELEE = 40, BULLET = 40, LASER = 50, ENERGY = 35, BOMB = 20, BIO = 0, RAD = 0, FIRE = 100, ACID = 100, STAMINA = 0, BLEED = 0)
+ max_temperature = 35000
+ operation_req_access = list(ACCESS_SYNDICATE)
+ internals_req_access = list(ACCESS_SYNDICATE)
+ wreckage = /obj/structure/mecha_wreckage/honker/dark
+ max_equip = 4
+
+/obj/vehicle/sealed/mecha/combat/honker/dark/add_cell(obj/item/stock_parts/cell/C)
+ if(C)
+ C.forceMove(src)
+ cell = C
+ return
+ cell = new /obj/item/stock_parts/cell/hyper(src)
+
+/obj/vehicle/sealed/mecha/combat/honker/dark/loaded/Initialize(mapload)
+ . = ..()
+ var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/thrusters/ion(src)
+ ME.attach(src)
+ ME = new /obj/item/mecha_parts/mecha_equipment/weapon/honker()
+ ME.attach(src)
+ ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/banana_mortar/bombanana()//Needed more offensive weapons.
+ ME.attach(src)
+ ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang/tearstache()//The mousetrap mortar was not up-to-snuff.
+ ME.attach(src)
+
+/obj/structure/mecha_wreckage/honker/dark
+ name = "\improper Dark H.O.N.K wreckage"
+ icon_state = "darkhonker-broken"
diff --git a/code/modules/vehicles/mecha/equipment/mecha_equipment.dm b/code/modules/vehicles/mecha/equipment/mecha_equipment.dm
index 30f1bc54abf0f..35ef34f9331a2 100644
--- a/code/modules/vehicles/mecha/equipment/mecha_equipment.dm
+++ b/code/modules/vehicles/mecha/equipment/mecha_equipment.dm
@@ -1,6 +1,7 @@
-//DO NOT ADD MECHA PARTS TO THE GAME WITH THE DEFAULT "SPRITE ME" SPRITE!
-//I'm annoyed I even have to tell you this! SPRITE FIRST, then commit.
-
+/**
+ * Mecha Equipment
+ * All mech equippables are currently childs of this
+ */
/obj/item/mecha_parts/mecha_equipment
name = "mecha equipment"
icon = 'icons/mecha/mecha_equipment.dmi'
@@ -8,32 +9,40 @@
force = 5
max_integrity = 300
item_flags = ISWEAPON
+ ///Cooldown in ticks required between activations of the equipment
var/equip_cooldown = 0
- var/equip_ready = TRUE //whether the equipment is ready for use. (or deactivated/activated for static stuff)
+ ///used for equipment that can be turned on/off, boolean
+ var/activated = TRUE
+ ///Chassis power cell quantity used on activation
var/energy_drain = 0
- var/obj/vehicle/sealed/mecha/chassis = null
+ ///Reference to mecha that this equipment is currently attached to
+ var/obj/vehicle/sealed/mecha/chassis
///Bitflag. Determines the range of the equipment.
var/range = MECHA_MELEE
/// Bitflag. Used by exosuit fabricator to assign sub-categories based on which exosuits can equip this.
var/mech_flags = NONE
- var/salvageable = 1
- var/detachable = TRUE // Set to FALSE for built-in equipment that cannot be removed
- var/selectable = 1 // Set to 0 for passive equipment such as mining scanner or armor plates
- var/harmful = FALSE //Controls if equipment can be used to attack by a pacifist.
+ ///boolean: FALSE if this equipment can not be removed/salvaged
+ var/detachable = TRUE
+ ///Boolean: whether we can equip this equipment through the mech UI or the cycling ability
+ var/selectable = TRUE
+ ///Boolean: whether a pacifist can use this equipment
+ var/harmful = FALSE
+ ///Sound file: Sound to play when this equipment is destroyed while still attached to the mech
var/destroy_sound = 'sound/mecha/critdestr.ogg'
+///Updates chassis equipment list html menu
/obj/item/mecha_parts/mecha_equipment/proc/update_chassis_page()
- if(chassis)
- send_byjax(chassis.occupants,"exosuit.browser","eq_list",chassis.get_equipment_list())
- send_byjax(chassis.occupants,"exosuit.browser","equipment_menu",chassis.get_equipment_menu(),"dropdowns")
- return TRUE
- return
+ SHOULD_CALL_PARENT(TRUE)
+ send_byjax(chassis.occupants,"exosuit.browser","eq_list", chassis.get_equipment_list())
+ send_byjax(chassis.occupants,"exosuit.browser","equipment_menu", chassis.get_equipment_menu(),"dropdowns")
+ return TRUE
+///Updates chassis equipment list html menu with custom data
/obj/item/mecha_parts/mecha_equipment/proc/update_equip_info()
- if(chassis)
- send_byjax(chassis.occupants,"exosuit.browser","[REF(src)]",get_equip_info())
- return TRUE
- return
+ if(!chassis)
+ return
+ send_byjax(chassis.occupants,"exosuit.browser","[REF(src)]",get_equip_info())
+ return TRUE
/obj/item/mecha_parts/mecha_equipment/Destroy()
if(chassis)
@@ -42,43 +51,50 @@
chassis.selected = null
update_chassis_page()
log_message("[src] is destroyed.", LOG_MECHA)
- if(chassis.occupants)
+ if(LAZYLEN(chassis.occupants))
to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)]
[src] is destroyed!")
playsound(chassis, destroy_sound, 50)
chassis = null
return ..()
-/obj/item/mecha_parts/mecha_equipment/try_attach_part(mob/user, obj/vehicle/sealed/mecha/M)
- if(!do_after(user, 15, M))
+/obj/item/mecha_parts/mecha_equipment/try_attach_part(mob/user, obj/vehicle/sealed/mecha/mech)
+ if(!do_after(user, 15, mech))
return FALSE
- if(!can_attach(M))
- to_chat(user, "
You are unable to attach [src] to [M]!")
+ if(!can_attach(mech))
+ to_chat(user, "
You are unable to attach [src] to [mech]!")
return FALSE
if(!user.temporarilyRemoveItemFromInventory(src))
return FALSE
- attach(M)
- user.visible_message("[user] attaches [src] to [M].", "
You attach [src] to [M].")
+ attach(mech)
+ user.visible_message("[user] attaches [src] to [mech].", "
You attach [src] to [mech].")
return TRUE
+///fetches and returns a html formatted string with equippability status
/obj/item/mecha_parts/mecha_equipment/proc/get_equip_info()
if(!chassis)
return
- var/txt = "
* "
+ var/txt = "
* "
if(chassis.selected == src)
- txt += "
[src.name]"
+ txt += "
[src]"
else if(selectable)
- txt += "
[src.name]"
+ txt += "
[src]"
else
- txt += "[src.name]"
+ txt += "[src]"
return txt
+/**
+ * Checks whether this mecha equipment can be activated
+ * Returns a bool
+ * Arguments:
+ * * target: atom we are activating/clicked on
+ */
/obj/item/mecha_parts/mecha_equipment/proc/action_checks(atom/target)
if(!target)
return FALSE
if(!chassis)
return FALSE
- if(!equip_ready)
+ if(!activated)
return FALSE
if(energy_drain && !chassis.has_charge(energy_drain))
return FALSE
@@ -93,68 +109,78 @@
/obj/item/mecha_parts/mecha_equipment/proc/action(mob/source, atom/target, params)
TIMER_COOLDOWN_START(chassis, COOLDOWN_MECHA_EQUIPMENT, equip_cooldown)//Cooldown is on the MECH so people dont bypass it by switching equipment
- send_byjax(chassis.occupants,"exosuit.browser","[REF(src)]",src.get_equip_info())
+ send_byjax(chassis.occupants,"exosuit.browser","[REF(src)]", get_equip_info())
chassis.use_power(energy_drain)
return TRUE
+/**
+ * Cooldown proc variant for using do_afters between activations instead of timers
+ * Example of usage is mech drills, rcds
+ * arguments:
+ * * target: targetted atom for action activation
+ * * user: occupant to display do after for
+ * * interaction_key: interaction key to pass to [/proc/do_after]
+ */
/obj/item/mecha_parts/mecha_equipment/proc/do_after_cooldown(atom/target, mob/user)
if(!chassis)
return
- var/C = chassis.loc
chassis.use_power(energy_drain)
. = do_after(user, equip_cooldown, target=target)
- if(!chassis || chassis.loc != C || src != chassis.selected || !(get_dir(chassis, target)&chassis.dir))
+ if(!chassis || src != chassis.selected || !(get_dir(chassis, target) & chassis.dir))
return FALSE
+///Do after wrapper for mecha equipment
/obj/item/mecha_parts/mecha_equipment/proc/do_after_mecha(atom/target, mob/user, delay)
if(!chassis)
return
- var/C = chassis.loc
. = do_after(user, delay, target=target)
- if(!chassis || chassis.loc != C || src != chassis.selected || !(get_dir(chassis, target)&chassis.dir))
+ if(!chassis || src != chassis.selected || !(get_dir(chassis, target) & chassis.dir))
return FALSE
+///Returns TRUE if equipment should be allowed to attach to the targetted necha
/obj/item/mecha_parts/mecha_equipment/proc/can_attach(obj/vehicle/sealed/mecha/M)
- if(LAZYLEN(M.equipment)
You start putting [target] into [src]...")
chassis.visible_message("[chassis] starts putting [target] into \the [src].")
- if(do_after(source, equip_cooldown, target=target))
- if(!chassis || src != chassis.selected || !(get_dir(chassis, target)&chassis.dir))
- return
- if(!patient_insertion_check(target, source))
- return
- target.forceMove(src)
- patient = target
- START_PROCESSING(SSobj, src)
- update_equip_info()
- to_chat(source, "[icon2html(src, source)][target] successfully loaded into [src]. Life support functions engaged.")
- chassis.visible_message("[chassis] loads [target] into [src].")
- log_message("[target] loaded. Life support functions engaged.", LOG_MECHA)
- return ..()
+ if(!do_after(source, equip_cooldown, target=target))
+ return
+ if(!chassis || src != chassis.selected || !(get_dir(chassis, target)&chassis.dir))
+ return
+ if(!patient_insertion_check(target, source))
+ return
+ target.forceMove(src)
+ patient = target
+ START_PROCESSING(SSobj, src)
+ update_equip_info()
+ to_chat(source, "[icon2html(src, source)][target] successfully loaded into [src]. Life support functions engaged.")
+ chassis.visible_message("[chassis] loads [target] into [src].")
+ log_message("[target] loaded. Life support functions engaged.", LOG_MECHA)
+ return ..()
/obj/item/mecha_parts/mecha_equipment/medical/sleeper/proc/patient_insertion_check(mob/living/carbon/target, mob/user)
if(target.buckled)
to_chat(user, "[icon2html(src, user)][target] will not fit into the sleeper because [target.p_theyre()] buckled to [target.buckled]!")
- return
+ return FALSE
if(target.has_buckled_mobs())
to_chat(user, "[icon2html(src, user)][target] will not fit into the sleeper because of the creatures attached to it!")
- return
+ return FALSE
if(patient)
to_chat(user, "[icon2html(src, user)]The sleeper is already occupied!")
- return
+ return FALSE
return TRUE
/obj/item/mecha_parts/mecha_equipment/medical/sleeper/proc/go_out()
@@ -214,37 +215,36 @@
update_equip_info()
/obj/item/mecha_parts/mecha_equipment/medical/sleeper/update_equip_info()
- if(..())
- if(patient)
- send_byjax(chassis.occupants,"msleeper.browser","lossinfo",get_patient_dam())
- send_byjax(chassis.occupants,"msleeper.browser","reagents",get_patient_reagents())
- send_byjax(chassis.occupants,"msleeper.browser","injectwith",get_available_reagents())
- return 1
- return
+ . = ..()
+ if(. && patient)
+ send_byjax(chassis.occupants,"msleeper.browser","lossinfo",get_patient_dam())
+ send_byjax(chassis.occupants,"msleeper.browser","reagents",get_patient_reagents())
+ send_byjax(chassis.occupants,"msleeper.browser","injectwith",get_available_reagents())
/obj/item/mecha_parts/mecha_equipment/medical/sleeper/container_resist(mob/living/user)
go_out()
-/obj/item/mecha_parts/mecha_equipment/medical/sleeper/process()
- if(..())
+/obj/item/mecha_parts/mecha_equipment/medical/sleeper/process(delta_time)
+ . = ..()
+ if(.)
return
if(!chassis.has_charge(energy_drain))
log_message("Deactivated.", LOG_MECHA)
to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)][src] deactivated - no power.")
STOP_PROCESSING(SSobj, src)
return
- var/mob/living/carbon/M = patient
- if(!M)
+ var/mob/living/carbon/ex_patient = patient
+ if(!ex_patient)
return
- if(M.health > 0)
- M.adjustOxyLoss(-1)
- M.AdjustStun(-80)
- M.AdjustKnockdown(-80)
- M.AdjustParalyzed(-80)
- M.AdjustImmobilized(-80)
- M.AdjustUnconscious(-80)
- if(M.reagents.get_reagent_amount(/datum/reagent/medicine/epinephrine) < 5)
- M.reagents.add_reagent(/datum/reagent/medicine/epinephrine, 5)
+ if(ex_patient.health > 0)
+ ex_patient.adjustOxyLoss(-0.5 * delta_time)
+ ex_patient.AdjustStun(-40 * delta_time)
+ ex_patient.AdjustKnockdown(-40 * delta_time)
+ ex_patient.AdjustParalyzed(-40 * delta_time)
+ ex_patient.AdjustImmobilized(-40 * delta_time)
+ ex_patient.AdjustUnconscious(-40 * delta_time)
+ if(ex_patient.reagents.get_reagent_amount(/datum/reagent/medicine/epinephrine) < 5)
+ ex_patient.reagents.add_reagent(/datum/reagent/medicine/epinephrine, 5)
chassis.use_power(energy_drain)
update_equip_info()
@@ -253,29 +253,36 @@
///////////////////////////////// Syringe Gun ///////////////////////////////////////////////////////////////
+#define FIRE_SYRINGE_MODE 0
+#define ANALYZE_SYRINGE_MODE 1
/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun
name = "exosuit syringe gun"
desc = "Equipment for medical exosuits. A chem synthesizer with syringe gun. Reagents inside are held in stasis, so no reactions will occur."
icon = 'icons/obj/guns/projectile.dmi'
icon_state = "syringegun"
+ range = MECHA_MELEE|MECHA_RANGED
+ equip_cooldown = 10
+ energy_drain = 10
+ ///Lazylist of syringes that we've picked up
var/list/syringes
+ ///List of all scanned reagents, starts with epinephrine and multiver
var/list/known_reagents
+ ///List of reagents we want to be creating this processing tick
var/list/processed_reagents
+ ///Maximu amount of syringes we can hold
var/max_syringes = 10
- var/max_volume = 75 //max reagent volume
- var/synth_speed = 5 //[num] reagent units per cycle
- energy_drain = 10
- var/mode = 0 //0 - fire syringe, 1 - analyze reagents.
- range = MECHA_MELEE|MECHA_RANGED
- equip_cooldown = 10
+ ///Maximum volume of reagents we can hold
+ var/max_volume = 75
+ ///Reagent amount in units we produce per two seconds
+ var/synth_speed = 5
+ ///Chooses what kind of action we should perform when clicking
+ var/mode = FIRE_SYRINGE_MODE // fire syringe or analyze reagents.
/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/Initialize(mapload)
. = ..()
create_reagents(max_volume, NO_REACT)
- syringes = new
known_reagents = list(/datum/reagent/medicine/epinephrine="Epinephrine",/datum/reagent/medicine/charcoal="Charcoal")
- processed_reagents = new
/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/detach()
STOP_PROCESSING(SSobj, src)
@@ -286,18 +293,16 @@
return ..()
/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/can_attach(obj/vehicle/sealed/mecha/medical/M)
- if(..())
- if(istype(M))
- return 1
- return 0
+ . = ..()
+ if(!istype(M))
+ return FALSE
/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/get_equip_info()
- var/output = ..()
+ var/output = ..()// no . = here to avoid obfuscation
if(output)
- return "[output] \[[mode? "Analyze" : "Launch"]\]
\[Syringes: [syringes.len]/[max_syringes] | Reagents: [reagents.total_volume]/[reagents.maximum_volume]\]
Reagents list"
- return
+ return "[output] \[[mode? "Analyze" : "Launch"]\]
\[Syringes: [LAZYLEN(syringes)]/[max_syringes] | Reagents: [reagents.total_volume]/[reagents.maximum_volume]\]
Reagents list"
-/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/action(mob/source, atom/movable/target, params)
+/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/action(mob/source, atom/target, params)
if(!action_checks(target))
return
if(istype(target, /obj/item/reagent_containers/syringe))
@@ -306,63 +311,23 @@
for(var/obj/item/reagent_containers/syringe/S in target.contents)
load_syringe(S, source)
return
- if(mode)
+ if(mode == ANALYZE_SYRINGE_MODE)
return analyze_reagents(target, source)
- if(!syringes.len)
+ //we're in syringe mode so lets do syringe stuff
+ if(!LAZYLEN(syringes))
to_chat(source, "[icon2html(src, source)]No syringes loaded.")
return
if(reagents.total_volume<=0)
to_chat(source, "[icon2html(src, source)]No available reagents to load syringe with.")
return
if(HAS_TRAIT(source, TRAIT_PACIFISM))
- to_chat(source, "The [src] is lethally chambered! You don't want to risk harming anyone...")
+ to_chat(source, "The [src] might be lethally chambered! You don't want to risk harming anyone...")
return
- var/turf/trg = get_turf(target)
- var/obj/item/reagent_containers/syringe/mechsyringe = syringes[1]
- mechsyringe.forceMove(get_turf(chassis))
- reagents.trans_to(mechsyringe, min(mechsyringe.volume, reagents.total_volume), transfered_by = source)
- syringes -= mechsyringe
- mechsyringe.icon = 'icons/obj/chemical.dmi'
- mechsyringe.icon_state = "syringeproj"
- playsound(chassis, 'sound/items/syringeproj.ogg', 50, 1)
- log_message("Launched [mechsyringe] from [src] by [source], targeting [target].", LOG_MECHA)
- spawn(0) //This code is trash and whoever wrote it should feel bad
- for(var/i=0, i<6, i++)
- if(!mechsyringe)
- break
- if(step_towards(mechsyringe,trg))
- var/list/mobs = new
- for(var/mob/living/carbon/M in mechsyringe.loc)
- mobs += M
- var/mob/living/carbon/M = safepick(mobs)
- if(M)
- var/R
- mechsyringe.visible_message(" [M] was hit by the syringe!")
- if(M.can_inject(null, 1))
- if(mechsyringe.reagents)
- for(var/datum/reagent/A in mechsyringe.reagents.reagent_list)
- R += "[A.name] ([num2text(A.volume)]"
- mechsyringe.icon_state = initial(mechsyringe.icon_state)
- mechsyringe.icon = initial(mechsyringe.icon)
- mechsyringe.reagents.reaction(M, INJECT)
- mechsyringe.reagents.trans_to(M, mechsyringe.reagents.total_volume, transfered_by = source)
- M.take_bodypart_damage(2)
- log_combat(source, M, "shot", "syringegun")
- break
- else if(mechsyringe.loc == trg)
- mechsyringe.icon_state = initial(mechsyringe.icon_state)
- mechsyringe.icon = initial(mechsyringe.icon)
- mechsyringe.update_icon()
- break
- else
- mechsyringe.icon_state = initial(mechsyringe.icon_state)
- mechsyringe.icon = initial(mechsyringe.icon)
- mechsyringe.update_icon()
- break
- sleep(1)
+ var/obj/item/ammo_casing/syringegun/chambered = new /obj/item/ammo_casing/syringegun(src)
+ log_message("Fired [chambered] from [src] by [source], targeting [target].", LOG_MECHA)
+ chambered.fire_casing(target, source, null, 0, 0, null, 0, src)
return ..()
-
/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/Topic(href,href_list)
..()
if (href_list["toggle_mode"])
@@ -370,18 +335,18 @@
update_equip_info()
return
if (href_list["select_reagents"])
- processed_reagents.len = 0
- var/m = 0
+ LAZYCLEARLIST(processed_reagents)
+ var/processingreagentamount = 0
var/message
for(var/i=1 to known_reagents.len)
- if(m>=synth_speed)
+ if(processingreagentamount >= synth_speed)
break
var/reagent = text2path(href_list["reagent_[i]"])
if(reagent && (reagent in known_reagents))
- message = "[m ? ", " : null][known_reagents[reagent]]"
- processed_reagents += reagent
- m++
- if(processed_reagents.len)
+ message = "[processingreagentamount ? ", " : null][known_reagents[reagent]]"
+ LAZYADD(processed_reagents, reagent)
+ processingreagentamount++
+ if(LAZYLEN(processed_reagents))
message += " added to production"
START_PROCESSING(SSobj, src)
to_chat(usr, message)
@@ -392,10 +357,12 @@
if(!(usr in chassis.occupants))
return
usr << browse(get_reagents_page(),"window=msyringegun")
+ return
if (href_list["purge_reagent"])
var/reagent = href_list["purge_reagent"]
- if(reagent)
- reagents.del_reagent(reagent)
+ if(!reagent)
+ return
+ reagents.del_reagent(reagent)
return
if (href_list["purge_all"])
reagents.clear_reagents()
@@ -463,18 +430,18 @@
return output || "None"
/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/proc/load_syringe(obj/item/reagent_containers/syringe/S, mob/user)
- if(syringes.lenUnable to load syringe!")
- return FALSE
- S.reagents.trans_to(src, S.reagents.total_volume, transfered_by = user)
- S.forceMove(src)
- syringes += S
- to_chat(user, "[icon2html(src, user)]Syringe loaded.")
- update_equip_info()
- return 1
- to_chat(user, "[icon2html(src, user)][src]'s syringe chamber is full!")
- return 0
+ if(LAZYLEN(syringes) >= max_syringes)
+ to_chat(user, "[icon2html(src, user)][src]'s syringe chamber is full!")
+ return FALSE
+ if(!chassis.Adjacent(S))
+ to_chat(user, "[icon2html(src, user)]Unable to load syringe!")
+ return FALSE
+ S.reagents.trans_to(src, S.reagents.total_volume, transfered_by = user)
+ S.forceMove(src)
+ LAZYADD(syringes,S)
+ to_chat(user, "[icon2html(src, user)]Syringe loaded.")
+ update_equip_info()
+ return TRUE
/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/proc/analyze_reagents(atom/A, mob/user)
if(get_dist(src,A) >= 4)
@@ -492,38 +459,40 @@
return TRUE
/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/proc/add_known_reagent(r_id,r_name)
- if(!(r_id in known_reagents))
- known_reagents += r_id
- known_reagents[r_id] = r_name
- return TRUE
- return FALSE
+ if(r_id in known_reagents)
+ return FALSE
+ known_reagents += r_id
+ known_reagents[r_id] = r_name
+ return TRUE
/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/update_equip_info()
- if(..())
+ . = ..()
+ if(.)
send_byjax(chassis.occupants,"msyringegun.browser","reagents",get_current_reagents())
send_byjax(chassis.occupants,"msyringegun.browser","reagents_form",get_reagents_form())
- return 1
- return
/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/on_reagent_change(changetype)
- ..()
+ . = ..()
update_equip_info()
return
-/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/process()
- if(..())
+/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/process(delta_time)
+ . = ..()
+ if(.)
return
- if(!processed_reagents.len || reagents.total_volume >= reagents.maximum_volume || !chassis.has_charge(energy_drain))
+ if(!LAZYLEN(processed_reagents) || reagents.total_volume >= reagents.maximum_volume || !chassis.has_charge(energy_drain))
to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)]Reagent processing stopped.")
log_message("Reagent processing stopped.", LOG_MECHA)
- STOP_PROCESSING(SSobj, src)
- return
- var/amount = synth_speed / processed_reagents.len
+ return PROCESS_KILL
+ var/amount = delta_time * synth_speed / LAZYLEN(processed_reagents)
for(var/reagent in processed_reagents)
reagents.add_reagent(reagent,amount)
chassis.use_power(energy_drain)
+#undef FIRE_SYRINGE_MODE
+#undef ANALYZE_SYRINGE_MODE
+
///////////////////////////////// Medical Beam ///////////////////////////////////////////////////////////////
/obj/item/mecha_parts/mecha_equipment/medical/mechmedbeam
@@ -533,6 +502,7 @@
energy_drain = 10
range = MECHA_MELEE|MECHA_RANGED
equip_cooldown = 0
+ ///The medical gun doing the actual healing. yes its wierd but its better than copypasting the entire thing
var/obj/item/gun/medbeam/mech/medigun
custom_materials = list(/datum/material/iron = 15000, /datum/material/glass = 8000, /datum/material/plasma = 3000, /datum/material/gold = 8000, /datum/material/diamond = 2000)
@@ -542,18 +512,18 @@
/obj/item/mecha_parts/mecha_equipment/medical/mechmedbeam/Destroy()
- qdel(medigun)
+ QDEL_NULL(medigun)
return ..()
-/obj/item/mecha_parts/mecha_equipment/medical/mechmedbeam/process()
- if(..())
+/obj/item/mecha_parts/mecha_equipment/medical/mechmedbeam/process(deltatime)
+ . = ..()
+ if(.)
return
- medigun.process()
+ medigun.process(SSOBJ_DT)
/obj/item/mecha_parts/mecha_equipment/medical/mechmedbeam/action(mob/source, atom/movable/target, params)
medigun.process_fire(target, loc)
-
/obj/item/mecha_parts/mecha_equipment/medical/mechmedbeam/detach()
STOP_PROCESSING(SSobj, src)
medigun.LoseTarget()
diff --git a/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm b/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm
index f5075feea0cd2..9434cdedbe7ce 100644
--- a/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm
+++ b/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm
@@ -176,7 +176,7 @@
qdel(src)
if(istype(loc, /obj/vehicle/sealed/mecha/working) && scanning_time <= world.time)
var/obj/vehicle/sealed/mecha/working/mecha = loc
- if(!mecha.occupants)
+ if(!LAZYLEN(mecha.occupants))
return
scanning_time = world.time + equip_cooldown
mineral_scan_pulse(get_turf(src))
diff --git a/code/modules/vehicles/mecha/equipment/tools/other_tools.dm b/code/modules/vehicles/mecha/equipment/tools/other_tools.dm
index 89c3f16316c46..b399a8220b70c 100644
--- a/code/modules/vehicles/mecha/equipment/tools/other_tools.dm
+++ b/code/modules/vehicles/mecha/equipment/tools/other_tools.dm
@@ -13,7 +13,8 @@
range = MECHA_RANGED
/obj/item/mecha_parts/mecha_equipment/teleporter/action(mob/source, atom/target, params)
- if(!action_checks(target) || is_centcom_level(loc.z))
+ var/area/ourarea = get_area(src)
+ if(!action_checks(target) || ourarea & TELEPORT_ALLOW_NONE)
return
var/turf/T = get_turf(target)
if(T)
@@ -26,7 +27,7 @@
/obj/item/mecha_parts/mecha_equipment/wormhole_generator
name = "mounted wormhole generator"
- desc = "An exosuit module that allows generating of small quasi-stable wormholes."
+ desc = "An exosuit module that allows generating of small quasi-stable wormholes, allowing for long-range innaccurate teleportation."
icon_state = "mecha_wholegen"
equip_cooldown = 50
energy_drain = 300
@@ -34,39 +35,38 @@
/obj/item/mecha_parts/mecha_equipment/wormhole_generator/action(mob/source, atom/target, params)
- if(!action_checks(target) || is_centcom_level(loc.z))
+ var/area/ourarea = get_area(src)
+ if(!action_checks(target) || (ourarea & TELEPORT_ALLOW_NONE))
return
- var/list/theareas = get_areas_in_range(100, chassis)
- if(!theareas.len)
+ var/area/targetarea = pick(get_areas_in_range(100, chassis))
+ if(!targetarea)//Literally middle of nowhere how did you even get here
return
- var/area/thearea = pick(theareas)
- var/list/L = list()
- var/turf/pos = get_turf(src)
- for(var/turf/T in get_area_turfs(thearea.type))
- if(!T.density && pos.get_virtual_z_level() == T.get_virtual_z_level())
- var/clear = 1
- for(var/obj/O in T)
- if(O.density)
- clear = 0
- break
- if(clear)
- L+=T
- if(!L.len)
- return
- var/turf/target_turf = pick(L)
+ var/list/validturfs = list()
+ var/turf/ourturf = get_turf(src)
+ for(var/t in get_area_turfs(targetarea.type))
+ var/turf/evaluated_turf = t
+ if(evaluated_turf.density || chassis.get_virtual_z_level() != evaluated_turf.get_virtual_z_level())
+ continue
+ for(var/obj/evaluated_obj in evaluated_turf)
+ if(!evaluated_obj.density)
+ continue
+ validturfs += evaluated_turf
+
+ var/turf/target_turf = pick(validturfs)
if(!target_turf)
return
- var/list/obj/effect/portal/created = create_portal_pair(get_turf(src), target_turf, src, 300, 1, /obj/effect/portal/anom)
- var/turf/T = get_turf(target)
- message_admins("[ADMIN_LOOKUPFLW(source)] used a Wormhole Generator in [ADMIN_VERBOSEJMP(T)]")
- log_game("[key_name(source)] used a Wormhole Generator in [AREACOORD(T)]")
- src = null
+ var/list/obj/effect/portal/created = create_portal_pair(ourturf, target_turf, 300, 1, /obj/effect/portal/anom)
+ message_admins("[ADMIN_LOOKUPFLW(source)] used a Wormhole Generator in [ADMIN_VERBOSEJMP(ourturf)]")
+ log_game("[key_name(source)] used a Wormhole Generator in [AREACOORD(ourturf)]")
QDEL_LIST_IN(created, rand(150,300))
return ..()
/////////////////////////////////////// GRAVITATIONAL CATAPULT ///////////////////////////////////////////
+#define GRAVSLING_MODE 1
+#define GRAVPUSH_MODE 2
+
/obj/item/mecha_parts/mecha_equipment/gravcatapult
name = "mounted gravitational catapult"
desc = "An exosuit mounted Gravitational Catapult."
@@ -74,73 +74,77 @@
equip_cooldown = 10
energy_drain = 100
range = MECHA_MELEE|MECHA_RANGED
- var/atom/movable/locked
- var/mode = 1 //1 - gravsling 2 - gravpush
+ ///Which atom we are movable_target onto for
+ var/atom/movable/movable_target
+ ///Whether we will throw movable atomstothrow by locking onto them or just throw them back from where we click
+ var/mode = GRAVSLING_MODE
/obj/item/mecha_parts/mecha_equipment/gravcatapult/action(mob/source, atom/movable/target, params)
if(!action_checks(target))
return
switch(mode)
- if(1)
- if(!locked)
+ if(GRAVSLING_MODE)
+ if(!movable_target)
if(!istype(target) || target.anchored || target.move_resist >= MOVE_FORCE_EXTREMELY_STRONG)
to_chat(source, "[icon2html(src, source)]Unable to lock on [target]!")
return
if(ismob(target))
var/mob/M = target
if(M.mob_negates_gravity())
- to_chat(source, "[icon2html(src, source)]Unable to lock on [target]!")
+ to_chat(source, "[icon2html(src, source)][target] immune to gravitational impulses, unable to lock!")
return
- locked = target
- to_chat(source, "[icon2html(src, source)]Locked on [target].")
- send_byjax(source,"exosuit.browser","[REF(src)]",src.get_equip_info())
- else if(target!=locked)
- if(locked in view(chassis))
+ movable_target = target
+ to_chat(source, "[icon2html(src, source)]locked on [target].")
+ send_byjax(source,"exosuit.browser","[REF(src)]", get_equip_info())
+ else if(target!=movable_target)
+ if(movable_target in view(chassis))
var/turf/targ = get_turf(target)
- var/turf/orig = get_turf(locked)
- locked.throw_at(target, 14, 1.5)
- locked = null
- send_byjax(source,"exosuit.browser","[REF(src)]",src.get_equip_info())
- log_game("[key_name(source)] used a Gravitational Catapult to throw [locked] (From [AREACOORD(orig)]) at [target] ([AREACOORD(targ)]).")
+ var/turf/orig = get_turf(movable_target)
+ movable_target.throw_at(target, 14, 1.5)
+ movable_target = null
+ send_byjax(source,"exosuit.browser","[REF(src)]", get_equip_info())
+ log_game("[key_name(source)] used a Gravitational Catapult to throw [movable_target] (From [AREACOORD(orig)]) at [target] ([AREACOORD(targ)]).")
return ..()
- else
- locked = null
- to_chat(source, "[icon2html(src, source)]Lock on [locked] disengaged.")
- send_byjax(source,"exosuit.browser","[REF(src)]",src.get_equip_info())
- if(2)
- var/list/atoms = list()
+ movable_target = null
+ to_chat(source, "[icon2html(src, source)]Lock on [movable_target] disengaged.")
+ send_byjax(source,"exosuit.browser","[REF(src)]", get_equip_info())
+
+ if(GRAVPUSH_MODE)
+ var/list/atomstothrow = list()
if(isturf(target))
- atoms = range(3, target)
+ atomstothrow = range(3, target)
else
- atoms = orange(3, target)
- for(var/atom/movable/A in atoms)
- if(A.anchored || A.move_resist >= MOVE_FORCE_EXTREMELY_STRONG)
+ atomstothrow = orange(3, target)
+ for(var/atom/movable/scatter in atomstothrow)
+ if(scatter.anchored || scatter.move_resist >= MOVE_FORCE_EXTREMELY_STRONG)
continue
- if(ismob(A))
- var/mob/M = A
- if(M.mob_negates_gravity())
+ if(ismob(scatter))
+ var/mob/scatter_mob = scatter
+ if(scatter_mob.mob_negates_gravity())
continue
- var/dist = 5 - get_dist(A, target)
- var/delay = 2
- SSmove_manager.move_away(A, target, delay = delay, timeout = delay * dist, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
- var/turf/T = get_turf(target)
- log_game("[key_name(source)] used a Gravitational Catapult repulse wave on [AREACOORD(T)]")
+ do_scatter(scatter, target)
+ var/turf/targetturf = get_turf(target)
+ log_game("[key_name(source)] used a Gravitational Catapult repulse wave on [AREACOORD(targetturf)]")
return ..()
+/obj/item/mecha_parts/mecha_equipment/gravcatapult/proc/do_scatter(atom/movable/scatter, atom/movable/target)
+ var/dist = 5 - get_dist(scatter, target)
+ var/delay = 2
+ SSmove_manager.move_away(scatter, target, delay = delay, timeout = delay * dist, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
/obj/item/mecha_parts/mecha_equipment/gravcatapult/get_equip_info()
- return "[..()] [mode==1?"([locked||"Nothing"])":null] \[S|P\]"
+ return "[..()] [mode==1?"([movable_target||"Nothing"])":null] \[S|P\]"
/obj/item/mecha_parts/mecha_equipment/gravcatapult/Topic(href, href_list)
..()
if(href_list["mode"])
mode = text2num(href_list["mode"])
send_byjax(chassis.occupants,"exosuit.browser","[REF(src)]",src.get_equip_info())
- return
-
+#undef GRAVSLING_MODE
+#undef GRAVPUSH_MODE
//////////////////////////// ARMOR BOOSTER MODULES //////////////////////////////////////////////////////////
@@ -154,7 +158,7 @@
range = 0
var/deflect_coeff = 1.15
var/damage_coeff = 0.8
- selectable = 0
+ selectable = FALSE
/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/proc/attack_react()
if(energy_drain && !chassis.has_charge(energy_drain))
@@ -173,7 +177,7 @@
range = 0
var/deflect_coeff = 1.15
var/damage_coeff = 0.8
- selectable = 0
+ selectable = FALSE
/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/proc/projectile_react()
if(energy_drain && !chassis.has_charge(energy_drain))
@@ -191,74 +195,76 @@
icon_state = "repair_droid"
energy_drain = 50
range = 0
- var/health_boost = 1
+
+ /// Repaired health per second
+ var/health_boost = 0.5
var/icon/droid_overlay
var/list/repairable_damage = list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH)
selectable = 0
/obj/item/mecha_parts/mecha_equipment/repair_droid/Destroy()
STOP_PROCESSING(SSobj, src)
- if(chassis)
- chassis.cut_overlay(droid_overlay)
+ chassis?.cut_overlay(droid_overlay)
return ..()
/obj/item/mecha_parts/mecha_equipment/repair_droid/attach(obj/vehicle/sealed/mecha/M)
- ..()
+ . = ..()
droid_overlay = new(src.icon, icon_state = "repair_droid")
M.add_overlay(droid_overlay)
/obj/item/mecha_parts/mecha_equipment/repair_droid/detach()
chassis.cut_overlay(droid_overlay)
STOP_PROCESSING(SSobj, src)
- ..()
+ return ..()
/obj/item/mecha_parts/mecha_equipment/repair_droid/get_equip_info()
- if(!chassis)
- return
- return "* [src.name] - [equip_ready?"A":"Dea"]ctivate"
+ return "* [src] - [activated?"Deactivate":"Activate"]"
/obj/item/mecha_parts/mecha_equipment/repair_droid/Topic(href, href_list)
..()
- if(href_list["toggle_repairs"])
- chassis.cut_overlay(droid_overlay)
- if(equip_ready)
- START_PROCESSING(SSobj, src)
- droid_overlay = new(src.icon, icon_state = "repair_droid_a")
- log_message("Activated.", LOG_MECHA)
- else
- STOP_PROCESSING(SSobj, src)
- droid_overlay = new(src.icon, icon_state = "repair_droid")
- log_message("Deactivated.", LOG_MECHA)
- chassis.add_overlay(droid_overlay)
- send_byjax(chassis.occupants,"exosuit.browser", "[REF(src)]", get_equip_info())
+ if(!href_list["toggle_repairs"])
+ return
+ chassis.cut_overlay(droid_overlay)
+ activated = !activated //now set to FALSE and active, so update the UI
+ update_equip_info()
+ if(activated)
+ START_PROCESSING(SSobj, src)
+ droid_overlay = new(icon, icon_state = "repair_droid_a")
+ log_message("Activated.", LOG_MECHA)
+ else
+ STOP_PROCESSING(SSobj, src)
+ droid_overlay = new(icon, icon_state = "repair_droid")
+ log_message("Deactivated.", LOG_MECHA)
+ chassis.add_overlay(droid_overlay)
+ send_byjax(chassis.occupants,"exosuit.browser", "[REF(src)]", get_equip_info())
-/obj/item/mecha_parts/mecha_equipment/repair_droid/process()
+/obj/item/mecha_parts/mecha_equipment/repair_droid/process(delta_time)
if(!chassis)
- STOP_PROCESSING(SSobj, src)
- return
- var/h_boost = health_boost
- var/repaired = 0
+ return PROCESS_KILL
+ var/h_boost = health_boost * delta_time
+ var/repaired = FALSE
if(chassis.internal_damage & MECHA_INT_SHORT_CIRCUIT)
h_boost *= -2
- else if(chassis.internal_damage && prob(15))
+ else if(chassis.internal_damage && DT_PROB(8, delta_time))
for(var/int_dam_flag in repairable_damage)
- if(chassis.internal_damage & int_dam_flag)
- chassis.clearInternalDamage(int_dam_flag)
- repaired = 1
- break
+ if(!(chassis.internal_damage & int_dam_flag))
+ continue
+ chassis.clear_internal_damage(int_dam_flag)
+ repaired = TRUE
+ break
if(h_boost<0 || chassis.get_integrity() < chassis.max_integrity)
chassis.repair_damage(h_boost)
- repaired = 1
+ repaired = TRUE
if(repaired)
if(!chassis.use_power(energy_drain))
- STOP_PROCESSING(SSobj, src)
+ return PROCESS_KILL
else //no repair needed, we turn off
- STOP_PROCESSING(SSobj, src)
chassis.cut_overlay(droid_overlay)
droid_overlay = new(src.icon, icon_state = "repair_droid")
chassis.add_overlay(droid_overlay)
+ return PROCESS_KILL
@@ -273,7 +279,7 @@
range = 0
var/coeff = 100
var/list/use_channels = list(AREA_USAGE_EQUIP,AREA_USAGE_ENVIRON,AREA_USAGE_LIGHT)
- selectable = 0
+ selectable = FALSE
/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/Destroy()
STOP_PROCESSING(SSobj, src)
@@ -281,31 +287,33 @@
/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/detach()
STOP_PROCESSING(SSobj, src)
- ..()
- return
+ return ..()
/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/proc/get_charge()
- if(equip_ready) //disabled
+ if(activated) //disabled
return
- var/area/A = get_area(chassis)
- var/pow_chan = GET_MUTATION_POWER_channel(A)
+ var/pow_chan = get_chassis_area_power(get_area(chassis))
if(pow_chan)
return 1000 //making magic
-/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/proc/GET_MUTATION_POWER_channel(var/area/A)
- var/pow_chan
- if(A)
- for(var/c in use_channels)
- if(A.powered(c))
- pow_chan = c
- break
+/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/proc/get_chassis_area_power(area/A)
+ if(!A)
+ return
+ var/pow_chan = 0
+ for(var/c in use_channels)
+ if(!A.powered(c))
+ continue
+ pow_chan = c
+ break
return pow_chan
/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/Topic(href, href_list)
..()
if(href_list["toggle_relay"])
- if(equip_ready) //inactive
+ activated = !activated //now set to FALSE and active, so update the UI
+ update_equip_info()
+ if(activated) //inactive
START_PROCESSING(SSobj, src)
log_message("Activated.", LOG_MECHA)
else
@@ -315,30 +323,24 @@
/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/get_equip_info()
if(!chassis)
return
- return "* [src.name] - [equip_ready?"A":"Dea"]ctivate"
+ return "* [src.name] - [activated?"Deactivate":"Activate"]"
-/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/process()
+/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/process(delta_time)
if(!chassis || chassis.internal_damage & MECHA_INT_SHORT_CIRCUIT)
- STOP_PROCESSING(SSobj, src)
- return
+ return PROCESS_KILL
var/cur_charge = chassis.get_charge()
if(isnull(cur_charge) || !chassis.cell)
- STOP_PROCESSING(SSobj, src)
to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)]No power cell detected.")
+ return PROCESS_KILL
+ if(cur_charge >= chassis.cell.maxcharge)
return
- if(cur_charge < chassis.cell.maxcharge)
- var/area/A = get_area(chassis)
- if(A)
- var/pow_chan
- for(var/c in use_channels)
- if(A.powered(c))
- pow_chan = c
- break
- if(pow_chan)
- var/delta = min(20, chassis.cell.maxcharge-cur_charge)
- chassis.give_power(delta)
- A.use_power(delta*coeff, pow_chan)
+ var/area/A = get_area(chassis)
+ var/pow_chan = get_chassis_area_power(A)
+ if(pow_chan)
+ var/delta = min(10 * delta_time, chassis.cell.maxcharge-cur_charge)
+ chassis.give_power(delta)
+ A.use_power(delta*coeff, pow_chan)
@@ -354,9 +356,12 @@
var/coeff = 100
var/obj/item/stack/sheet/fuel
var/max_fuel = 150000
- var/fuel_per_cycle_idle = 25
- var/fuel_per_cycle_active = 200
- var/power_per_cycle = 20
+ /// Fuel used per second while idle, not generating
+ var/fuelrate_idle = 12.5
+ /// Fuel used per second while actively generating
+ var/fuelrate_active = 100
+ /// Energy recharged per second
+ var/rechargerate = 10
/obj/item/mecha_parts/mecha_equipment/generator/Initialize(mapload)
. = ..()
@@ -371,12 +376,14 @@
/obj/item/mecha_parts/mecha_equipment/generator/detach()
STOP_PROCESSING(SSobj, src)
- ..()
+ return ..()
/obj/item/mecha_parts/mecha_equipment/generator/Topic(href, href_list)
..()
if(href_list["toggle"])
- if(equip_ready) //inactive
+ activated = !activated //now set to FALSE and active, so update the UI
+ update_equip_info()
+ if(activated) //inactive
START_PROCESSING(SSobj, src)
log_message("Activated.", LOG_MECHA)
else
@@ -386,13 +393,14 @@
/obj/item/mecha_parts/mecha_equipment/generator/get_equip_info()
var/output = ..()
if(output)
- return "[output] \[[fuel]: [round(fuel.amount*MINERAL_MATERIAL_AMOUNT,0.1)] cm3\] - [equip_ready?"A":"Dea"]ctivate"
+ return "[output] \[[fuel]: [round(fuel.amount*MINERAL_MATERIAL_AMOUNT,0.1)] cm3\] - [activated?"Deactivate":"Activate"]"
/obj/item/mecha_parts/mecha_equipment/generator/action(mob/source, atom/movable/target, params)
- if(chassis)
- if(load_fuel(target, source))
- send_byjax(chassis.occupants,"exosuit.browser","[REF(src)]",src.get_equip_info())
- return ..()
+ if(!chassis)
+ return
+ if(load_fuel(target, source))
+ send_byjax(chassis.occupants,"exosuit.browser","[REF(src)]",src.get_equip_info())
+ return ..()
/obj/item/mecha_parts/mecha_equipment/generator/proc/load_fuel(obj/item/stack/sheet/P, mob/user)
if(P.type == fuel.type && P.amount > 0)
@@ -413,27 +421,24 @@
/obj/item/mecha_parts/mecha_equipment/generator/attackby(weapon,mob/user, params)
load_fuel(weapon)
-/obj/item/mecha_parts/mecha_equipment/generator/process()
+/obj/item/mecha_parts/mecha_equipment/generator/process(delta_time)
if(!chassis)
- STOP_PROCESSING(SSobj, src)
- return
+ return PROCESS_KILL
if(fuel.amount<=0)
- STOP_PROCESSING(SSobj, src)
log_message("Deactivated - no fuel.", LOG_MECHA)
- return
+ to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)]Fuel reserves depleted.")
+ return PROCESS_KILL
var/cur_charge = chassis.get_charge()
if(isnull(cur_charge))
to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)]No power cell detected.")
log_message("Deactivated.", LOG_MECHA)
- STOP_PROCESSING(SSobj, src)
- return
- var/use_fuel = fuel_per_cycle_idle
+ return PROCESS_KILL
+ var/use_fuel = fuelrate_idle
if(cur_charge < chassis.cell.maxcharge)
- use_fuel = fuel_per_cycle_active
- chassis.give_power(power_per_cycle)
- fuel.amount -= min(use_fuel/MINERAL_MATERIAL_AMOUNT,fuel.amount)
+ use_fuel = fuelrate_active
+ chassis.give_power(rechargerate * delta_time)
+ fuel.amount -= min(delta_time * use_fuel / MINERAL_MATERIAL_AMOUNT, fuel.amount)
update_equip_info()
- return TRUE
/obj/item/mecha_parts/mecha_equipment/generator/nuclear
@@ -441,17 +446,18 @@
desc = "An exosuit module that generates power using uranium as fuel. Pollutes the environment."
icon_state = "tesla"
max_fuel = 50000
- fuel_per_cycle_idle = 10
- fuel_per_cycle_active = 30
- power_per_cycle = 50
- var/rad_per_cycle = 30
+ fuelrate_idle = 5
+ fuelrate_active = 15
+ rechargerate = 25
+ var/radrate = 15
/obj/item/mecha_parts/mecha_equipment/generator/nuclear/generator_init()
fuel = new /obj/item/stack/sheet/mineral/uranium(src, 0)
-/obj/item/mecha_parts/mecha_equipment/generator/nuclear/process()
- if(..())
- radiation_pulse(get_turf(src), rad_per_cycle)
+/obj/item/mecha_parts/mecha_equipment/generator/nuclear/process(delta_time)
+ . = ..()
+ if(!.) //process wasnt killed
+ radiation_pulse(get_turf(src), radrate * delta_time)
/////////////////////////////////////////// THRUSTERS /////////////////////////////////////////////
@@ -468,34 +474,35 @@
if(istype(I, src))
to_chat(user, "[M] already has this thruster package!")
return FALSE
- . = ..()
+ return ..()
/obj/item/mecha_parts/mecha_equipment/thrusters/attach(obj/vehicle/sealed/mecha/M)
M.active_thrusters = src //Enable by default
- . = ..()
+ return ..()
/obj/item/mecha_parts/mecha_equipment/thrusters/detach()
if(chassis?.active_thrusters == src)
chassis.active_thrusters = null
- . = ..()
+ return ..()
/obj/item/mecha_parts/mecha_equipment/thrusters/Destroy()
if(chassis?.active_thrusters == src)
chassis.active_thrusters = null
- . = ..()
+ return ..()
/obj/item/mecha_parts/mecha_equipment/thrusters/Topic(href,href_list)
..()
- if(!chassis)
- return
- if(href_list["mode"])
- var/mode = text2num(href_list["mode"])
- switch(mode)
- if(0)
- enable()
- if(1)
- disable()
- return
+ if(href_list["toggle"])
+ activated = !activated //now set to FALSE and active, so update the UI
+ update_equip_info()
+ if(activated) //inactive
+ START_PROCESSING(SSobj, src)
+ enable()
+ log_message("Activated.", LOG_MECHA)
+ else
+ STOP_PROCESSING(SSobj, src)
+ disable()
+ log_message("Deactivated.", LOG_MECHA)
/obj/item/mecha_parts/mecha_equipment/thrusters/proc/enable()
if (chassis.active_thrusters == src)
@@ -510,7 +517,9 @@
to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)][src] disabled.")
/obj/item/mecha_parts/mecha_equipment/thrusters/get_equip_info()
- return "[..()] \[Enable|Disable\]"
+ var/output = ..()
+ if(output)
+ return "[output] [activated?"Deactivate":"Activate"]"
/obj/item/mecha_parts/mecha_equipment/thrusters/proc/thrust(var/movement_dir)
if(!chassis)
@@ -535,7 +544,7 @@
if(!M.internal_tank)
to_chat(user, "[M] does not have an internal tank and cannot support this upgrade!")
return FALSE
- . = ..()
+ return ..()
/obj/item/mecha_parts/mecha_equipment/thrusters/gas/thrust(var/movement_dir)
if(!chassis || !chassis.internal_tank)
@@ -554,7 +563,6 @@
name = "Ion thruster package"
desc = "A set of thrusters that allow for exosuit movement in zero-gravity environments."
detachable = FALSE
- salvageable = FALSE
effect_type = /obj/effect/particle_effect/ion_trails
/obj/item/mecha_parts/mecha_equipment/thrusters/ion/thrust(var/movement_dir)
diff --git a/code/modules/vehicles/mecha/equipment/weapons/weapons.dm b/code/modules/vehicles/mecha/equipment/weapons/weapons.dm
index e7488c81f60d9..85b081a27c992 100644
--- a/code/modules/vehicles/mecha/equipment/weapons/weapons.dm
+++ b/code/modules/vehicles/mecha/equipment/weapons/weapons.dm
@@ -232,7 +232,7 @@
return TRUE
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/get_equip_info()
- return "[..()] \[[src.projectiles]\][(src.projectiles < initial(src.projectiles))?" - Rearm":null]"
+ return "[..()] \[[projectiles]\][(projectiles < initial(projectiles))?" - Rearm":null]"
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/rearm()
@@ -243,20 +243,19 @@
projectiles_to_add--
chassis.use_power(projectile_energy_cost)
send_byjax(chassis.occupants,"exosuit.browser","[REF(src)]",src.get_equip_info())
- log_message("Rearmed [src.name].", LOG_MECHA)
+ log_message("Rearmed [src].", LOG_MECHA)
return 1
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/needs_rearm()
- . = !(projectiles > 0)
+ return projectiles <= 0
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/Topic(href, href_list)
..()
if (href_list["rearm"])
- src.rearm()
- return
+ rearm()
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/action(mob/source, atom/target, params)
if(..())
@@ -456,3 +455,37 @@
var/atom/movable/AM = hit_atom
AM.safe_throw_at(get_edge_target_turf(AM,get_dir(src, AM)), 7, 2)
qdel(src)
+
+///dark honk weapons
+
+/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/banana_mortar/bombanana
+ name = "bombanana mortar"
+ desc = "Equipment for clown exosuits. Launches exploding banana peels."
+ icon_state = "mecha_bananamrtr"
+ projectile = /obj/item/grown/bananapeel/bombanana
+ projectiles = 8
+ projectile_energy_cost = 1000
+
+/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/banana_mortar/bombanana/can_attach(obj/vehicle/sealed/mecha/combat/honker/M)
+ if(..())
+ if(istype(M))
+ return TRUE
+ return FALSE
+
+/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang/tearstache
+ name = "\improper HONKeR-6 grenade launcher"
+ desc = "A weapon for combat exosuits. Launches primed tear-stache grenades."
+ icon_state = "mecha_grenadelnchr"
+ projectile = /obj/item/grenade/chem_grenade/teargas/moustache
+ fire_sound = 'sound/weapons/grenadelaunch.ogg'
+ projectiles = 6
+ missile_speed = 1.5
+ projectile_energy_cost = 800
+ equip_cooldown = 60
+ det_time = 20
+
+/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang/tearstache/can_attach(obj/vehicle/sealed/mecha/combat/honker/M)
+ if(..())
+ if(istype(M))
+ return TRUE
+ return FALSE
diff --git a/code/modules/vehicles/mecha/mech_bay.dm b/code/modules/vehicles/mecha/mech_bay.dm
index cf2a0d9b2aa00..f4f7813010d4c 100644
--- a/code/modules/vehicles/mecha/mech_bay.dm
+++ b/code/modules/vehicles/mecha/mech_bay.dm
@@ -46,14 +46,14 @@
var/MC
for(var/obj/item/stock_parts/capacitor/C in component_parts)
MC += C.rating
- recharge_power = MC * 25
+ recharge_power = MC * 12.5
/obj/machinery/mech_bay_recharge_port/examine(mob/user)
. = ..()
if(in_range(user, src) || isobserver(user))
- . += "The status display reads: Base recharge rate at [siunit(recharge_power, "W", 1)]."
+ . += "The status display reads: Recharge power [siunit(recharge_power, "W", 1)]."
-/obj/machinery/mech_bay_recharge_port/process()
+/obj/machinery/mech_bay_recharge_port/process(delta_time)
if(machine_stat & NOPOWER || !recharge_console)
return
if(!recharging_mech)
@@ -63,7 +63,7 @@
recharge_console.ui_update()
if(recharging_mech && recharging_mech.cell)
if(recharging_mech.cell.charge < recharging_mech.cell.maxcharge)
- var/delta = min(recharge_power, recharging_mech.cell.maxcharge - recharging_mech.cell.charge)
+ var/delta = min(recharge_power * delta_time, recharging_mech.cell.maxcharge - recharging_mech.cell.charge)
recharging_mech.give_power(delta)
use_power(delta*150)
else
diff --git a/code/modules/vehicles/mecha/mech_melee_attack.dm b/code/modules/vehicles/mecha/mech_melee_attack.dm
index e9c60f9dd2ef8..569965f02351d 100644
--- a/code/modules/vehicles/mecha/mech_melee_attack.dm
+++ b/code/modules/vehicles/mecha/mech_melee_attack.dm
@@ -79,6 +79,8 @@
to_chat(mecha_attacker, "You push [src] out of the way.")
/mob/living/carbon/human/mech_melee_attack(obj/vehicle/sealed/mecha/mecha_attacker, mob/user)
+ if(!user)
+ stack_trace("Warning: [src] had mech_melee_attack called on them with no mob?")
if(user.a_intent == INTENT_HARM)
if(HAS_TRAIT(user, TRAIT_PACIFISM))
to_chat(user, "You don't want to harm other living beings!")
diff --git a/code/modules/vehicles/mecha/mecha_control_console.dm b/code/modules/vehicles/mecha/mecha_control_console.dm
index b2194d82b028e..0af652f19922f 100644
--- a/code/modules/vehicles/mecha/mecha_control_console.dm
+++ b/code/modules/vehicles/mecha/mecha_control_console.dm
@@ -74,8 +74,8 @@
var/obj/vehicle/sealed/mecha/M = MT.chassis
if(M)
MT.shock()
- log_game("[key_name(usr)] has activated remote EMP on exosuit [M], located at [loc_name(M)], which [M.occupants ? "has the occupants [M.occupants]." : "without a pilot."] ")
- message_admins("[key_name_admin(usr)][ADMIN_FLW(usr)] has activated remote EMP on exosuit [M][ADMIN_JMP(M)], which is currently [M.occupants ? "occupied by [M.occupants][ADMIN_FLW(M)]." : "without a pilot."] ")
+ log_game("[key_name(usr)] has activated remote EMP on exosuit [M], located at [loc_name(M)], which is currently [LAZYLEN(M.occupants) ? "occupied by [M.occupants.Join(",")][ADMIN_FLW(M)]." : "without a pilot."]")
+ message_admins("[key_name_admin(usr)][ADMIN_FLW(usr)] has activated remote EMP on exosuit [M][ADMIN_JMP(M)], which is currently [LAZYLEN(M.occupants) ? "occupied by [M.occupants.Join(",")][ADMIN_FLW(M)]." : "without a pilot."]")
. = TRUE
/obj/item/mecha_parts/mecha_tracking
diff --git a/code/modules/vehicles/mecha/mecha_defense.dm b/code/modules/vehicles/mecha/mecha_defense.dm
index 99b4935f11291..bd3e0023e5a58 100644
--- a/code/modules/vehicles/mecha/mecha_defense.dm
+++ b/code/modules/vehicles/mecha/mecha_defense.dm
@@ -171,7 +171,7 @@
for(var/occus in occupants)
var/mob/living/occupant = occus
occupant.update_mouse_pointer()
- if(!equipment_disabled && occupants) //prevent spamming this message with back-to-back EMPs
+ if(!equipment_disabled && LAZYLEN(occupants)) //prevent spamming this message with back-to-back EMPs
to_chat(occupants, "Error -- Connection to equipment control unit has been lost.")
addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/vehicle/sealed/mecha, restore_equipment)), 3 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
equipment_disabled = 1
@@ -251,7 +251,7 @@
if(construction_state == MECHA_OPEN_HATCH && (internal_damage & MECHA_INT_SHORT_CIRCUIT))
var/obj/item/stack/cable_coil/CC = W
if(CC.use(2))
- clearInternalDamage(MECHA_INT_SHORT_CIRCUIT)
+ clear_internal_damage(MECHA_INT_SHORT_CIRCUIT)
to_chat(user, "You replace the fused wires.")
else
to_chat(user, "You need two lengths of cable to fix this mech!")
@@ -290,7 +290,7 @@
..()
. = TRUE
if(internal_damage & MECHA_INT_TEMP_CONTROL)
- clearInternalDamage(MECHA_INT_TEMP_CONTROL)
+ clear_internal_damage(MECHA_INT_TEMP_CONTROL)
to_chat(user, "You repair the damaged temperature controller.")
return
@@ -302,7 +302,7 @@
if(internal_damage & MECHA_INT_TANK_BREACH)
if(!W.use_tool(src, user, 0, volume=50, amount=1))
return
- clearInternalDamage(MECHA_INT_TANK_BREACH)
+ clear_internal_damage(MECHA_INT_TANK_BREACH)
to_chat(user, "You repair the damaged gas tank.")
return
if(obj_integrity < max_integrity)
@@ -337,15 +337,15 @@
if(cell && charge_cell)
cell.charge = cell.maxcharge
if(internal_damage & MECHA_INT_FIRE)
- clearInternalDamage(MECHA_INT_FIRE)
+ clear_internal_damage(MECHA_INT_FIRE)
if(internal_damage & MECHA_INT_TEMP_CONTROL)
- clearInternalDamage(MECHA_INT_TEMP_CONTROL)
+ clear_internal_damage(MECHA_INT_TEMP_CONTROL)
if(internal_damage & MECHA_INT_SHORT_CIRCUIT)
- clearInternalDamage(MECHA_INT_SHORT_CIRCUIT)
+ clear_internal_damage(MECHA_INT_SHORT_CIRCUIT)
if(internal_damage & MECHA_INT_TANK_BREACH)
- clearInternalDamage(MECHA_INT_TANK_BREACH)
+ clear_internal_damage(MECHA_INT_TANK_BREACH)
if(internal_damage & MECHA_INT_CONTROL_LOST)
- clearInternalDamage(MECHA_INT_CONTROL_LOST)
+ clear_internal_damage(MECHA_INT_CONTROL_LOST)
/obj/vehicle/sealed/mecha/narsie_act()
emp_act(EMP_HEAVY)
@@ -374,10 +374,10 @@
AI = crew
var/obj/structure/mecha_wreckage/WR = new wreckage(loc, AI)
for(var/obj/item/mecha_parts/mecha_equipment/E in equipment)
- if(E.salvageable && prob(30))
+ if(E.detachable && prob(30))
WR.crowbar_salvage += E
E.detach(WR) //detaches from src into WR
- E.equip_ready = 1
+ E.activated = TRUE
else
E.detach(loc)
qdel(E)
diff --git a/code/modules/vehicles/mecha/mecha_topic.dm b/code/modules/vehicles/mecha/mecha_topic.dm
index 26b09a97e3bc2..b7bc5104d16d3 100644
--- a/code/modules/vehicles/mecha/mecha_topic.dm
+++ b/code/modules/vehicles/mecha/mecha_topic.dm
@@ -395,7 +395,7 @@
///Repairs internal damage if the mech hasn't moved.
/obj/vehicle/sealed/mecha/proc/stationary_repair(location)
if(location == loc)
- clearInternalDamage(MECHA_INT_CONTROL_LOST)
+ clear_internal_damage(MECHA_INT_CONTROL_LOST)
to_chat(occupants, "[icon2html(src, occupants)]Recalibration successful.")
log_message("Recalibration of coordination system finished with 0 errors.", LOG_MECHA)
else
diff --git a/code/modules/vehicles/mecha/working/clarke.dm b/code/modules/vehicles/mecha/working/clarke.dm
index 905024e9437d6..9590c536310b3 100644
--- a/code/modules/vehicles/mecha/working/clarke.dm
+++ b/code/modules/vehicles/mecha/working/clarke.dm
@@ -65,7 +65,6 @@
icon_state = "bin"
selectable = FALSE
detachable = FALSE
- salvageable = FALSE
/// Var to avoid istype checking every time the topic button is pressed. This will only work inside Clarke mechs.
var/obj/vehicle/sealed/mecha/working/clarke/hostmech
diff --git a/code/modules/vehicles/sealed.dm b/code/modules/vehicles/sealed.dm
index 8a3b1aa6ab7b6..b37a594c8d8e2 100644
--- a/code/modules/vehicles/sealed.dm
+++ b/code/modules/vehicles/sealed.dm
@@ -122,12 +122,13 @@
/obj/vehicle/sealed/proc/DumpSpecificMobs(flag, randomstep = TRUE)
for(var/i in occupants)
- if((occupants[i] & flag))
- mob_exit(i, null, randomstep)
- if(iscarbon(i))
- var/mob/living/carbon/C = i
- C.Paralyze(40)
- C.uncuff()
+ if(!(occupants[i] & flag))
+ continue
+ mob_exit(i, null, randomstep)
+ if(iscarbon(i))
+ var/mob/living/carbon/C = i
+ C.Paralyze(40)
+ C.uncuff()
/obj/vehicle/sealed/AllowDrop()
diff --git a/code/modules/vehicles/vehicle_actions.dm b/code/modules/vehicles/vehicle_actions.dm
index 7fba7f7f2bd6c..174b7127ea6cd 100644
--- a/code/modules/vehicles/vehicle_actions.dm
+++ b/code/modules/vehicles/vehicle_actions.dm
@@ -21,7 +21,7 @@
grant_controller_actions(i) //refresh
/obj/vehicle/proc/grant_action_type_to_mob(actiontype, mob/m)
- if(isnull(occupants[m]) || !actiontype)
+ if(isnull(LAZYACCESS(occupants, m)) || !actiontype)
return FALSE
LAZYINITLIST(occupant_actions[m])
if(occupant_actions[m][actiontype])
@@ -32,7 +32,7 @@
return TRUE
/obj/vehicle/proc/remove_action_type_from_mob(actiontype, mob/m)
- if(isnull(occupants[m]) || !actiontype)
+ if(isnull(LAZYACCESS(occupants, m)) || !actiontype)
return FALSE
LAZYINITLIST(occupant_actions[m])
if(occupant_actions[m][actiontype])
@@ -50,7 +50,7 @@
remove_action_type_from_mob(v, M)
/obj/vehicle/proc/grant_controller_actions(mob/M)
- if(!istype(M) || isnull(occupants[M]))
+ if(!istype(M) || isnull(LAZYACCESS(occupants, M)))
return FALSE
for(var/i in GLOB.bitflags)
if(occupants[M] & i)
@@ -58,7 +58,7 @@
return TRUE
/obj/vehicle/proc/remove_controller_actions(mob/M)
- if(!istype(M) || isnull(occupants[M]))
+ if(!istype(M) || isnull(LAZYACCESS(occupants, M)))
return FALSE
for(var/i in GLOB.bitflags)
remove_controller_actions_by_flag(M, i)
diff --git a/html/changelogs/AutoChangeLog-pr-11149.yml b/html/changelogs/AutoChangeLog-pr-11149.yml
new file mode 100644
index 0000000000000..ebe2a28bbd3be
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11149.yml
@@ -0,0 +1,8 @@
+author: rkz, TiviPlus, TemporalOroboros, Timberpoes, MLGTASTICa
+delete-after: true
+changes:
+ - refactor: refactors mech initialization
+ - refactor: refactors medical mech tools
+ - code_imp: adds lazylist support for vehicle occupants
+ - bugfix: Phazons no longer can exponentially increase speed by phasing through
+ certain things...
diff --git a/html/changelogs/AutoChangeLog-pr-11381.yml b/html/changelogs/AutoChangeLog-pr-11381.yml
new file mode 100644
index 0000000000000..686dc86663092
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11381.yml
@@ -0,0 +1,5 @@
+author: XeonMations
+delete-after: true
+changes:
+ - tweak: Tapes now hold 20 minutes worth of recordings, 10 on each side.
+ - rscadd: Added more sounds to the universal recorder.
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index e6379f882ff31..d7467f636522b 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ
diff --git a/icons/obj/radial_tape.dmi b/icons/obj/radial_tape.dmi
new file mode 100644
index 0000000000000..ae5be96dd8a94
Binary files /dev/null and b/icons/obj/radial_tape.dmi differ
diff --git a/icons/obj/radial_taperecorder.dmi b/icons/obj/radial_taperecorder.dmi
new file mode 100644
index 0000000000000..29d97b0aba05f
Binary files /dev/null and b/icons/obj/radial_taperecorder.dmi differ
diff --git a/interface/stylesheet.dm b/interface/stylesheet.dm
index 75fad5d60bcff..f5624fe9aed9d 100644
--- a/interface/stylesheet.dm
+++ b/interface/stylesheet.dm
@@ -232,4 +232,5 @@ h1.alert, h2.alert {color: #000000;}
.monkeyhive {color: #774704;}
.monkeylead {color: #774704; font-size: 2;}
+.tape_recorder {color: #ff0000; font-family: 'Courier New', cursive, sans-serif;}
"}
diff --git a/sound/items/taperecorder/tape_flip.ogg b/sound/items/taperecorder/tape_flip.ogg
new file mode 100644
index 0000000000000..fae3e07373c5b
Binary files /dev/null and b/sound/items/taperecorder/tape_flip.ogg differ
diff --git a/sound/items/taperecorder/taperecorder_close.ogg b/sound/items/taperecorder/taperecorder_close.ogg
new file mode 100644
index 0000000000000..ab9f521c5f9fc
Binary files /dev/null and b/sound/items/taperecorder/taperecorder_close.ogg differ
diff --git a/sound/items/taperecorder/taperecorder_hiss_mid.ogg b/sound/items/taperecorder/taperecorder_hiss_mid.ogg
new file mode 100644
index 0000000000000..50ef4f2171b19
Binary files /dev/null and b/sound/items/taperecorder/taperecorder_hiss_mid.ogg differ
diff --git a/sound/items/taperecorder/taperecorder_hiss_start.ogg b/sound/items/taperecorder/taperecorder_hiss_start.ogg
new file mode 100644
index 0000000000000..fa57041a72281
Binary files /dev/null and b/sound/items/taperecorder/taperecorder_hiss_start.ogg differ
diff --git a/sound/items/taperecorder/taperecorder_open.ogg b/sound/items/taperecorder/taperecorder_open.ogg
new file mode 100644
index 0000000000000..7b7110fa58ba5
Binary files /dev/null and b/sound/items/taperecorder/taperecorder_open.ogg differ
diff --git a/sound/items/taperecorder/taperecorder_play.ogg b/sound/items/taperecorder/taperecorder_play.ogg
new file mode 100644
index 0000000000000..1bf4d7a3bd63a
Binary files /dev/null and b/sound/items/taperecorder/taperecorder_play.ogg differ
diff --git a/sound/items/taperecorder/taperecorder_print.ogg b/sound/items/taperecorder/taperecorder_print.ogg
new file mode 100644
index 0000000000000..7912d08dc9827
Binary files /dev/null and b/sound/items/taperecorder/taperecorder_print.ogg differ
diff --git a/sound/items/taperecorder/taperecorder_stop.ogg b/sound/items/taperecorder/taperecorder_stop.ogg
new file mode 100644
index 0000000000000..a3b0f659928f3
Binary files /dev/null and b/sound/items/taperecorder/taperecorder_stop.ogg differ
diff --git a/tgui/packages/tgui-panel/styles/goon/chat-dark.scss b/tgui/packages/tgui-panel/styles/goon/chat-dark.scss
index 7f0900ee516e1..e30a3478814e5 100644
--- a/tgui/packages/tgui-panel/styles/goon/chat-dark.scss
+++ b/tgui/packages/tgui-panel/styles/goon/chat-dark.scss
@@ -969,6 +969,11 @@ em {
font-family: 'Courier New', cursive, sans-serif;
}
+.tape_recorder {
+ color: #ff0000;
+ font-family: 'Courier New', cursive, sans-serif;
+}
+
.command_headset {
font-weight: bold;
font-size: 24px;
diff --git a/tgui/packages/tgui-panel/styles/goon/chat-light.scss b/tgui/packages/tgui-panel/styles/goon/chat-light.scss
index 44e3ea8702b07..0f9ef2f2be605 100644
--- a/tgui/packages/tgui-panel/styles/goon/chat-light.scss
+++ b/tgui/packages/tgui-panel/styles/goon/chat-light.scss
@@ -974,6 +974,11 @@ h2.alert {
font-family: 'Courier New', cursive, sans-serif;
}
+.tape_recorder {
+ color: #800000;
+ font-family: 'Courier New', cursive, sans-serif;
+}
+
.command_headset {
font-weight: bold;
font-size: 24px;
diff --git a/tgui/packages/tgui/constants.js b/tgui/packages/tgui/constants.js
index 4caf607c13261..182bcd0634c7a 100644
--- a/tgui/packages/tgui/constants.js
+++ b/tgui/packages/tgui/constants.js
@@ -20,6 +20,7 @@ export const COLORS = {
science: '#9b59b6',
engineering: '#f1c40f',
cargo: '#f39c12',
+ service: '#7cc46a',
centcom: '#00c100',
other: '#c38312',
},
diff --git a/tgui/packages/tgui/interfaces/CrewConsole.js b/tgui/packages/tgui/interfaces/CrewConsole.js
deleted file mode 100644
index bf72fad4e327a..0000000000000
--- a/tgui/packages/tgui/interfaces/CrewConsole.js
+++ /dev/null
@@ -1,139 +0,0 @@
-import { useBackend } from '../backend';
-import { Box, Button, ColorBox, Section, Table } from '../components';
-import { COLORS } from '../constants';
-import { Window } from '../layouts';
-import { sortBy } from 'common/collections';
-
-export const HEALTH_COLOR_BY_LEVEL = ['#17d568', '#2ecc71', '#e67e22', '#ed5100', '#e74c3c', '#ed2814'];
-
-export const jobIsHead = (jobId) => jobId % 10 === 0;
-
-export const jobToColor = (jobId) => {
- if (jobId >= 0 && jobId < 10) {
- return COLORS.department.captain;
- }
- if (jobId >= 10 && jobId < 20) {
- return COLORS.department.security;
- }
- if (jobId >= 20 && jobId < 30) {
- return COLORS.department.medbay;
- }
- if (jobId >= 30 && jobId < 40) {
- return COLORS.department.science;
- }
- if (jobId >= 40 && jobId < 50) {
- return COLORS.department.engineering;
- }
- if (jobId >= 50 && jobId < 60) {
- return COLORS.department.cargo;
- }
- if (jobId >= 200 && jobId < 230) {
- return COLORS.department.centcom;
- }
- if (jobId === -1) {
- return null;
- }
- return COLORS.department.other;
-};
-
-export const healthToColor = (oxy, tox, burn, brute) => {
- const healthSum = oxy + tox + burn + brute;
- const level = Math.min(Math.max(Math.ceil(healthSum / 25), 0), 5);
- return HEALTH_COLOR_BY_LEVEL[level];
-};
-
-export const HealthStat = (props) => {
- const { type, value } = props;
- return (
-
- {value}
-
- );
-};
-
-export const CrewConsole = () => {
- return (
-
-
-
-
-
- );
-};
-
-const CrewTable = (props, context) => {
- const { act, data } = useBackend(context);
- const sensors = sortBy((s) => s.ijob)(data.sensors ?? []);
- return (
-
-
- Name
-
-
- Vitals
-
- Position
- {!!data.link_allowed && (
-
- Tracking
-
- )}
-
- {sensors.map((sensor) => (
-
- ))}
-
- );
-};
-
-const CrewTableEntry = (props, context) => {
- const { act, data } = useBackend(context);
- const { link_allowed } = data;
- const { sensor_data } = props;
- const { name, assignment, ijob, life_status, oxydam, toxdam, burndam, brutedam, area, can_track } = sensor_data;
-
- return (
-
-
- {name}
- {assignment !== undefined ? ` (${assignment})` : ''}
-
-
- {life_status ? : }
-
-
- {oxydam !== undefined ? (
-
-
- {'/'}
-
- {'/'}
-
- {'/'}
-
-
- ) : life_status ? (
- 'Alive'
- ) : (
- 'Dead'
- )}
-
- {area !== undefined ? area : 'N/A'}
- {!!link_allowed && (
-
-
- )}
-
- );
-};
diff --git a/tgui/packages/tgui/interfaces/CrewConsole.tsx b/tgui/packages/tgui/interfaces/CrewConsole.tsx
new file mode 100644
index 0000000000000..192eac007d4f6
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/CrewConsole.tsx
@@ -0,0 +1,258 @@
+import { Box, Button, Icon, Input, Section, Table } from '../components';
+import { BooleanLike } from 'common/react';
+import { createSearch } from 'common/string';
+import { useBackend, useLocalState } from '../backend';
+import { COLORS } from '../constants';
+import { Window } from '../layouts';
+
+const HEALTH_COLOR_BY_LEVEL = ['#17d568', '#c4cf2d', '#e67e22', '#ed5100', '#e74c3c', '#801308'];
+
+const SORT_NAMES = {
+ ijob: 'Job',
+ name: 'Name',
+ area: 'Position',
+ health: 'Vitals',
+};
+
+const STAT_LIVING = 0;
+const STAT_DEAD = 4;
+
+const SORT_OPTIONS = ['health', 'ijob', 'name', 'area'];
+
+const jobIsHead = (jobId: number) => jobId % 10 === 0;
+
+const jobToColor = (jobId: number) => {
+ if (jobId === 0) {
+ return COLORS.department.captain;
+ }
+ if (jobId >= 10 && jobId < 20) {
+ return COLORS.department.security;
+ }
+ if (jobId >= 20 && jobId < 30) {
+ return COLORS.department.medbay;
+ }
+ if (jobId >= 30 && jobId < 40) {
+ return COLORS.department.science;
+ }
+ if (jobId >= 40 && jobId < 50) {
+ return COLORS.department.engineering;
+ }
+ if (jobId >= 50 && jobId < 60) {
+ return COLORS.department.cargo;
+ }
+ if (jobId >= 60 && jobId < 200) {
+ return COLORS.department.service;
+ }
+ if (jobId >= 200 && jobId < 230) {
+ return COLORS.department.centcom;
+ }
+ return COLORS.department.other;
+};
+
+const statToIcon = (life_status: number) => {
+ switch (life_status) {
+ case STAT_LIVING:
+ return 'heart';
+ case STAT_DEAD:
+ return 'skull';
+ }
+ return 'heartbeat';
+};
+
+const healthSort = (a: CrewSensor, b: CrewSensor) => {
+ if (a.life_status > b.life_status) return -1;
+ if (a.life_status < b.life_status) return 1;
+ if (a.health < b.health) return -1;
+ if (a.health > b.health) return 1;
+ return 0;
+};
+
+const areaSort = (a: CrewSensor, b: CrewSensor) => {
+ a.area ??= '~';
+ b.area ??= '~';
+ if (a.area < b.area) return -1;
+ if (a.area > b.area) return 1;
+ return 0;
+};
+
+const healthToAttribute = (oxy: number, tox: number, burn: number, brute: number, attributeList: string[]) => {
+ const healthSum = oxy + tox + burn + brute;
+ const level = Math.min(Math.max(Math.ceil(healthSum / 25), 0), 5);
+ return attributeList[level];
+};
+
+type HealthStatProps = {
+ type: string;
+ value: number;
+};
+
+const HealthStat = (props: HealthStatProps) => {
+ const { type, value } = props;
+ return (
+
+ {value}
+
+ );
+};
+
+export const CrewConsole = () => {
+ return (
+
+
+
+
+
+ );
+};
+
+type CrewSensor = {
+ name: string;
+ assignment: string | undefined;
+ ijob: number;
+ life_status: number;
+ oxydam: number;
+ toxdam: number;
+ burndam: number;
+ brutedam: number;
+ area: string | undefined;
+ health: number;
+ can_track: BooleanLike;
+ ref: string;
+};
+
+type CrewConsoleData = {
+ sensors: CrewSensor[];
+ link_allowed: BooleanLike;
+};
+
+const CrewTable = (props, context) => {
+ const { data } = useBackend(context);
+ const { sensors } = data;
+
+ const [sortAsc, setSortAsc] = useLocalState(context, 'sortAsc', true);
+ const [searchQuery, setSearchQuery] = useLocalState(context, 'searchQuery', '');
+ const [sortBy, setSortBy] = useLocalState(context, 'sortBy', SORT_OPTIONS[0]);
+
+ const cycleSortBy = () => {
+ let idx = SORT_OPTIONS.indexOf(sortBy) + 1;
+ if (idx === SORT_OPTIONS.length) idx = 0;
+ setSortBy(SORT_OPTIONS[idx]);
+ };
+
+ const nameSearch = createSearch(searchQuery, (crew: CrewSensor) => crew.name);
+
+ const sorted = sensors.filter(nameSearch).sort((a, b) => {
+ switch (sortBy) {
+ case 'name':
+ return sortAsc ? +(a.name > b.name) : +(b.name > a.name);
+ case 'ijob':
+ return sortAsc ? a.ijob - b.ijob : b.ijob - a.ijob;
+ case 'health':
+ return sortAsc ? healthSort(a, b) : healthSort(b, a);
+ case 'area':
+ return sortAsc ? areaSort(a, b) : areaSort(b, a);
+ default:
+ return 0;
+ }
+ });
+
+ return (
+
+ );
+};
+
+type CrewTableEntryProps = {
+ sensor_data: CrewSensor;
+};
+
+const CrewTableEntry = (props: CrewTableEntryProps, context) => {
+ const { act, data } = useBackend(context);
+ const { link_allowed } = data;
+ const { sensor_data } = props;
+ const { name, assignment, ijob, life_status, oxydam, toxdam, burndam, brutedam, area, can_track } = sensor_data;
+
+ return (
+
+
+ {name}
+ {assignment !== undefined ? ` (${assignment})` : ''}
+
+
+ {oxydam !== undefined ? (
+
+ ) : life_status !== STAT_DEAD ? (
+
+ ) : (
+
+ )}
+
+
+ {oxydam !== undefined ? (
+
+
+ {'/'}
+
+ {'/'}
+
+ {'/'}
+
+
+ ) : life_status !== STAT_DEAD ? (
+ 'Alive'
+ ) : (
+ 'Dead'
+ )}
+
+ {area !== '~' && area !== undefined ? area : }
+ {!!link_allowed && (
+
+
+
+ )}
+
+ );
+};