mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-09 16:14:13 +00:00
1800 lines
61 KiB
Plaintext
1800 lines
61 KiB
Plaintext
#define MECHA_INT_FIRE 1
|
|
#define MECHA_INT_TEMP_CONTROL 2
|
|
#define MECHA_INT_SHORT_CIRCUIT 4
|
|
#define MECHA_INT_TANK_BREACH 8
|
|
#define MECHA_INT_CONTROL_LOST 16
|
|
|
|
#define MELEE 1
|
|
#define RANGED 2
|
|
|
|
|
|
/obj/mecha
|
|
name = "Mecha"
|
|
desc = "Exosuit"
|
|
icon = 'icons/mecha/mecha.dmi'
|
|
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 the incoming projectiles, hits, or lesser the effect of ex_act.
|
|
//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/list/proc_res = list() //stores proc owners, like proc_res["functionname"] = owner reference
|
|
var/datum/effect/effect/system/spark_spread/spark_system = new
|
|
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/datum/global_iterator/pr_int_temp_processor //normalizes internal air mixture temperature
|
|
var/datum/global_iterator/pr_inertial_movement //controls intertial movement in spesss
|
|
var/datum/global_iterator/pr_give_air //moves air from tank to cabin
|
|
var/datum/global_iterator/pr_internal_damage //processes internal damage
|
|
|
|
|
|
var/wreckage
|
|
|
|
var/list/equipment = new
|
|
var/obj/item/mecha_parts/mecha_equipment/selected
|
|
var/max_equip = 3
|
|
var/datum/events/events
|
|
|
|
/obj/mecha/New()
|
|
..()
|
|
events = new
|
|
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.set_up(2, 0, src)
|
|
spark_system.attach(src)
|
|
add_cell()
|
|
add_iterators()
|
|
removeVerb(/obj/mecha/verb/disconnect_from_port)
|
|
removeVerb(/atom/movable/verb/pull)
|
|
log_message("[src.name] created.")
|
|
loc.Entered(src)
|
|
mechas_list += src //global mech list
|
|
return
|
|
|
|
/obj/mecha/Del()
|
|
src.go_out()
|
|
mechas_list -= src //global mech list
|
|
..()
|
|
return
|
|
|
|
////////////////////////
|
|
////// 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(var/obj/item/weapon/cell/C=null)
|
|
if(C)
|
|
C.forceMove(src)
|
|
cell = C
|
|
return
|
|
cell = new(src)
|
|
cell.charge = 15000
|
|
cell.maxcharge = 15000
|
|
|
|
/obj/mecha/proc/add_cabin()
|
|
cabin_air = new
|
|
cabin_air.temperature = T20C
|
|
cabin_air.volume = 200
|
|
cabin_air.oxygen = O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature)
|
|
cabin_air.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/add_iterators()
|
|
pr_int_temp_processor = new /datum/global_iterator/mecha_preserve_temp(list(src))
|
|
pr_inertial_movement = new /datum/global_iterator/mecha_intertial_movement(null,0)
|
|
pr_give_air = new /datum/global_iterator/mecha_tank_give_air(list(src))
|
|
pr_internal_damage = new /datum/global_iterator/mecha_internal_damage(list(src),0)
|
|
|
|
/obj/mecha/proc/do_after(delay as num)
|
|
sleep(delay)
|
|
if(src)
|
|
return 1
|
|
return 0
|
|
|
|
/obj/mecha/proc/enter_after(delay as num, var/mob/user as mob, var/numticks = 5)
|
|
var/delayfraction = delay/numticks
|
|
|
|
var/turf/T = user.loc
|
|
|
|
for(var/i = 0, i<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()
|
|
set src in view()
|
|
..()
|
|
var/integrity = health/initial(health)*100
|
|
switch(integrity)
|
|
if(85 to 100)
|
|
usr << "It's fully intact."
|
|
if(65 to 85)
|
|
usr << "It's slightly damaged."
|
|
if(45 to 65)
|
|
usr << "It's badly damaged."
|
|
if(25 to 45)
|
|
usr << "It's heavily damaged."
|
|
else
|
|
usr << "It's falling apart."
|
|
if(equipment && equipment.len)
|
|
usr << "It's equipped with:"
|
|
for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment)
|
|
usr << "\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 ////
|
|
////////////////////////////
|
|
/*
|
|
/atom/DblClick(object,location,control,params)
|
|
var/mob/M = src.mob
|
|
if(M && M.in_contents_of(/obj/mecha))
|
|
|
|
if(mech_click == world.time) return
|
|
mech_click = world.time
|
|
|
|
if(!istype(object, /atom)) return
|
|
if(istype(object, /obj/screen))
|
|
var/obj/screen/using = object
|
|
if(using.screen_loc == ui_acti || using.screen_loc == ui_iarrowleft || using.screen_loc == ui_iarrowright)//ignore all HUD objects save 'intent' and its arrows
|
|
return ..()
|
|
else
|
|
return
|
|
var/obj/mecha/Mech = M.loc
|
|
spawn() //this helps prevent clickspam fest.
|
|
if (Mech)
|
|
Mech.click_action(object,M)
|
|
// else
|
|
// return ..()
|
|
*/
|
|
|
|
/obj/mecha/proc/click_action(atom/target,mob/user)
|
|
if(!src.occupant || src.occupant != user ) return
|
|
if(user.stat) 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))//wrong direction
|
|
return
|
|
if(hasInternalDamage(MECHA_INT_CONTROL_LOST))
|
|
target = safepick(view(3,target))
|
|
if(!target)
|
|
return
|
|
if(get_dist(src, target)>1)
|
|
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/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 domove(direction)
|
|
|
|
/obj/mecha/proc/domove(direction)
|
|
return call((proc_res["dyndomove"]||src), "dyndomove")(direction)
|
|
|
|
/obj/mecha/proc/dyndomove(direction)
|
|
if(!can_move)
|
|
return 0
|
|
if(src.pr_inertial_movement.active())
|
|
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())
|
|
src.pr_inertial_movement.start(list(src,direction))
|
|
src.log_message("Movement control lost. Inertial movement started.")
|
|
if(do_after(step_in))
|
|
can_move = 1
|
|
return 1
|
|
return 0
|
|
|
|
/obj/mecha/proc/mechturn(direction)
|
|
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.HasEntered(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
|
|
pr_internal_damage.start()
|
|
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>")
|
|
pr_int_temp_processor.start()
|
|
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 = absorbDamage(amount,type)
|
|
health -= damage
|
|
update_health()
|
|
log_append_to_last("Took [damage] points of damage. Damage type: \"[type]\".",1)
|
|
return
|
|
|
|
/obj/mecha/proc/absorbDamage(damage,damage_type)
|
|
return call((proc_res["dynabsorbdamage"]||src), "dynabsorbdamage")(damage,damage_type)
|
|
|
|
/obj/mecha/proc/dynabsorbdamage(damage,damage_type)
|
|
return damage*(listgetindex(damage_absorption,damage_type) || 1)
|
|
|
|
|
|
/obj/mecha/proc/update_health()
|
|
if(src.health > 0)
|
|
src.spark_system.start()
|
|
else
|
|
src.destroy()
|
|
return
|
|
|
|
/obj/mecha/attack_hand(mob/user as mob)
|
|
src.log_message("Attack by hand/paw. Attacker - [user].",1)
|
|
|
|
if(ishuman(user))
|
|
if(istype(user:gloves, /obj/item/clothing/gloves/space_ninja)&&user:gloves:candrain&&!user:gloves:draining)
|
|
call(/obj/item/clothing/gloves/space_ninja/proc/drain)("MECHA",src,user:wear_suit)
|
|
return
|
|
|
|
if ((HULK in user.mutations) && !prob(src.deflect_chance))
|
|
src.take_damage(15)
|
|
src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
|
|
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/attack_paw(mob/user as mob)
|
|
return src.attack_hand(user)
|
|
|
|
|
|
/obj/mecha/attack_alien(mob/user as mob)
|
|
src.log_message("Attack by alien. Attacker - [user].",1)
|
|
if(!prob(src.deflect_chance))
|
|
src.take_damage(15)
|
|
src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
|
|
playsound(src.loc, 'sound/weapons/slash.ogg', 50, 1, -1)
|
|
user << "\red You slash at the armored suit!"
|
|
visible_message("\red The [user] slashes at [src.name]'s armor!")
|
|
else
|
|
src.log_append_to_last("Armor saved.")
|
|
playsound(src.loc, 'sound/weapons/slash.ogg', 50, 1, -1)
|
|
user << "\green Your claws had no effect!"
|
|
src.occupant_message("\blue The [user]'s claws are stopped by the armor.")
|
|
visible_message("\blue The [user] rebounds off [src.name]'s armor!")
|
|
return
|
|
|
|
|
|
/obj/mecha/attack_animal(mob/living/simple_animal/user as mob)
|
|
src.log_message("Attack by simple animal. Attacker - [user].",1)
|
|
if(user.melee_damage_upper == 0)
|
|
user.emote("[user.friendly] [src]")
|
|
else
|
|
if(!prob(src.deflect_chance))
|
|
var/damage = rand(user.melee_damage_lower, user.melee_damage_upper)
|
|
src.take_damage(damage)
|
|
src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
|
|
visible_message("\red <B>[user]</B> [user.attacktext] [src]!")
|
|
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("\blue The [user]'s attack is stopped by the armor.")
|
|
visible_message("\blue The [user] rebounds off [src.name]'s armor!")
|
|
user.attack_log += text("\[[time_stamp()]\] <font color='red'>attacked [src.name]</font>")
|
|
return
|
|
|
|
/obj/mecha/hitby(atom/movable/A as mob|obj) //wrapper
|
|
src.log_message("Hit by [A].",1)
|
|
call((proc_res["dynhitby"]||src), "dynhitby")(A)
|
|
return
|
|
|
|
/obj/mecha/proc/dynhitby(atom/movable/A)
|
|
if(istype(A, /obj/item/mecha_parts/mecha_tracking))
|
|
A.forceMove(src)
|
|
src.visible_message("The [A] fastens firmly to [src].")
|
|
return
|
|
if(prob(src.deflect_chance) || istype(A, /mob))
|
|
src.occupant_message("\blue The [A] bounces off the armor.")
|
|
src.visible_message("The [A] bounces off the [src.name] armor")
|
|
src.log_append_to_last("Armor saved.")
|
|
if(istype(A, /mob/living))
|
|
var/mob/living/M = A
|
|
M.take_organ_damage(10)
|
|
else if(istype(A, /obj))
|
|
var/obj/O = A
|
|
if(O.throwforce)
|
|
src.take_damage(O.throwforce)
|
|
src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
|
|
return
|
|
|
|
|
|
/obj/mecha/bullet_act(var/obj/item/projectile/Proj) //wrapper
|
|
src.log_message("Hit by projectile. Type: [Proj.name]([Proj.flag]).",1)
|
|
call((proc_res["dynbulletdamage"]||src), "dynbulletdamage")(Proj) //calls equipment
|
|
..()
|
|
return
|
|
|
|
/obj/mecha/proc/dynbulletdamage(var/obj/item/projectile/Proj)
|
|
if(prob(src.deflect_chance))
|
|
src.occupant_message("\blue The armor deflects incoming projectile.")
|
|
src.visible_message("The [src.name] armor deflects the projectile")
|
|
src.log_append_to_last("Armor saved.")
|
|
return
|
|
var/ignore_threshold
|
|
if(Proj.flag == "taser")
|
|
use_power(200)
|
|
return
|
|
if(istype(Proj, /obj/item/projectile/beam/pulse))
|
|
ignore_threshold = 1
|
|
src.take_damage(Proj.damage,Proj.flag)
|
|
src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),ignore_threshold)
|
|
Proj.on_hit(src)
|
|
return
|
|
|
|
/obj/mecha/proc/destroy()
|
|
spawn()
|
|
go_out()
|
|
var/turf/T = get_turf(src)
|
|
tag = "\ref[src]" //better safe then sorry
|
|
if(loc)
|
|
loc.Exited(src)
|
|
loc = null
|
|
if(T)
|
|
if(istype(src, /obj/mecha/working/ripley/))
|
|
var/obj/mecha/working/ripley/R = src
|
|
if(R.cargo)
|
|
for(var/obj/O in R.cargo) //Dump contents of stored cargo
|
|
O.loc = T
|
|
R.cargo -= O
|
|
T.Entered(O)
|
|
|
|
if(prob(30))
|
|
explosion(T, 0, 0, 1, 3)
|
|
spawn(0)
|
|
if(wreckage)
|
|
var/obj/effect/decal/mecha_wreckage/WR = new wreckage(T)
|
|
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
|
|
E.reliability = round(rand(E.reliability/3,E.reliability))
|
|
else
|
|
E.forceMove(T)
|
|
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.forceMove(T)
|
|
E.destroy()
|
|
spawn(0)
|
|
del(src)
|
|
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)
|
|
src.destroy()
|
|
if(2.0)
|
|
if (prob(30))
|
|
src.destroy()
|
|
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))
|
|
src.destroy()
|
|
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
|
|
|
|
/*Will fix later -Sieve
|
|
/obj/mecha/attack_blob(mob/user as mob)
|
|
src.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.loc, 'sound/effects/blobattack.ogg', 50, 1, -1)
|
|
user << "\red You smash at the armored suit!"
|
|
for (var/mob/V in viewers(src))
|
|
if(V.client && !(V.blinded))
|
|
V.show_message("\red The [user] smashes against [src.name]'s armor!", 1)
|
|
else
|
|
src.log_append_to_last("Armor saved.")
|
|
playsound(src.loc, 'sound/effects/blobattack.ogg', 50, 1, -1)
|
|
user << "\green Your attack had no effect!"
|
|
src.occupant_message("\blue The [user]'s attack is stopped by the armor.")
|
|
for (var/mob/V in viewers(src))
|
|
if(V.client && !(V.blinded))
|
|
V.show_message("\blue The [user] rebounds off the [src.name] armor!", 1)
|
|
return
|
|
*/
|
|
|
|
/obj/mecha/blob_act()
|
|
take_damage(30, "brute")
|
|
return
|
|
|
|
//TODO
|
|
/obj/mecha/meteorhit()
|
|
return ex_act(rand(1,3))//should do for now
|
|
|
|
/obj/mecha/emp_act(severity)
|
|
if(get_charge())
|
|
use_power((cell.charge/2)/severity)
|
|
take_damage(50 / severity,"energy")
|
|
src.log_message("EMP detected",1)
|
|
check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1)
|
|
return
|
|
|
|
/obj/mecha/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
|
if(exposed_temperature>src.max_temperature)
|
|
src.log_message("Exposed to dangerous temperature.",1)
|
|
src.take_damage(5,"fire")
|
|
src.check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL))
|
|
return
|
|
|
|
/obj/mecha/proc/dynattackby(obj/item/weapon/W as obj, mob/user as mob)
|
|
src.log_message("Attacked by [W]. Attacker - [user]")
|
|
if(prob(src.deflect_chance))
|
|
user << "\red 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("<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.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
|
|
|
|
//////////////////////
|
|
////// AttackBy //////
|
|
//////////////////////
|
|
|
|
/obj/mecha/attackby(obj/item/weapon/W as obj, mob/user as mob)
|
|
|
|
|
|
if(istype(W, /obj/item/device/mmi) || istype(W, /obj/item/device/mmi/posibrain))
|
|
if(mmi_move_inside(W,user))
|
|
user << "[src]-MMI interface initialized successfuly"
|
|
else
|
|
user << "[src]-MMI interface initialization failed."
|
|
return
|
|
|
|
if(istype(W, /obj/item/mecha_parts/mecha_equipment))
|
|
var/obj/item/mecha_parts/mecha_equipment/E = W
|
|
spawn()
|
|
if(E.can_attach(src))
|
|
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 << "\red Invalid ID: Access denied."
|
|
else
|
|
user << "\red Maintenance protocols disabled by operator."
|
|
else if(istype(W, /obj/item/weapon/wrench))
|
|
if(state==1)
|
|
state = 2
|
|
user << "You undo the securing bolts."
|
|
else if(state==2)
|
|
state = 1
|
|
user << "You tighten the securing bolts."
|
|
return
|
|
else if(istype(W, /obj/item/weapon/crowbar))
|
|
if(state==2)
|
|
state = 3
|
|
user << "You open the hatch to the power unit"
|
|
else if(state==3)
|
|
state=2
|
|
user << "You close the hatch to the power unit"
|
|
return
|
|
else if(istype(W, /obj/item/weapon/cable_coil))
|
|
if(state == 3 && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT))
|
|
var/obj/item/weapon/cable_coil/CC = W
|
|
if(CC.amount > 1)
|
|
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(istype(W, /obj/item/weapon/screwdriver))
|
|
if(hasInternalDamage(MECHA_INT_TEMP_CONTROL))
|
|
clearInternalDamage(MECHA_INT_TEMP_CONTROL)
|
|
user << "You repair the damaged temperature controller."
|
|
else if(state==3 && src.cell)
|
|
src.cell.forceMove(src.loc)
|
|
src.cell = null
|
|
state = 4
|
|
user << "You unscrew and pry out the powercell."
|
|
src.log_message("Powercell removed")
|
|
else if(state==4 && src.cell)
|
|
state=3
|
|
user << "You screw the cell in place"
|
|
return
|
|
|
|
else if(istype(W, /obj/item/weapon/cell))
|
|
if(state==4)
|
|
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(istype(W, /obj/item/weapon/weldingtool) && user.a_intent != "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 << "\blue You repair the damaged gas tank."
|
|
else
|
|
return
|
|
if(src.health<initial(src.health))
|
|
user << "\blue You repair some damage to [src.name]."
|
|
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))
|
|
user.drop_from_inventory(W)
|
|
W.forceMove(src)
|
|
user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src]")
|
|
return
|
|
|
|
else
|
|
call((proc_res["dynattackby"]||src), "dynattackby")(W,user)
|
|
/*
|
|
src.log_message("Attacked by [W]. Attacker - [user]")
|
|
if(prob(src.deflect_chance))
|
|
user << "\red 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("<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.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(!istype(user, /mob/living/silicon/ai))
|
|
return
|
|
var/output = {"<b>Assume direct control over [src]?</b>
|
|
<a href='?src=\ref[src];ai_take_control=\ref[user];duration=3000'>Yes</a><br>
|
|
"}
|
|
user << browse(output, "window=mecha_attack_ai")
|
|
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.return_temperature()
|
|
else
|
|
var/datum/gas_mixture/t_air = get_turf_air()
|
|
if(t_air)
|
|
. = t_air.return_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/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("\blue [name] connects to the port.")
|
|
src.verbs += /obj/mecha/verb/disconnect_from_port
|
|
src.verbs -= /obj/mecha/verb/connect_to_port
|
|
return
|
|
else
|
|
src.occupant_message("\red [name] failed to connect to the port.")
|
|
return
|
|
else
|
|
src.occupant_message("Nothing happens")
|
|
|
|
|
|
/obj/mecha/verb/disconnect_from_port()
|
|
set name = "Disconnect from port"
|
|
set category = "Exosuit Interface"
|
|
set src = usr.loc
|
|
set popup_menu = 0
|
|
if(!src.occupant) return
|
|
if(usr!=src.occupant)
|
|
return
|
|
if(disconnect())
|
|
src.occupant_message("\blue [name] disconnects from the port.")
|
|
src.verbs -= /obj/mecha/verb/disconnect_from_port
|
|
src.verbs += /obj/mecha/verb/connect_to_port
|
|
else
|
|
src.occupant_message("\red [name] is not connected to the port at the moment.")
|
|
|
|
/obj/mecha/verb/toggle_lights()
|
|
set name = "Toggle Lights"
|
|
set category = "Exosuit Interface"
|
|
set src = usr.loc
|
|
set popup_menu = 0
|
|
if(usr!=occupant) return
|
|
lights = !lights
|
|
if(lights) SetLuminosity(luminosity + lights_power)
|
|
else SetLuminosity(luminosity - 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
|
|
src.log_message("[usr] tries to move in.")
|
|
if (src.occupant)
|
|
usr << "\blue <B>The [src.name] is already occupied!</B>"
|
|
src.log_append_to_last("Permission denied.")
|
|
return
|
|
/*
|
|
if (usr.abiotic())
|
|
usr << "\blue <B>Subject cannot have abiotic items on.</B>"
|
|
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 << "\red Access denied"
|
|
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
|
|
// usr << "You start climbing into [src.name]"
|
|
|
|
visible_message("\blue [usr] starts to climb into [src.name]")
|
|
|
|
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.client.perspective = EYE_PERSPECTIVE
|
|
H.client.eye = 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()
|
|
dir = dir_in
|
|
playsound(src, 'sound/machines/windowdoor.ogg', 50, 1)
|
|
if(!hasInternalDamage())
|
|
src.occupant << sound('sound/mecha/nominal.ogg',volume=50)
|
|
return 1
|
|
else
|
|
return 0
|
|
|
|
/obj/mecha/proc/mmi_move_inside(var/obj/item/device/mmi/mmi_as_oc as obj,mob/user as mob)
|
|
if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client)
|
|
user << "Consciousness matrix not detected."
|
|
return 0
|
|
else if(mmi_as_oc.brainmob.stat)
|
|
user << "Beta-rhythm below acceptable level."
|
|
return 0
|
|
else if(occupant)
|
|
user << "Occupant detected."
|
|
return 0
|
|
else if(dna && dna!=mmi_as_oc.brainmob.dna.unique_enzymes)
|
|
user << "Stop it!"
|
|
return 0
|
|
//Added a message here since people assume their first click failed or something./N
|
|
// user << "Installing MMI, please stand by."
|
|
|
|
visible_message("\blue [usr] starts to insert an MMI into [src.name]")
|
|
|
|
if(enter_after(40,user))
|
|
if(!occupant)
|
|
return mmi_moved_inside(mmi_as_oc,user)
|
|
else
|
|
user << "Occupant detected."
|
|
else
|
|
user << "You stop inserting the MMI."
|
|
return 0
|
|
|
|
/obj/mecha/proc/mmi_moved_inside(var/obj/item/device/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)
|
|
user << "Consciousness matrix not detected."
|
|
return 0
|
|
else if(mmi_as_oc.brainmob.stat)
|
|
user << "Beta-rhythm below acceptable level."
|
|
return 0
|
|
user.drop_from_inventory(mmi_as_oc)
|
|
var/mob/brainmob = mmi_as_oc.brainmob
|
|
brainmob.reset_view(src)
|
|
/*
|
|
brainmob.client.eye = src
|
|
brainmob.client.perspective = EYE_PERSPECTIVE
|
|
*/
|
|
occupant = brainmob
|
|
brainmob.loc = src //should allow relaymove
|
|
brainmob.canmove = 1
|
|
mmi_as_oc.loc = src
|
|
mmi_as_oc.mecha = src
|
|
src.verbs -= /obj/mecha/verb/eject
|
|
src.Entered(mmi_as_oc)
|
|
src.Move(src.loc)
|
|
src.icon_state = src.reset_icon()
|
|
dir = dir_in
|
|
src.log_message("[mmi_as_oc] moved in as pilot.")
|
|
if(!hasInternalDamage())
|
|
src.occupant << sound('sound/mecha/nominal.ogg',volume=50)
|
|
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
|
|
//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()
|
|
if(!src.occupant) return
|
|
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
|
|
/*
|
|
if(ishuman(occupant) && (return_pressure() > HAZARD_HIGH_PRESSURE))
|
|
use_internal_tank = 0
|
|
var/datum/gas_mixture/environment = get_turf_air()
|
|
if(environment)
|
|
var/env_pressure = environment.return_pressure()
|
|
var/pressure_delta = (cabin.return_pressure() - env_pressure)
|
|
//Can not have a pressure delta that would cause environment pressure > tank pressure
|
|
|
|
var/transfer_moles = 0
|
|
if(pressure_delta > 0)
|
|
transfer_moles = pressure_delta*environment.volume/(cabin.return_temperature() * R_IDEAL_GAS_EQUATION)
|
|
|
|
//Actually transfer the gas
|
|
var/datum/gas_mixture/removed = cabin.air_contents.remove(transfer_moles)
|
|
loc.assume_air(removed)
|
|
|
|
occupant.SetStunned(5)
|
|
occupant.SetWeakened(5)
|
|
occupant << "You were blown out of the mech!"
|
|
*/
|
|
src.log_message("[mob_container] moved out.")
|
|
occupant.reset_view()
|
|
/*
|
|
if(src.occupant.client)
|
|
src.occupant.client.eye = src.occupant.client.mob
|
|
src.occupant.client.perspective = MOB_PERSPECTIVE
|
|
*/
|
|
src.occupant << browse(null, "window=exosuit")
|
|
if(istype(mob_container, /obj/item/device/mmi) || istype(mob_container, /obj/item/device/mmi/posibrain))
|
|
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.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"
|
|
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>[tank_temperature]°K|[tank_temperature - T0C]°C<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]°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")] 2555</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/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) ?"<a href='?src=\ref[src];set_internal_tank_valve=1;user=\ref[user]'>Set Cabin Air Pressure</a>":null]
|
|
</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
|
|
|
|
|
|
/////////////////
|
|
///// Topic /////
|
|
/////////////////
|
|
|
|
/obj/mecha/Topic(href, href_list)
|
|
..()
|
|
if(href_list["update_content"])
|
|
if(usr != src.occupant) return
|
|
send_byjax(src.occupant,"exosuit.browser","content",src.get_stats_part())
|
|
return
|
|
if(href_list["close"])
|
|
return
|
|
if(usr.stat > 0)
|
|
return
|
|
var/datum/topic_input/filter = new /datum/topic_input(href,href_list)
|
|
if(href_list["select_equip"])
|
|
if(usr != src.occupant) return
|
|
var/obj/item/mecha_parts/mecha_equipment/equip = filter.getObj("select_equip")
|
|
if(equip)
|
|
src.selected = equip
|
|
src.occupant_message("You switch to [equip]")
|
|
src.visible_message("[src] raises [equip]")
|
|
send_byjax(src.occupant,"exosuit.browser","eq_list",src.get_equipment_list())
|
|
return
|
|
if(href_list["eject"])
|
|
if(usr != src.occupant) return
|
|
src.eject()
|
|
return
|
|
if(href_list["toggle_lights"])
|
|
if(usr != src.occupant) return
|
|
src.toggle_lights()
|
|
return
|
|
if(href_list["toggle_airtank"])
|
|
if(usr != src.occupant) return
|
|
src.toggle_internal_tank()
|
|
return
|
|
if(href_list["rmictoggle"])
|
|
if(usr != src.occupant) return
|
|
radio.broadcasting = !radio.broadcasting
|
|
send_byjax(src.occupant,"exosuit.browser","rmicstate",(radio.broadcasting?"Engaged":"Disengaged"))
|
|
return
|
|
if(href_list["rspktoggle"])
|
|
if(usr != src.occupant) return
|
|
radio.listening = !radio.listening
|
|
send_byjax(src.occupant,"exosuit.browser","rspkstate",(radio.listening?"Engaged":"Disengaged"))
|
|
return
|
|
if(href_list["rfreq"])
|
|
if(usr != src.occupant) return
|
|
var/new_frequency = (radio.frequency + filter.getNum("rfreq"))
|
|
if (!radio.freerange || (radio.frequency < 1200 || radio.frequency > 1600))
|
|
new_frequency = sanitize_frequency(new_frequency)
|
|
radio.set_frequency(new_frequency)
|
|
send_byjax(src.occupant,"exosuit.browser","rfreq","[format_frequency(radio.frequency)]")
|
|
return
|
|
if(href_list["port_disconnect"])
|
|
if(usr != src.occupant) return
|
|
src.disconnect_from_port()
|
|
return
|
|
if (href_list["port_connect"])
|
|
if(usr != src.occupant) return
|
|
src.connect_to_port()
|
|
return
|
|
if (href_list["view_log"])
|
|
if(usr != src.occupant) return
|
|
src.occupant << browse(src.get_log_html(), "window=exosuit_log")
|
|
onclose(occupant, "exosuit_log")
|
|
return
|
|
if (href_list["change_name"])
|
|
if(usr != src.occupant) return
|
|
var/newname = strip_html_simple(input(occupant,"Choose new exosuit name","Rename exosuit",initial(name)) as text, MAX_NAME_LEN)
|
|
if(newname && trim(newname))
|
|
name = newname
|
|
else
|
|
alert(occupant, "nope.avi")
|
|
return
|
|
if (href_list["toggle_id_upload"])
|
|
if(usr != src.occupant) return
|
|
add_req_access = !add_req_access
|
|
send_byjax(src.occupant,"exosuit.browser","t_id_upload","[add_req_access?"L":"Unl"]ock ID upload panel")
|
|
return
|
|
if(href_list["toggle_maint_access"])
|
|
if(usr != src.occupant) return
|
|
if(state)
|
|
occupant_message("<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["add_req_access"] && add_req_access && filter.getObj("id_card"))
|
|
if(!in_range(src, usr)) return
|
|
operation_req_access += filter.getNum("add_req_access")
|
|
output_access_dialog(filter.getObj("id_card"),filter.getMob("user"))
|
|
return
|
|
if(href_list["del_req_access"] && add_req_access && filter.getObj("id_card"))
|
|
if(!in_range(src, usr)) return
|
|
operation_req_access -= filter.getNum("del_req_access")
|
|
output_access_dialog(filter.getObj("id_card"),filter.getMob("user"))
|
|
return
|
|
if(href_list["finish_req_access"])
|
|
if(!in_range(src, usr)) return
|
|
add_req_access = 0
|
|
var/mob/user = filter.getMob("user")
|
|
user << browse(null,"window=exosuit_add_access")
|
|
return
|
|
if(href_list["dna_lock"])
|
|
if(usr != src.occupant) return
|
|
if(src.occupant && (!istype(src.occupant, /obj/item/device/mmi/posibrain) || !istype(src.occupant, /obj/item/device/mmi)))
|
|
src.dna = src.occupant.dna.unique_enzymes
|
|
src.occupant_message("You feel a prick as the needle takes your DNA sample.")
|
|
return
|
|
if(href_list["reset_dna"])
|
|
if(usr != src.occupant) return
|
|
src.dna = null
|
|
if(href_list["repair_int_control_lost"])
|
|
if(usr != src.occupant) return
|
|
src.occupant_message("Recalibrating coordination system.")
|
|
src.log_message("Recalibration of coordination system started.")
|
|
var/T = src.loc
|
|
if(do_after(100))
|
|
if(T == src.loc)
|
|
src.clearInternalDamage(MECHA_INT_CONTROL_LOST)
|
|
src.occupant_message("<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
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
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 = 0
|
|
O.canmove = 1
|
|
O.name = AI.name
|
|
O.real_name = AI.real_name
|
|
O.anchored = 1
|
|
O.aiRestorePowerRoutine = 0
|
|
O.control_disabled = 1 // Can't control things remotely if you're stuck in a card!
|
|
O.laws = AI.laws
|
|
O.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()
|
|
del(O)
|
|
if (!AI.stat)
|
|
AI.icon_state = "ai"
|
|
else
|
|
AI.icon_state = "ai-crash"
|
|
src.occupant = cur_occupant
|
|
*/
|
|
return
|
|
|
|
///////////////////////
|
|
///// 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)
|
|
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
|
|
|
|
//////////////////////////////////////////
|
|
//////// Mecha global iterators ////////
|
|
//////////////////////////////////////////
|
|
|
|
|
|
/datum/global_iterator/mecha_preserve_temp //normalizing cabin air temperature to 20 degrees celsium
|
|
delay = 20
|
|
|
|
process(var/obj/mecha/mecha)
|
|
if(mecha.cabin_air && mecha.cabin_air.return_volume() > 0)
|
|
var/delta = mecha.cabin_air.temperature - T20C
|
|
mecha.cabin_air.temperature -= max(-10, min(10, round(delta/4,0.1)))
|
|
return
|
|
|
|
/datum/global_iterator/mecha_tank_give_air
|
|
delay = 15
|
|
|
|
process(var/obj/mecha/mecha)
|
|
if(mecha.internal_tank)
|
|
var/datum/gas_mixture/tank_air = mecha.internal_tank.return_air()
|
|
var/datum/gas_mixture/cabin_air = mecha.cabin_air
|
|
|
|
var/release_pressure = mecha.internal_tank_valve
|
|
var/cabin_pressure = cabin_air.return_pressure()
|
|
var/pressure_delta = min(release_pressure - cabin_pressure, (tank_air.return_pressure() - cabin_pressure)/2)
|
|
var/transfer_moles = 0
|
|
if(pressure_delta > 0) //cabin pressure lower than release pressure
|
|
if(tank_air.return_temperature() > 0)
|
|
transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION)
|
|
var/datum/gas_mixture/removed = tank_air.remove(transfer_moles)
|
|
cabin_air.merge(removed)
|
|
else if(pressure_delta < 0) //cabin pressure higher than release pressure
|
|
var/datum/gas_mixture/t_air = mecha.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.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION)
|
|
var/datum/gas_mixture/removed = cabin_air.remove(transfer_moles)
|
|
if(t_air)
|
|
t_air.merge(removed)
|
|
else //just delete the cabin gas, we're in space or some shit
|
|
del(removed)
|
|
else
|
|
return stop()
|
|
return
|
|
|
|
/datum/global_iterator/mecha_intertial_movement //inertial movement in space
|
|
delay = 7
|
|
|
|
process(var/obj/mecha/mecha as obj,direction)
|
|
if(direction)
|
|
if(!step(mecha, direction)||mecha.check_for_support())
|
|
src.stop()
|
|
else
|
|
src.stop()
|
|
return
|
|
|
|
/datum/global_iterator/mecha_internal_damage // processing internal damage
|
|
|
|
process(var/obj/mecha/mecha)
|
|
if(!mecha.hasInternalDamage())
|
|
return stop()
|
|
if(mecha.hasInternalDamage(MECHA_INT_FIRE))
|
|
if(!mecha.hasInternalDamage(MECHA_INT_TEMP_CONTROL) && prob(5))
|
|
mecha.clearInternalDamage(MECHA_INT_FIRE)
|
|
if(mecha.internal_tank)
|
|
if(mecha.internal_tank.return_pressure()>mecha.internal_tank.maximum_pressure && !(mecha.hasInternalDamage(MECHA_INT_TANK_BREACH)))
|
|
mecha.setInternalDamage(MECHA_INT_TANK_BREACH)
|
|
var/datum/gas_mixture/int_tank_air = mecha.internal_tank.return_air()
|
|
if(int_tank_air && int_tank_air.return_volume()>0) //heat the air_contents
|
|
int_tank_air.temperature = min(6000+T0C, int_tank_air.temperature+rand(10,15))
|
|
if(mecha.cabin_air && mecha.cabin_air.return_volume()>0)
|
|
mecha.cabin_air.temperature = min(6000+T0C, mecha.cabin_air.return_temperature()+rand(10,15))
|
|
if(mecha.cabin_air.return_temperature()>mecha.max_temperature/2)
|
|
mecha.take_damage(4/round(mecha.max_temperature/mecha.cabin_air.return_temperature(),0.1),"fire")
|
|
if(mecha.hasInternalDamage(MECHA_INT_TEMP_CONTROL)) //stop the mecha_preserve_temp loop datum
|
|
mecha.pr_int_temp_processor.stop()
|
|
if(mecha.hasInternalDamage(MECHA_INT_TANK_BREACH)) //remove some air from internal tank
|
|
if(mecha.internal_tank)
|
|
var/datum/gas_mixture/int_tank_air = mecha.internal_tank.return_air()
|
|
var/datum/gas_mixture/leaked_gas = int_tank_air.remove_ratio(0.10)
|
|
if(mecha.loc && hascall(mecha.loc,"assume_air"))
|
|
mecha.loc.assume_air(leaked_gas)
|
|
else
|
|
del(leaked_gas)
|
|
if(mecha.hasInternalDamage(MECHA_INT_SHORT_CIRCUIT))
|
|
if(mecha.get_charge())
|
|
mecha.spark_system.start()
|
|
mecha.cell.charge -= min(20,mecha.cell.charge)
|
|
mecha.cell.maxcharge -= min(20,mecha.cell.maxcharge)
|
|
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
|
|
*/
|