From ea1780ce970a69c786a43d2de33d0472985ce29f Mon Sep 17 00:00:00 2001 From: phil235 Date: Tue, 7 Jul 2015 19:40:32 +0200 Subject: [PATCH] - Mech equipment was using its unique processing method, with specific timing for each equipment, but now it uses obj processing, I changed the values so the equipment effect stays the same. Remove global_iterator datum. - fiddled with equip_ready procs of mech tools. - removed mecha/proc/do_after and /enter_after() procs. - Renamed mech sleeper occupant var to "patient" to avoid confusion with the occupant of the mech. - all non instant tool (drill) now show a progbar when drilling etc.. - action cooldown now use do_after_cooldown() (that itself uses do_after) and start_cooldown (for instantaneous actions). - Removed mecha_equipment's destroy proc, it's now all in Destroy(). No confusion. - modified mecha_equipment/proc/can_attach() to not check istype(mecha) b/c it can't not be. (so the child only have one istype check. - Removed diamonddrill/can_attach() , all done at drill level. - armor booster now only for combat mech, instead of all except honkmech. - Removed dynhitby, dynbulletdamage, dynattackby, dynusepower(), dyngetcharge(), dynabsorbdamage() - I split the tools.dm file into smaller ones: work tools, mining tools, other tools. - I split mecha.dm into mecha.dm, mecha_topic.dm and mecha_defense.dm - refactored mech weapon ballistic/launcher, new proj_init proc, more OOP. - Moving consumes energy! Lights consumes energy. Fixes #9425. - Fixed #7354 xeno not bursting if host is inside a mech. - Added action buttons to mech. Moved toggle lights, internal tank, eject, view stats from verbs to action buttons, these can now only be done via these buttons (removed them from the big stats window). - Removed port connect verbs b/C it's automated now. - regular hud is no longer hidden when inside a mech (doesn't matter b/c you can't interact with most stuff in it while in a mecha). Fixes issue 10387 - can't walk when shooting projectiles. Makes walking on your projectile a bit harder. Helps against issue 10315 (but doesn't fix it). - also made into action buttons: the special abilities of certain combat mechs. - Added thrown alerts for mech charge and integrity. - Fixes teleporting occupant out not properly removing it from the mech. Fixes issue 10330 - fixes ballistic mech weapons spinning when launched. proc/throw_at() now has a spin argument instead of using var/allow_spin that was added to all atoms just for that. - added a update_action_buttons() to ai/life() to handle ai mech. --- code/_onclick/hud/alert.dm | 7 + code/_onclick/hud/hud.dm | 2 - code/datums/helper_datums/global_iterator.dm | 160 -- code/game/atoms.dm | 5 - code/game/atoms_movable.dm | 4 +- .../gamemodes/malfunction/Malf_Modules.dm | 2 +- code/game/mecha/combat/durand.dm | 64 +- code/game/mecha/combat/gygax.dm | 10 +- code/game/mecha/combat/marauder.dm | 136 +- code/game/mecha/combat/phazon.dm | 96 +- code/game/mecha/combat/reticence.dm | 2 +- code/game/mecha/equipment/mecha_equipment.dm | 53 +- .../mecha/equipment/tools/medical_tools.dm | 265 ++-- .../mecha/equipment/tools/mining_tools.dm | 127 ++ .../game/mecha/equipment/tools/other_tools.dm | 518 ++++++ code/game/mecha/equipment/tools/tools.dm | 1245 --------------- code/game/mecha/equipment/tools/work_tools.dm | 424 +++++ code/game/mecha/equipment/weapons/weapons.dm | 134 +- code/game/mecha/mecha.dm | 1413 ++++------------- code/game/mecha/mecha_defense.dm | 345 ++++ code/game/mecha/mecha_parts.dm | 27 - code/game/mecha/mecha_topic.dm | 321 ++++ code/game/mecha/working/ripley.dm | 63 +- code/game/objects/items.dm | 2 +- code/game/objects/items/devices/PDA/PDA.dm | 2 +- code/modules/admin/verbs/debug.dm | 2 +- code/modules/mining/mine_turfs.dm | 2 +- .../carbon/alien/special/alien_embryo.dm | 5 +- code/modules/mob/living/silicon/ai/life.dm | 4 +- code/modules/power/singularity/singularity.dm | 2 +- .../modules/research/designs/mecha_designs.dm | 10 +- .../designs/mechfabricator_designs.dm | 18 +- data/mode.txt | 2 +- html/changelogs/phil235-MechRefactoring.yml | 4 + icons/mob/actions.dmi | Bin 21822 -> 29912 bytes icons/mob/screen_alert.dmi | Bin 23130 -> 23899 bytes tgstation.dme | 7 +- 37 files changed, 2465 insertions(+), 3018 deletions(-) delete mode 100644 code/datums/helper_datums/global_iterator.dm create mode 100644 code/game/mecha/equipment/tools/mining_tools.dm create mode 100644 code/game/mecha/equipment/tools/other_tools.dm delete mode 100644 code/game/mecha/equipment/tools/tools.dm create mode 100644 code/game/mecha/equipment/tools/work_tools.dm create mode 100644 code/game/mecha/mecha_defense.dm create mode 100644 code/game/mecha/mecha_topic.dm create mode 100644 html/changelogs/phil235-MechRefactoring.yml diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index e9b60141730..c8f7df027e5 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -228,6 +228,13 @@ office by your AI master or any qualified human may resolve this matter. Robotic so as to remain in compliance with the most up-to-date laws." timeout = 300 +//MECHS + +/obj/screen/alert/low_mech_integrity + name = "Mech Damaged" + desc = "Mech integrity is low." + + //OBJECT-BASED /obj/screen/alert/buckled diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index cb0ddfbf5ee..3d24b6c1c5b 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -198,8 +198,6 @@ var/datum/global_hud/global_hud = new() else if(isdrone(mymob)) drone_hud(ui_style) - if(istype(mymob.loc,/obj/mecha) && ishuman(mymob)) - show_hud(HUD_STYLE_REDUCED) //Version denotes which style should be displayed. blank or 0 means "next version" /datum/hud/proc/show_hud(var/version = 0) diff --git a/code/datums/helper_datums/global_iterator.dm b/code/datums/helper_datums/global_iterator.dm deleted file mode 100644 index 828f6cfe559..00000000000 --- a/code/datums/helper_datums/global_iterator.dm +++ /dev/null @@ -1,160 +0,0 @@ -/* -README: - -The global_iterator datum is supposed to provide a simple and robust way to -create some constantly "looping" processes with ability to stop and restart them at will. -Generally, the only thing you want to play with (meaning, redefine) is the process() proc. -It must contain all the things you want done. - -Control functions: - new - used to create datum. First argument (optional) - var list(to use in process() proc) as list, - second (optional) - autostart control. - If autostart == TRUE, the loop will be started immediately after datum creation. - - start(list/arguments) - starts the loop. Takes arguments(optional) as a list, which is then used - by process() proc. Returns null if datum already active, 1 if loop started successfully and 0 if there's - an error in supplied arguments (not list or empty list). - - stop() - stops the loop. Returns null if datum is already inactive and 1 on success. - - set_delay(new_delay) - sets the delay between iterations. Pretty selfexplanatory. - Returns 0 on error(new_delay is not numerical), 1 otherwise. - - set_process_args(list/arguments) - passes the supplied arguments to the process() proc. - - active() - Returns 1 if datum is active, 0 otherwise. - - toggle() - toggles datum state. Returns new datum state (see active()). - -Misc functions: - - get_last_exec_time() - Returns the time of last iteration. - - get_last_exec_time_as_text() - Returns the time of last iteration as text - - -Control vars: - - delay - delay between iterations - - check_for_null - if equals TRUE, on each iteration the supplied arguments will be checked for nulls. - If some varible equals null (and null only), the loop is stopped. - Usefull, if some var unexpectedly becomes null - due to object deletion, for example. - Of course, you can also check the variables inside process() proc to prevent runtime errors. - -Data storage vars: - - result - stores the value returned by process() proc -*/ - -/datum/global_iterator - var/control_switch = 0 - var/delay = 10 - var/list/arg_list = new - var/last_exec = null - var/check_for_null = 1 - var/forbid_garbage = 0 - var/result - var/state = 0 - -/datum/global_iterator/New(list/arguments=null,autostart=1) - delay = delay>0?(delay):1 - if(forbid_garbage) //prevents garbage collection with tag != null - tag = "\ref[src]" - set_process_args(arguments) - if(autostart) - start() - return - -/datum/global_iterator/Destroy() - tag = null - arg_list.Cut() - stop() - //Do not call ..() - -/datum/global_iterator/proc/main() - state = 1 - while(src && control_switch) - last_exec = world.timeofday - if(check_for_null && has_null_args()) - stop() - return 0 - result = process(arglist(arg_list)) - for(var/sleep_time=delay;sleep_time>0;sleep_time--) //uhh, this is ugly. But I see no other way to terminate sleeping proc. Such disgrace. - if(!control_switch) - return 0 - sleep(1) - return 0 - -/datum/global_iterator/proc/start(list/arguments=null) - if(active()) - return - if(arguments) - if(!set_process_args(arguments)) - return 0 - if(!state_check()) //the main loop is sleeping, wait for it to terminate. - return - control_switch = 1 - spawn() - state = main() - return 1 - -/datum/global_iterator/proc/stop() - if(!active()) - return - control_switch = 0 - spawn(-1) //report termination error but don't wait for state_check(). - state_check() - return 1 - -/datum/global_iterator/proc/state_check() - var/lag = 0 - while(state) - sleep(1) - if(++lag>10) - CRASH("The global_iterator loop \ref[src] failed to terminate in designated timeframe. This may be caused by server lagging.") - return 1 - -/datum/global_iterator/process() - return - -/datum/global_iterator/proc/active() - return control_switch - -/datum/global_iterator/proc/has_null_args() - if(null in arg_list) - return 1 - return 0 - - -/datum/global_iterator/proc/set_delay(new_delay) - if(isnum(new_delay)) - delay = max(1, round(new_delay)) - return 1 - else - return 0 - -/datum/global_iterator/proc/get_last_exec_time() - return (last_exec||0) - -/datum/global_iterator/proc/get_last_exec_time_as_text() - return (time2text(last_exec)||"Wasn't executed yet") - -/datum/global_iterator/proc/set_process_args(list/arguments) - if(arguments && istype(arguments, /list) && arguments.len) - arg_list = arguments - return 1 - else -// world << "\red Invalid arguments supplied for [src.type], ref = \ref[src]" - return 0 - -/datum/global_iterator/proc/toggle_null_checks() - check_for_null = !check_for_null - return check_for_null - -/datum/global_iterator/proc/toggle() - if(!stop()) - start() - return active() - - diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 52ac867c334..8b4019f2800 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -17,11 +17,6 @@ //HUD images that this atom can provide. var/list/hud_possible - //var/chem_is_open_container = 0 - // replaced by OPENCONTAINER flags and atom/proc/is_open_container() - ///Chemistry. - var/allow_spin = 1 - //Value used to increment ex_act() if reactionary_explosions is on var/explosion_block = 0 diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index f0083db4d8b..655741064cb 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -168,12 +168,12 @@ src.throw_impact(A) src.throwing = 0 -/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower) +/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, var/spin = 1) if(!target || !src || (flags & NODROP)) return 0 //use a modified version of Bresenham's algorithm to get from the atom's current position to that of the target src.throwing = 1 - if(target.allow_spin) // turns out 1000+ spinning objects being thrown at the singularity creates lag - Iamgoofball + if(spin) // turns out 1000+ spinning objects being thrown at the singularity creates lag - Iamgoofball SpinAnimation(5, 1) var/dist_x = abs(target.x - src.x) var/dist_y = abs(target.y - src.y) diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm index e89be96093c..7c17d354ae6 100644 --- a/code/game/gamemodes/malfunction/Malf_Modules.dm +++ b/code/game/gamemodes/malfunction/Malf_Modules.dm @@ -140,7 +140,7 @@ rcdmod.uses -- for(var/obj/item/weapon/rcd/rcd in world) rcd.disabled = 1 - for(var/obj/item/mecha_parts/mecha_equipment/tool/rcd/rcd in world) + for(var/obj/item/mecha_parts/mecha_equipment/rcd/rcd in world) rcd.disabled = 1 src << "Out of uses." diff --git a/code/game/mecha/combat/durand.dm b/code/game/mecha/combat/durand.dm index 3a49c11433a..fcbd128a5c3 100644 --- a/code/game/mecha/combat/durand.dm +++ b/code/game/mecha/combat/durand.dm @@ -13,15 +13,7 @@ var/defence = 0 var/defence_deflect = 35 wreckage = /obj/structure/mecha_wreckage/durand - -/* -/obj/mecha/combat/durand/New() - ..() - weapons += new /datum/mecha_weapon/ballistic/lmg(src) - weapons += new /datum/mecha_weapon/ballistic/scattershot(src) - selected_weapon = weapons[1] - return -*/ + var/datum/action/mecha/mech_defence_mode/defense_action = new /obj/mecha/combat/durand/relaymove(mob/user,direction) if(defence) @@ -32,43 +24,33 @@ . = ..() return +/obj/mecha/combat/durand/GrantActions(var/mob/living/user, var/human_occupant = 0) + ..() + defense_action.chassis = src + defense_action.Grant(user) -/obj/mecha/combat/durand/verb/defence_mode() - set category = "Exosuit Interface" - set name = "Toggle defence mode" - set src = usr.loc - set popup_menu = 0 - if(!can_use(usr)) - return - defence = !defence - if(defence) - deflect_chance = defence_deflect - src.occupant_message("You enable [src] defence mode.") - else - deflect_chance = initial(deflect_chance) - src.occupant_message("You disable [src] defence mode.") - src.log_message("Toggled defence mode.") - return - +/obj/mecha/combat/durand/RemoveActions(var/mob/living/user, var/human_occupant = 0) + ..() + defense_action.Remove(user) /obj/mecha/combat/durand/get_stats_part() var/output = ..() output += "Defence mode: [defence?"on":"off"]" return output -/obj/mecha/combat/durand/get_commands() - var/output = {"
-
Special
- -
- "} - output += ..() - return output +/datum/action/mecha/mech_defence_mode + name = "Toggle Defense Mode" + button_icon_state = "mech_toggle_defense_mode" -/obj/mecha/combat/durand/Topic(href, href_list) - ..() - if (href_list["toggle_defence_mode"]) - src.defence_mode() - return \ No newline at end of file +/datum/action/mecha/mech_defence_mode/Activate() + if(!owner || !chassis || chassis.occupant != owner) + return + var/obj/mecha/combat/durand/D = chassis + D.defence = !D.defence + if(D.defence) + D.deflect_chance = D.defence_deflect + D.occupant_message("You enable [D] defence mode.") + else + D.deflect_chance = initial(D.deflect_chance) + D.occupant_message("You disable [D] defence mode.") + D.log_message("Toggled defence mode.") \ No newline at end of file diff --git a/code/game/mecha/combat/gygax.dm b/code/game/mecha/combat/gygax.dm index 722b576f134..1a9ec2627cd 100644 --- a/code/game/mecha/combat/gygax.dm +++ b/code/game/mecha/combat/gygax.dm @@ -33,7 +33,7 @@ ..() var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/carbine ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang + ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang ME.attach(src) ME = new /obj/item/mecha_parts/mecha_equipment/teleporter ME.attach(src) @@ -71,16 +71,16 @@ src.log_message("Toggled leg actuators overload.") return -/obj/mecha/combat/gygax/dyndomove(direction) - if(!..()) return +/obj/mecha/combat/gygax/domove(direction) + if(!..()) + return if(overload) health-- if(health < initial(health) - initial(health)/3) overload = 0 step_in = initial(step_in) step_energy_drain = initial(step_energy_drain) - src.occupant_message("Leg actuators damage threshold exceded. Disabling overload.") - return + occupant_message("Leg actuators damage threshold exceded. Disabling overload.") /obj/mecha/combat/gygax/get_stats_part() diff --git a/code/game/mecha/combat/marauder.dm b/code/game/mecha/combat/marauder.dm index 89580944e01..2d39f58e103 100644 --- a/code/game/mecha/combat/marauder.dm +++ b/code/game/mecha/combat/marauder.dm @@ -20,6 +20,9 @@ internal_damage_threshold = 25 force = 45 max_equip = 4 + var/datum/action/mecha/mech_smoke/smoke_action = new + var/datum/action/mecha/mech_toggle_thrusters/thrusters_action = new + var/datum/action/mecha/mech_zoom/zoom_action = new /obj/mecha/combat/marauder/Destroy() qdel(smoke_system) @@ -51,7 +54,7 @@ ME.attach(src) ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot(src) ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack(src) + ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/missile_rack(src) ME.attach(src) ME = new /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay(src) ME.attach(src) @@ -65,7 +68,7 @@ ..() var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/weapon/energy/pulse(src) ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack(src) + ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/missile_rack(src) ME.attach(src) ME = new /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay(src) ME.attach(src) @@ -84,7 +87,7 @@ qdel(ME) ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot(src) ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack(src) + ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/missile_rack(src) ME.attach(src) ME = new /obj/item/mecha_parts/mecha_equipment/teleporter(src) ME.attach(src) @@ -110,54 +113,23 @@ return 1 return 0 -/obj/mecha/combat/marauder/verb/toggle_thrusters() - set category = "Exosuit Interface" - set name = "Toggle thrusters" - set src = usr.loc - set popup_menu = 0 - if(usr!=src.occupant) - return - if(src.occupant) - if(get_charge() > 0) - thrusters = !thrusters - src.log_message("Toggled thrusters.") - src.occupant_message("Thrusters [thrusters?"en":"dis"]abled.") - return +/obj/mecha/combat/marauder/GrantActions(var/mob/living/user, var/human_occupant = 0) + ..() + smoke_action.chassis = src + smoke_action.Grant(user) -/obj/mecha/combat/marauder/verb/smoke() - set category = "Exosuit Interface" - set name = "Smoke" - set src = usr.loc - set popup_menu = 0 - if(usr!=src.occupant) - return - if(smoke_ready && smoke>0) - src.smoke_system.start() - smoke-- - smoke_ready = 0 - spawn(smoke_cooldown) - smoke_ready = 1 - return + thrusters_action.chassis = src + thrusters_action.Grant(user) -/obj/mecha/combat/marauder/verb/zoom() - set category = "Exosuit Interface" - set name = "Zoom" - set src = usr.loc - set popup_menu = 0 - if(usr!=src.occupant) - return - if(src.occupant.client) - src.zoom = !src.zoom - src.log_message("Toggled zoom mode.") - src.occupant_message("Zoom mode [zoom?"en":"dis"]abled.") - if(zoom) - src.occupant.client.view = 12 - src.occupant << sound('sound/mecha/imag_enh.ogg',volume=50) - else - src.occupant.client.view = world.view//world.view - default mob view size - return + zoom_action.chassis = src + zoom_action.Grant(user) +/obj/mecha/combat/marauder/RemoveActions(var/mob/living/user, var/human_occupant = 0) + ..() + smoke_action.Remove(user) + thrusters_action.Remove(user) + zoom_action.Remove(user) /obj/mecha/combat/marauder/go_out() if(src.occupant && src.occupant.client) @@ -176,25 +148,53 @@ return output -/obj/mecha/combat/marauder/get_commands() - var/output = {"
-
Special
- -
- "} - output += ..() - return output +/datum/action/mecha/mech_smoke + name = "Smoke" + button_icon_state = "smoke" -/obj/mecha/combat/marauder/Topic(href, href_list) - ..() - if (href_list["toggle_thrusters"]) - src.toggle_thrusters() - if (href_list["smoke"]) - src.smoke() - if (href_list["toggle_zoom"]) - src.zoom() - return \ No newline at end of file +/datum/action/mecha/mech_smoke/Activate() + if(!owner || !chassis || chassis.occupant != owner) + return + var/obj/mecha/combat/marauder/M = chassis + if(M.smoke_ready && M.smoke>0) + M.smoke_system.start() + M.smoke-- + M.smoke_ready = 0 + spawn(M.smoke_cooldown) + M.smoke_ready = 1 + + +/datum/action/mecha/mech_toggle_thrusters + name = "Toggle Thrusters" + button_icon_state = "mech_toggle_thrusters" + +/datum/action/mecha/mech_toggle_thrusters/Activate() + if(!owner || !chassis || chassis.occupant != owner) + return + var/obj/mecha/combat/marauder/M = chassis + if(M.get_charge() > 0) + M.thrusters = !M.thrusters + if(M.thrusters) + button_icon_state = "mech_toggle_thrusters_on" + else + button_icon_state = "mech_toggle_thrusters" + M.log_message("Toggled thrusters.") + M.occupant_message("Thrusters [M.thrusters?"en":"dis"]abled.") + +/datum/action/mecha/mech_zoom + name = "Zoom" + button_icon_state = "mech_zoom" + +/datum/action/mecha/mech_zoom/Activate() + if(!owner || !chassis || chassis.occupant != owner) + return + var/obj/mecha/combat/marauder/M = chassis + if(owner.client) + M.zoom = !M.zoom + M.log_message("Toggled zoom mode.") + M.occupant_message("Zoom mode [M.zoom?"en":"dis"]abled.") + if(M.zoom) + owner.client.view = 12 + owner << sound('sound/mecha/imag_enh.ogg',volume=50) + else + owner.client.view = world.view//world.view - default mob view size diff --git a/code/game/mecha/combat/phazon.dm b/code/game/mecha/combat/phazon.dm index 4260976a41a..758dd4ccd62 100644 --- a/code/game/mecha/combat/phazon.dm +++ b/code/game/mecha/combat/phazon.dm @@ -17,6 +17,8 @@ var/phasing = 0 var/phasing_energy_drain = 200 max_equip = 3 + var/datum/action/mecha/mech_switch_damtype/switch_damtype_action = new + var/datum/action/mecha/mech_toggle_phasing/phasing_action = new /obj/mecha/combat/phazon/Bump(var/atom/obstacle) if(phasing && get_charge()>=phasing_energy_drain) @@ -24,8 +26,8 @@ if(can_move) can_move = 0 flick("phazon-phase", src) - src.loc = get_step(src,src.dir) - src.use_power(phasing_energy_drain) + loc = get_step(src, dir) + use_power(phasing_energy_drain) sleep(step_in*3) can_move = 1 else @@ -34,50 +36,56 @@ /obj/mecha/combat/phazon/click_action(atom/target,mob/user) if(phasing) - src.occupant_message("Unable to interact with objects while phasing") + occupant_message("Unable to interact with objects while phasing") return else return ..() -/obj/mecha/combat/phazon/verb/switch_damtype() - set category = "Exosuit Interface" - set name = "Reconfigure arm microtool arrays" - set src = usr.loc - set popup_menu = 0 - if(usr!=src.occupant) - return - var/new_damtype = alert(src.occupant,"Arm tool selection",null,"Fists","Torch","Toxic injector") - switch(new_damtype) - if("Fists") - damtype = "brute" - src.occupant_message("Your exosuit's hands form into fists.") - if("Torch") - damtype = "fire" - src.occupant_message("A torch tip extends from your exosuit's hand, glowing red.") - if("Toxic injector") - damtype = "tox" - src.occupant_message("A bone-chillingly thick plasteel needle protracts from the exosuit's palm.") - playsound(src, 'sound/mecha/mechmove01.ogg', 50, 1) - return - -/obj/mecha/combat/phazon/get_commands() - var/output = {" - "} - output += ..() - return output - -/obj/mecha/combat/phazon/Topic(href, href_list) +/obj/mecha/combat/phazon/GrantActions(var/mob/living/user, var/human_occupant = 0) ..() - if (href_list["switch_damtype"]) - src.switch_damtype() - if (href_list["phasing"]) - phasing = !phasing - send_byjax(src.occupant,"exosuit.browser","phasing_command","[phasing?"Dis":"En"]able phasing") - src.occupant_message("En":"#f00\">Dis"]abled phasing.") - return \ No newline at end of file + switch_damtype_action.chassis = src + switch_damtype_action.Grant(user) + + phasing_action.chassis = src + phasing_action.Grant(user) + + +/obj/mecha/combat/phazon/RemoveActions(var/mob/living/user, var/human_occupant = 0) + ..() + switch_damtype_action.Remove(user) + phasing_action.Remove(user) + + +/datum/action/mecha/mech_switch_damtype + name = "Reconfigure arm microtool arrays" + button_icon_state = "mech_switch_damtype" + +/datum/action/mecha/mech_switch_damtype/Activate() + if(!owner || !chassis || chassis.occupant != owner) + return + var/obj/mecha/combat/phazon/P = chassis + var/new_damtype + switch(P.damtype) + if("tox") + new_damtype = "brute" + P.occupant_message("Your exosuit's hands form into fists.") + if("brute") + new_damtype = "fire" + P.occupant_message("A torch tip extends from your exosuit's hand, glowing red.") + if("fire") + new_damtype = "tox" + P.occupant_message("A bone-chillingly thick plasteel needle protracts from the exosuit's palm.") + P.damtype = new_damtype. + playsound(src, 'sound/mecha/mechmove01.ogg', 50, 1) + + +/datum/action/mecha/mech_toggle_phasing + name = "Enable Phasing" + button_icon_state = "mech_toggle_phasing" + +/datum/action/mecha/mech_toggle_phasing/Activate() + if(!owner || !chassis || chassis.occupant != owner) + return + var/obj/mecha/combat/phazon/P = chassis + P.phasing = !P.phasing + P.occupant_message("En":"#f00\">Dis"]abled phasing.") \ No newline at end of file diff --git a/code/game/mecha/combat/reticence.dm b/code/game/mecha/combat/reticence.dm index 6b2fa70ee3a..612add46853 100644 --- a/code/game/mecha/combat/reticence.dm +++ b/code/game/mecha/combat/reticence.dm @@ -22,6 +22,6 @@ ..() var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/silenced ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/tool/rcd //HAHA IT MAKES WALLS GET IT + ME = new /obj/item/mecha_parts/mecha_equipment/rcd //HAHA IT MAKES WALLS GET IT ME.attach(src) return \ No newline at end of file diff --git a/code/game/mecha/equipment/mecha_equipment.dm b/code/game/mecha/equipment/mecha_equipment.dm index af4b08e73c2..d89765b5ddc 100644 --- a/code/game/mecha/equipment/mecha_equipment.dm +++ b/code/game/mecha/equipment/mecha_equipment.dm @@ -8,23 +8,14 @@ icon_state = "mecha_equip" force = 5 origin_tech = "materials=2" - var/equip_cooldown = 0 - var/equip_ready = 1 + var/equip_cooldown = 0 // cooldown after use + var/equip_ready = 1 //whether the equipment is ready for use. (or deactivated/activated for static stuff) var/energy_drain = 0 var/obj/mecha/chassis = null var/range = MELEE //bitflags reliability = 1000 var/salvageable = 1 - -/obj/item/mecha_parts/mecha_equipment/proc/do_after_cooldown(target=1) - sleep(equip_cooldown) - set_ready_state(1) - if(target && chassis && chassis.occupant) - return 1 - return 0 - - /obj/item/mecha_parts/mecha_equipment/New() ..() return @@ -42,10 +33,9 @@ return 1 return -/obj/item/mecha_parts/mecha_equipment/proc/destroy()//missiles detonating, teleporter creating singularity? +/obj/item/mecha_parts/mecha_equipment/Destroy() if(chassis) chassis.equipment -= src - listclearnulls(chassis.equipment) if(chassis.selected == src) chassis.selected = null src.update_chassis_page() @@ -55,13 +45,12 @@ chassis.occupant << sound('sound/mecha/weapdestr.ogg',volume=50) else chassis.occupant << sound('sound/mecha/critdestr.ogg',volume=50) - qdel(src) - return + chassis = null + ..() /obj/item/mecha_parts/mecha_equipment/proc/critfail() if(chassis) log_message("Critical failure",1) - return /obj/item/mecha_parts/mecha_equipment/proc/get_equip_info() if(!chassis) return @@ -90,13 +79,29 @@ /obj/item/mecha_parts/mecha_equipment/proc/action(atom/target) return -/obj/item/mecha_parts/mecha_equipment/proc/can_attach(obj/mecha/M as obj) - if(istype(M)) - if(M.equipment.len[target] will not fit into the sleeper because they are buckled to [target.buckled]!") return - if(occupant) + if(patient) occupant_message("The sleeper is already occupied!") return for(var/mob/living/simple_animal/slime/M in range(1,target)) @@ -53,81 +54,65 @@ return occupant_message("You start putting [target] into [src]...") chassis.visible_message("[chassis] starts putting [target] into \the [src].") - var/C = chassis.loc - var/T = target.loc if(do_after_cooldown(target)) - if(chassis.loc!=C || target.loc!=T) - return - if(occupant) + if(patient) occupant_message("The sleeper is already occupied!") return target.forceMove(src) - occupant = target + patient = target target.reset_view(src) - /* - if(target.client) - target.client.perspective = EYE_PERSPECTIVE - target.client.eye = chassis - */ - set_ready_state(0) - pr_mech_sleeper.start() + SSobj.processing |= src + update_equip_info() occupant_message("[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.") - return -/obj/item/mecha_parts/mecha_equipment/tool/sleeper/proc/go_out() - if(!occupant) +/obj/item/mecha_parts/mecha_equipment/sleeper/proc/go_out() + if(!patient) return - occupant.forceMove(get_turf(src)) - occupant_message("[occupant] ejected. Life support functions disabled.") - log_message("[occupant] ejected. Life support functions disabled.") - occupant.reset_view() - /* - if(occupant.client) - occupant.client.eye = occupant.client.mob - occupant.client.perspective = MOB_PERSPECTIVE - */ - occupant = null - pr_mech_sleeper.stop() - set_ready_state(1) - return + patient.forceMove(get_turf(src)) + occupant_message("[patient] ejected. Life support functions disabled.") + log_message("[patient] ejected. Life support functions disabled.") + patient.reset_view() + SSobj.processing.Remove(src) + update_equip_info() -/obj/item/mecha_parts/mecha_equipment/tool/sleeper/detach() - if(occupant) +/obj/item/mecha_parts/mecha_equipment/sleeper/detach() + if(patient) occupant_message("Unable to detach [src] - equipment occupied!") return - pr_mech_sleeper.stop() + SSobj.processing.Remove(src) + update_equip_info() return ..() -/obj/item/mecha_parts/mecha_equipment/tool/sleeper/get_equip_info() +/obj/item/mecha_parts/mecha_equipment/sleeper/get_equip_info() var/output = ..() if(output) var/temp = "" - if(occupant) - temp = "
\[Occupant: [occupant] ([occupant.stat > 1 ? "*DECEASED*" : "Health: [occupant.health]%"])\]
View stats|Eject" + if(patient) + temp = "
\[Occupant: [patient] ([patient.stat > 1 ? "*DECEASED*" : "Health: [patient.health]%"])\]
View stats|Eject" return "[output] [temp]" return -/obj/item/mecha_parts/mecha_equipment/tool/sleeper/Topic(href,href_list) +/obj/item/mecha_parts/mecha_equipment/sleeper/Topic(href,href_list) ..() var/datum/topic_input/filter = new /datum/topic_input(href,href_list) if(filter.get("eject")) go_out() if(filter.get("view_stats")) - chassis.occupant << browse(get_occupant_stats(),"window=msleeper") + chassis.occupant << browse(get_patient_stats(),"window=msleeper") onclose(chassis.occupant, "msleeper") return if(filter.get("inject")) inject_reagent(filter.getType("inject",/datum/reagent),filter.getObj("source")) return -/obj/item/mecha_parts/mecha_equipment/tool/sleeper/proc/get_occupant_stats() - if(!occupant) +/obj/item/mecha_parts/mecha_equipment/sleeper/proc/get_patient_stats() + if(!patient) return return {" - [occupant] statistics + [patient] statistics @@ -139,11 +124,11 @@

Health statistics

- [get_occupant_dam()] + [get_patient_dam()]

Reagents in bloodstream

- [get_occupant_reagents()] + [get_patient_reagents()]
[get_available_reagents()] @@ -151,9 +136,9 @@ "} -/obj/item/mecha_parts/mecha_equipment/tool/sleeper/proc/get_occupant_dam() +/obj/item/mecha_parts/mecha_equipment/sleeper/proc/get_patient_dam() var/t1 - switch(occupant.stat) + switch(patient.stat) if(0) t1 = "Conscious" if(1) @@ -162,26 +147,26 @@ t1 = "*dead*" else t1 = "Unknown" - return {"Health: [occupant.stat > 1 ? "[t1]" : "[occupant.health]% ([t1])"]
- Core Temperature: [src.occupant.bodytemperature-T0C]°C ([src.occupant.bodytemperature*1.8-459.67]°F)
- Brute Damage: [occupant.getBruteLoss()]%
- Respiratory Damage: [occupant.getOxyLoss()]%
- Toxin Content: [occupant.getToxLoss()]%
- Burn Severity: [occupant.getFireLoss()]%
- [occupant.getCloneLoss() ? "Subject appears to have cellular damage." : ""]
- [occupant.getBrainLoss() ? "Significant brain damage detected." : ""]
+ return {"Health: [patient.stat > 1 ? "[t1]" : "[patient.health]% ([t1])"]
+ Core Temperature: [patient.bodytemperature-T0C]°C ([patient.bodytemperature*1.8-459.67]°F)
+ Brute Damage: [patient.getBruteLoss()]%
+ Respiratory Damage: [patient.getOxyLoss()]%
+ Toxin Content: [patient.getToxLoss()]%
+ Burn Severity: [patient.getFireLoss()]%
+ [patient.getCloneLoss() ? "Subject appears to have cellular damage." : ""]
+ [patient.getBrainLoss() ? "Significant brain damage detected." : ""]
"} -/obj/item/mecha_parts/mecha_equipment/tool/sleeper/proc/get_occupant_reagents() - if(occupant.reagents) - for(var/datum/reagent/R in occupant.reagents.reagent_list) +/obj/item/mecha_parts/mecha_equipment/sleeper/proc/get_patient_reagents() + if(patient.reagents) + for(var/datum/reagent/R in patient.reagents.reagent_list) if(R.volume > 0) . += "[R]: [round(R.volume,0.01)]
" return . || "None" -/obj/item/mecha_parts/mecha_equipment/tool/sleeper/proc/get_available_reagents() +/obj/item/mecha_parts/mecha_equipment/sleeper/proc/get_available_reagents() var/output - var/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/SG = locate(/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun) in chassis + var/obj/item/mecha_parts/mecha_equipment/syringe_gun/SG = locate(/obj/item/mecha_parts/mecha_equipment/syringe_gun) in chassis if(SG && SG.reagents && islist(SG.reagents.reagent_list)) for(var/datum/reagent/R in SG.reagents.reagent_list) if(R.volume > 0) @@ -189,41 +174,41 @@ return output -/obj/item/mecha_parts/mecha_equipment/tool/sleeper/proc/inject_reagent(var/datum/reagent/R,var/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/SG) - if(!R || !occupant || !SG || !(SG in chassis.equipment)) +/obj/item/mecha_parts/mecha_equipment/sleeper/proc/inject_reagent(var/datum/reagent/R,var/obj/item/mecha_parts/mecha_equipment/syringe_gun/SG) + if(!R || !patient || !SG || !(SG in chassis.equipment)) return 0 var/to_inject = min(R.volume, inject_amount) - if(to_inject && occupant.reagents.get_reagent_amount(R.id) + to_inject <= inject_amount*2) - occupant_message("Injecting [occupant] with [to_inject] units of [R.name].") - log_message("Injecting [occupant] with [to_inject] units of [R.name].") - add_logs(chassis.occupant, occupant, "injected", object="[name] ([R] - [to_inject] units)") - SG.reagents.trans_id_to(occupant,R.id,to_inject) + if(to_inject && patient.reagents.get_reagent_amount(R.id) + to_inject <= inject_amount*2) + occupant_message("Injecting [patient] with [to_inject] units of [R.name].") + log_message("Injecting [patient] with [to_inject] units of [R.name].") + add_logs(chassis.occupant, patient, "injected", object="[name] ([R] - [to_inject] units)") + SG.reagents.trans_id_to(patient,R.id,to_inject) update_equip_info() return -/obj/item/mecha_parts/mecha_equipment/tool/sleeper/update_equip_info() +/obj/item/mecha_parts/mecha_equipment/sleeper/update_equip_info() if(..()) - send_byjax(chassis.occupant,"msleeper.browser","lossinfo",get_occupant_dam()) - send_byjax(chassis.occupant,"msleeper.browser","reagents",get_occupant_reagents()) + send_byjax(chassis.occupant,"msleeper.browser","lossinfo",get_patient_dam()) + send_byjax(chassis.occupant,"msleeper.browser","reagents",get_patient_reagents()) send_byjax(chassis.occupant,"msleeper.browser","injectwith",get_available_reagents()) return 1 return -/obj/item/mecha_parts/mecha_equipment/tool/sleeper/container_resist() +/obj/item/mecha_parts/mecha_equipment/sleeper/container_resist() go_out() -/datum/global_iterator/mech_sleeper -/datum/global_iterator/mech_sleeper/process(var/obj/item/mecha_parts/mecha_equipment/tool/sleeper/S) - if(!S.chassis) - S.set_ready_state(1) - return stop() - if(!S.chassis.has_charge(S.energy_drain)) - S.set_ready_state(1) - S.log_message("Deactivated.") - S.occupant_message("[src] deactivated - no power.") - return stop() - var/mob/living/carbon/M = S.occupant +/obj/item/mecha_parts/mecha_equipment/sleeper/process() + if(!chassis) + SSobj.processing.Remove(src) + return + if(!chassis.has_charge(energy_drain)) + set_ready_state(1) + log_message("Deactivated.") + occupant_message("[src] deactivated - no power.") + SSobj.processing.Remove(src) + return + var/mob/living/carbon/M = patient if(!M) return if(M.health > 0) @@ -234,11 +219,16 @@ M.AdjustStunned(-4) if(M.reagents.get_reagent_amount("epinephrine") < 5) M.reagents.add_reagent("epinephrine", 5) - S.chassis.use_power(S.energy_drain) - S.update_equip_info() - return + chassis.use_power(energy_drain) + update_equip_info() -/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun + + + +///////////////////////////////// Syringe Gun /////////////////////////////////////////////////////////////// + + +/obj/item/mecha_parts/mecha_equipment/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' @@ -251,42 +241,44 @@ var/synth_speed = 5 //[num] reagent units per cycle energy_drain = 10 var/mode = 0 //0 - fire syringe, 1 - analyze reagents. - var/datum/global_iterator/mech_synth/synth range = MELEE|RANGED equip_cooldown = 10 origin_tech = "materials=3;biotech=4;magnets=4;programming=3" -/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/New() +/obj/item/mecha_parts/mecha_equipment/syringe_gun/New() ..() flags |= NOREACT syringes = new known_reagents = list("epinephrine"="Epinephrine","charcoal"="Charcoal") processed_reagents = new create_reagents(max_volume) - synth = new (list(src),0) -/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/detach() - synth.stop() +/obj/item/mecha_parts/mecha_equipment/syringe_gun/detach() + SSobj.processing.Remove(src) return ..() -/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/critfail() +/obj/item/mecha_parts/mecha_equipment/syringe_gun/Destroy() + SSobj.processing.Remove(src) + ..() + +/obj/item/mecha_parts/mecha_equipment/syringe_gun/critfail() ..() flags &= ~NOREACT return -/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/can_attach(obj/mecha/medical/M) +/obj/item/mecha_parts/mecha_equipment/syringe_gun/can_attach(obj/mecha/medical/M) if(..()) if(istype(M)) return 1 return 0 -/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/get_equip_info() +/obj/item/mecha_parts/mecha_equipment/syringe_gun/get_equip_info() var/output = ..() if(output) return "[output] \[[mode? "Analyze" : "Launch"]\]
\[Syringes: [syringes.len]/[max_syringes] | Reagents: [reagents.total_volume]/[reagents.maximum_volume]\]
Reagents list" return -/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/action(atom/movable/target) +/obj/item/mecha_parts/mecha_equipment/syringe_gun/action(atom/movable/target) if(!action_checks(target)) return if(istype(target,/obj/item/weapon/reagent_containers/syringe)) @@ -303,8 +295,6 @@ if(reagents.total_volume<=0) occupant_message("No available reagents to load syringe with.") return - set_ready_state(0) - chassis.use_power(energy_drain) var/turf/trg = get_turf(target) var/obj/item/weapon/reagent_containers/syringe/mechsyringe = syringes[1] mechsyringe.forceMove(get_turf(chassis)) @@ -315,7 +305,7 @@ playsound(chassis, 'sound/items/syringeproj.ogg', 50, 1) log_message("Launched [mechsyringe] from [src], targeting [target].") var/mob/originaloccupant = chassis.occupant - spawn(-1) + spawn(0) src = null //if src is deleted, still process the syringe for(var/i=0, i<6, i++) if(!mechsyringe) @@ -350,11 +340,10 @@ mechsyringe.update_icon() break sleep(1) - do_after_cooldown() return 1 -/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/Topic(href,href_list) +/obj/item/mecha_parts/mecha_equipment/syringe_gun/Topic(href,href_list) ..() var/datum/topic_input/filter = new (href,href_list) if(filter.get("toggle_mode")) @@ -375,7 +364,7 @@ m++ if(processed_reagents.len) message += " added to production" - synth.start() + SSobj.processing |= src occupant_message(message) occupant_message("Reagent processing started.") log_message("Reagent processing started.") @@ -392,7 +381,7 @@ return return -/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/proc/get_reagents_page() +/obj/item/mecha_parts/mecha_equipment/syringe_gun/proc/get_reagents_page() var/output = {" Reagent Synthesizer @@ -420,7 +409,7 @@ "} return output -/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/proc/get_reagents_form() +/obj/item/mecha_parts/mecha_equipment/syringe_gun/proc/get_reagents_form() var/r_list = get_reagents_list() var/inputs if(r_list) @@ -435,14 +424,14 @@ "} return output -/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/proc/get_reagents_list() +/obj/item/mecha_parts/mecha_equipment/syringe_gun/proc/get_reagents_list() var/output for(var/i=1 to known_reagents.len) var/reagent_id = known_reagents[i] output += {" [known_reagents[reagent_id]]
"} return output -/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/proc/get_current_reagents() +/obj/item/mecha_parts/mecha_equipment/syringe_gun/proc/get_current_reagents() var/output for(var/datum/reagent/R in reagents.reagent_list) if(R.volume > 0) @@ -451,7 +440,7 @@ output += "Total: [round(reagents.total_volume,0.001)]/[reagents.maximum_volume] - Purge All" return output || "None" -/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/proc/load_syringe(obj/item/weapon/reagent_containers/syringe/S) +/obj/item/mecha_parts/mecha_equipment/syringe_gun/proc/load_syringe(obj/item/weapon/reagent_containers/syringe/S) if(syringes.len= 2) occupant_message("The syringe is too far away.") @@ -473,7 +462,7 @@ occupant_message("The [src] syringe chamber is full.") return 0 -/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/proc/analyze_reagents(atom/A) +/obj/item/mecha_parts/mecha_equipment/syringe_gun/proc/analyze_reagents(atom/A) if(get_dist(src,A) >= 4) occupant_message("The object is too far away.") return 0 @@ -488,42 +477,38 @@ occupant_message("Analyzis complete.") return 1 -/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/proc/add_known_reagent(r_id,r_name) - set_ready_state(0) - do_after_cooldown() +/obj/item/mecha_parts/mecha_equipment/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 1 return 0 -/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/update_equip_info() +/obj/item/mecha_parts/mecha_equipment/syringe_gun/update_equip_info() if(..()) send_byjax(chassis.occupant,"msyringegun.browser","reagents",get_current_reagents()) send_byjax(chassis.occupant,"msyringegun.browser","reagents_form",get_reagents_form()) return 1 return -/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/on_reagent_change() +/obj/item/mecha_parts/mecha_equipment/syringe_gun/on_reagent_change() ..() update_equip_info() return -/datum/global_iterator/mech_synth - delay = 100 -/datum/global_iterator/mech_synth/process(var/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun/S) - if(!S.chassis) - return stop() - var/energy_drain = S.energy_drain*10 - if(!S.processed_reagents.len || S.reagents.total_volume >= S.reagents.maximum_volume || !S.chassis.has_charge(energy_drain)) - S.occupant_message("Reagent processing stopped.") - S.log_message("Reagent processing stopped.") - return stop() - if(anyprob(S.reliability)) - S.critfail() - var/amount = S.synth_speed / S.processed_reagents.len - for(var/reagent in S.processed_reagents) - S.reagents.add_reagent(reagent,amount) - S.chassis.use_power(energy_drain) - return 1 +/obj/item/mecha_parts/mecha_equipment/syringe_gun/process() + if(!chassis) + SSobj.processing.Remove(src) + return + if(!processed_reagents.len || reagents.total_volume >= reagents.maximum_volume || !chassis.has_charge(energy_drain)) + occupant_message("Reagent processing stopped.") + log_message("Reagent processing stopped.") + SSobj.processing.Remove(src) + if(anyprob(reliability)) + critfail() + var/amount = synth_speed / processed_reagents.len + for(var/reagent in processed_reagents) + reagents.add_reagent(reagent,amount) + chassis.use_power(energy_drain) + diff --git a/code/game/mecha/equipment/tools/mining_tools.dm b/code/game/mecha/equipment/tools/mining_tools.dm new file mode 100644 index 00000000000..0cc2255b75f --- /dev/null +++ b/code/game/mecha/equipment/tools/mining_tools.dm @@ -0,0 +1,127 @@ + +// Drill, Diamond drill, Mining scanner + + +/obj/item/mecha_parts/mecha_equipment/drill + name = "exosuit drill" + desc = "Equipment for engineering and combat exosuits. This is the drill that'll pierce the heavens!" + icon_state = "mecha_drill" + equip_cooldown = 30 + energy_drain = 10 + force = 15 + +/obj/item/mecha_parts/mecha_equipment/drill/action(atom/target) + if(!action_checks(target)) + return + if(istype(target, /turf) && !istype(target, /turf/simulated)) + return + if(isobj(target)) + var/obj/target_obj = target + if(target_obj.unacidable) + return + target.visible_message("[chassis] starts to drill [target].", \ + "[chassis] starts to drill [target]...", \ + "You hear drilling.") + + if(do_after_cooldown(target)) + if(istype(target, /turf/simulated/wall/r_wall)) + if(istype(src , /obj/item/mecha_parts/mecha_equipment/drill/diamonddrill)) + if(do_after_cooldown(target))//To slow down how fast mechs can drill through the station + log_message("Drilled through [target]") + target.ex_act(3) + else + occupant_message("[target] is too durable to drill through.") + else if(istype(target, /turf/simulated/mineral)) + for(var/turf/simulated/mineral/M in range(chassis,1)) + if(get_dir(chassis,M)&chassis.dir) + M.gets_drilled(chassis.occupant) + log_message("Drilled through [target]") + if(locate(/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp) in chassis.equipment) + var/obj/structure/ore_box/ore_box = locate(/obj/structure/ore_box) in chassis:cargo + if(ore_box) + for(var/obj/item/weapon/ore/ore in range(chassis,1)) + if(get_dir(chassis,ore)&chassis.dir) + ore.Move(ore_box) + else if(istype(target, /turf/simulated/floor/plating/asteroid)) + for(var/turf/simulated/floor/plating/asteroid/M in range(chassis,1)) + if(get_dir(chassis,M)&chassis.dir) + M.gets_dug() + log_message("Drilled through [target]") + if(locate(/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp) in chassis.equipment) + var/obj/structure/ore_box/ore_box = locate(/obj/structure/ore_box) in chassis:cargo + if(ore_box) + for(var/obj/item/weapon/ore/ore in range(chassis,1)) + if(get_dir(chassis,ore)&chassis.dir) + ore.Move(ore_box) + else + log_message("Drilled through [target]") + if(isliving(target)) + if(istype(src , /obj/item/mecha_parts/mecha_equipment/drill/diamonddrill)) + drill_mob(target, chassis.occupant, 120) + else + drill_mob(target, chassis.occupant) + else + target.ex_act(2) + + +/obj/item/mecha_parts/mecha_equipment/drill/can_attach(obj/mecha/M as obj) + if(..()) + if(istype(M, /obj/mecha/working) || istype(M, /obj/mecha/combat)) + return 1 + return 0 + +/obj/item/mecha_parts/mecha_equipment/drill/proc/drill_mob(mob/living/target, mob/user, var/drill_damage=80) + target.visible_message("[chassis] drills [target] with [src].", \ + "[chassis] drills [target] with [src].") + add_logs(user, target, "attacked", object="[name]", addition="(INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])") + if(ishuman(target)) + var/mob/living/carbon/human/H = target + var/obj/item/organ/limb/affecting = H.get_organ("chest") + affecting.take_damage(drill_damage) + H.update_damage_overlays(0) + else + target.take_organ_damage(drill_damage) + if(target) + target.Paralyse(10) + target.updatehealth() + + + +/obj/item/mecha_parts/mecha_equipment/drill/diamonddrill + name = "diamond-tipped exosuit drill" + desc = "Equipment for engineering and combat exosuits. This is an upgraded version of the drill that'll pierce the heavens!" + icon_state = "mecha_diamond_drill" + origin_tech = "materials=4;engineering=3" + equip_cooldown = 20 + force = 15 + + + +/obj/item/mecha_parts/mecha_equipment/mining_scanner + name = "exosuit mining scanner" + desc = "Equipment for engineering and combat exosuits. It will automatically check surrounding rock for useful minerals." + icon_state = "mecha_analyzer" + origin_tech = "materials=3;engineering=2" + equip_cooldown = 30 + var/scanning = 0 + +/obj/item/mecha_parts/mecha_equipment/mining_scanner/New() + SSobj.processing |= src + +/obj/item/mecha_parts/mecha_equipment/mining_scanner/process() + if(!loc) + SSobj.processing.Remove(src) + qdel(src) + if(scanning) + return + if(istype(loc,/obj/mecha/working)) + var/obj/mecha/working/mecha = loc + if(!mecha.occupant) + return + var/list/occupant = list() + occupant |= mecha.occupant + scanning = 1 + mineral_scan_pulse(occupant,get_turf(loc)) + spawn(equip_cooldown) + scanning = 0 + diff --git a/code/game/mecha/equipment/tools/other_tools.dm b/code/game/mecha/equipment/tools/other_tools.dm new file mode 100644 index 00000000000..56e6aa089d2 --- /dev/null +++ b/code/game/mecha/equipment/tools/other_tools.dm @@ -0,0 +1,518 @@ + +// Teleporter, Wormhole generator, Gravitational catapult, Armor booster modules, +// Repair droid, Tesla Energy relay, Generators + +////////////////////////////////////////////// TELEPORTER /////////////////////////////////////////////// + +/obj/item/mecha_parts/mecha_equipment/teleporter + name = "mounted teleporter" + desc = "An exosuit module that allows exosuits to teleport to any position in view." + icon_state = "mecha_teleport" + origin_tech = "bluespace=10" + equip_cooldown = 150 + energy_drain = 1000 + range = RANGED + +/obj/item/mecha_parts/mecha_equipment/teleporter/action(atom/target) + if(!action_checks(target) || src.loc.z == ZLEVEL_CENTCOM) return + var/turf/T = get_turf(target) + if(T) + do_teleport(chassis, T, 4) + return 1 + + + +////////////////////////////////////////////// WORMHOLE GENERATOR ////////////////////////////////////////// + +/obj/item/mecha_parts/mecha_equipment/wormhole_generator + name = "mounted wormhole generator" + desc = "An exosuit module that allows generating of small quasi-stable wormholes." + icon_state = "mecha_wholegen" + origin_tech = "bluespace=3" + equip_cooldown = 50 + energy_drain = 300 + range = RANGED + + +/obj/item/mecha_parts/mecha_equipment/wormhole_generator/action(atom/target) + if(!action_checks(target) || src.loc.z == ZLEVEL_CENTCOM) return + var/list/theareas = list() + for(var/area/AR in orange(100, chassis)) + if(AR in theareas) continue + theareas += AR + if(!theareas.len) + 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.z == T.z) + 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) + if(!target_turf) + return + var/obj/effect/portal/P = new /obj/effect/portal(get_turf(target)) + P.target = target_turf + P.creator = null + P.icon = 'icons/obj/objects.dmi' + P.icon_state = "anom" + P.name = "wormhole" + var/turf/T = get_turf(target) + message_admins("[key_name_admin(chassis.occupant, chassis.occupant.client)](?) (FLW) used a Wormhole Generator in ([T.x],[T.y],[T.z] - JMP)",0,1) + log_game("[key_name(chassis.occupant)] used a Wormhole Generator in ([T.x],[T.y],[T.z])") + src = null + spawn(rand(150,300)) + qdel(P) + return 1 + + +/////////////////////////////////////// GRAVITATIONAL CATAPULT /////////////////////////////////////////// + +/obj/item/mecha_parts/mecha_equipment/gravcatapult + name = "mounted gravitational catapult" + desc = "An exosuit mounted Gravitational Catapult." + icon_state = "mecha_teleport" + origin_tech = "bluespace=2;magnets=3" + equip_cooldown = 10 + energy_drain = 100 + range = MELEE|RANGED + var/atom/movable/locked + var/mode = 1 //1 - gravsling 2 - gravpush + + +/obj/item/mecha_parts/mecha_equipment/gravcatapult/action(atom/movable/target) + if(!action_checks(target)) + return + switch(mode) + if(1) + if(!locked) + if(!istype(target) || target.anchored) + occupant_message("Unable to lock on [target]") + return + locked = target + occupant_message("Locked on [target]") + send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) + else if(target!=locked) + if(locked in view(chassis)) + locked.throw_at(target, 14, 1.5) + locked = null + send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) + return 1 + else + locked = null + occupant_message("Lock on [locked] disengaged.") + send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) + if(2) + var/list/atoms = list() + if(isturf(target)) + atoms = range(target,3) + else + atoms = orange(target,3) + for(var/atom/movable/A in atoms) + if(A.anchored) continue + spawn(0) + var/iter = 5-get_dist(A,target) + for(var/i=0 to iter) + step_away(A,target) + sleep(2) + var/turf/T = get_turf(target) + log_game("[chassis.occupant.ckey]([chassis.occupant]) used a Gravitational Catapult in ([T.x],[T.y],[T.z])") + return 1 + + +/obj/item/mecha_parts/mecha_equipment/gravcatapult/get_equip_info() + return "[..()] [mode==1?"([locked||"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.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) + return + + + + +//////////////////////////// ARMOR BOOSTER MODULES ////////////////////////////////////////////////////////// + + +/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster //what is that noise? A BAWWW from TK mutants. + name = "armor booster module (Close Combat Weaponry)" + desc = "Boosts exosuit armor against armed melee attacks. Requires energy to operate." + icon_state = "mecha_abooster_ccw" + origin_tech = "materials=3" + equip_cooldown = 10 + energy_drain = 50 + range = 0 + var/deflect_coeff = 1.15 + var/damage_coeff = 0.8 + +/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/can_attach(obj/mecha/combat/M as obj) + if(..()) + if(istype(M)) + return 1 + +/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/get_equip_info() + if(!chassis) return + return "* [src.name]" + +/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/proc/attack_react(mob/user as mob) + if(action_checks(user)) + start_cooldown() + return 1 + + + +/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster + name = "armor booster module (Ranged Weaponry)" + desc = "Boosts exosuit armor against ranged attacks. Completely blocks taser shots. Requires energy to operate." + icon_state = "mecha_abooster_proj" + origin_tech = "materials=4" + equip_cooldown = 10 + energy_drain = 50 + range = 0 + var/deflect_coeff = 1.15 + var/damage_coeff = 0.8 + +/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/can_attach(obj/mecha/combat/M as obj) + if(..()) + if(istype(M)) + return 1 + return 0 + +/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/get_equip_info() + if(!chassis) return + return "* [src.name]" + +/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/proc/projectile_react() + if(action_checks(src)) + start_cooldown() + return 1 + + +////////////////////////////////// REPAIR DROID ////////////////////////////////////////////////// + + +/obj/item/mecha_parts/mecha_equipment/repair_droid + name = "exosuit repair droid" + desc = "An automated repair droid for exosuits. Scans for damage and repairs it. Can fix almost all types of external or internal damage." + icon_state = "repair_droid" + origin_tech = "magnets=3;programming=3" + energy_drain = 50 + range = 0 + var/health_boost = 1 + var/icon/droid_overlay + var/list/repairable_damage = list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH) + +/obj/item/mecha_parts/mecha_equipment/repair_droid/Destroy() + SSobj.processing.Remove(src) + if(chassis) + chassis.overlays -= droid_overlay + ..() + +/obj/item/mecha_parts/mecha_equipment/repair_droid/attach(obj/mecha/M as obj) + ..() + droid_overlay = new(src.icon, icon_state = "repair_droid") + M.overlays += droid_overlay + +/obj/item/mecha_parts/mecha_equipment/repair_droid/detach() + chassis.overlays -= droid_overlay + SSobj.processing.Remove(src) + ..() + +/obj/item/mecha_parts/mecha_equipment/repair_droid/get_equip_info() + if(!chassis) return + return "* [src.name] - [equip_ready?"A":"Dea"]ctivate" + + +/obj/item/mecha_parts/mecha_equipment/repair_droid/Topic(href, href_list) + ..() + if(href_list["toggle_repairs"]) + chassis.overlays -= droid_overlay + if(equip_ready) + SSobj.processing |= src + droid_overlay = new(src.icon, icon_state = "repair_droid_a") + log_message("Activated.") + set_ready_state(0) + else + SSobj.processing.Remove(src) + droid_overlay = new(src.icon, icon_state = "repair_droid") + log_message("Deactivated.") + set_ready_state(1) + chassis.overlays += droid_overlay + send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) + + +/obj/item/mecha_parts/mecha_equipment/repair_droid/process() + if(!chassis) + SSobj.processing.Remove(src) + set_ready_state(1) + return + var/h_boost = health_boost + var/repaired = 0 + if(chassis.internal_damage & MECHA_INT_SHORT_CIRCUIT) + h_boost *= -2 + else if(chassis.internal_damage && prob(15)) + for(var/int_dam_flag in repairable_damage) + if(chassis.internal_damage & int_dam_flag) + chassis.clearInternalDamage(int_dam_flag) + repaired = 1 + break + if(health_boost<0 || chassis.health < initial(chassis.health)) + chassis.health += min(health_boost, initial(chassis.health)-chassis.health) + repaired = 1 + if(repaired) + if(!chassis.use_power(energy_drain)) + SSobj.processing.Remove(src) + set_ready_state(1) + else //no repair needed, we turn off + SSobj.processing.Remove(src) + set_ready_state(1) + + + + + +/////////////////////////////////// TESLA ENERGY RELAY //////////////////////////////////////////////// + +/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay + name = "exosuit energy relay" + desc = "An exosuit module that wirelessly drains energy from any available power channel in area. The performance index is quite low." + icon_state = "tesla" + origin_tech = "magnets=4;powerstorage=3" + energy_drain = 0 + range = 0 + var/coeff = 100 + var/list/use_channels = list(EQUIP,ENVIRON,LIGHT) + +/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/Destroy() + SSobj.processing.Remove(src) + ..() + +/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/detach() + SSobj.processing.Remove(src) + ..() + return + +/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/proc/get_charge() + if(equip_ready) //disabled + return + var/area/A = get_area(chassis) + var/pow_chan = get_power_channel(A) + if(pow_chan) + return 1000 //making magic + + +/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/proc/get_power_channel(var/area/A) + var/pow_chan + if(A) + for(var/c in use_channels) + if(A.master && A.master.powered(c)) + 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 + SSobj.processing |= src + set_ready_state(0) + log_message("Activated.") + else + SSobj.processing.Remove(src) + set_ready_state(1) + log_message("Deactivated.") + +/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/get_equip_info() + if(!chassis) return + return "* [src.name] - [equip_ready?"A":"Dea"]ctivate" + + +/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/process() + if(!chassis || chassis.internal_damage & MECHA_INT_SHORT_CIRCUIT) + SSobj.processing.Remove(src) + set_ready_state(1) + return + var/cur_charge = chassis.get_charge() + if(isnull(cur_charge) || !chassis.cell) + SSobj.processing.Remove(src) + set_ready_state(1) + occupant_message("No powercell detected.") + return + if(cur_charge < chassis.cell.maxcharge) + var/area/A = get_area(chassis) + if(A) + var/pow_chan + for(var/c in list(EQUIP,ENVIRON,LIGHT)) + if(A.master.powered(c)) + pow_chan = c + break + if(pow_chan) + var/delta = min(15, chassis.cell.maxcharge-cur_charge) + chassis.give_power(delta) + A.master.use_power(delta*coeff, pow_chan) + + + + +/////////////////////////////////////////// GENERATOR ///////////////////////////////////////////// + + +/obj/item/mecha_parts/mecha_equipment/generator + name = "exosuit plasma converter" + desc = "An exosuit module that generates power using solid plasma as fuel. Pollutes the environment." + icon_state = "tesla" + origin_tech = "plasmatech=2;powerstorage=2;engineering=1" + range = MELEE + 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 + reliability = 1000 + +/obj/item/mecha_parts/mecha_equipment/generator/New() + ..() + generator_init() + +/obj/item/mecha_parts/mecha_equipment/generator/Destroy() + SSobj.processing.Remove(src) + ..() + +/obj/item/mecha_parts/mecha_equipment/generator/proc/generator_init() + fuel = new /obj/item/stack/sheet/mineral/plasma(src) + fuel.amount = 0 + +/obj/item/mecha_parts/mecha_equipment/generator/detach() + SSobj.processing.Remove(src) + ..() + +/obj/item/mecha_parts/mecha_equipment/generator/Topic(href, href_list) + ..() + if(href_list["toggle"]) + if(equip_ready) //inactive + set_ready_state(0) + SSobj.processing |= src + log_message("Activated.") + else + set_ready_state(1) + SSobj.processing.Remove(src) + log_message("Deactivated.") + +/obj/item/mecha_parts/mecha_equipment/generator/get_equip_info() + var/output = ..() + if(output) + return "[output] \[[fuel]: [round(fuel.amount*fuel.perunit,0.1)] cm3\] - [equip_ready?"A":"Dea"]ctivate" + +/obj/item/mecha_parts/mecha_equipment/generator/action(target) + if(chassis) + var/result = load_fuel(target) + if(result) + send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) + +/obj/item/mecha_parts/mecha_equipment/generator/proc/load_fuel(var/obj/item/stack/sheet/P) + if(P.type == fuel.type && P.amount > 0) + var/to_load = max(max_fuel - fuel.amount*fuel.perunit,0) + if(to_load) + var/units = min(max(round(to_load / P.perunit),1),P.amount) + fuel.amount += units + P.use(units) + occupant_message("[units] unit\s of [fuel] successfully loaded.") + return units + else + occupant_message("Unit is full.") + return 0 + else + occupant_message("[fuel] traces in target minimal! [P] cannot be used as fuel.") + return + +/obj/item/mecha_parts/mecha_equipment/generator/attackby(weapon,mob/user, params) + load_fuel(weapon) + +/obj/item/mecha_parts/mecha_equipment/generator/critfail() + ..() + var/turf/simulated/T = get_turf(src) + if(!T) + return + var/datum/gas_mixture/GM = new + if(prob(10)) + GM.toxins += 100 + GM.temperature = 1500+T0C //should be enough to start a fire + T.visible_message("The [src] suddenly disgorges a cloud of heated plasma.") + qdel(src) + else + GM.toxins += 5 + GM.temperature = istype(T) ? T.air.return_temperature() : T20C + T.visible_message("The [src] suddenly disgorges a cloud of plasma.") + T.assume_air(GM) + return + +/obj/item/mecha_parts/mecha_equipment/generator/process() + if(!chassis) + SSobj.processing.Remove(src) + set_ready_state(1) + return + if(fuel.amount<=0) + SSobj.processing.Remove(src) + log_message("Deactivated - no fuel.") + set_ready_state(1) + return + if(anyprob(reliability)) + set_ready_state(1) //inactive + critfail() + SSobj.processing.Remove(src) + return + var/cur_charge = chassis.get_charge() + if(isnull(cur_charge)) + set_ready_state(1) + occupant_message("No powercell detected.") + log_message("Deactivated.") + SSobj.processing.Remove(src) + return + var/use_fuel = fuel_per_cycle_idle + if(cur_charge < chassis.cell.maxcharge) + use_fuel = fuel_per_cycle_active + chassis.give_power(power_per_cycle) + fuel.amount -= min(use_fuel/fuel.perunit,fuel.amount) + update_equip_info() + return 1 + + +/obj/item/mecha_parts/mecha_equipment/generator/nuclear + name = "exonuclear reactor" + desc = "An exosuit module that generates power using uranium as fuel. Pollutes the environment." + icon_state = "tesla" + origin_tech = "powerstorage=3;engineering=3" + max_fuel = 50000 + fuel_per_cycle_idle = 10 + fuel_per_cycle_active = 30 + power_per_cycle = 50 + var/rad_per_cycle = 0.3 + reliability = 1000 + +/obj/item/mecha_parts/mecha_equipment/generator/nuclear/generator_init() + fuel = new /obj/item/stack/sheet/mineral/uranium(src) + fuel.amount = 0 + +/obj/item/mecha_parts/mecha_equipment/generator/nuclear/critfail() + return + +/obj/item/mecha_parts/mecha_equipment/generator/nuclear/process() + if(..()) + for(var/mob/living/carbon/M in view(chassis)) + if(istype(M,/mob/living/carbon/human)) + M.irradiate(rad_per_cycle*3) + else + M.irradiate(rad_per_cycle) + + diff --git a/code/game/mecha/equipment/tools/tools.dm b/code/game/mecha/equipment/tools/tools.dm deleted file mode 100644 index 7b9a4867f03..00000000000 --- a/code/game/mecha/equipment/tools/tools.dm +++ /dev/null @@ -1,1245 +0,0 @@ -/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp - name = "hydraulic clamp" - desc = "Equipment for engineering exosuits. Lifts objects and loads them into cargo." - icon_state = "mecha_clamp" - equip_cooldown = 15 - energy_drain = 10 - var/dam_force = 20 - var/obj/mecha/working/ripley/cargo_holder - -/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp/can_attach(obj/mecha/working/ripley/M as obj) - if(..()) - if(istype(M)) - return 1 - return 0 - -/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp/attach(obj/mecha/M as obj) - ..() - cargo_holder = M - return - -/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp/detach(atom/moveto = null) - ..() - if(can_attach(chassis)) - cargo_holder = chassis - else - cargo_holder = null - -/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp/action(atom/target) - if(!action_checks(target)) - return - if(!cargo_holder) - return - if(istype(target,/obj)) - var/obj/O = target - if(!O.anchored) - if(cargo_holder.cargo.len < cargo_holder.cargo_capacity) - chassis.visible_message("[chassis] lifts [target] and starts to load it into cargo compartment.") - set_ready_state(0) - chassis.use_power(energy_drain) - O.anchored = 1 - var/T = chassis.loc - if(do_after_cooldown(target)) - if(T == chassis.loc && src == chassis.selected) - cargo_holder.cargo += O - O.loc = chassis - O.anchored = 0 - occupant_message("[target] successfully loaded.") - log_message("Loaded [O]. Cargo compartment capacity: [cargo_holder.cargo_capacity - cargo_holder.cargo.len]") - else - occupant_message("You must hold still while handling objects!") - O.anchored = initial(O.anchored) - else - occupant_message("Not enough room in cargo compartment!") - else - occupant_message("[target] is firmly secured!") - - else if(istype(target,/mob/living)) - var/mob/living/M = target - if(M.stat>1) return - if(chassis.occupant.a_intent == "harm") - M.take_overall_damage(dam_force) - if(!M) - return - M.adjustOxyLoss(round(dam_force/2)) - M.updatehealth() - target.visible_message("[chassis] squeezes [target].", \ - "[chassis] squeezes [target].",\ - "You hear something crack.") - add_logs(chassis.occupant, M, "attacked", object="[name]", addition="(INTENT: [uppertext(chassis.occupant.a_intent)]) (DAMTYE: [uppertext(damtype)])") - else - step_away(M,chassis) - occupant_message("You push [target] out of the way.") - chassis.visible_message("[chassis] pushes [target] out of the way.") - set_ready_state(0) - chassis.use_power(energy_drain) - do_after_cooldown() - return 1 - -/obj/item/mecha_parts/mecha_equipment/tool/drill - name = "exosuit drill" - desc = "Equipment for engineering and combat exosuits. This is the drill that'll pierce the heavens!" - icon_state = "mecha_drill" - equip_cooldown = 30 - energy_drain = 10 - force = 15 - -/obj/item/mecha_parts/mecha_equipment/tool/drill/action(atom/target) - if(!action_checks(target)) - return - if(istype(target, /turf) && !istype(target, /turf/simulated)) - return - if(isobj(target)) - var/obj/target_obj = target - if(target_obj.unacidable) - return - set_ready_state(0) - chassis.use_power(energy_drain) - target.visible_message("[chassis] starts to drill [target].", \ - "[chassis] starts to drill [target]...", \ - "You hear drilling.") - var/T = chassis.loc - var/C = target.loc //why are these backwards? we may never know -Pete - if(do_after_cooldown(target)) - if(T == chassis.loc && src == chassis.selected) - if(istype(target, /turf/simulated/wall/r_wall)) - if(istype(src , /obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill)) - if(do_after_cooldown(target))//To slow down how fast mechs can drill through the station - log_message("Drilled through [target]") - target.ex_act(3) - else - occupant_message("[target] is too durable to drill through.") - else if(istype(target, /turf/simulated/mineral)) - for(var/turf/simulated/mineral/M in range(chassis,1)) - if(get_dir(chassis,M)&chassis.dir) - M.gets_drilled(chassis.occupant) - log_message("Drilled through [target]") - if(locate(/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp) in chassis.equipment) - var/obj/structure/ore_box/ore_box = locate(/obj/structure/ore_box) in chassis:cargo - if(ore_box) - for(var/obj/item/weapon/ore/ore in range(chassis,1)) - if(get_dir(chassis,ore)&chassis.dir) - ore.Move(ore_box) - else if(istype(target, /turf/simulated/floor/plating/asteroid)) - for(var/turf/simulated/floor/plating/asteroid/M in range(chassis,1)) - if(get_dir(chassis,M)&chassis.dir) - M.gets_dug() - log_message("Drilled through [target]") - if(locate(/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp) in chassis.equipment) - var/obj/structure/ore_box/ore_box = locate(/obj/structure/ore_box) in chassis:cargo - if(ore_box) - for(var/obj/item/weapon/ore/ore in range(chassis,1)) - if(get_dir(chassis,ore)&chassis.dir) - ore.Move(ore_box) - else if(target.loc == C) - log_message("Drilled through [target]") - if(isliving(target)) - if(istype(src , /obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill)) - drill_mob(target, chassis.occupant, 120) - else - drill_mob(target, chassis.occupant) - else - target.ex_act(2) - return 1 - -/obj/item/mecha_parts/mecha_equipment/tool/drill/can_attach(obj/mecha/M as obj) - if(..()) - if(istype(M, /obj/mecha/working) || istype(M, /obj/mecha/combat)) - return 1 - return 0 - -/obj/item/mecha_parts/mecha_equipment/tool/drill/proc/drill_mob(mob/living/target, mob/user, var/drill_damage=80) - target.visible_message("[chassis] drills [target] with [src].", \ - "[chassis] drills [target] with [src].") - add_logs(user, target, "attacked", object="[name]", addition="(INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])") - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/limb/affecting = H.get_organ("chest") - affecting.take_damage(drill_damage) - H.update_damage_overlays(0) - else - target.take_organ_damage(drill_damage) - if(target) - target.Paralyse(10) - target.updatehealth() - -/obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill - name = "diamond-tipped exosuit drill" - desc = "Equipment for engineering and combat exosuits. This is an upgraded version of the drill that'll pierce the heavens!" - icon_state = "mecha_diamond_drill" - origin_tech = "materials=4;engineering=3" - equip_cooldown = 20 - force = 15 - - -/obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill/can_attach(obj/mecha/M as obj) - if(..()) - if(istype(M, /obj/mecha/working) || istype(M, /obj/mecha/combat)) - return 1 - return 0 - -/obj/item/mecha_parts/mecha_equipment/tool/mining_scanner - name = "exosuit mining scanner" - desc = "Equipment for engineering and combat exosuits. It will automatically check surrounding rock for useful minerals." - icon_state = "mecha_analyzer" - origin_tech = "materials=3;engineering=2" - equip_cooldown = 30 - var/scanning = 0 - -/obj/item/mecha_parts/mecha_equipment/tool/mining_scanner/New() - SSobj.processing |= src - -/obj/item/mecha_parts/mecha_equipment/tool/mining_scanner/process() - if(!loc) - SSobj.processing.Remove(src) - qdel(src) - if(scanning) - return - if(istype(loc,/obj/mecha/working)) - var/obj/mecha/working/mecha = loc - if(!mecha.occupant) - return - var/list/occupant = list() - occupant |= mecha.occupant - scanning = 1 - mineral_scan_pulse(occupant,get_turf(loc)) - spawn(equip_cooldown) - scanning = 0 - -/obj/item/mecha_parts/mecha_equipment/tool/extinguisher - name = "exosuit extinguisher" - desc = "Equipment for engineering exosuits. A rapid-firing high capacity fire extinguisher." - icon_state = "mecha_exting" - equip_cooldown = 5 - energy_drain = 0 - range = MELEE|RANGED - -/obj/item/mecha_parts/mecha_equipment/tool/extinguisher/New() - create_reagents(1000) - reagents.add_reagent("water", 1000) - ..() - return - -/obj/item/mecha_parts/mecha_equipment/tool/extinguisher/action(atom/target) //copypasted from extinguisher. TODO: Rewrite from scratch. - if(!action_checks(target) || get_dist(chassis, target)>3) return - if(get_dist(chassis, target)>2) return - set_ready_state(0) - if(do_after_cooldown(target)) - if(istype(target, /obj/structure/reagent_dispensers/watertank) && get_dist(chassis,target) <= 1) - var/obj/o = target - o.reagents.trans_to(src, 1000) - occupant_message("Extinguisher refilled.") - playsound(chassis, 'sound/effects/refill.ogg', 50, 1, -6) - else - if(src.reagents.total_volume > 0) - playsound(chassis, 'sound/effects/extinguish.ogg', 75, 1, -3) - var/direction = get_dir(chassis,target) - var/turf/T = get_turf(target) - var/turf/T1 = get_step(T,turn(direction, 90)) - var/turf/T2 = get_step(T,turn(direction, -90)) - - var/list/the_targets = list(T,T1,T2) - spawn(0) - for(var/a=0, a<5, a++) - var/obj/effect/effect/water/W = PoolOrNew(/obj/effect/effect/water, get_turf(chassis)) - if(!W) - return - var/turf/my_target = pick(the_targets) - var/datum/reagents/R = new/datum/reagents(5) - W.reagents = R - R.my_atom = W - src.reagents.trans_to(W,1) - for(var/b=0, b<4, b++) - if(!W) - return - step_towards(W,my_target) - if(!W) - return - var/turf/W_turf = get_turf(W) - W.reagents.reaction(W_turf) - for(var/atom/atm in W_turf) - W.reagents.reaction(atm) - if(W.loc == my_target) - break - sleep(2) - return 1 - -/obj/item/mecha_parts/mecha_equipment/tool/extinguisher/get_equip_info() - return "[..()] \[[src.reagents.total_volume]\]" - -/obj/item/mecha_parts/mecha_equipment/tool/extinguisher/on_reagent_change() - return - -/obj/item/mecha_parts/mecha_equipment/tool/extinguisher/can_attach(obj/mecha/working/M as obj) - if(..()) - if(istype(M)) - return 1 - return 0 - - -/obj/item/mecha_parts/mecha_equipment/tool/rcd - name = "mounted RCD" - desc = "An exosuit-mounted Rapid Construction Device." - icon_state = "mecha_rcd" - origin_tech = "materials=4;bluespace=3;magnets=4;powerstorage=4" - equip_cooldown = 10 - energy_drain = 250 - range = MELEE|RANGED - var/mode = 0 //0 - deconstruct, 1 - wall or floor, 2 - airlock. - var/disabled = 0 //malf - -/obj/item/mecha_parts/mecha_equipment/tool/rcd/action(atom/target) - if(istype(target,/area/shuttle)||istype(target, /turf/space/transit))//>implying these are ever made -Sieve - disabled = 1 - else - disabled = 0 - if(!istype(target, /turf) && !istype(target, /obj/machinery/door/airlock)) - target = get_turf(target) - if(!action_checks(target) || disabled || get_dist(chassis, target)>3) return - playsound(chassis, 'sound/machines/click.ogg', 50, 1) - //meh - switch(mode) - if(0) - if (istype(target, /turf/simulated/wall)) - var/turf/simulated/wall/W = target - occupant_message("Deconstructing [W]...") - set_ready_state(0) - if(do_after_cooldown(W)) - if(disabled) return - chassis.spark_system.start() - W.ChangeTurf(/turf/simulated/floor/plating) - playsound(W, 'sound/items/Deconstruct.ogg', 50, 1) - chassis.use_power(energy_drain) - else if (istype(target, /turf/simulated/floor)) - var/turf/simulated/floor/F = target - occupant_message("Deconstructing [F]...") - set_ready_state(0) - if(do_after_cooldown(F)) - if(disabled) return - chassis.spark_system.start() - F.ChangeTurf(F.baseturf) - playsound(F, 'sound/items/Deconstruct.ogg', 50, 1) - chassis.use_power(energy_drain) - else if (istype(target, /obj/machinery/door/airlock)) - occupant_message("Deconstructing [target]...") - set_ready_state(0) - if(do_after_cooldown(target)) - if(disabled) return - chassis.spark_system.start() - qdel(target) - playsound(target, 'sound/items/Deconstruct.ogg', 50, 1) - chassis.use_power(energy_drain) - if(1) - if(istype(target, /turf/space)) - var/turf/space/S = target - occupant_message("Building Floor...") - set_ready_state(0) - if(do_after_cooldown(S)) - if(disabled) return - S.ChangeTurf(/turf/simulated/floor/plating) - playsound(S, 'sound/items/Deconstruct.ogg', 50, 1) - chassis.spark_system.start() - chassis.use_power(energy_drain*2) - else if(istype(target, /turf/simulated/floor)) - var/turf/simulated/floor/F = target - occupant_message("Building Wall...") - set_ready_state(0) - if(do_after_cooldown(target)) - if(disabled) return - F.ChangeTurf(/turf/simulated/wall) - playsound(F, 'sound/items/Deconstruct.ogg', 50, 1) - chassis.spark_system.start() - chassis.use_power(energy_drain*2) - if(2) - if(istype(target, /turf/simulated/floor)) - occupant_message("Building Airlock...") - set_ready_state(0) - if(do_after_cooldown(target)) - if(disabled) return - chassis.spark_system.start() - var/obj/machinery/door/airlock/T = new /obj/machinery/door/airlock(target) - T.autoclose = 1 - playsound(target, 'sound/items/Deconstruct.ogg', 50, 1) - playsound(target, 'sound/effects/sparks2.ogg', 50, 1) - chassis.use_power(energy_drain*2) - return - - -/obj/item/mecha_parts/mecha_equipment/tool/rcd/Topic(href,href_list) - ..() - if(href_list["mode"]) - mode = text2num(href_list["mode"]) - switch(mode) - if(0) - occupant_message("Switched RCD to Deconstruct.") - if(1) - occupant_message("Switched RCD to Construct.") - if(2) - occupant_message("Switched RCD to Construct Airlock.") - return - -/obj/item/mecha_parts/mecha_equipment/tool/rcd/get_equip_info() - return "[..()] \[D|C|A\]" - - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer - name = "cable layer" - desc = "Equipment for engineering exosuits. Lays cable along the exosuit's path." - icon_state = "mecha_wire" - var/datum/event/event - var/turf/old_turf - var/obj/structure/cable/last_piece - var/obj/item/stack/cable_coil/cable - var/max_cable = 1000 - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/New() - cable = new(src) - cable.amount = 0 - ..() - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/can_attach(obj/mecha/working/M) - if(..()) - if(istype(M)) - return 1 - return 0 - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/attach() - ..() - event = chassis.events.addEvent("onMove",src,"layCable") - return - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/detach() - chassis.events.clearEvent("onMove",event) - return ..() - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/destroy() - chassis.events.clearEvent("onMove",event) - return ..() - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/action(var/obj/item/stack/cable_coil/target) - if(!action_checks(target)) - return - var/result = load_cable(target) - var/message - if(isnull(result)) - message = "Unable to load [target] - no cable found." - else if(!result) - message = "Reel is full." - else - message = "[result] meters of cable successfully loaded." - send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) - occupant_message(message) - return - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/Topic(href,href_list) - ..() - if(href_list["toggle"]) - set_ready_state(!equip_ready) - occupant_message("[src] [equip_ready?"dea":"a"]ctivated.") - log_message("[equip_ready?"Dea":"A"]ctivated.") - return - if(href_list["cut"]) - if(cable && cable.amount) - var/m = round(input(chassis.occupant,"Please specify the length of cable to cut","Cut cable",min(cable.amount,30)) as num, 1) - m = min(m, cable.amount) - if(m) - use_cable(m) - var/obj/item/stack/cable_coil/CC = new (get_turf(chassis)) - CC.amount = m - else - occupant_message("There's no more cable on the reel.") - return - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/get_equip_info() - var/output = ..() - if(output) - return "[output] \[Cable: [cable ? cable.amount : 0] m\][(cable && cable.amount) ? "- [!equip_ready?"Dea":"A"]ctivate|Cut" : null]" - return - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/proc/load_cable(var/obj/item/stack/cable_coil/CC) - if(istype(CC) && CC.amount) - var/cur_amount = cable? cable.amount : 0 - var/to_load = max(max_cable - cur_amount,0) - if(to_load) - to_load = min(CC.amount, to_load) - if(!cable) - cable = new(src) - cable.amount = 0 - cable.amount += to_load - CC.use(to_load) - return to_load - else - return 0 - return - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/proc/use_cable(amount) - if(!cable || cable.amount<1) - set_ready_state(1) - occupant_message("Cable depleted, [src] deactivated.") - log_message("Cable depleted, [src] deactivated.") - return - if(cable.amount < amount) - occupant_message("No enough cable to finish the task.") - return - cable.use(amount) - update_equip_info() - return 1 - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/proc/reset() - last_piece = null - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/proc/dismantleFloor(var/turf/new_turf) - if(istype(new_turf, /turf/simulated/floor)) - var/turf/simulated/floor/T = new_turf - if(!istype(T, /turf/simulated/floor/plating)) - if(!T.broken && !T.burnt) - new T.floor_tile(T) - T.make_plating() - return !new_turf.intact - -/obj/item/mecha_parts/mecha_equipment/tool/cable_layer/proc/layCable(var/turf/new_turf) - if(equip_ready || !istype(new_turf) || !dismantleFloor(new_turf)) - return reset() - var/fdirn = turn(chassis.dir,180) - for(var/obj/structure/cable/LC in new_turf) // check to make sure there's not a cable there already - if(LC.d1 == fdirn || LC.d2 == fdirn) - return reset() - if(!use_cable(1)) - return reset() - var/obj/structure/cable/NC = new(new_turf) - NC.cableColor("red") - NC.d1 = 0 - NC.d2 = fdirn - NC.updateicon() - - var/datum/powernet/PN - if(last_piece && last_piece.d2 != chassis.dir) - last_piece.d1 = min(last_piece.d2, chassis.dir) - last_piece.d2 = max(last_piece.d2, chassis.dir) - last_piece.updateicon() - PN = last_piece.powernet - - if(!PN) - PN = new() - powernets += PN - NC.powernet = PN - PN.cables += NC - NC.mergeConnectedNetworks(NC.d2) - - //NC.mergeConnectedNetworksOnTurf() - last_piece = NC - return 1 - - -/obj/item/mecha_parts/mecha_equipment/teleporter - name = "mounted teleporter" - desc = "An exosuit module that allows exosuits to teleport to any position in view." - icon_state = "mecha_teleport" - origin_tech = "bluespace=10" - equip_cooldown = 150 - energy_drain = 1000 - range = RANGED - -/obj/item/mecha_parts/mecha_equipment/teleporter/action(atom/target) - if(!action_checks(target) || src.loc.z == ZLEVEL_CENTCOM) return - var/turf/T = get_turf(target) - if(T) - set_ready_state(0) - chassis.use_power(energy_drain) - do_teleport(chassis, T, 4) - do_after_cooldown() - return - - -/obj/item/mecha_parts/mecha_equipment/wormhole_generator - name = "mounted wormhole generator" - desc = "An exosuit module that allows generating of small quasi-stable wormholes." - icon_state = "mecha_wholegen" - origin_tech = "bluespace=3" - equip_cooldown = 50 - energy_drain = 300 - range = RANGED - - -/obj/item/mecha_parts/mecha_equipment/wormhole_generator/action(atom/target) - if(!action_checks(target) || src.loc.z == ZLEVEL_CENTCOM) return - var/list/theareas = list() - for(var/area/AR in orange(100, chassis)) - if(AR in theareas) continue - theareas += AR - if(!theareas.len) - 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.z == T.z) - 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) - if(!target_turf) - return - chassis.use_power(energy_drain) - set_ready_state(0) - var/obj/effect/portal/P = new /obj/effect/portal(get_turf(target)) - P.target = target_turf - P.creator = null - P.icon = 'icons/obj/objects.dmi' - P.icon_state = "anom" - P.name = "wormhole" - var/turf/T = get_turf(target) - message_admins("[key_name_admin(chassis.occupant, chassis.occupant.client)](?) (FLW) used a Wormhole Generator in ([T.x],[T.y],[T.z] - JMP)",0,1) - log_game("[key_name(chassis.occupant)] used a Wormhole Generator in ([T.x],[T.y],[T.z])") - do_after_cooldown() - src = null - spawn(rand(150,300)) - qdel(P) - return - -/obj/item/mecha_parts/mecha_equipment/gravcatapult - name = "mounted gravitational catapult" - desc = "An exosuit mounted Gravitational Catapult." - icon_state = "mecha_teleport" - origin_tech = "bluespace=2;magnets=3" - equip_cooldown = 10 - energy_drain = 100 - range = MELEE|RANGED - var/atom/movable/locked - var/mode = 1 //1 - gravsling 2 - gravpush - - -/obj/item/mecha_parts/mecha_equipment/gravcatapult/action(atom/movable/target) - switch(mode) - if(1) - if(!action_checks(target) && !locked) return - if(!locked) - if(!istype(target) || target.anchored) - occupant_message("Unable to lock on [target]") - return - locked = target - occupant_message("Locked on [target]") - send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) - return - else if(target!=locked) - if(locked in view(chassis)) - locked.throw_at(target, 14, 1.5) - locked = null - send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) - set_ready_state(0) - chassis.use_power(energy_drain) - do_after_cooldown() - else - locked = null - occupant_message("Lock on [locked] disengaged.") - send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) - if(2) - if(!action_checks(target)) return - var/list/atoms = list() - if(isturf(target)) - atoms = range(target,3) - else - atoms = orange(target,3) - for(var/atom/movable/A in atoms) - if(A.anchored) continue - spawn(0) - var/iter = 5-get_dist(A,target) - for(var/i=0 to iter) - step_away(A,target) - sleep(2) - set_ready_state(0) - chassis.use_power(energy_drain) - var/turf/T = get_turf(target) - log_game("[chassis.occupant.ckey]([chassis.occupant]) used a Gravitational Catapult in ([T.x],[T.y],[T.z])") - do_after_cooldown() - return - -/obj/item/mecha_parts/mecha_equipment/gravcatapult/get_equip_info() - return "[..()] [mode==1?"([locked||"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.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) - return - - -/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster //what is that noise? A BAWWW from TK mutants. - name = "armor booster module (Close Combat Weaponry)" - desc = "Boosts exosuit armor against armed melee attacks. Requires energy to operate." - icon_state = "mecha_abooster_ccw" - origin_tech = "materials=3" - equip_cooldown = 10 - energy_drain = 50 - range = 0 - var/deflect_coeff = 1.15 - var/damage_coeff = 0.8 - -/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/can_attach(obj/mecha/M as obj) - if(..()) - if(!istype(M, /obj/mecha/combat/honker)) - if(!M.proc_res["dynattackby"]) - return 1 - return 0 - -/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/attach(obj/mecha/M as obj) - ..() - chassis.proc_res["dynattackby"] = src - return - -/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/detach() - chassis.proc_res["dynattackby"] = null - ..() - return - -/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/get_equip_info() - if(!chassis) return - return "* [src.name]" - -/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/proc/dynattackby(obj/item/weapon/W as obj, mob/user as mob) - if(!action_checks(user)) - return chassis.dynattackby(W,user) - chassis.log_message("Attacked by [W]. Attacker - [user]") - if(prob(chassis.deflect_chance*deflect_coeff)) - user << "The [W] bounces off [chassis] armor." - chassis.log_append_to_last("Armor saved.") - else - chassis.occupant_message("[user] hits [chassis] with [W].") - user.visible_message("[user] hits [chassis] with [W].", "You hit [src] with [W].") - chassis.take_damage(round(W.force*damage_coeff),W.damtype) - chassis.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - set_ready_state(0) - chassis.use_power(energy_drain) - do_after_cooldown() - return - - -/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster - name = "armor booster module (Ranged Weaponry)" - desc = "Boosts exosuit armor against ranged attacks. Completely blocks taser shots. Requires energy to operate." - icon_state = "mecha_abooster_proj" - origin_tech = "materials=4" - equip_cooldown = 10 - energy_drain = 50 - range = 0 - var/deflect_coeff = 1.15 - var/damage_coeff = 0.8 - -/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/can_attach(obj/mecha/M as obj) - if(..()) - if(!istype(M, /obj/mecha/combat/honker)) - if(!M.proc_res["dynbulletdamage"] && !M.proc_res["dynhitby"]) - return 1 - return 0 - -/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/attach(obj/mecha/M as obj) - ..() - chassis.proc_res["dynbulletdamage"] = src - chassis.proc_res["dynhitby"] = src - return - -/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/detach() - chassis.proc_res["dynbulletdamage"] = null - chassis.proc_res["dynhitby"] = null - ..() - return - -/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/get_equip_info() - if(!chassis) return - return "* [src.name]" - -/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/proc/dynbulletdamage(var/obj/item/projectile/Proj) - if(!action_checks(src)) - return chassis.dynbulletdamage(Proj) - if(prob(chassis.deflect_chance*deflect_coeff)) - chassis.visible_message("The [chassis.name] armor deflects the projectile") - chassis.log_append_to_last("Armor saved.") - else - chassis.take_damage(round(Proj.damage*src.damage_coeff),Proj.flag) - chassis.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - Proj.on_hit(chassis) - set_ready_state(0) - chassis.use_power(energy_drain) - do_after_cooldown() - return - -/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/proc/dynhitby(atom/movable/A) - if(!action_checks(A)) - return chassis.dynhitby(A) - if(prob(chassis.deflect_chance*deflect_coeff) || istype(A, /mob/living) || istype(A, /obj/item/mecha_parts/mecha_tracking)) - chassis.visible_message("The [A] bounces off the [chassis] armor") - chassis.log_append_to_last("Armor saved.") - if(istype(A, /mob/living)) - var/mob/living/M = A - M.take_organ_damage(10) - else if(istype(A, /obj)) - var/obj/O = A - if(O.throwforce) - chassis.take_damage(round(O.throwforce*damage_coeff)) - chassis.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - set_ready_state(0) - chassis.use_power(energy_drain) - do_after_cooldown() - return - - -/obj/item/mecha_parts/mecha_equipment/repair_droid - name = "exosuit repair droid" - desc = "An automated repair droid for exosuits. Scans for damage and repairs it. Can fix almost all types of external or internal damage." - icon_state = "repair_droid" - origin_tech = "magnets=3;programming=3" - equip_cooldown = 20 - energy_drain = 100 - range = 0 - var/health_boost = 2 - var/datum/global_iterator/pr_repair_droid - var/icon/droid_overlay - var/list/repairable_damage = list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH) - -/obj/item/mecha_parts/mecha_equipment/repair_droid/New() - ..() - pr_repair_droid = new /datum/global_iterator/mecha_repair_droid(list(src),0) - pr_repair_droid.set_delay(equip_cooldown) - return - -/obj/item/mecha_parts/mecha_equipment/repair_droid/Destroy() - qdel(pr_repair_droid) - pr_repair_droid = null - ..() - -/obj/item/mecha_parts/mecha_equipment/repair_droid/attach(obj/mecha/M as obj) - ..() - droid_overlay = new(src.icon, icon_state = "repair_droid") - M.overlays += droid_overlay - return - -/obj/item/mecha_parts/mecha_equipment/repair_droid/destroy() - chassis.overlays -= droid_overlay - ..() - return - -/obj/item/mecha_parts/mecha_equipment/repair_droid/detach() - chassis.overlays -= droid_overlay - pr_repair_droid.stop() - ..() - return - -/obj/item/mecha_parts/mecha_equipment/repair_droid/get_equip_info() - if(!chassis) return - return "* [src.name] - [pr_repair_droid.active()?"Dea":"A"]ctivate" - - -/obj/item/mecha_parts/mecha_equipment/repair_droid/Topic(href, href_list) - ..() - if(href_list["toggle_repairs"]) - chassis.overlays -= droid_overlay - if(pr_repair_droid.toggle()) - droid_overlay = new(src.icon, icon_state = "repair_droid_a") - log_message("Activated.") - else - droid_overlay = new(src.icon, icon_state = "repair_droid") - log_message("Deactivated.") - set_ready_state(1) - chassis.overlays += droid_overlay - send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) - return - - -/datum/global_iterator/mecha_repair_droid - -/datum/global_iterator/mecha_repair_droid/process(var/obj/item/mecha_parts/mecha_equipment/repair_droid/RD as obj) - if(!RD.chassis) - stop() - RD.set_ready_state(1) - return - var/health_boost = RD.health_boost - var/repaired = 0 - if(RD.chassis.hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) - health_boost *= -2 - else if(RD.chassis.hasInternalDamage() && prob(15)) - for(var/int_dam_flag in RD.repairable_damage) - if(RD.chassis.hasInternalDamage(int_dam_flag)) - RD.chassis.clearInternalDamage(int_dam_flag) - repaired = 1 - break - if(health_boost<0 || RD.chassis.health < initial(RD.chassis.health)) - RD.chassis.health += min(health_boost, initial(RD.chassis.health)-RD.chassis.health) - repaired = 1 - if(repaired) - if(RD.chassis.use_power(RD.energy_drain)) - RD.set_ready_state(0) - else - stop() - RD.set_ready_state(1) - return - else - RD.set_ready_state(1) - return - - -/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay - name = "exosuit energy relay" - desc = "An exosuit module that wirelessly drains energy from any available power channel in area. The performance index is quite low." - icon_state = "tesla" - origin_tech = "magnets=4;powerstorage=3" - equip_cooldown = 10 - energy_drain = 0 - range = 0 - var/datum/global_iterator/pr_energy_relay - var/coeff = 100 - var/list/use_channels = list(EQUIP,ENVIRON,LIGHT) - -/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/New() - ..() - pr_energy_relay = new /datum/global_iterator/mecha_energy_relay(list(src),0) - pr_energy_relay.set_delay(equip_cooldown) - return - -/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/Destroy() - qdel(pr_energy_relay) - pr_energy_relay = null - ..() - -/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/detach() - pr_energy_relay.stop() -// chassis.proc_res["dynusepower"] = null - chassis.proc_res["dyngetcharge"] = null - ..() - return - -/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/attach(obj/mecha/M) - ..() - chassis.proc_res["dyngetcharge"] = src -// chassis.proc_res["dynusepower"] = src - return - -/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/can_attach(obj/mecha/M) - if(..()) - if(!M.proc_res["dyngetcharge"])// && !M.proc_res["dynusepower"]) - return 1 - return 0 - -/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/proc/dyngetcharge() - if(equip_ready) //disabled - return chassis.dyngetcharge() - var/area/A = get_area(chassis) - var/pow_chan = get_power_channel(A) - var/charge = 0 - if(pow_chan) - charge = 1000 //making magic - else - return chassis.dyngetcharge() - return charge - -/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/proc/get_power_channel(var/area/A) - var/pow_chan - if(A) - for(var/c in use_channels) - if(A.master && A.master.powered(c)) - 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(pr_energy_relay.toggle()) - set_ready_state(0) - log_message("Activated.") - else - set_ready_state(1) - log_message("Deactivated.") - return - -/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/get_equip_info() - if(!chassis) return - return "* [src.name] - [pr_energy_relay.active()?"Dea":"A"]ctivate" - -/*/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/proc/dynusepower(amount) - if(!equip_ready) //enabled - var/area/A = get_area(chassis) - var/pow_chan = get_power_channel(A) - if(pow_chan) - A.master.use_power(amount*coeff, pow_chan) - return 1 - return chassis.dynusepower(amount)*/ - -/datum/global_iterator/mecha_energy_relay - -/datum/global_iterator/mecha_energy_relay/process(var/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/ER) - if(!ER.chassis || ER.chassis.hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) - stop() - ER.set_ready_state(1) - return - var/cur_charge = ER.chassis.get_charge() - if(isnull(cur_charge) || !ER.chassis.cell) - stop() - ER.set_ready_state(1) - ER.occupant_message("No powercell detected.") - return - if(cur_charge3\] - [pr_mech_generator.active()?"Dea":"A"]ctivate" - return - -/obj/item/mecha_parts/mecha_equipment/generator/action(target) - if(chassis) - var/result = load_fuel(target) - var/message - if(isnull(result)) - message = "[fuel] traces in target minimal! [target] cannot be used as fuel." - else if(!result) - message = "Unit is full." - else - message = "[result] unit\s of [fuel] successfully loaded." - send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) - occupant_message(message) - return - -/obj/item/mecha_parts/mecha_equipment/generator/proc/load_fuel(var/obj/item/stack/sheet/P) - if(P.type == fuel.type && P.amount) - var/to_load = max(max_fuel - fuel.amount*fuel.perunit,0) - if(to_load) - var/units = min(max(round(to_load / P.perunit),1),P.amount) - if(units) - fuel.amount += units - P.use(units) - return units - else - return 0 - return - -/obj/item/mecha_parts/mecha_equipment/generator/attackby(weapon,mob/user, params) - var/result = load_fuel(weapon) - if(isnull(result)) - user.visible_message("[user] tries to shove [weapon] into [src]. What a dumb-ass.","[fuel] traces minimal. [weapon] cannot be used as fuel.") - else if(!result) - user << "Unit is full." - else - user.visible_message("[user] loads [src] with [fuel].","[result] unit\s of [fuel] successfully loaded.") - return - -/obj/item/mecha_parts/mecha_equipment/generator/critfail() - ..() - var/turf/simulated/T = get_turf(src) - if(!T) - return - var/datum/gas_mixture/GM = new - if(prob(10)) - GM.toxins += 100 - GM.temperature = 1500+T0C //should be enough to start a fire - T.visible_message("The [src] suddenly disgorges a cloud of heated plasma.") - destroy() - else - GM.toxins += 5 - GM.temperature = istype(T) ? T.air.return_temperature() : T20C - T.visible_message("The [src] suddenly disgorges a cloud of plasma.") - T.assume_air(GM) - return - -/datum/global_iterator/mecha_generator - -/datum/global_iterator/mecha_generator/process(var/obj/item/mecha_parts/mecha_equipment/generator/EG) - if(!EG.chassis) - stop() - EG.set_ready_state(1) - return 0 - if(EG.fuel.amount<=0) - stop() - EG.log_message("Deactivated - no fuel.") - EG.set_ready_state(1) - return 0 - if(anyprob(EG.reliability)) - EG.critfail() - stop() - return 0 - var/cur_charge = EG.chassis.get_charge() - if(isnull(cur_charge)) - EG.set_ready_state(1) - EG.occupant_message("No powercell detected.") - EG.log_message("Deactivated.") - stop() - return 0 - var/use_fuel = EG.fuel_per_cycle_idle - if(cur_charge[target] successfully loaded.") - chassis.log_message("Loaded [O]. Cargo compartment capacity: [cargo_holder.cargo_capacity - cargo_holder.cargo.len]") - else - chassis.occupant_message("You must hold still while handling objects!") - O.anchored = initial(O.anchored) - else - chassis.occupant_message("Not enough room in cargo compartment!") - else - chassis.occupant_message("[target] is firmly secured!") - - else if(istype(target,/mob/living)) - var/mob/living/M = target - if(M.stat>1) return - if(chassis.occupant.a_intent == "harm") - target.visible_message("[chassis] destroys [target] in an unholy fury.", \ - "[chassis] destroys [target] in an unholy fury.") - if(chassis.occupant.a_intent == "disarm") - target.visible_message("[chassis] rips [target]'s arms off.", \ - "[chassis] rips [target]'s arms off.") - else - step_away(M,chassis) - target.visible_message("[chassis] tosses [target] like a piece of paper.") - set_ready_state(0) - chassis.use_power(energy_drain) - do_after_cooldown() - return 1 diff --git a/code/game/mecha/equipment/tools/work_tools.dm b/code/game/mecha/equipment/tools/work_tools.dm new file mode 100644 index 00000000000..515fe52c2a2 --- /dev/null +++ b/code/game/mecha/equipment/tools/work_tools.dm @@ -0,0 +1,424 @@ + +//Hydraulic clamp, Kill clamp, Extinguisher, RCD, Cable layer. + + +/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp + name = "hydraulic clamp" + desc = "Equipment for engineering exosuits. Lifts objects and loads them into cargo." + icon_state = "mecha_clamp" + equip_cooldown = 15 + energy_drain = 10 + var/dam_force = 20 + var/obj/mecha/working/ripley/cargo_holder + +/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/can_attach(obj/mecha/working/ripley/M as obj) + if(..()) + if(istype(M)) + return 1 + return 0 + +/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/attach(obj/mecha/M as obj) + ..() + cargo_holder = M + return + +/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/detach(atom/moveto = null) + ..() + cargo_holder = null + +/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/action(atom/target) + if(!action_checks(target)) + return + if(!cargo_holder) + return + if(istype(target,/obj)) + var/obj/O = target + if(!O.anchored) + if(cargo_holder.cargo.len < cargo_holder.cargo_capacity) + chassis.visible_message("[chassis] lifts [target] and starts to load it into cargo compartment.") + O.anchored = 1 + if(do_after_cooldown(target)) + cargo_holder.cargo += O + O.loc = chassis + O.anchored = 0 + occupant_message("[target] successfully loaded.") + log_message("Loaded [O]. Cargo compartment capacity: [cargo_holder.cargo_capacity - cargo_holder.cargo.len]") + else + O.anchored = initial(O.anchored) + else + occupant_message("Not enough room in cargo compartment!") + else + occupant_message("[target] is firmly secured!") + + else if(istype(target,/mob/living)) + var/mob/living/M = target + if(M.stat == DEAD) return + if(chassis.occupant.a_intent == "harm") + M.take_overall_damage(dam_force) + if(!M) + return + M.adjustOxyLoss(round(dam_force/2)) + M.updatehealth() + target.visible_message("[chassis] squeezes [target].", \ + "[chassis] squeezes [target].",\ + "You hear something crack.") + add_logs(chassis.occupant, M, "attacked", object="[name]", addition="(INTENT: [uppertext(chassis.occupant.a_intent)]) (DAMTYE: [uppertext(damtype)])") + else + step_away(M,chassis) + occupant_message("You push [target] out of the way.") + chassis.visible_message("[chassis] pushes [target] out of the way.") + return 1 + + + +//This is pretty much just for the death-ripley +/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/kill + name = "\improper KILL CLAMP" + desc = "They won't know what clamped them!" + energy_drain = 0 + +/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/kill/action(atom/target) + if(!action_checks(target)) return + if(!cargo_holder) return + if(istype(target,/obj)) + var/obj/O = target + if(!O.anchored) + if(cargo_holder.cargo.len < cargo_holder.cargo_capacity) + chassis.visible_message("[chassis] lifts [target] and starts to load it into cargo compartment.") + O.anchored = 1 + if(do_after_cooldown(target)) + cargo_holder.cargo += O + O.loc = chassis + O.anchored = 0 + occupant_message("[target] successfully loaded.") + log_message("Loaded [O]. Cargo compartment capacity: [cargo_holder.cargo_capacity - cargo_holder.cargo.len]") + else + O.anchored = initial(O.anchored) + else + occupant_message("Not enough room in cargo compartment!") + else + occupant_message("[target] is firmly secured!") + + else if(istype(target,/mob/living)) + var/mob/living/M = target + if(M.stat == DEAD) return + if(chassis.occupant.a_intent == "harm") + target.visible_message("[chassis] destroys [target] in an unholy fury.", \ + "[chassis] destroys [target] in an unholy fury.") + if(chassis.occupant.a_intent == "disarm") + target.visible_message("[chassis] rips [target]'s arms off.", \ + "[chassis] rips [target]'s arms off.") + else + step_away(M,chassis) + target.visible_message("[chassis] tosses [target] like a piece of paper.") + return 1 + + + +/obj/item/mecha_parts/mecha_equipment/extinguisher + name = "exosuit extinguisher" + desc = "Equipment for engineering exosuits. A rapid-firing high capacity fire extinguisher." + icon_state = "mecha_exting" + equip_cooldown = 5 + energy_drain = 0 + range = MELEE|RANGED + +/obj/item/mecha_parts/mecha_equipment/extinguisher/New() + create_reagents(1000) + reagents.add_reagent("water", 1000) + ..() + return + +/obj/item/mecha_parts/mecha_equipment/extinguisher/action(atom/target) //copypasted from extinguisher. TODO: Rewrite from scratch. + if(!action_checks(target) || get_dist(chassis, target)>3) + return + + if(istype(target, /obj/structure/reagent_dispensers/watertank) && get_dist(chassis,target) <= 1) + var/obj/structure/reagent_dispensers/watertank/WT = target + WT.reagents.trans_to(src, 1000) + occupant_message("Extinguisher refilled.") + playsound(chassis, 'sound/effects/refill.ogg', 50, 1, -6) + else + if(reagents.total_volume > 0) + playsound(chassis, 'sound/effects/extinguish.ogg', 75, 1, -3) + var/direction = get_dir(chassis,target) + var/turf/T = get_turf(target) + var/turf/T1 = get_step(T,turn(direction, 90)) + var/turf/T2 = get_step(T,turn(direction, -90)) + + var/list/the_targets = list(T,T1,T2) + spawn(0) + for(var/a=0, a<5, a++) + var/obj/effect/effect/water/W = PoolOrNew(/obj/effect/effect/water, get_turf(chassis)) + if(!W) + return + var/turf/my_target = pick(the_targets) + var/datum/reagents/R = new/datum/reagents(5) + W.reagents = R + R.my_atom = W + reagents.trans_to(W,1) + for(var/b=0, b<4, b++) + if(!W) + return + step_towards(W,my_target) + if(!W) + return + var/turf/W_turf = get_turf(W) + W.reagents.reaction(W_turf) + for(var/atom/atm in W_turf) + W.reagents.reaction(atm) + if(W.loc == my_target) + break + sleep(2) + return 1 + +/obj/item/mecha_parts/mecha_equipment/extinguisher/get_equip_info() + return "[..()] \[[src.reagents.total_volume]\]" + +/obj/item/mecha_parts/mecha_equipment/extinguisher/on_reagent_change() + return + +/obj/item/mecha_parts/mecha_equipment/extinguisher/can_attach(obj/mecha/working/M as obj) + if(..()) + if(istype(M)) + return 1 + return 0 + + + +/obj/item/mecha_parts/mecha_equipment/rcd + name = "mounted RCD" + desc = "An exosuit-mounted Rapid Construction Device." + icon_state = "mecha_rcd" + origin_tech = "materials=4;bluespace=3;magnets=4;powerstorage=4" + equip_cooldown = 10 + energy_drain = 250 + range = MELEE|RANGED + var/mode = 0 //0 - deconstruct, 1 - wall or floor, 2 - airlock. + var/disabled = 0 //malf + +/obj/item/mecha_parts/mecha_equipment/rcd/action(atom/target) + if(istype(target,/area/shuttle)||istype(target, /turf/space/transit))//>implying these are ever made -Sieve + disabled = 1 + else + disabled = 0 + if(!istype(target, /turf) && !istype(target, /obj/machinery/door/airlock)) + target = get_turf(target) + if(!action_checks(target) || disabled || get_dist(chassis, target)>3) + return + playsound(chassis, 'sound/machines/click.ogg', 50, 1) + + switch(mode) + if(0) + if (istype(target, /turf/simulated/wall)) + var/turf/simulated/wall/W = target + occupant_message("Deconstructing [W]...") + if(do_after_cooldown(W)) + chassis.spark_system.start() + W.ChangeTurf(/turf/simulated/floor/plating) + playsound(W, 'sound/items/Deconstruct.ogg', 50, 1) + else if (istype(target, /turf/simulated/floor)) + var/turf/simulated/floor/F = target + occupant_message("Deconstructing [F]...") + if(do_after_cooldown(target)) + chassis.spark_system.start() + F.ChangeTurf(F.baseturf) + playsound(F, 'sound/items/Deconstruct.ogg', 50, 1) + else if (istype(target, /obj/machinery/door/airlock)) + occupant_message("Deconstructing [target]...") + if(do_after_cooldown(target)) + chassis.spark_system.start() + qdel(target) + playsound(target, 'sound/items/Deconstruct.ogg', 50, 1) + if(1) + if(istype(target, /turf/space)) + var/turf/space/S = target + occupant_message("Building Floor...") + if(do_after_cooldown(S)) + S.ChangeTurf(/turf/simulated/floor/plating) + playsound(S, 'sound/items/Deconstruct.ogg', 50, 1) + chassis.spark_system.start() + else if(istype(target, /turf/simulated/floor)) + var/turf/simulated/floor/F = target + occupant_message("Building Wall...") + if(do_after_cooldown(F)) + if(disabled) return + F.ChangeTurf(/turf/simulated/wall) + playsound(F, 'sound/items/Deconstruct.ogg', 50, 1) + chassis.spark_system.start() + if(2) + if(istype(target, /turf/simulated/floor)) + occupant_message("Building Airlock...") + if(do_after_cooldown(target)) + chassis.spark_system.start() + var/obj/machinery/door/airlock/T = new /obj/machinery/door/airlock(target) + T.autoclose = 1 + playsound(target, 'sound/items/Deconstruct.ogg', 50, 1) + playsound(target, 'sound/effects/sparks2.ogg', 50, 1) + + + +/obj/item/mecha_parts/mecha_equipment/rcd/do_after_cooldown(var/atom/target) + . = ..() + if(disabled) + return 0 + +/obj/item/mecha_parts/mecha_equipment/rcd/Topic(href,href_list) + ..() + if(href_list["mode"]) + mode = text2num(href_list["mode"]) + switch(mode) + if(0) + occupant_message("Switched RCD to Deconstruct.") + energy_drain = initial(energy_drain) + if(1) + occupant_message("Switched RCD to Construct.") + energy_drain = 2*initial(energy_drain) + if(2) + occupant_message("Switched RCD to Construct Airlock.") + energy_drain = 2*initial(energy_drain) + return + +/obj/item/mecha_parts/mecha_equipment/rcd/get_equip_info() + return "[..()] \[D|C|A\]" + + + + +/obj/item/mecha_parts/mecha_equipment/cable_layer + name = "cable layer" + desc = "Equipment for engineering exosuits. Lays cable along the exosuit's path." + icon_state = "mecha_wire" + var/datum/event/event + var/turf/old_turf + var/obj/structure/cable/last_piece + var/obj/item/stack/cable_coil/cable + var/max_cable = 1000 + +/obj/item/mecha_parts/mecha_equipment/cable_layer/New() + cable = new(src) + cable.amount = 0 + ..() + +/obj/item/mecha_parts/mecha_equipment/cable_layer/can_attach(obj/mecha/working/M) + if(..()) + if(istype(M)) + return 1 + return 0 + +/obj/item/mecha_parts/mecha_equipment/cable_layer/attach() + ..() + event = chassis.events.addEvent("onMove",src,"layCable") + return + +/obj/item/mecha_parts/mecha_equipment/cable_layer/detach() + chassis.events.clearEvent("onMove",event) + return ..() + +/obj/item/mecha_parts/mecha_equipment/cable_layer/Destroy() + chassis.events.clearEvent("onMove",event) + ..() + +/obj/item/mecha_parts/mecha_equipment/cable_layer/action(var/obj/item/stack/cable_coil/target) + if(!action_checks(target)) + return + if(istype(target) && target.amount) + var/cur_amount = cable? cable.amount : 0 + var/to_load = max(max_cable - cur_amount,0) + if(to_load) + to_load = min(target.amount, to_load) + if(!cable) + cable = new(src) + cable.amount = 0 + cable.amount += to_load + target.use(to_load) + occupant_message("[to_load] meters of cable successfully loaded.") + send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) + else + occupant_message("Reel is full.") + else + occupant_message("Unable to load [target] - no cable found.") + + +/obj/item/mecha_parts/mecha_equipment/cable_layer/Topic(href,href_list) + ..() + if(href_list["toggle"]) + set_ready_state(!equip_ready) + occupant_message("[src] [equip_ready?"dea":"a"]ctivated.") + log_message("[equip_ready?"Dea":"A"]ctivated.") + return + if(href_list["cut"]) + if(cable && cable.amount) + var/m = round(input(chassis.occupant,"Please specify the length of cable to cut","Cut cable",min(cable.amount,30)) as num, 1) + m = min(m, cable.amount) + if(m) + use_cable(m) + var/obj/item/stack/cable_coil/CC = new (get_turf(chassis)) + CC.amount = m + else + occupant_message("There's no more cable on the reel.") + return + +/obj/item/mecha_parts/mecha_equipment/cable_layer/get_equip_info() + var/output = ..() + if(output) + return "[output] \[Cable: [cable ? cable.amount : 0] m\][(cable && cable.amount) ? "- [!equip_ready?"Dea":"A"]ctivate|Cut" : null]" + return + +/obj/item/mecha_parts/mecha_equipment/cable_layer/proc/use_cable(amount) + if(!cable || cable.amount<1) + set_ready_state(1) + occupant_message("Cable depleted, [src] deactivated.") + log_message("Cable depleted, [src] deactivated.") + return + if(cable.amount < amount) + occupant_message("No enough cable to finish the task.") + return + cable.use(amount) + update_equip_info() + return 1 + +/obj/item/mecha_parts/mecha_equipment/cable_layer/proc/reset() + last_piece = null + +/obj/item/mecha_parts/mecha_equipment/cable_layer/proc/dismantleFloor(var/turf/new_turf) + if(istype(new_turf, /turf/simulated/floor)) + var/turf/simulated/floor/T = new_turf + if(!istype(T, /turf/simulated/floor/plating)) + if(!T.broken && !T.burnt) + new T.floor_tile(T) + T.make_plating() + return !new_turf.intact + +/obj/item/mecha_parts/mecha_equipment/cable_layer/proc/layCable(var/turf/new_turf) + if(equip_ready || !istype(new_turf) || !dismantleFloor(new_turf)) + return reset() + var/fdirn = turn(chassis.dir,180) + for(var/obj/structure/cable/LC in new_turf) // check to make sure there's not a cable there already + if(LC.d1 == fdirn || LC.d2 == fdirn) + return reset() + if(!use_cable(1)) + return reset() + var/obj/structure/cable/NC = new(new_turf) + NC.cableColor("red") + NC.d1 = 0 + NC.d2 = fdirn + NC.updateicon() + + var/datum/powernet/PN + if(last_piece && last_piece.d2 != chassis.dir) + last_piece.d1 = min(last_piece.d2, chassis.dir) + last_piece.d2 = max(last_piece.d2, chassis.dir) + last_piece.updateicon() + PN = last_piece.powernet + + if(!PN) + PN = new() + powernets += PN + NC.powernet = PN + PN.cables += NC + NC.mergeConnectedNetworks(NC.d2) + + //NC.mergeConnectedNetworksOnTurf() + last_piece = NC + return 1 diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm index 0414658025d..266a0061be6 100644 --- a/code/game/mecha/equipment/weapons/weapons.dm +++ b/code/game/mecha/equipment/weapons/weapons.dm @@ -20,7 +20,6 @@ /obj/item/mecha_parts/mecha_equipment/weapon/action(atom/target) if(!action_checks(target)) return 0 - set_ready_state(0) var/turf/curloc = get_turf(chassis) var/turf/targloc = get_turf(target) @@ -29,6 +28,10 @@ if (targloc == curloc) return 0 + set_ready_state(0) + chassis.can_move = 0 + spawn(shot_delay*projectiles_per_shot) + chassis.can_move = 1 for(var/i=1 to get_shot_amount()) var/obj/item/projectile/A = new projectile(curloc) A.firer = chassis.occupant @@ -49,9 +52,9 @@ sleep(shot_delay) chassis.log_message("Fired from [src.name], targeting [target].") - do_after_cooldown() return 1 + //Base energy weapon type /obj/item/mecha_parts/mecha_equipment/weapon/energy name = "general energy weapon" @@ -59,10 +62,11 @@ /obj/item/mecha_parts/mecha_equipment/weapon/energy/get_shot_amount() return min(round(chassis.cell.charge / energy_drain), projectiles_per_shot) -/obj/item/mecha_parts/mecha_equipment/weapon/energy/action(atom/target) - ..() - chassis.use_power(energy_drain * get_shot_amount()) - +/obj/item/mecha_parts/mecha_equipment/weapon/energy/start_cooldown() + set_ready_state(0) + chassis.use_power(energy_drain*get_shot_amount()) + sleep(equip_cooldown) + set_ready_state(1) /obj/item/mecha_parts/mecha_equipment/weapon/energy/laser equip_cooldown = 8 @@ -112,9 +116,9 @@ projectile = /obj/item/projectile/plasma/adv/mech fire_sound = 'sound/weapons/Laser.ogg' -/obj/item/mecha_parts/mecha_equipment/weapon/energy/plasma/can_attach(obj/mecha/M as obj) - if(istype(M, /obj/mecha/working)) - if(M.equipment.len < M.max_equip) +/obj/item/mecha_parts/mecha_equipment/weapon/energy/plasma/can_attach(obj/mecha/working/M as obj) + if(..()) + if(istype(M)) return 1 return 0 @@ -158,8 +162,8 @@ return 0 /obj/item/mecha_parts/mecha_equipment/weapon/honker/action(target) - if(!action_checks(target)) return 0 - set_ready_state(0) + if(!action_checks(target)) + return playsound(chassis, 'sound/items/AirHorn.ogg', 100, 1) chassis.occupant_message("HONK") for(var/mob/living/carbon/M in ohearers(6, chassis)) @@ -177,24 +181,12 @@ M.Paralyse(4) else M.Jitter(500) - /* //else the mousetraps are useless - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - if(isobj(H.shoes)) - var/thingy = H.shoes - H.unEquip(H.shoes) - walk_away(thingy,chassis,15,2) - spawn(20) - if(thingy) - walk(thingy,0) - */ - chassis.use_power(energy_drain) + log_message("Honked from [src.name]. HONK!") var/turf/T = get_turf(src) message_admins("[key_name_admin(chassis.occupant, chassis.occupant.client)](?) (FLW) used a Mecha Honker in ([T.x],[T.y],[T.z] - JMP)",0,1) log_game("[chassis.occupant.ckey]([chassis.occupant]) used a Mecha Honker in ([T.x],[T.y],[T.z])") - do_after_cooldown() - return + return 1 //Base ballistic weapon type @@ -212,8 +204,6 @@ return 0 if(projectiles <= 0) return 0 - if(!equip_ready) - return 0 return 1 /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/get_equip_info() @@ -238,8 +228,9 @@ /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/action(atom/target) if(..()) - src.projectiles -= get_shot_amount() + projectiles -= get_shot_amount() send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) + return 1 /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/carbine @@ -284,7 +275,27 @@ deviation = 0.3 shot_delay = 2 -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher + var/missile_speed = 2 + var/missile_range = 30 + +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/action(target) + if(!action_checks(target)) + return + var/obj/O = new projectile(chassis.loc) + playsound(chassis, fire_sound, 50, 1) + log_message("Launched a [O.name] from [name], targeting [target].") + projectiles-- + proj_init(O) + spawn(0) + O.throw_at(target, missile_range, missile_speed, spin = 0) + return 1 + +//used for projectile initilisation (priming flashbang) and additional logging +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/proc/proj_init(var/obj/O) + return + +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/missile_rack name = "\improper SRM-8 missile rack" desc = "A weapon for combat exosuits. Shoots light explosive missiles." icon_state = "mecha_missilerack" @@ -293,24 +304,12 @@ projectiles = 8 projectile_energy_cost = 1000 equip_cooldown = 60 - var/missile_speed = 2 - var/missile_range = 30 -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/action(target) - if(!action_checks(target)) return - set_ready_state(0) - var/obj/item/missile/M = new projectile(chassis.loc) +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/missile_rack/proj_init(var/obj/item/missile/M) M.primed = 1 - playsound(chassis, fire_sound, 50, 1) - M.throw_at(target, missile_range, missile_speed) - projectiles-- - log_message("Fired from [src.name], targeting [target].") var/turf/T = get_turf(src) message_admins("[key_name(chassis.occupant, chassis.occupant.client)](?) fired a [src] in ([T.x],[T.y],[T.z] - JMP)",0,1) log_game("[key_name(chassis.occupant)] fired a [src] ([T.x],[T.y],[T.z])") - do_after_cooldown() - return - /obj/item/missile icon = 'icons/obj/grenade.dmi' @@ -324,9 +323,8 @@ qdel(src) else ..() - return -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang name = "\improper SGL-6 grenade launcher" desc = "A weapon for combat exosuits. Launches primed flashbangs." icon_state = "mecha_grenadelnchr" @@ -338,24 +336,16 @@ equip_cooldown = 60 var/det_time = 20 -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang/action(target) - if(!action_checks(target)) return - set_ready_state(0) - var/obj/item/weapon/grenade/flashbang/F = new projectile(chassis.loc) - playsound(chassis, fire_sound, 50, 1) - F.throw_at(target, missile_range, missile_speed) - projectiles-- - log_message("Fired from [src.name], targeting [target].") +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang/proj_init(var/obj/item/weapon/grenade/flashbang/F) var/turf/T = get_turf(src) - message_admins("[key_name(chassis.occupant, chassis.occupant.client)](?) (FLW) fired a [src] in ([T.x],[T.y],[T.z] - JMP)",0,1) + message_admins("[key_name(chassis.occupant, chassis.occupant.client)](?) fired a [src] in ([T.x],[T.y],[T.z] - JMP)",0,1) log_game("[key_name(chassis.occupant)] fired a [src] ([T.x],[T.y],[T.z])") spawn(det_time) if(F) F.prime() - do_after_cooldown() - return -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang/clusterbang //Because I am a heartless bastard -Sieve //Heartless? for making the poor man's honkblast? - Kaze + +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang/clusterbang //Because I am a heartless bastard -Sieve //Heartless? for making the poor man's honkblast? - Kaze name = "\improper SOB-3 grenade launcher" desc = "A weapon for combat exosuits. Launches primed clusterbangs. You monster." projectiles = 3 @@ -363,7 +353,7 @@ projectile_energy_cost = 1600 //getting off cheap seeing as this is 3 times the flashbangs held in the grenade launcher. equip_cooldown = 90 -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/banana_mortar +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/banana_mortar name = "banana mortar" desc = "Equipment for clown exosuits. Launches banana peels." icon_state = "mecha_bananamrtr" @@ -374,25 +364,13 @@ projectile_energy_cost = 100 equip_cooldown = 20 -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/banana_mortar/can_attach(obj/mecha/combat/honker/M as obj) +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/banana_mortar/can_attach(obj/mecha/combat/honker/M as obj) if(..()) if(istype(M)) return 1 return 0 -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/banana_mortar/action(target) - if(!action_checks(target)) return - set_ready_state(0) - var/obj/item/weapon/grown/bananapeel/B = new projectile(chassis.loc,60) - playsound(chassis, fire_sound, 60, 1) - B.throw_at(target, missile_range, missile_speed) - projectiles-- - log_message("Bananed from [src.name], targeting [target]. HONK!") - do_after_cooldown() - return - - -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/mousetrap_mortar +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/mousetrap_mortar name = "mousetrap mortar" desc = "Equipment for clown exosuits. Launches armed mousetraps." icon_state = "mecha_mousetrapmrtr" @@ -403,20 +381,12 @@ projectile_energy_cost = 100 equip_cooldown = 10 -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/mousetrap_mortar/can_attach(obj/mecha/combat/honker/M as obj) +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/mousetrap_mortar/can_attach(obj/mecha/combat/honker/M as obj) if(..()) if(istype(M)) return 1 return 0 -/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/mousetrap_mortar/action(target) - if(!action_checks(target)) return - set_ready_state(0) - var/obj/item/device/assembly/mousetrap/armed/M = new projectile(chassis.loc) +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/mousetrap_mortar/proj_init(var/obj/item/device/assembly/mousetrap/armed/M) M.secured = 1 - playsound(chassis, fire_sound, 60, 1) - M.throw_at(target, missile_range, missile_speed) - projectiles-- - log_message("Launched a mouse-trap from [src.name], targeting [target]. HONK!") - do_after_cooldown() - return + diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 48f34a6faaf..35512a1429f 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -57,10 +57,6 @@ var/list/operation_req_access = list()//required access level for mecha operation var/list/internals_req_access = list(access_engine,access_robotics)//required access level to open cell compartment - var/datum/global_iterator/pr_int_temp_processor //normalizes internal air mixture temperature - var/datum/global_iterator/pr_give_air //moves air from tank to cabin - var/datum/global_iterator/pr_internal_damage //processes internal damage - var/wreckage var/list/equipment = new @@ -74,6 +70,12 @@ var/melee_cooldown = 10 var/melee_can_hit = 1 + var/datum/action/mecha/mech_eject/eject_action = new + var/datum/action/mecha/mech_toggle_internals/internals_action = new + var/datum/action/mecha/mech_cycle_equip/cycle_action = new + var/datum/action/mecha/mech_toggle_lights/lights_action = new + var/datum/action/mecha/mech_view_stats/stats_action = new + /obj/mecha/New() ..() @@ -81,14 +83,11 @@ icon_state += "-open" add_radio() add_cabin() - if(!add_airtank()) //we check this here in case mecha does not have an internal tank available by default - WIP - removeVerb(/obj/mecha/verb/connect_to_port) - removeVerb(/obj/mecha/verb/toggle_internal_tank) + add_airtank() spark_system.set_up(2, 0, src) spark_system.attach(src) add_cell() - add_iterators() - removeVerb(/obj/mecha/verb/disconnect_from_port) + SSobj.processing |= src log_message("[src.name] created.") mechas_list += src //global mech list return @@ -114,7 +113,7 @@ E.reliability = round(rand(E.reliability/3,E.reliability)) else E.detach(loc) - E.destroy() + qdel(E) if(cell) WR.crowbar_salvage += cell cell.forceMove(WR) @@ -125,22 +124,16 @@ else for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) E.detach(loc) - E.destroy() + qdel(E) if(cell) qdel(cell) if(internal_tank) qdel(internal_tank) + SSobj.processing.Remove(src) equipment.Cut() cell = null internal_tank = null - - qdel(pr_int_temp_processor) - qdel(pr_give_air) - qdel(pr_internal_damage) qdel(spark_system) - pr_int_temp_processor = null - pr_give_air = null - pr_internal_damage = null spark_system = null mechas_list -= src //global mech list @@ -150,12 +143,6 @@ ////// Helpers ///////// //////////////////////// -/obj/mecha/proc/removeVerb(verb_path) - verbs -= verb_path - -/obj/mecha/proc/addVerb(verb_path) - verbs += verb_path - /obj/mecha/proc/add_airtank() internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src) return internal_tank @@ -184,37 +171,16 @@ radio.icon_state = icon_state radio.subspace_transmission = 1 -/obj/mecha/proc/add_iterators() - pr_int_temp_processor = new /datum/global_iterator/mecha_preserve_temp(list(src)) - pr_give_air = new /datum/global_iterator/mecha_tank_give_air(list(src)) - pr_internal_damage = new /datum/global_iterator/mecha_internal_damage(list(src),0) - -/obj/mecha/proc/do_after(delay as num) - sleep(delay) - if(src) - return 1 - return 0 - -/obj/mecha/proc/enter_after(delay as num, var/mob/user as mob, var/numticks = 5) - var/delayfraction = delay/numticks - - var/turf/T = user.loc - - for(var/i = 0, i internal_tank.maximum_pressure && !(internal_damage & MECHA_INT_TANK_BREACH)) + setInternalDamage(MECHA_INT_TANK_BREACH) + var/datum/gas_mixture/int_tank_air = internal_tank.return_air() + if(int_tank_air && int_tank_air.return_volume()>0) //heat the air_contents + int_tank_air.temperature = min(6000+T0C, int_tank_air.temperature+rand(10,15)) + if(cabin_air && cabin_air.return_volume()>0) + cabin_air.temperature = min(6000+T0C, cabin_air.return_temperature()+rand(10,15)) + if(cabin_air.return_temperature() > max_temperature/2) + take_damage(4/round(max_temperature/cabin_air.return_temperature(),0.1),"fire") + + if(internal_damage & MECHA_INT_TEMP_CONTROL) + internal_temp_regulation = 0 + + if(internal_damage & MECHA_INT_TANK_BREACH) //remove some air from internal tank + if(internal_tank) + var/datum/gas_mixture/int_tank_air = internal_tank.return_air() + var/datum/gas_mixture/leaked_gas = int_tank_air.remove_ratio(0.10) + if(loc) + loc.assume_air(leaked_gas) + else + del(leaked_gas) + + 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) + + if(internal_temp_regulation) + if(cabin_air && cabin_air.return_volume() > 0) + var/delta = cabin_air.temperature - T20C + cabin_air.temperature -= max(-10, min(10, round(delta/4,0.1))) + + if(internal_tank) + var/datum/gas_mixture/tank_air = internal_tank.return_air() + + var/release_pressure = internal_tank_valve + var/cabin_pressure = cabin_air.return_pressure() + var/pressure_delta = min(release_pressure - cabin_pressure, (tank_air.return_pressure() - cabin_pressure)/2) + var/transfer_moles = 0 + if(pressure_delta > 0) //cabin pressure lower than release pressure + if(tank_air.return_temperature() > 0) + transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) + var/datum/gas_mixture/removed = tank_air.remove(transfer_moles) + cabin_air.merge(removed) + else if(pressure_delta < 0) //cabin pressure higher than release pressure + var/datum/gas_mixture/t_air = return_air() + pressure_delta = cabin_pressure - release_pressure + if(t_air) + pressure_delta = min(cabin_pressure - t_air.return_pressure(), pressure_delta) + if(pressure_delta > 0) //if location pressure is lower than cabin pressure + transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) + var/datum/gas_mixture/removed = cabin_air.remove(transfer_moles) + if(t_air) + t_air.merge(removed) + else //just delete the cabin gas, we're in space or some shit + del(removed) + + if(occupant) + if(cell) + var/cellcharge = cell.charge/cell.maxcharge + switch(cellcharge) + if(0.75 to INFINITY) + occupant.clear_alert("charge") + if(0.5 to 0.75) + occupant.throw_alert("charge","lowcell",1) + if(0.25 to 0.5) + occupant.throw_alert("charge","lowcell",2) + if(0.01 to 0.25) + occupant.throw_alert("charge","lowcell",3) + else + occupant.throw_alert("charge","emptycell") + + var/integrity = health/initial(health)*100 + switch(integrity) + if(30 to 45) + occupant.throw_alert("mech damage", "low_mech_integrity", 1) + if(15 to 35) + occupant.throw_alert("mech damage", "low_mech_integrity", 2) + if(-INFINITY to 15) + occupant.throw_alert("mech damage", "low_mech_integrity", 3) + else + occupant.clear_alert("mech damage") + + if(occupant.loc != src) //something went wrong + occupant.clear_alert("charge") + occupant.clear_alert("mech damage") + RemoveActions(occupant, human_occupant=1) + occupant = null + + if(lights) + var/lights_energy_drain = 2 + use_power(lights_energy_drain) + + + + /obj/mecha/proc/drop_item()//Derpfix, but may be useful in future for engineering exosuits. return @@ -247,32 +321,11 @@ //////////////////////////// ///// Action processing //// //////////////////////////// -/* -/atom/DblClick(object,location,control,params) - var/mob/M = src.mob - if(M && M.in_contents_of(/obj/mecha)) - if(mech_click == world.time) return - mech_click = world.time - - if(!istype(object, /atom)) return - if(istype(object, /obj/screen)) - var/obj/screen/using = object - if(using.screen_loc == ui_acti || using.screen_loc == ui_iarrowleft || using.screen_loc == ui_iarrowright)//ignore all HUD objects save 'intent' and its arrows - return ..() - else - return - var/obj/mecha/Mech = M.loc - spawn() //this helps prevent clickspam fest. - if (Mech) - Mech.click_action(object,M) -// else -// return ..() -*/ /obj/mecha/proc/click_action(atom/target,mob/user) - if(!src.occupant || src.occupant != user ) return - if(user.stat || !user.canmove) + if(!occupant || occupant != user ) return + if(user.incapacitated()) return if(state) occupant_message("Maintenance protocols in effect.") @@ -284,49 +337,28 @@ var/dir_to_target = get_dir(src,target) if(dir_to_target && !(dir_to_target & src.dir))//wrong direction return - if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) + if(internal_damage & MECHA_INT_CONTROL_LOST) target = safepick(view(3,target)) if(!target) return if(!target.Adjacent(src)) if(selected && selected.is_ranged()) - selected.action(target) + if(selected.action(target)) + selected.start_cooldown() else if(selected && selected.is_melee()) - selected.action(target) + if(selected.action(target)) + selected.start_cooldown() else - if(internal_damage&MECHA_INT_CONTROL_LOST) + if(internal_damage & MECHA_INT_CONTROL_LOST) target = safepick(oview(1,src)) if(!melee_can_hit || !istype(target, /atom)) return target.mech_melee_attack(src) melee_can_hit = 0 - if(do_after(melee_cooldown)) + spawn(melee_cooldown) melee_can_hit = 1 return -/obj/mecha/proc/mech_toxin_damage(mob/living/target) - playsound(src, 'sound/effects/spray2.ogg', 50, 1) - if(target.reagents) - if(target.reagents.get_reagent_amount("cryptobiolin") + force < force*2) - target.reagents.add_reagent("cryptobiolin", force/2) - if(target.reagents.get_reagent_amount("toxin") + force < force*2) - target.reagents.add_reagent("toxin", force/2.5) - - -/atom/proc/mech_melee_attack(obj/mecha/M) - return - -/obj/mecha/mech_melee_attack(obj/mecha/M) - if(M.damtype =="brute") - playsound(src, 'sound/weapons/punch4.ogg', 50, 1) - else if(M.damtype == "fire") - playsound(src, 'sound/items/Welder.ogg', 50, 1) - else - return - visible_message("[M.name] has hit [src].") - take_damage(M.force, damtype) - add_logs(M.occupant, src, "attacked", object=M, addition="(INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])") - return /obj/mecha/proc/range_action(atom/target) return @@ -364,9 +396,6 @@ return domove(direction) /obj/mecha/proc/domove(direction) - return call((proc_res["dyndomove"]||src), "dyndomove")(direction) - -/obj/mecha/proc/dyndomove(direction) if(!can_move) return 0 if(!Process_Spacemove(direction)) @@ -374,15 +403,16 @@ if(!has_charge(step_energy_drain)) return 0 var/move_result = 0 - if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) + if(internal_damage & MECHA_INT_CONTROL_LOST) move_result = mechsteprand() else if(src.dir!=direction) move_result = mechturn(direction) else move_result = mechstep(direction) if(move_result) + use_power(step_energy_drain) can_move = 0 - if(do_after(step_in)) + spawn(step_in) can_move = 1 return 1 return 0 @@ -410,15 +440,15 @@ if(istype(obstacle, /obj)) var/obj/O = obstacle if(istype(O, /obj/effect/portal)) //derpfix - src.anchored = 0 + anchored = 0 O.Crossed(src) src.anchored = 1 else if(!O.anchored) - step(obstacle,src.dir) + step(obstacle, dir) else //I have no idea why I disabled this obstacle.Bumped(src) else if(istype(obstacle, /mob)) - step(obstacle,src.dir) + step(obstacle, dir) else obstacle.Bumped(src) return @@ -430,7 +460,7 @@ /obj/mecha/proc/check_for_internal_damage(var/list/possible_int_damage,var/ignore_threshold=null) if(!islist(possible_int_damage) || isemptylist(possible_int_damage)) return if(prob(20)) - if(ignore_threshold || src.health*100/initial(src.health)Life support system reactivated.") + if(MECHA_INT_FIRE) + occupant_message("Internal fire extinquished.") + if(MECHA_INT_TANK_BREACH) + occupant_message("Damaged internal tank has been sealed.") internal_damage &= ~int_dam_flag - switch(int_dam_flag) - if(MECHA_INT_TEMP_CONTROL) - if(pr_int_temp_processor) - occupant_message("Life support system reactivated.") - pr_int_temp_processor.start() - if(MECHA_INT_FIRE) - occupant_message("Internal fire extinquished.") - if(MECHA_INT_TANK_BREACH) - occupant_message("Damaged internal tank has been sealed.") - return -//////////////////////////////////////// -//////// Health related procs //////// -//////////////////////////////////////// - -/obj/mecha/proc/take_damage(amount, type="brute") - if(amount) - var/damage = absorbDamage(amount,type) - health -= damage - update_health() - occupant_message("Taking damage!") - log_append_to_last("Took [damage] points of damage. Damage type: \"[type]\".",1) - -/obj/mecha/proc/absorbDamage(damage,damage_type) - return call((proc_res["dynabsorbdamage"]||src), "dynabsorbdamage")(damage,damage_type) - -/obj/mecha/proc/dynabsorbdamage(damage,damage_type) - return damage*(listgetindex(damage_absorption,damage_type) || 1) - - -/obj/mecha/proc/update_health() - if(src.health > 0) - src.spark_system.start() - else - qdel(src) - -/obj/mecha/attack_hulk(mob/living/carbon/human/user) - if(!prob(src.deflect_chance)) - ..(user, 1) - take_damage(15) - check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - user.visible_message("[user] hits [src.name], doing some damage.", "You hit [src.name] with all your might. The metal creaks and bends.") - occupant_message("[user] hits [src.name], doing some damage.") - return 1 - return 0 - -/obj/mecha/attack_hand(mob/living/user as mob) - user.changeNext_move(CLICK_CD_MELEE) // Ugh. Ideally we shouldn't be setting cooldowns outside of click code. - user.do_attack_animation(src) - src.log_message("Attack by hand/paw. Attacker - [user].",1) - user.visible_message("[user] hits [src.name]. Nothing happens","You hit [src.name] with no visible effect.") - src.log_append_to_last("Armor saved.") - return - -/obj/mecha/attack_paw(mob/user as mob) - return src.attack_hand(user) - - -/obj/mecha/attack_alien(mob/living/user as mob) - src.log_message("Attack by alien. Attacker - [user].",1) - user.changeNext_move(CLICK_CD_MELEE) //Now stompy alien killer mechs are actually scary to aliens! - user.do_attack_animation(src) - if(!prob(src.deflect_chance)) - src.take_damage(15) - src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - playsound(src.loc, 'sound/weapons/slash.ogg', 50, 1, -1) - visible_message("The [user] slashes at [src.name]'s armor!") - else - src.log_append_to_last("Armor saved.") - playsound(src.loc, 'sound/weapons/slash.ogg', 50, 1, -1) - user << "\green Your claws had no effect!" - visible_message("The [user] rebounds off [src.name]'s armor!") - return - - -/obj/mecha/attack_animal(mob/living/simple_animal/user as mob) - src.log_message("Attack by simple animal. Attacker - [user].",1) - user.changeNext_move(CLICK_CD_MELEE) - if(user.melee_damage_upper == 0) - user.emote("[user.friendly] [src]") - else - user.do_attack_animation(src) - if(!prob(src.deflect_chance)) - var/damage = rand(user.melee_damage_lower, user.melee_damage_upper) - src.take_damage(damage) - src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - visible_message("[user] [user.attacktext] [src]!") - add_logs(user, src, "attacked", admin=0) - else - src.log_append_to_last("Armor saved.") - playsound(src.loc, 'sound/weapons/slash.ogg', 50, 1, -1) - visible_message("The [user] rebounds off [src.name]'s armor!") - add_logs(user, src, "attacked", admin=0) - return - -/obj/mecha/attack_tk() - return - -/obj/mecha/hitby(atom/movable/A as mob|obj) //wrapper - src.log_message("Hit by [A].",1) - call((proc_res["dynhitby"]||src), "dynhitby")(A) - return - -/obj/mecha/proc/dynhitby(atom/movable/A) - if(istype(A, /obj/item/mecha_parts/mecha_tracking)) - A.forceMove(src) - src.visible_message("The [A] fastens firmly to [src].") - return - if(prob(src.deflect_chance) || istype(A, /mob)) - src.visible_message("[A] bounces off the [src.name] armor") - src.log_append_to_last("Armor saved.") - if(istype(A, /mob/living)) - var/mob/living/M = A - M.take_organ_damage(10) - else if(istype(A, /obj)) - var/obj/O = A - if(O.throwforce) - src.visible_message("[src.name] is hit by [A].") - src.take_damage(O.throwforce) - src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - return - - -/obj/mecha/bullet_act(var/obj/item/projectile/Proj) //wrapper - src.log_message("Hit by projectile. Type: [Proj.name]([Proj.flag]).",1) - call((proc_res["dynbulletdamage"]||src), "dynbulletdamage")(Proj) //calls equipment - ..() - return - -/obj/mecha/proc/dynbulletdamage(var/obj/item/projectile/Proj) - if(prob(src.deflect_chance)) - src.visible_message("The [src.name] armor deflects the projectile") - src.log_append_to_last("Armor saved.") - return - var/ignore_threshold - if(Proj.flag == "taser") - use_power(200) - return - if(istype(Proj, /obj/item/projectile/beam/pulse)) - ignore_threshold = 1 - if((Proj.damage_type == BRUTE || Proj.damage_type == BURN)) - src.take_damage(Proj.damage,Proj.flag) - src.visible_message("[src.name] is hit by [Proj].") - src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),ignore_threshold) - Proj.on_hit(src) - return - -/obj/mecha/ex_act(severity, target) - src.log_message("Affected by explosion of severity: [severity].",1) - if(prob(src.deflect_chance)) - severity++ - src.log_append_to_last("Armor saved, changing severity to [severity].") - switch(severity) - if(1.0) - qdel(src) - if(2.0) - if (prob(30)) - qdel(src) - else - src.take_damage(initial(src.health)/2) - src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) - if(3.0) - if (prob(5)) - qdel(src) - else - src.take_damage(initial(src.health)/5) - src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) - return - -/obj/mecha/blob_act() - take_damage(30, "brute") - return - -/obj/mecha/emp_act(severity) - if(get_charge()) - use_power((cell.charge/2)/severity) - take_damage(50 / severity,"energy") - src.log_message("EMP detected",1) - check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) - return - -/obj/mecha/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(exposed_temperature>src.max_temperature) - src.log_message("Exposed to dangerous temperature.",1) - src.take_damage(5,"fire") - src.check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL)) - return - -/obj/mecha/proc/dynattackby(obj/item/weapon/W as obj, mob/living/user as mob) - user.changeNext_move(CLICK_CD_MELEE) // Ugh. Ideally we shouldn't be setting cooldowns outside of click code. - user.do_attack_animation(src) - src.log_message("Attacked by [W]. Attacker - [user]") - if(prob(src.deflect_chance)) - user << "The [W] bounces off [src.name] armor." - src.log_append_to_last("Armor saved.") - return 0 - else - user.visible_message("[user] hits [src] with [W].", "You hit [src] with [W].") - src.take_damage(W.force,W.damtype) - src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - return 1 - -////////////////////// -////// AttackBy ////// -////////////////////// - -/obj/mecha/attackby(obj/item/W as obj, mob/user as mob, params) - - if(istype(W, /obj/item/device/mmi)) - if(mmi_move_inside(W,user)) - user << "[src]-[W] interface initialized successfuly" - else - user << "[src]-[W] interface initialization failed." - return - - if(istype(W, /obj/item/mecha_parts/mecha_equipment)) - var/obj/item/mecha_parts/mecha_equipment/E = W - spawn() - if(E.can_attach(src)) - if(!user.drop_item()) - return - E.attach(src) - user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") - else - user << "You were unable to attach [W] to [src]!" - return - if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) - if(add_req_access || maint_access) - if(internals_access_allowed(user)) - var/obj/item/weapon/card/id/id_card - if(istype(W, /obj/item/weapon/card/id)) - id_card = W - else - var/obj/item/device/pda/pda = W - id_card = pda.id - output_maintenance_dialog(id_card, user) - return - else - user << "Invalid ID: Access denied." - else - user << "Maintenance protocols disabled by operator." - else if(istype(W, /obj/item/weapon/wrench)) - if(state==1) - state = 2 - user << "You undo the securing bolts." - else if(state==2) - state = 1 - user << "You tighten the securing bolts." - return - else if(istype(W, /obj/item/weapon/crowbar)) - if(state==2) - state = 3 - user << "You open the hatch to the power unit." - else if(state==3) - state=2 - user << "You close the hatch to the power unit." - return - else if(istype(W, /obj/item/stack/cable_coil)) - if(state == 3 && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) - var/obj/item/stack/cable_coil/CC = W - if(CC.use(2)) - clearInternalDamage(MECHA_INT_SHORT_CIRCUIT) - user << "You replace the fused wires." - else - user << "You need two lengths of cable to fix this mech!" - return - else if(istype(W, /obj/item/weapon/screwdriver)) - if(hasInternalDamage(MECHA_INT_TEMP_CONTROL)) - clearInternalDamage(MECHA_INT_TEMP_CONTROL) - user << "You repair the damaged temperature controller." - else if(state==3 && src.cell) - src.cell.forceMove(src.loc) - src.cell = null - state = 4 - user << "You unscrew and pry out the powercell." - src.log_message("Powercell removed") - else if(state==4 && src.cell) - state=3 - user << "You screw the cell in place." - return - - else if(istype(W, /obj/item/weapon/stock_parts/cell)) - if(state==4) - if(!src.cell) - if(!user.drop_item()) - return - var/obj/item/weapon/stock_parts/cell/C = W - user << "You install the powercell." - C.forceMove(src) - C.use(C.charge * 0.8) - src.cell = C - src.log_message("Powercell installed") - else - user << "There's already a powercell installed." - return - - else if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent != "harm") - user.changeNext_move(CLICK_CD_MELEE) - var/obj/item/weapon/weldingtool/WT = W - if(src.healthYou repair the damaged gas tank." - else - user.visible_message("[user] repairs some damage to [src.name].") - src.health += min(10, initial(src.health)-src.health) - else - user << "The welder must be on for this task!" - return 1 - else - user << "The [src.name] is at full integrity!" - return 1 - - else if(istype(W, /obj/item/mecha_parts/mecha_tracking)) - if(!user.unEquip(W)) - user << "\the [W] is stuck to your hand, you cannot put it in \the [src]!" - return - W.forceMove(src) - user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") - return - - else if(!(W.flags&NOBLUDGEON)) - call((proc_res["dynattackby"]||src), "dynattackby")(W,user) - return - ///////////////////////////////////// //////////// AI piloting //////////// ///////////////////////////////////// @@ -861,7 +568,7 @@ occupant = AI icon_state = initial(icon_state) playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) - if(!hasInternalDamage()) + if(!internal_damage) occupant << sound('sound/mecha/nominal.ogg',volume=50) AI.cancel_camera() AI.controlled_mech = src @@ -871,7 +578,7 @@ AI << "[interaction == AI_MECH_HACK ? "Takeover of [name] complete! You are now permanently loaded onto the onboard computer. Do not attempt to leave the station sector!" \ : "You have been uploaded to a mech's onboard computer."]" AI << "Use Middle-Mouse to activate mech functions and equipment. Click normally for AI interactions." - + GrantActions(AI) ///////////////////////////////////// //////// Atmospheric stuff //////// @@ -929,81 +636,6 @@ /obj/mecha/portableConnectorReturnAir() return internal_tank.return_air() -///////////////////////// -//////// Verbs //////// -///////////////////////// - - -/obj/mecha/verb/connect_to_port() - set name = "Connect to port" - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - - if(!can_use(usr)) - return - - var/obj/machinery/atmospherics/unary/portables_connector/possible_port = locate(/obj/machinery/atmospherics/unary/portables_connector/) in loc - if(possible_port) - if(connect(possible_port)) - src.occupant_message("[name] connects to the port.") - src.verbs += /obj/mecha/verb/disconnect_from_port - src.verbs -= /obj/mecha/verb/connect_to_port - return - else - src.occupant_message("[name] failed to connect to the port.") - return - else - src.occupant_message("Nothing happens") - - -/obj/mecha/verb/disconnect_from_port() - set name = "Disconnect from port" - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - - if(!can_use(usr)) - return - - if(disconnect()) - src.occupant_message("[name] disconnects from the port.") - src.verbs -= /obj/mecha/verb/disconnect_from_port - src.verbs += /obj/mecha/verb/connect_to_port - else - src.occupant_message("[name] is not connected to the port at the moment.") - -/obj/mecha/verb/toggle_lights() - set name = "Toggle Lights" - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - - if(!can_use(usr)) - return - - lights = !lights - if(lights) AddLuminosity(lights_power) - else AddLuminosity(-lights_power) - src.occupant_message("Toggled lights [lights?"on":"off"].") - log_message("Toggled lights [lights?"on":"off"].") - return - - -/obj/mecha/verb/toggle_internal_tank() - set name = "Toggle internal airtank usage." - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - - if(!can_use(usr)) - return - - use_internal_tank = !use_internal_tank - src.occupant_message("Now taking air from [use_internal_tank?"internal airtank":"environment"].") - src.log_message("Now taking air from [use_internal_tank?"internal airtank":"environment"].") - return - /obj/mecha/MouseDrop_T(mob/M as mob, mob/user as mob) if (!user.canUseTopic(src) || (user != M)) @@ -1034,13 +666,13 @@ visible_message("[user] starts to climb into [src.name].") - if(enter_after(40,user)) - if(src.health <= 0) + if(do_after(user, 40, target = src)) + if(health <= 0) user << "You cannot get in the [src.name], it has been destroyed!" - else if(!src.occupant) - moved_inside(user) - else if(src.occupant!=user) + else if(occupant) user << "[src.occupant] was faster! Try better next time, loser." + else + moved_inside(user) else user << "You stop entering the exosuit!" return @@ -1048,25 +680,18 @@ /obj/mecha/proc/moved_inside(var/mob/living/carbon/human/H as mob) if(H && H.client && H in range(1)) H.reset_view(src) - /* - H.client.perspective = EYE_PERSPECTIVE - H.client.eye = src - */ H.stop_pulling() H.forceMove(src) - if(H.hud_used) - last_user_hud = H.hud_used.hud_shown - H.hud_used.show_hud(HUD_STYLE_REDUCED) - - src.occupant = H - src.add_fingerprint(H) - src.forceMove(src.loc) - src.log_append_to_last("[H] moved in as pilot.") - src.icon_state = initial(icon_state) + occupant = H + add_fingerprint(H) + GrantActions(H, human_occupant=1) + forceMove(loc) + log_append_to_last("[H] moved in as pilot.") + icon_state = initial(icon_state) dir = dir_in playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) - if(!hasInternalDamage()) - src.occupant << sound('sound/mecha/nominal.ogg',volume=50) + if(!internal_damage) + occupant << sound('sound/mecha/nominal.ogg',volume=50) return 1 else return 0 @@ -1089,7 +714,7 @@ visible_message("[user] starts to insert an MMI into [src.name].") - if(enter_after(40,user)) + if(do_after(user, 40, target = src)) if(!occupant) return mmi_moved_inside(mmi_as_oc,user) else @@ -1111,122 +736,65 @@ return var/mob/brainmob = mmi_as_oc.brainmob brainmob.reset_view(src) - /* - brainmob.client.eye = src - brainmob.client.perspective = EYE_PERSPECTIVE - */ occupant = brainmob brainmob.loc = src //should allow relaymove brainmob.canmove = 1 mmi_as_oc.loc = src mmi_as_oc.mecha = src - src.verbs -= /obj/mecha/verb/eject - src.icon_state = initial(icon_state) + icon_state = initial(icon_state) dir = dir_in - src.log_message("[mmi_as_oc] moved in as pilot.") - if(!hasInternalDamage()) - src.occupant << sound('sound/mecha/nominal.ogg',volume=50) + log_message("[mmi_as_oc] moved in as pilot.") + if(!internal_damage) + occupant << sound('sound/mecha/nominal.ogg',volume=50) + GrantActions(brainmob) return 1 else return 0 -/obj/mecha/verb/view_stats() - set name = "View Stats" - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - - if(!can_use(usr)) - return - //pr_update_stats.start() - src.occupant << browse(src.get_stats_html(), "window=exosuit") - return - -/* -/obj/mecha/verb/force_eject() - set category = "Object" - set name = "Force Eject" - set src in view(5) - src.go_out() - return -*/ - -/obj/mecha/verb/eject() - set name = "Eject" - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - - if(!can_use(usr)) - return - - src.go_out() - add_fingerprint(usr) - return - /obj/mecha/container_resist() go_out() -/obj/mecha/proc/go_out(var/forced) - if(!src.occupant) return +/obj/mecha/Exited(var/atom/movable/M, var/atom/newloc) + if(occupant && occupant == M) // The occupant exited the mech without calling go_out() + go_out(1, newloc) + +/obj/mecha/proc/go_out(var/forced, var/atom/newloc = loc) + if(!occupant) return var/atom/movable/mob_container + occupant.clear_alert("charge") + occupant.clear_alert("mech damage") if(ishuman(occupant)) - mob_container = src.occupant - if(occupant.hud_used && last_user_hud) - occupant.hud_used.show_hud(HUD_STYLE_STANDARD) + mob_container = occupant + RemoveActions(occupant, human_occupant=1) else if(istype(occupant, /mob/living/carbon/brain)) var/mob/living/carbon/brain/brain = occupant + RemoveActions(brain) mob_container = brain.container else if(isAI(occupant) && forced) //This should only happen if there are multiple AIs in a round, and at least one is Malf. + RemoveActions(occupant) occupant.gib() //If one Malf decides to steal a mech from another AI (even other Malfs!), they are destroyed, as they have nowhere to go when replaced. occupant = null return else return - if(mob_container.forceMove(src.loc))//ejecting mob container - /* - if(ishuman(occupant) && (return_pressure() > HAZARD_HIGH_PRESSURE)) - use_internal_tank = 0 - var/datum/gas_mixture/environment = get_turf_air() - if(environment) - var/env_pressure = environment.return_pressure() - var/pressure_delta = (cabin.return_pressure() - env_pressure) - //Can not have a pressure delta that would cause environment pressure > tank pressure - - var/transfer_moles = 0 - if(pressure_delta > 0) - transfer_moles = pressure_delta*environment.volume/(cabin.return_temperature() * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed = cabin.air_contents.remove(transfer_moles) - loc.assume_air(removed) - - occupant.SetStunned(5) - occupant.SetWeakened(5) - occupant << "You were blown out of the mech!" - */ - src.log_message("[mob_container] moved out.") - occupant.reset_view() - /* - if(src.occupant.client) - src.occupant.client.eye = src.occupant.client.mob - src.occupant.client.perspective = MOB_PERSPECTIVE - */ - src.occupant << browse(null, "window=exosuit") + var/mob/living/L = occupant + occupant = null //we need it null when forceMove calls Exited(). + if(mob_container.forceMove(newloc))//ejecting mob container + log_message("[mob_container] moved out.") + L.reset_view() + L << browse(null, "window=exosuit") if(istype(mob_container, /obj/item/device/mmi)) var/obj/item/device/mmi/mmi = mob_container if(mmi.brainmob) - occupant.loc = mmi + L.loc = mmi mmi.mecha = null mmi.update_icon() - src.occupant.canmove = 0 - src.verbs += /obj/mecha/verb/eject - src.occupant = null - src.icon_state = initial(icon_state)+"-open" - src.dir = dir_in + L.canmove = 0 + icon_state = initial(icon_state)+"-open" + dir = dir_in return ///////////////////////// @@ -1244,211 +812,6 @@ return allowed(M) -//////////////////////////////////// -///// Rendering stats window /////// -//////////////////////////////////// - -/obj/mecha/proc/get_stats_html() - var/output = {" - [src.name] data - - - - -
- [src.get_stats_part()] -
-
- [src.get_equipment_list()] -
-
-
- [src.get_commands()] -
- - - "} - return output - - -/obj/mecha/proc/report_internal_damage() - var/output = null - var/list/dam_reports = list( - "[MECHA_INT_FIRE]" = "INTERNAL FIRE", - "[MECHA_INT_TEMP_CONTROL]" = "LIFE SUPPORT SYSTEM MALFUNCTION", - "[MECHA_INT_TANK_BREACH]" = "GAS TANK BREACH", - "[MECHA_INT_CONTROL_LOST]" = "COORDINATION SYSTEM CALIBRATION FAILURE - Recalibrate", - "[MECHA_INT_SHORT_CIRCUIT]" = "SHORT CIRCUIT" - ) - for(var/tflag in dam_reports) - var/intdamflag = text2num(tflag) - if(hasInternalDamage(intdamflag)) - output += dam_reports[tflag] - output += "
" - if(return_pressure() > WARNING_HIGH_PRESSURE) - output += "DANGEROUSLY HIGH CABIN PRESSURE
" - return output - - -/obj/mecha/proc/get_stats_part() - var/integrity = health/initial(health)*100 - var/cell_charge = get_charge() - var/tank_pressure = internal_tank ? round(internal_tank.return_pressure(),0.01) : "None" - var/tank_temperature = internal_tank ? internal_tank.return_temperature() : "Unknown" - var/cabin_pressure = round(return_pressure(),0.01) - var/output = {"[report_internal_damage()] - [integrity<30?"DAMAGE LEVEL CRITICAL
":null] - Integrity: [integrity]%
- Powercell charge: [isnull(cell_charge)?"No powercell installed":"[cell.percent()]%"]
- Air source: [use_internal_tank?"Internal Airtank":"Environment"]
- Airtank pressure: [tank_pressure]kPa
- Airtank temperature: [tank_temperature]°K|[tank_temperature - T0C]°C
- Cabin pressure: [cabin_pressure>WARNING_HIGH_PRESSURE ? "[cabin_pressure]": cabin_pressure]kPa
- Cabin temperature: [return_temperature()]°K|[return_temperature() - T0C]°C
- Lights: [lights?"on":"off"]
- [src.dna?"DNA-locked:
[src.dna] \[Reset\]
":null] - "} - return output - -/obj/mecha/proc/get_commands() - var/output = {"
-
Electronics
- -
-
-
Airtank
- -
- -
[get_equipment_menu()]
-
- [(/obj/mecha/verb/eject in src.verbs)?"Eject
":null] - "} - return output - -/obj/mecha/proc/get_equipment_menu() //outputs mecha html equipment menu - var/output - if(equipment.len) - output += {"
-
Equipment
-
" - return output - -/obj/mecha/proc/get_equipment_list() //outputs mecha equipment list in html - if(!equipment.len) - return - var/output = "Equipment:
" - for(var/obj/item/mecha_parts/mecha_equipment/MT in equipment) - output += "
[MT.get_equip_info()]
" - output += "
" - return output - - -/obj/mecha/proc/get_log_html() - var/output = "[src.name] Log" - for(var/list/entry in log) - output += {"
[entry["time"]] [time2text(entry["date"],"MMM DD")] [entry["year"]]
-
[entry["message"]]
- "} - output += "" - return output - - -/obj/mecha/proc/output_access_dialog(obj/item/weapon/card/id/id_card, mob/user) - if(!id_card || !user) return - var/output = {" - - - -

Following keycodes are present in this system:

"} - for(var/a in operation_req_access) - output += "[get_access_desc(a)] - Delete
" - output += "

Following keycodes were detected on portable device:

" - for(var/a in id_card.access) - if(a in operation_req_access) continue - var/a_name = get_access_desc(a) - if(!a_name) continue //there's some strange access without a name - output += "[a_name] - Add
" - output += "
Finish (Warning! The ID upload panel will be locked. It can be unlocked only through Exosuit Interface.)" - output += "" - user << browse(output, "window=exosuit_add_access") - onclose(user, "exosuit_add_access") - return - -/obj/mecha/proc/output_maintenance_dialog(obj/item/weapon/card/id/id_card,mob/user) - if(!id_card || !user) return - var/output = {" - - - - - [add_req_access?"Edit operation keycodes":null] - [maint_access?"Initiate maintenance protocol":null] - [(state>0) ?"Set Cabin Air Pressure":null] - - "} - user << browse(output, "window=exosuit_maint_console") - onclose(user, "exosuit_maint_console") - return - //////////////////////////////// /////// Messages and Log /////// @@ -1473,172 +836,6 @@ var/year = time2text(world.realtime,"YYYY") var/year_integer = text2num(year) // = 2013??? -///////////////// -///// Topic ///// -///////////////// - -/obj/mecha/Topic(href, href_list) - ..() - if(href_list["update_content"]) - if(usr != src.occupant) return - send_byjax(src.occupant,"exosuit.browser","content",src.get_stats_part()) - return - if(href_list["close"]) - return - if(usr.stat > 0) - return - var/datum/topic_input/filter = new /datum/topic_input(href,href_list) - if(href_list["select_equip"]) - if(usr != src.occupant) return - var/obj/item/mecha_parts/mecha_equipment/equip = filter.getObj("select_equip") - if(equip) - src.selected = equip - src.occupant_message("You switch to [equip]") - src.visible_message("[src] raises [equip]") - send_byjax(src.occupant,"exosuit.browser","eq_list",src.get_equipment_list()) - return - if(href_list["eject"]) - if(usr != src.occupant) return - src.eject() - return - if(href_list["toggle_lights"]) - if(usr != src.occupant) return - src.toggle_lights() - return - if(href_list["toggle_airtank"]) - if(usr != src.occupant) return - src.toggle_internal_tank() - return - if(href_list["rmictoggle"]) - if(usr != src.occupant) return - radio.broadcasting = !radio.broadcasting - send_byjax(src.occupant,"exosuit.browser","rmicstate",(radio.broadcasting?"Engaged":"Disengaged")) - return - if(href_list["rspktoggle"]) - if(usr != src.occupant) return - radio.listening = !radio.listening - send_byjax(src.occupant,"exosuit.browser","rspkstate",(radio.listening?"Engaged":"Disengaged")) - return - if(href_list["rfreq"]) - if(usr != src.occupant) return - var/new_frequency = (radio.frequency + filter.getNum("rfreq")) - if (!radio.freerange || (radio.frequency < 1200 || radio.frequency > 1600)) - new_frequency = sanitize_frequency(new_frequency) - radio.set_frequency(new_frequency) - send_byjax(src.occupant,"exosuit.browser","rfreq","[format_frequency(radio.frequency)]") - return - if(href_list["port_disconnect"]) - if(usr != src.occupant) return - src.disconnect_from_port() - return - if (href_list["port_connect"]) - if(usr != src.occupant) return - src.connect_to_port() - return - if (href_list["view_log"]) - if(usr != src.occupant) return - src.occupant << browse(src.get_log_html(), "window=exosuit_log") - onclose(occupant, "exosuit_log") - return - if (href_list["change_name"]) - if(usr != src.occupant) return - var/newname = stripped_input(occupant,"Choose new exosuit name","Rename exosuit","", MAX_NAME_LEN) - if(newname && trim(newname)) - name = newname - else - alert(occupant, "nope.avi") - return - if (href_list["toggle_id_upload"]) - if(usr != src.occupant) return - add_req_access = !add_req_access - send_byjax(src.occupant,"exosuit.browser","t_id_upload","[add_req_access?"L":"Unl"]ock ID upload panel") - return - if(href_list["toggle_maint_access"]) - if(usr != src.occupant) return - if(state) - occupant_message("Maintenance protocols in effect") - return - maint_access = !maint_access - send_byjax(src.occupant,"exosuit.browser","t_maint_access","[maint_access?"Forbid":"Permit"] maintenance protocols") - return - if(href_list["req_access"] && add_req_access) - if(!in_range(src, usr)) return - output_access_dialog(filter.getObj("id_card"),filter.getMob("user")) - return - if(href_list["maint_access"] && maint_access) - if(!in_range(src, usr)) return - var/mob/user = filter.getMob("user") - if(user) - if(state==0) - state = 1 - user << "The securing bolts are now exposed." - else if(state==1) - state = 0 - user << "The securing bolts are now hidden." - output_maintenance_dialog(filter.getObj("id_card"),user) - return - if(href_list["set_internal_tank_valve"] && state >=1) - if(!in_range(src, usr)) return - var/mob/user = filter.getMob("user") - if(user) - var/new_pressure = input(user,"Input new output pressure","Pressure setting",internal_tank_valve) as num - if(new_pressure) - internal_tank_valve = new_pressure - user << "The internal pressure valve has been set to [internal_tank_valve]kPa." - if(href_list["add_req_access"] && add_req_access && filter.getObj("id_card")) - if(!in_range(src, usr)) return - operation_req_access += filter.getNum("add_req_access") - output_access_dialog(filter.getObj("id_card"),filter.getMob("user")) - return - if(href_list["del_req_access"] && add_req_access && filter.getObj("id_card")) - if(!in_range(src, usr)) return - operation_req_access -= filter.getNum("del_req_access") - output_access_dialog(filter.getObj("id_card"),filter.getMob("user")) - return - if(href_list["finish_req_access"]) - if(!in_range(src, usr)) return - add_req_access = 0 - var/mob/user = filter.getMob("user") - user << browse(null,"window=exosuit_add_access") - return - if(href_list["dna_lock"]) - if(usr != src.occupant) - return - if(src.occupant && !iscarbon(src.occupant)) - src.occupant << " You do not have any DNA!" - return - src.dna = src.occupant.dna.unique_enzymes - src.occupant_message("You feel a prick as the needle takes your DNA sample.") - return - if(href_list["reset_dna"]) - if(usr != src.occupant) return - src.dna = null - if(href_list["repair_int_control_lost"]) - if(usr != src.occupant) return - src.occupant_message("Recalibrating coordination system...") - src.log_message("Recalibration of coordination system started.") - var/T = src.loc - if(do_after(100)) - if(T == src.loc) - src.clearInternalDamage(MECHA_INT_CONTROL_LOST) - src.occupant_message("Recalibration successful.") - src.log_message("Recalibration of coordination system finished with 0 errors.") - else - src.occupant_message("Recalibration failed!") - src.log_message("Recalibration of coordination system failed with 1 error.",1) - - //debug - /* - if(href_list["debug"]) - if(href_list["set_i_dam"]) - setInternalDamage(filter.getNum("set_i_dam")) - if(href_list["clear_i_dam"]) - clearInternalDamage(filter.getNum("clear_i_dam")) - return - */ - - return - /////////////////////// ///// Power stuff ///// /////////////////////// @@ -1647,16 +844,14 @@ var/year_integer = text2num(year) // = 2013??? return (get_charge()>=amount) /obj/mecha/proc/get_charge() - return call((proc_res["dyngetcharge"]||src), "dyngetcharge")() - -/obj/mecha/proc/dyngetcharge()//returns null if no powercell, else returns cell.charge - if(!src.cell) return - return max(0, src.cell.charge) + for(var/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/R in equipment) + var/relay_charge = R.get_charge() + if(relay_charge) + return relay_charge + if(cell) + return max(0, cell.charge) /obj/mecha/proc/use_power(amount) - return call((proc_res["dynusepower"]||src), "dynusepower")(amount) - -/obj/mecha/proc/dynusepower(amount) if(get_charge()) cell.use(amount) return 1 @@ -1672,123 +867,121 @@ var/year_integer = text2num(year) // = 2013??? return 0 -////////////////////////////////////////// -//////// Mecha global iterators //////// -////////////////////////////////////////// +//////////////////////////////////////// Action Buttons /////////////////////////////////////////////// + +/obj/mecha/proc/GrantActions(var/mob/living/user, var/human_occupant = 0) + if(human_occupant) + eject_action.chassis = src + eject_action.Grant(user) + + internals_action.chassis = src + internals_action.Grant(user) + + cycle_action.chassis = src + cycle_action.Grant(user) + + lights_action.chassis = src + lights_action.Grant(user) + + stats_action.chassis = src + stats_action.Grant(user) -/datum/global_iterator/mecha_preserve_temp //normalizing cabin air temperature to 20 degrees celsium - delay = 20 +/obj/mecha/proc/RemoveActions(var/mob/living/user, var/human_occupant = 0) + if(human_occupant) + eject_action.Remove(user) + internals_action.Remove(user) + cycle_action.Remove(user) + lights_action.Remove(user) + stats_action.Remove(user) - process(var/obj/mecha/mecha) - if(mecha.cabin_air && mecha.cabin_air.return_volume() > 0) - var/delta = mecha.cabin_air.temperature - T20C - mecha.cabin_air.temperature -= max(-10, min(10, round(delta/4,0.1))) + +/datum/action/mecha + check_flags = AB_CHECK_RESTRAINED | AB_CHECK_STUNNED | AB_CHECK_ALIVE + action_type = AB_INNATE + var/obj/mecha/chassis + + +/datum/action/mecha/mech_eject + name = "Eject From Mech" + button_icon_state = "mech_eject" + +/datum/action/mecha/mech_eject/Activate() + if(!owner || !iscarbon(owner)) return - -/datum/global_iterator/mecha_tank_give_air - delay = 15 - - process(var/obj/mecha/mecha) - if(mecha.internal_tank) - var/datum/gas_mixture/tank_air = mecha.internal_tank.return_air() - var/datum/gas_mixture/cabin_air = mecha.cabin_air - - var/release_pressure = mecha.internal_tank_valve - var/cabin_pressure = cabin_air.return_pressure() - var/pressure_delta = min(release_pressure - cabin_pressure, (tank_air.return_pressure() - cabin_pressure)/2) - var/transfer_moles = 0 - if(pressure_delta > 0) //cabin pressure lower than release pressure - if(tank_air.return_temperature() > 0) - transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) - var/datum/gas_mixture/removed = tank_air.remove(transfer_moles) - cabin_air.merge(removed) - else if(pressure_delta < 0) //cabin pressure higher than release pressure - var/datum/gas_mixture/t_air = mecha.return_air() - pressure_delta = cabin_pressure - release_pressure - if(t_air) - pressure_delta = min(cabin_pressure - t_air.return_pressure(), pressure_delta) - if(pressure_delta > 0) //if location pressure is lower than cabin pressure - transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) - var/datum/gas_mixture/removed = cabin_air.remove(transfer_moles) - if(t_air) - t_air.merge(removed) - else //just delete the cabin gas, we're in space or some shit - del(removed) - else - return stop() + if(!chassis || chassis.occupant != owner) return + chassis.go_out() -/datum/global_iterator/mecha_internal_damage // processing internal damage +/datum/action/mecha/mech_toggle_internals + name = "Toggle Internal Airtank Usage" + button_icon_state = "mech_toggle_internals" - process(var/obj/mecha/mecha) - if(!mecha.hasInternalDamage()) - return stop() - if(mecha.hasInternalDamage(MECHA_INT_FIRE)) - if(!mecha.hasInternalDamage(MECHA_INT_TEMP_CONTROL) && prob(5)) - mecha.clearInternalDamage(MECHA_INT_FIRE) - if(mecha.internal_tank) - if(mecha.internal_tank.return_pressure()>mecha.internal_tank.maximum_pressure && !(mecha.hasInternalDamage(MECHA_INT_TANK_BREACH))) - mecha.setInternalDamage(MECHA_INT_TANK_BREACH) - var/datum/gas_mixture/int_tank_air = mecha.internal_tank.return_air() - if(int_tank_air && int_tank_air.return_volume()>0) //heat the air_contents - int_tank_air.temperature = min(6000+T0C, int_tank_air.temperature+rand(10,15)) - if(mecha.cabin_air && mecha.cabin_air.return_volume()>0) - mecha.cabin_air.temperature = min(6000+T0C, mecha.cabin_air.return_temperature()+rand(10,15)) - if(mecha.cabin_air.return_temperature()>mecha.max_temperature/2) - mecha.take_damage(4/round(mecha.max_temperature/mecha.cabin_air.return_temperature(),0.1),"fire") - if(mecha.hasInternalDamage(MECHA_INT_TEMP_CONTROL)) //stop the mecha_preserve_temp loop datum - mecha.pr_int_temp_processor.stop() - if(mecha.hasInternalDamage(MECHA_INT_TANK_BREACH)) //remove some air from internal tank - if(mecha.internal_tank) - var/datum/gas_mixture/int_tank_air = mecha.internal_tank.return_air() - var/datum/gas_mixture/leaked_gas = int_tank_air.remove_ratio(0.10) - if(mecha.loc && hascall(mecha.loc,"assume_air")) - mecha.loc.assume_air(leaked_gas) - else - del(leaked_gas) - if(mecha.hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) - if(mecha.get_charge()) - mecha.spark_system.start() - mecha.cell.charge -= min(20,mecha.cell.charge) - mecha.cell.maxcharge -= min(20,mecha.cell.maxcharge) +/datum/action/mecha/mech_toggle_internals/Activate() + if(!owner || !chassis || chassis.occupant != owner) return + chassis.use_internal_tank = !chassis.use_internal_tank + if(chassis.use_internal_tank) + button_icon_state = "mech_toggle_internals_on" + else + button_icon_state = "mech_toggle_internals" + chassis.occupant_message("Now taking air from [chassis.use_internal_tank?"internal airtank":"environment"].") + chassis.log_message("Now taking air from [chassis.use_internal_tank?"internal airtank":"environment"].") -///////////// +/datum/action/mecha/mech_cycle_equip + name = "Cycle Equipment" + button_icon_state = "mech_cycle_equip" -//debug -/* -/obj/mecha/verb/test_int_damage() - set name = "Test internal damage" - set category = "Exosuit Interface" - set src in view(0) - if(!occupant) return - if(usr!=occupant) +/datum/action/mecha/mech_cycle_equip/Activate() + if(!owner || !chassis || chassis.occupant != owner) return - var/output = {" - - - -

Set:

- MECHA_INT_FIRE
- MECHA_INT_TEMP_CONTROL
- MECHA_INT_SHORT_CIRCUIT
- MECHA_INT_TANK_BREACH
- MECHA_INT_CONTROL_LOST
-
-

Clear:

- MECHA_INT_FIRE
- MECHA_INT_TEMP_CONTROL
- MECHA_INT_SHORT_CIRCUIT
- MECHA_INT_TANK_BREACH
- MECHA_INT_CONTROL_LOST
- - "} + if(chassis.equipment.len == 0) + chassis.occupant_message("No equipment available.") + return + if(!chassis.selected) + chassis.selected = chassis.equipment[1] + chassis.occupant_message("You select [chassis.selected]") + send_byjax(chassis.occupant,"exosuit.browser","eq_list",chassis.get_equipment_list()) + return + var/number = 0 + for(var/A in chassis.equipment) + number++ + if(A == chassis.selected) + if(chassis.equipment.len == number) + chassis.selected = null + chassis.occupant_message("You switch to no equipment") + else + chassis.selected = chassis.equipment[number+1] + chassis.occupant_message("You switch to [chassis.selected]") + send_byjax(chassis.occupant,"exosuit.browser","eq_list",chassis.get_equipment_list()) + return - occupant << browse(output, "window=ex_debug") - //src.health = initial(src.health)/2.2 - //src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - return -*/ + +/datum/action/mecha/mech_toggle_lights + name = "Toggle Lights" + button_icon_state = "mech_toggle_lights" + +/datum/action/mecha/mech_toggle_lights/Activate() + if(!owner || !chassis || chassis.occupant != owner) + return + chassis.lights = !chassis.lights + if(chassis.lights) + chassis.AddLuminosity(chassis.lights_power) + button_icon_state = "mech_toggle_lights_on" + else + chassis.AddLuminosity(-chassis.lights_power) + button_icon_state = "mech_toggle_lights" + chassis.occupant_message("Toggled lights [chassis.lights?"on":"off"].") + chassis.log_message("Toggled lights [chassis.lights?"on":"off"].") + + +/datum/action/mecha/mech_view_stats + name = "View Stats" + button_icon_state = "mech_view_stats" + +/datum/action/mecha/mech_view_stats/Activate() + if(!owner || !chassis || chassis.occupant != owner) + return + chassis.occupant << browse(chassis.get_stats_html(), "window=exosuit") diff --git a/code/game/mecha/mecha_defense.dm b/code/game/mecha/mecha_defense.dm new file mode 100644 index 00000000000..3b61b31887f --- /dev/null +++ b/code/game/mecha/mecha_defense.dm @@ -0,0 +1,345 @@ + +/obj/mecha/proc/take_damage(amount, type="brute") + if(amount) + var/damage = absorbDamage(amount,type) + health -= damage + update_health() + occupant_message("Taking damage!") + log_append_to_last("Took [damage] points of damage. Damage type: \"[type]\".",1) + +/obj/mecha/proc/absorbDamage(damage,damage_type) + var/coeff = 1 + if(damage_absorption[damage_type]) + coeff = damage_absorption[damage_type] + return damage*coeff + + +/obj/mecha/proc/update_health() + if(src.health > 0) + src.spark_system.start() + else + qdel(src) + +/obj/mecha/attack_hulk(mob/living/carbon/human/user) + if(!prob(src.deflect_chance)) + ..(user, 1) + take_damage(15) + check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + user.visible_message("[user] hits [src.name], doing some damage.", "You hit [src.name] with all your might. The metal creaks and bends.") + occupant_message("[user] hits [src.name], doing some damage.") + return 1 + return 0 + +/obj/mecha/attack_hand(mob/living/user as mob) + user.changeNext_move(CLICK_CD_MELEE) // Ugh. Ideally we shouldn't be setting cooldowns outside of click code. + user.do_attack_animation(src) + src.log_message("Attack by hand/paw. Attacker - [user].",1) + user.visible_message("[user] hits [src.name]. Nothing happens","You hit [src.name] with no visible effect.") + src.log_append_to_last("Armor saved.") + return + +/obj/mecha/attack_paw(mob/user as mob) + return src.attack_hand(user) + + +/obj/mecha/attack_alien(mob/living/user as mob) + src.log_message("Attack by alien. Attacker - [user].",1) + user.changeNext_move(CLICK_CD_MELEE) //Now stompy alien killer mechs are actually scary to aliens! + user.do_attack_animation(src) + if(!prob(src.deflect_chance)) + take_damage(15) + check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + playsound(src.loc, 'sound/weapons/slash.ogg', 50, 1, -1) + visible_message("The [user] slashes at [src.name]'s armor!") + else + src.log_append_to_last("Armor saved.") + playsound(src.loc, 'sound/weapons/slash.ogg', 50, 1, -1) + user << "\green Your claws had no effect!" + visible_message("The [user] rebounds off [src.name]'s armor!") + return + + +/obj/mecha/attack_animal(mob/living/simple_animal/user as mob) + src.log_message("Attack by simple animal. Attacker - [user].",1) + user.changeNext_move(CLICK_CD_MELEE) + if(user.melee_damage_upper == 0) + user.emote("[user.friendly] [src]") + else + user.do_attack_animation(src) + if(!prob(src.deflect_chance)) + var/damage = rand(user.melee_damage_lower, user.melee_damage_upper) + take_damage(damage) + check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + visible_message("[user] [user.attacktext] [src]!") + add_logs(user, src, "attacked", admin=0) + else + src.log_append_to_last("Armor saved.") + playsound(src.loc, 'sound/weapons/slash.ogg', 50, 1, -1) + visible_message("The [user] rebounds off [src.name]'s armor!") + add_logs(user, src, "attacked", admin=0) + return + +/obj/mecha/attack_tk() + return + +/obj/mecha/hitby(atom/movable/A as mob|obj) //wrapper + log_message("Hit by [A].",1) + var/deflection = deflect_chance + var/dam_coeff = 1 + var/counter_tracking = 0 + for(var/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/B in equipment) + if(B.projectile_react()) + deflection *= B.deflect_coeff + dam_coeff *= B.damage_coeff + counter_tracking = 1 + break + if(istype(A, /obj/item/mecha_parts/mecha_tracking)) + if(!counter_tracking) + A.forceMove(src) + visible_message("The [A] fastens firmly to [src].") + return + else + deflection = 100 //will bounce off + if(prob(deflection) || istype(A, /mob)) + visible_message("[A] bounces off the [src.name] armor") + log_append_to_last("Armor saved.") + if(istype(A, /mob/living)) + var/mob/living/M = A + M.take_organ_damage(10) + else if(istype(A, /obj)) + var/obj/O = A + if(O.throwforce) + visible_message("[src.name] is hit by [A].") + take_damage(O.throwforce*dam_coeff) + check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + return + + +/obj/mecha/bullet_act(var/obj/item/projectile/Proj) //wrapper + log_message("Hit by projectile. Type: [Proj.name]([Proj.flag]).",1) + var/deflection = deflect_chance + var/dam_coeff = 1 + for(var/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/B in equipment) + if(B.projectile_react()) + deflection *= B.deflect_coeff + dam_coeff *= B.damage_coeff + break + if(prob(deflection)) + visible_message("The [src.name] armor deflects the projectile") + log_append_to_last("Armor saved.") + return + var/ignore_threshold + if(Proj.flag == "taser") + use_power(200) + return + if(istype(Proj, /obj/item/projectile/beam/pulse)) + ignore_threshold = 1 + if((Proj.damage_type == BRUTE || Proj.damage_type == BURN)) + take_damage(Proj.damage*dam_coeff,Proj.flag) + visible_message("[src.name] is hit by [Proj].") + check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),ignore_threshold) + Proj.on_hit(src) + +/obj/mecha/ex_act(severity, target) + src.log_message("Affected by explosion of severity: [severity].",1) + if(prob(src.deflect_chance)) + severity++ + log_append_to_last("Armor saved, changing severity to [severity].") + switch(severity) + if(1.0) + qdel(src) + if(2.0) + if (prob(30)) + qdel(src) + else + take_damage(initial(src.health)/2) + check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) + if(3.0) + if (prob(5)) + qdel(src) + else + take_damage(initial(src.health)/5) + check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) + return + +/obj/mecha/blob_act() + take_damage(30, "brute") + return + +/obj/mecha/emp_act(severity) + if(get_charge()) + use_power((cell.charge/2)/severity) + take_damage(50 / severity,"energy") + src.log_message("EMP detected",1) + check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) + return + +/obj/mecha/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(exposed_temperature>src.max_temperature) + log_message("Exposed to dangerous temperature.",1) + take_damage(5,"fire") + check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL)) + return + + +/obj/mecha/attackby(obj/item/W as obj, mob/user as mob, params) + + if(istype(W, /obj/item/device/mmi)) + if(mmi_move_inside(W,user)) + user << "[src]-[W] interface initialized successfuly" + else + user << "[src]-[W] interface initialization failed." + return + + if(istype(W, /obj/item/mecha_parts/mecha_equipment)) + var/obj/item/mecha_parts/mecha_equipment/E = W + spawn() + if(E.can_attach(src)) + if(!user.drop_item()) + return + E.attach(src) + user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") + else + user << "You were unable to attach [W] to [src]!" + return + if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) + if(add_req_access || maint_access) + if(internals_access_allowed(user)) + var/obj/item/weapon/card/id/id_card + if(istype(W, /obj/item/weapon/card/id)) + id_card = W + else + var/obj/item/device/pda/pda = W + id_card = pda.id + output_maintenance_dialog(id_card, user) + return + else + user << "Invalid ID: Access denied." + else + user << "Maintenance protocols disabled by operator." + else if(istype(W, /obj/item/weapon/wrench)) + if(state==1) + state = 2 + user << "You undo the securing bolts." + else if(state==2) + state = 1 + user << "You tighten the securing bolts." + return + else if(istype(W, /obj/item/weapon/crowbar)) + if(state==2) + state = 3 + user << "You open the hatch to the power unit." + else if(state==3) + state=2 + user << "You close the hatch to the power unit." + return + else if(istype(W, /obj/item/stack/cable_coil)) + if(state == 3 && (internal_damage & MECHA_INT_SHORT_CIRCUIT)) + var/obj/item/stack/cable_coil/CC = W + if(CC.use(2)) + clearInternalDamage(MECHA_INT_SHORT_CIRCUIT) + user << "You replace the fused wires." + else + user << "You need two lengths of cable to fix this mech!" + return + else if(istype(W, /obj/item/weapon/screwdriver)) + if(internal_damage & MECHA_INT_TEMP_CONTROL) + clearInternalDamage(MECHA_INT_TEMP_CONTROL) + user << "You repair the damaged temperature controller." + else if(state==3 && src.cell) + src.cell.forceMove(src.loc) + src.cell = null + state = 4 + user << "You unscrew and pry out the powercell." + src.log_message("Powercell removed") + else if(state==4 && src.cell) + state=3 + user << "You screw the cell in place." + return + + else if(istype(W, /obj/item/weapon/stock_parts/cell)) + if(state==4) + if(!src.cell) + if(!user.drop_item()) + return + var/obj/item/weapon/stock_parts/cell/C = W + user << "You install the powercell." + C.forceMove(src) + C.use(C.charge * 0.8) + src.cell = C + src.log_message("Powercell installed") + else + user << "There's already a powercell installed." + return + + else if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent != "harm") + user.changeNext_move(CLICK_CD_MELEE) + var/obj/item/weapon/weldingtool/WT = W + if(src.healthYou repair the damaged gas tank.
" + else + user.visible_message("[user] repairs some damage to [src.name].") + src.health += min(10, initial(src.health)-src.health) + else + user << "The welder must be on for this task!" + return 1 + else + user << "The [src.name] is at full integrity!" + return 1 + + else if(istype(W, /obj/item/mecha_parts/mecha_tracking)) + if(!user.unEquip(W)) + user << "\the [W] is stuck to your hand, you cannot put it in \the [src]!" + return + W.forceMove(src) + user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") + return + + else if(!(W.flags&NOBLUDGEON)) + user.changeNext_move(CLICK_CD_MELEE) // Ugh. Ideally we shouldn't be setting cooldowns outside of click code. + user.do_attack_animation(src) + log_message("Attacked by [W]. Attacker - [user]") + var/deflection = deflect_chance + var/dam_coeff = 1 + for(var/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/B in equipment) + if(B.attack_react(user)) + deflection *= B.deflect_coeff + dam_coeff *= B.damage_coeff + break + if(prob(deflection)) + user << "The [W] bounces off [src.name] armor." + src.log_append_to_last("Armor saved.") + return 0 + else + user.visible_message("[user] hits [src] with [W].", "You hit [src] with [W].") + take_damage(round(W.force*dam_coeff),W.damtype) + check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + return 1 + + +/obj/mecha/proc/mech_toxin_damage(mob/living/target) + playsound(src, 'sound/effects/spray2.ogg', 50, 1) + if(target.reagents) + if(target.reagents.get_reagent_amount("cryptobiolin") + force < force*2) + target.reagents.add_reagent("cryptobiolin", force/2) + if(target.reagents.get_reagent_amount("toxin") + force < force*2) + target.reagents.add_reagent("toxin", force/2.5) + + +/atom/proc/mech_melee_attack(obj/mecha/M) + return + +/obj/mecha/mech_melee_attack(obj/mecha/M) + if(M.damtype =="brute") + playsound(src, 'sound/weapons/punch4.ogg', 50, 1) + else if(M.damtype == "fire") + playsound(src, 'sound/items/Welder.ogg', 50, 1) + else + return + visible_message("[M.name] has hit [src].") + take_damage(M.force, damtype) + add_logs(M.occupant, src, "attacked", object=M, addition="(INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])") + return \ No newline at end of file diff --git a/code/game/mecha/mecha_parts.dm b/code/game/mecha/mecha_parts.dm index 0e6d689b85f..ec04357cf2c 100644 --- a/code/game/mecha/mecha_parts.dm +++ b/code/game/mecha/mecha_parts.dm @@ -175,27 +175,7 @@ /obj/item/mecha_parts/chassis/firefighter/New() ..() construct = new /datum/construction/mecha/firefighter_chassis(src) -/* -/obj/item/mecha_parts/part/firefighter_torso - name="Ripley-on-Fire Torso" - icon_state = "ripley_harness" -/obj/item/mecha_parts/part/firefighter_left_arm - name="Ripley-on-Fire Left Arm" - icon_state = "ripley_l_arm" - -/obj/item/mecha_parts/part/firefighter_right_arm - name="Ripley-on-Fire Right Arm" - icon_state = "ripley_r_arm" - -/obj/item/mecha_parts/part/firefighter_left_leg - name="Ripley-on-Fire Left Leg" - icon_state = "ripley_l_leg" - -/obj/item/mecha_parts/part/firefighter_right_leg - name="Ripley-on-Fire Right Leg" - icon_state = "ripley_r_leg" -*/ ////////// HONK @@ -334,13 +314,6 @@ icon_state = "odysseus_r_leg" origin_tech = "programming=2;materials=2;engineering=2" -/*/obj/item/mecha_parts/part/odysseus_armor - name="Odysseus Carapace" - icon_state = "odysseus_armor" - origin_tech = "materials=3;engineering=3" - construction_time = 200 - construction_cost = list("metal"=15000)*/ - ///////// Circuitboards diff --git a/code/game/mecha/mecha_topic.dm b/code/game/mecha/mecha_topic.dm new file mode 100644 index 00000000000..25ba30fde4a --- /dev/null +++ b/code/game/mecha/mecha_topic.dm @@ -0,0 +1,321 @@ + +//////////////////////////////////// +///// Rendering stats window /////// +//////////////////////////////////// + +/obj/mecha/proc/get_stats_html() + var/output = {" + [src.name] data + + + + +
+ [src.get_stats_part()] +
+
+ [src.get_equipment_list()] +
+
+
+ [src.get_commands()] +
+ + + "} + return output + + +/obj/mecha/proc/report_internal_damage() + var/output = null + var/list/dam_reports = list( + "[MECHA_INT_FIRE]" = "INTERNAL FIRE", + "[MECHA_INT_TEMP_CONTROL]" = "LIFE SUPPORT SYSTEM MALFUNCTION", + "[MECHA_INT_TANK_BREACH]" = "GAS TANK BREACH", + "[MECHA_INT_CONTROL_LOST]" = "COORDINATION SYSTEM CALIBRATION FAILURE - Recalibrate", + "[MECHA_INT_SHORT_CIRCUIT]" = "SHORT CIRCUIT" + ) + for(var/tflag in dam_reports) + var/intdamflag = text2num(tflag) + if(internal_damage & intdamflag) + output += dam_reports[tflag] + output += "
" + if(return_pressure() > WARNING_HIGH_PRESSURE) + output += "DANGEROUSLY HIGH CABIN PRESSURE
" + return output + + +/obj/mecha/proc/get_stats_part() + var/integrity = health/initial(health)*100 + var/cell_charge = get_charge() + var/tank_pressure = internal_tank ? round(internal_tank.return_pressure(),0.01) : "None" + var/tank_temperature = internal_tank ? internal_tank.return_temperature() : "Unknown" + var/cabin_pressure = round(return_pressure(),0.01) + var/output = {"[report_internal_damage()] + [integrity<30?"DAMAGE LEVEL CRITICAL
":null] + Integrity: [integrity]%
+ Powercell charge: [isnull(cell_charge)?"No powercell installed":"[cell.percent()]%"]
+ Air source: [use_internal_tank?"Internal Airtank":"Environment"]
+ Airtank pressure: [tank_pressure]kPa
+ Airtank temperature: [tank_temperature]°K|[tank_temperature - T0C]°C
+ Cabin pressure: [cabin_pressure>WARNING_HIGH_PRESSURE ? "[cabin_pressure]": cabin_pressure]kPa
+ Cabin temperature: [return_temperature()]°K|[return_temperature() - T0C]°C
+ [src.dna?"DNA-locked:
[src.dna] \[Reset\]
":null] + "} + return output + +/obj/mecha/proc/get_commands() + var/output = {"
+
Electronics
+ +
+ +
[get_equipment_menu()]
+ "} + return output + +/obj/mecha/proc/get_equipment_menu() //outputs mecha html equipment menu + var/output + if(equipment.len) + output += {"
+
Equipment
+
" + return output + +/obj/mecha/proc/get_equipment_list() //outputs mecha equipment list in html + if(!equipment.len) + return + var/output = "Equipment:
" + for(var/obj/item/mecha_parts/mecha_equipment/MT in equipment) + output += "
[MT.get_equip_info()]
" + output += "
" + return output + + +/obj/mecha/proc/get_log_html() + var/output = "[src.name] Log" + for(var/list/entry in log) + output += {"
[entry["time"]] [time2text(entry["date"],"MMM DD")] [entry["year"]]
+
[entry["message"]]
+ "} + output += "" + return output + + +/obj/mecha/proc/output_access_dialog(obj/item/weapon/card/id/id_card, mob/user) + if(!id_card || !user) return + var/output = {" + + + +

Following keycodes are present in this system:

"} + for(var/a in operation_req_access) + output += "[get_access_desc(a)] - Delete
" + output += "

Following keycodes were detected on portable device:

" + for(var/a in id_card.access) + if(a in operation_req_access) continue + var/a_name = get_access_desc(a) + if(!a_name) continue //there's some strange access without a name + output += "[a_name] - Add
" + output += "
Finish (Warning! The ID upload panel will be locked. It can be unlocked only through Exosuit Interface.)" + output += "" + user << browse(output, "window=exosuit_add_access") + onclose(user, "exosuit_add_access") + return + +/obj/mecha/proc/output_maintenance_dialog(obj/item/weapon/card/id/id_card,mob/user) + if(!id_card || !user) return + var/output = {" + + + + + [add_req_access?"Edit operation keycodes":null] + [maint_access?"Initiate maintenance protocol":null] + [(state>0) ?"Set Cabin Air Pressure":null] + + "} + user << browse(output, "window=exosuit_maint_console") + onclose(user, "exosuit_maint_console") + return + + + +///////////////// +///// Topic ///// +///////////////// + +/obj/mecha/Topic(href, href_list) + ..() + if(href_list["close"]) + return + + if(usr.incapacitated()) + return + + var/datum/topic_input/filter = new /datum/topic_input(href,href_list) + + if(in_range(src, usr)) + + if(href_list["req_access"] && add_req_access) + output_access_dialog(filter.getObj("id_card"),filter.getMob("user")) + + if(href_list["maint_access"] && maint_access) + var/mob/user = filter.getMob("user") + if(user) + if(state==0) + state = 1 + user << "The securing bolts are now exposed." + else if(state==1) + state = 0 + user << "The securing bolts are now hidden." + output_maintenance_dialog(filter.getObj("id_card"),user) + + if(href_list["set_internal_tank_valve"] && state >=1) + var/mob/user = filter.getMob("user") + if(user) + var/new_pressure = input(user,"Input new output pressure","Pressure setting",internal_tank_valve) as num + if(new_pressure) + internal_tank_valve = new_pressure + user << "The internal pressure valve has been set to [internal_tank_valve]kPa." + + if(href_list["add_req_access"] && add_req_access && filter.getObj("id_card")) + operation_req_access += filter.getNum("add_req_access") + output_access_dialog(filter.getObj("id_card"),filter.getMob("user")) + + if(href_list["del_req_access"] && add_req_access && filter.getObj("id_card")) + operation_req_access -= filter.getNum("del_req_access") + output_access_dialog(filter.getObj("id_card"),filter.getMob("user")) + + if(href_list["finish_req_access"]) + add_req_access = 0 + var/mob/user = filter.getMob("user") + user << browse(null,"window=exosuit_add_access") + + if(usr != occupant) + return + + if(href_list["update_content"]) + send_byjax(src.occupant,"exosuit.browser","content",src.get_stats_part()) + + if(href_list["select_equip"]) + var/obj/item/mecha_parts/mecha_equipment/equip = filter.getObj("select_equip") + if(equip) + src.selected = equip + src.occupant_message("You switch to [equip]") + src.visible_message("[src] raises [equip]") + send_byjax(src.occupant,"exosuit.browser","eq_list",src.get_equipment_list()) + + if(href_list["rmictoggle"]) + radio.broadcasting = !radio.broadcasting + send_byjax(src.occupant,"exosuit.browser","rmicstate",(radio.broadcasting?"Engaged":"Disengaged")) + + if(href_list["rspktoggle"]) + radio.listening = !radio.listening + send_byjax(src.occupant,"exosuit.browser","rspkstate",(radio.listening?"Engaged":"Disengaged")) + + if(href_list["rfreq"]) + var/new_frequency = (radio.frequency + filter.getNum("rfreq")) + if (!radio.freerange || (radio.frequency < 1200 || radio.frequency > 1600)) + new_frequency = sanitize_frequency(new_frequency) + radio.set_frequency(new_frequency) + send_byjax(src.occupant,"exosuit.browser","rfreq","[format_frequency(radio.frequency)]") + + if (href_list["view_log"]) + src.occupant << browse(src.get_log_html(), "window=exosuit_log") + onclose(occupant, "exosuit_log") + + if (href_list["change_name"]) + var/newname = stripped_input(occupant,"Choose new exosuit name","Rename exosuit","", MAX_NAME_LEN) + if(newname && trim(newname)) + name = newname + else + alert(occupant, "nope.avi") + + if (href_list["toggle_id_upload"]) + add_req_access = !add_req_access + send_byjax(src.occupant,"exosuit.browser","t_id_upload","[add_req_access?"L":"Unl"]ock ID upload panel") + + if(href_list["toggle_maint_access"]) + if(state) + occupant_message("Maintenance protocols in effect") + return + maint_access = !maint_access + send_byjax(src.occupant,"exosuit.browser","t_maint_access","[maint_access?"Forbid":"Permit"] maintenance protocols") + + if(href_list["dna_lock"]) + if(occupant && !iscarbon(occupant)) + occupant << " You do not have any DNA!" + return + dna = src.occupant.dna.unique_enzymes + occupant_message("You feel a prick as the needle takes your DNA sample.") + + if(href_list["reset_dna"]) + dna = null + + if(href_list["repair_int_control_lost"]) + occupant_message("Recalibrating coordination system...") + log_message("Recalibration of coordination system started.") + var/T = loc + spawn(100) + if(T == loc) + clearInternalDamage(MECHA_INT_CONTROL_LOST) + occupant_message("Recalibration successful.") + log_message("Recalibration of coordination system finished with 0 errors.") + else + occupant_message("Recalibration failed!") + log_message("Recalibration of coordination system failed with 1 error.",1) + diff --git a/code/game/mecha/working/ripley.dm b/code/game/mecha/working/ripley.dm index 78fcf6d541a..a83a4a8c422 100644 --- a/code/game/mecha/working/ripley.dm +++ b/code/game/mecha/working/ripley.dm @@ -13,15 +13,9 @@ var/list/cargo = new var/cargo_capacity = 15 -/* -/obj/mecha/working/ripley/New() - ..() - return -*/ - /obj/mecha/working/ripley/Move() . = ..() - if(. && (locate(/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp) in equipment)) + if(. && (locate(/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp) in equipment)) var/obj/structure/ore_box/ore_box = locate(/obj/structure/ore_box) in cargo if(ore_box) for(var/obj/item/weapon/ore/ore in get_turf(src)) @@ -29,10 +23,11 @@ update_pressure() /obj/mecha/working/ripley/Destroy() - while(src.damage_absorption.["brute"] < 0.6) - new /obj/item/asteroid/goliath_hide(src.loc) - src.damage_absorption.["brute"] = src.damage_absorption.["brute"] + 0.1 //If a goliath-plated ripley gets killed, all the plates drop - for(var/atom/movable/A in src.cargo) + var/hides = round((initial(damage_absorption["brute"]) - damage_absorption["brute"])*10) + for(var/i=0, i < hides, i++) + new /obj/item/asteroid/goliath_hide(loc) //If a goliath-plated ripley gets killed, all the plates drop + damage_absorption["brute"] = initial(damage_absorption["brute"]) + for(var/atom/movable/A in cargo) A.loc = loc step_rand(A) cargo.Cut() @@ -40,30 +35,25 @@ /obj/mecha/working/ripley/go_out() ..() - if (src.damage_absorption["brute"] < 0.6 && src.damage_absorption["brute"] > 0.3) - src.overlays = null - src.overlays += image("icon" = "mecha.dmi", "icon_state" = "ripley-g-open") - else if (src.damage_absorption.["brute"] == 0.3) - src.overlays = null - src.overlays += image("icon" = "mecha.dmi", "icon_state" = "ripley-g-full-open") + update_icon() /obj/mecha/working/ripley/moved_inside(var/mob/living/carbon/human/H as mob) ..() - if (src.damage_absorption["brute"] < 0.6 && src.damage_absorption["brute"] > 0.3) - src.overlays = null - src.overlays += image("icon" = "mecha.dmi", "icon_state" = "ripley-g") - else if (src.damage_absorption["brute"] == 0.3) - src.overlays = null - src.overlays += image("icon" = "mecha.dmi", "icon_state" = "ripley-g-full") + update_icon() /obj/mecha/working/ripley/mmi_moved_inside(var/obj/item/device/mmi/mmi_as_oc as obj,mob/user as mob) ..() - if (src.damage_absorption["brute"] < 0.6 && src.damage_absorption["brute"] > 0.3) - src.overlays = null - src.overlays += image("icon" = "mecha.dmi", "icon_state" = "ripley-g") - else if (src.damage_absorption["brute"] == 0.3) - src.overlays = null - src.overlays += image("icon" = "mecha.dmi", "icon_state" = "ripley-g-full") + update_icon() + +/obj/mecha/working/ripley/update_icon() + ..() + if (damage_absorption["brute"] < 0.6 && damage_absorption["brute"] > 0.3) + overlays = null + overlays += image("icon" = "mecha.dmi", "icon_state" = "ripley-g-open") + else if (damage_absorption.["brute"] == 0.3) + overlays = null + overlays += image("icon" = "mecha.dmi", "icon_state" = "ripley-g-full-open") + /obj/mecha/working/ripley/firefighter desc = "Autonomous Power Loader Unit. This model is refitted with additional thermal protection." @@ -76,6 +66,7 @@ max_equip = 5 // More armor, less tools wreckage = /obj/structure/mecha_wreckage/ripley/firefighter + /obj/mecha/working/ripley/deathripley desc = "OH SHIT IT'S THE DEATHSQUAD WE'RE ALL GONNA DIE" name = "\improper DEATH-RIPLEY" @@ -88,7 +79,7 @@ /obj/mecha/working/ripley/deathripley/New() ..() - var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/tool/safety_clamp + var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/kill ME.attach(src) return @@ -100,10 +91,10 @@ ..() //Attach drill if(prob(25)) //Possible diamond drill... Feeling lucky? - var/obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill/D = new /obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill + var/obj/item/mecha_parts/mecha_equipment/drill/diamonddrill/D = new /obj/item/mecha_parts/mecha_equipment/drill/diamonddrill D.attach(src) else - var/obj/item/mecha_parts/mecha_equipment/tool/drill/D = new /obj/item/mecha_parts/mecha_equipment/tool/drill + var/obj/item/mecha_parts/mecha_equipment/drill/D = new /obj/item/mecha_parts/mecha_equipment/drill D.attach(src) //Add possible plasma cutter @@ -115,12 +106,12 @@ cargo.Add(new /obj/structure/ore_box(src)) //Attach hydraulic clamp - var/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp/HC = new /obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp + var/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/HC = new /obj/item/mecha_parts/mecha_equipment/hydraulic_clamp HC.attach(src) for(var/obj/item/mecha_parts/mecha_tracking/B in src.contents)//Deletes the beacon so it can't be found easily qdel(B) - var/obj/item/mecha_parts/mecha_equipment/tool/mining_scanner/scanner = new /obj/item/mecha_parts/mecha_equipment/tool/mining_scanner + var/obj/item/mecha_parts/mecha_equipment/mining_scanner/scanner = new /obj/item/mecha_parts/mecha_equipment/mining_scanner scanner.attach(src) /obj/mecha/working/ripley/Exit(atom/movable/O) @@ -159,9 +150,9 @@ if(pressure < 20) step_in = 3 - for(var/obj/item/mecha_parts/mecha_equipment/tool/drill/drill in equipment) + for(var/obj/item/mecha_parts/mecha_equipment/drill/drill in equipment) drill.equip_cooldown = initial(drill.equip_cooldown)/2 else step_in = 5 - for(var/obj/item/mecha_parts/mecha_equipment/tool/drill/drill in equipment) + for(var/obj/item/mecha_parts/mecha_equipment/drill/drill in equipment) drill.equip_cooldown = initial(drill.equip_cooldown) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index b71cb447ff7..3879f0071e4 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -430,7 +430,7 @@ var/global/image/fire_overlay = image("icon" = 'icons/effects/fire.dmi', "icon_s /obj/item/singularity_pull(S, current_size) spawn(0) //this is needed or multiple items will be thrown sequentially and not simultaneously if(current_size >= STAGE_FOUR) - throw_at(S,14,3) + throw_at(S,14,3, spin=0) else ..() /obj/item/acid_act(var/acidpwr, var/acid_volume) diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index 6ef0a3fa4f8..26dc5d73cb7 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -243,7 +243,7 @@ var/global/list/obj/item/device/pda/PDAs = list() /obj/item/device/pda/proc/can_use(mob/user) if(user && ismob(user)) - if(user.stat || user.restrained() || user.paralysis || user.stunned || user.weakened) + if(user.incapacitated()) return 0 if(loc == user) return 1 diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 9dc2e9482ba..92004a21780 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -398,7 +398,7 @@ var/list/TYPES_SHORTCUTS = list( /obj/item/weapon/reagent_containers = "REAGENT_CONTAINERS", /obj/machinery/atmospherics = "ATMOS", /obj/machinery/portable_atmospherics = "PORT_ATMOS", - /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack = "MECHA_MISSILE_RACK", + /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/missile_rack = "MECHA_MISSILE_RACK", /obj/item/mecha_parts/mecha_equipment = "MECHA_EQUIP", ) diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm index 2f3218f4e27..11bca2a452b 100644 --- a/code/modules/mining/mine_turfs.dm +++ b/code/modules/mining/mine_turfs.dm @@ -487,7 +487,7 @@ var/global/list/rockTurfEdgeCache return /* else if(istype(AM,/obj/mecha)) var/obj/mecha/M = AM - if(istype(M.selected,/obj/item/mecha_parts/mecha_equipment/tool/drill)) + if(istype(M.selected,/obj/item/mecha_parts/mecha_equipment/drill)) src.attackby(M.selected,M) return*/ //Aparantly mechs are just TOO COOL to call Bump(), so fuck em (for now) diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm index d7c223124b6..7779f227a18 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -72,7 +72,10 @@ var/const/ALIEN_AFK_BRACKET = 450 // 45 seconds else affected_mob.overlays += image('icons/mob/alien.dmi', loc = affected_mob, icon_state = "burst_stand") spawn(6) - var/mob/living/carbon/alien/larva/new_xeno = new(affected_mob.loc) + var/location = get_turf(affected_mob) + if(!location) + location = affected_mob.loc + var/mob/living/carbon/alien/larva/new_xeno = new(location) new_xeno.key = C.key new_xeno << sound('sound/voice/hiss5.ogg',0,0,0,100) //To get the player's attention if(gib_on_success) diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index cf6548b0e05..840f19ef3bb 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -10,10 +10,12 @@ src.reset_view(null) src.unset_machine() - src.updatehealth() + updatehealth() update_gravity(mob_has_gravity()) + update_action_buttons() + if (src.malfhack) if (src.malfhack.aidisabled) src << "ERROR: APC access disabled, hack attempt canceled." diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index 90c60fc5769..44e06e7d5c2 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -26,7 +26,7 @@ var/last_failed_movement = 0//Will not move in the same dir if it couldnt before, will help with the getting stuck on fields thing var/last_warning var/consumedSupermatter = 0 //If the singularity has eaten a supermatter shard and can go to stage six - allow_spin = 0 + /obj/singularity/New(loc, var/starting_energy = 50, var/temp = 0) //CARN: admin-alert for chuckle-fuckery. admin_investigate_setup() diff --git a/code/modules/research/designs/mecha_designs.dm b/code/modules/research/designs/mecha_designs.dm index 95d25e4592b..a93cf7e7b52 100644 --- a/code/modules/research/designs/mecha_designs.dm +++ b/code/modules/research/designs/mecha_designs.dm @@ -227,7 +227,7 @@ id = "mech_grenade_launcher" build_type = MECHFAB req_tech = list("combat" = 3) - build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang + build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang materials = list(MAT_METAL=22000,MAT_GOLD=6000,MAT_SILVER=8000) construction_time = 100 category = list("Exosuit Equipment") @@ -238,7 +238,7 @@ id = "mech_missile_rack" build_type = MECHFAB req_tech = list("combat" = 6, "materials" = 6) - build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack + build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/missile_rack materials = list(MAT_METAL=22000,MAT_GOLD=6000,MAT_SILVER=8000) construction_time = 100 category = list("Exosuit Equipment") @@ -249,7 +249,7 @@ id = "clusterbang_launcher" build_type = MECHFAB req_tech = list("combat"= 5, "materials" = 5, "syndicate" = 3) - build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang/clusterbang + build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang/clusterbang materials = list(MAT_METAL=20000,MAT_GOLD=10000,MAT_URANIUM=10000) construction_time = 100 category = list("Exosuit Equipment") @@ -282,7 +282,7 @@ id = "mech_rcd" build_type = MECHFAB req_tech = list("materials" = 4, "bluespace" = 3, "magnets" = 4, "powerstorage"=4, "engineering" = 4) - build_path = /obj/item/mecha_parts/mecha_equipment/tool/rcd + build_path = /obj/item/mecha_parts/mecha_equipment/rcd materials = list(MAT_METAL=30000,MAT_GOLD=20000,MAT_PLASMA=25000,MAT_SILVER=20000) construction_time = 1200 category = list("Exosuit Equipment") @@ -348,7 +348,7 @@ id = "mech_diamond_drill" build_type = MECHFAB req_tech = list("materials" = 4, "engineering" = 3) - build_path = /obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill + build_path = /obj/item/mecha_parts/mecha_equipment/drill/diamonddrill materials = list(MAT_METAL=10000,MAT_DIAMOND=6500) construction_time = 100 category = list("Exosuit Equipment") diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm index ce602a5e800..c3a14613b65 100644 --- a/code/modules/research/designs/mechfabricator_designs.dm +++ b/code/modules/research/designs/mechfabricator_designs.dm @@ -490,7 +490,7 @@ name = "Exosuit Engineering Equipment (Hydraulic Clamp)" id = "mech_hydraulic_clamp" build_type = MECHFAB - build_path = /obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp + build_path = /obj/item/mecha_parts/mecha_equipment/hydraulic_clamp materials = list(MAT_METAL=10000) construction_time = 100 category = list("Exosuit Equipment") @@ -499,7 +499,7 @@ name = "Exosuit Engineering Equipment (Drill)" id = "mech_drill" build_type = MECHFAB - build_path = /obj/item/mecha_parts/mecha_equipment/tool/drill + build_path = /obj/item/mecha_parts/mecha_equipment/drill materials = list(MAT_METAL=10000) construction_time = 100 category = list("Exosuit Equipment") @@ -508,7 +508,7 @@ name = "Exosuit Engineering Equipement (Mining Scanner)" id = "mech_mscanner" build_type = MECHFAB - build_path = /obj/item/mecha_parts/mecha_equipment/tool/mining_scanner + build_path = /obj/item/mecha_parts/mecha_equipment/mining_scanner materials = list(MAT_METAL=5000,MAT_GLASS=2500) construction_time = 50 category = list("Exosuit Equipment") @@ -517,7 +517,7 @@ name = "Exosuit Engineering Equipment (Extinguisher)" id = "mech_extinguisher" build_type = MECHFAB - build_path = /obj/item/mecha_parts/mecha_equipment/tool/extinguisher + build_path = /obj/item/mecha_parts/mecha_equipment/extinguisher materials = list(MAT_METAL=10000) construction_time = 100 category = list("Exosuit Equipment") @@ -526,7 +526,7 @@ name = "Exosuit Engineering Equipment (Cable Layer)" id = "mech_cable_layer" build_type = MECHFAB - build_path = /obj/item/mecha_parts/mecha_equipment/tool/cable_layer + build_path = /obj/item/mecha_parts/mecha_equipment/cable_layer materials = list(MAT_METAL=10000) construction_time = 100 category = list("Exosuit Equipment") @@ -535,7 +535,7 @@ name = "Exosuit Medical Equipment (Mounted Sleeper)" id = "mech_sleeper" build_type = MECHFAB - build_path = /obj/item/mecha_parts/mecha_equipment/tool/sleeper + build_path = /obj/item/mecha_parts/mecha_equipment/sleeper materials = list(MAT_METAL=5000,MAT_GLASS=10000) construction_time = 100 category = list("Exosuit Equipment") @@ -544,7 +544,7 @@ name = "Exosuit Medical Equipment (Syringe Gun)" id = "mech_syringe_gun" build_type = MECHFAB - build_path = /obj/item/mecha_parts/mecha_equipment/tool/syringe_gun + build_path = /obj/item/mecha_parts/mecha_equipment/syringe_gun materials = list(MAT_METAL=3000,MAT_GLASS=2000) construction_time = 200 category = list("Exosuit Equipment") @@ -580,7 +580,7 @@ name = "H.O.N.K Mousetrap Mortar" id = "mech_mousetrap_mortar" build_type = MECHFAB - build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/mousetrap_mortar + build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/mousetrap_mortar materials = list(MAT_METAL=20000,MAT_BANANIUM=5000) construction_time = 300 category = list("Exosuit Equipment") @@ -589,7 +589,7 @@ name = "H.O.N.K Banana Mortar" id = "mech_banana_mortar" build_type = MECHFAB - build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/banana_mortar + build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/banana_mortar materials = list(MAT_METAL=20000,MAT_BANANIUM=5000) construction_time = 300 category = list("Exosuit Equipment") diff --git a/data/mode.txt b/data/mode.txt index cf1ffd5c991..ef18df98db4 100644 --- a/data/mode.txt +++ b/data/mode.txt @@ -1 +1 @@ -extended \ No newline at end of file +extended diff --git a/html/changelogs/phil235-MechRefactoring.yml b/html/changelogs/phil235-MechRefactoring.yml new file mode 100644 index 00000000000..c8e188f6fc5 --- /dev/null +++ b/html/changelogs/phil235-MechRefactoring.yml @@ -0,0 +1,4 @@ +author: phil235 +delete-after: True +changes: + - rscadd: "Mechs no longer use object verbs and the exosuit interface tab. They now use action buttons. Mechs now have a cycle equipment action button to change weapon faster than by using the view stats window. Mechs get two special alerts when their battery or health is low. Mechs now properly consume energy when moving and having their lights on. Using a mech tool that takes time to act now shows a progress bar (e.g. mech drill)." diff --git a/icons/mob/actions.dmi b/icons/mob/actions.dmi index 4ced7caebed4b66461ad6c277e4396986b1b4055..0636ebac478a469a37372c34de9684144a836ceb 100644 GIT binary patch literal 29912 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ⅈ~h$lggt!LC3Xna&cXPl6~UHVm-%&^0=&8zoWKBj+*jmL~Ae zl!1=IA1TB&r9HlDghA(!6~Qul(AQaEy&jbK!9cRhW?1jfncy zgQrxR5yZ{<{M>?87_>;#AE7@q=lQS~8fOtd@>JN9gXNjJh@n=a`K6hqPo$dnCTj|{ zs|YYXN>y#Q_!T>Y=tmF&%?VAZMAphE=g;2S__9>eUK^LI9q36-cXZ#zg(?oay@NV` z#Z+CY(rSK%{D=Mf^<&(WN8yPxT_oREh~8PRq$Vk{Toc~$%xYwPv3~p)L zb|%-y=OECEjgC!D(Xww0VkCMYkuI}-DJ~`|9TZ$y9ojCD}i|#E!@DL&uQ+zjB7}bnrPiz zl#ZhEE+3lVJD7X6e65`&m?#tAo(NQOa8y}B~0uj;@$Uqyf_+Y)_IRLA%o_s6?V@!gAKSYN} z%BTC|mDp0?t++mEB(jk(AEfjzHF?sGzY7g+7?SWfL@;{<2S5ZQgY}6BB@8n(X-#16 z_FQT`_42s#&||%#xgbHK_t4;dlaV%ItbGhM?L>8 zoIKe7Iq5YV$_(g}$|`c6p>LrrvHSUPWT63(a3&;^Xx7^+`yfCHkoxVkk7MeO@?+!# zK6$z^Hat^08gyqanx<_Yu;tA(U}faXHzON}ceKB7I(wx1zA->1aaGrP3vXzEM5-U0 z$Lx9H0C`A9`$_#bczQiY7UpZxvuO1c^ri*WriKXprzvgE5`CS`8zo&HF5@nL zmi)`ImWn*7HmMdvUBV`$5YpLAk*hWCAss&wV`F2@`6!P$vdCYPhNg2+b7To$b)YYC zWF>I~!=Px(c0%02KNJvfDQL=Oo9K`mHe+%+himI{2`?pH05~A@@IL~#((^dZYaJ4A zq+mg^*UzAY9e34_RdybpX8NF%P7KOl1ld+YKVJwlG|m&ZgR<2;Q(~U;DJQZz9=cy) zCpvsl{;6}YIX!OEk}6j(@2E{eV@W7%6(7_0b@Ak8aC#gCLEuT-vRxwrR{Oe`_BCl? zw0fdSPM=`B5_7+q{jZJ>oK@P~8Om&LzKvKU8J21*o*W-{;?q1Y;A3a+%AVXk^Mlg` zOkIdmhu2@4UvKR`$Q3dlM~Hl3`C=p6T}sASTm#EDn2-MRVBTE|(C|JtoMC~SzBe4h z$K>`D3{pP9pVI0_pB?hdA3Xeo;>@9BP;DdxqQ5r%{$)_KSe3*1rW7L*E``nLDhV%0 z=hz5Jw;+)nfg$BM$I*w0q>wOd5-Ax)&aTf%^rIJXa47(x#Ci6aR_@fbVZo4ZN+_)F z86{p0XIAK^>REP}kMMIolfT^|UfD^Z(g)p<2tLVc9RfOSs@SAItb4ZBA5tA+sMPj8Qmc z)0ejEiV*{SAf00e;)fv#gZaR-qlv*fauEFwLp~}z%oeLkSDzNc{KZ3x9Mx3uOqS*y zEy0nwNZI-JoUO=1gdc(%v*q(Q8X@UHQrD}Q3;Ee|_;VhKT6cMCBnE;H*ea8{OPHP z2{gR%+&saU3r()()3LQ5<2o82gV&V5E9{GHz)}vS(=YuOF$$6;_nTiw*Hy)czTP*6 z<>1C#kMZLo13Y`l;y{%iKwY&V{ST^vHFro)0L2mfPKb6Qu+7M~#gOl>s4ZAW?m7`A zM0Gp4X~X>@5HLnX&b2xSI-`s#=QBH6b24ZP4MCu*-J8`rU=VEf z^|{?bfZD|SheLDj>2A4E4~=)j(`okO`S#StOB@GBfsBLHRO@prmrH+T0niF?8q5)q7PaQk|M0 zua}*D9V&bQGpD7oc_ogVcXx+uUVD!SE|PJD^0XojYCcC_| z8$8efJ=e!#$&XU%Pw4A*zR4-vH@;uGI1KQxdI2DpT$D?N6$BTnC%q-<4^`LV<#_w~8LIK)@78&N7y6x_fxNEoE%49M{N3$u^MH!w9K24D;k(Eh&hXuDGdRB^7FB&c}R~r1-sQ)iYvqb6@+agQ`o>0fhQ%rlxB2 z3+_8M2=MC-fFY}W>g&Y%z=dxGX&$cSq9vLSKff8DJC^Of0rhyt_+&GUE}$iI6-cPm-~=7TnuW269H>-J8NfI-D# z@q+l!Wf2qXkNjldP$oPrT8dOwigO)+>%iCaodq905XMW3Lr; zenlW*%g!m2lEiFa9Q_7@xe0D__lCl`_*twdQbj`%vhbt$0V~ta4|4Y4uJfX(hk=FQ z-+Dd_rrO*!QA?*2AiiclHnP9psHPa5i=VmGmC!p8axoi!U$NrzB6t3t>K?y&d`L_h z_;&hUlrRJ#1M{6p;C=mh8AGVD&0j-R^uHboABJaK>37wFdwASv5>}ewce<7bN-%FE z0(s}z1jq4jic|XipyOkU&k2mfvc#141 zlGl^g|Kxy(KTGc+lt1z@qS$e3$5xm09r63%3wi%ZVuo;{xm5uwxP4}~KjXVH%4v}+ z5)_Qfi(({|>KmM&sy1gh&WT2M71PBp#f|*|vA+MzaGTauYH^INUXM!rf*~|3Z&J3P zVa*7~!_1o-#jR}(m)bcZ#PNa0R5pSTNu>Uo5P@+lWCnpt$pE4(k}WF$W^GTmJdDu? z;RiBC4oW=rsGjKKJ8`Q@G~hXUGiz+-MISrx@^(uFuS!Bgk}ua6RdCSc@=6UGjXJJ*H6l&m>&9ZXbTpw zFT#dO!64Ct9xhRk6!l3Kk$>}5tEm7A;mI&N9X_ynwIsUw8%B4VCTAa!kf|Ggpo@uV zs+EeoR-s=1lT3rsBkg!B39oBiwnNYo1j@X|`<6}_;RbPjP0H9CVGQdX{QzbcAgzOq zyjFAs^CemsROr5wS9_3>Fu~A3hFH=a4-xG8?5A#(79GHu#rT@^g(Mt6fC*$@ zDddr&I66q5eN@<=!FzSk^z~oBKvIt(iCDJNhGF_zVi(>si6Jbn7J?T1riN6Mpi~rq zS5I?Ny~4Ga&IsY`pg`k8;xm(foFhqLQ)W-=_HMDAEq}srLyYTs*@pRMwai0`hw_Ji zi{X;guLv;B--SvJzg8q72t@y)kroPX_3=nYz?Kd9wlbmvPF=-@KGU)*UbO&2Cq{s# ze21jWAPy9p(dcRpWXwi7L3lbAh9D^&wsZ7E`I3ZRCreVN~Emjp}wgG7gJLIrz!kh*QUh|9o^-YB-E zbExZ3!Xn7;@cWUJp)~<+sCdAo~c6=f3Mv2n-&+@f#y%3N>3AT$`q5`rI?@m#7q zuZO{d`*k;+M?HwHVA5xhGggWIHz-}cZJCc<5PBv1dJoTEN?APh@cZK7A&43&MMXuA z!@E|`TpQkdHZ9V?prXrmQ(VCxVmX5M(>YRb{H55@y_*$MhrX8O=l6|`kIPE^8a6!m zz9(qXj=`6gemiNpf`;w~VymM5!}gG+c-q?iwD{hvKSSOQWbipDX?S zS9*Fma+mi6ay*6qJ$hAI9}_rtD(##?xb0AW*ntYUA_ zfV2)1On_D}{-;NWy_va1Qoq!Y zAfm{3NOR^uu+Ws+2ot2z?q5F5YSzWVIs_%reJ6}@<_U>=v_5<1h?VsXRJXeM?72C&0_g3r+Kdw}Rat z8MNaUPL7T)9^$D87+UzJmEH9rBHk)bBp)RU==0IO0q;RRu)g38&k_I-LT^*CBOWhj zJYj(%ZTr0QB700*s7+eg-IQ?K-6*@kQeKvb#;i?$!Hxzg^<&&@f@6guWT6d_{9|BP z`TQ+E1k4-onw4SLAR6gAVSpOv+jIJDDMV`~EA{=a@YQ=Ae8SOm#)5DJUY?#@1a82T zaNSCOJbz&H`4G#$^5XEW6hmpRU!{!gOnp=T-2(YF2+W9#q1q3bW}b~Ri{@SF7?Ncc zpFU*+Qf&eFxP2jx!yrenx3`Z>OzaE5{#TOANIZQ~oW&%P{KU93;?L=we>4MrHfzz% z&>~^C#P)!}ecbI0VWVK0LBzvd@q>8&3P#V$Hvv>mu~c>)1ZHXyt_zk%JGlw(67ywDEbK4l^tB7-gzl)*Fb6y3|o_% zuBmjKc5$xTi&KB;+nTqRnpPL3(dcW-9o&u&b(1uC2E2@-0qAQEiCpWJNuSr3gmM3N z#7NH8^*r#tq5pMrzK{O5-1F*v02AgrAa|sK)d~OiL=d~(;};jZcVF(p32(~{ium7_ zEc-jI{rd+(u`j7o2jC=X+~ur``ik@PmO2F(C&h2iH>ZxHUrgN_cZGdux#hA9;~~Uo z3;bnx$tN>JhpT$K$zPHE631*~ZLP)TX^YhW1Q4he_;J8?j>;pCAwLZk`)+?mR^?h2=!bWXCu1Tzp)B=l)HN(p^6GU+R$51|87vkks0E6 zZ`+TSQZ0WExa%i(1I80Fl1TpEEIYlDdd0(s6F_kuXN`E0%MExez#R69z2koU&>b{H ziQCG|t+oZ8slHKuH5=cE-gmu0M&TMJ@iso?*()IHD}q#c)v*1q6e%9BjK>{z2ae$?fUw;WNXyfqB9xc%#U@pNf7cj%jT`3uQW33r#`~H&uQFdy$m|GKS+UBMH0^enwd#|=ZEbj+T za+0ZP+f*q}KuE)jHit$9a!CmMm>#TfwSGksMl0r*lq6a+PlvNChsWzTxr+(N&bno* z`ge)rKG%9?mrDSupNz3Nl+O31Sh%Pv1)68m7ts9E%7CYNcT_d{n%h0Pz_;b~&D69T z>m4;AQLL!0S6sx4H4fO7I!c__Xl4QBNcv&rx$KRX-)RIPpjuZ=_YcpUnBZG%uFFB` zL8SocE6Grw<+hUhD~ZvtbElGKMv=}67lf7LVx+$;&h+$?%Dls-zi1U&O40~QWNr}w z28xNVrgU}q%ltFSvkj5%7xO_$bMI(hd=Z+KP<t}wceIb2?}VdW3m<6WVNfI%#w@<3?! z5N~tt%1E$d^9lry4s0~(qX_`5gdFF_Zg1)}5QGp%KUsz>DZR-chM9Cxf@a9HbioXM z*aaz0Qy*kJh44IY%qe-Ca3dyS*n(&~P6ZdeomYhB-a{yr*}hZi=fc8eN{;I&IDi6= z6U|of3yia=O525*-3Rc@KrYzW=GxUaBq3R7#;k-%5E%B1V}=3jWwvg<8e>~oiX78E z_w~xjtAoC%st-b?jwj%jchDNmP=s-)S2Rw4c=^Vj86{ts8Y_qguCKFpeQbI6*l4E0 za;oaiAWk$-^wUR_E)0|c_W9HzPEYUkn{tX5Wrwx`@exspA!)Z_V$~VAISUVPm*h{Z zDuBEuQc??8*s<#PV5s8_zKO~P^&B*DK26!Ih2Fn+_}3W%M0HWfX#+NicF<4ZysV7> zoJ|c-Y_^p#dJR1aXON(NlA;4uIw_}YuK7$-0%q@g)B{BJYg5N|TXxDyxF}1f;>pm+ z#dmmOKI6k_kc`l0twqmkT z6TihQf*2RHTt4F&6ZZ;_SgCUhT+}`<^%K0%fsYCX8GW1ReHVKD^SVv7c|@Sg;mDEx z%Bv*;HTu%i6XO0}mQRo6SS|h%dE^YlP@rNa-U)oY`Zy(=4HG(v=8*@Ht zJfcXi{AXAotaef+%vlXc+P@-ERP1~tDI0Knyq#Mt@g(;t(2O7fz~c+H1-v_KA|z-M z*us`<{>}ev0AgrRNZM?*{}*f{(G|%YmsDcgw*Hw*R=WuDTGCNRj2s zg(4@=(=k z`JwT_$76_u&{y$a1MeCT|LE7kOfDovz$r znCLh?zu$5q0WqbUfPgm~w6FcZZ~!V$CUz)r3_nNh^=I*PA}-8k0XG2<3xD94B+M2( zy~BM=otlLc%0?1Kj*rzD+iajg_GB?W`(=`)RlA|&J4kb|#|p;PHO~E+qbD~g&cabt zT2#O+i{KKwBqupsh!Y#ttgysxT=5kn?rQN5_4XJ)W16Ryv9lzBuI{`72D;$=wqu#$ z{mkH2GWg|sD(`Lxz;gn>WaJyq*SmebKB&!6@MV(Rd6eADL>VB@I zza5U7OJK&X85g9^$3KGFi1hn*s{IDl5ef zo>X?KQWU(6&4P??h$156i4uO^T)T^d5;93N0cJnLhd|ggA`Rp48BYiS!$Aq=uZfr{ zdJn9ZQF(H22v0V3_~Cua2)y@ms7rRzhj;ku@mRJNZ!cC;Ix!7$LfDe5-O$pfyb z(L-U7=5FdcoltF=Fu~`pN(NF0YshIR!^DfcnKg4CAi_`53;FS0j#5VK=^e^5Q)2CsY z4_Egi2@S;`&qT2?AUdWDH%Y_E=TDwnLo7llUa^vRxnz(6CTl~{81T>uC)NJbR96uy z#(zPcNHaymMg7J7=RNeSs#HdJgwkzKRPWCU288A1(JK2gazMYo${_RdfqM%JNcA`` z4*HG+NeoT^p|qUaZ({#ifJ2x9$&y zd_g|T{#zxw!=LY6>{12M@rCKW%`S}_TKIfbLTfe*Gp6H`5;Xm`62 z`9oV0(sKwyCdtNy&P*mDu-V<6+ctXCeFwqNhgPDE%pSJO))+bdzDJ}3T-i%DD(4Xa zDBg2A1&e!-bC_C|OvF6@{)oPi*0>aKJWVA^=zt0f&)B`w|6@5h9kV_Cj0=~!YXDCt;z?U zFBmc7&05mv1tJ3l==`M9ZtlcB}n`sp;@8 z-e-SQU#0TZ+{|b;=}xW)BwhrNmlflFkZ}1Dug*0*!f1IW9caz>pR+)nyEwT2*g&*B z#PIErh3ILw$Nv(VDE6Onb0G`tRjb1P6O-?jOR*%aqyz)DO&>RFY@+e5C zlqlb#f#8ED8aE-4Q-Jr&kdnTdhJ;P@fZTYl&#fjpkwRDXS>>}Ukwdp`}>7)ADcMR4^yAPWCbA*q?;Sp@o|7`<~`8U zU?_{+O?_Y%)9`vn1TFYExqr|s@AB;l=qE2;xWL00q_2*Qw=U&a*>;o_r%OOP1T!ek z`LA{KV9>-M<3H*+wTIVlSqnW>?;82DKq;?0!1zTbPkvP4>M(7WKEohyEE(ZEyZvu9 z+~hp<2~P_>vP+4;VwzYDiTR)avUpMshW^s2GUmcr*rl;G~kb&M;7bwzw^KlgN>qjpWYSsVls%JNuDDe!3Qg}&sAwloBP&{JUVUQB1_l|u< zt_pmqi{anOu!rne)&fnBN!RKcdd2Onxq4LvSGzaIyHA`TUmy|FChC4l>G7z0ka4IP zl@OOV{XKB=VhkbIS7uj9@_Un`(qdI^nE8jDa>BO|d%&JWHV3=jM% zI5WfbDG-{!!brDv^~Yl)Q#H=k2kxjXRO=!l?_X(C<)@sATH zecv#bx0H-f;=xG(&(@OnqZyhtPL5&pIpc=Wi{ODB`q{{m=`TdEOYm)nVflJ`MmgR7 z{=S8kRV&ywwS4B#Q~uRxs$}_d@c`3FzUODbfzoxOivwQL)^be701%^0w@Ze^Z{EI$ zOQBOFZHKe;x)rrgAhgK8{ft&}cs{79BaYyY15`gB7lKZ2sF_ACHRzsi#LFwT$2< z50P>G_}E{6X}KUH;IH=+!7 z;=AYJ`T$ElxQ*EbOXYhLWYO^#Rv#IXcg^h!T#lcZt}id#6PPDZGpjpWZIhMFK{*P zE&bB@%-m5eQcRj}!3tR*9NvMZ{U9-GM(+KZ?9UtN4f;{~$;b_OzLKO=HJSXo?78+yURMDaPH?WMp2Pz|XMAdv8(5jopkU=I zj|tAE`Y4TeH$j!&Hz(xZoAqJ~P?$^{1?GHx7!MxRp$60ok|NuVAq_ z%?+hhF@G0Z`rp%&A@gBs@YIuEiSzQP_$pcA!q`G_!%FX)C8@#x>jj|MTCXmUx;s!z zx-FwO9^`}oQ^$2<4mJ2ol`g z2MHEra19!q;1DE1gWKR1T*KfJTt9-#PJVm#oc*KENY7Ngs#n$Z`qjO+v!K*0-ETn~ zZ>*kk=RP(dm`9sm^p<*Ewg%RqHTZ67Wuw!d>qfxwpD`&065Lt;{R9|;>O<)`fJnT- z&)q~riGg!R7jme^*=8Ac_ZjQ;7}6!gfS1!t3c32@cB!D98WkNi0i1H5qBCj7q>k#7 z`$(cDj|L}Sk?ypHWBOmgj^4)r5OCJM-ft&}Eiq`SX&V3Xod1Fde3{G&OEp`7D>66^!zqju zMwr1}-Bt)O3@)T}**l%+;WY?UO@@c{DnTvZ4i&~K+*BY@sz<89cD(n4KF(x z1Qkx+$542|Ed?EWp#`d}ST>hKa-z?V=22y7km(&M7$=aktxFL%hcl#%!I|1D1XSBV z0uhaC;ViPCIuGoxHarJ)-OIX(EQ{5n!|OS%0Nm_zuiRd^i+gELUwn4{{3&W^AY2c= z4ZcXsSYRCrDLlHlN&BPn4$YoTKJU}1NQ6`Ks<+Xory~iXX6lPGV;2R^yC3o>Lsc_H ztO;pE@cIY>%p%z}$lZHzoRUa7J<^^6Gz&G(I;2(v( zpb{&K8cU~ri(jIunb`$zdS+nO*61Pq`-T;sY^BuHZEX5BZ|*t6u3@y&czr*vJwi)=;~`L z87UG%plv+pqoZaZ71<++Owsc1O|O|VPJ_^Qw1GOmeI_Mtn7&8A5%y$vIoa8@!7o9o zUqy)@ZqA1OjOS?ABh4g$a!<0&T(0$W)9|Byq$?veZ+k9ndT>Rp;z(CdyGz%j=~J1D zKqjd<9Dd>{rauC_ma6ELQhwB_la1TC=mKB(;BPo~>|mVc<$0)zCU|qlONH`(HG&M* zi!qWqKZ&Zp)yKp-RF!34wDSdDgrd3JY?R?X{1vZL)%a3&cw_+&$BzWyt<}RvVKg6z#I=J76wi3p=@8=`vm04W;(J zV=?z}fsA!wIaMvBuww@$!e{XCWOAt6ILlGmq;pPZb2}vzDN1QQtno^BF+};NJ7|~uAAl%(xfx&Lbc5!24ER2@2Prxz#RS!=JRDOX0rO>2z|VHdq2<(Md;CqQiB5-#-VCw9jPN@*DXSL75^I!a3zE< zd+KaxOSIYR2JI=LPI}DLE9QGG#!&vHIxO!c!cXOnNa8kW{hRtqC$+_++6ww#){(^^ z*kBj^>i9;VcYuY@@7)s{@5Qb6Q=_+NZC)G6icxriLq%5h|`) zk57DiUp9JQz4|9u2Lhn*X~zEi9L@JC^W()#i&Y~h z*8RrQV)VCdq9(V1SG{L;e}XwfoyZ>r_CJ%^@)pk7e~?6#1N|sVjOjufwnT7tX68p+ zf64f%vwE|0RO{rn)AllQ1E4u6< z)#Me(0OtOWbrVImr_|7$_c z8@EA@nvPICQ$J)Y1zZhwu1qji!8$@vJ?$#CSbE`^`Py>5a!Tyj6lo?&vxsThQzaH_el!;#QJDX|k0mg3s z4w93ef0M8GED=>9=&vu=B8o3~Hfm?RxE^Tzk7VP06>2$IjF7VXM6fXmpntU*aTW36 z$8&PD<_&W}YF>?mEA+4<rlMm|M)4`;)N~o#dT18gK<_r#g$GzNPw3pxL&Y245t&*_`ld zQ0T~ONjB`eB}piDsVvV5RqjYM_`1@k+SJRZkyWjAV76$p05|BnW(aiLO#gR^} z_+f$%UfKz_;xVViZ84T%F!%q4VBmkp5DTo%{`dB6elRkhpVKM_7QKnp7r2QW zA~*YbaDs=Q)AnG?j%qW?hMP*d`LF%WY(p}4%n*J0N$IltHY-qaEzCrMGei-D7np#V zW^;g#pLF<0OdH(i@tat40QvZ+RAr1?rfpH1@Qu_reWeS&ACu6T;5w*Q=yze(tfQ)! zvC#l4;-}ijV9wgt=Hm&}*>iw+=eeaX8+h^|zd$l-s63_t4rKwKY-((jLQzwB2F1=g z!lZt*(u}$4f7SUaZH8o1_R$UL*d?K7Nn@%CnRm)BCA;)qpNE)YEMw|f|4UJCHebUhBv%Add*e$sr>t05^#xTO`{(Aq+-fFyfw zA34P=Hz%r}Ra7C*?>z=n_(lQR*Ti} z3*TJfQSfuj1C8lYF}VEAl?^hlTK*_iSn{KEC^r;Ba(A^6{FoGE^V^W%MA9CP%Sf+3 zhB~*K2>mUkE9|RF5p{0Fe|hu@g>aS+t!1U#{gGe{?%)In5f}%*u+E2!80YU^{T7?O z{MBK%lbC^GEo=Tu%uJNphXm}m zQLu>g-)nlUOEC%>u}GUd7Sr@$gw33wM>k*M8okz8Ma1irR%~;H{u^!Yx`dE>IKBaZ zYQ8cqM&qb}Vb{aQTw(psQ=iO=K5Knn4-zibviR{+FvqUeF}NUu6Kc+O@}uKD!w>nx zenn%yZ9@ihoD2p;ewk{S3}?)BHAqAW>H7Qb-hn|+-S#i82N_UQ;7g$J*mbCxEBPV_a{;B z^ZT|Vh^i198BWYuq>=*pr;w}gAfwTU zD%(*TIWc0dPqHe^wn$z(;ZdkQy=2LL0I=x$JrL){!Ur4?ZKsMDc+)8hf@}dsKTfmY z+GfNdxk@G)j2+a-{YvQcxTN&qYLFcx%6%&!mv&r-M?FRP9T!E3SWX=%&j|#~J6991 z$>Z;FNy?ud_k{u2*u+>OpeYk}&MaVz{3g};hFV$k z;RPP>SiwWIcU0?_J+t`F-3zkPCoc_>!GikhQer_u7@){P#mhC`ojgoUzvA2@pH^!D zbNg>M1aG}IHR^|E|L;o!S?(|joHn(^5l?RivQBrUd~5%Os$QTyvRshXF#rh%&wi=2sjUl|fR7TfvnMIwx!q$VU;*s-)cKyy2B zjb9x~Jf6&ca+Mw4aB2-z5T4C+bSUh8JnV1(-R&UN6Uh8rmz%s=1Fy#3Z2>`UMh3bz zypX}XP$bHpb~MD$xsVQZXYh4<6?HfC1a=u}G& zA_|EnUJFlMh!MWa9XuR#6sqPr88!3`jvuD&$LQEd3wYEXG$(S`?PjPI_wF@tVDg!m z1!=_Kn^m9L_r(f@O-&@Nt?(^04^K~bm`n3CT(v7#GLRxs;(EzI21nt!^OK{d@Jwu( zfT?N?D=OgSg>^oT?Ov`_!|cbo#T=WPoPrl^haK>$9!|qI#XJfASFQ?ACsE(au8e^J zxu}>J93)cJ0e^k};X~x+%jL$53eUi*&wAj8%n{V)j6reATiVg%%A1zCu$p*dept6$y2@$pDN{A`_NJK@`nR`Vs`wf6S-&`khm$-gZ>HQEmalrU-{>Qy; z^VQSp`qQe}YFmDP@T1*Iy9cK1r&;7_7*F?l;?&yfQ=<08tx{hnzJa9|yemFv1DokTD z{y47-M-u2;tXJw*O?`HKT!noC_sy!`@yYh>XkgO>S zY&{VWN%7E0L;B7Hll+S3q=vBI7|dM;@;P4WsqVa9R@!PdXmlio-=?*;p*OtVOHyUV ze29&r{x8iF4XKDT8BI(g_-iIxPe!b+Q0JR@zfo$l3%r7vimujd0%QA!0iwh`;N zlNBMv-XdqUEYF*|vN&52p_~^X^K<#=-~NWrz@bey4L*dcAFlj^I2^U6m%!Zz?}C0y zzTtYTx9{OHc%hAM54CSz>w^&%&4MM`+S~0`#eBTHYQ>5dx4O4@Bf;(%ZhX3a#0fqi<09AvF10xd*LEA!=`$nHoc;fI%@en_4~JysHmvEii*lz zuAI-CQ@Ox_{Ilu4+P6}C*LKtPQV<~|g!kz+=u42|ca1UL?>`)~QrT*hS_JdW|MWRU zE>GGj$yuE>6KTWZ@lTLbA6=qUmW|oH;fM|GEVPz+%n=~no*LEp?H}TpY;(tXf#|mf zr?Kj7!tZda4jxOtT>lA-y4Hvmj|Y>%-IXdY+rS6_(hT^_pU2HTbxFY|q`|%1s{s#R z9*S9R=sOr*azo$Tya)tcC*~X-M!UBB_4A&-=i$zL^5=KKDOk9;Y7Kg%_F3tn`E<u9)ITi@9YX>X80`t-~U!rS*4TirgpBr8+RZM@z!5sn@> zjh2q6`ta)EZjMOu7+=P&^#tG+l6n9G4G8~y4>z)P7?mha%HP9mQ}=Q0nHpGY*klek zrQ}&S5&dV-b)H%?C)^-ycO>5VE0{A(cPs$LLvvJP*8Ti_tzG!QHu1!&?$LbQ8)d|r zfM-j1;qPBdSoUAxS|8j;4m(K{}@Qu_6;mTp?Tn>b$GCIBedEEa~it#SPv; z+HvYwh?f#U@CsHh8vpf?7M|ACj-Zxrs9+QZiGqFK{u45}!cFEV7XLIq>ai}hAb_urnio~7PqkmhXseFLTZ4#pqFIMEaEF=I>1 zos9u(3sOpq&hK$I+NGYUuOm@F$andKTT*Zd0BeJ4*xw}q&_Ey7Cyc2zvo}^i@1~G^ z@soaM+h=NtO5#h#GUHv5`mtD6vjkv5a(rIDK(#-y2hIx zL=>^^A0IQt7MWiu^T019ER8iR-b*#C%Jit3ZWK9vwF4`EdwDQm|KZT{cs+bmTg2>U z75+T<=Ql2*@&GPi)Z{!O7UIOO$UK;&8gJp{Wj&+vhEu#${PVXj-|BV|Sqdphy!v|W zy6yvXV_FhTKZ=d9;Q~8dy(+Ul8`WYj*l%2EvKE4_GH&SZ$39ioOXcJQ*vdJ`^Zh_% z#mVRRs+~X1clP07K&iQ~kpAlWnjuTIsJWR`PEIa01zalIe6yJSm*<_G9ZSrBrG9r% z;3je0z~R~hY&O`0=dlRfka&W3Oy4tIOi_o7yW@sY|Me}`fOKGBAONu$OIPzJKKl=8 z!rLFa5cB-xv&QyNPIbTgwct;)XSRPw`R~JG{P)I~v&Lq=F}>RW1TMwuY4|{UcOA6i zb)zN-Yi9EinDWwTu!Y9UL4HS<=OGJYx^&N!7}WtMyn#>meN%&%yLCx&(HvS0JG+C$GFd?L>k>}u?Pm~EH*1D z$o90Kc7~Hh+Ev`D<)x@M!La@mb4p+*3JqRq!xnMqVb8yCiYDmRqRDPDua_6T|0U7# zcsX`)xPelVDkmJsz|&VkLV|hJpFX-y7va2eijh5+}7rM(|=wL^O zk+?mW^P%wDi<~0Mi}<%|jzazO`D!9-ZEYNLde@$>ZVyevqr~@158ntZ;cgP4%|ITz zIlns(Vvx;Do%Dr=SjliDPpVJX$86CrrCm0PEdTX!8F}p@={;uuxHUZtN^040107F< z-kJq$%UTO~+67k{OFNvIEW;+5o-1q#bI}T9k85dthsc-CNC~xxZ|2(fhyFCdTp7JV zQ!0UWXFR8Vd!heq9zd4<&!XFll{fX)&a$ndU&IHv?jstLFuySNn=rh{Tq|pC_K}f2 z9~d?cpPWCRD_;E7{Ovk2z<&TIEKV$r1k|*Z@vqa*M z+;0f;QCq$F9%s~l@@A|HS78}(6lKK>sG2Tn&#LMLhKDxmh2CWZ?b}FyXkqAcS~B}z z5X62dURXhu^^j05osgEC5SMC@+veUzt->a+BMU2OvHxsrXi71_n*1cRw%-yAnK8QW z%!&_);@zr(dCAqX;;Cz~whbgQmYv8%G}%p*e4j-z>Ju(QaRE*_4Fhx3Obc-*gG19i z9~tmnb%}T|^}Auw1bs;yW@r*WCzYn!HRk;5yvcGaH<6D34o*Xf1c61AldApL-0Jbg z?fUxCoQHu5wBfrg4~_*Cp%E&85v5;F!a) z$(|HlCo8S9O8sA10$vkX^Z|~^{G^Q?T-4__Rz zwvni)F43qi&c(6)-geO+Z}tmyzxBeb#iFZ#Xd#dyn#dyiC<3VOiQdL+BV`;HnYsn} ziRg|`a}Kr2zc;oV?!v=o;?+zhK%=4gX@FwrCtW}%G8pqszAI$UL2)Dc+XfIj(CuWs zm>aP4L(QnpVU~FaJ$IXE(d+g03fE~im-iX&j#Mz$dx)!{!Fa8Lc4ab|at{5PWru4P zQ>7hWwr&#Dq?TYR5qX#*8c26`V^%J_#AcrxACR8mJ3pXTp8u(chEI+-r%EuAJZn7% zmh(2^MIuD7ab|ULWSdvCj})xT`EdQ69$KAjQUJ@gecDwWsK=T&DGKT&(T>Vf9|_A+A_-LJ^6-0D@GuxFk}sy^5A8pUQ_o-p+AlK| zG&-03!Bs@v8_W{^MSHEk{-;s1+_`;B;T;RLr=)1e*4jE8AtJ3yaA7<#u38EtmB4gdBj1PFj=L;xSB~A$b8N^)M17lTuiPvDJ6Sp41K36Wi-It-E#E9BcPU-3q*xdUjeg zn}l>Qk`3M+Asu3rlGyF*(3Oo13B*{T*c~!&suD6Z(rM8YK|{a@E2gw7At>P2X!eYF z`WQd~x(wKoa3vkp(UPhxJJOs2kDQJlG1o!HoGRHbK>(d4tKZ8#QeCD(umQ1Lq5mUM z0ACL6d$Qzqh#UZu8Z><@GrrERoblev4$S#>c8l_Gre~HA+2@YNP42G_5|Ie~VzYH{ zw$jUA8JX}vTa}|`v{BldkMSwEj!}3$7jklZtXQyMfS2#!K{Eo5S}n2gYZ&9weDfxS zufSG&DsvD^0UZrB0T1n0__2rFB@$>JRqd-jM8`3l3Aw;d2W`7}0}X_R8us2ko_{p1 zWZ+CLHEPc*u2sX}0ENS;<;VAtabt8}>a~O4H5`6vun%z5-DQzuIC&d_?LG!xEB?Q> z04sqvtH-D9XSM>3Vi_keueD2JA#X5RZ+?+ItV|BvKGmd#3jB1LE$s5AIP75!@>ZEiQO)XT#+)jOt(%aYN#b(HiEs&W*$N!ANz+Ya3HkA_J*r4DZm1USLt zB*vCnNj#d!U8@ZD7t5Z2_DTMqmPSOxTl*c7Ypum&LcMyX!iU*A8aRoqJQcQ0~G{_KTTO{n2KwAZyV{FzSTb#j=U?1<6K~>sl zh{80CHB($xCv71~M^xzS2ElpxC%S}$#O(yMDtZo_7c#S3B++O*-)a;5Z-m_)u%S6= zMgv7s<7Wc#%dY2~)DMgPKL*}Iog+anG%>ZMrufgbsQHuoWXMzL3$@4qA{Z5*_Io)( zIIjWLfLa77D)2}G1vB+NS7NlE5Q@YdqyL`W95DY=JiPVqdy4=+e?yv$Jc zwcvk_-4+J~ie~%J2z{`b}+9 z{Lk*#ril>w0IGKccWYfHBy~m8sBN5R^DJTQ!bmdH?u*MLx&BXBt|D-nIS@byW!Cuv zj1u4e%(xSu{rkAP3?mW?RoXNE^$s0WN>!)>;LFSU%9Mo*#f93xMl~KElXEpFRPrXC7hH_!&4ila8N&@Llm%nQ&B+*eqomi9n`_U~XnZ>*Uk5KivIM}# zx5pDMc27BD&Q;e`jZGj(J3Le4`}TGQ>R7g|N^u(uGy%ih=+fAV!|x*%LPE%)nLrCR zp{(X0BgfM%tT`18AnW~wX_ihXr5VVisq`>YDPv2)D~KGK9U=3Iw~y`@|0^hlw&|t+ zDt6vN!MiQ_7~T-+7)oAf$jv={OD6zGjhonaP44q$XY1W1->RVdL8uQj=ED*%~u z)weBhH0K5#&WF1+gmtk?bp3-Dou9j51z|zPk?Y%8CT^q*kc#Db_U?n@?&tfXeX*4& zY(3{pfro)yzmRt?e4rK_;UMvvtDa{<-?Qu+t6M2w)^C31k23p@E!L&^tx)sgk>!hwiy>7^hH~4=yOJ>8uvvRBfvR1 zpj#YB7D|0odz+#NizY6r{%P*OdPs$>_HSREgiqq)gvL4}j{Q2Gp*C4Tcw5Lr?~@T} zz22u-GAQU^5R>#r+LAa=NOAB7#%E*tgt1f8D2*x!$NW2j3Aw04Kmsnx|VDYthfd33>3~fM1k@pXvl0VsP){%P zftGPT`o;=nryNq!p1KKS5pOZo*z1Se|7Nm?B%pt4Q^q&IDQ0vzpuSS&oJppRp1gl`Hd` z-4NFrok($Le%(wBjD@FhlYclMuq-`M9(XZl>UGWE#B9QJE|X0!A+ND2GIaY8GZ@ii zmeAxPit@ba9?QtHMdN6z zP-OWU$EhFz@v6B!bD>BnQdzSRnJ5I~F~eY_ITyU5@~H-N0E>#pu&laFS1oXse|()Z z5-@6>IB{u%uX0PRo-B2GRM|?8z*L(vIj?EX>O#yr<;nXc8Hy7%B;-I0bB(JtRFv!X=mO48{es*I64q>?ebfA$%SW~GQU^%X$S)( z0bW6}Plm-n=^#7w6WmoVG4e|>n*qtVz#|zjL-FW>FKG#RT{IC~}4wyNM z^l`t?;7cD^<84%(Qp}bib@OAw8N-@pY0J(?Dce1ZkeZ5CWe>Ta4_)z%WXp(M#C#u zI#jK$Yx~IIeX<-;9m6wo+FE5Y^6l2R6b2%iFyk_3 zD>L4l*{=fsB1!N74lY4At*p7FuFtwHvD=GSRGH`<@+UIQng>uN_DkFyE#XjLr}e|f zWVBE0KP<2>`w%lYw>ptpB0=4v@A`3pSo;BCn=8QWoW48Gbc(JOF_vWsdKZ@a1~0U^ z5TALoXOK{HnB0<_VlR@N?8Acc9}hbK%G1idMURnQ=#GIObBrN-9kqdtWR7s|OG(%6 zI~Hdwnl;iX@x4}$KigXx|E_@p#Q18P_95^{e$u3^o8lU%eRx?|Scn08`@`?GRE4B2 zDHV1U06=^2oLrA(C(S{hV7Vb(p5OLf`8Pocr8YMl6%o%FZL;Ya9j)&c`Gt@ZeVAD?}K}H6Ds4-UJF=2z3hRiYrPvXtD!Y9x_`;i)k=HPpXWKG24bB(QP}vBx znDdZ3!j}MTLmqRUSW%d@y`COvoIFFDZKcv`N^ztGb9u#qs+xewIQ}#iL=z)nyXQR) zQ9L*tjYyT@3pya(QxfA6UQ8vNfLcftX(O}fLFXTNkzRHbnT97*98{CkVM%?(l7DQO z$^g*_*n*SHpCWGl=91S=&d<(X@l$9HI@*{Tv^k+t`^0zhi-` zd4K>Wf|#FcpSC|fvFXb34^B<}d>!oa(HOn_%gi@1jkF~WlEFLHXOZv`0m5{BTLV!AY84PuKlCc_mZo2onyir15@h)su)(p~jF70<6wzsN=DZ>t`Sg z|5wxx%UEOy-SHPoyujAV9#Pc@ei=x$1DVF&PY`1snKFl+yt?CsQdiQ(B?=oo(#BiX z{?G5znp<3s3eMyE;flgD(Fq#qt+XE8VhQR zGN&lmi9oFR?OT2Fb>}r7-$R|Z!z83#kh8W?VPIrl8TftD% z!^*vx!#|m&r35+mScpqA&PN9S@q;n(l~+Q!!Rj0cI*KpIg4tFz?KaX8mras1w)lf1-7BdS2sX;#m+!7?(JeS2ZpAhhI=Agz@=5$iOjh*vMM0#zatP z0gZsHSVG!|zsO~@^pUz@{bBo5UyOwWCHbX2TEF*8XYlHVL-t~8-rJh

eR0579QX}Bu4 z{K1B%h&OjZ-t=TR;Z&0HfWYU_Nax%`#4Lpb4;L&>MNY!>LfmZsQ^B!0T(^LL_^ta*}xf|Sc~XFLXAQ$ zZbIInqN18`phDf)sEu}~f`s)k*iKME#T*9V{bXCP{nXGGgC1m_@mo4elL$FB}PLM{WIH8mjmJ8}YLHuyftrbv!$ ze7pv?TIbQ;Y3zwu1Ar5kmCXnlPb`lg{=AXB80OT4CGXn*WFUOt7qU6_L{e0G32*=z8MmAZB9QAB{(#E1m}t z2!haVEasD3>}OD2hzY9>eP@JdT_8g1_}GH;v+exoA5nN&HiIi}LpsjK_%ix%;r3@4 zh(J8Hn%y)dghc<-`rW9d{wSm^0zyTNYN%KSsj>tCcyp{tBDyt4VsQukZVj6m*q*lQ zJ(~eV?5LU*w^{s=du~5oLNn*1U#2&BzSgeySfH?b1BnvP8a4yn>|e}`%nSS7Se}7* zk2pBCPY!mZIys(nf1 z*`tO=2?gOvpKSTCgQ!Uav=spDSC6g%-qh-O;xYNIYmIbP&$up+=4;QBdG7u}s8JMb zM?e`tH}&=U?NL>&n;EX;TN;C;@b%!QW1(Z%Vpy6+;0k_sm>hPlAX28%BWBhK57C`? z&dcv(6ju^haLmR}qE%F9v$rEdp`XC}i-KmIR{a>mE zsNnJ9(Gp$nKMi}R6Q;X%)&4<4AYo41GGjdJE@ZPoR6zQeAPmo048J_h?x-^P3=__OyyBQA7B(pyzhu0mB?9wmiHj}ezCqmiFKE6+*z zHoH)NV&(a`gHf{g#piH5iw84fEFXfpVV#J!@eZlM>~q}b>WYLchF#D^1Ca;O0Oq3k zZ-(|M%Jb<0i=E(8DU<={v@A{C?mIGrl`XbCG%un(r1$f5u~&F=9*LV7jN|K}+WF&* zl%F!J2CEd_ez%kd;4r1v;jq9eyW%*BTk&^KVP$8lk>Wr*wd zwdN4M!cPBm`8^gTUER*cpx@lMrev;eJEF3LQmnoA3c_Dw?i19Cfv-yrurI`7=1uIdftgTvCIqe~Txr`*paQ z;@yfo91Iu+G12#KKkNQ}g#UajeN2hN8}DG7GnfCT_r|?anq|)UUH5mbPoJp2zYaz7 zvN+w2{BY=gs2~K{@(`cJURG9C%KJwN#^oKI6Z6eE?M5r=4-J_<+MUjos%GT)J*vKN zbpNs_v=l&0q{{nv8~Igq;0<~dJm54Q7wr@gIr6W7wC8$qt$|(WG4cWylOLXh!jVsf z5D5v1&3Uh^Y)?%RXQ<9MW_O3Iw0OU%Z=26NjAlFg+eYY6B>qnG((9rnxt(3L$&<7+ z^$ywFL1yhRa505CKPnh;bbiXvK)H&CxOvrX%A(%8PgK(8Bi2cQ@ZSOQEokkdP2UYI4{S zCo4NQDdGEz0rY#dR@cHZD>yRGz4#xibdpr=%JM_-khWv!Fz+8RIui*vtx9t5sdD4T zIQd)CKfm;U6+N<}5Ni{B>Oc(O{S!7TVKsp(woZ&F{Z)~PrFZ607X(%RYm=A2_FsGa z@JVbyA3n;tw&Rt|XtLYuo9n;i@~I=smJ(yrecZ9~WmaHf5p!9`Kl*VSMPUykQ$a4t z^Ksgu@hdouV!f3d+S=YOkTk}=`-2{}h+J0~iUIoO=H|wh-tCOEjE&3L0}pb{eMI0o zKnm7(>*_{7RLwIWC}l7n{g`?;Wn8aQHf9#McJ>Zbcb1Wn5!~|@qzqOD(QWuWosru# z6S?@l#P_V&w%7nB5c2wiF!R^2k<$?Fxk?7t<*_l_p4U-v>H(M!CXYJ(6=*Lyt`com z26O)GQIJllLdwRJzH?QVAjrU}Q!|PTdzX#-aJUh{ zTlMryJHU2FL95y0T0%~zoB>xlSB}=A2Kw=EkU92?ck_2?I1y?W7Mmep)3Kct#bS1L zq<(jAexdZaK05*uqbH#5#PE7N7X5$UepY>MS#x;9bpN}8oN>Sp#}9vs*evSN)QyMB zIr1GdI)K#sbgcP8Ft&8xkPt$7lc*}vSDb1{^yx`eaDKHYn zkd7qwysADQ*Ca=BaIdDZ7utz-T#MFO-jjtTjhmL9f1@jf);sNR4F$_5cV*-%;m+9w z%3z(pKAo3pmZ}VOdR~J-wOw~a>#YvtBLB8vf2`XVzl@Ok|F+y@M=Z;^G*xgFEqti86YThdu0x3~pqgC6&^~5l=uJ z7!D&bq)wTY>WFD&=lGSewq+EQV$%Ek9tYp1zAQ~<=fu|72lS<^Q}(9?9DC zeAViAZ`9NAcjTvkpzjk}(E|Deqp@q*ix_tSBPB>FO{*NvZdM)P@p6Pb(ewI19rT}F z>9gP;Or%RLE5Ix)ESNproz2_wm;$!<<7QUTNk&Q>ahvVytW?$0fbQu=*U37wUW*wQ zs=9#z^RjKrImqMWLG*dMo&5KmB#DR*jor&3PMt%Q*A60RrZ6{NibP#EZBfYB*H<`& zS#Q`qId|i~b`Q`OmzGRti{zc|SAw8NYNGDG2U>AZYPdp0ne8srx5)L5Z{1Hbvgx$s zlDvpVdgpfkHcwdIS|1$S_2t+R&?x|I^;*b)G@#PiVX@|+4Uf~V;?;pDGq1IPG2^s8 zf`c@{daRWdT4OPG-f53QpQ#PUL@Uzn;wf)xFF}SoEJph+N>cg)gPF8abUs zvBPBgt?aOlIHRCPfOn&m8H2>U8E~AtZikH~Zf|8@Q|zwT*|4m+GEfnEYPtk)klH{^ zKkw6U0Py4W;uKt1OP3pj?ULnNISrFyTgI72-5OP7?c%~*pNoGqr-RFjyAY64l2vHw z$%1h&4E7TP=cVgQQw6ckOiYe4FhHA1DM5K9C81_!0vhcLv6R_N3{#DY))lHH6jQRU z3mq)qS-x(g)z?*FImEVqn=FuOX5)NJXMs40D zoWazA37_>LW9%awNTG>*+VF+nE@1Dxd;?tWk$KA=7Y{TaxU<79@_jcczj+~kX8#D0 zQa!cA|M5t4ds5Irv0Q=Hkfwp@&X_`-X%y2DM7hXG+z|5H`EmteOgN|NE#6ybB|VY^ z>@u299?vO`KxoTjQ)~Uvz)p1cqd6O;Y_B+!ZPquu!EkNzuYD%vd4on2!!mu!DPG9TBG8 zFMNk)SJyZ91r+4s?BIxFX?i2csGnXf+04zqFrAFQ^kT3@`B=lCv0=$101Bpw%J;IK z9nu(FwzDWSsf)kZa`01Z&bY8VSNb<(5OmCW6B1w^STTlhqt^c#n&D%^)b5W_aa^Ux zOiHxuc)6%0zl{7aPIXsE*`R#?n#w@@f$nI=GN%A^@!~KQ)O7%m*OIfR($$HmctDRY*xM{zyZoDH)R>txE zB&?$RU|PR73}3fI5@5GIg!G`2Wj&a1ZWK$q*+a(5npKLyJ-6&Xr%Xs}DTd)#m)E}6 z*v35|QC5Nc#$Hy@Xs`Sokx2T5V1+6iA30tMg?>Ag;pBaEJcQ}gUBg7Rr@-&2;@a_9 zjr2;Mc56Tz@jHth3k?FZn!0>g z@zQ0rcKHZh6gbhgF*uk{h{u3R41S2KW!;gMP#$cGXVW;dwRqp0J`h`65hhYg{0sG# zz{oH_-Bbn^G#t%16s{SaSItaOIKuQ)eO8SD6I16M-kGaa-`U1rh>f^&1wLF5l^T2W z?|VJ@1qZ6C)m9f+LTOlle@fs!>fo1Y52H{L+S8PyH?paY*ChPPh5q@tZ#EyS&>qYA zhAyY4Ks9@)h-=M3hg#x(syO+Yx$M{c-c8z-U$FlC#1}&fbC)hRC{Y$IzpIfH{s1P6 zZz0SHD$%`;;9mI9)C8y6{8MGwx9prg*<0Pz(}iS3b7r*(6a}GB}=97mgd1CJOIG zU(+O)_-hPZWhf>RLAhUfo~03@jMPb$b_HE77JcMD{|fQ@R6}3og0feC(c;D_VpvdK z-i23}V|#WQNB$EyTbZt3f14g}yRpbuh=n4cPQn9b> zAn0G0(vV=Wop28u11XH}0gP4=a8b!#VIflT1m7RhkqYjUrF11IbDzH%(vyEmsi;Pz zH({!Kvkj4;QDB^87Q8+9gr1&sU^>ztqbK?tSLQon6|qtZqr0Z9u^Nw1?}-lZtg;s+=nj)1xP zT@>AsIPMXRNoSb6NGOgxfxJlbas8QAGF#B`NL&Mw47HJGDVO}cnu&>wT-orZ|1-6~ z?QbfN&K!}k(r!x{M^@{GvN7KMXkLhBBYti7yZE*-a$650%8ejDQL=>-?@oMl;&*xU zh^DVq_iCbqW)+AG-3qJxHb0(U?Y49TAtIr-#Y`F6J&sT@JmU1n?{^=6VN zJn}Db`<#UWJWePqC%e%6LYl`RcPB57G)8 zbH}msGhy#NzTrGI?}KHTA0uyPZCuQ@cn^83yXy@f+6@*wU{ZN5zcAcSVF?ewqBa#p zKAnh;^R9`kuyQnqmbGDFc@^;%x2EE0#|cyYII;2Z*+zhM*&Iy=FGQlr)PJXz$ zza2oA=Q1}7Ak4!DyuFI@%Zob*T4zdbB}*2JiqK9>Vsq{}b0iP+F5K^c!StRc3P9~e z!*D|Rew`vEnEHt^s{>0qx*K=3YXnM-`o?7B&;w`H7T8dF{<=h`yc9ByCg7@uYGb@ z%h0SvApN5gYL5kjDW2vh;SjRsTfUXVyFp>I5U;{dbo?g{&!qG)i9RbcO6gPCW!`42 zEa091q|i9+gRAY{GlHgLg!Ty-b6D))TIB~_PUXG77=}B-2qQPn#TouVjvrcz(m!Tk zm{uN@yBbm))+eI6^RN}v&aEQ5IS5flYUOnol@ZA4KO1!o5{QtWVw45cI%18^IPzKa z*w><9I6KTTqAboXO?DjlU8_9rLVW5Xj#S#?(C9g>MnMY7Je=9SrL}}~3H7`kmHg_V zPMeeu`i*pNS0z~~t?AE;v&u=y+YxVLJrDM+Q;k6C-BJLbrx)=$TP}AE!kS zn6UVL=A2K^TgX|$c=$N3ETZjQ_PysYjyhkA7jd@&zPX#J7!NR~KSqXeX!cm*q~1_v zs#EDo`Ic+{6;S1q7J>;u{TIxr^hzL}9i`F6A4i2Y?O`oQRk*K&V{InzE_Y|p99_KZ zsL#6M*sI3BG)w>bWx|R8bxuTdNKNVm=lxj8@07{5@9T!O8|6H>rP!RJ9&PPx`^;`h zzHl;t!&VcF%lTe3=LXW}{IFFNcF#4?ErnJEntiiRY71CND4%jyE!bWrkr7%QYBN|$Y2k@~}NqQk6b%j?V9_@lO-3YBb#rs3e)!|}B9)~Ci(&80H2 z34XFhbIs~M zdaJ1xpY!9&BcfE~j~1LY4{MwKC2Rm}>YDQeWgG1#^7#$nx;`KLx|9}o8D*)c zJcDbT=}o~&QZMo19`q<*lZ$&Itk{bT{SSm!zU85UuvmV_zNkuSX79Ew%AP$ zff-yqTw1n}mR&sNGD=D0=V6BQvOD;~*aj#61ohj|?0)vL{=?fdXzdfR+7hW%uk)O-zZ5dLW2%OUWu4UwL!H7ii zBA_3(k5O#W6FjFI9T0DGn~Cvpg>l0bBJ8-)Z}NoS#|L1T(7&sB>h#i@>!(qnnM)<0 zgRU&siAut(i;fG2jyVkmRl=awS1uE5FCANK=(DbJCBQ? znl+MMQc8h6`7*lA(^@~GyByII?b939C@%v6d7b2g?>#i9u7>Mjx#R4b5&ncEANIcwp2ob8oVo@3hm{71+`i$qCjV zbg_#qx~$Sog4=diOc{-yoW^5K@qUp!B?$sg)X<|FwyO+3T5?B=Vj9ElZQt2$<}lRj z+v!xtWi}&!nYdUJK%H1ZKM=D#aePb;BtTm|=-_+_(pi0oHj5wYIg+fII^pQBL_mw>KwzgS)lTFL#?u4_bXN%(^8| z4gy55nG`&yv6JLc$adW)}<%k*1!GM$oDRt>3?QJ>Y1*x zg8!QdCZoN>eg$h3f_UEchUvA@#AsFZfb>@X7*6)`=Dy$6H190p{&zi=p4^+^Y*+Sw zucq{GYB>9S>AWhRCJ!@g9w?(I45)FxSjNAUCX@;wJ90&)tlTn&C5ql$H#tw_`E!;k z-+QmEmaH$=lJ7%r-+=wBdo>=+loe0z3Fu)#fQ{Ya z9+@4w&?u45?;-OI8m={5AJJ?dcl<3h-=*)}zA>4q5$3ypPG@M&ilzVComYZ&LyABD{l7N?Vv$4GLF=~#TxsGw@?9-Pe~NI2W_S6k zjGY~`Nv)2Z$oeDeJ}$Cgsme#g@&a>3kWY@Q;N;8p8@g@o6t&gLtjkfC*<~isllQq^ zStx6*$j`6qbxQFmCios?*dH3z1V#c8Q$Azt$^9@_p#AsK!EU?BQsr~(bd|{!j}fP& zcyRA__RnRRzlF73WuR?$-xU=Rq_f+QxIGuiDf8P9Sk|u{I)n$Eig>84#7JG2o);bk z@v3^?xRVw&NgLg{ygvXxk z_BtzJ7+2*CdN^b&otb`}?xfsdFzu^Zf2>^s zH+zU5yt`p~{9BzTD3JbnoC!SsW<4)?aIqLjr2-QMYYO(yqQv{z7#2h6){P7Unds@sa&EtbNX&*T%Oz z&jP4Tj(5uQ?vUuLOb{kfDY|sPP5J;V*hb%7gL_@U{|CKqbDk5X3v_&Opg-6 z0J)@|$3LT{A3ykR1%HZ*_bDm=KG>^1|BwgIId|Rhc)g&SVEJi7vebwT!ELR(w+H;= zpv@AIB7Sn(xkyO%rwLo=$GOY32kY8ZdA9N5COts{P? zmWfFoDaEV@L*SW?1{}g}9%esBl+exO{?(Uv@b+?xea?}`a9o?KL=?=O)M(O?UYsOl zr3ON`<6n=q?^CHqpD}^_VxjllDJ_&s`0Kx$FTKh7M)J(#W_!hNjw!BFMQvjk=xqd; zoPL~$Tfz^?Di>D{REq7_pEJUof!B`xhm;3nhry(luN!u%vh<4WeG`hnb4_i8tRZi$Bghx*%zwtr$fe{fE@WE+4G;{BXS8&-xN$g{AIJSA#-#Wz_{n67GnmyW@@+ z!ov(64O& zW#0JMqIogoWwdMz-;;%G59^W;3gkDFjQWY6u4QJUl2wIO4BuA#r%j*yofgkv<%<=Ajzd@Qu-SR=x)@(4K7{aLW z`WY9C-T#K3o))*3Yh08#e@?ppM4z7JW@Xa>o-0)}{~rDr;Pl?O^ZJQH7*-}x3ieSS3jPqC-Cs+&%X#!ZBmGi-LNT%g{KuD62pad;TXT-*3C9%>OHolL#W z&th|=ly()d28au5ZLSonQ66Gc3n~jDHk>vi3K0x#y=M6H9}zyE-XYO0e*p$B!zD8- zh0A7--P9W1f{KdDKD8f5cEdB%B0O*saA%=m@$>hoU4b1yX3VZwd*z0ng_?Wt%VWZ^ z*zSFLNgQ|mU-OLu=^b8=>GwKBpZ$8?*}qXVp@2iZw?CnN{{OWAkicF!g2*Tw zCj-#;Wa(kX(vEJEZv7FX4jP{TBdTrnwuy( zbV*LoD_X{Mk57da5=aJOji4${dIWiZ{y`|k70N+kG{KNKh%DpBA9OGf7|7q%=J#pT zCf0Q=)kSv&Hw%UWEw)m9H(v=Jq7@%)-^*0Rj7Wb_ULn?fy=5fPy-6u~$HPFxsj7%a%Qmwlv8M52qz+$o!w8uecU=XnkY$I3M89~rM^xyo zsot+MC{Back7Xfab4RWgjVtI&RfkqEu&3j7L;9yzSjC$|L* z@&#fLOphG&OQCbRVAt@1KS&`G8)6g~C?OI=pD|xL+|Q6u-EsAAcZ?V*;b>GJ2?8T` z`UGo!Jy&emX{dZyXVT#-Zus6_Oq``1xu{xMm_sZ#IEpuqH0ne$a^fn`rCrI4AziPA z0hh=S;f4D^07e9izU z8d2~WM_L$!TC9aECJ6yk(nvut8H-6qoOIjBB_LoZM)CYlCz2WKX5DNR%LBd|vC_Hh zf+QZnY?WFHi1nu9G!(Pec%A_RV>Gvw7-w{fwXcxXHZNU6qQooy{OFHhDRkxgJCMa> zmi)K#4Q80UK#GfewNW_;Bb@OLp9(dnq9kMnQ-Vuq3V|=?S{!MZ0FXNj1Tk<6AC6Lr zkrvbxjKNIvg~(MP+n6I=KR_(+*Wq>eTnVj+XT9j)nBUgb$?aE(3S%9a|luN_$5koPNI0mFxD-KtwC@F$uBm_bU zj@f+%dW8>xlgc@CBZ?lTFj7UN3=zm?*)-!&=T{K0y7*_~U;J=@%6E>n?G(>{Zjsa&DXoz&`S|DZ@7|b;&%$7G4P8@<1h*6XzF-d$3q106Q4y!EzS#wF}n#(K4_=FZj2xaNq)vLnO3R7CCcOYGzvm%E{8N+S4)8sLp_;b3ACY##e(nk2(w`Y!(9%*!;$NqP^ zeC=>lh<%4=!ThxZl;V$p)_e>2@^q$Z7Xx#%$;on7bU^N}yS}C<(>5v?=$};sjTXd; zC-W->1_niXay}ps9BK6}z8Gf`gq*|t87q&RL+KTgU#UTix}!aBBqQny<-OOPqt~5i zP!$|$rU)udb!mw(Fcb|+j!58=*v(I@+m&>Y?R|2FvGD@V0$DIWW4a0;eUhba@?xg* zb#d2amJ+`iIR7TUhYfr2AZWtlUJHYd1v~91pr){W{cZbot(Gup57rqejUf%@Q5E!4 ziKl|jvQrJh=YFbFQ(39Fd;I_@s=y7<7!NNP9g}xesc}UVk4QWA$e~Zt5T=w;h2)cr zgcmTh-+FZciX{N_t&U?5H5$ics$&>GXT|086zDq7ZMKMSG`-F#34!xenva$7t|TVw zA`|hE%0cRCTzXUi~C@QgxK1*t-M#Ujt44~Hn5|5cHq1rj?W|FCEp%Evl;(2nU z5UDn-P~duMVK#6oP`eU0T85e)5}2bgjvTm!DUIp;ss<}2%eW9xAjg$4NVn&Y^$)Zg zx{g%S8>kuihA1+RqM{jC(UG!y|K>?7g4l_|%p&)f6%*Fkjnqfv6YoPAxuFC7U%lt^ zy>Q;Q#cYlIqw5!)24U^E;@T}mqa|5M6|nb+Dl89?b?U@)?mHTzX@3c-yf?UuIj8W< z0wNG#0ai3m8Y505`<{ltABkh7OV-W7ho1dTSr9Q*M%3^EIlh>=@p8l*jDq|mteits zATBY|(MM3RCaR7&WvR1#og4Qv>EH5X_UrWF2P!NoN%T70nZuqc&Du|}(SN_%H5?E1 zXefix<0CLIf|r=%(Lup1;CVU9Fh4cbU6sir7vY?*2mMWZaBpD&( zk1;K_L&R3|9+TWNYi`<*JL2^RF~(I$?ur^|Ptw2Ahml8x#mjw~PyEBdqD zJsOHkq``Li+=v4;*;AAUzxavXKKMwm5untQ;3<-0NHDMlu+6FGwBY0=Jrc^7>!%K1 z3(*EU24lo%jN3HpVozP`@@G`g2NyjrYpwGinjX~}YTc2)t-wQkvI()d*5rm=qe8RL z35d%vvWrR5$VX$4|Jpm#!Gu5!Yd=BW1qCuhPz0;V3>vfjv7Aews4kF!P`-=VZE6@h zLI8;9s?`vd6pFO5@dhdYr@Kc62w zPU}Y+uwGkNZA}hxx&$6*Y#qYrM`!gi&B8K)A}5{255nke>0CSsFkjfF@GWk6rJLmL=}; zNiZ&qsZRn7DvJUF!k`EaHrtGgNcg4Dq+6XgBZtjk26J`wEEZN!h6@(}7#t-Aq$sI* z67L8kzfYB{iu(PxMO~$_3K(0GY0y*+0??=*#%yJX04Wm3;7ZZ}p6HP&!~~}yQhr#b z0)iqE9E_pDSGCH8E9Y@>n&5w)Ip|yb z1!B(g;2<6Gmm>+5Em(KG3lg8V z;sSh}I~ao;l*$lcukiKz(uwf)rH4LQ?00~giQCnnN&_l>ng)TUuHzzW5T~xiqh;o3 zA%TEFxdQPSodO5top5lR8U@D1IKL!wk+HH`OximsbrcUFGV-bG0_{pSXN|IX`iL)< zKoQZ8^}m|-t!v~c@7l~1XC7hij;&`i$#tSmrNtM<9$i_-lK_cH!h&?a8pw_ZG6H-C zT@0WAJ`@irmD53~N(&`6eruPEY}sl%&x^0XYN6 z*3g;zx3S2F1~*9r+=!XEdtxR`H8BmbyuCmy+FQ9)D7P z4+!)#62JTJMl{lzAtX9ZK{ z$~8+vv!8Y%{mow`_u?cI{(OTs^Y|0+@h1KwI)lQHT=Q#4v)RmTWcl2}%5H{_%QyCK zdk;P?lo2v=b15THWXNutGZ*vzB8oOP_XA?NXFlWR5`3OB)@lrMblv zX!U5mTyy(U;eGBtVe~jRSuMV&47E}T3<#<#3^v~E_EiuS<1Gh=FI!(XRWAfQ`-D#y z7w>N0Kj2U$r^LwW*?7FG1I`y&8` zX>=7Vww+WCEtLL>E>~n8oD#-o*e0X=n`F|qXb-l=D=&iOY3BaVEV&==k=!D6B^gCF zDXGrgE_s}$GQGM;n}+&_5-X)`A|kZH@*|oE*%afcjoRrI#^!6UPAey__iNTl*#fu| ztY$zGFfn>WO|$Xi*HT`BEqB4>`_uMUo{=CIEYF16M;K$ zi(x_b&wPAz$>O4IB!0Q*_N|&n@4u5kX>c$04|5BiY);gLsL1!t3&9{oJl28(A=t&Y zOIE}uo(UBFw|?DzoVfVmnc1ku$qbNb4yJj-W81pH#ZyPPwon626*EYhae;w|o#Z6v zjq~jEPj7iiq7LvBP{PYdHO5}?LyD4Eu#|_}7g*SgInvxl|A$#sD~p*ImqhH3r|Zy( zX1d{n|4^C{Z=?pLmPMm~JRCEDP@BN~Co_R;dz{$Ok^E4){eKUd^gETlvUO&cRFB6e z4)mnPR#towZTN<8d6`m53zGsiFr8$qB+S1Ec&Z?#GO;0JTAo-jl^qtLvQ09SO)V4n$>aR7&F(-2Fv_=@VHH7 z`n|W|3GR1t*(!d(8RnwXX^O1slCq^=0lEfRe!|z)C1ps`M766%i%Nq1pu|o1U~O~H zB%UtiBUWPkk+)lnm8o^mO?e@jb=sEEh-S=-dM_PR*%q^?V+{haJSB*{p##~e)7t8h zsB(>($oT3`TOcv^{>#|twxcH&I+sVMA#HA|<*BwXp0P>A(zGK%cJ|qYS0~$nTPfu{ zP^Ly8>{a!pfy2$##%|<4&@BG;p|lE#9!nYlJ^xAzIclvNYFLUK>64-K;MkE3frAlF znlKxeRjh>d3Ia=0M+^kABqz@mbz^ghBVdOLJWMQpD)bYnY>v>acAK}ZF08BiUOWtg zZ2hI>6EzFG&%;A01L(9e5e8zHT~mg%ym$lyB0$k-y{y<8H`-9Vj*_7@&wCdc+`ewW zmW~u>$@OKqjtU1)LH-VqdqHO>4Ad;9g)d{<{hP^S)Ahik`qTGgE2|G_cc;>{CHdD< z)LD}%AyA-e%+)buZ9*2^F-gf-3P7>CD>r z1Iw{`dI&PHOcHwEk);PFtWF#mY2ey{MSX|K$hS{^w+=ZJr5P#(R)ZI8cV+He^%@8)e>2yC zxYtK2s(`+8b6kSvLGIr0CU(qoC^ z7axY;ZaumnaL-8PLKQf0prIx+D{BEn5R!?oAuAAjKPqW8!^m8NZ=J)_1$GeSq`s6x zO7vxd#3>DD!oCbmX*%RGy_a@e^i<$R8Tcy8sWY~w)~iOJyBgKB_P$7dg`=-~P-C7= zLboWjC`>H+*?|WmIoLfFC+{c`I`Qn<)cl>)l18hilD$AKf{res$kM_jXQu2Q6bL*( zsbWE6BPlBCcXj{0IxNDESeJ&Z&wi}G|FSgkwZ8~klvFIlJwW44Ii1UsqaR$Nw3wN0 z6Cd3Q+H8eZuWmp^OaxthAP03!-BMM_!-qp466lv>yLDs(`AjRAKY}xlA0I(tfo*?Yb2B7b(kh_##3c zWoMVRJRLTJ&^@e^-{CK#@3wtsEN!%RqAIa^Of$>AvoL?-Cas!wLW4bH>zl0m%PonK zxXw_!!z}?u*Ba-F(IJJX_lvv(j#hLF+NX(61;$cp2-0c(hgM&Q^CjH?b%#$*y6+K2 zGmx!hYXkW3VX`_a^lB_QP2}a7%y+6qAI>z#%&S6qx&U(^dB?#$iqTlHlDewyQOtxa zuyOL_^|V4OXe=qVGRKz;{>jFKV}DocGvl41F!?X@U3J$BjNx4CPyiw03%@5lZ;kSu zh*jGA1jxM8NSht%Ag|2TibvgVT==N2XRtFq1XSvSQ~Cpo>w2+_?&wfzBCNkOYpWxF zsOw;YP{8Zbgy_X)-2O~z!AmwBrONA_y#Xc&fGgh0^~(OGF9?gR@`Bb~)d9W#S7w6U zN#>BY^5XCxk=Y3yjRRP4f$r2dtK;yqM=h)2la6wW1mpS+$5 zy6z;jnzd5fa+N%EHn~aDkba1A&OR~*vXCQG#4YVix(1kQ!6%-iy)_2funY}a@&9~* zRRt;yRO8P`$ku9&-S1M0Pt<~lLTpor0H`4RpxB{2>w#0J3d56auSu0B=FuJ?-bJ3C z6UIyKv9vBquXwJb@^hSyq1e1CtE$SD17J|SXMd3@BTOtTKdO!}2)Nh3sC$^!lC=?_XuM~`yErdJ7VtV9M#yO7hJAZb=TCjdB9@-lpq!{FdA$j`)}hv-zVvtSXv4KQyUTi z{Ydu0k$wQj0rLbgEWxb*)D!~dak?UH9H)l!gQj$*=KEa0C%CR!S<%>*s{MnZGI0e_1b3d*gF!esKS4j3_123=`48EmW)A$ciL&y)uKJKJ=%lmHOF8A5xdEDE7^W}3h z8yiSn8w9sP)7dzd$SlpxE$`uuPi`-YSB z;2UoHLAy39dJ35ov2rzL*&l;`3o@DWy~v zvpupQT?38o=37o}`xn%3;5(|l6MJIRl;A-AdgE^&F_w!5+s%&z@+=6AKO=PbBG$81 z7r1x0ieTPQP-u3!Ffn1E1aCPDusiLT?d|V_L3h%WyTAyN&x{$M#uRWpn~`&+#l}hw za9V{{gN^{-2WA4$`eMs`fN}^3tWM9&gs!AS_8+`71Ax~CjsNqjgDfX_2$tBsA#O$QWiEXXmuYeFiJdKPboVV zyLlWxj^G}=v&Q|HS$hk$X`QLL`%vAZ4L82XB*P*@tjl2S+uk^)4_ z8ZW)da%dIrr#>8v-|5PAazT#RKmES{81H`K`vD&@9s@aHa|Y2KHO9faN1rr?%s3R; zK#{%V+-R6mJ$?&Y8_E50Jdy`z1OO1lYVz2}i>+-{wu~jrOxzX`%jfm(lq-&D(`hN+ z%fc2hrCzuQ({V^WPvbH z-sLY?0}E<-3)Ut)_!O+H=(5B`ThZEul3$O2I+#|38M!h;wQsMupJoU-+m*+K3NiIs zeaY~oOa?j9k#KV3l(&Rkf3(PQEgT&W{zhg+qRwuoyEznnqJ{6r-v6);qL|p!q+43; z4&P1u$@bX0#PF4`M*r~sfg*+!`Mj^4?C0kn>$8LF9e&ArANVlkiW0O~{@y|R`?P*d zS(PJJ=kSE={2z#5DD`Ktn@{$56|UG>@cEUuByCF{Pf zd-U7_p?h`4#OdSckz72cT5~vJaBb3X30@nxDVGdAi@oOjtok;lEkXn*^d8C=y0a zLbXc+Ms~&7aDdu`oLTqXr}Owkdetw-^G6E)fR2Wj>rD$cAex1Mkbwy&s-Elpz%TMR zbY6ckP`FxDoYF5Mq8Na{l{rW2G6Es@2GT2)iCmU*SQ?|TxcC=UuP$6!c9t0B&+i`m zft>$S#C6BR@qO*pqJEmq8DRkM00i3riV=p{;6qDOCG$s$Uy zTCglu`Ayz`-oNH^@66mY^PF?;J$L3gC+tDY2je!RPIHSC&47c151InlboZ4BwKV_v zNieP$rPLEmT3ZNgZw7qf;bxaUD=jB-OTb(NJaY3xW*IQKl#~>wlAGU;)(+<7m9Jjp zhh{-6j#zHpJfqarVxB9bSq+&+6pn$YUEpFy6zFv?!K&V{sj;}CkjUwHbi#>Hg~`CZ z63JKvbfvmmZh+{J5s&y5QQFM?XiJ^kZ8}>QkAJ}$`+Q@xmE`AMr|(TYFI;yoU?&lh zXjsRh)I8$;O|l?0I+`w@9P1-&C_3Z3x3i;{f&?D(QGFvYlcHoA7+ ztJQ8w5i@>gkA0F*X+rRFPOqHabSfsYhPUr$CPUQ=0|z{mojb_Xxxps%6ZHWCrqp9P zJuW%`g{tnx?;qL;#B6v6{dw> z>pmW}CX8BvJ$*Vd4(CM=laB=T)DgL}2EK`vzwgJO&fT{Q>dp3uu4;hV4SpLF0d$Mh zv+f)VfGJq4K?2EV}{E)8&0NrxdN%SMgR{Y(l(1QtY+@T z)qC&(2f*bDHTINB-Wgv=qyxU?XN+a>)2=%h;DlgLnohHgz8ekRjsN`*R+;}PjaQcK z*{mR4ipo~5BuTb-=--EzJEijpC>&(BHRt8y?J?q0N zEthM|-8BoSz-NLf_o9Qcb|Ukw&P@@ov6NOh1PjalZX>G5P()x$W!#g_vFB}!D777$ zc-%?Dp>_^!)UR4Z_qzAb2T^f%d*Z)%2Gw7Nkv>+#1PU44cRCqRYBhogvi{7HF<1QP zmzw|j(U?HB@$9P)GvJ{&bE50d+>MAlN~nzI&wJ z5&(#sNs^OU!RzWopNHc+7X5DD&NB?7F#ckiem3{~30f6qBp+V2QJxz^|3UsC^8{D! z%K+ZI@Y`4toUrQ+`Sqzt%Ia zf6u0oBsi1Vf_Pb$m!$g_`~FExCJE08-RgheEW}HB=Re`)sJ1knQqdMx)%j3BIQ15W zXGytn_oN8MmC_CR7b1C}igGg_xAO2vsq z%!-8>kW}dOqyob;=Xa%?_&$|z{!Uh^ecApX8VboLfXv;1MyMMCumO*%g4Xv2!7l~( zk@G)K=H)!ya$YEXPAnoD_KRz$5KoN$gFsCT(x*rt;e_4q>OL{H)u!&wvcw#&QO90|1u!B~q*1>Nek<*b*XHX*L zZH~c~X{-7$(O-l>SY)C=JN^ZFHi??m2hwul=)PZ=WK~??HcJhX$~7qQ{yC6H+rlVW zOVh+}#T3xJrOfeTvSn>5^5IsCf}PkgN*_NIrwq! zkFwNUcmi%vK{9NVQOY+%?E8DMeo3e0s&X~*5S8t>@%ulP4C~NubfwSks2oSZDkEZ} z`bU`c$Z%%m(YB^X)_<2S(J4ny3*JDz6pe}he!n^k+&lOxq7*^PXPlORaudGS!wXss z#vTYZ`2q&K_$^4pDrk%L+sO|;`ey<7!i5p>B{(y(0bE0=d_*F9>jSn8V|0x-L@gvo9XY%3roiPZ-=p&`S>dtUY44cuI$N;7pcMWzwqZ5#F5S z+ub5BJBxdU_njYJajv~`%vv|?A@s#&xUwKMnJ z8*34|b+(Am0g`X?FIX!7fbP0oCfo9VswwKgzy-A|`OS z{f0Z9iZ|Q+%KaM1)X>sAubsfd1+oTd2^=}Kj0v(eMpm*Je$3oYf;P-hB*AtKdIzE4 zA@V~7>C(WxnRe4Qw8lFrg4dm23->n2kFQ4E|MF#$dRa=*)%cQn2ir40JiRoE(IRO_ zP`@Sg=3`~8%ne>)k^Txy3lhNctwK;|S^u%>#U#CcX5`YFY9J(m{E?Q%Ouwksm+C94 zSo^m&&X+pxKAzzBCz%R;O);1lQ|Rx>_77Ju9Azidq!8?35l#+W4_yrH%mQ*lT%IS#o%1$vTZ$cTE;j}xwCoC3buKM z-O5ks-H((kk_>clB1FY`?-<9)9d22q^C2^`59XTIi!UQlLE*bv9puXOTO`F@SJ)Nd zXyrXV`<6-3%M4eRH#DNc$9X%KMwHosx@hQqNiyZlJo3fKdIaPqlvEPUaS#1tQF2po zE}g^XKG5{RA7hIRQ%;G{Slc{K1~>(;f)iH(Yis`mwA zmVeGxQ&G_a{B-}@PpwN9FTl;Qx+2@c19ptsXl9%(Grxj&1S+z*;Ha?2Mg?bvM-aHVSwoW0aPc9ivvV?A2Gvp0f1pfe| zSvIjG=XYx})7(FeW$gYzAh@d|lqu6Yie288Z6Y~xivf|sG7ksN+mug_=Lwd@{ zQ=~g>@(_jx9ff)g%2DM{CcBH8*zT)d=gZ%)EuV6~wxmOPnk36ZFDMi#3cBT^v%+r- zu5wP{UnDEH-AoPnb}6 zGm*Mo=gU{(I+@Ht)r}C40wC_Qjvc5{!8OcjLX`&5o>yl$HBzRBKUS8**?o0O^JEkk z^r(A*RJS4c+Gb}jVGY}@xbAC7mzbD{0wDX~_?}-b!G*J<^ooQB7gvum%}uF8S4~M9 z?6VN@YW6KV5_OCRZR;ZZ>FhFoPMPQ@mtx=SgzZ^T13F_{CP`f;^T}$EwIufYe0_w& zbGeL+_BXeR_lgmoAy_R3aR?)TBu4^OLIfy+r!hHOQXNS*m$YtxlOil@CIM^ODOlK3 zBr2Ry(j?S?!pH9-Fr7>+3kH-ac=NfKLnL0>&s;w_Q{R+P*G7(F7sPPcC+BpXvMxA< zh;OTw$-tL_Sq+e$n_K=16R}UW79XRm=FtFXN<{tMU;2mX`(l?v0^y^_8yUq{(JIF> zy2693Fon7WyltkNFqT~3Gu;TGdy}V{8h1{A_Ui@#BiZqz+L+ak6O`#GFQx#HC3Ej5 z59w0P+)~2n4#oZiG#E%N-XqDf&L?-R!+9-pmCwEL_))AJ~ zFF93)^m!`+6uEQK`FD6`-wMTa)`5Q`SJaxFX32X!0Lwb|@3a&g)Wp8ku$V*HB=t6a znHf-944}0Qj4-QVk2JFIyWK?ilBJ|y+^hUw!V*iZw zm0<}4y829JG7~^ZXd-BLS|9<^C;Ge{xZ!7T2CMYvenIVF9d04F7F0Ho$FDl^q?&R6 zb|dQ%WYU5be>=Mkc=kC_AwFQVY%qYB=OS9`{JG=^dvBY`@7+_mbx6&2yPVDAj)Ww3 z^A?^)ewPkoqeNYiPZnHcuiJNV$m+UyqE<>i9|OKg)`zDD6B*Y#Z2fAxJf9vjD%eac zg4i@8)nsRaG~;dMfLEpI#1<08ku4zZoL}yW@aWy+@HKXO6-NF~?3+|Jt!2d==BKWr zpX@eMB36H(FIE~3P$tsMfGbvF!#=TFxT;;3Bq*AK1LEDYwE_@+Lf3qit7* zn8APxMoo-XMO`q`Y2xd%;I*tvz~z#wv3?v@nX%zGA~@6Z;v_YkKwVUjhKi1It&&aD F{{T#Xq~HJm diff --git a/icons/mob/screen_alert.dmi b/icons/mob/screen_alert.dmi index 0ed43592376e0513ba34baa4f0ed1e94e3c0c729..1b707cda5e039736087908846454c09f7712ec5f 100644 GIT binary patch delta 3317 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 Ef|Zfr_y7O^ diff --git a/tgstation.dme b/tgstation.dme index f1be4b290bc..cabc33c6f09 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -215,7 +215,6 @@ #include "code\datums\helper_datums\construction_datum.dm" #include "code\datums\helper_datums\events.dm" #include "code\datums\helper_datums\getrev.dm" -#include "code\datums\helper_datums\global_iterator.dm" #include "code\datums\helper_datums\icon_snapshot.dm" #include "code\datums\helper_datums\teleport.dm" #include "code\datums\helper_datums\topic_input.dm" @@ -501,7 +500,9 @@ #include "code\game\mecha\mecha.dm" #include "code\game\mecha\mecha_construction_paths.dm" #include "code\game\mecha\mecha_control_console.dm" +#include "code\game\mecha\mecha_defense.dm" #include "code\game\mecha\mecha_parts.dm" +#include "code\game\mecha\mecha_topic.dm" #include "code\game\mecha\mecha_wreckage.dm" #include "code\game\mecha\combat\combat.dm" #include "code\game\mecha\combat\durand.dm" @@ -512,7 +513,9 @@ #include "code\game\mecha\combat\reticence.dm" #include "code\game\mecha\equipment\mecha_equipment.dm" #include "code\game\mecha\equipment\tools\medical_tools.dm" -#include "code\game\mecha\equipment\tools\tools.dm" +#include "code\game\mecha\equipment\tools\mining_tools.dm" +#include "code\game\mecha\equipment\tools\other_tools.dm" +#include "code\game\mecha\equipment\tools\work_tools.dm" #include "code\game\mecha\equipment\weapons\weapons.dm" #include "code\game\mecha\medical\medical.dm" #include "code\game\mecha\medical\odysseus.dm"