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 && ( - - + + setSearchQuery((e.target as HTMLTextAreaElement).value)} /> + + }> + + + Name + + + Vitals + + + Position + + {!!data.link_allowed && ( + + Tracking + + )} + + {sorted.map((sensor) => ( + + ))} +
+ + ); +}; + +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 && ( + + + + )} + + ); +};