Files
Aurora.3/code/game/mecha/mecha.dm
2017-12-14 12:09:47 -06:00

2149 lines
75 KiB
Plaintext

#define MECHA_INT_FIRE 1 << 0
#define MECHA_INT_TEMP_CONTROL 1 << 1
#define MECHA_INT_SHORT_CIRCUIT 1 << 2
#define MECHA_INT_TANK_BREACH 1 << 3
#define MECHA_INT_CONTROL_LOST 1 << 4
#define MECHA_PROC_MOVEMENT 1 << 0
#define MECHA_PROC_DAMAGE 1 << 1
#define MECHA_PROC_INT_TEMP 1 << 2
#define MELEE 1
#define RANGED 2
#define NOMINAL 0
#define FIRSTRUN 1
#define POWER 2
#define DAMAGE 3
#define IMAGE 4
#define WEAPONDOWN 5
/obj/mecha
name = "Mecha"
desc = "Exosuit"
icon = 'icons/mecha/mecha.dmi'
w_class = 20
density = 1 //Dense. To raise the heat.
opacity = 1 //opaque. Menacing.
anchored = 1 //no pulling around.
unacidable = 1 //and no deleting hoomans inside
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/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/deflect_chance = 10 //chance to deflect incoming projectiles, hits, or lesser the effect of ex_act.
var/r_deflect_coeff = 1
var/m_deflect_coeff = 1
//ranged and melee damage multipliers
var/r_damage_coeff = 1
var/m_damage_coeff = 1
var/rhit_power_use = 0
var/mhit_power_use = 0
// These control what toggleable processes are executed within process().
var/current_processes = MECHA_PROC_INT_TEMP
//the values in this list show how much damage will pass through, not how much will be absorbed.
var/list/damage_absorption = list("brute"=0.8,"fire"=1.2,"bullet"=0.9,"laser"=1,"energy"=1,"bomb"=1)
var/obj/item/weapon/cell/cell
var/state = 0
var/list/log = new
var/last_message = 0
var/add_req_access = 1
var/maint_access = 1
var/dna //dna-locking the mech
var/datum/effect_system/sparks/spark_system
var/lights = 0
var/lights_power = 6
//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/device/radio/radio = null
var/max_temperature = 25000
var/internal_damage_threshold = 50 //health percentage below which internal damage is possible
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 = new
var/obj/item/mecha_parts/mecha_equipment/selected
var/max_equip = 3
var/datum/mecha_events/events
var/lastcrash
var/crash_cooldown = 30
var/power_alert_status = 0
var/damage_alert_status = 0
var/noexplode = 0 // Used for cases where an exosuit is spawned and turned into wreckage
can_hold_mob = TRUE
// Warning variables.
var/sound/powerloop //Looping alert sounds played at alert 1
var/sound/damageloop
var/damage_warning_delay = 200 // Basic delay between warnings in alert status 2
var/power_warning_delay = 200 // Starts at 20 seconds but the delay will increase with each warning
var/last_power_warning = 0
var/last_damage_warning = 0
var/float_direction = 0
// Process() iterator count.
var/process_ticks = 0
/obj/mecha/drain_power(var/drain_check)
if(drain_check)
return 1
if(!cell)
return 0
return cell.drain_power(drain_check)
/obj/mecha/New()
..()
events = new
icon_state += "-open"
add_radio()
add_cabin()
add_airtank() //All mecha currently have airtanks. No need to check unless changes are made.
add_cell()
removeVerb(/obj/mecha/verb/disconnect_from_port)
log_message("[src.name] created.")
loc.Entered(src)
mechas_list += src //global mech list
narrator_message(FIRSTRUN)
spark_system = bind_spark(src, 2)
/obj/mecha/Initialize()
. = ..()
START_PROCESSING(SSfast_process, src)
/obj/mecha/Destroy()
STOP_PROCESSING(SSfast_process, src)
src.go_out()
for(var/mob/M in src) //Let's just be ultra sure
M.forceMove(loc)
if(!noexplode && prob(30))
explosion(get_turf(loc), 0, 0, 1, 3)
if(wreckage)
var/obj/effect/decal/mecha_wreckage/WR = new wreckage(loc)
WR.icon_state = "[initial_icon]-broken"
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 = 1
else
E.forceMove(loc)
E.destroy()
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()
if(cell)
qdel(cell)
if(internal_tank)
qdel(internal_tank)
equipment.Cut()
cell = null
internal_tank = null
QDEL_NULL(spark_system)
mechas_list -= src //global mech list
return ..()
// 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()
if (!(process_ticks % 16))
process_warnings()
// Max value is 16. So we let it run between [0, 16] with this.
process_ticks = (process_ticks + 1) % 17
////////////////////////
////// Helpers /////////
////////////////////////
/obj/mecha/proc/removeVerb(verb_path)
verbs -= verb_path
/obj/mecha/proc/addVerb(verb_path)
verbs += verb_path
/obj/mecha/proc/add_airtank()
internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src)
return internal_tank
/obj/mecha/proc/add_cell()
cell = new /obj/item/weapon/cell/mecha(src)
/obj/mecha/proc/add_cabin()
cabin_air = new
cabin_air.temperature = T20C
cabin_air.volume = 200
cabin_air.adjust_multi("oxygen", O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature), "nitrogen", 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_mecha(delay as num)
sleep(delay)
if(src)
return 1
return 0
/obj/mecha/proc/enter_after(delay as num, var/mob/user as mob, var/numticks = 5)
var/delayfraction = delay/numticks
var/turf/T = user.loc
for(var/i = 0, i<numticks, i++)
sleep(delayfraction)
if(!src || !user || !user.canmove || !(user.loc == T))
return 0
return 1
/obj/mecha/proc/check_for_support()
if(locate(/obj/structure/grille, orange(1, src)) || locate(/obj/structure/lattice, orange(1, src)) || locate(/turf/simulated, orange(1, src)) || locate(/turf/unsimulated, orange(1, src)))
return 1
else
return 0
/obj/mecha/examine(mob/user)
..(user)
var/integrity = health/initial(health)*100
switch(integrity)
if(85 to 100)
user << "It's fully intact."
if(65 to 85)
user << "It's slightly damaged."
if(45 to 65)
user << "It's badly damaged."
if(25 to 45)
user << "It's heavily damaged."
else
user << "It's falling apart."
if(equipment && equipment.len)
user << "It's equipped with:"
for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment)
user << "\icon[ME] [ME]"
return
/obj/mecha/proc/drop_item() //Derpfix, but may be useful in future for engineering exosuits.
return
/obj/mecha/hear_talk(mob/M as mob, text)
if(M==occupant && radio.broadcasting)
radio.talk_into(M, text)
return
////////////////////////////
///// Action processing ////
////////////////////////////
/obj/mecha/proc/click_action(atom/target,mob/user)
if(!src.occupant || src.occupant != user ) return
if(user.stat) return
if(state)
occupant_message("<font color='red'>Maintenance protocols in effect.</font>")
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))
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)
else
src.melee_action(target)
return
/obj/mecha/proc/interface_action(obj/machinery/target)
if(istype(target, /obj/machinery/access_button) && target.Adjacent(src))
src.occupant_message("<span class='notice'>Interfacing with [target].</span>")
src.log_message("Interfaced with [target].")
target.attack_hand(src.occupant)
return 1
if(istype(target, /obj/machinery/embedded_controller))
if(target in view(2,src))
src.occupant_message("<span class='notice'>Interfacing with [target].</span>")
src.log_message("Interfaced with [target].")
target.ui_interact(src.occupant)
else
src.occupant_message("<span class='notice'>Unable to interface with [target], target out of range.</span>")
return 1
return 0
/obj/mecha/contents_nano_distance(var/src_object, var/mob/living/user)
. = user.shared_living_nano_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 class='notice'>Interfacing with [src_object]...</span>")
src.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.
//Menu for physically removing things from the suit while fiddling around in the maintenance panel
/obj/mecha/proc/removal_menu(var/mob/living/user)
var/list/options = list()
if(src.cell)
options += "Power Cell"
var/obj/item/mecha_parts/mecha_tracking/beacon = locate(/obj/item/mecha_parts/mecha_tracking) in src
if (beacon)
options += "Tracking Beacon"
if (!options.len)
user << "There are no components in [src] that can be removed"
return
var/choice = input(user, "Choose a component to remove.", "Component removal") as null|anything in options
if (!choice || !user)
return
switch(choice)
if("Power Cell")
if (!cell)
return
user.put_in_hands(cell)
cell = null
user << "You unscrew and pry out the powercell."
log_message("Powercell removed.")
if ("Tracking Beacon")
if (beacon && beacon.loc == src) // safety
beacon.uninstall(user)
/obj/mecha/proc/melee_action(atom/target)
return
/obj/mecha/proc/range_action(atom/target)
return
//////////////////////////////////
//////// Movement procs ////////
//////////////////////////////////
/obj/mecha/Move()
. = ..()
if(.)
events.fireEvent("onMove",get_turf(src))
return
/obj/mecha/relaymove(mob/user,direction)
if(user != src.occupant) //While not "realistic", this piece is player friendly.
user.forceMove(get_turf(src))
user << "You climb out from [src]"
return 0
if(connected_port)
if(world.time - last_message > 20)
src.occupant_message("Unable to move while connected to the air system port.")
last_message = world.time
return 0
if(state)
occupant_message("<font color='red'>Maintenance protocols in effect.</font>")
return
return do_move(direction)
/obj/mecha/proc/do_move(direction)
if(!can_move)
return 0
if(current_processes & MECHA_PROC_MOVEMENT)
return 0
if(!has_charge(step_energy_drain))
return 0
var/move_result = 0
if(hasInternalDamage(MECHA_INT_CONTROL_LOST))
move_result = mechsteprand()
else if(src.dir!=direction)
move_result = mechturn(direction)
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.log_message("Movement control lost. Inertial movement started.")
if(do_after_mecha(step_in))
can_move = 1
return 1
return 0
/obj/mecha/proc/mechturn(direction)
set_dir(direction)
playsound(src,'sound/mecha/mechturn.ogg',40,1)
return 1
/obj/mecha/proc/mechstep(direction)
var/result = step(src,direction)
if(result)
playsound(src,'sound/mecha/mechstep.ogg',40,1)
return result
/obj/mecha/proc/mechsteprand()
var/result = step_rand(src)
if(result)
playsound(src,'sound/mecha/mechstep.ogg',40,1)
return result
/obj/mecha/Bump(var/atom/obstacle)
// src.inertia_dir = null
if(istype(obstacle, /obj))
var/obj/O = obstacle
if(istype(O, /obj/effect/portal)) //derpfix
src.anchored = 0
O.Crossed(src)
spawn(0) // countering portal teleport spawn(0), hurr
src.anchored = 1
else if(!O.anchored)
step(obstacle,src.dir)
else //I have no idea why I disabled this
obstacle.Bumped(src)
else if(istype(obstacle, /mob))
step(obstacle,src.dir)
else
obstacle.Bumped(src)
return
///////////////////////////////////
//////// Internal damage ////////
///////////////////////////////////
/obj/mecha/proc/check_for_internal_damage(var/list/possible_int_damage,var/ignore_threshold=null)
if(!islist(possible_int_damage) || isemptylist(possible_int_damage)) return
if(prob(20))
if(ignore_threshold || src.health*100/initial(src.health)<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)
if(prob(5))
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/machines/warning-buzzer.ogg',wait=0)
return
/obj/mecha/proc/clearInternalDamage(int_dam_flag)
internal_damage &= ~int_dam_flag
switch(int_dam_flag)
if(MECHA_INT_TEMP_CONTROL)
occupant_message("<font color='blue'><b>Life support system reactivated.</b></font>")
start_process(MECHA_PROC_INT_TEMP)
if(MECHA_INT_FIRE)
occupant_message("<font color='blue'><b>Internal fire extinquished.</b></font>")
if(MECHA_INT_TANK_BREACH)
occupant_message("<font color='blue'><b>Damaged internal tank has been sealed.</b></font>")
return
////////////////////////////////////////
//////// Health related procs ////////
////////////////////////////////////////
/obj/mecha/proc/take_damage(amount, type="brute")
if(amount)
var/damage = absorb_damage(amount,type)
health -= damage
update_health()
log_append_to_last("Took [damage] points of damage. Damage type: \"[type]\".",1)
return
/obj/mecha/proc/absorb_damage(damage,damage_type)
return damage*(listgetindex(damage_absorption,damage_type) || 1)
/obj/mecha/proc/hit_damage(damage, type="brute", is_melee=0)
var/power_to_use
var/damage_coeff_to_use
if(is_melee)
power_to_use = mhit_power_use
damage_coeff_to_use = m_damage_coeff
else
power_to_use = rhit_power_use
damage_coeff_to_use = r_damage_coeff
if(power_to_use && use_power(power_to_use))
take_damage(round(damage*damage_coeff_to_use), type)
start_booster_cooldown(is_melee)
return
else
start_booster_cooldown(is_melee)
take_damage(round(damage*damage_coeff_to_use), type)
return
/obj/mecha/proc/deflect_hit(is_melee=0)
var/power_to_use
var/deflect_coeff_to_use
if(is_melee)
power_to_use = mhit_power_use
deflect_coeff_to_use = m_damage_coeff
else
power_to_use = rhit_power_use
deflect_coeff_to_use = r_damage_coeff
if(power_to_use)
if(prob(src.deflect_chance*deflect_coeff_to_use))
use_power(power_to_use)
start_booster_cooldown(is_melee)
return 1
else
return 0
else
start_booster_cooldown(is_melee)
if(prob(src.deflect_chance*deflect_coeff_to_use))
return 1
return 0
/obj/mecha/proc/start_booster_cooldown(is_melee)
for(var/obj/item/mecha_parts/mecha_equipment/armor_booster/B in equipment) //Ideally this would be done by the armor booster itself; attempts weren't great for performance.
if(B.melee == is_melee && B.equip_ready)
B.set_ready_state(0)
B.do_after_cooldown()
/obj/mecha/airlock_crush(var/crush_damage)
..()
hit_damage(crush_damage, is_melee=1)
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.queue()
else
qdel(src)
return
/obj/mecha/attack_hand(mob/user as mob)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
src.log_message("Attack by hand/paw. Attacker - [user].",1)
if(istype(user,/mob/living/carbon/human))
var/mob/living/carbon/human/H = user
if(H.species.can_shred(user))
if(!deflect_hit(is_melee=1))
src.hit_damage(damage=15, is_melee=1)
src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
playsound(src.loc, 'sound/weapons/slash.ogg', 50, 1, -1)
user << "<span class='danger'>You slash at the armored suit!</span>"
visible_message("<span class='danger'>\The [user] slashes at [src.name]'s armor!</span>")
else
src.log_append_to_last("Armor saved.")
playsound(src.loc, 'sound/weapons/slash.ogg', 50, 1, -1)
user << "<span class='danger'>Your claws had no effect!</span>"
src.occupant_message("<span class='notice'>\The [user]'s claws are stopped by the armor.</span>")
visible_message("<span class='warning'>\The [user] rebounds off [src.name]'s armor!</span>")
else
user.visible_message("<span class='danger'>\The [user] hits \the [src]. Nothing happens.</span>","<span class='danger'>You hit \the [src] with no visible effect.</span>")
src.log_append_to_last("Armor saved.")
return
else if ((HULK in user.mutations) && !deflect_hit(is_melee=1))
src.hit_damage(damage=15, is_melee=1)
src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
user.visible_message("<font color='red'><b>[user] hits [src.name], doing some damage.</b></font>", "<font color='red'><b>You hit [src.name] with all your might. The metal creaks and bends.</b></font>")
else
user.visible_message("<font color='red'><b>[user] hits [src.name]. Nothing happens</b></font>","<font color='red'><b>You hit [src.name] with no visible effect.</b></font>")
src.log_append_to_last("Armor saved.")
return
/obj/mecha/hitby(atom/movable/A as mob|obj)
..()
src.log_message("Hit by [A].",1)
if(istype(A, /obj/item/mecha_parts/mecha_tracking))
A.forceMove(src)
src.visible_message("The [A] fastens firmly to [src].")
return
if(deflect_hit(is_melee=0) || istype(A, /mob))
src.occupant_message("<span class='notice'>\The [A] bounces off the armor.</span>")
src.visible_message("\The [A] bounces off \the [src] armor.")
src.log_append_to_last("Armor saved.")
if(istype(A, /mob/living))
var/mob/living/M = A
M.take_organ_damage(10)
else if(istype(A, /obj))
var/obj/O = A
if(O.throwforce)
src.hit_damage(O.throwforce, is_melee=0)
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)
if(Proj.damage_type == HALLOSS && !(src.r_deflect_coeff > 1))
use_power(Proj.agony * 5)
src.log_message("Hit by projectile. Type: [Proj.name]([Proj.check_armour]).",1)
if(deflect_hit(is_melee=0))
src.occupant_message("<span class='notice'>The armor deflects incoming projectile.</span>")
src.visible_message("The [src.name] armor deflects the projectile.")
src.log_append_to_last("Armor saved.")
return
if(!(Proj.nodamage))
var/ignore_threshold
if(istype(Proj, /obj/item/projectile/beam/pulse))
ignore_threshold = 1
src.hit_damage(Proj.damage, Proj.check_armour, is_melee=0)
if(prob(25))
spark_system.queue()
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
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
/obj/mecha/ex_act(severity)
src.log_message("Affected by explosion of severity: [severity].",1)
if(prob(src.deflect_chance))
severity++
src.log_append_to_last("Armor saved, changing severity to [severity].")
switch(severity)
if(1.0)
qdel(src)
if(2.0)
if (prob(30))
qdel(src)
else
src.take_damage(initial(src.health)/2)
src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1)
if(3.0)
if (prob(5))
qdel(src)
else
src.take_damage(initial(src.health)/5)
src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1)
return
/obj/mecha/emp_act(severity)
if(get_charge())
use_power((4000)/severity)
take_damage(30 / severity,"energy")
random_internal_damage(7/severity)
misconfigure_systems(10/severity)
src.log_message("EMP detected",1)
check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1)
return
/obj/mecha/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
if(exposed_temperature>src.max_temperature)
src.log_message("Exposed to dangerous temperature.",1)
src.take_damage(5,"fire")
src.check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL))
return
//////////////////////
////// AttackBy //////
//////////////////////
/obj/mecha/attackby(obj/item/weapon/W as obj, mob/user as mob)
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
user << "You were unable to attach [W] to [src]"
return
if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
if(add_req_access || maint_access)
if(internals_access_allowed(usr))
var/obj/item/weapon/card/id/id_card
if(istype(W, /obj/item/weapon/card/id))
id_card = W
else
var/obj/item/device/pda/pda = W
id_card = pda.id
output_maintenance_dialog(id_card, user)
return
else
user << "<span class='warning'>Invalid ID: Access denied.</span>"
else
user << "<span class='warning'>Maintenance protocols disabled by operator.</span>"
else if(iswrench(W))
if(state==1)
state = 2
user << "You undo the securing bolts."
playsound(get_turf(src), 'sound/items/Ratchet.ogg', 50, 1)
else if(state==2)
state = 1
user << "You tighten the securing bolts."
playsound(get_turf(src), 'sound/items/Ratchet.ogg', 50, 1)
return
else if(iscrowbar(W))
if(state==2)
state = 3
user << "You open the hatch to the power unit"
playsound(get_turf(src), 'sound/items/Deconstruct.ogg', 50, 1)
else if(state==3)
state=2
user << "You close the hatch to the power unit"
playsound(get_turf(src), 'sound/items/Crowbar.ogg', 50, 1)
return
else if(iscoil(W))
if(state == 3 && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT))
var/obj/item/stack/cable_coil/CC = W
if(CC.use(2))
clearInternalDamage(MECHA_INT_SHORT_CIRCUIT)
user << "You replace the fused wires."
else
user << "There's not enough wire to finish the task."
return
else if(isscrewdriver(W))
if(hasInternalDamage(MECHA_INT_TEMP_CONTROL))
clearInternalDamage(MECHA_INT_TEMP_CONTROL)
user << "You repair the damaged temperature controller."
else if(state==3)
removal_menu(user)
return
else if(ismultitool(W))
if(state>=3 && src.occupant)
user << "You attempt to eject the pilot using the maintenance controls."
if(src.occupant.stat)
src.go_out()
src.log_message("[src.occupant] was ejected using the maintenance controls.")
else
user << "<span class='warning'>Your attempt is rejected.</span>"
src.occupant_message("<span class='warning'>An attempt to eject you was made using the maintenance controls.</span>")
src.log_message("Eject attempt made using maintenance controls - rejected.")
return
else if(istype(W, /obj/item/weapon/cell))
if(state==3)
if(!src.cell)
user << "You install the powercell"
user.drop_item()
W.forceMove(src)
src.cell = W
src.log_message("Powercell installed")
else
user << "There's already a powercell installed."
return
else if(iswelder(W) && user.a_intent != I_HURT)
var/obj/item/weapon/weldingtool/WT = W
if (WT.remove_fuel(0,user))
if (hasInternalDamage(MECHA_INT_TANK_BREACH))
clearInternalDamage(MECHA_INT_TANK_BREACH)
user << "<span class='notice'>You repair the damaged gas tank.</span>"
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
else
return
if(src.health<initial(src.health))
user << "<span class='notice'>You repair some damage to [src.name].</span>"
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
src.health += min(10, initial(src.health)-src.health)
else
user << "The [src.name] is at full integrity"
return
else if(istype(W, /obj/item/mecha_parts/mecha_tracking))
var/obj/item/mecha_parts/mecha_tracking/C = W
C.install(src, user)
return
else
src.log_message("Attacked by [W]. Attacker - [user]")
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
if(deflect_hit(is_melee=1))
user << "<span class='danger'>\The [W] bounces off [src.name].</span>"
src.log_append_to_last("Armor saved.")
else
src.occupant_message("<font color='red'><b>[user] hits [src] with [W].</b></font>")
user.visible_message("<font color='red'><b>[user] hits [src] with [W].</b></font>", "<font color='red'><b>You hit [src] with [W].</b></font>")
src.hit_damage(W.force, W.damtype, is_melee=1)
src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
return
/////////////////////////////////////
//////// 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()
if(use_internal_tank)
return cabin_air
return get_turf_air()
/obj/mecha/proc/return_pressure()
. = 0
if(use_internal_tank)
. = 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
if(use_internal_tank)
. = 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 != src.loc)
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
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
src.log_message("Disconnected from gas port.")
return 1
/////////////////////////
//////// Verbs ////////
/////////////////////////
/obj/mecha/verb/crash()
set name = "Crash"
set desc = "Throw your exosuit's mass against whatever's infront of you, and try to clear a path through."
set category = "Exosuit Interface"
set src = usr.loc
var/brokesomething = 0 // true if we break anything
var/done = 0 // Set true if we fail to break something. We won't try to break anything for the rest of the proc
if(!src.occupant) return
if(usr!=src.occupant)
return
if ((world.time - lastcrash) < crash_cooldown) // prevent spamming it and breaking things too quickly
return
if (!use_power(step_energy_drain*20)) // Forcefully crashing into something costs 20x the power of taking a normal step
occupant << "<span class='warning'>[src] lacks the remaining power to do that!</span>"
return 0
//TODO: Add in a check for exosuit thrusters here after reworking them.
//Exosuits with thrusters should be able to use crash in space, and without the 0.5sec windup time
if (!check_for_support())
occupant << "<span class='warning'>The [src] has no traction! There is nothing solid in reach to launch off.</span>"
return 0
lastcrash = world.time
occupant << "<span class='warning'>You take a step back, and then...</span>"
sleep(5)
//Crashing is done in five stages
//1. We check if we can move into the tile. If so, then we just lunge forward clumsily
var/turf/target = get_step(src, dir)
if (target.Enter(src, null))
mechstep(dir)
sleep(2)
mechstep(dir)
src.visible_message("<span class='danger'>[src.name] lunges forward clumsily!</span>")
done = 1
return
//2. We check for anything blocking us from leaving the tile. IE windoors or window panes,
//and if they're present try to smash them
//Failing to break any object we crash into will return and end execution
for(var/obj/obstacle in get_turf(src))
if((obstacle.flags & ON_BORDER) && (src != obstacle))
if(!obstacle.CheckExit(src, target))
brokesomething++
if (!crash_into(obstacle))
done = 1 // If it survived the impact then we stop breaking things for this proc
//3. Now we hit the turf itself, if it's a wall
if (!done && !target.CanPass(src, target))
crash_into(target)
brokesomething++
if (!target.CanPass(src, target))
done = 1
//4. Now we search the target tile for any dense objects that also block us.
//This could be girders left behind from the wall we just destroyed
if (!done)
for (var/atom/A in target)
if (A.density && A != src && A != occupant && A.loc != src)
brokesomething++
if (!crash_into(A))
done = 1 // If it survived the impact then we stop breaking things for this proc
//If we hit any >0 number of things, whether they broke or not, we play the impact sound exactly once, and we send admin logs
if (brokesomething)
playsound(get_turf(target), 'sound/weapons/heavysmash.ogg', 100, 1)
occupant.attack_log += "\[[time_stamp()]\]<font color='red'> driving [name] crashed into [brokesomething] objects at ([target.x];[target.y];[target.z]) </font>"
msg_admin_attack("[key_name_admin(occupant)] driving [name] crashed into [brokesomething] objects at (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[target.x];Y=[target.y];Z=[target.z]'>JMP</a>)",ckey=key_name(occupant))
//5. If we get here, then we've broken through everything that could stop us
//Step forward into the tile and display a victory message!
//Its also possible to get here if we crashed against something that offered no resistance
//like an airlock that opened when bumped
//Or a mob/locker that got pushed away
//No damage will be taken in this case
if (!done && target.Enter(src, null))
if (health <= 0)
return 0 // This prevents bugginess if the exosuit breaks while crashing into stuff
mechstep(dir)
if (brokesomething)
src.visible_message("<span class='danger'>[src.name] breaks through!</span>")
return
else
//if we fail to step forward, then we do the attack animation instead
target = get_step(src, dir) // re-fetch target just incase
do_attack_animation(target)
/obj/mecha/proc/crash_into(var/atom/A)
var/aname = A.name // Cache this mainly because turfs change name when broken
var/oldtype = A.type
if (health <= 0)
return 0 // This prevents bugginess if the exosuit explodes/dies while crashing into stuff
var/damage = crash_damage(A)
if (istype(A, /mob/living))
var/mob/living/M = A
occupant.attack_log += "\[[time_stamp()]\]<font color='red'> Crashed into [key_name(M)]with exosuit [name] </font>"
M.attack_log += "\[[time_stamp()]\]<font color='orange'> Was rammed with the exosuit [name] driven by [key_name(occupant)]</font>"
msg_admin_attack("[key_name_admin(occupant)] driving [name] crashed into [key_name(M)] at (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[M.x];Y=[M.y];Z=[M.z]'>JMP</a>)",ckey=key_name(occupant),ckey_target=key_name(M) )
A.ex_act(3)
sleep(1)
if (!QDELETED(A) && A.type == oldtype) // We check if the object has been qdel'd or (for turfs) changed type
src.visible_message("<span class='danger'>[src.name] crashes into the [aname]!</span>")
take_damage(damage)
return 0 // If it survived the impact then we stop breaking things for this proc
else
take_damage(damage*0.5) // An object that breaks hurts less than one that resists the impact
return 1
/obj/mecha/proc/crash_damage(var/A)
if (istype(A, /mob/living))
var/mob/living/M = A
return min((M.mob_size / 3),2) // Crashing into a cow or cyborg hurts more than crashing into a dog
//2 is a fallback for mobs with undefined size
else if (istype(A, /obj/structure/window))
return 1.5 // windows are fragile
else if (istype(A, /obj/structure/grille))
return 3 // Grilles are flexible and flimsy structures
else if (istype(A, /obj/machinery))
return 3
else if (istype(A, /obj/structure))
return 6
else if (istype(A, /turf)) // walls are tough
return 8
else
return 3
/obj/mecha/verb/connect_to_port()
set name = "Connect to port"
set category = "Exosuit Interface"
set src = usr.loc
set popup_menu = 0
if(!src.occupant) return
if(usr!=src.occupant)
return
var/obj/machinery/atmospherics/portables_connector/possible_port = locate(/obj/machinery/atmospherics/portables_connector/) in loc
if(possible_port)
if(connect(possible_port))
src.occupant_message("<span class='notice'>\The [name] connects to the port.</span>")
src.verbs += /obj/mecha/verb/disconnect_from_port
src.verbs -= /obj/mecha/verb/connect_to_port
return
else
src.occupant_message("<span class='danger'>\The [name] failed to connect to the port.</span>")
return
else
src.occupant_message("Nothing happens")
/obj/mecha/verb/disconnect_from_port()
set name = "Disconnect from port"
set category = "Exosuit Interface"
set src = usr.loc
set popup_menu = 0
if(!src.occupant) return
if(usr!=src.occupant)
return
if(disconnect())
src.occupant_message("<span class='notice'>[name] disconnects from the port.</span>")
src.verbs -= /obj/mecha/verb/disconnect_from_port
src.verbs += /obj/mecha/verb/connect_to_port
else
src.occupant_message("<span class='danger'>[name] is not connected to the port at the moment.</span>")
/obj/mecha/verb/toggle_lights()
set name = "Toggle Lights"
set category = "Exosuit Interface"
set src = usr.loc
set popup_menu = 0
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"].")
log_message("Toggled lights [lights?"on":"off"].")
return
/obj/mecha/verb/toggle_internal_tank()
set name = "Toggle internal airtank usage."
set category = "Exosuit Interface"
set src = usr.loc
set popup_menu = 0
if(usr!=src.occupant)
return
use_internal_tank = !use_internal_tank
src.occupant_message("Now taking air from [use_internal_tank?"internal airtank":"environment"].")
src.log_message("Now taking air from [use_internal_tank?"internal airtank":"environment"].")
return
/obj/mecha/verb/move_inside()
set category = "Object"
set name = "Enter Exosuit"
set src in oview(1)
if (usr.stat || !ishuman(usr))
return
if (usr.buckled)
usr << "<span class='warning'>You can't climb into the exosuit while buckled!</span>"
return
src.log_message("[usr] tries to move in.")
if(iscarbon(usr))
var/mob/living/carbon/C = usr
if(C.handcuffed)
usr << "<span class='danger'>Kinda hard to climb in while handcuffed don't you think?</span>"
return
if (src.occupant)
usr << "<span class='danger'>The [src.name] is already occupied!</span>"
src.log_append_to_last("Permission denied.")
return
var/passed
if(src.dna)
if(usr.dna.unique_enzymes==src.dna)
passed = 1
else if(src.operation_allowed(usr))
passed = 1
if(!passed)
usr << "<span class='warning'>Access denied</span>"
src.log_append_to_last("Permission denied.")
return
for(var/mob/living/carbon/slime/M in range(1,usr))
if(M.Victim == usr)
usr << "You're too busy getting your life sucked out of you."
return
visible_message("<span class='notice'>\The [usr] starts to climb into [src.name]</span>")
if(enter_after(40,usr))
if(!src.occupant)
moved_inside(usr)
else if(src.occupant!=usr)
usr << "[src.occupant] was faster. Try better next time, loser."
else
usr << "You stop entering the exosuit."
return
/obj/mecha/proc/moved_inside(var/mob/living/carbon/human/H as mob)
if(H && H.client && H in range(1))
H.reset_view(src)
H.stop_pulling()
H.forceMove(src)
src.occupant = H
src.add_fingerprint(H)
src.forceMove(src.loc)
src.log_append_to_last("[H] moved in as pilot.")
src.icon_state = src.reset_icon()
set_dir(dir_in)
resume_sounds()
playsound(src, 'sound/machines/windowdoor.ogg', 50, 1)
if(cell && !hasInternalDamage() && cell.charge >= cell.maxcharge && health >= initial(health))
narrator_message(NOMINAL)
return 1
else
return 0
/obj/mecha/verb/view_stats()
set name = "View Stats"
set category = "Exosuit Interface"
set src = usr.loc
set popup_menu = 0
if(usr!=src.occupant)
return
src.occupant << browse(src.get_stats_html(), "window=exosuit")
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()
if(!src.occupant) return
stop_sound(1) // We stop any looping warning sounds
stop_sound(2)
var/atom/movable/mob_container
if(ishuman(occupant))
mob_container = src.occupant
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.log_message("[mob_container] moved out.")
occupant.reset_view()
src.occupant << browse(null, "window=exosuit")
if(istype(mob_container, /obj/item/device/mmi))
var/obj/item/device/mmi/mmi = mob_container
if(mmi.brainmob)
occupant.loc = mmi
mmi.mecha = null
src.occupant.canmove = 0
src.verbs += /obj/mecha/verb/eject
src.occupant = null
src.icon_state = src.reset_icon()+"-open"
src.set_dir(dir_in)
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)
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
return 0
/obj/mecha/check_access(obj/item/weapon/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/device/pda))
var/obj/item/device/pda/pda = I
I = pda.id
if(!istype(I) || !I.access) //not ID or no access
return 0
if(access_list==src.operation_req_access)
for(var/req in access_list)
if(!(req in I.access)) //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.access)
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]" = "<font color='red'><b>INTERNAL FIRE</b></font>",
"[MECHA_INT_TEMP_CONTROL]" = "<font color='red'><b>LIFE SUPPORT SYSTEM MALFUNCTION</b></font>",
"[MECHA_INT_TANK_BREACH]" = "<font color='red'><b>GAS TANK BREACH</b></font>",
"[MECHA_INT_CONTROL_LOST]" = "<font color='red'><b>COORDINATION SYSTEM CALIBRATION FAILURE</b></font> - <a href='?src=\ref[src];repair_int_control_lost=1'>Recalibrate</a>",
"[MECHA_INT_SHORT_CIRCUIT]" = "<font color='red'><b>SHORT CIRCUIT</b></font>"
)
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 += "<font color='red'><b>DANGEROUSLY HIGH CABIN PRESSURE</b></font><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" //Results in type mismatch if there is no tank.
var/cabin_pressure = round(return_pressure(),0.01)
var/output = {"[report_internal_damage()]
[integrity<30?"<font color='red'><b>DAMAGE LEVEL CRITICAL</b></font><br>":null]
<b>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>[isnum(tank_temperature) ? "[tank_temperature]K|[tank_temperature - T0C]&deg;C" : "Unknown"]<br>
<b>Cabin pressure: </b>[cabin_pressure>WARNING_HIGH_PRESSURE ? "<font color='red'>[cabin_pressure]</font>": 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='?src=\ref[src];reset_dna=1'>Reset</a>\]<br>":null]
"}
return output
/obj/mecha/proc/get_commands()
var/output = {"<div class='wr'>
<div class='header'>Electronics</div>
<div class='links'>
<a href='?src=\ref[src];toggle_lights=1'>Toggle Lights</a><br>
<b>Radio settings:</b><br>
Microphone: <a href='?src=\ref[src];rmictoggle=1'><span id="rmicstate">[radio.broadcasting?"Engaged":"Disengaged"]</span></a><br>
Speaker: <a href='?src=\ref[src];rspktoggle=1'><span id="rspkstate">[radio.listening?"Engaged":"Disengaged"]</span></a><br>
Frequency:
<a href='?src=\ref[src];rfreq=-10'>-</a>
<a href='?src=\ref[src];rfreq=-2'>-</a>
<span id="rfreq">[format_frequency(radio.frequency)]</span>
<a href='?src=\ref[src];rfreq=2'>+</a>
<a href='?src=\ref[src];rfreq=10'>+</a><br>
</div>
</div>
<div class='wr'>
<div class='header'>Airtank</div>
<div class='links'>
<a href='?src=\ref[src];toggle_airtank=1'>Toggle Internal Airtank Usage</a><br>
[(/obj/mecha/verb/disconnect_from_port in src.verbs)?"<a href='?src=\ref[src];port_disconnect=1'>Disconnect from port</a><br>":null]
[(/obj/mecha/verb/connect_to_port in src.verbs)?"<a href='?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='?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='?src=\ref[src];toggle_maint_access=1'><span id='t_maint_access'>[maint_access?"Forbid":"Permit"] maintenance protocols</span></a><br>
<a href='?src=\ref[src];dna_lock=1'>DNA-lock</a><br>
<a href='?src=\ref[src];view_log=1'>View internal log</a><br>
<a href='?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='?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 equipment)
output += "[W.name] <a href='?src=\ref[W];detach=1'>Detach</a><br>"
output += "<b>Available equipment slots:</b> [max_equip-equipment.len]"
output += "</div></div>"
return output
/obj/mecha/proc/get_equipment_list() //outputs mecha equipment list in html
if(!equipment.len)
return
var/output = "<b>Equipment:</b><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")] [game_year]</div>
<div style='margin-left:15px; margin-bottom:10px;'>[entry["message"]]</div>
"}
output += "</body></html>"
return output
/obj/mecha/proc/output_access_dialog(obj/item/weapon/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='?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.access)
if(a in operation_req_access) continue
var/a_name = get_access_desc(a)
if(!a_name) continue //there's some strange access without a name
output += "[a_name] - <a href='?src=\ref[src];add_req_access=[a];user=\ref[user];id_card=\ref[id_card]'>Add</a><br>"
output += "<hr><a href='?src=\ref[src];finish_req_access=1;user=\ref[user]'>Finish</a> <font color='red'>(Warning! The ID upload panel will be locked. It can be unlocked only through Exosuit Interface.)</font>"
output += "</body></html>"
user << browse(output, "window=exosuit_add_access")
onclose(user, "exosuit_add_access")
return
/obj/mecha/proc/output_maintenance_dialog(obj/item/weapon/card/id/id_card,mob/user)
if(!id_card || !user) return
var/maint_options = "<a href='?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='?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='?src=\ref[src];req_access=1;id_card=\ref[id_card];user=\ref[user]'>Edit operation keycodes</a>":null]
[maint_access?"<a href='?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)
src.occupant << "\icon[src] [message]"
return
/obj/mecha/proc/log_message(message as text,red=null)
log.len++
log[log.len] = list("time"=world.timeofday,"message"="[red?"<font color='red'>":null][message][red?"</font>":null]")
return log.len
/obj/mecha/proc/log_append_to_last(message as text,red=null)
var/list/last_entry = src.log[src.log.len]
last_entry["message"] += "<br>[red?"<font color='red'>":null][message][red?"</font>":null]"
return
/obj/mecha/proc/narrator_message(var/state)
var/file
switch(state)
if(NOMINAL)
file = 'sound/mecha/nominalnano.ogg'
if(FIRSTRUN)
file = 'sound/mecha/LongNanoActivation.ogg'
if(POWER)
file = 'sound/mecha/lowpowernano.ogg'
if(DAMAGE)
file = 'sound/mecha/critdestrnano.ogg'
if(WEAPONDOWN)
file = 'sound/mecha/weapdestrnano.ogg'
else
playsound(src.loc, file, 100, 0, -6.6, environment=1) // using padded room environment to reduce echo
/////////////////
///// Topic /////
/////////////////
/obj/mecha/Topic(href, href_list)
..()
if(href_list["update_content"])
if(usr != src.occupant) return
send_byjax(src.occupant,"exosuit.browser","content",src.get_stats_part())
return
if(href_list["close"])
return
if(usr.stat > 0)
return
var/datum/topic_input/filter = new /datum/topic_input(href,href_list)
if(href_list["select_equip"])
if(usr != src.occupant) return
var/obj/item/mecha_parts/mecha_equipment/equip = filter.getObj("select_equip")
if(equip)
src.selected = equip
src.occupant_message("You switch to [equip]")
src.visible_message("[src] raises [equip]")
send_byjax(src.occupant,"exosuit.browser","eq_list",src.get_equipment_list())
return
if(href_list["eject"])
if(usr != src.occupant) return
src.eject()
return
if(href_list["toggle_lights"])
if(usr != src.occupant) return
src.toggle_lights()
return
if(href_list["toggle_airtank"])
if(usr != src.occupant) return
src.toggle_internal_tank()
return
if(href_list["rmictoggle"])
if(usr != src.occupant) return
radio.broadcasting = !radio.broadcasting
send_byjax(src.occupant,"exosuit.browser","rmicstate",(radio.broadcasting?"Engaged":"Disengaged"))
return
if(href_list["rspktoggle"])
if(usr != src.occupant) return
radio.listening = !radio.listening
send_byjax(src.occupant,"exosuit.browser","rspkstate",(radio.listening?"Engaged":"Disengaged"))
return
if(href_list["rfreq"])
if(usr != src.occupant) return
var/new_frequency = (radio.frequency + filter.getNum("rfreq"))
if ((radio.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(input(occupant,"Choose new exosuit name","Rename exosuit",initial(name)) as text, MAX_NAME_LEN)
if(newname)
name = newname
else
alert(occupant, "nope.avi")
return
if (href_list["toggle_id_upload"])
if(usr != src.occupant) return
add_req_access = !add_req_access
send_byjax(src.occupant,"exosuit.browser","t_id_upload","[add_req_access?"L":"Unl"]ock ID upload panel")
return
if(href_list["toggle_maint_access"])
if(usr != src.occupant) return
if(state)
occupant_message("<font color='red'>Maintenance protocols in effect</font>")
return
maint_access = !maint_access
send_byjax(src.occupant,"exosuit.browser","t_maint_access","[maint_access?"Forbid":"Permit"] maintenance protocols")
return
if(href_list["req_access"] && add_req_access)
if(!in_range(src, usr)) return
output_access_dialog(filter.getObj("id_card"),filter.getMob("user"))
return
if(href_list["maint_access"] && maint_access)
if(!in_range(src, usr)) return
var/mob/user = filter.getMob("user")
if(user)
if(state==0)
state = 1
user << "The securing bolts are now exposed."
else if(state==1)
state = 0
user << "The securing bolts are now hidden."
output_maintenance_dialog(filter.getObj("id_card"),user)
return
if(href_list["set_internal_tank_valve"] && state >=1)
if(!in_range(src, usr)) return
var/mob/user = filter.getMob("user")
if(user)
var/new_pressure = input(user,"Input new output pressure","Pressure setting",internal_tank_valve) as num
if(new_pressure)
internal_tank_valve = new_pressure
user << "The internal pressure valve has been set to [internal_tank_valve]kPa."
if(href_list["remove_passenger"] && state >= 1)
var/mob/user = 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)
user << "<span class='warning'>There are no passengers to remove.</span>"
return
var/pname = input(user, "Choose a passenger to forcibly remove.", "Forcibly Remove Passenger") as null|anything in 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 class='warning'>[user] begins opening the hatch on \the [P]...</span>", "<span class='notice'>You begin opening the hatch on \the [P]...</span>")
if (do_after(user, 40, needhand=0))
user.visible_message("<span class='danger'>[user] opens the hatch on \the [P] and removes [occupant]!</span>", "<span class='danger'>You open the hatch on \the [P] and remove [occupant]!</span>")
P.go_out()
P.log_message("[occupant] was removed.")
return
if(href_list["add_req_access"] && add_req_access && filter.getObj("id_card"))
if(!in_range(src, usr)) return
operation_req_access += filter.getNum("add_req_access")
output_access_dialog(filter.getObj("id_card"),filter.getMob("user"))
return
if(href_list["del_req_access"] && add_req_access && filter.getObj("id_card"))
if(!in_range(src, usr)) return
operation_req_access -= filter.getNum("del_req_access")
output_access_dialog(filter.getObj("id_card"),filter.getMob("user"))
return
if(href_list["finish_req_access"])
if(!in_range(src, usr)) return
add_req_access = 0
var/mob/user = filter.getMob("user")
user << browse(null,"window=exosuit_add_access")
return
if(href_list["dna_lock"])
if(usr != src.occupant) return
if(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.log_message("Recalibration of coordination system started.")
var/T = src.loc
if(do_after_mecha(100))
if(T == src.loc)
src.clearInternalDamage(MECHA_INT_CONTROL_LOST)
src.occupant_message("<font color='blue'>Recalibration successful.</font>")
src.log_message("Recalibration of coordination system finished with 0 errors.")
else
src.occupant_message("<font color='red'>Recalibration failed.</font>")
src.log_message("Recalibration of coordination system failed with 1 error.",1)
//debug
/*
if(href_list["debug"])
if(href_list["set_i_dam"])
setInternalDamage(filter.getNum("set_i_dam"))
if(href_list["clear_i_dam"])
clearInternalDamage(filter.getNum("clear_i_dam"))
return
*/
return
///////////////////////
///// Power stuff /////
///////////////////////
/obj/mecha/proc/has_charge(amount)
return (get_charge()>=amount)
/obj/mecha/proc/get_charge()
if(!src.cell)
return
return max(0, src.cell.charge)
/obj/mecha/proc/use_power(amount)
if(get_charge())
cell.use(amount)
return 1
return 0
/obj/mecha/proc/give_power(amount)
if(!isnull(get_charge()))
cell.give(amount)
return 1
return 0
/obj/mecha/proc/reset_icon()
if (initial_icon)
icon_state = initial_icon
else
icon_state = initial(icon_state)
return icon_state
/obj/mecha/attack_generic(var/mob/user, var/damage, var/attack_message)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
if(!damage)
return 0
src.log_message("Attacked. Attacker - [user].",1)
user.do_attack_animation(src)
if(!deflect_hit(is_melee=1))
src.hit_damage(damage, is_melee=1)
src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
visible_message("<span class='danger'>[user] [attack_message] [src]!</span>")
user.attack_log += text("\[[time_stamp()]\] <font color='red'>attacked [src.name]</font>")
else
src.log_append_to_last("Armor saved.")
playsound(src.loc, 'sound/weapons/slash.ogg', 50, 1, -1)
src.occupant_message("<span class='notice'>\The [user]'s attack is stopped by the armor.</span>")
visible_message("<span class='notice'>\The [user] rebounds off [src.name]'s armor!</span>")
user.attack_log += text("\[[time_stamp()]\] <font color='red'>attacked [src.name]</font>")
return 1
//Used by randomstock.dm in generating damaged exosuits.
//This does an individual check for each piece of equipment on the exosuit, and removes it if
//this probability passes a check
/obj/mecha/proc/lose_equipment(var/probability)
for(var/obj/item/mecha_parts/mecha_equipment/E in equipment)
if (prob(probability))
E.detach(loc)
qdel(E)
//Used by randomstock.dm in generating damaged exosuits.
//Does a random check for each possible type of internal damage, and adds it if it passes
//The probability should be somewhat low unless you just want to saturate it with damage
//Fire is excepted. We're not going to set the exosuit on fire while its in longterm storage
/obj/mecha/proc/random_internal_damage(var/probability)
if (prob(probability))
setInternalDamage(MECHA_INT_TEMP_CONTROL)
if (prob(probability))
setInternalDamage(MECHA_INT_SHORT_CIRCUIT)
if (prob(probability))
setInternalDamage(MECHA_INT_TANK_BREACH)
if (prob(probability))
setInternalDamage(MECHA_INT_CONTROL_LOST)
//Does a number of checks at probability, and alters some configuration values if succeeded
/obj/mecha/proc/misconfigure_systems(var/probability)
if (prob(probability))
internal_tank_valve = rand(0,10000) // Screw up the cabin air pressure.
//This will probably kill the pilot if they dont check it before climbing in
if (prob(probability))
state = 1 // Enable maintenance mode. It won't move.
if (prob(probability))
use_internal_tank = !use_internal_tank // Flip internal tank mode on or off
if (prob(probability))
toggle_lights() // toggle the lights
if (prob(probability)) // Some settings to screw up the radio
radio.broadcasting = !radio.broadcasting
if (prob(probability))
radio.listening = !radio.listening
if (prob(probability))
radio.set_frequency(rand(1200,1600))
if (prob(probability))
maint_access = 0 // Disallow maintenance mode
/////////////////////////////////////////
//////// Mecha process() helpers ////////
/////////////////////////////////////////
/obj/mecha/proc/stop_process(process)
current_processes &= ~process
/obj/mecha/proc/start_process(process)
current_processes |= process
/////////////////////////////////////////////////
//////// Mecha process() subcomponents ////////
/////////////////////////////////////////////////
// Handles the internal alarms for a mech.
// Called every 16 iterations (80 deciseconds).
/obj/mecha/proc/process_warnings()
//power/damage alerts have 3 different statuses
//0 = fine, no alert
//1 = Alert just started. Plays a looping sound for a few minutes
//2 = Alert status has lasted a while. Stops the looping sound and just plays an occasional warning.
var/static/looptime = 1800 //time we stay in stage 1
if (!cell)
if(power_alert_status || damage_alert_status)
// No cell will kill warnings.
// Makes sense, caution systems are battery powered.
power_alert_status = 0 // cancel the alert status
power_warning_delay = initial(power_warning_delay) // Reset the delay
stop_sound(1)
damage_alert_status = 0
damage_warning_delay = initial(damage_warning_delay) // Reset the delay
stop_sound(2)
return
if (!power_alert_status && cell) // If we're in the fine status
if (cell.charge < (cell.maxcharge*0.3)) // but power is below 30%
power_alert_status = 1 // Switch to the alert status
narrator_message(POWER) // And send a vocal warning
log_append_to_last("Entered critical power alert.")
//No 'else' here, we want this to run in the same proc if the alert status was just enabled
if (power_alert_status) // IF we're in either warning status
if (cell.charge >= (cell.maxcharge*0.3)) // But power has risen back above danger levels
power_alert_status = 0 // cancel the alert status
power_warning_delay = initial(power_warning_delay) // Reset the delay
stop_sound(1)
occupant << "<span class='notice'>[src] power levels have returned to within safe operating parameters. Power alert status cancelled.</span>"
log_append_to_last("Power alert cleared")
return
if (power_alert_status == 1) // If we're in alert 1, constant loop
if (!powerloop) // If the powerloop sound var is still null, it means we havent started playing it yet
occupant << "<span class='danger'>WARNING: [src] power levels below 30%. Please pilot to the nearest recharging station immediately.</span>"
create_sound(1) // We create it
occupant << powerloop // and start playing it to the occupant
last_power_warning = world.time // We set this var when we enter alert 1, to track how long we've been in it
if ((world.time - last_power_warning) >= looptime) //If we've been in looping mode for long enough
occupant << "<span class='danger'>Alert: [src] power levels have remained in critical state for an unacceptably long period. Now switching to low-frequency warning mode to conserve power.</span>"
stop_sound(1) // We stop the soundloop
power_alert_status = 2 // And switch to alert 2
last_power_warning = world.time
else if (power_alert_status == 2) // If we're in alert 2 - infrequent vocal warnings
if ((world.time - last_power_warning) >= power_warning_delay) // IF its been long enough since the last warning
narrator_message(POWER) // We send a warning message to remind them
power_warning_delay *= 1.05 // We increase the delay between warnings by 5% multiplicatively each time
last_power_warning = world.time
//This causes the warnings to become less frequent and not be a constant annoyance
//The following block is basically a carbon copy of the above with minor alterations for damage
//--------------------------------------
if (!damage_alert_status)
if (health < (initial(health)*0.3))
damage_alert_status = 1
narrator_message(DAMAGE)
log_append_to_last("Entered critical hull integrity alert.")
if (damage_alert_status)
if (health >= (initial(health)*0.3))
damage_alert_status = 0
damage_warning_delay = initial(damage_warning_delay) // Reset the delay
stop_sound(2)
occupant << "<span class='notice'>[src] hull integrity is now within safe operating parameters. Integrity alert status cancelled.</span>"
log_append_to_last("Hull integrity alert cleared.")
return
if (damage_alert_status == 1)
if (!damageloop)
occupant << "<span class='danger'>WARNING: [src] hull integrity below 30%. Please report to the nearest Nanotrasen Certified Robotics Laboratory for urgent repairs.</span>"
create_sound(2)
occupant << damageloop
last_damage_warning = world.time
if ((world.time - last_damage_warning) >= (looptime * 0.3)) //Looptime is shorter for the damage sound because its so horribly grating.
occupant << "<span class='danger'>Alert: [src] hull integrity has remained in critical state for a significant period of time. Now switching to low-frequency alert mode. Please seek repair as soon as possible.</span>"
stop_sound(2) // We stop the soundloop
damage_alert_status = 2 // And switch to alert 2
last_damage_warning = world.time
else if (damage_alert_status == 2)
if ((world.time - last_damage_warning) >= damage_warning_delay)
narrator_message(DAMAGE)
damage_warning_delay *= 1.05
last_damage_warning = world.time
//This creates the sound loop datums as necessary
//They are destroyed when the sound stops, and re-created when it starts looping.
/obj/mecha/proc/create_sound(var/type)
if (type == 1)
if (!powerloop)
powerloop = new /sound()
powerloop.file = 'sound/mecha/lowpower.ogg'
powerloop.repeat = 1
powerloop.volume = 15
powerloop.channel = 50 // Only one mech can be heard at one time. So fuck the rand() deal.
else if (type == 2)
if (!damageloop)
damageloop = new /sound()
damageloop.file = 'sound/mecha/internaldmgalarm.ogg'
damageloop.repeat = 1
damageloop.volume = 5 //lower volume because the sound file is louder
damageloop.channel = 51
/obj/mecha/proc/stop_sound(var/type)
if (type == 1 && powerloop)
occupant << sound(null, channel=powerloop.channel) // this stops the sound
powerloop = null
else if (type == 2 && damageloop)
occupant << sound(null, channel=damageloop.channel) // this stops the sound
damageloop = null
//This function exists for if someone enters the exosuit while its at alert stage 1
//It starts playing the alert loops for the new occupant
/obj/mecha/proc/resume_sounds()
if (power_alert_status == 1)
create_sound(1)
occupant << powerloop
if (damage_alert_status == 2)
create_sound(2)
occupant << damageloop
// 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.queue()
cell.charge -= min(20,cell.charge)
cell.maxcharge -= min(20,cell.maxcharge)
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='?src=\ref[src];debug=1;set_i_dam=[MECHA_INT_FIRE]'>MECHA_INT_FIRE</a><br />
<a href='?src=\ref[src];debug=1;set_i_dam=[MECHA_INT_TEMP_CONTROL]'>MECHA_INT_TEMP_CONTROL</a><br />
<a href='?src=\ref[src];debug=1;set_i_dam=[MECHA_INT_SHORT_CIRCUIT]'>MECHA_INT_SHORT_CIRCUIT</a><br />
<a href='?src=\ref[src];debug=1;set_i_dam=[MECHA_INT_TANK_BREACH]'>MECHA_INT_TANK_BREACH</a><br />
<a href='?src=\ref[src];debug=1;set_i_dam=[MECHA_INT_CONTROL_LOST]'>MECHA_INT_CONTROL_LOST</a><br />
<hr />
<h3>Clear:</h3>
<a href='?src=\ref[src];debug=1;clear_i_dam=[MECHA_INT_FIRE]'>MECHA_INT_FIRE</a><br />
<a href='?src=\ref[src];debug=1;clear_i_dam=[MECHA_INT_TEMP_CONTROL]'>MECHA_INT_TEMP_CONTROL</a><br />
<a href='?src=\ref[src];debug=1;clear_i_dam=[MECHA_INT_SHORT_CIRCUIT]'>MECHA_INT_SHORT_CIRCUIT</a><br />
<a href='?src=\ref[src];debug=1;clear_i_dam=[MECHA_INT_TANK_BREACH]'>MECHA_INT_TANK_BREACH</a><br />
<a href='?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
*/
#undef NOMINAL
#undef FIRSTRUN
#undef POWER
#undef DAMAGE
#undef IMAGE
#undef WEAPONDOWN