From 6d248c903980a2e911bc185c311edeee8a904af1 Mon Sep 17 00:00:00 2001
From: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
Date: Tue, 14 Jul 2026 04:53:23 +0200
Subject: [PATCH] Cleans up mech code, refactors Durand (#96942)
## About The Pull Request
Mechs store almost all of their behaviors on base /mecha type, such as
Maradeur's smoke charges and zoom or Phazon's phasing. I've moved all
mech type specific variables and interactions onto their own types,
cleaned up general code and refactored how Durand's shield is handled
(it is now displayed in vis_contents and no longer an object present on
the turf)
## Changelog
:cl:
code: Cleaned up generic mech code and moved subtype specific behaviors
to said subtypes
refactor: Refactored how Durand's shield works
/:cl:
---
code/modules/vehicles/mecha/_mecha.dm | 267 ++++++++---------
code/modules/vehicles/mecha/combat/durand.dm | 269 +++++++-----------
.../modules/vehicles/mecha/combat/marauder.dm | 67 ++++-
code/modules/vehicles/mecha/combat/phazon.dm | 53 +++-
.../vehicles/mecha/combat/savannah_ivanov.dm | 35 ++-
.../mecha/equipment/mecha_equipment.dm | 4 +-
code/modules/vehicles/mecha/mecha_actions.dm | 3 +-
code/modules/vehicles/mecha/mecha_defense.dm | 111 ++++----
.../vehicles/mecha/mecha_mob_interaction.dm | 1 -
code/modules/vehicles/mecha/mecha_movement.dm | 80 +++---
code/modules/vehicles/mecha/working/clarke.dm | 10 +-
11 files changed, 457 insertions(+), 443 deletions(-)
diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm
index 2d046b2596a..2e98a96fe66 100644
--- a/code/modules/vehicles/mecha/_mecha.dm
+++ b/code/modules/vehicles/mecha/_mecha.dm
@@ -37,15 +37,15 @@
mouse_pointer = 'icons/effects/mouse_pointers/mecha_mouse.dmi'
/// Significantly heavier than humans
inertia_force_weight = 5
- ///How much energy the mech will consume each time it moves. this is the current active energy consumed
+ /// How much energy the mech will consume each time it moves. this is the current active energy consumed
var/step_energy_drain = 0.008 * STANDARD_CELL_CHARGE
- ///How much energy we drain each time we mechpunch someone
+ /// How much energy we drain each time we mechpunch someone
var/melee_energy_drain = 0.015 * STANDARD_CELL_CHARGE
- ///Power we use to have the lights on
+ /// Power we use to have the lights on
var/light_power_drain = 0.002 * STANDARD_CELL_RATE
- ///Modifiers for directional damage reduction
+ /// Modifiers for directional damage reduction
var/list/facing_modifiers = list(MECHA_FRONT_ARMOUR = 0.5, MECHA_SIDE_ARMOUR = 1, MECHA_BACK_ARMOUR = 1.5)
- ///if we cant use our equipment(such as due to EMP)
+ /// If we cant use our equipment(such as due to EMP)
var/equipment_disabled = FALSE
/// Keeps track of the mech's cell
var/obj/item/stock_parts/power_store/cell
@@ -55,59 +55,59 @@
var/obj/item/stock_parts/capacitor/capacitor
/// Keeps track of the mech's servo motor
var/obj/item/stock_parts/servo/servo
- ///Contains flags for the mecha
+ /// Contains flags for the mecha
var/mecha_flags = CAN_STRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE | BEACON_TRACKABLE | AI_COMPATIBLE | BEACON_CONTROLLABLE
- ///Spark effects are handled by this datum
+ /// Spark effects are handled by this datum
var/datum/effect_system/basic/spark_spread/spark_system
- ///How powerful our lights are
+ /// How powerful our lights are
var/lights_power = 6
- ///Just stop the mech from doing anything
+ /// Just stop the mech from doing anything
var/completely_disabled = FALSE
- ///Whether this mech is allowed to move diagonally
+ /// Whether this mech is allowed to move diagonally
var/allow_diagonal_movement = TRUE
- ///Whether this mech moves into a direct as soon as it goes to move. Basically, turn and step in the same key press.
+ /// Whether this mech moves into a direct as soon as it goes to move. Basically, turn and step in the same key press.
var/pivot_step = FALSE
- ///Whether or not the mech destroys walls by running into it.
+ /// Whether or not the mech destroys walls by running into it.
var/bumpsmash = FALSE
- ///////////ATMOS
- ///Whether the cabin exchanges gases with the environment
+ // Atmos stuff
+ /// Whether the cabin exchanges gases with the environment
var/cabin_sealed = FALSE
- ///Internal air mix datum
+ /// Internal air mix datum
var/datum/gas_mixture/cabin_air
- ///Volume of the cabin
+ /// Volume of the cabin
var/cabin_volume = TANK_STANDARD_VOLUME * 3
- ///List of installed remote tracking beacons, including AI control beacons
+ /// List of installed remote tracking beacons, including AI control beacons
var/list/trackers = list()
- ///Camera installed into the mech
+ /// Camera installed into the mech
var/obj/machinery/camera/exosuit/chassis_camera
var/max_temperature = 25000
- ///Bitflags for internal damage
+ /// Bitflags for internal damage
var/internal_damage = NONE
/// % chance for internal damage to occur
var/internal_damage_probability = 20
- /// list of possibly dealt internal damage for this mech type
+ /// List of possibly dealt internal damage for this mech type
var/possible_int_damage = MECHA_INT_FIRE|MECHA_INT_TEMP_CONTROL|MECHA_CABIN_AIR_BREACH|MECHA_INT_CONTROL_LOST|MECHA_INT_SHORT_CIRCUIT
- /// damage threshold above which we take component damage
+ /// Damage threshold above which we take component damage
var/component_damage_threshold = 10
- ///Stores the DNA enzymes of a carbon so tht only they can access the mech
+ /// Stores the DNA enzymes of a carbon so tht only they can access the mech
var/dna_lock
/// A list of all granted accesses
var/list/accesses = list()
/// If the mech should require ALL or only ONE of the listed accesses
var/one_access = TRUE
- ///Typepath for the wreckage it spawns when destroyed
+ /// Typepath for the wreckage it spawns when destroyed
var/wreckage
- ///single flag for the type of this mech, determines what kind of equipment can be attached to it
+ /// Single flag for the type of this mech, determines what kind of equipment can be attached to it
var/mech_type
- ///assoc list: key-typepathlist before init, key-equipmentlist after
+ /// Assoc list: key-typepathlist before init, key-equipmentlist after
var/list/equip_by_category = list(
MECHA_L_ARM = null,
MECHA_R_ARM = null,
@@ -115,7 +115,7 @@
MECHA_POWER = list(),
MECHA_ARMOR = list(),
)
- ///assoc list: max equips for modules key-count
+ /// Assoc list: max equips for modules key-count
var/list/max_equip_by_category = list(
MECHA_L_ARM = 1,
MECHA_R_ARM = 1,
@@ -123,94 +123,71 @@
MECHA_POWER = 1,
MECHA_ARMOR = 0,
)
- ///flat equipment for iteration
+ /// Flat equipment for iteration
var/list/flat_equipment
- ///Handles an internal ore box for mining mechs
+ /// Internal ore box for mining mechs, or one picked up by a hydraulic clamp
var/obj/structure/ore_box/ore_box
///Whether our steps are silent due to no gravity
var/step_silent = FALSE
- ///Sound played when the mech moves
+ /// Sound played when the mech moves
var/stepsound = 'sound/vehicles/mecha/mechstep.ogg'
- ///Sound played when the mech walks
+ /// Sound played when the mech walks
var/turnsound = 'sound/vehicles/mecha/mechturn.ogg'
- ///Sounds for types of melee attack
+ /// Sounds for types of melee attack
var/brute_attack_sound = 'sound/items/weapons/punch4.ogg'
var/burn_attack_sound = 'sound/items/tools/welder.ogg'
var/tox_attack_sound = 'sound/effects/spray2.ogg'
- ///Sound on wall destroying
+ /// Sound on wall destroying
var/destroy_wall_sound = 'sound/effects/meteorimpact.ogg'
- ///Melee attack verb
+ /// Melee attack verb
var/list/attack_verbs = list("hit", "hits", "hitting")
- ///Cooldown duration between melee punches
+ /// Cooldown duration between melee punches
var/melee_cooldown = CLICK_CD_SLOW
- ///TIme taken to leave the mech
+ /// Time taken to leave the mech
var/exit_delay = 2 SECONDS
- ///Time you get slept for if you get forcible ejected by the mech exploding
+ /// Time you get slept for if you get forcible ejected by the mech exploding
var/destruction_sleep_duration = 2 SECONDS
- ///In case theres a different iconstate for AI/MMI pilot(currently only used for ripley)
+ /// In case theres a different iconstate for AI/MMI pilot(currently only used for ripley)
var/silicon_icon_state = null
- ///Currently ejecting, and unable to do things
+ /// Currently ejecting, and unable to do things
var/is_currently_ejecting = FALSE
- ///Safety for weapons. Won't fire if enabled, and toggled by middle click.
+ /// Safety for weapons. Won't fire if enabled, and toggled by middle click.
var/weapons_safety = FALSE
- ///Don't play standard sound when set safety if TRUE.
+ /// Don't play standard sound when set safety if TRUE.
var/safety_sound_custom = FALSE
- var/datum/effect_system/fluid_spread/smoke/smoke_system
-
- ////Action vars
- ///Ref to any active thrusters we might have
+ // Action vars
+ /// Ref to any active thrusters we might have
var/obj/item/mecha_parts/mecha_equipment/thrusters/active_thrusters
- ///Bool for energy shield on/off
- var/defense_mode = FALSE
-
- ///Bool for leg overload on/off
+ /// Bool for leg overload on/off
VAR_FINAL/overclock_mode = FALSE
- ///Whether it is possible to toggle overclocking from the cabin
+ /// Whether it is possible to toggle overclocking from the cabin
var/can_use_overclock = FALSE
- ///Speed and energy usage modifier for leg overload
+ /// Speed and energy usage modifier for leg overload
var/overclock_coeff = 1.5
- ///Current leg actuator temperature. Increases when overloaded, decreases when not.
+ /// Current leg actuator temperature. Increases when overloaded, decreases when not.
var/overclock_temp = 0
- ///Temperature threshold at which actuators may start causing internal damage
+ /// Temperature threshold at which actuators may start causing internal damage
var/overclock_temp_danger = 15
- ///Whether the mech has an option to enable safe overclocking
+ /// Whether the mech has an option to enable safe overclocking
var/overclock_safety_available = FALSE
- ///Whether the overclocking turns off automatically when overheated
+ /// Whether the overclocking turns off automatically when overheated
var/overclock_safety = FALSE
/// Action type for overclocking, if null - no action
var/overclock_action_type = /datum/action/vehicle/sealed/mecha/mech_overclock
/// Name of the overclock action
var/overclock_name = "overclock"
- //Bool for zoom on/off
- var/zoom_mode = FALSE
-
- ///Remaining smoke charges
- var/smoke_charges = 5
- ///Cooldown between using smoke
- var/smoke_cooldown = 10 SECONDS
-
- ///check for phasing, if it is set to text (to describe how it is phasing: "flying", "phasing") it will let the mech walk through walls.
- var/phasing = ""
- ///Power we use every time we phaze through something
- var/phasing_energy_drain = 0.2 * STANDARD_CELL_CHARGE
- ///icon_state for flick() when phazing
- var/phase_state = ""
-
- ///Wether we are strafing
+ /// Whether we are strafing
var/strafe = FALSE
- ///Bool for whether this mech can only be used on lavaland
- var/lavaland_only = FALSE
-
- /// ref to screen object that displays in the middle of the UI
+ /// Ref to screen object that displays in the middle of the UI
var/atom/movable/screen/map_view/ui_view
/// Theme of the mech TGUI
@@ -234,7 +211,6 @@
spark_system = new(src, 2, FALSE)
spark_system.attach(src)
- smoke_system = new(src, 3, holder = src)
cabin_air = new(cabin_volume)
@@ -274,9 +250,9 @@
AddElement(/datum/element/hostile_machine)
/obj/vehicle/sealed/mecha/Destroy()
- /// If the former occupants get polymorphed, mutated, chestburstered,
- /// or otherwise replaced by another mob, that mob is no longer in .occupants
- /// and gets deleted with the mech. However, they do remain in .contents
+ // If the former occupants get polymorphed, mutated, chestburstered,
+ // or otherwise replaced by another mob, that mob is no longer in .occupants
+ // and gets deleted with the mech. However, they do remain in .contents
var/list/potential_occupants = contents | occupants
for(var/mob/buggy_ejectee in potential_occupants)
mob_exit(buggy_ejectee, silent = TRUE, forced = TRUE)
@@ -297,7 +273,6 @@
QDEL_NULL(servo)
QDEL_NULL(cabin_air)
QDEL_NULL(spark_system)
- QDEL_NULL(smoke_system)
QDEL_NULL(ui_view)
QDEL_LIST(trackers)
QDEL_NULL(chassis_camera)
@@ -307,7 +282,7 @@
diag_hud.remove_atom_from_hud(src) //YEET
return ..()
-///Add parts on mech spawning. Skipped in manual construction.
+/// Add parts on mech spawning. Skipped in manual construction.
/obj/vehicle/sealed/mecha/proc/populate_parts()
cell = new /obj/item/stock_parts/power_store/cell/high(src)
scanmod = new /obj/item/stock_parts/scanning_module(src)
@@ -329,37 +304,42 @@
var/mob/living/silicon/ai/unlucky_ai
for(var/mob/living/occupant as anything in occupants)
- if(isAI(occupant))
- //FIXME: Nothiing about this block works
- var/mob/living/silicon/ai/ai = occupant
- if(!ai.linked_core && !ai.can_shunt) // we probably shouldnt gib AIs with a core or shunting abilities
- unlucky_ai = occupant
- ai.investigate_log("has been gibbed by having their mech destroyed.", INVESTIGATE_DEATHS)
- ai.gib(DROP_ALL_REMAINS) //No wreck, no AI to recover
- else
- mob_exit(ai, silent = TRUE, forced = TRUE) // so we dont ghost the AI
- continue
- else
+ if(!isAI(occupant))
mob_exit(occupant, forced = TRUE)
if(!isbrain(occupant)) // who would win.. 1 brain vs 1 sleep proc..
occupant.SetSleeping(destruction_sleep_duration)
+ continue
- if(wreckage)
- var/obj/structure/mecha_wreckage/WR = new wreckage(loc, unlucky_ai)
- for(var/obj/item/mecha_parts/mecha_equipment/E in flat_equipment)
- if(E.detachable && prob(30))
- WR.crowbar_salvage += E
- E.detach(WR) //detaches from src into WR
- E.active = TRUE
- else
- E.detach(loc)
- qdel(E)
- if(cell)
- WR.crowbar_salvage += cell
- cell.forceMove(WR)
- cell.use(rand(0, cell.charge), TRUE)
- cell = null
- return ..()
+ var/mob/living/silicon/ai/ai = occupant
+ if(ai.linked_core || ai.can_shunt) // we probably shouldnt gib AIs with a core or shunting abilities
+ mob_exit(ai, silent = TRUE, forced = TRUE) // so we dont ghost the AI
+ continue
+
+ unlucky_ai = occupant
+ ai.investigate_log("has been gibbed by having their mech destroyed.", INVESTIGATE_DEATHS)
+ ai.gib(DROP_ALL_REMAINS) //No wreck, no AI to recover
+
+ if(ore_box)
+ INVOKE_ASYNC(ore_box, TYPE_PROC_REF(/obj/structure/ore_box, dump_box_contents))
+
+ if(!wreckage)
+ return ..()
+
+ var/obj/structure/mecha_wreckage/wreck = new wreckage(loc, unlucky_ai)
+ for(var/obj/item/mecha_parts/mecha_equipment/equipment in flat_equipment)
+ if(equipment.detachable && prob(30))
+ wreck.crowbar_salvage += equipment
+ equipment.detach(wreck) //detaches from src into wreck
+ equipment.active = TRUE
+ else
+ equipment.detach(loc)
+ qdel(equipment)
+
+ if(cell)
+ wreck.crowbar_salvage += cell
+ cell.forceMove(wreck)
+ cell.use(rand(0, cell.charge), TRUE)
+ cell = null
/obj/vehicle/sealed/mecha/update_icon_state()
@@ -400,9 +380,9 @@
mob_occupant.update_mouse_pointer()
//override this proc if you need to split up mecha control between multiple people (see savannah_ivanov.dm)
-/obj/vehicle/sealed/mecha/auto_assign_occupant_flags(mob/M)
+/obj/vehicle/sealed/mecha/auto_assign_occupant_flags(mob/occupant)
if(driver_amount() < max_drivers)
- add_control_flags(M, FULL_MECHA_CONTROL)
+ add_control_flags(occupant, FULL_MECHA_CONTROL)
/obj/vehicle/sealed/mecha/generate_actions()
initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_eject)
@@ -416,14 +396,15 @@
initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/strafe, VEHICLE_CONTROL_DRIVE)
/obj/vehicle/sealed/mecha/add_occupant(mob/M, control_flags, forced)
- if(..())
+ . = ..()
+ if(.)
generate_equipment_actions(M)
/obj/vehicle/sealed/mecha/remove_occupant(mob/M)
remove_all_equipment_actions(M)
return ..()
-///Generates action buttons for all eligible equipment and grants them to the occupant with VEHICLE_CONTROL_SETTINGS flag.
+/// Generates action buttons for all eligible equipment and grants them to the occupant with VEHICLE_CONTROL_SETTINGS flag.
/obj/vehicle/sealed/mecha/proc/generate_equipment_actions(mob/occupant)
if(!(occupant in occupants) || !(occupants[occupant] & VEHICLE_CONTROL_SETTINGS))
return
@@ -433,7 +414,7 @@
grant_equipment_action(occupant, equipment)
-///Removes all equipment actions from a specific occupant.
+/// Removes all equipment actions from a specific occupant.
/obj/vehicle/sealed/mecha/proc/remove_all_equipment_actions(mob/occupant)
var/list/actions = LAZYACCESS(occupant_actions, occupant)
if(!actions)
@@ -442,7 +423,6 @@
for(var/equipment_type in actions)
if(!ispath(equipment_type, /obj/item/mecha_parts/mecha_equipment))
continue
-
remove_action_type_from_mob(equipment_type, occupant)
/**
@@ -497,16 +477,6 @@
return base_icon_state
return "[base_icon_state]-open"
-/obj/vehicle/sealed/mecha/CanPassThrough(atom/blocker, movement_dir, blocker_opinion)
- if(!phasing || get_charge() <= phasing_energy_drain || throwing)
- return ..()
- if(phase_state)
- flick(phase_state, src)
- var/turf/destination_turf = get_step(loc, movement_dir)
- if(!check_teleport_valid(src, destination_turf) || SSmapping.level_trait(destination_turf.z, ZTRAIT_NOPHASE))
- return FALSE
- return TRUE
-
/obj/vehicle/sealed/mecha/get_cell()
return cell
@@ -521,7 +491,8 @@
to_chat(mob_occupant, span_notice("Equipment control unit has been rebooted successfully."))
set_mouse_pointer()
-/obj/vehicle/sealed/mecha/proc/update_part_values() ///Updates the values given by scanning module and capacitor tier, called when a part is removed or inserted.
+/// Updates the values given by scanning module and capacitor tier, called when a part is removed or inserted.
+/obj/vehicle/sealed/mecha/proc/update_part_values()
update_energy_drain()
if(capacitor)
@@ -599,7 +570,7 @@
/obj/vehicle/sealed/mecha/generate_integrity_message()
var/examine_text = ""
- var/integrity = atom_integrity*100/max_integrity
+ var/integrity = atom_integrity / max_integrity * 100
switch(integrity)
if(85 to 100)
@@ -615,7 +586,7 @@
return examine_text
-///Locate an internal tack in the utility modules
+/// Locate an internal tack in the utility modules
/obj/vehicle/sealed/mecha/proc/get_internal_tank()
var/obj/item/mecha_parts/mecha_equipment/air_tank/module = locate(/obj/item/mecha_parts/mecha_equipment/air_tank) in equip_by_category[MECHA_UTILITY]
return module?.internal_tank
@@ -742,7 +713,7 @@
playsound(src,'sound/machines/clockcult/brass_skewer.ogg', 40, TRUE)
log_message("Toggled lights off due to the lack of power.", LOG_MECHA)
-///Called when a driver clicks somewhere. Handles everything like equipment, punches, etc.
+/// Called when a driver clicks somewhere. Handles everything like equipment, punches, etc.
/obj/vehicle/sealed/mecha/proc/on_mouseclick(mob/user, atom/target, list/modifiers)
SIGNAL_HANDLER
if(LAZYACCESS(modifiers, MIDDLE_CLICK))
@@ -756,14 +727,7 @@
return
if(!isturf(target) && !isturf(target.loc)) // Prevents inventory from being drilled
return
- if(completely_disabled || is_currently_ejecting || (mecha_flags & CANNOT_INTERACT))
- return
- if(phasing)
- balloon_alert(user, "not while [phasing]!")
- return
- if(user.incapacitated)
- return
- if(!get_charge())
+ if(can_interact_with(target, user, modifiers))
return
if(src == target)
return
@@ -818,6 +782,15 @@
if(target.mech_melee_attack(src, user))
TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_MELEE_ATTACK, melee_cooldown)
+/obj/vehicle/sealed/mecha/proc/can_interact_with(atom/target, mob/user, list/modifiers)
+ if(completely_disabled || is_currently_ejecting || (mecha_flags & CANNOT_INTERACT))
+ return FALSE
+ if(user.incapacitated)
+ return FALSE
+ if(!get_charge())
+ return FALSE
+ return TRUE
+
/// Driver alt clicks anything while in mech
/obj/vehicle/sealed/mecha/proc/on_click_alt(mob/user, atom/target, params)
SIGNAL_HANDLER
@@ -837,13 +810,13 @@
toggle_strafe()
-/// middle mouse click signal wrapper for AI users
+/// Middle mouse click signal wrapper for AI users
/obj/vehicle/sealed/mecha/proc/on_middlemouseclick(mob/user, atom/target, params)
SIGNAL_HANDLER
if(isAI(user))
on_mouseclick(user, target, params)
-///Displays a special speech bubble when someone inside the mecha speaks
+/// Displays a special speech bubble when someone inside the mecha speaks
/obj/vehicle/sealed/mecha/proc/display_speech_bubble(datum/source, list/speech_args)
SIGNAL_HANDLER
var/list/speech_bubble_recipients = list()
@@ -854,10 +827,7 @@
var/image/mech_speech = image('icons/mob/effects/talk.dmi', src, "machine[say_test(speech_args[SPEECH_MESSAGE])]",MOB_LAYER+1)
INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(flick_overlay_global), mech_speech, speech_bubble_recipients, 3 SECONDS)
-/////////////////////////////////////
-//////// Atmospheric stuff ////////
-/////////////////////////////////////
-
+// Atmospheric stuff
/obj/vehicle/sealed/mecha/remove_air(amount)
if((mecha_flags & IS_ENCLOSED) && cabin_sealed)
return cabin_air.remove(amount)
@@ -871,17 +841,17 @@
/obj/vehicle/sealed/mecha/return_analyzable_air()
return cabin_air
-///fetches pressure of the gas mixture we are using
+/// Fetches pressure of the gas mixture we are using
/obj/vehicle/sealed/mecha/proc/return_pressure()
var/datum/gas_mixture/air = return_air()
return air?.return_pressure()
-///fetches temp of the gas mixture we are using
+/// Fetches temp of the gas mixture we are using
/obj/vehicle/sealed/mecha/return_temperature()
var/datum/gas_mixture/air = return_air()
return air?.return_temperature()
-///makes cabin unsealed, dumping cabin air outside or airtight filling the cabin with external air mix
+/// Makes cabin unsealed, dumping cabin air outside or airtight filling the cabin with external air mix
/obj/vehicle/sealed/mecha/proc/set_cabin_seal(mob/user, cabin_sealed)
if(!(mecha_flags & IS_ENCLOSED))
balloon_alert(user, "cabin can't be sealed!")
@@ -962,16 +932,18 @@
overclock_mode = forced_state
else
overclock_mode = !overclock_mode
+
log_message("Toggled [overclock_name].", LOG_MECHA)
for(var/mob/occupant as anything in occupants)
balloon_alert(occupant, "[overclock_name] [overclock_mode ? "on":"off"]")
var/datum/action/act = locate(/datum/action/vehicle/sealed/mecha/mech_overclock) in occupant.actions
act?.build_all_button_icons(UPDATE_BUTTON_ICON)
+
if(overclock_mode)
- movedelay = movedelay / overclock_coeff
+ movedelay /= overclock_coeff
visible_message(span_notice("[src] starts heating up, making humming sounds."))
else
- movedelay = initial(movedelay)
+ movedelay *= overclock_coeff
visible_message(span_notice("[src] cools down and the humming stops."))
update_energy_drain()
return TRUE
@@ -982,15 +954,14 @@
step_energy_drain = initial(step_energy_drain) / servo.rating
else
step_energy_drain = 2 * initial(step_energy_drain)
+
if(overclock_mode)
step_energy_drain *= overclock_coeff
if(capacitor)
- phasing_energy_drain = initial(phasing_energy_drain) / capacitor.rating
melee_energy_drain = initial(melee_energy_drain) / capacitor.rating
light_power_drain = initial(light_power_drain) / capacitor.rating
else
- phasing_energy_drain = initial(phasing_energy_drain)
melee_energy_drain = initial(melee_energy_drain)
light_power_drain = initial(light_power_drain)
diff --git a/code/modules/vehicles/mecha/combat/durand.dm b/code/modules/vehicles/mecha/combat/durand.dm
index 99f32ddc987..05641719ee0 100644
--- a/code/modules/vehicles/mecha/combat/durand.dm
+++ b/code/modules/vehicles/mecha/combat/durand.dm
@@ -20,7 +20,13 @@
MECHA_POWER = 1,
MECHA_ARMOR = 1,
)
- var/obj/durand_shield/shield
+
+ /// Bool for energy shield on/off
+ var/defense_mode = FALSE
+ /// Fake shield object we use as a way to redirect attacks off ourselves
+ var/obj/durand_shield/shield = null
+ /// Is the shield currently switching modes?
+ var/switching = FALSE
/datum/armor/mecha_durand
melee = 40
@@ -33,137 +39,145 @@
/obj/vehicle/sealed/mecha/durand/Initialize(mapload)
. = ..()
- shield = new /obj/durand_shield(loc, src, plane, layer, dir)
- RegisterSignal(src, COMSIG_MECHA_ACTION_TRIGGER, PROC_REF(relay))
+ shield = new(src, src)
+ vis_contents += shield
/obj/vehicle/sealed/mecha/durand/Destroy()
if(shield)
QDEL_NULL(shield)
return ..()
-
/obj/vehicle/sealed/mecha/durand/generate_actions()
. = ..()
initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_defense_mode)
/obj/vehicle/sealed/mecha/durand/process()
. = ..()
- if(defense_mode && !use_energy(0.01 * STANDARD_CELL_CHARGE)) //Defence mode can only be on with a occupant so we check if one of them can toggle it and toggle
- for(var/O in occupants)
- var/mob/living/occupant = O
- var/datum/action/action = LAZYACCESSASSOC(occupant_actions, occupant, /datum/action/vehicle/sealed/mecha/mech_defense_mode)
- if(action)
- action.Trigger()
- break
-
-/obj/vehicle/sealed/mecha/durand/Move(direction)
- . = ..()
- if(shield)
- shield.forceMove(loc)
- shield.setDir(dir)
-
-/obj/vehicle/sealed/mecha/durand/forceMove(turf/T)
- . = ..()
- shield.forceMove(T)
+ // Defence mode can only be on with a occupant so we check if one of them can toggle it and toggle
+ if(defense_mode && !use_energy(0.01 * STANDARD_CELL_CHARGE))
+ toggle_defense()
/obj/vehicle/sealed/mecha/durand/mob_exit(mob/M, silent = FALSE, randomstep = FALSE, forced = FALSE)
if(defense_mode)
- var/datum/action/action = LAZYACCESSASSOC(occupant_actions, M, /datum/action/vehicle/sealed/mecha/mech_defense_mode)
- if(action)
- INVOKE_ASYNC(action, TYPE_PROC_REF(/datum/action, Trigger), null, NONE, FALSE)
+ toggle_defense()
return ..()
-///Relays the signal from the action button to the shield, and creates a new shield if the old one is MIA.
-/obj/vehicle/sealed/mecha/durand/proc/relay(datum/source, mob/owner, list/signal_args)
- SIGNAL_HANDLER
- if(!shield) //if the shield somehow got deleted
- stack_trace("Durand triggered relay without a shield")
- shield = new /obj/durand_shield(loc, src, layer)
- shield.setDir(dir)
-
//Redirects projectiles to the shield if defense_check decides they should be blocked and returns true.
/obj/vehicle/sealed/mecha/durand/projectile_hit(obj/projectile/hitting_projectile, def_zone, piercing_hit, blocked)
if(defense_check(hitting_projectile.loc) && shield)
return shield.projectile_hit(hitting_projectile, def_zone, piercing_hit, blocked)
return ..()
-/**Checks if defense mode is enabled, and if the attacker is standing in an area covered by the shield.
-Expects a turf. Returns true if the attack should be blocked, false if not.*/
+/**
+ * Checks if defense mode is enabled, and if the attacker is standing in an area covered by the shield.
+ * Expects a turf. Returns true if the attack should be blocked, false if not.
+ **/
/obj/vehicle/sealed/mecha/durand/proc/defense_check(turf/aloc)
- if (!defense_mode || !shield || shield.switching)
+ if (!defense_mode || !shield || switching)
return FALSE
- . = FALSE
+
switch(dir)
- if (1)
- if(abs(x - aloc.x) <= (y - aloc.y) * -2)
- . = TRUE
- if (2)
- if(abs(x - aloc.x) <= (y - aloc.y) * 2)
- . = TRUE
- if (4)
- if(abs(y - aloc.y) <= (x - aloc.x) * -2)
- . = TRUE
- if (8)
- if(abs(y - aloc.y) <= (x - aloc.x) * 2)
- . = TRUE
- return
+ if (NORTH)
+ return abs(x - aloc.x) <= (y - aloc.y) * -2
+ if (SOUTH)
+ return abs(x - aloc.x) <= (y - aloc.y) * 2
+ if (EAST)
+ return abs(y - aloc.y) <= (x - aloc.x) * -2
+ if (WEST)
+ return abs(y - aloc.y) <= (x - aloc.x) * 2
+
+/obj/vehicle/sealed/mecha/durand/proc/toggle_defense(mob/living/user)
+ if(!LAZYLEN(occupants))
+ return
+
+ if(switching && (user || !defense_mode)) // Allow force shutdowns during animation
+ return
+
+ if(!defense_mode && cell?.charge < 100) // If it's off, and we have less than 100 units of power
+ if (user)
+ balloon_alert(user, "insufficient power")
+ return
+
+ switching = TRUE
+ defense_mode = !defense_mode
+
+ if(user)
+ balloon_alert(user, "shield [defense_mode ? "enabled" : "disabled"]")
+ log_message("User has toggled defense mode -- now [defense_mode ? "enabled" : "disabled"].", LOG_MECHA)
+ else
+ log_message("defense mode state changed -- now [defense_mode ? "enabled" : "disabled"].", LOG_MECHA)
+
+ for(var/mob/living/occupant as anything in occupants)
+ var/datum/action/button = occupant_actions[occupant][/datum/action/vehicle/sealed/mecha/mech_defense_mode]
+ button.button_icon_state = "mech_defense_mode_[defense_mode ? "on" : "off"]"
+ button.build_all_button_icons()
+
+ shield.set_light_on(defense_mode)
+
+ if(defense_mode)
+ shield.icon_state = "shield"
+ flick("shield_raise", shield)
+ playsound(src, 'sound/vehicles/mecha/mech_shield_raise.ogg', 50, FALSE)
+ else
+ shield.icon_state = "shield_null"
+ flick("shield_drop", shield)
+ playsound(src, 'sound/vehicles/mecha/mech_shield_drop.ogg', 50, FALSE)
+
+ addtimer(VARSET_CALLBACK(src, switching, FALSE), 0.7 SECONDS) // Shield animation length
/obj/vehicle/sealed/mecha/durand/attack_generic(mob/user, damage_amount = 0, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, armor_penetration = 0)
- if(defense_check(user.loc))
+ if(defense_check(get_turf(user)))
log_message("Attack absorbed by defense field. Attacker - [user].", LOG_MECHA, color="orange")
return shield.attack_generic(user, damage_amount, damage_type, damage_flag, sound_effect, armor_penetration)
return ..()
-/obj/vehicle/sealed/mecha/durand/blob_act(obj/structure/blob/B)
- if(defense_check(B.loc))
- log_message("Attack by blob. Attacker - [B].", LOG_MECHA, color="red")
- log_message("Attack absorbed by defense field.", LOG_MECHA, color="orange")
- shield.blob_act(B)
- else
- . = ..()
+/obj/vehicle/sealed/mecha/durand/blob_act(obj/structure/blob/blob)
+ if(!defense_check(get_turf(blob)))
+ return ..()
+ log_message("Attack by blob. Attacker - [blob].", LOG_MECHA, color="red")
+ log_message("Attack absorbed by defense field.", LOG_MECHA, color="orange")
+ return shield.blob_act(blob)
-/obj/vehicle/sealed/mecha/durand/attackby(obj/item/W as obj, mob/user as mob, list/modifiers, list/attack_modifiers)
- if(defense_check(user.loc))
- log_message("Attack absorbed by defense field. Attacker - [user], with [W]", LOG_MECHA, color="orange")
- shield.attackby(W, user, modifiers)
- else
- . = ..()
+/obj/vehicle/sealed/mecha/durand/attackby(obj/item/weapon, mob/user as mob, list/modifiers, list/attack_modifiers)
+ if(defense_check(get_turf(user)))
+ log_message("Attack absorbed by defense field. Attacker - [user], with [weapon]", LOG_MECHA, color="orange")
+ return shield.attackby(weapon, user, modifiers)
+ return ..()
/obj/vehicle/sealed/mecha/durand/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
- if(defense_check(AM.loc))
+ if(defense_check(get_turf(AM)))
log_message("Impact with [AM] absorbed by defense field.", LOG_MECHA, color="orange")
- shield.hitby(AM, skipcatch, hitpush, blocked, throwingdatum)
- else
- . = ..()
+ return shield.hitby(AM, skipcatch, hitpush, blocked, throwingdatum)
+ return ..()
/datum/action/vehicle/sealed/mecha/mech_defense_mode
- name = "Toggle an energy shield that blocks all attacks from the faced direction at a heavy power cost."
+ name = "Toggle Shield"
+ desc = "Toggle an energy shield that blocks all attacks from the faced direction at a heavy power cost."
button_icon_state = "mech_defense_mode_off"
/datum/action/vehicle/sealed/mecha/mech_defense_mode/Trigger(mob/clicker, trigger_flags, forced_state = FALSE)
. = ..()
if(!.)
return
- if(!chassis || !(owner in chassis.occupants))
- return
- SEND_SIGNAL(chassis, COMSIG_MECHA_ACTION_TRIGGER, owner, args) //Signal sent to the mech, to be handed to the shield. See durand.dm for more details
+ if(chassis && (owner in chassis.occupants))
+ var/obj/vehicle/sealed/mecha/durand/durand = chassis
+ durand.toggle_defense(owner)
-////////////////////////////
-///// Shield processing ////
-////////////////////////////
+// Shield processing
-/**An object to take the hit for us when using the Durand's defense mode.
-It is spawned in during the durand's initilization, and always stays on the same tile.
-Normally invisible, until defense mode is actvated. When the durand detects an attack that should be blocked, the
-attack is passed to the shield. The shield takes the damage, uses it to calculate charge cost, and then sets its
-own integrity back to max. Shield is automatically dropped if we run out of power or the user gets out.*/
+/**
+ * An object to take the hit for us when using the Durand's defense mode.
+ * It is spawned in during the durand's initilization.
+ * Normally invisible, until defense mode is actvated. When the durand detects an attack that should be blocked, the
+ * attack is passed to the shield. The shield takes the damage, uses it to calculate charge cost, and then sets its
+ * own integrity back to max. Shield is automatically dropped if we run out of power or the user gets out.
+ **/
/obj/durand_shield //projectiles get passed to this when defense mode is enabled
name = "defense grid"
icon = 'icons/mob/effects/durand_shield.dmi'
icon_state = "shield_null"
- invisibility = INVISIBILITY_MAXIMUM //no showing on right-click
- pixel_y = 4
+ pixel_z = 4
max_integrity = 10000
anchored = TRUE
light_system = OVERLAY_LIGHT
@@ -171,100 +185,22 @@ own integrity back to max. Shield is automatically dropped if we run out of powe
light_power = 1
light_color = LIGHT_COLOR_FAINT_CYAN
light_on = FALSE
- resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF //The shield should not take damage from fire, lava, or acid; that's the mech's job.
- ///Our link back to the durand
+ light_flags = LIGHT_ATTACHED
+ vis_flags = VIS_INHERIT_DIR | VIS_INHERIT_LAYER | VIS_INHERIT_PLANE | VIS_INHERIT_ID
+ resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF // The shield should not take damage from fire, lava, or acid; that's the mech's job.
+ /// Our link back to the durand
var/obj/vehicle/sealed/mecha/durand/chassis
- ///To keep track of things during the animation
- var/switching = FALSE
-/obj/durand_shield/Initialize(mapload, chassis, plane, layer, dir)
+/obj/durand_shield/Initialize(mapload, obj/vehicle/sealed/mecha/durand/chassis)
. = ..()
src.chassis = chassis
- src.layer = ABOVE_MOB_LAYER
- SET_PLANE_IMPLICIT(src, plane)
- setDir(dir)
- RegisterSignal(chassis, COMSIG_MECHA_ACTION_TRIGGER, PROC_REF(activate))
- RegisterSignal(chassis, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, PROC_REF(shield_glide_size_update))
/obj/durand_shield/Destroy()
- UnregisterSignal(src, COMSIG_MECHA_ACTION_TRIGGER)
if(chassis)
- UnregisterSignal(chassis, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE)
chassis.shield = null
chassis = null
return ..()
-/obj/durand_shield/proc/shield_glide_size_update(datum/source, target)
- SIGNAL_HANDLER
- glide_size = target
-
-/**
- * Handles activating and deactivating the shield.
- *
- * This proc is called by a signal sent from the mech's action button and
- * relayed by the mech itself. The "forced" variable, `signal_args[1]`, will
- * skip the to-pilot text and is meant for when the shield is disabled by
- * means other than the action button (like running out of power).
- *
- * Arguments:
- * * source: the shield
- * * owner: mob that activated the shield
- * * signal_args: whether it's forced
- */
-/obj/durand_shield/proc/activate(datum/source, mob/owner, list/signal_args)
- SIGNAL_HANDLER
- if(!LAZYLEN(chassis?.occupants))
- return
- if(switching && !signal_args[1])
- return
- if(!chassis.defense_mode && (!chassis.cell || chassis.cell.charge < 100)) //If it's off, and we have less than 100 units of power
- chassis.balloon_alert(owner, "insufficient power")
- return
- switching = TRUE
- chassis.defense_mode = !chassis.defense_mode
- if(!signal_args[1])
- chassis.balloon_alert(owner, "shield [chassis.defense_mode?"enabled":"disabled"]")
- chassis.log_message("User has toggled defense mode -- now [chassis.defense_mode?"enabled":"disabled"].", LOG_MECHA)
- else
- chassis.log_message("defense mode state changed -- now [chassis.defense_mode?"enabled":"disabled"].", LOG_MECHA)
- for(var/occupant in chassis.occupants)
- var/datum/action/button = chassis.occupant_actions[occupant][/datum/action/vehicle/sealed/mecha/mech_defense_mode]
- button.button_icon_state = "mech_defense_mode_[chassis.defense_mode ? "on" : "off"]"
- button.build_all_button_icons()
-
- set_light_on(chassis.defense_mode)
-
- if(chassis.defense_mode)
- SetInvisibility(INVISIBILITY_NONE, id=type)
- flick("shield_raise", src)
- playsound(src, 'sound/vehicles/mecha/mech_shield_raise.ogg', 50, FALSE)
- icon_state = "shield"
- resetdir(chassis, dir, dir) // to set the plane for the shield properly when it's turned on
- RegisterSignal(chassis, COMSIG_ATOM_DIR_CHANGE, PROC_REF(resetdir))
- else
- flick("shield_drop", src)
- playsound(src, 'sound/vehicles/mecha/mech_shield_drop.ogg', 50, FALSE)
- icon_state = "shield_null"
- addtimer(CALLBACK(src, PROC_REF(make_invisible)), 1 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE)
- UnregisterSignal(chassis, COMSIG_ATOM_DIR_CHANGE)
- switching = FALSE
-
-/**
- * Sets invisibility to INVISIBILITY_MAXIMUM if defense mode is disabled
- *
- * We need invisibility set to higher than 25 for the shield to not appear
- * in the right-click context menu, but if we do it too early, we miss the
- * deactivate animation. Hense, timer and this proc.
- */
-/obj/durand_shield/proc/make_invisible()
- if(!chassis.defense_mode)
- RemoveInvisibility(type)
-
-/obj/durand_shield/proc/resetdir(datum/source, olddir, newdir)
- SIGNAL_HANDLER
-
- setDir(newdir)
-
/obj/durand_shield/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", sound_effect = TRUE, attack_dir, armour_penetration = 0)
if(!chassis)
qdel(src)
@@ -277,10 +213,7 @@ own integrity back to max. Shield is automatically dropped if we run out of powe
return
if(!chassis.use_energy(. * (STANDARD_CELL_CHARGE / 150)))
chassis.cell?.charge = 0
- for(var/O in chassis.occupants)
- var/mob/living/occupant = O
- var/datum/action/action = LAZYACCESSASSOC(chassis.occupant_actions, occupant, /datum/action/vehicle/sealed/mecha/mech_defense_mode)
- action.Trigger()
+ chassis.toggle_defense()
atom_integrity = 10000
/obj/durand_shield/play_attack_sound()
diff --git a/code/modules/vehicles/mecha/combat/marauder.dm b/code/modules/vehicles/mecha/combat/marauder.dm
index e316de70bf7..083bf66e3dd 100644
--- a/code/modules/vehicles/mecha/combat/marauder.dm
+++ b/code/modules/vehicles/mecha/combat/marauder.dm
@@ -24,6 +24,15 @@
)
bumpsmash = TRUE
+ /// Reusable smoke generator system
+ var/datum/effect_system/fluid_spread/smoke/smoke_system
+ /// Remaining smoke charges
+ var/smoke_charges = 5
+ /// Cooldown between using smoke
+ var/smoke_cooldown = 10 SECONDS
+ /// Bool for zoom on/off
+ var/zoom_mode = FALSE
+
/datum/armor/mecha_marauder
melee = 70
bullet = 60
@@ -33,6 +42,14 @@
fire = 100
acid = 100
+/obj/vehicle/sealed/mecha/marauder/Initialize(mapload, built_manually)
+ . = ..()
+ smoke_system = new(src, 3, holder = src)
+
+/obj/vehicle/sealed/mecha/marauder/Destroy()
+ QDEL_NULL(smoke_system)
+ return ..()
+
/obj/vehicle/sealed/mecha/marauder/generate_actions()
. = ..()
initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_smoke)
@@ -54,20 +71,51 @@
servo = new /obj/item/stock_parts/servo/femto(src)
update_part_values()
+/obj/vehicle/sealed/mecha/marauder/remove_occupant(mob/driver)
+ . = ..()
+ zoom_mode = FALSE
+
+/obj/vehicle/sealed/mecha/marauder/can_move(direction)
+ . = ..()
+ if(!. || !zoom_mode)
+ return
+
+ if(TIMER_COOLDOWN_FINISHED(src, COOLDOWN_MECHA_MESSAGE))
+ to_chat(occupants, "[icon2html(src, occupants)][span_warning("Unable to move while in zoom mode!")]")
+ TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_MESSAGE, 2 SECONDS)
+ return FALSE
+
/datum/action/vehicle/sealed/mecha/mech_smoke
name = "Smoke"
button_icon_state = "mech_smoke"
+/datum/action/vehicle/sealed/mecha/mech_smoke/IsAvailable(feedback)
+ . = ..()
+ if (!.)
+ return
+
+ var/obj/vehicle/sealed/mecha/marauder/maradeur = chassis
+ if(!TIMER_COOLDOWN_FINISHED(maradeur, COOLDOWN_MECHA_SMOKE))
+ if (feedback)
+ owner.balloon_alert(owner, "smoke charges on cooldown!")
+ return FALSE
+
+ if (!maradeur.smoke_charges)
+ if (feedback)
+ owner.balloon_alert(owner, "out of smoke charges!")
+ return FALSE
+
/datum/action/vehicle/sealed/mecha/mech_smoke/Trigger(mob/clicker, trigger_flags)
. = ..()
if(!.)
return
if(!chassis || !(owner in chassis.occupants))
return
- if(TIMER_COOLDOWN_FINISHED(src, COOLDOWN_MECHA_SMOKE) && chassis.smoke_charges>0)
- chassis.smoke_system.start()
- chassis.smoke_charges--
- TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_SMOKE, chassis.smoke_cooldown)
+ var/obj/vehicle/sealed/mecha/marauder/maradeur = chassis
+ if(TIMER_COOLDOWN_FINISHED(maradeur, COOLDOWN_MECHA_SMOKE) && maradeur.smoke_charges)
+ maradeur.smoke_system.start()
+ maradeur.smoke_charges--
+ TIMER_COOLDOWN_START(maradeur, COOLDOWN_MECHA_SMOKE, maradeur.smoke_cooldown)
/datum/action/vehicle/sealed/mecha/mech_zoom
name = "Zoom"
@@ -79,11 +127,12 @@
return
if(!owner.client || !chassis || !(owner in chassis.occupants))
return
- chassis.zoom_mode = !chassis.zoom_mode
- button_icon_state = "mech_zoom_[chassis.zoom_mode ? "on" : "off"]"
- chassis.log_message("Toggled zoom mode.", LOG_MECHA)
- to_chat(owner, "[icon2html(chassis, owner)]Zoom mode [chassis.zoom_mode?"en":"dis"]abled.")
- if(chassis.zoom_mode)
+ var/obj/vehicle/sealed/mecha/marauder/maradeur = chassis
+ maradeur.zoom_mode = !maradeur.zoom_mode
+ button_icon_state = "mech_zoom_[maradeur.zoom_mode ? "on" : "off"]"
+ maradeur.log_message("Toggled zoom mode.", LOG_MECHA)
+ to_chat(owner, "[icon2html(maradeur, owner)]Zoom mode [maradeur.zoom_mode ? "en" : "dis"]abled.")
+ if(maradeur.zoom_mode)
owner.client.view_size.setTo(4.5)
SEND_SOUND(owner, sound('sound/vehicles/mecha/imag_enh.ogg', volume=50))
else
diff --git a/code/modules/vehicles/mecha/combat/phazon.dm b/code/modules/vehicles/mecha/combat/phazon.dm
index e193f4e5e1c..5ca91ce5817 100644
--- a/code/modules/vehicles/mecha/combat/phazon.dm
+++ b/code/modules/vehicles/mecha/combat/phazon.dm
@@ -1,6 +1,6 @@
/obj/vehicle/sealed/mecha/phazon
- desc = "This is a Phazon exosuit. The pinnacle of scientific research and pride of Nanotrasen, it uses cutting edge anomalous technology and expensive materials."
name = "\improper Phazon"
+ desc = "This is a Phazon exosuit. The pinnacle of scientific research and pride of Nanotrasen, it uses cutting edge anomalous technology and expensive materials."
icon_state = "phazon"
base_icon_state = "phazon"
movedelay = 2
@@ -21,7 +21,13 @@
MECHA_POWER = 1,
MECHA_ARMOR = 2,
)
- phase_state = "phazon-phase"
+
+ /// Are we currently phasing through walls?
+ var/phasing = FALSE
+ /// Power we use every time we phaze through something
+ var/phasing_energy_drain = 0.2 * STANDARD_CELL_CHARGE
+ /// Icon_state for flick() when phasing
+ var/phase_state = "phazon-phase"
/datum/armor/mecha_phazon
melee = 30
@@ -37,6 +43,42 @@
initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_toggle_phasing)
initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_switch_damtype)
+/obj/vehicle/sealed/mecha/phazon/CanPassThrough(atom/blocker, movement_dir, blocker_opinion)
+ if(!phasing || get_charge() <= phasing_energy_drain || throwing)
+ return ..()
+ if(phase_state)
+ flick(phase_state, src)
+ var/turf/destination_turf = get_step(loc, movement_dir)
+ if(!check_teleport_valid(src, destination_turf) || SSmapping.level_trait(destination_turf.z, ZTRAIT_NOPHASE))
+ return FALSE
+ return TRUE
+
+/obj/vehicle/sealed/mecha/phazon/vehicle_move(direction, forcerotate)
+ . = ..()
+ if(. && phasing)
+ use_energy(phasing_energy_drain)
+
+/obj/vehicle/sealed/mecha/phazon/try_bumpsmash(atom/obstacle)
+ if(phasing) // Theres only one cause for phasing canpass fails
+ to_chat(occupants, "[icon2html(src, occupants)][span_warning("A dull, universal force is preventing you from phasing here!")]")
+ spark_system.start()
+ return
+ return ..()
+
+/obj/vehicle/sealed/mecha/phazon/update_energy_drain()
+ . = ..()
+ if(capacitor)
+ phasing_energy_drain = initial(phasing_energy_drain) / capacitor.rating
+ else
+ phasing_energy_drain = initial(phasing_energy_drain)
+
+/obj/vehicle/sealed/mecha/phazon/can_interact_with(atom/target, mob/user, list/modifiers)
+ . = ..()
+ if (!. || !phasing)
+ return
+ balloon_alert(user, "not while phasing!")
+ return FALSE
+
/datum/action/vehicle/sealed/mecha/mech_switch_damtype
name = "Reconfigure arm microtool arrays"
button_icon_state = "mech_damtype_brute"
@@ -73,7 +115,8 @@
return
if(!chassis || !(owner in chassis.occupants))
return
- chassis.phasing = chassis.phasing ? "" : "phasing"
- button_icon_state = "mech_phasing_[chassis.phasing ? "on" : "off"]"
- chassis.balloon_alert(owner, "[chassis.phasing ? "enabled" : "disabled"] phasing")
+ var/obj/vehicle/sealed/mecha/phazon/phazon = chassis
+ phazon.phasing = !phazon.phasing
+ button_icon_state = "mech_phasing_[phazon.phasing ? "on" : "off"]"
+ phazon.balloon_alert(owner, "[phazon.phasing ? "enabled" : "disabled"] phasing")
build_all_button_icons()
diff --git a/code/modules/vehicles/mecha/combat/savannah_ivanov.dm b/code/modules/vehicles/mecha/combat/savannah_ivanov.dm
index 13f594f243b..4f9794a7c93 100644
--- a/code/modules/vehicles/mecha/combat/savannah_ivanov.dm
+++ b/code/modules/vehicles/mecha/combat/savannah_ivanov.dm
@@ -37,8 +37,8 @@
MECHA_POWER = 1,
MECHA_ARMOR = 1,
)
- //no tax on flying, since the power cost is in the leap itself.
- phasing_energy_drain = 0
+ /// Are we currently in the middle of a skyfall jump?
+ var/flying = FALSE
/datum/armor/mecha_savannah_ivanov
melee = 45
@@ -71,6 +71,22 @@
initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/skyfall, VEHICLE_CONTROL_DRIVE)
initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/ivanov_strike, VEHICLE_CONTROL_EQUIPMENT)
+// Pass through obstacles freely if we're flying
+/obj/vehicle/sealed/mecha/savannah_ivanov/CanPassThrough(atom/blocker, movement_dir, blocker_opinion)
+ if(!flying || throwing)
+ return ..()
+ var/turf/destination_turf = get_step(loc, movement_dir)
+ if(!check_teleport_valid(src, destination_turf) || SSmapping.level_trait(destination_turf.z, ZTRAIT_NOPHASE))
+ return FALSE
+ return TRUE
+
+/obj/vehicle/sealed/mecha/savannah_ivanov/can_interact_with(atom/target, mob/user, list/modifiers)
+ . = ..()
+ if (!. || !flying)
+ return
+ balloon_alert(user, "not while airborne!")
+ return FALSE
+
///Savannah Skyfall
/datum/action/vehicle/sealed/mecha/skyfall
name = "Savannah Skyfall"
@@ -86,17 +102,18 @@
return
if(!owner || !chassis || !(owner in chassis.occupants))
return
- if(chassis.phasing)
+ var/obj/vehicle/sealed/mecha/savannah_ivanov/savannah = chassis
+ if(savannah.flying)
to_chat(owner, span_warning("You're already airborne!"))
return
- if(TIMER_COOLDOWN_RUNNING(chassis, COOLDOWN_MECHA_SKYFALL))
- var/timeleft = S_TIMER_COOLDOWN_TIMELEFT(chassis, COOLDOWN_MECHA_SKYFALL)
+ if(TIMER_COOLDOWN_RUNNING(savannah, COOLDOWN_MECHA_SKYFALL))
+ var/timeleft = S_TIMER_COOLDOWN_TIMELEFT(savannah, COOLDOWN_MECHA_SKYFALL)
to_chat(owner, span_warning("You need to wait [DisplayTimeText(timeleft, 1)] before attempting to Skyfall."))
return
if(skyfall_charge_level)
abort_skyfall()
return
- chassis.balloon_alert(owner, "charging skyfall...")
+ savannah.balloon_alert(owner, "charging skyfall...")
INVOKE_ASYNC(src, PROC_REF(skyfall_charge_loop))
/**
@@ -146,7 +163,8 @@
new /obj/effect/skyfall_landingzone(launch_turf, chassis)
chassis.resistance_flags |= INDESTRUCTIBLE //not while jumping at least
chassis.mecha_flags |= QUIET_STEPS|QUIET_TURNS|CANNOT_INTERACT
- chassis.phasing = "flying"
+ var/obj/vehicle/sealed/mecha/savannah_ivanov/savannah = chassis
+ savannah.flying = TRUE
chassis.movedelay = 1
chassis.set_density(FALSE)
chassis.layer = ABOVE_ALL_MOB_LAYER
@@ -177,7 +195,8 @@
playsound(chassis, 'sound/effects/explosion/explosion1.ogg', 50, 1)
chassis.resistance_flags &= ~INDESTRUCTIBLE
chassis.mecha_flags &= ~(QUIET_STEPS|QUIET_TURNS|CANNOT_INTERACT)
- chassis.phasing = initial(chassis.phasing)
+ var/obj/vehicle/sealed/mecha/savannah_ivanov/savannah = chassis
+ savannah.flying = FALSE
chassis.movedelay = initial(chassis.movedelay)
chassis.set_density(TRUE)
chassis.layer = initial(chassis.layer)
diff --git a/code/modules/vehicles/mecha/equipment/mecha_equipment.dm b/code/modules/vehicles/mecha/equipment/mecha_equipment.dm
index e367837fb6d..f2f42d1f85b 100644
--- a/code/modules/vehicles/mecha/equipment/mecha_equipment.dm
+++ b/code/modules/vehicles/mecha/equipment/mecha_equipment.dm
@@ -160,8 +160,8 @@
if(flags & MECH_DO_AFTER_ADJACENCY_FLAG && !(chassis.Adjacent(target)))
return FALSE
-/obj/item/mecha_parts/mecha_equipment/proc/can_attach(obj/vehicle/sealed/mecha/M, attach_right = FALSE, mob/user)
- return default_can_attach(M, attach_right, user)
+/obj/item/mecha_parts/mecha_equipment/proc/can_attach(obj/vehicle/sealed/mecha/mech, attach_right = FALSE, mob/user)
+ return default_can_attach(mech, attach_right, user)
/obj/item/mecha_parts/mecha_equipment/proc/default_can_attach(obj/vehicle/sealed/mecha/mech, attach_right = FALSE, mob/user)
if(!(mech_flags & mech.mech_type))
diff --git a/code/modules/vehicles/mecha/mecha_actions.dm b/code/modules/vehicles/mecha/mecha_actions.dm
index 2fd55c14e4b..34d9e82e53b 100644
--- a/code/modules/vehicles/mecha/mecha_actions.dm
+++ b/code/modules/vehicles/mecha/mecha_actions.dm
@@ -107,7 +107,6 @@
chassis.toggle_strafe()
-
/obj/vehicle/sealed/mecha/proc/toggle_strafe()
if(!(mecha_flags & CAN_STRAFE))
to_chat(occupants, "this mecha doesn't support strafing!")
@@ -124,7 +123,7 @@
var/datum/action/action = LAZYACCESSASSOC(occupant_actions, occupant, /datum/action/vehicle/sealed/mecha/strafe)
action?.build_all_button_icons()
-///swap seats, for two person mecha
+/// Swap seats, for two person mecha
/datum/action/vehicle/sealed/mecha/swap_seat
name = "Switch Seats"
button_icon_state = "mech_seat_swap"
diff --git a/code/modules/vehicles/mecha/mecha_defense.dm b/code/modules/vehicles/mecha/mecha_defense.dm
index f41b3006e03..393c478b519 100644
--- a/code/modules/vehicles/mecha/mecha_defense.dm
+++ b/code/modules/vehicles/mecha/mecha_defense.dm
@@ -59,7 +59,7 @@
/obj/vehicle/sealed/mecha/attack_hand(mob/living/user, list/modifiers)
. = ..()
- if(.)
+ if(. || !user.combat_mode)
return
user.changeNext_move(CLICK_CD_MELEE) // Ugh. Ideally we shouldn't be setting cooldowns outside of click code.
user.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
@@ -73,26 +73,25 @@
/obj/vehicle/sealed/mecha/attack_alien(mob/living/user, list/modifiers)
log_message("Attack by alien. Attacker - [user].", LOG_MECHA, color="red")
playsound(loc, 'sound/items/weapons/slash.ogg', 100, TRUE)
- attack_generic(user, rand(user.melee_damage_lower, user.melee_damage_upper), BRUTE, MELEE, 0)
+ return attack_generic(user, rand(user.melee_damage_lower, user.melee_damage_upper), BRUTE, MELEE, 0)
/obj/vehicle/sealed/mecha/attack_animal(mob/living/simple_animal/user, list/modifiers)
log_message("Attack by simple animal. Attacker - [user].", LOG_MECHA, color="red")
if(!user.melee_damage_upper && !user.obj_damage)
user.emote("custom", message = "[user.friendly_verb_continuous] [src].")
- return 0
- else
- var/play_soundeffect = 1
- if(user.environment_smash)
- play_soundeffect = 0
- playsound(src, 'sound/effects/bang.ogg', 50, TRUE)
- var/animal_damage = rand(user.melee_damage_lower,user.melee_damage_upper)
- if(user.obj_damage)
- animal_damage = user.obj_damage
- animal_damage = min(animal_damage, 20*user.environment_smash)
- log_combat(user, src, "attacked")
- attack_generic(user, animal_damage, user.melee_damage_type, MELEE, play_soundeffect)
- return 1
+ return FALSE
+ var/play_soundeffect = 1
+ if(user.environment_smash)
+ play_soundeffect = 0
+ playsound(src, 'sound/effects/bang.ogg', 50, TRUE)
+
+ var/animal_damage = rand(user.melee_damage_lower,user.melee_damage_upper)
+ if(user.obj_damage)
+ animal_damage = user.obj_damage
+ animal_damage = min(animal_damage, 20*user.environment_smash)
+ log_combat(user, src, "attacked")
+ return attack_generic(user, animal_damage, user.melee_damage_type, MELEE, play_soundeffect)
/obj/vehicle/sealed/mecha/hulk_damage()
return 15
@@ -420,16 +419,18 @@
if(servo)
stock_parts += servo
- if(length(stock_parts))
- var/obj/item/stock_parts/part_to_remove = tgui_input_list(user, "Which part to remove?", "Part Removal", stock_parts)
- if(!(locate(part_to_remove) in contents))
- return
- user.put_in_hands(part_to_remove)
- locate_parts()
- diag_hud_set_mechcell()
- tool.play_tool_sound(src)
+ if(!length(stock_parts))
+ balloon_alert(user, "no parts!")
return
- balloon_alert(user, "no parts!")
+
+ var/obj/item/stock_parts/part_to_remove = tgui_input_list(user, "Which part to remove?", "Part Removal", stock_parts)
+ if(!(locate(part_to_remove) in contents))
+ return
+
+ user.put_in_hands(part_to_remove)
+ locate_parts()
+ diag_hud_set_mechcell()
+ tool.play_tool_sound(src)
/obj/vehicle/sealed/mecha/welder_act(mob/living/user, obj/item/W)
if(user.combat_mode)
@@ -445,7 +446,7 @@
return
user.balloon_alert_to_viewers("started welding [src]", "started repairing [src]")
audible_message(span_hear("You hear welding."))
- var/did_the_thing
+ var/did_the_thing = FALSE
while(atom_integrity < max_integrity)
if(W.use_tool(src, user, 2.5 SECONDS, volume=50))
did_the_thing = TRUE
@@ -453,12 +454,12 @@
audible_message(span_hear("You hear welding."))
else
break
+
if(did_the_thing)
user.balloon_alert_to_viewers("[(atom_integrity >= max_integrity) ? "fully" : "partially"] repaired [src]")
else
user.balloon_alert_to_viewers("stopped welding [src]", "interrupted the repair!")
-
/obj/vehicle/sealed/mecha/proc/full_repair(charge_cell)
repair_damage(max_integrity)
if(cell && charge_cell)
@@ -489,62 +490,66 @@
visual_effect_icon = ATTACK_EFFECT_MECHFIRE
else if(damtype == TOX)
visual_effect_icon = ATTACK_EFFECT_MECHTOXIN
- ..()
+ return ..()
-
-/obj/vehicle/sealed/mecha/proc/ammo_resupply(obj/item/mecha_ammo/A, mob/user,fail_chat_override = FALSE)
- if(!A.rounds)
+/obj/vehicle/sealed/mecha/proc/ammo_resupply(obj/item/mecha_ammo/ammo, mob/user,fail_chat_override = FALSE)
+ if(!ammo.rounds)
if(!fail_chat_override)
balloon_alert(user, "the box is empty!")
return FALSE
+
var/ammo_needed
var/found_gun
+
for(var/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/gun in flat_equipment)
ammo_needed = 0
- if(gun.ammo_type != A.ammo_type)
+ if(gun.ammo_type != ammo.ammo_type)
continue
found_gun = TRUE
- if(A.direct_load)
+ if(ammo.direct_load)
ammo_needed = initial(gun.projectiles) - gun.projectiles
else
ammo_needed = gun.projectiles_cache_max - gun.projectiles_cache
if(!ammo_needed)
continue
- if(ammo_needed < A.rounds)
- if(A.direct_load)
+
+ if(ammo_needed < ammo.rounds)
+ if(ammo.direct_load)
gun.projectiles = gun.projectiles + ammo_needed
else
gun.projectiles_cache = gun.projectiles_cache + ammo_needed
- playsound(get_turf(user),A.load_audio,50,TRUE)
- to_chat(user, span_notice("You add [ammo_needed] [A.ammo_type][ammo_needed > 1?"s":""] to \the [gun]"))
- A.rounds = A.rounds - ammo_needed
- if(A.custom_materials) //Change material content of the ammo box according to the amount of ammo deposited into the weapon
+ playsound(get_turf(user), ammo.load_audio, 50, TRUE)
+ to_chat(user, span_notice("You add [ammo_needed] [ammo.ammo_type][ammo_needed > 1 ? "s" : ""] to \the [gun]"))
+ ammo.rounds = ammo.rounds - ammo_needed
+ if(ammo.custom_materials) //Change material content of the ammo box according to the amount of ammo deposited into the weapon
/// list of materials contained in the ammo box after we put it through the equation so we can stick this list into set_custom_materials()
var/list/new_material_content = list()
- for(var/datum/material/current_material in A.custom_materials)
+ for(var/datum/material/current_material in ammo.custom_materials)
if(istype(current_material, /datum/material/iron)) //we can flatten an empty ammo box into a sheet of iron (2000 units) so we have to make sure the box always has this amount at minimum
- new_material_content[current_material] = (A.custom_materials[current_material] - SHEET_MATERIAL_AMOUNT) * (A.rounds / initial(A.rounds)) + SHEET_MATERIAL_AMOUNT
+ new_material_content[current_material] = (ammo.custom_materials[current_material] - SHEET_MATERIAL_AMOUNT) * (ammo.rounds / initial(ammo.rounds)) + SHEET_MATERIAL_AMOUNT
else
- new_material_content[current_material] = A.custom_materials[current_material] * (A.rounds / initial(A.rounds))
- A.set_custom_materials(new_material_content)
- A.update_name()
+ new_material_content[current_material] = ammo.custom_materials[current_material] * (ammo.rounds / initial(ammo.rounds))
+ ammo.set_custom_materials(new_material_content)
+ ammo.update_name()
return TRUE
- if(A.direct_load)
- gun.projectiles = gun.projectiles + A.rounds
+ if(ammo.direct_load)
+ gun.projectiles = gun.projectiles + ammo.rounds
else
- gun.projectiles_cache = gun.projectiles_cache + A.rounds
- playsound(get_turf(user),A.load_audio,50,TRUE)
- to_chat(user, span_notice("You add [A.rounds] [A.ammo_type][A.rounds > 1?"s":""] to \the [gun]"))
- if(A.qdel_on_empty)
- qdel(A)
+ gun.projectiles_cache = gun.projectiles_cache + ammo.rounds
+
+ playsound(get_turf(user),ammo.load_audio,50,TRUE)
+ to_chat(user, span_notice("You add [ammo.rounds] [ammo.ammo_type][ammo.rounds > 1 ? "s" : ""] to \the [gun]"))
+ if(ammo.qdel_on_empty)
+ qdel(ammo)
return TRUE
- A.rounds = 0
- A.set_custom_materials(list(/datum/material/iron=SHEET_MATERIAL_AMOUNT))
- A.update_appearance()
+ ammo.rounds = 0
+ ammo.set_custom_materials(list(/datum/material/iron=SHEET_MATERIAL_AMOUNT))
+ ammo.update_appearance()
return TRUE
+
if(!fail_chat_override)
if(found_gun)
balloon_alert(user, "ammo storage is full!")
diff --git a/code/modules/vehicles/mecha/mecha_mob_interaction.dm b/code/modules/vehicles/mecha/mecha_mob_interaction.dm
index 296352dc8c4..3b341120231 100644
--- a/code/modules/vehicles/mecha/mecha_mob_interaction.dm
+++ b/code/modules/vehicles/mecha/mecha_mob_interaction.dm
@@ -197,7 +197,6 @@
if(driver.client)
driver.update_mouse_pointer()
driver.client.view_size.resetToDefault()
- zoom_mode = FALSE
. = ..()
update_appearance()
diff --git a/code/modules/vehicles/mecha/mecha_movement.dm b/code/modules/vehicles/mecha/mecha_movement.dm
index 5477f5f2581..d0121a1b333 100644
--- a/code/modules/vehicles/mecha/mecha_movement.dm
+++ b/code/modules/vehicles/mecha/mecha_movement.dm
@@ -75,49 +75,24 @@
/obj/vehicle/sealed/mecha/vehicle_move(direction, forcerotate = FALSE)
if(!COOLDOWN_FINISHED(src, cooldown_vehicle_move))
return FALSE
+
COOLDOWN_START(src, cooldown_vehicle_move, movedelay)
+
if(completely_disabled)
return FALSE
+
if(!direction)
return FALSE
+
+ if(!can_move(direction))
+ return FALSE
+
if(ismovable(loc)) //Mech is inside an object, tell it we moved
var/atom/loc_atom = loc
return loc_atom.relaymove(src, direction)
- var/obj/machinery/portable_atmospherics/canister/internal_tank = get_internal_tank()
- if(internal_tank?.connected_port)
- if(TIMER_COOLDOWN_FINISHED(src, COOLDOWN_MECHA_MESSAGE))
- to_chat(occupants, "[icon2html(src, occupants)][span_warning("Unable to move while connected to the air system port!")]")
- TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_MESSAGE, 2 SECONDS)
- return FALSE
+
if(!Process_Spacemove(direction))
return FALSE
- if(zoom_mode)
- if(TIMER_COOLDOWN_FINISHED(src, COOLDOWN_MECHA_MESSAGE))
- to_chat(occupants, "[icon2html(src, occupants)][span_warning("Unable to move while in zoom mode!")]")
- TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_MESSAGE, 2 SECONDS)
- return FALSE
- var/list/missing_parts = list()
- if(isnull(cell))
- missing_parts += "power cell"
- if(isnull(capacitor))
- missing_parts += "capacitor"
- if(isnull(servo))
- missing_parts += "servo"
- if(length(missing_parts))
- if(TIMER_COOLDOWN_FINISHED(src, COOLDOWN_MECHA_MESSAGE))
- to_chat(occupants, "[icon2html(src, occupants)][span_warning("Missing [english_list(missing_parts)].")]")
- TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_MESSAGE, 2 SECONDS)
- return FALSE
- if((step_energy_drain != 0) && !use_energy(step_energy_drain))
- if(TIMER_COOLDOWN_FINISHED(src, COOLDOWN_MECHA_MESSAGE))
- to_chat(occupants, "[icon2html(src, occupants)][span_warning("Insufficient power to move!")]")
- TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_MESSAGE, 2 SECONDS)
- return FALSE
- if(lavaland_only && is_mining_level(z))
- if(TIMER_COOLDOWN_FINISHED(src, COOLDOWN_MECHA_MESSAGE))
- to_chat(occupants, "[icon2html(src, occupants)][span_warning("Invalid Environment.")]")
- TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_MESSAGE, 2 SECONDS)
- return FALSE
var/olddir = dir
@@ -148,20 +123,46 @@
//Otherwise just walk normally
. = try_step_multiz(direction)
- if(phasing)
- use_energy(phasing_energy_drain)
if(strafe)
setDir(olddir)
+/// Check if anything is blocking our movement
+/obj/vehicle/sealed/mecha/proc/can_move(direction)
+ var/obj/machinery/portable_atmospherics/canister/internal_tank = get_internal_tank()
+ if(internal_tank?.connected_port)
+ if(TIMER_COOLDOWN_FINISHED(src, COOLDOWN_MECHA_MESSAGE))
+ to_chat(occupants, "[icon2html(src, occupants)][span_warning("Unable to move while connected to the air system port!")]")
+ TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_MESSAGE, 2 SECONDS)
+ return FALSE
+
+ var/list/missing_parts = list()
+ if(isnull(cell))
+ missing_parts += "power cell"
+ if(isnull(capacitor))
+ missing_parts += "capacitor"
+ if(isnull(servo))
+ missing_parts += "servo"
+
+ if(length(missing_parts))
+ if(TIMER_COOLDOWN_FINISHED(src, COOLDOWN_MECHA_MESSAGE))
+ to_chat(occupants, "[icon2html(src, occupants)][span_warning("Missing [english_list(missing_parts)].")]")
+ TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_MESSAGE, 2 SECONDS)
+ return FALSE
+
+ if((step_energy_drain != 0) && !use_energy(step_energy_drain))
+ if(TIMER_COOLDOWN_FINISHED(src, COOLDOWN_MECHA_MESSAGE))
+ to_chat(occupants, "[icon2html(src, occupants)][span_warning("Insufficient power to move!")]")
+ TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_MESSAGE, 2 SECONDS)
+ return FALSE
+ return TRUE
+
/obj/vehicle/sealed/mecha/Bump(atom/obstacle)
. = ..()
- if(phasing) //Theres only one cause for phasing canpass fails
- to_chat(occupants, "[icon2html(src, occupants)][span_warning("A dull, universal force is preventing you from [phasing] here!")]")
- spark_system.start()
- return
if(.) //mech was thrown/door/whatever
return
+ try_bumpsmash(obstacle)
+/obj/vehicle/sealed/mecha/proc/try_bumpsmash(atom/obstacle)
// Whether or not we're on our mecha melee cooldown
var/on_cooldown = TIMER_COOLDOWN_RUNNING(src, COOLDOWN_MECHA_MELEE_ATTACK)
@@ -172,6 +173,7 @@
TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_MELEE_ATTACK, melee_cooldown * 0.3)
if(!obstacle || obstacle.CanPass(src, get_dir(obstacle, src) || dir)) // The else is in case the obstacle is in the same turf.
step(src,dir)
+
if(isobj(obstacle))
var/obj/obj_obstacle = obstacle
if(!obj_obstacle.anchored && obj_obstacle.move_resist <= move_force)
diff --git a/code/modules/vehicles/mecha/working/clarke.dm b/code/modules/vehicles/mecha/working/clarke.dm
index 98907b8a26e..5a484fbe07b 100644
--- a/code/modules/vehicles/mecha/working/clarke.dm
+++ b/code/modules/vehicles/mecha/working/clarke.dm
@@ -47,19 +47,13 @@
. = ..()
ore_box = new(src)
-/obj/vehicle/sealed/mecha/clarke/atom_destruction()
- if(ore_box)
- INVOKE_ASYNC(ore_box, TYPE_PROC_REF(/obj/structure/ore_box, dump_box_contents))
- return ..()
-
/obj/vehicle/sealed/mecha/clarke/generate_actions()
. = ..()
initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_search_ruins)
initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/clarke_scoop_body)
-//Ore Box Controls
-
-///Special equipment for the Clarke mech, handles moving ore without giving the mech a hydraulic clamp and cargo compartment.
+// Ore Box Controls
+/// Special equipment for the Clarke mech, handles moving ore without giving the mech a hydraulic clamp and cargo compartment.
/obj/item/mecha_parts/mecha_equipment/orebox_manager
name = "ore storage module"
desc = "An automated ore box management device, complete with a built-in boulder processor."