Files
CHOMPStation2/code/game/mecha/mecha.dm
CHOMPStation2StaffMirrorBot f7de0bb70b [MIRROR] Start of TG Click Code Port (#12071)
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
2025-12-06 03:18:32 -05:00

2922 lines
101 KiB
Plaintext

#define MECHA_OPERATING 0
#define MECHA_BOLTS_SECURED 1
#define MECHA_PANEL_LOOSE 2
#define MECHA_CELL_OPEN 3
#define MECHA_CELL_OUT 4
/obj/mecha
name = "Mecha"
desc = "Exosuit"
description_info = "Alt click to strafe."
icon = 'icons/mecha/mecha.dmi'
density = TRUE //Dense. To raise the heat.
opacity = 1 //Opaque. Menacing.
anchored = TRUE //No pulling around.
unacidable = TRUE //And no deleting hoomans inside
flags = REMOTEVIEW_ON_ENTER
layer = MOB_LAYER //Icon draw layer
infra_luminosity = 15 //Byond implementation is bugged.
var/initial_icon = null //Mech type for resetting icon. Only used for reskinning kits (see custom items)
var/can_move = 1
var/mob/living/carbon/occupant = null
var/step_in = 10 //Make a step in step_in/10 sec.
var/encumbrance_gap = 1 //How many points of slowdown are negated from equipment? Added to the mech's base step_in.
var/dir_in = 2 //What direction will the mech face when entered/powered on? Defaults to South.
var/step_energy_drain = 10
var/health = 300 //Health is health
var/maxhealth = 300 //Maxhealth is maxhealth.
var/deflect_chance = 10 //Chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act.
var/damage_minimum = 10 //Incoming damage lower than this won't actually deal damage. Scrapes shouldn't be a real thing.
var/minimum_penetration = 15 //Incoming damage won't be fully applied if you don't have at least 20. Almost all AP clears this.
var/fail_penetration_value = 0.66 //By how much failing to penetrate reduces your shit. 66% by default. 100dmg = 66dmg if failed pen
var/obj/item/cell/cell
var/state = MECHA_OPERATING
var/list/log = list()
var/last_message = 0
var/add_req_access = 1
var/maint_access = 1
var/dna //Dna-locking the mech
var/list/proc_res = list() //Stores proc owners, like proc_res["functionname"] = owner reference
var/datum/effect/effect/system/spark_spread/spark_system
var/lights = 0
var/lights_power = 6
var/force = 0
var/damage_type = BRUTE
var/mech_faction = null
var/firstactivation = 0 //It's simple. If it's 0, no one entered it yet. Otherwise someone entered it at least once.
var/stomp_sound = 'sound/mecha/mechstep.ogg'
var/stomp_sound_2 = 'sound/mecha/mechstep.ogg' // CHOMPedit: Used for 1-2 step patterns instead of random choice.
var/swivel_sound = 'sound/mecha/mechturn.ogg'
var/reps = 0 // CHOMPedit: Used for 1-2 step patterns.
//inner atmos
var/use_internal_tank = 0
var/internal_tank_valve = ONE_ATMOSPHERE
var/obj/machinery/portable_atmospherics/canister/internal_tank
var/datum/gas_mixture/cabin_air
var/obj/machinery/atmospherics/portables_connector/connected_port = null
var/obj/item/radio/radio = null
var/max_temperature = 25000 //Kelvin values.
var/internal_damage_threshold = 33 //Health percentage below which internal damage is possible
var/internal_damage_minimum = 15 //At least this much damage to trigger some real bad hurt.
var/internal_damage = 0 //Contains bitflags
var/list/operation_req_access = list() //Required access level for mecha operation
var/list/internals_req_access = list(ACCESS_ENGINE,ACCESS_ROBOTICS) //Required access level to open cell compartment
var/wreckage
var/list/equipment = list() //This lists holds what stuff you bolted onto your baby ride
var/obj/item/mecha_parts/mecha_equipment/selected
var/max_equip = 2
// What direction to float in, if inertial movement is active.
var/float_direction = 0
// Process() iterator count.
var/process_ticks = 0
// These control what toggleable processes are executed within process().
var/current_processes = MECHA_PROC_INT_TEMP
//mechaequipt2 stuffs
var/list/hull_equipment = list()
var/list/weapon_equipment = list()
var/list/utility_equipment = list()
var/list/universal_equipment = list()
var/list/special_equipment = list()
var/max_hull_equip = 2
var/max_weapon_equip = 2
var/max_utility_equip = 2
var/max_universal_equip = 2
var/max_special_equip = 1
var/list/starting_equipment = null // List containing starting tools.
// Mech Components, similar to Cyborg, but Bigger.
var/list/internal_components = list(
MECH_HULL = null,
MECH_ACTUATOR = null,
MECH_ARMOR = null,
MECH_GAS = null,
MECH_ELECTRIC = null
)
var/list/starting_components = list(
/obj/item/mecha_parts/component/hull,
/obj/item/mecha_parts/component/actuator,
/obj/item/mecha_parts/component/armor,
/obj/item/mecha_parts/component/gas,
/obj/item/mecha_parts/component/electrical
)
//Working exosuit vars
var/list/cargo = list()
var/cargo_capacity = 3
var/static/image/radial_image_eject = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_eject")
var/static/image/radial_image_airtoggle = image(icon= 'icons/mob/radial.dmi', icon_state = "radial_airtank")
var/static/image/radial_image_lighttoggle = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_light")
var/static/image/radial_image_statpanel = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_examine2")
//Mech actions
var/datum/mini_hud/mech/minihud //VOREStation Edit
var/strafing = 0 //Are we strafing or not?
var/defence_mode_possible = 0 //Can we even use defence mode? This is used to assign it to mechs and check for verbs.
var/defence_mode = 0 //Are we in defence mode
var/defence_deflect = 35 //How much it deflect
var/overload_possible = 0 //Same as above. Don't forget to GRANT the verb&actions if you want everything to work proper.
var/overload = 0 //Are our legs overloaded
var/overload_coeff = 1 //How much extra energy you use when use the L E G
var/zoom = 0
var/zoom_possible = 0
var/thrusters = 0
var/thrusters_possible = 0
var/phasing = 0 //Are we currently phasing
var/phasing_possible = 0 //This is to allow phasing.
var/can_phase = TRUE //This is an internal check during the relevant procs.
var/phasing_energy_drain = 200
var/switch_dmg_type_possible = 0 //Can you switch damage type? It is mostly for the Phazon and its children.
var/smoke_possible = 0
var/smoke_reserve = 5 //How many shots you have. Might make a reload later on. MIGHT.
var/smoke_ready = 1 //This is a check for the whether or not the cooldown is ongoing.
var/smoke_cooldown = 100 //How long you have between uses.
var/datum/effect/effect/system/smoke_spread/smoke_system
var/cloak_possible = FALSE // Can this exosuit innately cloak?
////All of those are for the HUD buttons in the top left. See Grant and Remove procs in mecha_actions.
var/datum/action/innate/mecha/mech_eject/eject_action
var/datum/action/innate/mecha/mech_toggle_internals/internals_action
var/datum/action/innate/mecha/mech_toggle_lights/lights_action
var/datum/action/innate/mecha/mech_view_stats/stats_action
var/datum/action/innate/mecha/strafe/strafing_action
var/datum/action/innate/mecha/mech_defence_mode/defence_action
var/datum/action/innate/mecha/mech_overload_mode/overload_action
var/datum/action/innate/mecha/mech_smoke/smoke_action
var/datum/action/innate/mecha/mech_zoom/zoom_action
var/datum/action/innate/mecha/mech_toggle_thrusters/thrusters_action
var/datum/action/innate/mecha/mech_cycle_equip/cycle_action
var/datum/action/innate/mecha/mech_switch_damtype/switch_damtype_action
var/datum/action/innate/mecha/mech_toggle_phasing/phasing_action
var/datum/action/innate/mecha/mech_toggle_cloaking/cloak_action
var/weapons_only_cycle = FALSE //So combat mechs don't switch to their equipment at times.
//Micro Mech Code
var/max_micro_utility_equip = 0
var/max_micro_weapon_equip = 0
var/list/micro_utility_equipment = list()
var/list/micro_weapon_equipment = list()
/obj/mecha/Initialize(mapload)
. = ..()
//All this used to be set to =new on the object itself which...bad.
eject_action = new
internals_action = new
lights_action = new
stats_action = new
strafing_action = new
defence_action = new
overload_action = new
smoke_action = new
zoom_action = new
thrusters_action = new
cycle_action = new
switch_damtype_action = new
phasing_action = new
cloak_action = new
for(var/path in starting_components)
var/obj/item/mecha_parts/component/C = new path(src)
C.attach(src)
if(starting_equipment && LAZYLEN(starting_equipment))
for(var/path in starting_equipment)
var/obj/item/mecha_parts/mecha_equipment/ME = new path(src)
ME.attach(src)
START_PROCESSING(SSobj, src)
update_transform()
icon_state += "-open"
add_radio()
add_cabin()
if(!add_airtank()) //we check this here in case mecha does not have an internal tank available by default - WIP
removeVerb(/obj/mecha/verb/connect_to_port)
removeVerb(/obj/mecha/verb/toggle_internal_tank)
spark_system = new
spark_system.set_up(2, 0, src)
spark_system.attach(src)
smoke_system = new
if(smoke_possible)//I am pretty sure that's needed here.
src.smoke_system.set_up(3, 0, src)
src.smoke_system.attach(src)
add_cell()
removeVerb(/obj/mecha/verb/disconnect_from_port)
src.mecha_log_message("[src.name] created.")
loc.Entered(src)
GLOB.mechas_list += src //global mech list
/obj/mecha/drain_power(var/drain_check)
if(drain_check)
return 1
if(!cell)
return 0
return cell.drain_power(drain_check)
/obj/mecha/Exit(atom/movable/O)
if(O in cargo)
return 0
return ..()
/obj/mecha/Destroy()
src.go_out()
for(var/mob/M in src) //Be Extra Sure
M.forceMove(get_turf(src))
M.loc.Entered(M)
if(M != src.occupant)
step_rand(M)
for(var/atom/movable/A in src.cargo)
A.forceMove(get_turf(src))
var/turf/T = get_turf(A)
if(T)
T.Entered(A)
step_rand(A)
if(loc)
loc.Exited(src)
if(prob(30))
explosion(get_turf(loc), 0, 0, 1, 3)
if(wreckage)
var/obj/effect/decal/mecha_wreckage/WR = new wreckage(loc)
hull_equipment.Cut()
weapon_equipment.Cut()
utility_equipment.Cut()
universal_equipment.Cut()
special_equipment.Cut()
for(var/obj/item/mecha_parts/mecha_equipment/E in equipment)
if(E.salvageable && prob(30))
WR.crowbar_salvage += E
E.forceMove(WR)
E.equip_ready = TRUE
else
E.forceMove(loc)
E.destroy()
for(var/slot in internal_components)
var/obj/item/mecha_parts/component/C = internal_components[slot]
if(istype(C))
C.damage_part(rand(10, 20))
C.detach()
WR.crowbar_salvage += C
C.forceMove(WR)
if(cell)
WR.crowbar_salvage += cell
cell.forceMove(WR)
cell.charge = rand(0, cell.charge)
if(internal_tank)
WR.crowbar_salvage += internal_tank
internal_tank.forceMove(WR)
else
for(var/obj/item/mecha_parts/mecha_equipment/E in equipment)
E.detach(loc)
E.destroy()
for(var/slot in internal_components)
var/obj/item/mecha_parts/component/C = internal_components[slot]
if(istype(C))
C.detach()
qdel(C)
if(cell)
qdel(cell)
if(internal_tank)
qdel(internal_tank)
equipment.Cut()
cell = null
internal_tank = null
if(smoke_possible) //Just making sure nothing is running.
qdel(smoke_system)
GLOB.mech_destroyed_roundstat++
QDEL_NULL(spark_system)
QDEL_NULL(minihud)
STOP_PROCESSING(SSobj, src)
GLOB.mechas_list -= src //global mech list
. = ..()
// The main process loop to replace the ancient global iterators.
// It's a bit hardcoded but I don't see anyone else adding stuff to
// mechas, and it's easy enough to modify.
/obj/mecha/process()
var/static/max_ticks = 16
if (current_processes & MECHA_PROC_MOVEMENT)
process_inertial_movement()
if ((current_processes & MECHA_PROC_DAMAGE) && !(process_ticks % 2))
process_internal_damage()
if ((current_processes & MECHA_PROC_INT_TEMP) && !(process_ticks % 4))
process_preserve_temp()
if (!(process_ticks % 3))
process_tank_give_air()
// Max value is 16. So we let it run between [0, 16] with this.
process_ticks = (process_ticks + 1) % 17
// Normalizing cabin air temperature to 20 degrees celsius.
// Called every fourth process() tick (20 deciseconds).
/obj/mecha/proc/process_preserve_temp()
if (cabin_air && cabin_air.volume > 0)
var/delta = cabin_air.temperature - T20C
cabin_air.temperature -= max(-10, min(10, round(delta/4,0.1)))
// Handles internal air tank action.
// Called every third process() tick (15 deciseconds).
/obj/mecha/proc/process_tank_give_air()
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.temperature > 0)
transfer_moles = pressure_delta*cabin_air.volume/(cabin_air.temperature * R_IDEAL_GAS_EQUATION)
var/datum/gas_mixture/removed = tank_air.remove(transfer_moles)
cabin_air.merge(removed)
else if(pressure_delta < 0) //cabin pressure higher than release pressure
var/datum/gas_mixture/t_air = get_turf_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.volume/(cabin_air.temperature * R_IDEAL_GAS_EQUATION)
var/datum/gas_mixture/removed = cabin_air.remove(transfer_moles)
if(t_air)
t_air.merge(removed)
else //just delete the cabin gas, we're in space or some shit
qdel(removed)
// Inertial movement in space.
// Called every process() tick (5 deciseconds).
/obj/mecha/proc/process_inertial_movement()
if(float_direction)
if(!step(src, float_direction) || check_for_support())
stop_process(MECHA_PROC_MOVEMENT)
else
stop_process(MECHA_PROC_MOVEMENT)
return
// Processes internal damage.
// Called every other process() tick (10 deciseconds).
/obj/mecha/proc/process_internal_damage()
if(!hasInternalDamage())
stop_process(MECHA_PROC_DAMAGE)
return
if(hasInternalDamage(MECHA_INT_FIRE))
if(!hasInternalDamage(MECHA_INT_TEMP_CONTROL) && prob(5))
clearInternalDamage(MECHA_INT_FIRE)
if(internal_tank)
if(internal_tank.return_pressure()>internal_tank.maximum_pressure && !(hasInternalDamage(MECHA_INT_TANK_BREACH)))
setInternalDamage(MECHA_INT_TANK_BREACH)
var/datum/gas_mixture/int_tank_air = internal_tank.return_air()
if(int_tank_air && int_tank_air.volume>0) //heat the air_contents
int_tank_air.temperature = min(6000+T0C, int_tank_air.temperature+rand(10,15))
if(cabin_air && cabin_air.volume>0)
cabin_air.temperature = min(6000+T0C, cabin_air.temperature+rand(10,15))
if(cabin_air.temperature>max_temperature/2)
take_damage(4/round(max_temperature/cabin_air.temperature,0.1),"fire")
if(hasInternalDamage(MECHA_INT_TEMP_CONTROL))
stop_process(MECHA_PROC_INT_TEMP)
if(hasInternalDamage(MECHA_INT_TANK_BREACH)) //remove some air from internal tank
if(internal_tank)
var/datum/gas_mixture/int_tank_air = internal_tank.return_air()
var/datum/gas_mixture/leaked_gas = int_tank_air.remove_ratio(0.10)
if(istype(loc, /turf/simulated))
loc.assume_air(leaked_gas)
else
qdel(leaked_gas)
if(hasInternalDamage(MECHA_INT_SHORT_CIRCUIT))
if(get_charge())
spark_system.start()
cell.charge -= min(20,cell.charge)
cell.maxcharge -= min(20,cell.maxcharge)
return
////////////////////////
////// Helpers /////////
////////////////////////
/obj/mecha/proc/removeVerb(verb_path)
src.verbs -= verb_path
/obj/mecha/proc/addVerb(verb_path)
src.verbs += verb_path
/obj/mecha/proc/add_airtank()
internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src)
return internal_tank
/obj/mecha/proc/add_cell(var/obj/item/cell/C=null)
if(C)
C.forceMove(src)
cell = C
return
cell = new /obj/item/cell/mech(src)
/obj/mecha/get_cell()
return cell
/obj/mecha/proc/add_cabin()
cabin_air = new
cabin_air.temperature = T20C
cabin_air.volume = 200
cabin_air.adjust_multi(GAS_O2, O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature), GAS_N2, N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature))
return cabin_air
/obj/mecha/proc/add_radio()
radio = new(src)
radio.name = "[src] radio"
radio.icon = icon
radio.icon_state = icon_state
radio.subspace_transmission = 1
/obj/mecha/proc/do_after_action(delay as num) //This is literally just a sleep disguised as a proc. Fucking bullshit.
sleep(delay)
if(src)
return 1
return 0
/obj/mecha/proc/enter_after(delay as num, var/mob/user as mob, var/numticks = 5)
var/delayfraction = delay/numticks
var/turf/T = user.loc
for(var/i = 0, i<numticks, i++)
sleep(delayfraction)
if(!src || !user || !user.canmove || !(user.loc == T))
return 0
return 1
/obj/mecha/proc/check_for_support()
var/list/things = orange(1, src)
if(locate(/obj/structure/grille) in things || locate(/obj/structure/lattice) in things || locate(/turf/simulated) in things || locate(/turf/unsimulated) in things)
return 1
else
return 0
/obj/mecha/examine(mob/user)
. = ..()
var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR]
var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL]
if(AC)
. += "It has [AC] attached. [AC.get_efficiency()<0.5?"It is severely damaged.":""]"
else
. += "It has no armor plating."
if(HC)
if(!AC || AC.get_efficiency() < 0.7)
. += "It has [HC] attached. [HC.get_efficiency()<0.5?"It is severely damaged.":""]"
else
. += "You cannot tell what type of hull it has."
else
. += "It does not seem to have a completed hull."
var/integrity = health/initial(health)*100
switch(integrity)
if(85 to 100)
. += "It's fully intact."
if(65 to 85)
. += "It's slightly damaged."
if(45 to 65)
. += span_notice("It's badly damaged.")
if(25 to 45)
. += span_warning("It's heavily damaged.")
else
. += span_warning(span_bold(" It's falling apart.") + " ")
if(equipment?.len)
. += "It's equipped with:"
for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment)
. += "[icon2html(ME,user.client)] [ME]"
/obj/mecha/proc/drop_item()//Derpfix, but may be useful in future for engineering exosuits.
return
/obj/mecha/hear_talk(mob/M, list/message_pieces, verb)
if(M == occupant && radio.broadcasting)
radio.talk_into(M, message_pieces)
/obj/mecha/proc/check_occupant_radial(var/mob/user)
if(!user)
return FALSE
if(user.stat)
return FALSE
if(user != occupant)
return FALSE
if(user.incapacitated())
return FALSE
return TRUE
/obj/mecha/proc/show_radial_occupant(var/mob/user)
var/list/choices = list(
"Toggle Airtank" = radial_image_airtoggle,
"Toggle Light" = radial_image_lighttoggle,
"View Stats" = radial_image_statpanel
)
var/choice = show_radial_menu(user, src, choices, custom_check = CALLBACK(src, PROC_REF(check_occupant_radial), user), require_near = TRUE, tooltips = TRUE)
if(!check_occupant_radial(user))
return
if(!choice)
return
switch(choice)
if("Toggle Airtank")
use_internal_tank = !use_internal_tank
occupant_message("Now taking air from [use_internal_tank?"internal airtank":"environment"].")
src.mecha_log_message("Now taking air from [use_internal_tank?"internal airtank":"environment"].")
if("Toggle Light")
lights = !lights
if(lights)
set_light(light_range + lights_power)
else
set_light(light_range - lights_power)
occupant_message("Toggled lights [lights?"on":"off"].")
src.mecha_log_message("Toggled lights [lights?"on":"off"].")
playsound(src, 'sound/mecha/heavylightswitch.ogg', 50, 1)
if("View Stats")
occupant << browse(src.get_stats_html(), "window=exosuit")
////////////////////////////
///// Action processing ////
////////////////////////////
/*
/atom/DblClick(object,location,control,params)
var/mob/M = src.mob
if(M && M.in_contents_of(/obj/mecha))
if(mech_click == world.time) return
mech_click = world.time
if(!istype(object, /atom)) return
if(istype(object, /atom/movable/screen))
var/atom/movable/screen/using = object
if(using.screen_loc == ui_acti || using.screen_loc == ui_iarrowleft || using.screen_loc == ui_iarrowright)//ignore all HUD objects save 'intent' and its arrows
return ..()
else
return
var/obj/mecha/Mech = M.loc
spawn() //this helps prevent clickspam fest.
if (Mech)
Mech.click_action(object,M)
// else
// return ..()
*/
/obj/mecha/proc/click_action(atom/target,mob/user, params)
if(!src.occupant || src.occupant != user ) return
if(user.stat) return
if(target == src && user == occupant)
show_radial_occupant(user)
return
if(state)
occupant_message(span_red("Maintenance protocols in effect"))
return
if(phasing)//Phazon and other mechs with phasing.
src.occupant_message("Unable to interact with objects while phasing")//Haha dumbass.
return
if(!get_charge()) return
if(src == target) return
var/dir_to_target = get_dir(src,target)
if(dir_to_target && !(dir_to_target & src.dir))//wrong direction
return
if(hasInternalDamage(MECHA_INT_CONTROL_LOST))
target = safepick(view(3,target))
if(!target)
return
if(istype(target, /obj/machinery))
if (src.interface_action(target))
return
if(!target.Adjacent(src))
if(selected && selected.is_ranged())
selected.action(target)
else if(selected && selected.is_melee())
selected.action(target, params)
else
src.melee_action(target)
return
/obj/mecha/proc/interface_action(obj/machinery/target)
if(istype(target, /obj/machinery/access_button))
src.occupant_message(span_notice("Interfacing with [target]."))
src.mecha_log_message("Interfaced with [target].")
target.attack_hand(src.occupant)
return 1
if(istype(target, /obj/machinery/embedded_controller))
target.tgui_interact(src.occupant)
return 1
return 0
/obj/mecha/contents_tgui_distance(var/src_object, var/mob/living/user)
. = user.shared_living_tgui_distance(src_object) //allow them to interact with anything they can interact with normally.
if(. != STATUS_INTERACTIVE)
//Allow interaction with the mecha or anything that is part of the mecha
if(src_object == src || (src_object in src))
return STATUS_INTERACTIVE
if(src.Adjacent(src_object))
src.occupant_message(span_notice("Interfacing with [src_object]..."))
src.mecha_log_message("Interfaced with [src_object].")
return STATUS_INTERACTIVE
if(src_object in view(2, src))
return STATUS_UPDATE //if they're close enough, allow the occupant to see the screen through the viewport or whatever.
/obj/mecha/proc/melee_action(atom/target)
return
/obj/mecha/proc/range_action(atom/target)
return
//////////////////////////////////
//////// Movement procs ////////
//////////////////////////////////
/obj/mecha/Moved(atom/old_loc, direction, forced = FALSE)
. = ..()
MoveAction()
/obj/mecha/proc/MoveAction() //Allows mech equipment to do an action once the mech moves
if(!equipment.len)
return
for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment)
ME.MoveAction()
/obj/mecha/relaymove(mob/user,direction)
if(user != src.occupant) //While not "realistic", this piece is player friendly.
if(istype(user,/mob/living/carbon/brain))
to_chat(user, span_warning("You try to move, but you are not the pilot! The exosuit doesn't respond."))
return 0
user.forceMove(get_turf(src))
to_chat(user, "You climb out from [src]")
return 0
var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL]
if(!HC)
occupant_message(span_notice("You can't operate an exosuit that doesn't have a hull!"))
return
if(connected_port)
if(world.time - last_message > 20)
src.occupant_message(span_warning("Unable to move while connected to the air system port"))
last_message = world.time
return 0
if(state)
occupant_message(span_warning("Maintenance protocols in effect"))
return
/*
if(zoom)
if(world.time - last_message > 20)
src.occupant_message("Unable to move while in zoom mode.")
last_message = world.time
return 0
*/
return domove(direction)
/obj/mecha/proc/can_ztravel()
for(var/obj/item/mecha_parts/mecha_equipment/tool/jetpack/jp in equipment)
return jp.equip_ready
return FALSE
/obj/mecha/proc/domove(direction)
return call((proc_res["dyndomove"]||src), "dyndomove")(direction)
/obj/mecha/proc/get_step_delay()
var/tally = 0
if(LAZYLEN(equipment))
for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment)
if(ME.get_step_delay())
tally += ME.get_step_delay()
if(tally <= encumbrance_gap) // If the total is less than our encumbrance gap, ignore equipment weight.
tally = 0
else // Otherwise, start the tally after cutting that gap out.
tally -= encumbrance_gap
for(var/slot in internal_components)
var/obj/item/mecha_parts/component/C = internal_components[slot]
if(C && C.get_step_delay())
tally += C.get_step_delay()
var/obj/item/mecha_parts/component/actuator/actuator = internal_components[MECH_ACTUATOR]
if(!actuator) // Relying purely on hydraulic pumps. You're going nowhere fast.
tally = 2 SECONDS
return tally
tally += 0.5 SECONDS * (1 - actuator.get_efficiency()) // Damaged actuators run slower, slowing as damage increases beyond its threshold.
if(strafing)
tally = round(tally * actuator.strafing_multiplier)
for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment)
if(istype(ME, /obj/item/mecha_parts/mecha_equipment/speedboost))
var/obj/item/mecha_parts/mecha_equipment/speedboost/SB = ME
for(var/path in ME.required_type)
if(istype(src, path))
tally = round(tally * SB.slowdown_multiplier)
break
break
if(overload) // At the end, because this would normally just make the mech *slower* since tally wasn't starting at 0.
tally = min(1, round(tally/2))
return max(1, round(tally, 0.1)) // Round the total to the nearest 10th. Can't go lower than 1 tick. Even humans have a delay longer than that.
/obj/mecha/proc/dyndomove(direction)
if(!can_move)
return 0
if(current_processes & MECHA_PROC_MOVEMENT)
return 0
if(!has_charge(step_energy_drain))
return 0
//Can we even move, below is if yes.
if(defence_mode)//Check if we are currently locked down
if(world.time - last_message > 20)
src.occupant_message(span_red("Unable to move while in defence mode"))
last_message = world.time
return 0
if(zoom)//:eyes:
if(world.time - last_message > 20)
src.occupant_message("Unable to move while in zoom mode.")
last_message = world.time
return 0
if(!thrusters && (current_processes & MECHA_PROC_MOVEMENT)) //I think this mean 'if you try to move in space without thruster, u no move'
return 0
if(overload)//Check if you have leg overload
health--
if(health < initial(health) - initial(health)/3)
overload = 0
step_energy_drain = initial(step_energy_drain)
src.occupant_message(span_red("Leg actuators damage threshold exceded. Disabling overload."))
var/move_result = 0
if(hasInternalDamage(MECHA_INT_CONTROL_LOST))
move_result = mechsteprand()
//Up/down zmove
else if(direction == UP || direction == DOWN)
if(!can_ztravel())
occupant_message(span_warning("Your vehicle lacks the capacity to move in that direction!"))
return FALSE
//We're using locs because some mecha are 2x2 turfs. So thicc!
var/result = TRUE
for(var/turf/T in locs)
if(!T.CanZPass(src,direction))
occupant_message(span_warning("You can't move that direction from here!"))
result = FALSE
break
var/turf/dest = (direction == UP) ? GetAbove(src) : GetBelow(src)
if(!dest)
occupant_message(span_notice("There is nothing of interest in this direction."))
result = FALSE
break
if(!dest.CanZPass(src,direction))
occupant_message(span_warning("There's something blocking your movement in that direction!"))
result = FALSE
break
if(result)
move_result = mechstep(direction)
//Turning
else if(src.dir != direction)
if(strafing)
move_result = mechstep(direction)
else
move_result = mechturn(direction)
//Stepping
else
move_result = mechstep(direction)
if(move_result)
can_move = 0
use_power(step_energy_drain)
if(istype(src.loc, /turf/space))
if(!src.check_for_support())
float_direction = direction
start_process(MECHA_PROC_MOVEMENT)
src.mecha_log_message(span_warning("Movement control lost. Inertial movement started."))
if(do_after_action(get_step_delay()))
can_move = 1
return 1
return 0
/obj/mecha/proc/handle_equipment_movement()
for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment)
if(ME.chassis == src) //Sanity
ME.handle_movement_action()
return
/obj/mecha/proc/mechturn(direction)
set_dir(direction)
if(swivel_sound)
playsound(src,swivel_sound,40,1)
return 1
/obj/mecha/proc/mechstep(direction)
var/current_dir = dir //For strafing
var/result = get_step(src,direction)
if(result && Move(result))
if(stomp_sound)
playsound(src, reps ? stomp_sound : stomp_sound_2,50,0) // CHOMPedit: 1-2 step sequence.
reps = (reps+1)%2 // CHOMPedit: 1-2 step sequence.
handle_equipment_movement()
if(strafing) //Also for strafing
set_dir(current_dir)
return result
/obj/mecha/proc/mechsteprand()
var/result = get_step_rand(src)
if(result && Move(result))
if(stomp_sound)
playsound(src, reps ? stomp_sound : stomp_sound_2,50,0) // CHOMPedit: 1-2 step sequence.
reps = (reps+1)%2 // CHOMPedit: 1-2 step sequence.
handle_equipment_movement()
return result
/obj/mecha/Bump(var/atom/obstacle)
// src.inertia_dir = null
if(istype(obstacle, /mob))//First we check if it is a mob. Mechs mostly shouln't go through them, even while phasing.
var/mob/M = obstacle
M.Move(get_step(obstacle,src.dir))
else if(phasing && get_charge()>=phasing_energy_drain)//Phazon check. This could use an improvement elsewhere.
src.use_power(phasing_energy_drain)
phase()
. = ..(obstacle)
return
else if(istype(obstacle, /obj))//Then we check for regular obstacles.
var/obj/O = obstacle
if(istype(O, /obj/effect/portal)) //derpfix
src.anchored = 0 // Portals can only move unanchored objects.
O.Crossed(src)
spawn(0)//countering portal teleport spawn(0), hurr
src.anchored = 1
if(O.anchored)
obstacle.Bumped(src)
else
step(obstacle,src.dir)
else//No idea when this triggers, so i won't touch it.
. = ..(obstacle)
return
/obj/mecha/proc/phase() // Force the mecha to move forward by phasing.
set waitfor = FALSE
if(can_phase)
can_phase = FALSE
flick("[initial_icon]-phase", src)
forceMove(get_step(src,src.dir))
sleep(get_step_delay() * 3)
can_phase = TRUE
occupant_message("Phazed.")
return TRUE // In the event this is sequenced
return FALSE
///////////////////////////////////
//////// Internal damage ////////
///////////////////////////////////
//ATM, the ignore_threshold is literally only used for the pulse rifles beams used mostly by deathsquads.
/obj/mecha/proc/check_for_internal_damage(var/list/possible_int_damage,var/ignore_threshold=null)
if(!islist(possible_int_damage) || isemptylist(possible_int_damage)) return
if(prob(30))
if(ignore_threshold || src.health*100/initial(src.health) < src.internal_damage_threshold)
for(var/T in possible_int_damage)
if(internal_damage & T)
possible_int_damage -= T
var/int_dam_flag = safepick(possible_int_damage)
if(int_dam_flag)
setInternalDamage(int_dam_flag)
return //It already hurts to get some, lets not get both.
if(prob(10))
if(ignore_threshold || src.health*100/initial(src.health) < src.internal_damage_threshold)
var/obj/item/mecha_parts/mecha_equipment/destr = safepick(equipment)
if(destr)
destr.destroy()
return
/obj/mecha/proc/hasInternalDamage(int_dam_flag=null)
return int_dam_flag ? internal_damage&int_dam_flag : internal_damage
/obj/mecha/proc/setInternalDamage(int_dam_flag)
internal_damage |= int_dam_flag
start_process(MECHA_PROC_DAMAGE)
log_append_to_last("Internal damage of type [int_dam_flag].",1)
occupant << sound('sound/mecha/internaldmgalarm.ogg',volume=50) //Better sounding.
return
/obj/mecha/proc/clearInternalDamage(int_dam_flag)
internal_damage &= ~int_dam_flag
switch(int_dam_flag)
if(MECHA_INT_TEMP_CONTROL)
occupant_message(span_infoplain(span_blue(span_bold("Life support system reactivated."))))
start_process(MECHA_PROC_INT_TEMP)
if(MECHA_INT_FIRE)
occupant_message(span_infoplain(span_blue(span_bold("Internal fire extinquished."))))
if(MECHA_INT_TANK_BREACH)
occupant_message(span_infoplain(span_blue(span_bold("Damaged internal tank has been sealed."))))
return
////////////////////////////////////////
//////// Health related procs ////////
////////////////////////////////////////
/obj/mecha/take_damage(amount, type=BRUTE)
update_damage_alerts()
if(amount)
var/damage = absorbDamage(amount,type)
damage = components_handle_damage(damage,type)
health -= damage
update_health()
log_append_to_last("Took [damage] points of damage. Damage type: \"[type]\".",1)
return
/obj/mecha/proc/components_handle_damage(var/damage, var/type = BRUTE)
var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR]
if(AC)
var/armor_efficiency = AC.get_efficiency()
var/damage_change = armor_efficiency * (damage * 0.5) * AC.damage_absorption[type]
AC.damage_part(damage_change, type)
damage -= damage_change
var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL]
if(HC)
if(HC.integrity)
var/hull_absorb = round(rand(5, 10) / 10, 0.1) * damage
HC.damage_part(hull_absorb, type)
damage -= hull_absorb
for(var/obj/item/mecha_parts/component/C in (internal_components - list(MECH_HULL, MECH_ARMOR)))
if(prob(C.relative_size))
var/damage_part_amt = round(damage / 4, 0.1)
C.damage_part(damage_part_amt)
damage -= damage_part_amt
return damage
/obj/mecha/proc/get_damage_absorption()
var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR]
if(istype(AC) && AC.get_efficiency() > 0.25)
return AC.damage_absorption
/obj/mecha/proc/absorbDamage(damage,damage_type)
return call((proc_res["dynabsorbdamage"]||src), "dynabsorbdamage")(damage,damage_type)
/obj/mecha/proc/dynabsorbdamage(damage,damage_type)
return damage*(listgetindex(get_damage_absorption(),damage_type) || 1)
/obj/mecha/airlock_crush(var/crush_damage)
..()
take_damage(crush_damage)
if(prob(50)) //Try to avoid that.
check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
return 1
/obj/mecha/proc/update_health()
if(src.health > 0)
src.spark_system.start()
else
qdel(src)
return
/obj/mecha/attack_hand(mob/user as mob)
if(user == occupant)
show_radial_occupant(user)
return
user.setClickCooldown(user.get_attack_speed())
src.mecha_log_message("Attack by hand/paw. Attacker - [user].",1)
var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR]
var/temp_deflect_chance = deflect_chance
if(!ArmC)
temp_deflect_chance = 1
else
temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0))
if(ishuman(user))
var/mob/living/carbon/human/H = user
if(H.species.can_shred(user))
if(!prob(temp_deflect_chance))
src.take_damage(15) //The take_damage() proc handles armor values
if(prob(25)) //Why would they get free internal damage. At least make it a bit RNG.
src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
playsound(src, 'sound/weapons/slash.ogg', 50, 1, -1)
to_chat(user, span_danger("You slash at the armored suit!"))
visible_message(span_danger("\The [user] slashes at [src.name]'s armor!"))
else
src.log_append_to_last("Armor saved.")
playsound(src, 'sound/weapons/slash.ogg', 50, 1, -1)
to_chat(user, span_danger("Your claws had no effect!"))
src.occupant_message(span_notice("\The [user]'s claws are stopped by the armor."))
visible_message(span_warning("\The [user] rebounds off [src.name]'s armor!"))
else
user.visible_message(span_danger("\The [user] hits \the [src]. Nothing happens."),span_danger("You hit \the [src] with no visible effect."))
src.log_append_to_last("Armor saved.")
return
else if ((HULK in user.mutations) && !prob(temp_deflect_chance))
src.take_damage(15) //The take_damage() proc handles armor values
if(prob(25)) //Hulks punch hard but lets not give them consistent internal damage.
src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
user.visible_message(span_warning(span_red(span_bold("[user] hits [src.name], doing some damage."))), span_warning(span_red(span_bold("You hit [src.name] with all your might. The metal creaks and bends."))))
else
user.visible_message(span_infoplain((span_red(span_bold("[user] hits [src.name]. Nothing happens.")))),span_infoplain(span_red(span_bold("You hit [src.name] with no visible effect."))))
src.log_append_to_last("Armor saved.")
return
/obj/mecha/hitby(atom/movable/source) //wrapper
..()
src.mecha_log_message("Hit by [source].",1)
call((proc_res["dynhitby"]||src), "dynhitby")(source)
return
//I think this is relative to throws.
/obj/mecha/proc/dynhitby(atom/movable/A)
var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR]
var/temp_deflect_chance = deflect_chance
//var/temp_damage_minimum = damage_minimum //CHOMPremove
//var/temp_minimum_penetration = minimum_penetration //CHOMPremove
var/temp_fail_penetration_value = fail_penetration_value
if(!ArmC)
temp_deflect_chance = 0
//temp_damage_minimum = 0 //CHOMPremove
//temp_minimum_penetration = 0
temp_fail_penetration_value = 1
else
temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0))
//temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) //CHOMPremove
//temp_minimum_penetration = round(ArmC.get_efficiency() * ArmC.minimum_penetration) //CHOMPremove
temp_fail_penetration_value = round(ArmC.get_efficiency() * ArmC.fail_penetration_value)
if(istype(A, /obj/item/mecha_parts/mecha_tracking))
A.forceMove(src)
src.visible_message("The [A] fastens firmly to [src].")
return
if(prob(temp_deflect_chance) || istype(A, /mob))
src.occupant_message(span_notice("\The [A] bounces off the armor."))
src.visible_message("\The [A] bounces off \the [src] armor")
src.log_append_to_last("Armor saved.")
if(isliving(A))
var/mob/living/M = A
M.take_organ_damage(10)
else if(istype(A, /obj/item))
var/obj/item/O = A
if(O.throwforce)
var/pass_damage = O.throwforce
var/pass_damage_reduc_mod
if(pass_damage <= damage_minimum)//Too little to go through. //CHOMPedit temp_damage_mininum -> damage_minimum
src.occupant_message(span_notice("\The [A] bounces off the armor."))
src.visible_message("\The [A] bounces off \the [src] armor")
return
else if(O.armor_penetration < minimum_penetration) //If you don't have enough pen, you won't do full damage //CHOMPedit, temp_minimum_penetration -> minimum_penetration
src.occupant_message(span_notice("\The [A] struggles to bypass \the [src] armor."))
src.visible_message("\The [A] struggles to bypass \the [src] armor")
pass_damage_reduc_mod = temp_fail_penetration_value //This will apply to reduce damage to 2/3 or 66% by default
else
src.occupant_message(span_notice("\The [A] manages to pierce \the [src] armor."))
// src.visible_message("\The [A] manages to pierce \the [src] armor")
pass_damage_reduc_mod = 1
for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment)
pass_damage = ME.handle_ranged_contact(A, pass_damage)
pass_damage = (pass_damage*pass_damage_reduc_mod)//Applying damage reduction
src.take_damage(pass_damage) //The take_damage() proc handles armor values
if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage.
src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
return
/obj/mecha/bullet_act(var/obj/item/projectile/Proj) //wrapper
if(istype(Proj, /obj/item/projectile/test))
var/obj/item/projectile/test/Test = Proj
Test.hit |= occupant // Register a hit on the occupant, for things like turrets, or in simple-mob cases stopping friendly fire in firing line mode.
return
src.mecha_log_message("Hit by projectile. Type: [Proj.name]([Proj.check_armour]).",1)
call((proc_res["dynbulletdamage"]||src), "dynbulletdamage")(Proj) //calls equipment
..()
return
/obj/mecha/proc/dynbulletdamage(var/obj/item/projectile/Proj)
var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR]
var/temp_deflect_chance = deflect_chance
//var/temp_damage_minimum = damage_minimum //CHOMPremove
//var/temp_minimum_penetration = minimum_penetration //CHOMPremove
var/temp_fail_penetration_value = fail_penetration_value
if(!ArmC)
temp_deflect_chance = 0
//temp_damage_minimum = 0 //CHOMPremove
//temp_minimum_penetration = 0 //CHOMPremove
temp_fail_penetration_value = 1
else
temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0))
//temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) //CHOMPremove
//temp_minimum_penetration = round(ArmC.get_efficiency() * ArmC.minimum_penetration) //CHOMPremove
temp_fail_penetration_value = round(ArmC.get_efficiency() * ArmC.fail_penetration_value)
if(prob(temp_deflect_chance))
src.occupant_message(span_notice("The armor deflects incoming projectile."))
src.visible_message("The [src.name] armor deflects the projectile")
src.log_append_to_last("Armor saved.")
return
if(Proj.damage_type == HALLOSS)
use_power(Proj.agony * 5)
if(!(Proj.nodamage))
var/ignore_threshold
if(istype(Proj, /obj/item/projectile/beam/pulse)) //ATM, this is literally only for the pulse rifles used mostly by deathsquads.
ignore_threshold = 1
var/pass_damage = Proj.damage
var/pass_damage_reduc_mod
for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment)
pass_damage = ME.handle_projectile_contact(Proj, pass_damage)
if(pass_damage < damage_minimum)//too pathetic to really damage you. //CHOMPedit temp_damage_minimum -> damage_minimum
src.occupant_message(span_notice("The armor deflects incoming projectile."))
src.visible_message("The [src.name] armor deflects\the [Proj]")
return
else if(Proj.armor_penetration < minimum_penetration) //If you don't have enough pen, you won't do full damage //CHOMPedit temp_minimum_penetration -> damage_minimum
src.occupant_message(span_notice("\The [Proj] struggles to pierce \the [src] armor."))
src.visible_message("\The [Proj] struggles to pierce \the [src] armor")
pass_damage_reduc_mod = temp_fail_penetration_value //This will apply to reduce damage to 2/3 or 66% by default
else //You go through completely because you use AP. Nice.
src.occupant_message(span_notice("\The [Proj] manages to pierce \the [src] armor."))
// src.visible_message("\The [Proj] manages to pierce \the [src] armor")
pass_damage_reduc_mod = 1
pass_damage = (pass_damage_reduc_mod*pass_damage)//Apply damage reduction before usage.
//CHOMPEdit Start we can spark even when taking no damage. But don't check after a proc that might have deleted this
if(prob(25))
spark_system.start()
src.take_damage(pass_damage, Proj.check_armour) //The take_damage() proc handles armor values
//CHOMPEdit End
if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage.
src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),ignore_threshold)
//AP projectiles have a chance to cause additional damage
if(Proj.penetrating)
var/distance = get_dist(Proj.starting, get_turf(loc))
var/hit_occupant = 1 //only allow the occupant to be hit once
for(var/i in 1 to min(Proj.penetrating, round(Proj.damage/15)))
if(src.occupant && hit_occupant && prob(20))
Proj.attack_mob(src.occupant, distance)
hit_occupant = 0
else
if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage.
src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT), 1)
Proj.penetrating--
if(prob(15))
break //give a chance to exit early
Proj.on_hit(src) //on_hit just returns if it's argument is not a living mob so does this actually do anything?
return
//This refer to whenever you are caught in an explosion.
/obj/mecha/ex_act(severity)
var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR]
var/temp_deflect_chance = deflect_chance
if(!ArmC)
temp_deflect_chance = 0
else
temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0))
src.mecha_log_message("Affected by explosion of severity: [severity].",1)
if(prob(temp_deflect_chance))
severity++
src.log_append_to_last("Armor saved, changing severity to [severity].")
switch(severity)
if(1.0)
src.take_damage(initial(src.health), "bomb")
if(2.0)
if (prob(30))
src.take_damage(initial(src.health), "bomb")
else
src.take_damage(initial(src.health)/2, "bomb")
src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1)
if(3.0)
if (prob(5))
qdel(src)
else
src.take_damage(initial(src.health)/5, "bomb")
src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1)
return
/*Will fix later -Sieve
/obj/mecha/attack_blob(mob/user as mob)
src.mecha_log_message("Attack by blob. Attacker - [user].",1)
if(!prob(src.deflect_chance))
src.take_damage(6)
src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
playsound(src, 'sound/effects/blobattack.ogg', 50, 1, -1)
to_chat(user, span_danger("You smash at the armored suit!"))
for (var/mob/V in viewers(src))
if(V.client && !(V.blinded))
V.show_message(span_danger("\The [user] smashes against [src.name]'s armor!"), 1)
else
src.log_append_to_last("Armor saved.")
playsound(src, 'sound/effects/blobattack.ogg', 50, 1, -1)
to_chat(user, span_warning("Your attack had no effect!"))
src.occupant_message(span_warning("\The [user]'s attack is stopped by the armor."))
for (var/mob/V in viewers(src))
if(V.client && !(V.blinded))
V.show_message(span_warning("\The [user] rebounds off the [src.name] armor!"), 1)
return
*/
/obj/mecha/emp_act(severity, recursive)
if(get_charge())
use_power((cell.charge/2)/severity)
take_damage(50 / severity,"energy")
src.mecha_log_message("EMP detected",1)
if(prob(80))
check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1)
return
/obj/mecha/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
if(exposed_temperature>src.max_temperature)
src.mecha_log_message("Exposed to dangerous temperature.",1)
src.take_damage(5,"fire") //The take_damage() proc handles armor values
src.check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL))
return
/obj/mecha/proc/dynattackby(obj/item/W as obj, mob/user as mob)
user.setClickCooldown(user.get_attack_speed(W))
src.mecha_log_message("Attacked by [W]. Attacker - [user]")
var/pass_damage_reduc_mod //Modifer for failing to bring AP.
var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR]
var/temp_deflect_chance = deflect_chance
//var/temp_damage_minimum = damage_minimum //CHOMPremove
//var/temp_minimum_penetration = minimum_penetration //CHOMPremove
var/temp_fail_penetration_value = fail_penetration_value
if(!ArmC)
temp_deflect_chance = 0
//temp_damage_minimum = 0 //CHOMPremove
//temp_minimum_penetration = 0 //CHOMPremove
temp_fail_penetration_value = 1
else
temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0))
//temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) //CHOMPremove
//temp_minimum_penetration = round(ArmC.get_efficiency() * ArmC.minimum_penetration) //CHOMPremove
temp_fail_penetration_value = round(ArmC.get_efficiency() * ArmC.fail_penetration_value)
if(prob(temp_deflect_chance)) //Does your attack get deflected outright.
src.occupant_message(span_notice("\The [W] bounces off [src.name]."))
to_chat(user, span_danger("\The [W] bounces off [src.name]."))
src.log_append_to_last("Armor saved.")
else if(W.force < damage_minimum) //Is your attack too PATHETIC to do anything. 3 damage to a person shouldn't do anything to a mech. //CHOMPedit temp_damage_minimum -> damage_minumum
src.occupant_message(span_notice("\The [W] bounces off the armor."))
src.visible_message(span_infoplain("\The [W] bounces off \the [src] armor"))
return
else if(W.armor_penetration < minimum_penetration) //If you don't have enough pen, you won't do full damage ////CHOMPedit temp_minimum_penetration -> minimum_penetration
src.occupant_message(span_notice("\The [W] struggles to bypass \the [src] armor."))
src.visible_message(span_infoplain("\The [W] struggles to bypass \the [src] armor"))
pass_damage_reduc_mod = temp_fail_penetration_value //This will apply to reduce damage to 2/3 or 66% by default
else
pass_damage_reduc_mod = 1 //Just making sure.
src.occupant_message(span_warning(span_red(span_bold("[user] hits [src] with [W]."))))
user.visible_message(span_warning(span_red(span_bold("[user] hits [src] with [W]."))), span_danger(span_red(span_bold("You hit [src] with [W]."))))
var/pass_damage = W.force
pass_damage = (pass_damage*pass_damage_reduc_mod) //Apply the reduction of damage from not having enough armor penetration. This is not regular armor values at play.
for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment)
pass_damage = ME.handle_projectile_contact(W, user, pass_damage)
src.take_damage(pass_damage,W.damtype) //The take_damage() proc handles armor values
if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage.
src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
return
//////////////////////
////// AttackBy //////
//////////////////////
/obj/mecha/attackby(obj/item/W as obj, mob/user as mob)
if(istype(W, /obj/item/mmi))
if(mmi_move_inside(W,user))
to_chat(user, "[src]-MMI interface initialized successfuly")
else
to_chat(user, "[src]-MMI interface initialization failed.")
return
if(istype(W, /obj/item/robotanalyzer))
var/obj/item/robotanalyzer/RA = W
RA.do_scan(src, user)
return
if(istype(W, /obj/item/mecha_parts/mecha_equipment))
var/obj/item/mecha_parts/mecha_equipment/E = W
spawn()
if(E.can_attach(src))
user.drop_item()
E.attach(src)
user.visible_message("[user] attaches [W] to [src]", "You attach [W] to [src]")
else
to_chat(user, "You were unable to attach [W] to [src]")
return
if(istype(W, /obj/item/mecha_parts/component) && state == MECHA_CELL_OUT)
var/obj/item/mecha_parts/component/MC = W
spawn()
if(MC.attach(src))
user.drop_item()
MC.forceMove(src)
user.visible_message("[user] installs \the [W] in \the [src]", "You install \the [W] in \the [src].")
return
if(istype(W, /obj/item/card/robot))
var/obj/item/card/robot/RoC = W
return attackby(RoC.dummy_card, user)
if(istype(W, /obj/item/card/id)||istype(W, /obj/item/pda))
if(add_req_access || maint_access)
if(internals_access_allowed(user))
var/obj/item/card/id/id_card
if(istype(W, /obj/item/card/id))
id_card = W
else
var/obj/item/pda/pda = W
id_card = pda.id
output_maintenance_dialog(id_card, user)
return
else
to_chat(user, span_warning("Invalid ID: Access denied."))
else
to_chat(user, span_warning("Maintenance protocols disabled by operator."))
else if(W.has_tool_quality(TOOL_WRENCH))
if(state==MECHA_BOLTS_SECURED)
state = MECHA_PANEL_LOOSE
to_chat(user, "You undo the securing bolts.")
else if(state==MECHA_PANEL_LOOSE)
state = MECHA_BOLTS_SECURED
to_chat(user, "You tighten the securing bolts.")
return
else if(W.has_tool_quality(TOOL_CROWBAR))
if(state==MECHA_PANEL_LOOSE)
state = MECHA_CELL_OPEN
to_chat(user, "You open the hatch to the power unit")
else if(state==MECHA_CELL_OPEN)
state=MECHA_PANEL_LOOSE
to_chat(user, "You close the hatch to the power unit")
else if(state==MECHA_CELL_OUT)
var/list/removable_components = list()
for(var/slot in internal_components)
var/obj/item/mecha_parts/component/MC = internal_components[slot]
if(istype(MC))
removable_components[MC.name] = MC
else
to_chat(user, span_notice("\The [src] appears to be missing \the [slot]."))
var/remove = tgui_input_list(user, "Which component do you want to pry out?", "Remove Component", removable_components)
if(!remove)
return
var/obj/item/mecha_parts/component/RmC = removable_components[remove]
RmC.detach()
return
else if(istype(W, /obj/item/stack/cable_coil))
if(state >= MECHA_CELL_OPEN && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT))
var/obj/item/stack/cable_coil/CC = W
if(CC.use(2))
clearInternalDamage(MECHA_INT_SHORT_CIRCUIT)
to_chat(user, "You replace the fused wires.")
else
to_chat(user, "There's not enough wire to finish the task.")
return
else if(W.has_tool_quality(TOOL_SCREWDRIVER))
if(hasInternalDamage(MECHA_INT_TEMP_CONTROL))
clearInternalDamage(MECHA_INT_TEMP_CONTROL)
to_chat(user, "You repair the damaged temperature controller.")
else if(state==MECHA_CELL_OPEN && src.cell)
src.cell.forceMove(src.loc)
src.cell = null
state = MECHA_CELL_OUT
to_chat(user, "You unscrew and pry out the powercell.")
src.mecha_log_message("Powercell removed")
else if(state==MECHA_CELL_OUT && src.cell)
state=MECHA_CELL_OPEN
to_chat(user, "You screw the cell in place")
return
else if(istype(W, /obj/item/multitool))
if(state>=MECHA_CELL_OPEN && src.occupant)
to_chat(user, "You attempt to eject the pilot using the maintenance controls.")
if(src.occupant.stat)
src.go_out()
src.mecha_log_message("[src.occupant] was ejected using the maintenance controls.")
else
to_chat(user, span_warning("Your attempt is rejected."))
src.occupant_message(span_warning("An attempt to eject you was made using the maintenance controls."))
src.mecha_log_message("Eject attempt made using maintenance controls - rejected.")
return
else if(istype(W, /obj/item/cell))
if(state==MECHA_CELL_OUT)
if(!src.cell)
to_chat(user, "You install the powercell")
user.drop_item()
W.forceMove(src)
src.cell = W
src.mecha_log_message("Powercell installed")
else
to_chat(user, "There's already a powercell installed.")
return
else if(W.has_tool_quality(TOOL_WELDER) && user.a_intent != I_HURT)
var/obj/item/weldingtool/WT = W.get_welder()
var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL]
var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR]
if (WT.remove_fuel(0,user))
if (hasInternalDamage(MECHA_INT_TANK_BREACH))
clearInternalDamage(MECHA_INT_TANK_BREACH)
to_chat(user, span_notice("You repair the damaged gas tank."))
else
return
if((src.health<initial(src.health)) || (HC.integrity<HC.max_integrity) || (AC.integrity<AC.max_integrity))
if(src.health<initial(src.health))
to_chat(user, span_notice("You repair some damage to [src.name]."))
src.health += min(10, initial(src.health)-src.health)
update_damage_alerts()
else if(HC.integrity<HC.max_integrity)
to_chat(user, span_notice("You repair some damage to [HC.name]."))
HC.integrity += min(10, HC.max_integrity-HC.integrity)
update_damage_alerts()
else if(AC.integrity<AC.max_integrity)
to_chat(user, span_notice("You repair some damage to [AC.name]."))
AC.integrity += min(10, AC.max_integrity-AC.integrity)
update_damage_alerts()
else
to_chat(user, "The [src.name] is at full integrity")
return
else if(istype(W, /obj/item/mecha_parts/mecha_tracking))
user.drop_from_inventory(W)
W.forceMove(src)
user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src]")
return
else if(istype(W,/obj/item/stack/nanopaste))
if(state >= MECHA_PANEL_LOOSE)
var/obj/item/stack/nanopaste/NP = W
for(var/slot in internal_components)
var/obj/item/mecha_parts/component/C = internal_components[slot]
if(!C)
to_chat(user, span_notice("There are no components installed!"))
return
if(C.integrity >= C.max_integrity)
to_chat(user, span_notice("\The [C] does not require repairs."))
else if(C.integrity < C.max_integrity)
to_chat(user, span_notice("You start to repair damage to \the [C]."))
while(C.integrity < C.max_integrity && NP)
if(do_after(user, 1 SECOND, target = src))
NP.use(1)
C.adjust_integrity(NP.mech_repair)
if(C.integrity >= C.max_integrity)
to_chat(user, span_notice("You finish repairing \the [C]."))
break
else if(NP.amount == 0)
to_chat(user, span_warning("Insufficient nanopaste to complete repairs!"))
break
return
else
to_chat(user, span_notice("You can't reach \the [src]'s internal components."))
return
else
call((proc_res["dynattackby"]||src), "dynattackby")(W,user)
/*
src.mecha_log_message("Attacked by [W]. Attacker - [user]")
if(prob(src.deflect_chance))
to_chat(user, span_warning("\The [W] bounces off [src.name] armor."))
src.log_append_to_last("Armor saved.")
/*
for (var/mob/V in viewers(src))
if(V.client && !(V.blinded))
V.show_message("The [W] bounces off [src.name] armor.", 1)
*/
else
src.occupant_message(span_boldwarning("[user] hits [src] with [W].")))
user.visible_message(span_boldwarning("[user] hits [src] with [W]."))", span_boldwarning("You hit [src] with [W].")))
src.take_damage(W.force,W.damtype)
src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
*/
return
/*
/obj/mecha/attack_ai(var/mob/living/silicon/ai/user as mob)
if(!isAI(user))
return
var/output = {"<b>Assume direct control over [src]?</b>
<a href='byond://?src=\ref[src];ai_take_control=\ref[user];duration=3000'>Yes</a><br>
"}
user << browse("<html>[output]</html>", "window=mecha_attack_ai")
return
*/
///////////////////////////////
//////// Brain Stuff ////////
///////////////////////////////
/obj/mecha/proc/mmi_move_inside(var/obj/item/mmi/mmi_as_oc as obj,mob/user as mob)
if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client)
to_chat(user, "Consciousness matrix not detected.")
return 0
else if(mmi_as_oc.brainmob.stat)
to_chat(user, "Brain activity below acceptable level.")
return 0
else if(occupant)
to_chat(user, "Occupant detected.")
return 0
else if(dna && dna!=mmi_as_oc.brainmob.dna.unique_enzymes)
to_chat(user, "Genetic sequence or serial number incompatible with locking mechanism.")
return 0
//Added a message here since people assume their first click failed or something./N
// to_chat(user, "Installing MMI, please stand by.")
visible_message(span_notice("[usr] starts to insert a brain into [src.name]"))
if(enter_after(40,user))
if(!occupant)
return mmi_moved_inside(mmi_as_oc,user)
else
to_chat(user, "Occupant detected.")
else
to_chat(user, "You stop attempting to install the brain.")
return 0
/obj/mecha/proc/mmi_moved_inside(var/obj/item/mmi/mmi_as_oc as obj,mob/user as mob)
if(mmi_as_oc && (user in range(1)))
if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client)
to_chat(user, "Consciousness matrix not detected.")
return 0
else if(mmi_as_oc.brainmob.stat)
to_chat(user, "Beta-rhythm below acceptable level.")
return 0
user.drop_from_inventory(mmi_as_oc)
var/mob/brainmob = mmi_as_oc.brainmob
occupant = brainmob
brainmob.loc = src //should allow relaymove
brainmob.canmove = 1
mmi_as_oc.loc = src
mmi_as_oc.mecha = src
src.verbs += /obj/mecha/verb/eject
src.Entered(mmi_as_oc)
src.Move(src.loc)
update_icon()
set_dir(dir_in)
src.mecha_log_message("[mmi_as_oc] moved in as pilot.")
if(!hasInternalDamage())
src.occupant << sound('sound/mecha/nominal.ogg',volume=50)
update_icon()
return 1
else
return 0
/////////////////////////////////////
//////// Atmospheric stuff ////////
/////////////////////////////////////
/obj/mecha/proc/get_turf_air()
var/turf/T = get_turf(src)
if(T)
. = T.return_air()
return
/obj/mecha/remove_air(amount)
if(use_internal_tank)
return cabin_air.remove(amount)
else
var/turf/T = get_turf(src)
if(T)
return T.remove_air(amount)
return
/obj/mecha/return_air()
var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS]
if(use_internal_tank && (GC && prob(GC.get_efficiency() * 100)))
return cabin_air
return get_turf_air()
/obj/mecha/proc/return_pressure()
. = 0
var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS]
if(use_internal_tank && (GC && prob(GC.get_efficiency() * 100)))
. = cabin_air.return_pressure()
else
var/datum/gas_mixture/t_air = get_turf_air()
if(t_air)
. = t_air.return_pressure()
return
//skytodo: //No idea what you want me to do here, mate.
/obj/mecha/proc/return_temperature()
. = 0
var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS]
if(use_internal_tank && (GC && prob(GC.get_efficiency() * 100)))
. = cabin_air.temperature
else
var/datum/gas_mixture/t_air = get_turf_air()
if(t_air)
. = t_air.temperature
return
/obj/mecha/proc/connect(obj/machinery/atmospherics/portables_connector/new_port)
//Make sure not already connected to something else
if(connected_port || !new_port || new_port.connected_device)
return 0
//Make sure are close enough for a valid connection
if(!(new_port.loc in locs))
return 0
//Perform the connection
connected_port = new_port
connected_port.connected_device = src
//Actually enforce the air sharing
var/datum/pipe_network/network = connected_port.return_network(src)
if(network && !(internal_tank.return_air() in network.gases))
network.gases += internal_tank.return_air()
network.update = 1
playsound(src, 'sound/mecha/gasconnected.ogg', 50, 1)
src.mecha_log_message("Connected to gas port.")
return 1
/obj/mecha/proc/disconnect()
if(!connected_port)
return 0
var/datum/pipe_network/network = connected_port.return_network(src)
if(network)
network.gases -= internal_tank.return_air()
connected_port.connected_device = null
connected_port = null
playsound(src, 'sound/mecha/gasdisconnected.ogg', 50, 1)
src.mecha_log_message("Disconnected from gas port.")
return 1
/////////////////////////
//////// Verbs ////////
/////////////////////////
/obj/mecha/verb/connect_to_port()
set name = "Connect to port"
set category = "Exosuit Interface"
set src = usr.loc
set popup_menu = 0
if(!occupant)
return
if(usr != occupant)
return
var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS]
if(!GC)
return
for(var/turf/T in locs)
var/obj/machinery/atmospherics/portables_connector/possible_port = locate(/obj/machinery/atmospherics/portables_connector) in T
if(possible_port)
if(connect(possible_port))
occupant_message(span_notice("\The [name] connects to the port."))
verbs += /obj/mecha/verb/disconnect_from_port
verbs -= /obj/mecha/verb/connect_to_port
return
else
occupant_message(span_danger("\The [name] failed to connect to the port."))
return
else
occupant_message("Nothing happens")
/obj/mecha/verb/disconnect_from_port()
set name = "Disconnect from port"
set category = "Exosuit Interface"
set src = usr.loc
set popup_menu = 0
if(!occupant)
return
if(usr != occupant)
return
if(disconnect())
occupant_message(span_notice("[name] disconnects from the port."))
verbs -= /obj/mecha/verb/disconnect_from_port
verbs += /obj/mecha/verb/connect_to_port
else
occupant_message(span_danger("[name] is not connected to the port at the moment."))
/obj/mecha/verb/toggle_lights()
set name = "Toggle Lights"
set category = "Exosuit Interface"
set src = usr.loc
set popup_menu = 0
lights()
/obj/mecha/verb/lights()
if(usr!=occupant) return
lights = !lights
if(lights) set_light(light_range + lights_power)
else set_light(light_range - lights_power)
src.occupant_message("Toggled lights [lights?"on":"off"].")
src.mecha_log_message("Toggled lights [lights?"on":"off"].")
playsound(src, 'sound/mecha/heavylightswitch.ogg', 50, 1)
return
/obj/mecha/verb/toggle_internal_tank()
set name = "Toggle internal airtank usage"
set category = "Exosuit Interface"
set src = usr.loc
set popup_menu = 0
internal_tank()
/obj/mecha/proc/internal_tank()
if(usr!=src.occupant)
return
var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS]
if(!GC)
to_chat(occupant, span_warning("The life support systems don't seem to respond."))
return
if(!prob(GC.get_efficiency() * 100))
to_chat(occupant, span_warning("\The [GC] shudders and barks, before returning to how it was before."))
return
use_internal_tank = !use_internal_tank
src.occupant_message("Now taking air from [use_internal_tank?"internal airtank":"environment"].")
src.mecha_log_message("Now taking air from [use_internal_tank?"internal airtank":"environment"].")
playsound(src, 'sound/mecha/gasdisconnected.ogg', 30, 1)
return
/obj/mecha/verb/toggle_strafing()
set name = "Toggle strafing"
set category = "Exosuit Interface"
set src = usr.loc
set popup_menu = 0
strafing()
/obj/mecha/proc/strafing()
if(usr!=src.occupant)
return
strafing = !strafing
src.occupant_message("Toggled strafing mode [strafing?"on":"off"].")
src.mecha_log_message("Toggled strafing mode [strafing?"on":"off"].")
return
/obj/mecha/MouseDrop_T(mob/O, mob/user)
//Humans can pilot mechs.
if(!ishuman(O))
return
//Can't put other people into mechs (can comment this out if you want that to be possible)
if(O != user)
return
move_inside(user)
/obj/mecha/verb/enter()
set category = "Object"
set name = "Enter Exosuit"
set src in oview(1)
move_inside(usr)
//returns an equipment object if we have one of that type, useful since is_type_in_list won't return the object
//since is_type_in_list uses caching, this is a slower operation, so only use it if needed
/obj/mecha/proc/get_equipment(var/equip_type)
for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment)
if(istype(ME,equip_type))
return ME
return null
/obj/mecha/proc/move_inside(mob/user)
if (user.stat || !ishuman(user) || user.is_incorporeal())
return
if (user.buckled)
to_chat(user, span_warning("You can't climb into the exosuit while buckled!"))
return
src.mecha_log_message("[user] tries to move in.")
if(iscarbon(user))
var/mob/living/carbon/C = user
if(C.handcuffed)
to_chat(user, span_danger("Kinda hard to climb in while handcuffed don't you think?"))
return
if (src.occupant)
to_chat(user, span_danger("The [src.name] is already occupied!"))
src.log_append_to_last("Permission denied.")
return
/*
if (user.abiotic())
to_chat(user, span_notice("Subject cannot have abiotic items on."))
return
*/
var/passed
if(src.dna)
if(user.dna.unique_enzymes==src.dna)
passed = 1
else if(src.operation_allowed(user))
passed = 1
if(!passed)
to_chat(usr, span_warning("Access denied"))
src.log_append_to_last("Permission denied.")
return
if(isliving(user))
var/mob/living/L = user
if(L.has_buckled_mobs())
to_chat(L, span_warning("You have other entities attached to yourself. Remove them first."))
return
// to_chat(user, "You start climbing into [src.name]")
if(get_equipment(/obj/item/mecha_parts/mecha_equipment/runningboard))
visible_message(span_notice("\The [user] is instantly lifted into [src.name] by the running board!"))
moved_inside(user)
if(ishuman(occupant))
GrantActions(occupant, 1)
else
visible_message(span_infoplain(span_bold("\The [user]") + " starts to climb into [src.name]"))
if(enter_after(40, user))
if(!src.occupant)
moved_inside(user)
if(ishuman(occupant)) //Aeiou
GrantActions(occupant, 1)
else if(src.occupant != user)
to_chat(usr, "[src.occupant] was faster. Try better next time, loser.")
else
to_chat(user, "You stop entering the exosuit.")
return
/obj/mecha/proc/moved_inside(var/mob/living/carbon/human/H)
if(H && H.client && (H in range(1)))
H.stop_pulling()
H.forceMove(src)
src.occupant = H
src.add_fingerprint(H)
src.verbs += /obj/mecha/verb/eject
src.log_append_to_last("[H] moved in as pilot.")
update_icon()
if(occupant.hud_used)
minihud = new (occupant.hud_used, src)
//This part removes all the verbs if you don't have them the _possible on your mech. This is a little clunky, but it lets you just add that to any mech.
//And it's not like this 10yo code wasn't clunky before.
if(!smoke_possible) //Can't use smoke? No verb for you.
verbs -= /obj/mecha/verb/toggle_smoke
if(!thrusters_possible) //Can't use thrusters? No verb for you.
verbs -= /obj/mecha/verb/toggle_thrusters
if(!defence_mode_possible) //Do i need to explain everything?
verbs -= /obj/mecha/verb/toggle_defence_mode
if(!overload_possible)
verbs -= /obj/mecha/verb/toggle_overload
if(!zoom_possible)
verbs -= /obj/mecha/verb/toggle_zoom
if(!phasing_possible)
verbs -= /obj/mecha/verb/toggle_phasing
if(!switch_dmg_type_possible)
verbs -= /obj/mecha/verb/switch_damtype
if(!cloak_possible)
verbs -= /obj/mecha/verb/toggle_cloak
occupant.in_enclosed_vehicle = 1 //Useful for when you need to know if someone is in a mecho.
update_cell_alerts()
update_damage_alerts()
set_dir(dir_in)
playsound(src, 'sound/machines/door/windowdoor.ogg', 50, 1)
if(occupant.client && cloaked_selfimage)
occupant.client.images += cloaked_selfimage
play_entered_noise(occupant)
return 1
else
return 0
/obj/mecha/proc/play_entered_noise(var/mob/who)
if(!hasInternalDamage()) //Otherwise it's not nominal!
switch(mech_faction)
if(MECH_FACTION_NT)//The good guys category
if(firstactivation)//First time = long activation sound
firstactivation = 1
who << sound('sound/mecha/longnanoactivation.ogg',volume=50)
else
who << sound('sound/mecha/nominalnano.ogg',volume=50)
if(MECH_FACTION_SYNDI)//Bad guys
if(firstactivation)
firstactivation = 1
who << sound('sound/mecha/longsyndiactivation.ogg',volume=50)
else
who << sound('sound/mecha/nominalsyndi.ogg',volume=50)
else//Everyone else gets the normal noise
who << sound('sound/mecha/nominal.ogg',volume=50)
/obj/mecha/click_alt(mob/living/user)
if(user == occupant)
strafing()
/obj/mecha/verb/view_stats()
set name = "View Stats"
set category = "Exosuit Interface"
set src = usr.loc
set popup_menu = 0
if(usr!=src.occupant)
return
//pr_update_stats.start()
src.occupant << browse(src.get_stats_html(), "window=exosuit")
return
/*
/obj/mecha/verb/force_eject()
set category = "Object"
set name = "Force Eject"
set src in view(5)
src.go_out()
return
*/
/obj/mecha/verb/eject()
set name = "Eject"
set category = "Exosuit Interface"
set src = usr.loc
set popup_menu = 0
if(usr!=src.occupant)
return
src.go_out()
add_fingerprint(usr)
return
/obj/mecha/proc/go_out() //Eject/Exit the mech. Yes this is for easier searching.
if(!src.occupant) return
var/atom/movable/mob_container
QDEL_NULL(minihud)
if(ishuman(occupant))
mob_container = src.occupant
RemoveActions(occupant, human_occupant=1)//AEIOU
else if(istype(occupant, /mob/living/carbon/brain))
var/mob/living/carbon/brain/brain = occupant
mob_container = brain.container
else
return
if(mob_container.forceMove(src.loc))//ejecting mob container
src.mecha_log_message("[mob_container] moved out.")
occupant << browse(null, "window=exosuit")
if(occupant.client && cloaked_selfimage)
occupant.client.images -= cloaked_selfimage
if(istype(mob_container, /obj/item/mmi))
var/obj/item/mmi/mmi = mob_container
if(mmi.brainmob)
occupant.forceMove(mmi)
mmi.mecha = null
occupant.canmove = 0
occupant.clear_alert("charge")
occupant.clear_alert("mech damage")
occupant.in_enclosed_vehicle = 0
occupant = null
update_icon()
set_dir(dir_in)
verbs -= /obj/mecha/verb/eject
//src.zoom = 0
// Doesn't seem needed.
if(src.occupant && src.occupant.client)
src.occupant.client.view = world.view
src.zoom = 0
strafing = 0
return
/////////////////////////
////// Access stuff /////
/////////////////////////
/obj/mecha/proc/operation_allowed(mob/living/carbon/human/H)
for(var/ID in list(H.get_active_hand(), H.wear_id, H.belt))
if(src.check_access(ID,src.operation_req_access))
return 1
return 0
/obj/mecha/proc/internals_access_allowed(mob/living/carbon/human/H)
if(istype(H))
for(var/atom/ID in list(H.get_active_hand(), H.wear_id, H.belt))
if(src.check_access(ID,src.internals_req_access))
return 1
else if(isrobot(H))
var/mob/living/silicon/robot/R = H
if(src.check_access(R.idcard,src.internals_req_access))
return 1
return 0
/obj/mecha/check_access(obj/item/card/id/I, list/access_list)
if(!istype(access_list))
return 1
if(!access_list.len) //no requirements
return 1
if(istype(I, /obj/item/pda))
var/obj/item/pda/pda = I
I = pda.id
if(!istype(I) || !I.GetAccess()) //not ID or no access
return 0
if(access_list==src.operation_req_access)
for(var/req in access_list)
if(!(req in I.GetAccess())) //doesn't have this access
return 0
else if(access_list==src.internals_req_access)
for(var/req in access_list)
if(req in I.GetAccess())
return 1
return 1
////////////////////////////////////
///// Rendering stats window ///////
////////////////////////////////////
/obj/mecha/proc/get_stats_html()
var/output = {"<html>
<head><title>[src.name] data</title>
<style>
body {color: #00ff00; background: #000000; font-family:"Lucida Console",monospace; font-size: 12px;}
hr {border: 1px solid #0f0; color: #0f0; background-color: #0f0;}
a {padding:2px 5px;;color:#0f0;}
.wr {margin-bottom: 5px;}
.header {cursor:pointer;}
.open, .closed {background: #32CD32; color:#000; padding:1px 2px;}
.links a {margin-bottom: 2px;padding-top:3px;}
.visible {display: block;}
.hidden {display: none;}
</style>
<script language='javascript' type='text/javascript'>
[js_byjax]
[js_dropdowns]
function ticker() {
setInterval(function(){
window.location='byond://?src=\ref[src]&update_content=1';
}, 1000);
}
window.onload = function() {
dropdowns();
ticker();
}
</script>
</head>
<body>
<div id='content'>
[src.get_stats_part()]
</div>
<div id='eq_list'>
[src.get_equipment_list()]
</div>
<hr>
<div id='commands'>
[src.get_commands()]
</div>
</body>
</html>
"}
return output
/obj/mecha/proc/report_internal_damage()
var/output = null
var/list/dam_reports = list(
"[MECHA_INT_FIRE]" = span_red(span_bold("INTERNAL FIRE")),
"[MECHA_INT_TEMP_CONTROL]" = span_red(span_bold("LIFE SUPPORT SYSTEM MALFUNCTION")),
"[MECHA_INT_TANK_BREACH]" = span_red(span_bold("GAS TANK BREACH")),
"[MECHA_INT_CONTROL_LOST]" = span_red(span_bold("COORDINATION SYSTEM CALIBRATION FAILURE")) + " - <a href='byond://?src=\ref[src];repair_int_control_lost=1'>Recalibrate</a>",
"[MECHA_INT_SHORT_CIRCUIT]" = span_red(span_bold("SHORT CIRCUIT"))
)
for(var/tflag in dam_reports)
var/intdamflag = text2num(tflag)
if(hasInternalDamage(intdamflag))
output += dam_reports[tflag]
output += "<br />"
if(return_pressure() > WARNING_HIGH_PRESSURE)
output += span_red(span_bold("DANGEROUSLY HIGH CABIN PRESSURE")) + "<br />"
return output
/obj/mecha/proc/get_stats_part()
var/integrity = health/initial(health)*100
var/cell_charge = get_charge()
var/tank_pressure = internal_tank ? round(internal_tank.return_pressure(),0.01) : "None"
var/tank_temperature = internal_tank ? internal_tank.return_temperature() : "Unknown"
var/cabin_pressure = round(return_pressure(),0.01)
var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL]
var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR]
var/output = {"[report_internal_damage()]
<b>Armor Integrity: </b>[AC?"[round(AC.integrity / AC.max_integrity * 100, 0.1)]%":span_warning("ARMOR MISSING")]<br>
<b>Hull Integrity: </b>[HC?"[round(HC.integrity / HC.max_integrity * 100, 0.1)]%":span_warning("HULL MISSING")]<br>
[integrity<30? span_red(span_bold("DAMAGE LEVEL CRITICAL")) + "<br>":null]
<b>Chassis Integrity: </b> [integrity]%<br>
<b>Powercell charge: </b>[isnull(cell_charge)?"No powercell installed":"[cell.percent()]%"]<br>
<b>Air source: </b>[use_internal_tank?"Internal Airtank":"Environment"]<br>
<b>Airtank pressure: </b>[tank_pressure]kPa<br>
<b>Airtank temperature: </b>[tank_temperature]K|[tank_temperature - T0C]&deg;C<br>
<b>Cabin pressure: </b>[cabin_pressure>WARNING_HIGH_PRESSURE ? span_red("[cabin_pressure]"): cabin_pressure]kPa<br>
<b>Cabin temperature: </b> [return_temperature()]K|[return_temperature() - T0C]&deg;C<br>
<b>Lights: </b>[lights?"on":"off"]<br>
[src.dna?"<b>DNA-locked:</b><br> <span style='font-size:10px;letter-spacing:-1px;'>[src.dna]</span> \[<a href='byond://?src=\ref[src];reset_dna=1'>Reset</a>\]<br>":null]
"}
if(defence_mode_possible)
output += span_bold("Defence mode: [defence_mode?"on":"off"]") + "<br>"
if(overload_possible)
output += span_bold("Leg actuators overload: [overload?"on":"off"]") + "<br>"
if(smoke_possible)
output += span_bold("Smoke:") + " [smoke_reserve]<br>"
if(thrusters_possible)
output += span_bold("Thrusters:") + " [thrusters?"on":"off"]<br>"
//Cargo components. Keep this last otherwise it does weird alignment issues.
output += span_bold("Cargo Compartment Contents:") + "<div style=\"margin-left: 15px;\">"
if(src.cargo.len)
for(var/obj/O in src.cargo)
output += "<a href='byond://?src=\ref[src];drop_from_cargo=\ref[O]'>Unload</a> : [O]<br>"
else
output += "Nothing"
output += "</div>"
return output
/obj/mecha/proc/get_commands()
var/output = {"<div class='wr'>
<div class='header'>Electronics</div>
<div class='links'>
<a href='byond://?src=\ref[src];toggle_lights=1'>Toggle Lights</a><br>
<b>Radio settings:</b><br>
Microphone: <a href='byond://?src=\ref[src];rmictoggle=1'><span id="rmicstate">[radio.broadcasting?"Engaged":"Disengaged"]</span></a><br>
Speaker: <a href='byond://?src=\ref[src];rspktoggle=1'><span id="rspkstate">[radio.listening?"Engaged":"Disengaged"]</span></a><br>
Frequency:
<a href='byond://?src=\ref[src];rfreq=-10'>-</a>
<a href='byond://?src=\ref[src];rfreq=-2'>-</a>
<span id="rfreq">[format_frequency(radio.frequency)]</span>
<a href='byond://?src=\ref[src];rfreq=2'>+</a>
<a href='byond://?src=\ref[src];rfreq=10'>+</a><br>
</div>
</div>
<div class='wr'>
<div class='header'>Airtank</div>
<div class='links'>
<a href='byond://?src=\ref[src];toggle_airtank=1'>Toggle Internal Airtank Usage</a><br>
[(/obj/mecha/verb/disconnect_from_port in src.verbs)?"<a href='byond://?src=\ref[src];port_disconnect=1'>Disconnect from port</a><br>":null]
[(/obj/mecha/verb/connect_to_port in src.verbs)?"<a href='byond://?src=\ref[src];port_connect=1'>Connect to port</a><br>":null]
</div>
</div>
<div class='wr'>
<div class='header'>Permissions & Logging</div>
<div class='links'>
<a href='byond://?src=\ref[src];toggle_id_upload=1'><span id='t_id_upload'>[add_req_access?"L":"Unl"]ock ID upload panel</span></a><br>
<a href='byond://?src=\ref[src];toggle_maint_access=1'><span id='t_maint_access'>[maint_access?"Forbid":"Permit"] maintenance protocols</span></a><br>
<a href='byond://?src=\ref[src];dna_lock=1'>DNA-lock</a><br>
<a href='byond://?src=\ref[src];view_log=1'>View internal log</a><br>
<a href='byond://?src=\ref[src];change_name=1'>Change exosuit name</a><br>
</div>
</div>
<div id='equipment_menu'>[get_equipment_menu()]</div>
<hr>
[(/obj/mecha/verb/eject in src.verbs)?"<a href='byond://?src=\ref[src];eject=1'>Eject</a><br>":null]
"}
return output
/obj/mecha/proc/get_equipment_menu() //outputs mecha html equipment menu
var/output
if(equipment.len)
output += {"<div class='wr'>
<div class='header'>Equipment</div>
<div class='links'>"}
for(var/obj/item/mecha_parts/mecha_equipment/W in hull_equipment)
output += "Hull Module: [W.name] <a href='byond://?src=\ref[W];detach=1'>Detach</a><br>"
for(var/obj/item/mecha_parts/mecha_equipment/W in weapon_equipment)
output += "Weapon Module: [W.name] <a href='byond://?src=\ref[W];detach=1'>Detach</a><br>"
for(var/obj/item/mecha_parts/mecha_equipment/W in utility_equipment)
output += "Utility Module: [W.name] <a href='byond://?src=\ref[W];detach=1'>Detach</a><br>"
for(var/obj/item/mecha_parts/mecha_equipment/W in universal_equipment)
output += "Universal Module: [W.name] <a href='byond://?src=\ref[W];detach=1'>Detach</a><br>"
for(var/obj/item/mecha_parts/mecha_equipment/W in special_equipment)
output += "Special Module: [W.name] <a href='byond://?src=\ref[W];detach=1'>Detach</a><br>"
for(var/obj/item/mecha_parts/mecha_equipment/W in micro_utility_equipment) // VOREstation Edit - Adds micro equipent to the menu
output += "Micro Utility Module: [W.name] <a href='byond://?src=\ref[W];detach=1'>Detach</a><br>"
for(var/obj/item/mecha_parts/mecha_equipment/W in micro_weapon_equipment)
output += "Micro Weapon Module: [W.name] <a href='byond://?src=\ref[W];detach=1'>Detach</a><br>"
output += {"<b>Available hull slots:</b> [max_hull_equip-hull_equipment.len]<br>
<b>Available weapon slots:</b> [max_weapon_equip-weapon_equipment.len]<br>
<b>Available micro weapon slots:</b> [max_micro_weapon_equip-micro_weapon_equipment.len]<br>
<b>Available utility slots:</b> [max_utility_equip-utility_equipment.len]<br>
<b>Available micro utility slots:</b> [max_micro_utility_equip-micro_utility_equipment.len]<br>
<b>Available universal slots:</b> [max_universal_equip-universal_equipment.len]<br>
<b>Available special slots:</b> [max_special_equip-special_equipment.len]<br>
</div></div>
"}
return output
/obj/mecha/proc/get_equipment_list() //outputs mecha equipment list in html
if(!equipment.len)
return
var/output = span_bold("Equipment:") + "<div style=\"margin-left: 15px;\">"
for(var/obj/item/mecha_parts/mecha_equipment/MT in equipment)
output += "<div id='\ref[MT]'>[MT.get_equip_info()]</div>"
output += "</div>"
return output
/obj/mecha/proc/get_log_html()
var/output = "<html><head><title>[src.name] Log</title></head><body style='font: 13px 'Courier', monospace;'>"
for(var/list/entry in log)
output += {"<div style='font-weight: bold;'>[time2text(entry["time"],"DDD MMM DD hh:mm:ss")] [GLOB.game_year]</div>
<div style='margin-left:15px; margin-bottom:10px;'>[entry["message"]]</div>
"}
output += "</body></html>"
return output
/obj/mecha/proc/get_log_tgui()
var/list/data = list()
for(var/list/entry in log)
data.Add(list(list(
"time" = time2text(entry["time"], "DDD MMM DD hh:mm:ss"),
"year" = GLOB.game_year,
"message" = entry["message"],
)))
return data
/obj/mecha/proc/output_access_dialog(obj/item/card/id/id_card, mob/user)
if(!id_card || !user) return
var/output = {"<html>
<head><style>
h1 {font-size:15px;margin-bottom:4px;}
body {color: #00ff00; background: #000000; font-family:"Courier New", Courier, monospace; font-size: 12px;}
a {color:#0f0;}
</style>
</head>
<body>
<h1>Following keycodes are present in this system:</h1>"}
for(var/a in operation_req_access)
output += "[get_access_desc(a)] - <a href='byond://?src=\ref[src];del_req_access=[a];user=\ref[user];id_card=\ref[id_card]'>Delete</a><br>"
output += "<hr><h1>Following keycodes were detected on portable device:</h1>"
for(var/a in id_card.GetAccess())
if(a in operation_req_access) continue
var/a_name = get_access_desc(a)
if(!a_name) continue //there's some strange access without a name
output += "[a_name] - <a href='byond://?src=\ref[src];add_req_access=[a];user=\ref[user];id_card=\ref[id_card]'>Add</a><br>"
output += "<hr><a href='byond://?src=\ref[src];finish_req_access=1;user=\ref[user]'>Finish</a> " + span_red("(Warning! The ID upload panel will be locked. It can be unlocked only through Exosuit Interface.)")
output += "</body></html>"
user << browse(output, "window=exosuit_add_access")
onclose(user, "exosuit_add_access")
return
/obj/mecha/proc/output_maintenance_dialog(obj/item/card/id/id_card,mob/user)
if(!id_card || !user) return
var/maint_options = "<a href='byond://?src=\ref[src];set_internal_tank_valve=1;user=\ref[user]'>Set Cabin Air Pressure</a>"
if (locate(/obj/item/mecha_parts/mecha_equipment/tool/passenger) in contents)
maint_options += "<a href='byond://?src=\ref[src];remove_passenger=1;user=\ref[user]'>Remove Passenger</a>"
var/output = {"<html>
<head>
<style>
body {color: #00ff00; background: #000000; font-family:"Courier New", Courier, monospace; font-size: 12px;}
a {padding:2px 5px; background:#32CD32;color:#000;display:block;margin:2px;text-align:center;text-decoration:none;}
</style>
</head>
<body>
[add_req_access?"<a href='byond://?src=\ref[src];req_access=1;id_card=\ref[id_card];user=\ref[user]'>Edit operation keycodes</a>":null]
[maint_access?"<a href='byond://?src=\ref[src];maint_access=1;id_card=\ref[id_card];user=\ref[user]'>Initiate maintenance protocol</a>":null]
[(state>0) ? maint_options : ""]
</body>
</html>"}
user << browse(output, "window=exosuit_maint_console")
onclose(user, "exosuit_maint_console")
return
////////////////////////////////
/////// Messages and Log ///////
////////////////////////////////
/obj/mecha/proc/occupant_message(message as text)
if(message)
if(src.occupant && src.occupant.client)
to_chat(src.occupant, "[icon2html(src, src.occupant.client)] [message]")
return
/obj/mecha/proc/mecha_log_message(message as text,red=null)
log.len++
if(red)
message = span_red(message)
log[log.len] = list("time"=world.timeofday,"message"=message)
return log.len
/obj/mecha/proc/log_append_to_last(message as text,red=null)
var/list/last_entry = src.log[src.log.len]
if(red)
message = span_red(message)
last_entry["message"] += "<br>" + message
return
/////////////////
///// Topic /////
/////////////////
/obj/mecha/Topic(href, href_list)
..()
if(href_list["update_content"])
if(usr != src.occupant) return
send_byjax(src.occupant,"exosuit.browser","content",src.get_stats_part())
return
if(href_list["close"])
return
if(usr.stat > 0)
return
var/datum/topic_input/top_filter = new /datum/topic_input(href,href_list)
if(href_list["select_equip"])
if(usr != src.occupant) return
var/obj/item/mecha_parts/mecha_equipment/equip = top_filter.getObj("select_equip")
if(equip)
src.selected = equip
src.occupant_message("You switch to [equip].")
src.visible_message("[src] raises [equip].")
send_byjax(src.occupant,"exosuit.browser","eq_list",src.get_equipment_list())
return
if(href_list["eject"])
if(usr != src.occupant) return
src.eject()
return
if(href_list["toggle_lights"])
if(usr != src.occupant) return
src.lights()
return
/*
if(href_list["toggle_strafing"])
if(usr != src.occupant) return
src.strafing()
return*/
if(href_list["toggle_airtank"])
if(usr != src.occupant) return
src.internal_tank()
return
if (href_list["toggle_thrusters"])
src.toggle_thrusters()
if (href_list["smoke"])
src.smoke()
if (href_list["toggle_zoom"])
src.zoom()
if(href_list["toggle_defence_mode"])
src.defence_mode()
if(href_list["switch_damtype"])
src.switch_damtype()
if(href_list["phasing"])
src.phasing()
if(href_list["rmictoggle"])
if(usr != src.occupant) return
radio.broadcasting = !radio.broadcasting
send_byjax(src.occupant,"exosuit.browser","rmicstate",(radio.broadcasting?"Engaged":"Disengaged"))
return
if(href_list["rspktoggle"])
if(usr != src.occupant) return
radio.listening = !radio.listening
send_byjax(src.occupant,"exosuit.browser","rspkstate",(radio.listening?"Engaged":"Disengaged"))
return
if(href_list["rfreq"])
if(usr != src.occupant) return
var/new_frequency = (radio.frequency + top_filter.getNum("rfreq"))
if ((radio.frequency < PUBLIC_LOW_FREQ || radio.frequency > PUBLIC_HIGH_FREQ))
new_frequency = sanitize_frequency(new_frequency)
radio.set_frequency(new_frequency)
send_byjax(src.occupant,"exosuit.browser","rfreq","[format_frequency(radio.frequency)]")
return
if(href_list["port_disconnect"])
if(usr != src.occupant) return
src.disconnect_from_port()
return
if (href_list["port_connect"])
if(usr != src.occupant) return
src.connect_to_port()
return
if (href_list["view_log"])
if(usr != src.occupant) return
src.occupant << browse(src.get_log_html(), "window=exosuit_log")
onclose(occupant, "exosuit_log")
return
if (href_list["change_name"])
if(usr != src.occupant) return
var/newname = sanitizeSafe(tgui_input_text(occupant,"Choose new exosuit name","Rename exosuit",initial(name), MAX_NAME_LEN, encode = FALSE), MAX_NAME_LEN)
if(newname)
name = newname
else
tgui_alert_async(occupant, "nope.avi")
return
if (href_list["toggle_id_upload"])
if(usr != src.occupant) return
add_req_access = !add_req_access
send_byjax(src.occupant,"exosuit.browser","t_id_upload","[add_req_access?"L":"Unl"]ock ID upload panel")
return
if(href_list["toggle_maint_access"])
if(usr != src.occupant) return
if(state)
occupant_message(span_red("Maintenance protocols in effect"))
return
maint_access = !maint_access
send_byjax(src.occupant,"exosuit.browser","t_maint_access","[maint_access?"Forbid":"Permit"] maintenance protocols")
return
if(href_list["req_access"] && add_req_access)
if(!in_range(src, usr)) return
output_access_dialog(top_filter.getObj("id_card"),top_filter.getMob("user"))
return
if(href_list["maint_access"] && maint_access)
if(!in_range(src, usr)) return
var/mob/user = top_filter.getMob("user")
if(user)
if(state==MECHA_OPERATING)
state = MECHA_BOLTS_SECURED
to_chat(user, "The securing bolts are now exposed.")
else if(state==MECHA_BOLTS_SECURED)
state = MECHA_OPERATING
to_chat(user, "The securing bolts are now hidden.")
output_maintenance_dialog(top_filter.getObj("id_card"),user)
return
if(href_list["set_internal_tank_valve"] && state >=MECHA_BOLTS_SECURED)
if(!in_range(src, usr)) return
var/mob/user = top_filter.getMob("user")
if(user)
var/new_pressure = tgui_input_number(user,"Input new output pressure","Pressure setting",internal_tank_valve, round_value=FALSE)
if(new_pressure)
internal_tank_valve = new_pressure
to_chat(user, "The internal pressure valve has been set to [internal_tank_valve]kPa.")
if(href_list["remove_passenger"] && state >= MECHA_BOLTS_SECURED)
var/mob/user = top_filter.getMob("user")
var/list/passengers = list()
for (var/obj/item/mecha_parts/mecha_equipment/tool/passenger/P in contents)
if (P.occupant)
passengers["[P.occupant]"] = P
if (!passengers)
to_chat(user, span_warning("There are no passengers to remove."))
return
var/pname = tgui_input_list(user, "Choose a passenger to forcibly remove.", "Forcibly Remove Passenger", passengers)
if (!pname)
return
var/obj/item/mecha_parts/mecha_equipment/tool/passenger/P = passengers[pname]
var/mob/occupant = P.occupant
user.visible_message(span_infoplain(span_bold("\The [user]") + " begins opening the hatch on \the [P]..."), span_notice("You begin opening the hatch on \the [P]..."))
if (!do_after(user, 4 SECONDS, target = src))
return
user.visible_message(span_infoplain(span_bold("\The [user]") + " opens the hatch on \the [P] and removes [occupant]!"), span_notice("You open the hatch on \the [P] and remove [occupant]!"))
P.go_out()
P.mecha_log_message("[occupant] was removed.")
return
if(href_list["add_req_access"] && add_req_access && top_filter.getObj("id_card"))
if(!in_range(src, usr)) return
operation_req_access += top_filter.getNum("add_req_access")
output_access_dialog(top_filter.getObj("id_card"),top_filter.getMob("user"))
return
if(href_list["del_req_access"] && add_req_access && top_filter.getObj("id_card"))
if(!in_range(src, usr)) return
operation_req_access -= top_filter.getNum("del_req_access")
output_access_dialog(top_filter.getObj("id_card"),top_filter.getMob("user"))
return
if(href_list["finish_req_access"])
if(!in_range(src, usr)) return
add_req_access = 0
var/mob/user = top_filter.getMob("user")
user << browse(null,"window=exosuit_add_access")
return
if(href_list["dna_lock"])
if(usr != src.occupant) return
if(istype(occupant, /mob/living/carbon/brain))
occupant_message("You are a brain. No.")
return
if(src.occupant)
src.dna = src.occupant.dna.unique_enzymes
src.occupant_message("You feel a prick as the needle takes your DNA sample.")
return
if(href_list["reset_dna"])
if(usr != src.occupant) return
src.dna = null
if(href_list["repair_int_control_lost"])
if(usr != src.occupant) return
src.occupant_message("Recalibrating coordination system.")
src.mecha_log_message("Recalibration of coordination system started.")
var/T = src.loc
if(do_after_action(100))
if(T == src.loc)
src.clearInternalDamage(MECHA_INT_CONTROL_LOST)
src.occupant_message(span_blue("Recalibration successful."))
src.mecha_log_message("Recalibration of coordination system finished with 0 errors.")
else
src.occupant_message(span_red("Recalibration failed."))
src.mecha_log_message("Recalibration of coordination system failed with 1 error.",1)
if(href_list["drop_from_cargo"])
var/obj/O = locate(href_list["drop_from_cargo"])
if(O && (O in src.cargo))
src.occupant_message(span_notice("You unload [O]."))
O.forceMove(get_turf(src))
src.cargo -= O
var/turf/T = get_turf(O)
if(T)
T.Entered(O)
src.mecha_log_message("Unloaded [O]. Cargo compartment capacity: [cargo_capacity - src.cargo.len]")
return
//debug
/*
if(href_list["debug"])
if(href_list["set_i_dam"])
setInternalDamage(top_filter.getNum("set_i_dam"))
if(href_list["clear_i_dam"])
clearInternalDamage(top_filter.getNum("clear_i_dam"))
return
*/
/*
if (href_list["ai_take_control"])
var/mob/living/silicon/ai/AI = locate(href_list["ai_take_control"])
var/duration = text2num(href_list["duration"])
var/mob/living/silicon/ai/O = new /mob/living/silicon/ai(src)
var/cur_occupant = src.occupant
O.invisibility = INVISIBILITY_NONE
O.canmove = 1
O.name = AI.name
O.real_name = AI.real_name
O.anchored = TRUE
O.aiRestorePowerRoutine = 0
O.control_disabled = 1 // Can't control things remotely if you're stuck in a card!
O.laws = AI.laws
O.set_stat(AI.stat)
O.oxyloss = AI.getOxyLoss()
O.fireloss = AI.getFireLoss()
O.bruteloss = AI.getBruteLoss()
O.toxloss = AI.toxloss
O.updatehealth()
src.occupant = O
if(AI.mind)
AI.mind.transfer_to(O)
AI.name = "Inactive AI"
AI.real_name = "Inactive AI"
AI.icon_state = "ai-empty"
spawn(duration)
AI.name = O.name
AI.real_name = O.real_name
if(O.mind)
O.mind.transfer_to(AI)
AI.control_disabled = 0
AI.laws = O.laws
AI.oxyloss = O.getOxyLoss()
AI.fireloss = O.getFireLoss()
AI.bruteloss = O.getBruteLoss()
AI.toxloss = O.toxloss
AI.updatehealth()
qdel(O)
if (!AI.stat)
AI.icon_state = "ai"
else
AI.icon_state = "ai-crash"
src.occupant = cur_occupant
*/
///////////////////////
///// Power stuff /////
///////////////////////
/obj/mecha/proc/has_charge(amount)
return (get_charge()>=amount)
/obj/mecha/proc/get_charge()
return call((proc_res["dyngetcharge"]||src), "dyngetcharge")()
/obj/mecha/proc/dyngetcharge()//returns null if no powercell, else returns cell.charge
if(!src.cell) return
return max(0, src.cell.charge)
/obj/mecha/proc/use_power(amount)
return call((proc_res["dynusepower"]||src), "dynusepower")(amount)
/obj/mecha/proc/dynusepower(amount)
update_cell_alerts()
var/obj/item/mecha_parts/component/electrical/EC = internal_components[MECH_ELECTRIC]
if(EC)
amount = amount * (2 - EC.get_efficiency()) * EC.charge_cost_mod
else
amount *= 5
if(get_charge())
cell.use(amount)
return 1
return 0
/obj/mecha/proc/give_power(amount)
update_cell_alerts()
var/obj/item/mecha_parts/component/electrical/EC = internal_components[MECH_ELECTRIC]
if(!EC)
amount /= 4
else
amount *= EC.get_efficiency()
if(!isnull(get_charge()))
cell.give(amount)
return 1
return 0
//This is for mobs mostly.
/obj/mecha/attack_generic(var/mob/user, var/damage, var/attack_message)
var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR]
var/temp_deflect_chance = deflect_chance
//var/temp_damage_minimum = damage_minimum //CHOMPremove
if(!ArmC)
temp_deflect_chance = 1
//temp_damage_minimum = 0 //CHOMPremove
else
temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0))
//temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) CHOMPremove
user.setClickCooldown(user.get_attack_speed())
if(!damage)
return 0
src.mecha_log_message("Attacked. Attacker - [user].",1)
user.do_attack_animation(src)
if(prob(temp_deflect_chance))//Deflected
src.log_append_to_last("Armor saved.")
src.occupant_message(span_notice("\The [user]'s attack is stopped by the armor."))
visible_message(span_infoplain(span_bold("\The [user]") + " rebounds off [src.name]'s armor!"))
user.attack_log += text("\[[time_stamp()]\] [span_red("attacked [src.name]")]")
playsound(src, 'sound/weapons/slash.ogg', 50, 1, -1)
else if(damage < damage_minimum)//Pathetic damage levels just don't harm MECH. //CHOMPedit temp_damage_minimum -> damage_minimum
src.occupant_message(span_notice("\The [user]'s doesn't dent \the [src] paint."))
src.visible_message("\The [user]'s attack doesn't dent \the [src] armor")
src.log_append_to_last("Armor saved.")
playsound(src, 'sound/effects/Glasshit.ogg', 50, 1)
return
else
src.take_damage(damage) //Apply damage - The take_damage() proc handles armor values
if(damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage.
src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
visible_message(span_danger("[user] [attack_message] [src]!"))
user.attack_log += text("\[[time_stamp()]\] [span_red("attacked [src.name]")]")
return 1
/////////////////////////////////////////
//////// Mecha process() helpers ////////
/////////////////////////////////////////
/obj/mecha/proc/stop_process(process)
current_processes &= ~process
/obj/mecha/proc/start_process(process)
current_processes |= process
/////////////
/obj/mecha/cloak()
. = ..()
if(occupant && occupant.client && cloaked_selfimage)
occupant.client.images += cloaked_selfimage
/obj/mecha/uncloak()
if(occupant && occupant.client && cloaked_selfimage)
occupant.client.images -= cloaked_selfimage
return ..()
//debug
/*
/obj/mecha/verb/test_int_damage()
set name = "Test internal damage"
set category = "Exosuit Interface"
set src in view(0)
if(!occupant) return
if(usr!=occupant)
return
var/output = {"<html>
<head>
</head>
<body>
<h3>Set:</h3>
<a href='byond://?src=\ref[src];debug=1;set_i_dam=[MECHA_INT_FIRE]'>MECHA_INT_FIRE</a><br />
<a href='byond://?src=\ref[src];debug=1;set_i_dam=[MECHA_INT_TEMP_CONTROL]'>MECHA_INT_TEMP_CONTROL</a><br />
<a href='byond://?src=\ref[src];debug=1;set_i_dam=[MECHA_INT_SHORT_CIRCUIT]'>MECHA_INT_SHORT_CIRCUIT</a><br />
<a href='byond://?src=\ref[src];debug=1;set_i_dam=[MECHA_INT_TANK_BREACH]'>MECHA_INT_TANK_BREACH</a><br />
<a href='byond://?src=\ref[src];debug=1;set_i_dam=[MECHA_INT_CONTROL_LOST]'>MECHA_INT_CONTROL_LOST</a><br />
<hr />
<h3>Clear:</h3>
<a href='byond://?src=\ref[src];debug=1;clear_i_dam=[MECHA_INT_FIRE]'>MECHA_INT_FIRE</a><br />
<a href='byond://?src=\ref[src];debug=1;clear_i_dam=[MECHA_INT_TEMP_CONTROL]'>MECHA_INT_TEMP_CONTROL</a><br />
<a href='byond://?src=\ref[src];debug=1;clear_i_dam=[MECHA_INT_SHORT_CIRCUIT]'>MECHA_INT_SHORT_CIRCUIT</a><br />
<a href='byond://?src=\ref[src];debug=1;clear_i_dam=[MECHA_INT_TANK_BREACH]'>MECHA_INT_TANK_BREACH</a><br />
<a href='byond://?src=\ref[src];debug=1;clear_i_dam=[MECHA_INT_CONTROL_LOST]'>MECHA_INT_CONTROL_LOST</a><br />
</body>
</html>"}
occupant << browse(output, "window=ex_debug")
//src.health = initial(src.health)/2.2
//src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
return
*/
/obj/mecha/proc/update_cell_alerts()
if(occupant && 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)
/obj/mecha/proc/update_damage_alerts()
if(occupant)
var/integrity = health/initial(health)*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")
/obj/mecha/blob_act(var/obj/structure/blob/B)
var/datum/blob_type/blob = B?.overmind?.blob_type
if(!istype(blob))
return FALSE
var/damage = rand(blob.damage_lower, blob.damage_upper)
src.take_damage(damage, blob.damage_type)
visible_message(span_danger("\The [B] [blob.attack_verb] \the [src]!"), span_danger("[blob.attack_message_synth]!"))
playsound(src, 'sound/effects/attackblob.ogg', 50, 1)
return ..()
#undef MECHA_OPERATING
#undef MECHA_BOLTS_SECURED
#undef MECHA_PANEL_LOOSE
#undef MECHA_CELL_OPEN
#undef MECHA_CELL_OUT