Files
GS13NG/code/modules/vehicles/mecha/_mecha.dm
DeltaFire 9c2da5bcf4 power pass
mechs actually use power when moving
mining z check isn't falsely inverted
actuator overload cost returned to pre accidental buff levels
correct value for step energy cost now on mechs
parts are now less wacky nor broken for energy cost
2022-05-23 02:16:45 +02:00

1228 lines
46 KiB
Plaintext

/***************** WELCOME TO MECHA.DM, ENJOY YOUR STAY *****************/
/**
* Mechs are now (finally) vehicles, this means you can make them multicrew
* They can also grant select ability buttons based on occupant bitflags
*
* Movement is handled through vehicle_move() which is called by relaymove
* Clicking is done by way of signals registering to the entering mob
* NOTE: MMIS are NOT mobs but instead contain a brain that is, so you need special checks
* AI also has special checks becaus it gets in and out of the mech differently
* Always call remove_occupant(mob) when leaving the mech so the mob is removed properly
*
* For multi-crew, you need to set how the occupants recieve ability bitflags corresponding to their status on the vehicle(i.e: driver, gunner etc)
* Abilities can then be set to only apply for certain bitflags and are assigned as such automatically
*
* Clicks are wither translated into mech_melee_attack (see mech_melee_attack.dm)
* Or are used to call action() on equipped gear
* Cooldown for gear is on the mech because exploits
*/
/obj/vehicle/sealed/mecha
name = "mecha"
desc = "Exosuit"
icon = 'icons/mecha/mecha.dmi'
resistance_flags = FIRE_PROOF | ACID_PROOF
flags_1 = HEAR_1
max_integrity = 300
armor = list(MELEE = 20, BULLET = 10, LASER = 0, ENERGY = 0, BOMB = 10, BIO = 0, RAD = 0, FIRE = 100, ACID = 100)
movedelay = 1 SECONDS
anchored = TRUE
emulate_door_bumps = TRUE
COOLDOWN_DECLARE(mecha_bump_smash)
var/light_on = FALSE
///What direction will the mech face when entered/powered on? Defaults to South.
var/dir_in = SOUTH
///How much energy the mech will consume each time it moves. This variable is a backup for when leg actuators affect the energy drain.
var/normal_step_energy_drain = 10
///How much energy the mech will consume each time it moves. this is the current active energy consumed
var/step_energy_drain = 10
///How much energy we drain each time we mechpunch someone
var/melee_energy_drain = 15
///The minimum amount of energy charge consumed by leg overload
var/overload_step_energy_drain_min = 100
///chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act.
var/deflect_chance = 10
///Modifiers for directional armor
var/list/facing_modifiers = list(MECHA_FRONT_ARMOUR = 1.5, MECHA_SIDE_ARMOUR = 1, MECHA_BACK_ARMOUR = 0.5)
///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/cell/cell
/// Keeps track of the mech's scanning module
var/obj/item/stock_parts/scanning_module/scanmod
/// Keeps track of the mech's capacitor
var/obj/item/stock_parts/capacitor/capacitor
///Whether the mechs maintenance protocols are on or off
var/construction_state = MECHA_LOCKED
///Contains flags for the mecha
var/mecha_flags = ADDING_ACCESS_POSSIBLE | CANSTRAFE | IS_ENCLOSED | HAS_LIGHTS
///Stores the DNA enzymes of a carbon so tht only they can access the mech
var/dna_lock
///Spark effects are handled by this datum
var/datum/effect_system/spark_spread/spark_system = new
///How powerful our lights are
var/lights_power = 6
///Just stop the mech from doing anything
var/completely_disabled = FALSE
///Whether this mech is allowed to move diagonally
var/allow_diagonal_movement = TRUE
///Whether or not the mech destroys walls by running into it.
var/bumpsmash = FALSE
///////////ATMOS
///Whether we are currrently drawing from the internal tank
var/use_internal_tank = FALSE
///The setting of the valve on the internal tank
var/internal_tank_valve = ONE_ATMOSPHERE
///The internal air tank obj of the mech
var/obj/machinery/portable_atmospherics/canister/internal_tank
///Internal air mix datum
var/datum/gas_mixture/cabin_air
///The connected air port, if we have one
var/obj/machinery/atmospherics/components/unary/portables_connector/connected_port
///Special version of the radio, which is unsellable
var/obj/item/radio/mech/radio
var/list/trackers = list()
var/max_temperature = 25000
///health percentage below which internal damage is possible
var/internal_damage_threshold = 50
///Bitflags for internal damage
var/internal_damage = NONE
///required access level for mecha operation
var/list/operation_req_access = list()
///required access to change internal components
var/list/internals_req_access = list(ACCESS_ENGINE, ACCESS_ROBOTICS)
///Typepath for the wreckage it spawns when destroyed
var/wreckage
var/list/equipment = new
///Current active equipment
var/obj/item/mecha_parts/mecha_equipment/selected
///Maximum amount of equipment we can have
var/max_equip = 3
///Whether our steps are silent, for example in zero-G
var/step_silent = FALSE
///Sound played when the mech moves
var/stepsound = 'sound/mecha/mechstep.ogg'
///Sound played when the mech walks
var/turnsound = 'sound/mecha/mechturn.ogg'
///Cooldown duration between melee punches
var/melee_cooldown = 10
///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
var/destruction_sleep_duration = 2 SECONDS
///Whether outside viewers can see the pilot inside
///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
var/is_currently_ejecting = FALSE
var/datum/effect_system/smoke_spread/smoke_system = new
////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
var/leg_overload_mode = FALSE
///Energy use modifier for leg overload
var/leg_overload_coeff = 100
//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
///Bool for if the mech is currently phasing
var/phasing = FALSE
///Power we use every time we phaze through something
var/phasing_energy_drain = 200
///icon_state for flick() when phazing
var/phase_state = ""
///Wether we are strafing
var/strafe = FALSE
///Cooldown length between bumpsmashes
var/smashcooldown = 3
///Bool for whether this mech can only be used on lavaland
var/lavaland_only = FALSE
hud_possible = list (DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_TRACK_HUD)
/obj/item/radio/mech //this has to go somewhere
/obj/vehicle/sealed/mecha/Initialize(mapload)
. = ..()
add_radio()
add_cabin()
if(enclosed)
add_airtank()
RegisterSignal(src, COMSIG_MOVABLE_PRE_MOVE , .proc/disconnect_air)
RegisterSignal(src, COMSIG_MOVABLE_MOVED, .proc/play_stepsound)
spark_system.set_up(2, 0, src)
spark_system.attach(src)
smoke_system.set_up(3, src)
smoke_system.attach(src)
add_cell()
add_scanmod()
add_capacitor()
START_PROCESSING(SSobj, src)
GLOB.poi_list |= src
log_message("[src.name] created.", LOG_MECHA)
GLOB.mechas_list += src //global mech list
prepare_huds()
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
diag_hud.add_to_hud(src)
diag_hud_set_mechhealth()
diag_hud_set_mechcell()
diag_hud_set_mechstat()
update_icon()
/obj/vehicle/sealed/mecha/Destroy()
if(obj_integrity > 0) //no explody if we have hp remaining!
explode_on_death = FALSE
for(var/M in occupants)
var/mob/living/occupant = M
if(isAI(occupant))
occupant.gib() //No wreck, no AI to recover
else
occupant.forceMove(loc)
occupant.SetSleeping(destruction_sleep_duration)
if(LAZYLEN(equipment))
for(var/E in equipment)
var/obj/item/mecha_parts/mecha_equipment/equip = E
equip.detach(loc)
qdel(equip)
if(cell)
QDEL_NULL(cell)
if(scanmod)
QDEL_NULL(scanmod)
if(capacitor)
QDEL_NULL(capacitor)
if(internal_tank)
QDEL_NULL(internal_tank)
STOP_PROCESSING(SSobj, src)
GLOB.poi_list.Remove(src)
LAZYCLEARLIST(equipment)
if(loc)
loc.assume_air(cabin_air)
air_update_turf()
else
qdel(cabin_air)
cabin_air = null
QDEL_NULL(spark_system)
QDEL_NULL(smoke_system)
GLOB.mechas_list -= src //global mech list
return ..()
/obj/vehicle/sealed/mecha/update_icon()
icon_state = get_mecha_occupancy_state()
return ..()
//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)
if(driver_amount() < max_drivers)
add_control_flags(M, FULL_MECHA_CONTROL)
/obj/vehicle/sealed/mecha/proc/get_mecha_occupancy_state()
if((mecha_flags & SILICON_PILOT) && silicon_icon_state)
return silicon_icon_state
if(LAZYLEN(occupants))
return initial(icon_state)
return "[initial(icon_state)]-open"
/obj/vehicle/sealed/mecha/get_cell()
return cell
/obj/vehicle/sealed/mecha/rust_heretic_act()
take_damage(500, BRUTE)
/obj/vehicle/sealed/mecha/proc/restore_equipment()
equipment_disabled = FALSE
for(var/occupant in occupants)
var/mob/mob_occupant
SEND_SOUND(mob_occupant, sound('sound/items/timer.ogg', volume=50))
to_chat(mob_occupant, "<span=notice>Equipment control unit has been rebooted successfully.</span>")
mob_occupant.update_mouse_pointer()
/obj/vehicle/sealed/mecha/CheckParts(list/parts_list)
..()
cell = locate(/obj/item/stock_parts/cell) in contents
scanmod = locate(/obj/item/stock_parts/scanning_module) in contents
capacitor = locate(/obj/item/stock_parts/capacitor) in contents
update_part_values()
/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.
if(scanmod)
normal_step_energy_drain = initial(normal_step_energy_drain) * (1.5 / (scanmod.rating - 0.5)) //movement power cost is 3x of default at T1, 1x at T2, 0.6x at T3 and 0.4x at T4
step_energy_drain = normal_step_energy_drain
else
normal_step_energy_drain = 500
step_energy_drain = normal_step_energy_drain
if(capacitor)
armor = armor.modifyRating(energy = (capacitor.rating * 5)) //Each level of capacitor protects the mech against emp by 5%
else //because we can still be hit without a cap, even if we can't move
armor = armor.setRating(energy = 0)
////////////////////////
////// Helpers /////////
////////////////////////
/obj/vehicle/sealed/mecha/proc/add_airtank()
internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src)
return internal_tank
///Adds a cell, for use in Map-spawned mechs, Nuke Ops mechs, and admin-spawned mechs. Mechs built by hand will replace this.
/obj/vehicle/sealed/mecha/proc/add_cell(obj/item/stock_parts/cell/C=null)
QDEL_NULL(cell)
if(C)
C.forceMove(src)
cell = C
return
cell = new /obj/item/stock_parts/cell/high/plus(src)
///Adds a scanning module, for use in Map-spawned mechs, Nuke Ops mechs, and admin-spawned mechs. Mechs built by hand will replace this.
/obj/vehicle/sealed/mecha/proc/add_scanmod(obj/item/stock_parts/scanning_module/sm=null)
QDEL_NULL(scanmod)
if(sm)
sm.forceMove(src)
scanmod = sm
return
scanmod = new /obj/item/stock_parts/scanning_module(src)
///Adds a capacitor, for use in Map-spawned mechs, Nuke Ops mechs, and admin-spawned mechs. Mechs built by hand will replace this.
/obj/vehicle/sealed/mecha/proc/add_capacitor(obj/item/stock_parts/capacitor/cap=null)
QDEL_NULL(capacitor)
if(cap)
cap.forceMove(src)
capacitor = cap
else
capacitor = new /obj/item/stock_parts/capacitor(src)
/obj/vehicle/sealed/mecha/proc/add_cabin()
cabin_air = new(200)
cabin_air.set_temperature(T20C)
cabin_air.set_moles(GAS_O2,O2STANDARD*cabin_air.return_volume()/(R_IDEAL_GAS_EQUATION*cabin_air.return_temperature()))
cabin_air.set_moles(GAS_N2,N2STANDARD*cabin_air.return_volume()/(R_IDEAL_GAS_EQUATION*cabin_air.return_temperature()))
return cabin_air
/obj/vehicle/sealed/mecha/proc/add_radio()
radio = new(src)
radio.name = "[src] radio"
radio.icon = icon
radio.icon_state = icon_state
radio.subspace_transmission = TRUE
/obj/vehicle/sealed/mecha/proc/can_use(mob/user)
if(istype(user) && is_occupant(user))
if(!user.incapacitated())
return TRUE
return FALSE
////////////////////////////////////////////////////////////////////////////////
/obj/vehicle/sealed/mecha/examine(mob/user)
. = ..()
var/integrity = obj_integrity*100/max_integrity
switch(integrity)
if(85 to 100)
. += "It's fully intact."
if(65 to 85)
. += "It's slightly damaged."
if(45 to 65)
. += "It's badly damaged."
if(25 to 45)
. += "It's heavily damaged."
else
. += "It's falling apart."
var/hide_weapon = locate(/obj/item/mecha_parts/concealed_weapon_bay) in contents
var/hidden_weapon = hide_weapon ? (locate(/obj/item/mecha_parts/mecha_equipment/weapon) in equipment) : null
var/list/visible_equipment = equipment - hidden_weapon
if(visible_equipment.len)
. += "It's equipped with:"
for(var/obj/item/mecha_parts/mecha_equipment/ME in visible_equipment)
. += "[icon2html(ME, user)] \A [ME]."
if(!enclosed)
if(mecha_flags & SILICON_PILOT)
. += "[src] appears to be piloting itself..."
else
for(var/occupante in occupants)
. += "You can see [occupante] inside."
if(ishuman(user))
var/mob/living/carbon/human/H = user
for(var/O in H.held_items)
if(istype(O, /obj/item/gun))
. += "<span class='warning'>It looks like you can hit the pilot directly if you target the center or above.</span>"
break //in case user is holding two guns
//processing internal damage, temperature, air regulation, alert updates, lights power use.
/obj/vehicle/sealed/mecha/process()
var/internal_temp_regulation = 1
if(internal_damage)
if(internal_damage & MECHA_INT_FIRE)
if(!(internal_damage & MECHA_INT_TEMP_CONTROL) && prob(5))
clearInternalDamage(MECHA_INT_FIRE)
if(internal_tank)
var/datum/gas_mixture/int_tank_air = internal_tank.return_air()
if(int_tank_air.return_pressure() > internal_tank.maximum_pressure && !(internal_damage & MECHA_INT_TANK_BREACH))
setInternalDamage(MECHA_INT_TANK_BREACH)
if(int_tank_air && int_tank_air.return_volume() > 0) //heat the air_contents
int_tank_air.set_temperature(min(6000+T0C, int_tank_air.return_temperature()+rand(10,15)))
if(cabin_air && cabin_air.return_volume()>0)
cabin_air.set_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), BURN, 0, 0)
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)
assume_air_ratio(internal_tank.return_air(), 0.1)
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.return_temperature() - T20C
cabin_air.set_temperature(cabin_air.return_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)
tank_air.transfer_to(cabin_air,transfer_moles)
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)
cabin_air.transfer_to(t_air, transfer_moles)
if(occupants)
for(var/i in occupants)
var/mob/living/occupant = i
if(cell)
var/cellcharge = cell.charge/cell.maxcharge
switch(cellcharge)
if(0.75 to INFINITY)
occupant.clear_alert("charge")
if(0.5 to 0.75)
occupant.throw_alert("charge", /atom/movable/screen/alert/lowcell, 1)
if(0.25 to 0.5)
occupant.throw_alert("charge", /atom/movable/screen/alert/lowcell, 2)
if(0.01 to 0.25)
occupant.throw_alert("charge", /atom/movable/screen/alert/lowcell, 3)
else
occupant.throw_alert("charge", /atom/movable/screen/alert/emptycell)
var/integrity = obj_integrity/max_integrity*100
switch(integrity)
if(30 to 45)
occupant.throw_alert("mech damage", /atom/movable/screen/alert/low_mech_integrity, 1)
if(15 to 35)
occupant.throw_alert("mech damage", /atom/movable/screen/alert/low_mech_integrity, 2)
if(-INFINITY to 15)
occupant.throw_alert("mech damage", /atom/movable/screen/alert/low_mech_integrity, 3)
else
occupant.clear_alert("mech damage")
var/atom/checking = occupant.loc
// recursive check to handle all cases regarding very nested occupants,
// such as brainmob inside brainitem inside MMI inside mecha
while(!isnull(checking))
if(isturf(checking))
// hit a turf before hitting the mecha, seems like they have been moved out
occupant.clear_alert("charge")
occupant.clear_alert("mech damage")
occupant = null
break
else if (checking == src)
break // all good
checking = checking.loc
if(mecha_flags & LIGHTS_ON)
var/lights_energy_drain = 2
use_power(lights_energy_drain)
for(var/b in occupants)
var/mob/living/occupant = b
if(!enclosed && occupant?.incapacitated()) //no sides mean it's easy to just sorta fall out if you're incapacitated.
visible_message("<span class='warning'>[occupant] tumbles out of the cockpit!</span>")
mob_try_exit(occupant, TRUE, TRUE) //bye bye
//Diagnostic HUD updates
diag_hud_set_mechhealth()
diag_hud_set_mechcell()
diag_hud_set_mechstat()
/obj/vehicle/sealed/mecha/fire_act() //Check if we should ignite the pilot of an open-canopy mech
. = ..()
if(LAZYLEN(occupants) && !enclosed && !(mecha_flags & SILICON_PILOT))
for(var/M in occupants)
var/mob/living/cookedalive = M
if(cookedalive.fire_stacks < 5)
cookedalive.fire_stacks += 1
cookedalive.IgniteMob()
/obj/vehicle/sealed/mecha/proc/display_speech_bubble(datum/source, list/speech_args)
SIGNAL_HANDLER
var/list/speech_bubble_recipients = get_hearers_in_view(7,src)
for(var/mob/M in speech_bubble_recipients)
if(M.client)
speech_bubble_recipients.Add(M.client)
INVOKE_ASYNC(GLOBAL_PROC, /proc/flick_overlay, image('icons/mob/talk.dmi', src, "machine[say_test(speech_args[SPEECH_MESSAGE])]",MOB_LAYER+1), speech_bubble_recipients, 30)
////////////////////////////
///// Action processing ////
////////////////////////////
/obj/vehicle/sealed/mecha/proc/on_mouseclick(mob/user, atom/target, params)
SIGNAL_HANDLER
if(!locate(/turf) in list(target,target.loc)) // Prevents inventory from being drilled
return
if(completely_disabled || is_currently_ejecting || (mecha_flags & CANNOT_INTERACT))
return
var/list/mouse_control = params2list(params)
if(isAI(user) && !mouse_control["middle"])//AIs use MMB
return
if(phasing)
to_chat(occupants, "[icon2html(src, occupants)]<span class='warning'>Unable to interact with objects while phasing.</span>")
return
if(user.incapacitated())
return
if(construction_state)
to_chat(occupants, "[icon2html(src, occupants)]<span class='warning'>Maintenance protocols in effect.</span>")
return
if(!get_charge())
return
if(src == target)
return
var/dir_to_target = get_dir(src,target)
if(dir_to_target && !(dir_to_target & dir))//wrong direction
return
if(internal_damage & MECHA_INT_CONTROL_LOST)
target = pick(view(3,target))
if(!target)
return
var/mob/living/L = user
if(selected)
if(!(L in return_controllers_with_flag(VEHICLE_CONTROL_EQUIPMENT)))
to_chat(user, "You can't control mech equipment from here!")
return
if(!Adjacent(target) && (selected.range & MECHA_RANGED))
if(HAS_TRAIT(L, TRAIT_PACIFISM) && selected.harmful)
to_chat(L, "<span class='warning'>You don't want to harm other living beings!</span>")
return
if(SEND_SIGNAL(src, COMSIG_MECHA_EQUIPMENT_CLICK, L, target) & COMPONENT_CANCEL_EQUIPMENT_CLICK)
return
INVOKE_ASYNC(selected, /obj/item/mecha_parts/mecha_equipment.proc/action, user, target, params)
return
if((selected.range & MECHA_MELEE) && Adjacent(target))
if(isliving(target) && selected.harmful && HAS_TRAIT(L, TRAIT_PACIFISM))
to_chat(L, "<span class='warning'>You don't want to harm other living beings!</span>")
return
if(SEND_SIGNAL(src, COMSIG_MECHA_EQUIPMENT_CLICK, L, target) & COMPONENT_CANCEL_EQUIPMENT_CLICK)
return
INVOKE_ASYNC(selected, /obj/item/mecha_parts/mecha_equipment.proc/action, user, target, params)
return
if(!(L in return_controllers_with_flag(VEHICLE_CONTROL_MELEE)))
to_chat(L, "<span class='warning'>You're in the wrong seat to interact with your hands.</span>")
return
var/on_cooldown = TIMER_COOLDOWN_CHECK(src, COOLDOWN_MECHA_MELEE_ATTACK)
var/adjacent = Adjacent(target)
if(SEND_SIGNAL(src, COMSIG_MECHA_MELEE_CLICK, L, target, on_cooldown, adjacent) & COMPONENT_CANCEL_MELEE_CLICK)
return
if(on_cooldown || !adjacent)
return
if(internal_damage & MECHA_INT_CONTROL_LOST)
var/list/possible_targets = oview(1,src)
if(!length(possible_targets))
return
target = pick(possible_targets)
target.mech_melee_attack(src, user)
TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_MELEE_ATTACK, melee_cooldown)
//////////////////////////////////
//////// Movement procs ////////
//////////////////////////////////
///Plays the mech step sound effect. Split from movement procs so that other mechs (HONK) can override this one specific part.
/obj/vehicle/sealed/mecha/proc/play_stepsound()
SIGNAL_HANDLER
if(stepsound)
playsound(src,stepsound,40,1)
/obj/vehicle/sealed/mecha/proc/disconnect_air()
SIGNAL_HANDLER
if(internal_tank.disconnect()) // Something moved us and broke connection
to_chat(occupants, "[icon2html(src, occupants)]<span class='warning'>Air port connection has been severed!</span>")
log_message("Lost connection to gas port.", LOG_MECHA)
/obj/vehicle/sealed/mecha/Process_Spacemove(movement_dir = 0)
. = ..()
if(.)
return TRUE
var/atom/movable/backup = get_spacemove_backup()
if(backup)
if(istype(backup) && movement_dir && !backup.anchored)
if(backup.newtonian_move(turn(movement_dir, 180)))
step_silent = TRUE
if(return_drivers())
to_chat(occupants, "[icon2html(src, occupants)]<span class='info'>The [src] push off [backup] to propel yourself.</span>")
return TRUE
if(movedelay <= world.time && active_thrusters && movement_dir && active_thrusters.thrust(movement_dir))
step_silent = TRUE
return TRUE
return FALSE
/obj/vehicle/sealed/mecha/relaymove(mob/living/user, direction)
. = TRUE
if(!canmove || !(user in return_drivers()))
return
vehicle_move(direction)
/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(internal_tank?.connected_port)
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_MECHA_MESSAGE))
to_chat(occupants, "[icon2html(src, occupants)]<span class='warning'>Unable to move while connected to the air system port!</span>")
TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_MESSAGE, 2 SECONDS)
return FALSE
if(construction_state)
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_MECHA_MESSAGE))
to_chat(occupants, "[icon2html(src, occupants)]<span class='danger'>Maintenance protocols in effect.</span>")
TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_MESSAGE, 2 SECONDS)
return FALSE
if(!Process_Spacemove(direction))
return FALSE
if(!has_charge(step_energy_drain))
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_MECHA_MESSAGE))
to_chat(occupants, "[icon2html(src, occupants)]<span class='warning'>Insufficient power to move!</span>")
TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_MESSAGE, 2 SECONDS)
return FALSE
if(zoom_mode)
to_chat(occupants, "[icon2html(src, occupants)]<span class='warning'>Unable to move while in zoom mode!</span>")
return FALSE
if(!cell)
to_chat(occupants, "[icon2html(src, occupants)]<span class='warning'>Missing power cell.</span>")
return FALSE
if(!scanmod || !capacitor)
to_chat(occupants, "[icon2html(src, occupants)]<span class='warning'>Missing [scanmod? "capacitor" : "scanning module"].</span>")
return FALSE
if(lavaland_only && !is_mining_level(z))
to_chat(occupants, "[icon2html(src, occupants)]<span class='warning'>Invalid Environment.</span>")
return FALSE
var/olddir = dir
if(internal_damage & MECHA_INT_CONTROL_LOST)
direction = pick(GLOB.alldirs)
//only mechs with diagonal movement may move diagonally
if(!allow_diagonal_movement && ISDIAGONALDIR(direction))
return TRUE
//if we're not facing the way we're going rotate us
var/no_strafe = FALSE
if(dir != direction || forcerotate)
if(strafe)
for(var/D in return_drivers())
var/mob/driver = D
if(driver.client?.keys_held["Alt"])
no_strafe = TRUE
setDir(direction)
if(turnsound)
playsound(src,turnsound,40,TRUE)
return TRUE
else
setDir(direction)
if(turnsound)
playsound(src,turnsound,40,TRUE)
return TRUE
set_glide_size(DELAY_TO_GLIDE_SIZE(movedelay))
use_power(step_energy_drain)
//Otherwise just walk normally
. = step(src,direction, dir)
if(strafe && !no_strafe)
setDir(olddir)
/obj/vehicle/sealed/mecha/Bump(atom/obstacle)
if(phasing && get_charge() >= phasing_energy_drain && !throwing)
if(phase_state)
flick(phase_state, src)
forceMove(get_step(src,dir))//This is jank I hate it thanks, this should be done thrugh move not this dumb shit
use_power(phasing_energy_drain)
addtimer(VARSET_CALLBACK(src, movedelay, TRUE), movedelay*3)
return
. = ..()
if(.) //mech was thrown/door/whatever
return
if(bumpsmash) //Need a pilot to push the PUNCH button.
if(COOLDOWN_FINISHED(src, mecha_bump_smash))
obstacle.mech_melee_attack(src)
COOLDOWN_START(src, mecha_bump_smash, smashcooldown)
if(!obstacle || obstacle.CanPass(src,get_step(src,dir)))
step(src,dir)
if(isobj(obstacle))
var/obj/obj_obstacle = obstacle
if(!obj_obstacle.anchored && obj_obstacle.move_resist <= move_force)
step(obstacle, dir)
else if(ismob(obstacle))
var/mob/mob_obstacle = obstacle
if(mob_obstacle.move_resist <= move_force)
step(obstacle, dir)
///////////////////////////////////
//////// Internal damage ////////
///////////////////////////////////
/obj/vehicle/sealed/mecha/proc/check_for_internal_damage(list/possible_int_damage,ignore_threshold=null)
if(!islist(possible_int_damage) || !length(possible_int_damage))
return
if(prob(20))
if(ignore_threshold || obj_integrity*100/max_integrity < internal_damage_threshold)
for(var/T in possible_int_damage)
if(internal_damage & T)
possible_int_damage -= T
if (length(possible_int_damage))
var/int_dam_flag = pick(possible_int_damage)
if(int_dam_flag)
setInternalDamage(int_dam_flag)
if(prob(5))
if(ignore_threshold || obj_integrity*100/max_integrity < internal_damage_threshold)
if(LAZYLEN(equipment))
var/obj/item/mecha_parts/mecha_equipment/ME = pick(equipment)
qdel(ME)
/obj/vehicle/sealed/mecha/proc/setInternalDamage(int_dam_flag)
internal_damage |= int_dam_flag
log_message("Internal damage of type [int_dam_flag].", LOG_MECHA)
SEND_SOUND(occupants, sound('sound/machines/warning-buzzer.ogg',wait=0))
diag_hud_set_mechstat()
/obj/vehicle/sealed/mecha/proc/clearInternalDamage(int_dam_flag)
if(internal_damage & int_dam_flag)
switch(int_dam_flag)
if(MECHA_INT_TEMP_CONTROL)
to_chat(occupants, "[icon2html(src, occupants)]<span class='boldnotice'>Life support system reactivated.</span>")
if(MECHA_INT_FIRE)
to_chat(occupants, "[icon2html(src, occupants)]<span class='boldnotice'>Internal fire extinguished.</span>")
if(MECHA_INT_TANK_BREACH)
to_chat(occupants, "[icon2html(src, occupants)]<span class='boldnotice'>Damaged internal tank has been sealed.</span>")
internal_damage &= ~int_dam_flag
diag_hud_set_mechstat()
/////////////////////////////////////
//////////// AI piloting ////////////
/////////////////////////////////////
/obj/vehicle/sealed/mecha/attack_ai(mob/living/silicon/ai/user)
if(!isAI(user))
return
//Allows the Malf to scan a mech's status and loadout, helping it to decide if it is a worthy chariot.
if(user.can_dominate_mechs)
examine(user) //Get diagnostic information!
for(var/obj/item/mecha_parts/mecha_tracking/B in trackers)
to_chat(user, "<span class='danger'>Warning: Tracking Beacon detected. Enter at your own risk. Beacon Data:</span>")
to_chat(user, "[B.get_mecha_info()]")
break
//Nothing like a big, red link to make the player feel powerful!
to_chat(user, "<a href='?src=[REF(user)];ai_take_control=[REF(src)]'><span class='userdanger'>ASSUME DIRECT CONTROL?</span></a><br>")
else
examine(user)
if(length(return_drivers()) > 0)
to_chat(user, "<span class='warning'>This exosuit has a pilot and cannot be controlled.</span>")
return
var/can_control_mech = 0
for(var/obj/item/mecha_parts/mecha_tracking/ai_control/A in trackers)
can_control_mech = 1
to_chat(user, "<span class='notice'>[icon2html(src, user)] Status of [name]:</span>\n[A.get_mecha_info()]")
break
if(!can_control_mech)
to_chat(user, "<span class='warning'>You cannot control exosuits without AI control beacons installed.</span>")
return
to_chat(user, "<a href='?src=[REF(user)];ai_take_control=[REF(src)]'><span class='boldnotice'>Take control of exosuit?</span></a><br>")
/obj/vehicle/sealed/mecha/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card)
if(!..())
return
//Transfer from core or card to mech. Proc is called by mech.
switch(interaction)
if(AI_TRANS_TO_CARD) //Upload AI from mech to AI card.
if(!construction_state) //Mech must be in maint mode to allow carding.
to_chat(user, "<span class='warning'>[name] must have maintenance protocols active in order to allow a transfer.</span>")
return
if(!locate(AI) in occupants) //Mech does not have an AI for a pilot
to_chat(user, "<span class='warning'>No AI detected in the [name] onboard computer.</span>")
return
for(var/mob/living/silicon/ai in occupants)
AI.ai_restore_power()//So the AI initially has power.
AI.control_disabled = TRUE
AI.radio_enabled = FALSE
AI.disconnect_shell()
remove_occupant(AI)
mecha_flags &= ~SILICON_PILOT
AI.forceMove(card)
card.AI = AI
AI.controlled_mech = null
AI.remote_control = null
to_chat(AI, "<span class='notice'>You have been downloaded to a mobile storage device. Wireless connection offline.</span>")
to_chat(user, "<span class='boldnotice'>Transfer successful</span>: [AI.name] ([rand(1000,9999)].exe) removed from [name] and stored within local memory.")
if(AI_MECH_HACK) //Called by AIs on the mech
AI.linked_core = new /obj/structure/AIcore/deactivated(AI.loc)
if(AI.can_dominate_mechs)
if(LAZYLEN(occupants)) //Oh, I am sorry, were you using that?
to_chat(AI, "<span class='warning'>Occupants detected! Forced ejection initiated!</span>")
to_chat(occupants, "<span class='danger'>You have been forcibly ejected!</span>")
ejectall() //IT IS MINE, NOW. SUCK IT, RD!
ai_enter_mech(AI, interaction)
if(AI_TRANS_FROM_CARD) //Using an AI card to upload to a mech.
AI = card.AI
if(!AI)
to_chat(user, "<span class='warning'>There is no AI currently installed on this device.</span>")
return
if(AI.deployed_shell) //Recall AI if shelled so it can be checked for a client
AI.disconnect_shell()
if(AI.stat || !AI.client)
to_chat(user, "<span class='warning'>[AI.name] is currently unresponsive, and cannot be uploaded.</span>")
return
if((LAZYLEN(occupants) >= max_occupants) || dna_lock) //Normal AIs cannot steal mechs!
to_chat(user, "<span class='warning'>Access denied. [name] is [LAZYLEN(occupants) >= max_occupants ? "currently fully occupied" : "secured with a DNA lock"].</span>")
return
AI.control_disabled = FALSE
AI.radio_enabled = TRUE
to_chat(user, "<span class='boldnotice'>Transfer successful</span>: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.")
card.AI = null
ai_enter_mech(AI, interaction)
//Hack and From Card interactions share some code, so leave that here for both to use.
/obj/vehicle/sealed/mecha/proc/ai_enter_mech(mob/living/silicon/ai/AI, interaction)
AI.ai_restore_power()
mecha_flags |= SILICON_PILOT
moved_inside(AI)
AI.cancel_camera()
AI.controlled_mech = src
AI.remote_control = src
AI.mobility_flags = ALL //Much easier than adding AI checks! Be sure to set this back to 0 if you decide to allow an AI to leave a mech somehow.
if(interaction == AI_MECH_HACK)
AI.can_shunt = FALSE //ONE AI ENTERS. NO AI LEAVES.
to_chat(AI, AI.can_dominate_mechs ? "<span class='announce'>Takeover of [name] complete! You are now loaded onto the onboard computer. Do not attempt to leave the station sector!</span>" :\
"<span class='notice'>You have been uploaded to a mech's onboard computer.</span>")
to_chat(AI, "<span class='reallybig boldnotice'>Use Middle-Mouse to activate mech functions and equipment. Click normally for AI interactions.</span>")
///Handles an actual AI (simple_animal mecha pilot) entering the mech
/obj/vehicle/sealed/mecha/proc/aimob_enter_mech(mob/living/simple_animal/hostile/syndicate/mecha_pilot/pilot_mob)
if(pilot_mob && pilot_mob.Adjacent(src))
if(LAZYLEN(occupants))
return
LAZYADD(occupants, src)
pilot_mob.mecha = src
pilot_mob.forceMove(src)
update_icon()
///Handles an actual AI (simple_animal mecha pilot) exiting the mech
/obj/vehicle/sealed/mecha/proc/aimob_exit_mech(mob/living/simple_animal/hostile/syndicate/mecha_pilot/pilot_mob)
LAZYREMOVE(occupants, pilot_mob)
if(pilot_mob.mecha == src)
pilot_mob.mecha = null
pilot_mob.forceMove(get_turf(src))
update_icon()
/////////////////////////////////////
//////// Atmospheric stuff ////////
/////////////////////////////////////
/obj/vehicle/sealed/mecha/remove_air(amount)
if(use_internal_tank)
return cabin_air.remove(amount)
return ..()
/obj/vehicle/sealed/mecha/remove_air_ratio(ratio)
if(use_internal_tank)
return cabin_air.remove_ratio(ratio)
return ..()
/obj/vehicle/sealed/mecha/return_air()
if(use_internal_tank)
return cabin_air
return ..()
/obj/vehicle/sealed/mecha/proc/return_pressure()
var/datum/gas_mixture/t_air = return_air()
if(t_air)
. = t_air.return_pressure()
return
/obj/vehicle/sealed/mecha/return_temperature()
var/datum/gas_mixture/t_air = return_air()
if(t_air)
. = t_air.return_temperature()
return
/obj/vehicle/sealed/mecha/mob_try_enter(mob/M)
if(!ishuman(M)) // no silicons or drones in mechas.
return
log_message("[M] tries to move into [src].", LOG_MECHA)
if(dna_lock && M.has_dna())
var/mob/living/carbon/entering_carbon = M
if(entering_carbon.dna.unique_enzymes != dna_lock)
to_chat(M, "<span class='warning'>Access denied. [name] is secured with a DNA lock.</span>")
log_message("Permission denied (DNA LOCK).", LOG_MECHA)
return
if(!operation_allowed(M))
to_chat(M, "<span class='warning'>Access denied. Insufficient operation keycodes.</span>")
log_message("Permission denied (No keycode).", LOG_MECHA)
return
if(M.buckled)
to_chat(M, "<span class='warning'>You are currently buckled and cannot move.</span>")
log_message("Permission denied (Buckled).", LOG_MECHA)
return
if(M.has_buckled_mobs()) //mob attached to us
to_chat(M, "<span class='warning'>You can't enter the exosuit with other creatures attached to you!</span>")
log_message("Permission denied (Attached mobs).", LOG_MECHA)
return
visible_message("<span class='notice'>[M] starts to climb into [name].</span>")
if(do_after(M, enter_delay, target = src))
if(obj_integrity <= 0)
to_chat(M, "<span class='warning'>You cannot get in the [name], it has been destroyed!</span>")
else if(LAZYLEN(occupants) >= max_occupants)
to_chat(M, "<span class='danger'>[occupants[occupants.len]] was faster! Try better next time, loser.</span>")//get the last one that hopped in
else if(M.buckled)
to_chat(M, "<span class='warning'>You can't enter the exosuit while buckled.</span>")
else if(M.has_buckled_mobs())
to_chat(M, "<span class='warning'>You can't enter the exosuit with other creatures attached to you!</span>")
else
moved_inside(M)
return ..()
else
to_chat(M, "<span class='warning'>You stop entering the exosuit!</span>")
/obj/vehicle/sealed/mecha/generate_actions()
initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_eject) // I don't see a single problem in generating exit vehicle action.
initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/mech_toggle_internals, VEHICLE_CONTROL_SETTINGS)
initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/mech_cycle_equip, VEHICLE_CONTROL_EQUIPMENT)
initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/mech_toggle_lights, VEHICLE_CONTROL_SETTINGS)
initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/mech_view_stats, VEHICLE_CONTROL_SETTINGS)
initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/strafe, VEHICLE_CONTROL_DRIVE)
if(max_occupants > 1)
initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/swap_seat)
/obj/vehicle/sealed/mecha/proc/moved_inside(mob/living/H)
. = FALSE
if(!(H?.client))
return
if(ishuman(H) && !Adjacent(H))
return
H.forceMove(src)
add_occupant(H)
add_fingerprint(H)
log_message("[H] moved in as pilot.", LOG_MECHA)
setDir(dir_in)
playsound(src, 'sound/machines/windowdoor.ogg', 50, TRUE)
if(!internal_damage)
SEND_SOUND(H, sound('sound/mecha/nominal.ogg',volume=50))
return TRUE
/obj/vehicle/sealed/mecha/proc/mmi_move_inside(obj/item/mmi/M, mob/user)
if(!M.brainmob || !M.brainmob.client)
to_chat(user, "<span class='warning'>Consciousness matrix not detected!</span>")
return FALSE
else if(M.brainmob.stat)
to_chat(user, "<span class='warning'>Beta-rhythm below acceptable level!</span>")
return FALSE
var/mob/living/brain/B = M.brainmob
if(LAZYLEN(occupants) >= max_occupants)
to_chat(user, "<span class='warning'>It's full!</span>")
return FALSE
if(dna_lock && (!B.stored_dna || (dna_lock != B.stored_dna.unique_enzymes)))
to_chat(user, "<span class='warning'>Access denied. [name] is secured with a DNA lock.</span>")
return FALSE
visible_message("<span class='notice'>[user] starts to insert an MMI into [name].</span>")
if(do_after(user, 40, target = src))
if(LAZYLEN(occupants) < max_occupants)
return mmi_moved_inside(M, user)
else
to_chat(user, "<span class='warning'>Maximum occupants detected!</span>")
else
to_chat(user, "<span class='notice'>You stop inserting the MMI.</span>")
return FALSE
/obj/vehicle/sealed/mecha/proc/mmi_moved_inside(obj/item/mmi/M, mob/user)
if(!(Adjacent(M) && Adjacent(user)))
return FALSE
if(!M.brainmob || !M.brainmob.client)
to_chat(user, "<span class='warning'>Consciousness matrix not detected!</span>")
return FALSE
else if(M.brainmob.stat)
to_chat(user, "<span class='warning'>Beta-rhythm below acceptable level!</span>")
var/mob/living/brain/B = M.brainmob
if(!user.transferItemToLoc(M, src))
to_chat(user, "<span class='warning'>\the [M] is stuck to your hand, you cannot put it in \the [src]!</span>")
return FALSE
M.mecha = src
add_occupant(B)//Note this forcemoves the brain into the mech to allow relaymove
mecha_flags |= SILICON_PILOT
B.reset_perspective(src)
B.remote_control = src
B.update_mobility()
setDir(dir_in)
log_message("[M] moved in as pilot.", LOG_MECHA)
if(!internal_damage)
SEND_SOUND(M, sound('sound/mecha/nominal.ogg',volume=50))
log_game("[key_name(user)] has put the MMI/posibrain of [key_name(B)] into [src] at [AREACOORD(src)]")
return TRUE
/obj/vehicle/sealed/mecha/container_resist(mob/living/user)
if(isAI(user))
var/mob/living/silicon/ai/AI = user
if(!AI.can_shunt)
to_chat(AI, "<span class='notice'>You can't leave a mech after dominating it!.</span>")
return FALSE
to_chat(user, "<span class='notice'>You begin the ejection procedure. Equipment is disabled during this process. Hold still to finish ejecting.</span>")
is_currently_ejecting = TRUE
if(do_after(user, exit_delay , target = src))
to_chat(user, "<span class='notice'>You exit the mech.</span>")
mob_try_exit(user, silent = FALSE)
else
to_chat(user, "<span class='notice'>You stop exiting the mech. Weapons are enabled again.</span>")
is_currently_ejecting = FALSE
/obj/vehicle/sealed/mecha/proc/ejectall()
for(var/ejectee in occupants)
mob_try_exit(ejectee, TRUE, TRUE)
/obj/vehicle/sealed/mecha/mob_try_exit(mob/M, silent, randomstep)
mob_exit(M, silent, randomstep)
/obj/vehicle/sealed/mecha/mob_exit(mob/M, silent, forced)
var/newloc = get_turf(src)
var/atom/movable/mob_container
if(ishuman(M))
remove_occupant(M)
..()
return
else if(isbrain(M))
var/mob/living/brain/brain = M
mob_container = brain.container
else if(isAI(M))
var/mob/living/silicon/ai/AI = M
if(forced)//This should only happen if there are multiple AIs in a round, and at least one is Malf.
AI.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.
AI = null
mecha_flags &= ~SILICON_PILOT
return
else
if(!AI.linked_core)
to_chat(AI, "<span class='userdanger'>Inactive core destroyed. Unable to return.</span>")
AI.linked_core = null
return
to_chat(AI, "<span class='notice'>Returning to core...</span>")
AI.controlled_mech = null
AI.remote_control = null
mob_container = AI
newloc = get_turf(AI.linked_core)
qdel(AI.linked_core)
else
return ..()
var/mob/living/L = M
mecha_flags &= ~SILICON_PILOT
if(mob_container.forceMove(newloc))
log_message("[mob_container] moved out.", LOG_MECHA)
L << browse(null, "window=exosuit")
if(istype(mob_container, /obj/item/mmi))
var/obj/item/mmi/mmi = mob_container
if(mmi.brainmob)
L.forceMove(mmi)
L.reset_perspective()
remove_occupant(L)
mmi.mecha = null
mmi.update_icon()
L.mobility_flags = NONE
setDir(dir_in)
return ..()
/obj/vehicle/sealed/mecha/add_occupant(mob/M, control_flags)
RegisterSignal(M, COMSIG_MOB_DEATH, .proc/mob_exit)
RegisterSignal(M, COMSIG_MOB_CLICKON, .proc/on_mouseclick)
RegisterSignal(M, COMSIG_MOB_SAY, .proc/display_speech_bubble)
return ..()
/obj/vehicle/sealed/mecha/after_add_occupant(mob/M)
. = ..()
update_icon()
M.update_mouse_pointer()
/obj/vehicle/sealed/mecha/remove_occupant(mob/M)
UnregisterSignal(M, COMSIG_MOB_DEATH)
UnregisterSignal(M, COMSIG_MOB_CLICKON)
UnregisterSignal(M, COMSIG_MOB_SAY)
M.clear_alert("charge")
M.clear_alert("mech damage")
if(M.client)
M.client.view_size.resetToDefault()
zoom_mode = 0
return ..()
/obj/vehicle/sealed/mecha/after_remove_occupant(mob/M)
. = ..()
update_icon()
M.update_mouse_pointer()
/////////////////////////
////// Access stuff /////
/////////////////////////
/obj/vehicle/sealed/mecha/proc/operation_allowed(mob/M)
req_access = operation_req_access
req_one_access = list()
return allowed(M)
/obj/vehicle/sealed/mecha/proc/internals_access_allowed(mob/M)
req_one_access = internals_req_access
req_access = list()
return allowed(M)
///////////////////////
///// Power stuff /////
///////////////////////
/obj/vehicle/sealed/mecha/proc/has_charge(amount)
return (get_charge()>=amount)
/obj/vehicle/sealed/mecha/proc/get_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/vehicle/sealed/mecha/proc/use_power(amount)
if(get_charge() && cell.use(amount))
return TRUE
return FALSE
/obj/vehicle/sealed/mecha/proc/give_power(amount)
if(!isnull(get_charge()))
cell.give(amount)
return TRUE
return FALSE
///////////////////////
////// Ammo stuff /////
///////////////////////
/obj/vehicle/sealed/mecha/proc/ammo_resupply(obj/item/mecha_ammo/A, mob/user,fail_chat_override = FALSE)
if(!A.rounds)
if(!fail_chat_override)
to_chat(user, "<span class='warning'>This box of ammo is empty!</span>")
return FALSE
var/ammo_needed
var/found_gun
for(var/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/gun in equipment)
ammo_needed = 0
if(istype(gun, /obj/item/mecha_parts/mecha_equipment/weapon/ballistic) && gun.ammo_type == A.ammo_type)
found_gun = TRUE
if(A.direct_load)
ammo_needed = initial(gun.projectiles) - gun.projectiles
else
ammo_needed = gun.projectiles_cache_max - gun.projectiles_cache
if(ammo_needed)
if(ammo_needed < A.rounds)
if(A.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 class='notice'>You add [ammo_needed] [A.round_term][ammo_needed > 1?"s":""] to the [gun.name]</span>")
A.rounds = A.rounds - ammo_needed
A.update_name()
return TRUE
else
if(A.direct_load)
gun.projectiles = gun.projectiles + A.rounds
else
gun.projectiles_cache = gun.projectiles_cache + A.rounds
playsound(get_turf(user),A.load_audio,50,TRUE)
to_chat(user, "<span class='notice'>You add [A.rounds] [A.round_term][A.rounds > 1?"s":""] to the [gun.name]</span>")
A.rounds = 0
A.update_name()
return TRUE
if(!fail_chat_override)
if(found_gun)
to_chat(user, "<span class='notice'>You can't fit any more ammo of this type!</span>")
else
to_chat(user, "<span class='notice'>None of the equipment on this exosuit can use this ammo!</span>")
return FALSE