#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 #define MECHA_OPERATING 0 #define MECHA_BOLTS_SECURED 1 #define MECHA_PANEL_LOOSE 2 #define MECHA_CELL_OPEN 3 #define MECHA_CELL_OUT 4 #define MECH_FACTION_NT "nano" #define MECH_FACTION_SYNDI "syndi" #define MECH_FACTION_NONE "none" /obj/mecha name = "Mecha" desc = "Exosuit" description_info = "Alt click to strafe." 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/encumbrance_gap = 1 //How many points of slowdown are negated from equipment? Added to the mech's base step_in. var/dir_in = 2 //What direction will the mech face when entered/powered on? Defaults to South. var/step_energy_drain = 10 var/health = 300 //Health is health var/maxhealth = 300 //Maxhealth is maxhealth. var/deflect_chance = 10 //Chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act. //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, "bio"=1, "rad"=1 ) var/damage_minimum = 10 //Incoming damage lower than this won't actually deal damage. Scrapes shouldn't be a real thing. var/minimum_penetration = 15 //Incoming damage won't be fully applied if you don't have at least 20. Almost all AP clears this. var/fail_penetration_value = 0.66 //By how much failing to penetrate reduces your shit. 66% by default. 100dmg = 66dmg if failed pen var/obj/item/weapon/cell/cell var/state = MECHA_OPERATING 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 var/force = 0 var/mech_faction = null var/firstactivation = 0 //It's simple. If it's 0, no one entered it yet. Otherwise someone entered it at least once. var/stomp_sound = 'sound/mecha/mechstep.ogg' var/swivel_sound = 'sound/mecha/mechturn.ogg' //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 //Kelvin values. var/internal_damage_threshold = 33 //Health percentage below which internal damage is possible var/internal_damage_minimum = 15 //At least this much damage to trigger some real bad hurt. var/internal_damage = 0 //Contains bitflags var/list/operation_req_access = list() //Required access level for mecha operation var/list/internals_req_access = list(access_engine,access_robotics) //Required access level to open cell compartment var/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 //This lists holds what stuff you bolted onto your baby ride var/obj/item/mecha_parts/mecha_equipment/selected var/max_equip = 2 var/datum/events/events //mechaequipt2 stuffs var/list/hull_equipment = new var/list/weapon_equipment = new var/list/utility_equipment = new var/list/universal_equipment = new var/list/special_equipment = new var/max_hull_equip = 2 var/max_weapon_equip = 2 var/max_utility_equip = 2 var/max_universal_equip = 2 var/max_special_equip = 1 var/list/starting_equipment = null // List containing starting tools. // Mech Components, similar to Cyborg, but Bigger. var/list/internal_components = list( MECH_HULL = null, MECH_ACTUATOR = null, MECH_ARMOR = null, MECH_GAS = null, MECH_ELECTRIC = null ) var/list/starting_components = list( /obj/item/mecha_parts/component/hull, /obj/item/mecha_parts/component/actuator, /obj/item/mecha_parts/component/armor, /obj/item/mecha_parts/component/gas, /obj/item/mecha_parts/component/electrical ) //Working exosuit vars var/list/cargo = list() var/cargo_capacity = 3 var/static/image/radial_image_eject = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_eject") var/static/image/radial_image_airtoggle = image(icon= 'icons/mob/radial.dmi', icon_state = "radial_airtank") var/static/image/radial_image_lighttoggle = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_light") var/static/image/radial_image_statpanel = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_examine2") var/datum/mini_hud/mech/minihud //Mech actions var/strafing = 0 //Are we strafing or not? var/defence_mode_possible = 0 //Can we even use defence mode? This is used to assign it to mechs and check for verbs. var/defence_mode = 0 //Are we in defence mode var/defence_deflect = 35 //How much it deflect var/overload_possible = 0 //Same as above. Don't forget to GRANT the verb&actions if you want everything to work proper. var/overload = 0 //Are our legs overloaded var/overload_coeff = 1 //How much extra energy you use when use the L E G var/zoom = 0 var/zoom_possible = 0 var/thrusters = 0 var/thrusters_possible = 0 var/phasing = 0 //Are we currently phasing var/phasing_possible = 0 //This is to allow phasing. var/can_phase = TRUE //This is an internal check during the relevant procs. var/phasing_energy_drain = 200 var/switch_dmg_type_possible = 0 //Can you switch damage type? It is mostly for the Phazon and its children. var/smoke_possible = 0 var/smoke_reserve = 5 //How many shots you have. Might make a reload later on. MIGHT. var/smoke_ready = 1 //This is a check for the whether or not the cooldown is ongoing. var/smoke_cooldown = 100 //How long you have between uses. var/datum/effect/effect/system/smoke_spread/smoke_system = new var/cloak_possible = FALSE // Can this exosuit innately cloak? ////All of those are for the HUD buttons in the top left. See Grant and Remove procs in mecha_actions. var/datum/action/innate/mecha/mech_eject/eject_action = new var/datum/action/innate/mecha/mech_toggle_internals/internals_action = new var/datum/action/innate/mecha/mech_toggle_lights/lights_action = new var/datum/action/innate/mecha/mech_view_stats/stats_action = new var/datum/action/innate/mecha/strafe/strafing_action = new var/datum/action/innate/mecha/mech_defence_mode/defence_action = new var/datum/action/innate/mecha/mech_overload_mode/overload_action = new var/datum/action/innate/mecha/mech_smoke/smoke_action = new var/datum/action/innate/mecha/mech_zoom/zoom_action = new var/datum/action/innate/mecha/mech_toggle_thrusters/thrusters_action = new var/datum/action/innate/mecha/mech_cycle_equip/cycle_action = new var/datum/action/innate/mecha/mech_switch_damtype/switch_damtype_action = new var/datum/action/innate/mecha/mech_toggle_phasing/phasing_action = new var/datum/action/innate/mecha/mech_toggle_cloaking/cloak_action = new var/weapons_only_cycle = FALSE //So combat mechs don't switch to their equipment at times. /obj/mecha/Initialize() . = ..() for(var/path in starting_components) var/obj/item/mecha_parts/component/C = new path(src) C.attach(src) if(starting_equipment && LAZYLEN(starting_equipment)) for(var/path in starting_equipment) var/obj/item/mecha_parts/mecha_equipment/ME = new path(src) ME.attach(src) update_transform() /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() 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) if(smoke_possible)//I am pretty sure that's needed here. src.smoke_system.set_up(3, 0, src) src.smoke_system.attach(src) add_cell() add_iterators() removeVerb(/obj/mecha/verb/disconnect_from_port) log_message("[src.name] created.") loc.Entered(src) mechas_list += src //global mech list return /obj/mecha/Exit(atom/movable/O) if(O in cargo) return 0 return ..() /obj/mecha/Destroy() src.go_out() for(var/mob/M in src) //Be Extra Sure M.forceMove(get_turf(src)) M.loc.Entered(M) if(M != src.occupant) step_rand(M) for(var/atom/movable/A in src.cargo) A.forceMove(get_turf(src)) var/turf/T = get_turf(A) if(T) T.Entered(A) step_rand(A) if(loc) loc.Exited(src) if(prob(30)) explosion(get_turf(loc), 0, 0, 1, 3) if(wreckage) var/obj/effect/decal/mecha_wreckage/WR = new wreckage(loc) hull_equipment.Cut() weapon_equipment.Cut() utility_equipment.Cut() universal_equipment.Cut() special_equipment.Cut() for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) if(E.salvageable && prob(30)) WR.crowbar_salvage += E E.forceMove(WR) E.equip_ready = 1 else E.forceMove(loc) E.destroy() for(var/slot in internal_components) var/obj/item/mecha_parts/component/C = internal_components[slot] if(istype(C)) C.damage_part(rand(10, 20)) C.detach() WR.crowbar_salvage += C C.forceMove(WR) if(cell) WR.crowbar_salvage += cell cell.forceMove(WR) cell.charge = rand(0, cell.charge) if(internal_tank) WR.crowbar_salvage += internal_tank internal_tank.forceMove(WR) else for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) E.detach(loc) E.destroy() for(var/slot in internal_components) var/obj/item/mecha_parts/component/C = internal_components[slot] if(istype(C)) C.detach() qdel(C) if(cell) qdel(cell) if(internal_tank) qdel(internal_tank) equipment.Cut() cell = null internal_tank = null if(smoke_possible) //Just making sure nothing is running. qdel(smoke_system) GLOB.mech_destroyed_roundstat++ QDEL_NULL(pr_int_temp_processor) QDEL_NULL(pr_inertial_movement) QDEL_NULL(pr_give_air) QDEL_NULL(pr_internal_damage) QDEL_NULL(spark_system) QDEL_NULL(minihud) mechas_list -= src //global mech list . = ..() //////////////////////// ////// 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.name = "mecha power cell" cell.charge = 15000 cell.maxcharge = 15000 /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/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, iMaintenance protocols in effect") return if(phasing)//Phazon and other mechs with phasing. src.occupant_message("Unable to interact with objects while phasing")//Haha dumbass. return if(!get_charge()) return if(src == target) return var/dir_to_target = get_dir(src,target) if(dir_to_target && !(dir_to_target & src.dir))//wrong direction return if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) target = safepick(view(3,target)) if(!target) return if(istype(target, /obj/machinery)) if (src.interface_action(target)) return if(!target.Adjacent(src)) if(selected && selected.is_ranged()) selected.action(target) else if(selected && selected.is_melee()) selected.action(target, params) else src.melee_action(target) return /obj/mecha/proc/interface_action(obj/machinery/target) if(istype(target, /obj/machinery/access_button)) src.occupant_message("Interfacing with [target].") src.log_message("Interfaced with [target].") target.attack_hand(src.occupant) return 1 if(istype(target, /obj/machinery/embedded_controller)) target.ui_interact(src.occupant) 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("Interfacing with [src_object]...") 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. /obj/mecha/proc/melee_action(atom/target) return /obj/mecha/proc/range_action(atom/target) return ////////////////////////////////// //////// Movement procs //////// ////////////////////////////////// /obj/mecha/Moved(atom/old_loc, direction, forced = FALSE) . = ..() MoveAction() /obj/mecha/proc/MoveAction() //Allows mech equipment to do an action once the mech moves if(!equipment.len) return for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) ME.MoveAction() /obj/mecha/relaymove(mob/user,direction) if(user != src.occupant) //While not "realistic", this piece is player friendly. if(istype(user,/mob/living/carbon/brain)) to_chat(user, "You try to move, but you are not the pilot! The exosuit doesn't respond.") return 0 user.forceMove(get_turf(src)) to_chat(user, "You climb out from [src]") return 0 var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL] if(!HC) occupant_message("You can't operate an exosuit that doesn't have a hull!") return if(connected_port) if(world.time - last_message > 20) src.occupant_message("Unable to move while connected to the air system port") last_message = world.time return 0 if(state) occupant_message("Maintenance protocols in effect") return /* if(zoom) if(world.time - last_message > 20) src.occupant_message("Unable to move while in zoom mode.") last_message = world.time return 0 */ return domove(direction) /obj/mecha/proc/can_ztravel() for(var/obj/item/mecha_parts/mecha_equipment/tool/jetpack/jp in equipment) return jp.equip_ready return FALSE /obj/mecha/proc/domove(direction) return call((proc_res["dyndomove"]||src), "dyndomove")(direction) /obj/mecha/proc/get_step_delay() var/tally = 0 if(LAZYLEN(equipment)) for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) if(ME.get_step_delay()) tally += ME.get_step_delay() if(tally <= encumbrance_gap) // If the total is less than our encumbrance gap, ignore equipment weight. tally = 0 else // Otherwise, start the tally after cutting that gap out. tally -= encumbrance_gap for(var/slot in internal_components) var/obj/item/mecha_parts/component/C = internal_components[slot] if(C && C.get_step_delay()) tally += C.get_step_delay() var/obj/item/mecha_parts/component/actuator/actuator = internal_components[MECH_ACTUATOR] if(!actuator) // Relying purely on hydraulic pumps. You're going nowhere fast. tally = 2 SECONDS return tally tally += 0.5 SECONDS * (1 - actuator.get_efficiency()) // Damaged actuators run slower, slowing as damage increases beyond its threshold. if(strafing) tally = round(tally * actuator.strafing_multiplier) for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) if(istype(ME, /obj/item/mecha_parts/mecha_equipment/speedboost)) var/obj/item/mecha_parts/mecha_equipment/speedboost/SB = ME for(var/path in ME.required_type) if(istype(src, path)) tally = round(tally * SB.slowdown_multiplier) break break if(overload) // At the end, because this would normally just make the mech *slower* since tally wasn't starting at 0. tally = min(1, round(tally/2)) return max(1, round(tally, 0.1)) // Round the total to the nearest 10th. Can't go lower than 1 tick. Even humans have a delay longer than that. /obj/mecha/proc/dyndomove(direction) if(!can_move) return 0 if(src.pr_inertial_movement.active()) return 0 if(!has_charge(step_energy_drain)) return 0 //Can we even move, below is if yes. if(defence_mode)//Check if we are currently locked down if(world.time - last_message > 20) src.occupant_message("Unable to move while in defence mode") last_message = world.time return 0 if(zoom)//:eyes: if(world.time - last_message > 20) src.occupant_message("Unable to move while in zoom mode.") last_message = world.time return 0 /* //A first draft of a check to stop mechs from moving fully. TBD when all thrusters modules are unified. if(!thrusters && !src.pr_inertial_movement.active() && isspace(src.loc))//No thrsters, not drifting, in space src.occupant_message("Error 543")//debug return 0 */ if(!thrusters && src.pr_inertial_movement.active()) //I think this mean 'if you try to move in space without thruster, u no move' return 0 if(overload)//Check if you have leg overload health-- if(health < initial(health) - initial(health)/3) overload = 0 step_energy_drain = initial(step_energy_drain) src.occupant_message("Leg actuators damage threshold exceded. Disabling overload.") var/move_result = 0 if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) move_result = mechsteprand() //Up/down zmove else if(direction & UP || direction & DOWN) if(!can_ztravel()) occupant_message("Your vehicle lacks the capacity to move in that direction!") return FALSE //We're using locs because some mecha are 2x2 turfs. So thicc! var/result = TRUE for(var/turf/T in locs) if(!T.CanZPass(src,direction)) occupant_message("You can't move that direction from here!") result = FALSE break var/turf/dest = direction & UP ? GetAbove(T) : GetBelow(T) if(!dest) occupant_message("There is nothing of interest in this direction.") result = FALSE break if(!dest.CanZPass(src,direction)) occupant_message("There's something blocking your movement in that direction!") result = FALSE break if(result) move_result = mechstep(direction) //Turning else if(src.dir != direction) if(strafing) move_result = mechstep(direction) else move_result = mechturn(direction) //Stepping else move_result = mechstep(direction) if(move_result) can_move = 0 use_power(step_energy_drain) if(istype(src.loc, /turf/space)) if(!src.check_for_support()) src.pr_inertial_movement.start(list(src,direction)) src.log_message("Movement control lost. Inertial movement started.") if(do_after(get_step_delay())) can_move = 1 return 1 return 0 /obj/mecha/proc/handle_equipment_movement() for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) if(ME.chassis == src) //Sanity ME.handle_movement_action() return /obj/mecha/proc/mechturn(direction) set_dir(direction) if(swivel_sound) playsound(src,swivel_sound,40,1) return 1 /obj/mecha/proc/mechstep(direction) var/current_dir = dir //For strafing var/result = get_step(src,direction) if(result && Move(result)) if(stomp_sound) playsound(src,stomp_sound,40,1) handle_equipment_movement() if(strafing) //Also for strafing set_dir(current_dir) return result /obj/mecha/proc/mechsteprand() var/result = get_step_rand(src) if(result && Move(result)) if(stomp_sound) playsound(src,stomp_sound,40,1) handle_equipment_movement() return result /obj/mecha/Bump(var/atom/obstacle) // src.inertia_dir = null if(istype(obstacle, /mob))//First we check if it is a mob. Mechs mostly shouln't go through them, even while phasing. var/mob/M = obstacle M.Move(get_step(obstacle,src.dir)) else if(istype(obstacle, /obj))//Then we check for regular obstacles. var/obj/O = obstacle if(phasing && get_charge()>=phasing_energy_drain)//Phazon check. This could use an improvement elsewhere. spawn() if(can_phase) can_phase = FALSE flick("[initial_icon]-phase", src) src.loc = get_step(src,src.dir) src.use_power(phasing_energy_drain) sleep(get_step_delay() * 3) can_phase = TRUE occupant_message("Phazed.") . = ..(obstacle) return if(istype(O, /obj/effect/portal)) //derpfix src.anchored = 0 //I have no idea what this really fix. O.Crossed(src) spawn(0)//countering portal teleport spawn(0), hurr src.anchored = 1 else if(O.anchored) obstacle.Bumped(src) else step(obstacle,src.dir) else//No idea when this triggers, so i won't touch it. . = ..(obstacle) return /////////////////////////////////// //////// Internal damage //////// /////////////////////////////////// //ATM, the ignore_threshold is literally only used for the pulse rifles beams used mostly by deathsquads. /obj/mecha/proc/check_for_internal_damage(var/list/possible_int_damage,var/ignore_threshold=null) if(!islist(possible_int_damage) || isemptylist(possible_int_damage)) return if(prob(30)) if(ignore_threshold || src.health*100/initial(src.health) < src.internal_damage_threshold) for(var/T in possible_int_damage) if(internal_damage & T) possible_int_damage -= T var/int_dam_flag = safepick(possible_int_damage) if(int_dam_flag) setInternalDamage(int_dam_flag) return //It already hurts to get some, lets not get both. if(prob(10)) if(ignore_threshold || src.health*100/initial(src.health) < src.internal_damage_threshold) var/obj/item/mecha_parts/mecha_equipment/destr = safepick(equipment) if(destr) destr.destroy() return /obj/mecha/proc/hasInternalDamage(int_dam_flag=null) return int_dam_flag ? internal_damage&int_dam_flag : internal_damage /obj/mecha/proc/setInternalDamage(int_dam_flag) if(!pr_internal_damage) return internal_damage |= int_dam_flag pr_internal_damage.start() log_append_to_last("Internal damage of type [int_dam_flag].",1) occupant << sound('sound/mecha/internaldmgalarm.ogg',volume=50) //Better sounding. return /obj/mecha/proc/clearInternalDamage(int_dam_flag) internal_damage &= ~int_dam_flag switch(int_dam_flag) if(MECHA_INT_TEMP_CONTROL) occupant_message("Life support system reactivated.") pr_int_temp_processor.start() if(MECHA_INT_FIRE) occupant_message("Internal fire extinquished.") if(MECHA_INT_TANK_BREACH) occupant_message("Damaged internal tank has been sealed.") return //////////////////////////////////////// //////// Health related procs //////// //////////////////////////////////////// /obj/mecha/take_damage(amount, type="brute") update_damage_alerts() if(amount) var/damage = absorbDamage(amount,type) damage = components_handle_damage(damage,type) health -= damage update_health() log_append_to_last("Took [damage] points of damage. Damage type: \"[type]\".",1) return /obj/mecha/proc/components_handle_damage(var/damage, var/type = BRUTE) var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR] if(AC) var/armor_efficiency = AC.get_efficiency() var/damage_change = armor_efficiency * (damage * 0.5) * AC.damage_absorption[type] AC.damage_part(damage_change, type) damage -= damage_change var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL] if(HC) if(HC.integrity) var/hull_absorb = round(rand(5, 10) / 10, 0.1) * damage HC.damage_part(hull_absorb, type) damage -= hull_absorb for(var/obj/item/mecha_parts/component/C in (internal_components - list(MECH_HULL, MECH_ARMOR))) if(prob(C.relative_size)) var/damage_part_amt = round(damage / 4, 0.1) C.damage_part(damage_part_amt) damage -= damage_part_amt return damage /obj/mecha/proc/get_damage_absorption() var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR] if(!istype(AC)) return else if(AC.get_efficiency() > 0.25) return AC.damage_absorption 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(get_damage_absorption(),damage_type) || 1) /obj/mecha/airlock_crush(var/crush_damage) ..() take_damage(crush_damage) if(prob(50)) //Try to avoid that. check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) return 1 /obj/mecha/proc/update_health() if(src.health > 0) src.spark_system.start() else qdel(src) return /obj/mecha/attack_hand(mob/user as mob) if(user == occupant) show_radial_occupant(user) return user.setClickCooldown(user.get_attack_speed()) src.log_message("Attack by hand/paw. Attacker - [user].",1) var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] var/temp_deflect_chance = deflect_chance if(!ArmC) temp_deflect_chance = 1 else temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) if(istype(user,/mob/living/carbon/human)) var/mob/living/carbon/human/H = user if(H.species.can_shred(user)) if(!prob(temp_deflect_chance)) src.take_damage(15) //The take_damage() proc handles armor values if(prob(25)) //Why would they get free internal damage. At least make it a bit RNG. src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) playsound(src, 'sound/weapons/slash.ogg', 50, 1, -1) to_chat(user, "You slash at the armored suit!") visible_message("\The [user] slashes at [src.name]'s armor!") else src.log_append_to_last("Armor saved.") playsound(src, 'sound/weapons/slash.ogg', 50, 1, -1) to_chat(user, "Your claws had no effect!") src.occupant_message("\The [user]'s claws are stopped by the armor.") visible_message("\The [user] rebounds off [src.name]'s armor!") else user.visible_message("\The [user] hits \the [src]. Nothing happens.","You hit \the [src] with no visible effect.") src.log_append_to_last("Armor saved.") return else if ((HULK in user.mutations) && !prob(temp_deflect_chance)) src.take_damage(15) //The take_damage() proc handles armor values if(prob(25)) //Hulks punch hard but lets not give them consistent internal damage. src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) user.visible_message("[user] hits [src.name], doing some damage.", "You hit [src.name] with all your might. The metal creaks and bends.") else user.visible_message("[user] hits [src.name]. Nothing happens.","You hit [src.name] with no visible effect.") src.log_append_to_last("Armor saved.") 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 //I think this is relative to throws. /obj/mecha/proc/dynhitby(atom/movable/A) var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] var/temp_deflect_chance = deflect_chance var/temp_damage_minimum = damage_minimum var/temp_minimum_penetration = minimum_penetration var/temp_fail_penetration_value = fail_penetration_value if(!ArmC) temp_deflect_chance = 0 temp_damage_minimum = 0 temp_minimum_penetration = 0 temp_fail_penetration_value = 1 else temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) temp_minimum_penetration = round(ArmC.get_efficiency() * ArmC.minimum_penetration) temp_fail_penetration_value = round(ArmC.get_efficiency() * ArmC.fail_penetration_value) if(istype(A, /obj/item/mecha_parts/mecha_tracking)) A.forceMove(src) src.visible_message("The [A] fastens firmly to [src].") return if(prob(temp_deflect_chance) || istype(A, /mob)) src.occupant_message("\The [A] bounces off the armor.") 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) var/pass_damage = O.throwforce var/pass_damage_reduc_mod if(pass_damage <= temp_damage_minimum)//Too little to go through. src.occupant_message("\The [A] bounces off the armor.") src.visible_message("\The [A] bounces off \the [src] armor") return else if(O.armor_penetration < temp_minimum_penetration) //If you don't have enough pen, you won't do full damage src.occupant_message("\The [A] struggles to bypass \the [src] armor.") src.visible_message("\The [A] struggles to bypass \the [src] armor") pass_damage_reduc_mod = temp_fail_penetration_value //This will apply to reduce damage to 2/3 or 66% by default else src.occupant_message("\The [A] manages to pierce \the [src] armor.") // src.visible_message("\The [A] manages to pierce \the [src] armor") pass_damage_reduc_mod = 1 for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) pass_damage = ME.handle_ranged_contact(A, pass_damage) pass_damage = (pass_damage*pass_damage_reduc_mod)//Applying damage reduction src.take_damage(pass_damage) //The take_damage() proc handles armor values if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) return /obj/mecha/bullet_act(var/obj/item/projectile/Proj) //wrapper if(istype(Proj, /obj/item/projectile/test)) var/obj/item/projectile/test/Test = Proj Test.hit |= occupant // Register a hit on the occupant, for things like turrets, or in simple-mob cases stopping friendly fire in firing line mode. return src.log_message("Hit by projectile. Type: [Proj.name]([Proj.check_armour]).",1) call((proc_res["dynbulletdamage"]||src), "dynbulletdamage")(Proj) //calls equipment ..() return /obj/mecha/proc/dynbulletdamage(var/obj/item/projectile/Proj) var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] var/temp_deflect_chance = deflect_chance var/temp_damage_minimum = damage_minimum var/temp_minimum_penetration = minimum_penetration var/temp_fail_penetration_value = fail_penetration_value if(!ArmC) temp_deflect_chance = 0 temp_damage_minimum = 0 temp_minimum_penetration = 0 temp_fail_penetration_value = 1 else temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) temp_minimum_penetration = round(ArmC.get_efficiency() * ArmC.minimum_penetration) temp_fail_penetration_value = round(ArmC.get_efficiency() * ArmC.fail_penetration_value) if(prob(temp_deflect_chance)) src.occupant_message("The armor deflects incoming projectile.") src.visible_message("The [src.name] armor deflects the projectile") src.log_append_to_last("Armor saved.") return if(Proj.damage_type == HALLOSS) use_power(Proj.agony * 5) if(!(Proj.nodamage)) var/ignore_threshold if(istype(Proj, /obj/item/projectile/beam/pulse)) //ATM, this is literally only for the pulse rifles used mostly by deathsquads. ignore_threshold = 1 var/pass_damage = Proj.damage var/pass_damage_reduc_mod for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) pass_damage = ME.handle_projectile_contact(Proj, pass_damage) if(pass_damage < temp_damage_minimum)//too pathetic to really damage you. src.occupant_message("The armor deflects incoming projectile.") src.visible_message("The [src.name] armor deflects\the [Proj]") return else if(Proj.armor_penetration < temp_minimum_penetration) //If you don't have enough pen, you won't do full damage src.occupant_message("\The [Proj] struggles to pierce \the [src] armor.") src.visible_message("\The [Proj] struggles to pierce \the [src] armor") pass_damage_reduc_mod = temp_fail_penetration_value //This will apply to reduce damage to 2/3 or 66% by default else //You go through completely because you use AP. Nice. src.occupant_message("\The [Proj] manages to pierce \the [src] armor.") // src.visible_message("\The [Proj] manages to pierce \the [src] armor") pass_damage_reduc_mod = 1 pass_damage = (pass_damage_reduc_mod*pass_damage)//Apply damage reduction before usage. src.take_damage(pass_damage, Proj.check_armour) //The take_damage() proc handles armor values if(prob(25)) spark_system.start() if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),ignore_threshold) //AP projectiles have a chance to cause additional damage if(Proj.penetrating) var/distance = get_dist(Proj.starting, get_turf(loc)) var/hit_occupant = 1 //only allow the occupant to be hit once for(var/i in 1 to min(Proj.penetrating, round(Proj.damage/15))) if(src.occupant && hit_occupant && prob(20)) Proj.attack_mob(src.occupant, distance) hit_occupant = 0 else if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT), 1) Proj.penetrating-- if(prob(15)) break //give a chance to exit early Proj.on_hit(src) //on_hit just returns if it's argument is not a living mob so does this actually do anything? return //This refer to whenever you are caught in an explosion. /obj/mecha/ex_act(severity) var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] var/temp_deflect_chance = deflect_chance if(!ArmC) temp_deflect_chance = 0 else temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) src.log_message("Affected by explosion of severity: [severity].",1) if(prob(temp_deflect_chance)) severity++ src.log_append_to_last("Armor saved, changing severity to [severity].") switch(severity) if(1.0) src.take_damage(initial(src.health), "bomb") if(2.0) if (prob(30)) src.take_damage(initial(src.health), "bomb") else src.take_damage(initial(src.health)/2, "bomb") src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) if(3.0) if (prob(5)) qdel(src) else src.take_damage(initial(src.health)/5, "bomb") src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) return /*Will fix later -Sieve /obj/mecha/attack_blob(mob/user as mob) src.log_message("Attack by blob. Attacker - [user].",1) if(!prob(src.deflect_chance)) src.take_damage(6) src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) playsound(src, 'sound/effects/blobattack.ogg', 50, 1, -1) to_chat(user, "You smash at the armored suit!") for (var/mob/V in viewers(src)) if(V.client && !(V.blinded)) V.show_message("\The [user] smashes against [src.name]'s armor!", 1) else src.log_append_to_last("Armor saved.") playsound(src, 'sound/effects/blobattack.ogg', 50, 1, -1) to_chat(user, "Your attack had no effect!") src.occupant_message("\The [user]'s attack is stopped by the armor.") for (var/mob/V in viewers(src)) if(V.client && !(V.blinded)) V.show_message("\The [user] rebounds off the [src.name] armor!", 1) return */ /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) if(prob(80)) check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) return /obj/mecha/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) if(exposed_temperature>src.max_temperature) src.log_message("Exposed to dangerous temperature.",1) src.take_damage(5,"fire") //The take_damage() proc handles armor values src.check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL)) return /obj/mecha/proc/dynattackby(obj/item/weapon/W as obj, mob/user as mob) user.setClickCooldown(user.get_attack_speed(W)) src.log_message("Attacked by [W]. Attacker - [user]") var/pass_damage_reduc_mod //Modifer for failing to bring AP. var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] var/temp_deflect_chance = deflect_chance var/temp_damage_minimum = damage_minimum var/temp_minimum_penetration = minimum_penetration var/temp_fail_penetration_value = fail_penetration_value if(!ArmC) temp_deflect_chance = 0 temp_damage_minimum = 0 temp_minimum_penetration = 0 temp_fail_penetration_value = 1 else temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) temp_minimum_penetration = round(ArmC.get_efficiency() * ArmC.minimum_penetration) temp_fail_penetration_value = round(ArmC.get_efficiency() * ArmC.fail_penetration_value) if(prob(temp_deflect_chance)) //Does your attack get deflected outright. src.occupant_message("\The [W] bounces off [src.name].") to_chat(user, "\The [W] bounces off [src.name].") src.log_append_to_last("Armor saved.") else if(W.force < temp_damage_minimum) //Is your attack too PATHETIC to do anything. 3 damage to a person shouldn't do anything to a mech. src.occupant_message("\The [W] bounces off the armor.") src.visible_message("\The [W] bounces off \the [src] armor") return else if(W.armor_penetration < temp_minimum_penetration) //If you don't have enough pen, you won't do full damage src.occupant_message("\The [W] struggles to bypass \the [src] armor.") src.visible_message("\The [W] struggles to bypass \the [src] armor") pass_damage_reduc_mod = temp_fail_penetration_value //This will apply to reduce damage to 2/3 or 66% by default else pass_damage_reduc_mod = 1 //Just making sure. src.occupant_message("[user] hits [src] with [W].") user.visible_message("[user] hits [src] with [W].", "You hit [src] with [W].") var/pass_damage = W.force pass_damage = (pass_damage*pass_damage_reduc_mod) //Apply the reduction of damage from not having enough armor penetration. This is not regular armor values at play. for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) pass_damage = ME.handle_projectile_contact(W, user, pass_damage) src.take_damage(pass_damage,W.damtype) //The take_damage() proc handles armor values if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) return ////////////////////// ////// AttackBy ////// ////////////////////// /obj/mecha/attackby(obj/item/weapon/W as obj, mob/user as mob) if(istype(W, /obj/item/device/mmi)) if(mmi_move_inside(W,user)) to_chat(user, "[src]-MMI interface initialized successfuly") else to_chat(user, "[src]-MMI interface initialization failed.") return if(istype(W, /obj/item/device/robotanalyzer)) var/obj/item/device/robotanalyzer/RA = W RA.do_scan(src, user) return if(istype(W, /obj/item/mecha_parts/mecha_equipment)) var/obj/item/mecha_parts/mecha_equipment/E = W spawn() if(E.can_attach(src)) user.drop_item() E.attach(src) user.visible_message("[user] attaches [W] to [src]", "You attach [W] to [src]") else to_chat(user, "You were unable to attach [W] to [src]") return if(istype(W, /obj/item/mecha_parts/component) && state == MECHA_CELL_OUT) var/obj/item/mecha_parts/component/MC = W spawn() if(MC.attach(src)) user.drop_item() MC.forceMove(src) user.visible_message("[user] installs \the [W] in \the [src]", "You install \the [W] in \the [src].") return if(istype(W, /obj/item/weapon/card/robot)) var/obj/item/weapon/card/robot/RoC = W return attackby(RoC.dummy_card, user) 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 to_chat(user, "Invalid ID: Access denied.") else to_chat(user, "Maintenance protocols disabled by operator.") else if(W.is_wrench()) if(state==MECHA_BOLTS_SECURED) state = MECHA_PANEL_LOOSE to_chat(user, "You undo the securing bolts.") else if(state==MECHA_PANEL_LOOSE) state = MECHA_BOLTS_SECURED to_chat(user, "You tighten the securing bolts.") return else if(W.is_crowbar()) if(state==MECHA_PANEL_LOOSE) state = MECHA_CELL_OPEN to_chat(user, "You open the hatch to the power unit") else if(state==MECHA_CELL_OPEN) state=MECHA_PANEL_LOOSE to_chat(user, "You close the hatch to the power unit") else if(state==MECHA_CELL_OUT) var/list/removable_components = list() for(var/slot in internal_components) var/obj/item/mecha_parts/component/MC = internal_components[slot] if(istype(MC)) removable_components[MC.name] = MC else to_chat(user, "\The [src] appears to be missing \the [slot].") var/remove = input(user, "Which component do you want to pry out?", "Remove Component") as null|anything in removable_components if(!remove) return var/obj/item/mecha_parts/component/RmC = removable_components[remove] RmC.detach() return else if(istype(W, /obj/item/stack/cable_coil)) if(state >= MECHA_CELL_OPEN && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) var/obj/item/stack/cable_coil/CC = W if(CC.use(2)) clearInternalDamage(MECHA_INT_SHORT_CIRCUIT) to_chat(user, "You replace the fused wires.") else to_chat(user, "There's not enough wire to finish the task.") return else if(W.is_screwdriver()) if(hasInternalDamage(MECHA_INT_TEMP_CONTROL)) clearInternalDamage(MECHA_INT_TEMP_CONTROL) to_chat(user, "You repair the damaged temperature controller.") else if(state==MECHA_CELL_OPEN && src.cell) src.cell.forceMove(src.loc) src.cell = null state = MECHA_CELL_OUT to_chat(user, "You unscrew and pry out the powercell.") src.log_message("Powercell removed") else if(state==MECHA_CELL_OUT && src.cell) state=MECHA_CELL_OPEN to_chat(user, "You screw the cell in place") return else if(istype(W, /obj/item/device/multitool)) if(state>=MECHA_CELL_OPEN && src.occupant) to_chat(user, "You attempt to eject the pilot using the maintenance controls.") if(src.occupant.stat) src.go_out() src.log_message("[src.occupant] was ejected using the maintenance controls.") else to_chat(user, "Your attempt is rejected.") src.occupant_message("An attempt to eject you was made using the maintenance controls.") src.log_message("Eject attempt made using maintenance controls - rejected.") return else if(istype(W, /obj/item/weapon/cell)) if(state==MECHA_CELL_OUT) if(!src.cell) to_chat(user, "You install the powercell") user.drop_item() W.forceMove(src) src.cell = W src.log_message("Powercell installed") else to_chat(user, "There's already a powercell installed.") return else if(istype(W, /obj/item/weapon/weldingtool) && 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) to_chat(user, "You repair the damaged gas tank.") else return if(src.healthYou repair some damage to [src.name].") src.health += min(10, initial(src.health)-src.health) update_damage_alerts() else to_chat(user, "The [src.name] is at full integrity") return else if(istype(W, /obj/item/mecha_parts/mecha_tracking)) user.drop_from_inventory(W) W.forceMove(src) user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src]") return else if(istype(W,/obj/item/stack/nanopaste)) if(state >= MECHA_PANEL_LOOSE) var/obj/item/stack/nanopaste/NP = W for(var/slot in internal_components) var/obj/item/mecha_parts/component/C = internal_components[slot] if(C) if(C.integrity < C.max_integrity) while(C.integrity < C.max_integrity && NP && do_after(user, 1 SECOND, src)) if(NP.use(1)) C.adjust_integrity(10) to_chat(user, "You repair damage to \the [C].") return else to_chat(user, "You can't reach \the [src]'s internal components.") return else call((proc_res["dynattackby"]||src), "dynattackby")(W,user) /* src.log_message("Attacked by [W]. Attacker - [user]") if(prob(src.deflect_chance)) to_chat(user, "\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("[user] hits [src] with [W].") user.visible_message("[user] hits [src] with [W].", "You hit [src] with [W].") src.take_damage(W.force,W.damtype) src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) */ return /* /obj/mecha/attack_ai(var/mob/living/silicon/ai/user as mob) if(!istype(user, /mob/living/silicon/ai)) return var/output = {"Assume direct control over [src]? Yes
"} user << browse(output, "window=mecha_attack_ai") return */ /////////////////////////////// //////// Brain Stuff //////// /////////////////////////////// /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) to_chat(user, "Consciousness matrix not detected.") return 0 else if(mmi_as_oc.brainmob.stat) to_chat(user, "Brain activity below acceptable level.") return 0 else if(occupant) to_chat(user, "Occupant detected.") return 0 else if(dna && dna!=mmi_as_oc.brainmob.dna.unique_enzymes) to_chat(user, "Genetic sequence or serial number incompatible with locking mechanism.") return 0 //Added a message here since people assume their first click failed or something./N // to_chat(user, "Installing MMI, please stand by.") visible_message("[usr] starts to insert a brain into [src.name]") if(enter_after(40,user)) if(!occupant) return mmi_moved_inside(mmi_as_oc,user) else to_chat(user, "Occupant detected.") else to_chat(user, "You stop attempting to install the brain.") return 0 /obj/mecha/proc/mmi_moved_inside(var/obj/item/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) to_chat(user, "Consciousness matrix not detected.") return 0 else if(mmi_as_oc.brainmob.stat) to_chat(user, "Beta-rhythm below acceptable level.") return 0 user.drop_from_inventory(mmi_as_oc) var/mob/brainmob = mmi_as_oc.brainmob 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) update_icon() set_dir(dir_in) src.log_message("[mmi_as_oc] moved in as pilot.") if(!hasInternalDamage()) src.occupant << sound('sound/mecha/nominal.ogg',volume=50) update_icon() return 1 else return 0 ///////////////////////////////////// //////// Atmospheric stuff //////// ///////////////////////////////////// /obj/mecha/proc/get_turf_air() var/turf/T = get_turf(src) if(T) . = T.return_air() return /obj/mecha/remove_air(amount) var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] if(use_internal_tank && (GC && prob(GC.get_efficiency() * 100))) return cabin_air.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 var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] if(use_internal_tank && (GC && prob(GC.get_efficiency() * 100))) . = cabin_air.return_pressure() else var/datum/gas_mixture/t_air = get_turf_air() if(t_air) . = t_air.return_pressure() return //skytodo: //No idea what you want me to do here, mate. /obj/mecha/proc/return_temperature() . = 0 var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] if(use_internal_tank && (GC && prob(GC.get_efficiency() * 100))) . = cabin_air.temperature else var/datum/gas_mixture/t_air = get_turf_air() if(t_air) . = t_air.temperature return /obj/mecha/proc/connect(obj/machinery/atmospherics/portables_connector/new_port) //Make sure not already connected to something else if(connected_port || !new_port || new_port.connected_device) return 0 //Make sure are close enough for a valid connection if(!(new_port.loc in locs)) return 0 //Perform the connection connected_port = new_port connected_port.connected_device = src //Actually enforce the air sharing var/datum/pipe_network/network = connected_port.return_network(src) if(network && !(internal_tank.return_air() in network.gases)) network.gases += internal_tank.return_air() network.update = 1 playsound(src, 'sound/mecha/gasconnected.ogg', 50, 1) log_message("Connected to gas port.") return 1 /obj/mecha/proc/disconnect() if(!connected_port) return 0 var/datum/pipe_network/network = connected_port.return_network(src) if(network) network.gases -= internal_tank.return_air() connected_port.connected_device = null connected_port = null playsound(src, 'sound/mecha/gasdisconnected.ogg', 50, 1) src.log_message("Disconnected from gas port.") return 1 ///////////////////////// //////// Verbs //////// ///////////////////////// /obj/mecha/verb/connect_to_port() set name = "Connect to port" set category = "Exosuit Interface" set src = usr.loc set popup_menu = 0 if(!occupant) return if(usr != occupant) return var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] if(!GC) return for(var/turf/T in locs) var/obj/machinery/atmospherics/portables_connector/possible_port = locate(/obj/machinery/atmospherics/portables_connector) in T if(possible_port) if(connect(possible_port)) occupant_message("\The [name] connects to the port.") verbs += /obj/mecha/verb/disconnect_from_port verbs -= /obj/mecha/verb/connect_to_port return else occupant_message("\The [name] failed to connect to the port.") return else occupant_message("Nothing happens") /obj/mecha/verb/disconnect_from_port() set name = "Disconnect from port" set category = "Exosuit Interface" set src = usr.loc set popup_menu = 0 if(!occupant) return if(usr != occupant) return if(disconnect()) occupant_message("[name] disconnects from the port.") verbs -= /obj/mecha/verb/disconnect_from_port verbs += /obj/mecha/verb/connect_to_port else occupant_message("[name] is not connected to the port at the moment.") /obj/mecha/verb/toggle_lights() set name = "Toggle Lights" set category = "Exosuit Interface" set src = usr.loc set popup_menu = 0 lights() /obj/mecha/verb/lights() if(usr!=occupant) return lights = !lights if(lights) set_light(light_range + lights_power) else set_light(light_range - lights_power) src.occupant_message("Toggled lights [lights?"on":"off"].") log_message("Toggled lights [lights?"on":"off"].") playsound(src, 'sound/mecha/heavylightswitch.ogg', 50, 1) return /obj/mecha/verb/toggle_internal_tank() set name = "Toggle internal airtank usage" set category = "Exosuit Interface" set src = usr.loc set popup_menu = 0 internal_tank() /obj/mecha/proc/internal_tank() if(usr!=src.occupant) return var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] if(!GC) to_chat(occupant, "The life support systems don't seem to respond.") return if(!prob(GC.get_efficiency() * 100)) to_chat(occupant, "\The [GC] shudders and barks, before returning to how it was before.") return use_internal_tank = !use_internal_tank src.occupant_message("Now taking air from [use_internal_tank?"internal airtank":"environment"].") src.log_message("Now taking air from [use_internal_tank?"internal airtank":"environment"].") playsound(src, 'sound/mecha/gasdisconnected.ogg', 30, 1) return /obj/mecha/verb/toggle_strafing() set name = "Toggle strafing" set category = "Exosuit Interface" set src = usr.loc set popup_menu = 0 strafing() /obj/mecha/proc/strafing() if(usr!=src.occupant) return strafing = !strafing src.occupant_message("Toggled strafing mode [strafing?"on":"off"].") src.log_message("Toggled strafing mode [strafing?"on":"off"].") return /obj/mecha/MouseDrop_T(mob/O, mob/user as mob) //Humans can pilot mechs. if(!ishuman(O)) return //Can't put other people into mechs (can comment this out if you want that to be possible) if(O != user) return move_inside() /obj/mecha/verb/enter() set category = "Object" set name = "Enter Exosuit" set src in oview(1) move_inside() //returns an equipment object if we have one of that type, useful since is_type_in_list won't return the object //since is_type_in_list uses caching, this is a slower operation, so only use it if needed /obj/mecha/proc/get_equipment(var/equip_type) for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) if(istype(ME,equip_type)) return ME return null /obj/mecha/proc/move_inside() if (usr.stat || !ishuman(usr)) return if (usr.buckled) to_chat(usr, "You can't climb into the exosuit while buckled!") return src.log_message("[usr] tries to move in.") if(iscarbon(usr)) var/mob/living/carbon/C = usr if(C.handcuffed) to_chat(usr, "Kinda hard to climb in while handcuffed don't you think?") return if (src.occupant) to_chat(usr, "The [src.name] is already occupied!") src.log_append_to_last("Permission denied.") return /* if (usr.abiotic()) to_chat(usr, "Subject cannot have abiotic items on.") 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) to_chat(usr, "Access denied") src.log_append_to_last("Permission denied.") return if(isliving(usr)) var/mob/living/L = usr if(L.has_buckled_mobs()) to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first.")) return // to_chat(usr, "You start climbing into [src.name]") if(get_equipment(/obj/item/mecha_parts/mecha_equipment/runningboard)) visible_message("\The [usr] is instantly lifted into [src.name] by the running board!") moved_inside(usr) if(ishuman(occupant)) GrantActions(occupant, 1) else visible_message("\The [usr] starts to climb into [src.name]") if(enter_after(40,usr)) if(!src.occupant) moved_inside(usr) if(ishuman(occupant)) //Aeiou GrantActions(occupant, 1) else if(src.occupant!=usr) to_chat(usr, "[src.occupant] was faster. Try better next time, loser.") else to_chat(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.verbs += /obj/mecha/verb/eject src.log_append_to_last("[H] moved in as pilot.") update_icon() //This part removes all the verbs if you don't have them the _possible on your mech. This is a little clunky, but it lets you just add that to any mech. //And it's not like this 10yo code wasn't clunky before. if(!smoke_possible) //Can't use smoke? No verb for you. verbs -= /obj/mecha/verb/toggle_smoke if(!thrusters_possible) //Can't use thrusters? No verb for you. verbs -= /obj/mecha/verb/toggle_thrusters if(!defence_mode_possible) //Do i need to explain everything? verbs -= /obj/mecha/verb/toggle_defence_mode if(!overload_possible) verbs -= /obj/mecha/verb/toggle_overload if(!zoom_possible) verbs -= /obj/mecha/verb/toggle_zoom if(!phasing_possible) verbs -= /obj/mecha/verb/toggle_phasing if(!switch_dmg_type_possible) verbs -= /obj/mecha/verb/switch_damtype if(!cloak_possible) verbs -= /obj/mecha/verb/toggle_cloak occupant.in_enclosed_vehicle = 1 //Useful for when you need to know if someone is in a mecho. if(occupant.hud_used) minihud = new (occupant.hud_used, src) update_cell_alerts() update_damage_alerts() set_dir(dir_in) playsound(src, 'sound/machines/door/windowdoor.ogg', 50, 1) if(occupant.client && cloaked_selfimage) occupant.client.images += cloaked_selfimage play_entered_noise(occupant) return 1 else return 0 /obj/mecha/proc/play_entered_noise(var/mob/who) if(!hasInternalDamage()) //Otherwise it's not nominal! switch(mech_faction) if(MECH_FACTION_NT)//The good guys category if(firstactivation)//First time = long activation sound firstactivation = 1 who << sound('sound/mecha/LongNanoActivation.ogg',volume=50) else who << sound('sound/mecha/nominalnano.ogg',volume=50) if(MECH_FACTION_SYNDI)//Bad guys if(firstactivation) firstactivation = 1 who << sound('sound/mecha/LongSyndiActivation.ogg',volume=50) else who << sound('sound/mecha/nominalsyndi.ogg',volume=50) else//Everyone else gets the normal noise who << sound('sound/mecha/nominal.ogg',volume=50) /obj/mecha/AltClick(mob/living/user) if(user == occupant) strafing() /obj/mecha/verb/view_stats() set name = "View Stats" set category = "Exosuit Interface" set src = usr.loc set popup_menu = 0 if(usr!=src.occupant) return //pr_update_stats.start() src.occupant << browse(src.get_stats_html(), "window=exosuit") return /* /obj/mecha/verb/force_eject() set category = "Object" set name = "Force Eject" set src in view(5) src.go_out() return */ /obj/mecha/verb/eject() set name = "Eject" set category = "Exosuit Interface" set src = usr.loc set popup_menu = 0 if(usr!=src.occupant) return src.go_out() add_fingerprint(usr) return /obj/mecha/proc/go_out() //Eject/Exit the mech. Yes this is for easier searching. if(!src.occupant) return var/atom/movable/mob_container QDEL_NULL(minihud) if(ishuman(occupant)) mob_container = src.occupant RemoveActions(occupant, human_occupant=1)//AEIOU else if(istype(occupant, /mob/living/carbon/brain)) var/mob/living/carbon/brain/brain = occupant mob_container = brain.container else return if(mob_container.forceMove(src.loc))//ejecting mob container log_message("[mob_container] moved out.") occupant.reset_view() occupant << browse(null, "window=exosuit") if(occupant.client && cloaked_selfimage) occupant.client.images -= cloaked_selfimage 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 occupant.canmove = 0 occupant.clear_alert("charge") occupant.clear_alert("mech damage") occupant.in_enclosed_vehicle = 0 occupant = null update_icon() set_dir(dir_in) verbs -= /obj/mecha/verb/eject //src.zoom = 0 // Doesn't seem needed. if(src.occupant && src.occupant.client) src.occupant.client.view = world.view src.zoom = 0 strafing = 0 return ///////////////////////// ////// Access stuff ///// ///////////////////////// /obj/mecha/proc/operation_allowed(mob/living/carbon/human/H) for(var/ID in list(H.get_active_hand(), H.wear_id, H.belt)) if(src.check_access(ID,src.operation_req_access)) return 1 return 0 /obj/mecha/proc/internals_access_allowed(mob/living/carbon/human/H) if(istype(H)) for(var/atom/ID in list(H.get_active_hand(), H.wear_id, H.belt)) if(src.check_access(ID,src.internals_req_access)) return 1 else if(istype(H, /mob/living/silicon/robot)) var/mob/living/silicon/robot/R = H if(src.check_access(R.idcard,src.internals_req_access)) return 1 return 0 /obj/mecha/check_access(obj/item/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 = {" [src.name] data
[src.get_stats_part()]
[src.get_equipment_list()]

[src.get_commands()]
"} return output /obj/mecha/proc/report_internal_damage() var/output = null var/list/dam_reports = list( "[MECHA_INT_FIRE]" = "INTERNAL FIRE", "[MECHA_INT_TEMP_CONTROL]" = "LIFE SUPPORT SYSTEM MALFUNCTION", "[MECHA_INT_TANK_BREACH]" = "GAS TANK BREACH", "[MECHA_INT_CONTROL_LOST]" = "COORDINATION SYSTEM CALIBRATION FAILURE - Recalibrate", "[MECHA_INT_SHORT_CIRCUIT]" = "SHORT CIRCUIT" ) for(var/tflag in dam_reports) var/intdamflag = text2num(tflag) if(hasInternalDamage(intdamflag)) output += dam_reports[tflag] output += "
" if(return_pressure() > WARNING_HIGH_PRESSURE) output += "DANGEROUSLY HIGH CABIN PRESSURE
" return output /obj/mecha/proc/get_stats_part() var/integrity = health/initial(health)*100 var/cell_charge = get_charge() var/tank_pressure = internal_tank ? round(internal_tank.return_pressure(),0.01) : "None" var/tank_temperature = internal_tank ? internal_tank.return_temperature() : "Unknown" var/cabin_pressure = round(return_pressure(),0.01) var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL] var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR] var/output = {"[report_internal_damage()] Armor Integrity: [AC?"[round(AC.integrity / AC.max_integrity * 100, 0.1)]%":"ARMOR MISSING"]
Hull Integrity: [HC?"[round(HC.integrity / HC.max_integrity * 100, 0.1)]%":"HULL MISSING"]
[integrity<30?"DAMAGE LEVEL CRITICAL
":null] Chassis Integrity: [integrity]%
Powercell charge: [isnull(cell_charge)?"No powercell installed":"[cell.percent()]%"]
Air source: [use_internal_tank?"Internal Airtank":"Environment"]
Airtank pressure: [tank_pressure]kPa
Airtank temperature: [tank_temperature]K|[tank_temperature - T0C]°C
Cabin pressure: [cabin_pressure>WARNING_HIGH_PRESSURE ? "[cabin_pressure]": cabin_pressure]kPa
Cabin temperature: [return_temperature()]K|[return_temperature() - T0C]°C
Lights: [lights?"on":"off"]
[src.dna?"DNA-locked:
[src.dna] \[Reset\]
":null] "} if(defence_mode_possible) output += "Defence mode: [defence_mode?"on":"off"]
" if(overload_possible) output += "Leg actuators overload: [overload?"on":"off"]
" if(smoke_possible) output += "Smoke: [smoke_reserve]
" if(thrusters_possible) output += "Thrusters: [thrusters?"on":"off"]
" //Cargo components. Keep this last otherwise it does weird alignment issues. output += "Cargo Compartment Contents:
" if(src.cargo.len) for(var/obj/O in src.cargo) output += "Unload : [O]
" else output += "Nothing" output += "
" return output /obj/mecha/proc/get_commands() var/output = {"
Electronics
Airtank
[get_equipment_menu()]

[(/obj/mecha/verb/eject in src.verbs)?"Eject
":null] "} return output /obj/mecha/proc/get_equipment_menu() //outputs mecha html equipment menu var/output if(equipment.len) output += {"
Equipment
"} return output /obj/mecha/proc/get_equipment_list() //outputs mecha equipment list in html if(!equipment.len) return var/output = "Equipment:
" for(var/obj/item/mecha_parts/mecha_equipment/MT in equipment) output += "
[MT.get_equip_info()]
" output += "
" return output /obj/mecha/proc/get_log_html() var/output = "[src.name] Log" for(var/list/entry in log) output += {"
[time2text(entry["time"],"DDD MMM DD hh:mm:ss")] [game_year]
[entry["message"]]
"} output += "" return output /obj/mecha/proc/output_access_dialog(obj/item/weapon/card/id/id_card, mob/user) if(!id_card || !user) return var/output = {"

Following keycodes are present in this system:

"} for(var/a in operation_req_access) output += "[get_access_desc(a)] - Delete
" output += "

Following keycodes were detected on portable device:

" 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] - Add
" output += "
Finish (Warning! The ID upload panel will be locked. It can be unlocked only through Exosuit Interface.)" output += "" 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 = "Set Cabin Air Pressure" if (locate(/obj/item/mecha_parts/mecha_equipment/tool/passenger) in contents) maint_options += "Remove Passenger" var/output = {" [add_req_access?"Edit operation keycodes":null] [maint_access?"Initiate maintenance protocol":null] [(state>0) ? maint_options : ""] "} user << browse(output, "window=exosuit_maint_console") onclose(user, "exosuit_maint_console") return //////////////////////////////// /////// Messages and Log /////// //////////////////////////////// /obj/mecha/proc/occupant_message(message as text) if(message) if(src.occupant && src.occupant.client) to_chat(src.occupant, "[bicon(src)] [message]") return /obj/mecha/proc/log_message(message as text,red=null) log.len++ log[log.len] = list("time"=world.timeofday,"message"="[red?"":null][message][red?"":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"] += "
[red?"":null][message][red?"":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/top_filter = new /datum/topic_input(href,href_list) if(href_list["select_equip"]) if(usr != src.occupant) return var/obj/item/mecha_parts/mecha_equipment/equip = top_filter.getObj("select_equip") if(equip) src.selected = equip src.occupant_message("You switch to [equip]") src.visible_message("[src] raises [equip]") send_byjax(src.occupant,"exosuit.browser","eq_list",src.get_equipment_list()) return if(href_list["eject"]) if(usr != src.occupant) return src.eject() return if(href_list["toggle_lights"]) if(usr != src.occupant) return src.lights() return /* if(href_list["toggle_strafing"]) if(usr != src.occupant) return src.strafing() return*/ if(href_list["toggle_airtank"]) if(usr != src.occupant) return src.internal_tank() return if (href_list["toggle_thrusters"]) src.toggle_thrusters() if (href_list["smoke"]) src.smoke() if (href_list["toggle_zoom"]) src.zoom() if(href_list["toggle_defence_mode"]) src.defence_mode() if(href_list["switch_damtype"]) src.switch_damtype() if(href_list["phasing"]) src.phasing() if(href_list["rmictoggle"]) if(usr != src.occupant) return radio.broadcasting = !radio.broadcasting send_byjax(src.occupant,"exosuit.browser","rmicstate",(radio.broadcasting?"Engaged":"Disengaged")) return if(href_list["rspktoggle"]) if(usr != src.occupant) return radio.listening = !radio.listening send_byjax(src.occupant,"exosuit.browser","rspkstate",(radio.listening?"Engaged":"Disengaged")) return if(href_list["rfreq"]) if(usr != src.occupant) return var/new_frequency = (radio.frequency + top_filter.getNum("rfreq")) if ((radio.frequency < PUBLIC_LOW_FREQ || radio.frequency > PUBLIC_HIGH_FREQ)) new_frequency = sanitize_frequency(new_frequency) radio.set_frequency(new_frequency) send_byjax(src.occupant,"exosuit.browser","rfreq","[format_frequency(radio.frequency)]") return if(href_list["port_disconnect"]) if(usr != src.occupant) return src.disconnect_from_port() return if (href_list["port_connect"]) if(usr != src.occupant) return src.connect_to_port() return if (href_list["view_log"]) if(usr != src.occupant) return src.occupant << browse(src.get_log_html(), "window=exosuit_log") onclose(occupant, "exosuit_log") return if (href_list["change_name"]) if(usr != src.occupant) return var/newname = sanitizeSafe(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("Maintenance protocols in effect") return maint_access = !maint_access send_byjax(src.occupant,"exosuit.browser","t_maint_access","[maint_access?"Forbid":"Permit"] maintenance protocols") return if(href_list["req_access"] && add_req_access) if(!in_range(src, usr)) return output_access_dialog(top_filter.getObj("id_card"),top_filter.getMob("user")) return if(href_list["maint_access"] && maint_access) if(!in_range(src, usr)) return var/mob/user = top_filter.getMob("user") if(user) if(state==MECHA_OPERATING) state = MECHA_BOLTS_SECURED to_chat(user, "The securing bolts are now exposed.") else if(state==MECHA_BOLTS_SECURED) state = MECHA_OPERATING to_chat(user, "The securing bolts are now hidden.") output_maintenance_dialog(top_filter.getObj("id_card"),user) return if(href_list["set_internal_tank_valve"] && state >=MECHA_BOLTS_SECURED) if(!in_range(src, usr)) return var/mob/user = top_filter.getMob("user") if(user) var/new_pressure = input(user,"Input new output pressure","Pressure setting",internal_tank_valve) as num if(new_pressure) internal_tank_valve = new_pressure to_chat(user, "The internal pressure valve has been set to [internal_tank_valve]kPa.") if(href_list["remove_passenger"] && state >= MECHA_BOLTS_SECURED) var/mob/user = top_filter.getMob("user") var/list/passengers = list() for (var/obj/item/mecha_parts/mecha_equipment/tool/passenger/P in contents) if (P.occupant) passengers["[P.occupant]"] = P if (!passengers) to_chat(user, "There are no passengers to remove.") 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("\The [user] begins opening the hatch on \the [P]...", "You begin opening the hatch on \the [P]...") if (!do_after(user, 40, needhand=0)) return user.visible_message("\The [user] opens the hatch on \the [P] and removes [occupant]!", "You open the hatch on \the [P] and remove [occupant]!") P.go_out() P.log_message("[occupant] was removed.") return if(href_list["add_req_access"] && add_req_access && top_filter.getObj("id_card")) if(!in_range(src, usr)) return operation_req_access += top_filter.getNum("add_req_access") output_access_dialog(top_filter.getObj("id_card"),top_filter.getMob("user")) return if(href_list["del_req_access"] && add_req_access && top_filter.getObj("id_card")) if(!in_range(src, usr)) return operation_req_access -= top_filter.getNum("del_req_access") output_access_dialog(top_filter.getObj("id_card"),top_filter.getMob("user")) return if(href_list["finish_req_access"]) if(!in_range(src, usr)) return add_req_access = 0 var/mob/user = top_filter.getMob("user") user << browse(null,"window=exosuit_add_access") return if(href_list["dna_lock"]) if(usr != src.occupant) return if(istype(occupant, /mob/living/carbon/brain)) occupant_message("You are a brain. No.") return if(src.occupant) src.dna = src.occupant.dna.unique_enzymes src.occupant_message("You feel a prick as the needle takes your DNA sample.") return if(href_list["reset_dna"]) if(usr != src.occupant) return src.dna = null if(href_list["repair_int_control_lost"]) if(usr != src.occupant) return src.occupant_message("Recalibrating coordination system.") src.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("Recalibration successful.") src.log_message("Recalibration of coordination system finished with 0 errors.") else src.occupant_message("Recalibration failed.") src.log_message("Recalibration of coordination system failed with 1 error.",1) if(href_list["drop_from_cargo"]) var/obj/O = locate(href_list["drop_from_cargo"]) if(O && O in src.cargo) src.occupant_message("You unload [O].") O.forceMove(get_turf(src)) src.cargo -= O var/turf/T = get_turf(O) if(T) T.Entered(O) src.log_message("Unloaded [O]. Cargo compartment capacity: [cargo_capacity - src.cargo.len]") return //debug /* if(href_list["debug"]) if(href_list["set_i_dam"]) setInternalDamage(top_filter.getNum("set_i_dam")) if(href_list["clear_i_dam"]) clearInternalDamage(top_filter.getNum("clear_i_dam")) return */ /* if (href_list["ai_take_control"]) var/mob/living/silicon/ai/AI = locate(href_list["ai_take_control"]) var/duration = text2num(href_list["duration"]) var/mob/living/silicon/ai/O = new /mob/living/silicon/ai(src) var/cur_occupant = src.occupant O.invisibility = 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.set_stat(AI.stat) O.oxyloss = AI.getOxyLoss() O.fireloss = AI.getFireLoss() O.bruteloss = AI.getBruteLoss() O.toxloss = AI.toxloss O.updatehealth() src.occupant = O if(AI.mind) AI.mind.transfer_to(O) AI.name = "Inactive AI" AI.real_name = "Inactive AI" AI.icon_state = "ai-empty" spawn(duration) AI.name = O.name AI.real_name = O.real_name if(O.mind) O.mind.transfer_to(AI) AI.control_disabled = 0 AI.laws = O.laws AI.oxyloss = O.getOxyLoss() AI.fireloss = O.getFireLoss() AI.bruteloss = O.getBruteLoss() AI.toxloss = O.toxloss AI.updatehealth() qdel(O) if (!AI.stat) AI.icon_state = "ai" else AI.icon_state = "ai-crash" src.occupant = cur_occupant */ 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) update_cell_alerts() var/obj/item/mecha_parts/component/electrical/EC = internal_components[MECH_ELECTRIC] if(EC) amount = amount * (2 - EC.get_efficiency()) * EC.charge_cost_mod else amount *= 5 if(get_charge()) cell.use(amount) return 1 return 0 /obj/mecha/proc/give_power(amount) update_cell_alerts() var/obj/item/mecha_parts/component/electrical/EC = internal_components[MECH_ELECTRIC] if(!EC) amount /= 4 else amount *= EC.get_efficiency() if(!isnull(get_charge())) cell.give(amount) return 1 return 0 //This is for mobs mostly. /obj/mecha/attack_generic(var/mob/user, var/damage, var/attack_message) var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] var/temp_deflect_chance = deflect_chance var/temp_damage_minimum = damage_minimum if(!ArmC) temp_deflect_chance = 1 temp_damage_minimum = 0 else temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) user.setClickCooldown(user.get_attack_speed()) if(!damage) return 0 src.log_message("Attacked. Attacker - [user].",1) user.do_attack_animation(src) if(prob(temp_deflect_chance))//Deflected src.log_append_to_last("Armor saved.") src.occupant_message("\The [user]'s attack is stopped by the armor.") visible_message("\The [user] rebounds off [src.name]'s armor!") user.attack_log += text("\[[time_stamp()]\] attacked [src.name]") playsound(src, 'sound/weapons/slash.ogg', 50, 1, -1) else if(damage < temp_damage_minimum)//Pathetic damage levels just don't harm MECH. src.occupant_message("\The [user]'s doesn't dent \the [src] paint.") src.visible_message("\The [user]'s attack doesn't dent \the [src] armor") src.log_append_to_last("Armor saved.") playsound(src, 'sound/effects/Glasshit.ogg', 50, 1) return else src.take_damage(damage) //Apply damage - The take_damage() proc handles armor values if(damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) visible_message("[user] [attack_message] [src]!") user.attack_log += text("\[[time_stamp()]\] attacked [src.name]") return 1 ////////////////////////////////////////// //////// 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.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 /datum/global_iterator/mecha_tank_give_air/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.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 = 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.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) else return stop() return /datum/global_iterator/mecha_intertial_movement //inertial movement in space delay = 7 /datum/global_iterator/mecha_intertial_movement/process(var/obj/mecha/mecha as obj,direction) if(direction) if(!step(mecha, direction)||mecha.check_for_support()) src.stop() mecha.handle_equipment_movement() else src.stop() return /datum/global_iterator/mecha_internal_damage // processing internal damage /datum/global_iterator/mecha_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.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.volume>0) mecha.cabin_air.temperature = min(6000+T0C, mecha.cabin_air.temperature+rand(10,15)) if(mecha.cabin_air.temperature>mecha.max_temperature/2) mecha.take_damage(4/round(mecha.max_temperature/mecha.cabin_air.temperature,0.1),"fire") //The take_damage() proc handles armor values 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 qdel(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 ///////////// /obj/mecha/cloak() . = ..() if(occupant && occupant.client && cloaked_selfimage) occupant.client.images += cloaked_selfimage /obj/mecha/uncloak() if(occupant && occupant.client && cloaked_selfimage) occupant.client.images -= cloaked_selfimage return ..() //debug /* /obj/mecha/verb/test_int_damage() set name = "Test internal damage" set category = "Exosuit Interface" set src in view(0) if(!occupant) return if(usr!=occupant) return var/output = {"

Set:

MECHA_INT_FIRE
MECHA_INT_TEMP_CONTROL
MECHA_INT_SHORT_CIRCUIT
MECHA_INT_TANK_BREACH
MECHA_INT_CONTROL_LOST

Clear:

MECHA_INT_FIRE
MECHA_INT_TEMP_CONTROL
MECHA_INT_SHORT_CIRCUIT
MECHA_INT_TANK_BREACH
MECHA_INT_CONTROL_LOST
"} occupant << browse(output, "window=ex_debug") //src.health = initial(src.health)/2.2 //src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) return */ /obj/mecha/proc/update_cell_alerts() if(occupant && cell) var/cellcharge = cell.charge/cell.maxcharge switch(cellcharge) if(0.75 to INFINITY) occupant.clear_alert("charge") if(0.5 to 0.75) occupant.throw_alert("charge", /obj/screen/alert/lowcell, 1) if(0.25 to 0.5) occupant.throw_alert("charge", /obj/screen/alert/lowcell, 2) if(0.01 to 0.25) occupant.throw_alert("charge", /obj/screen/alert/lowcell, 3) else occupant.throw_alert("charge", /obj/screen/alert/emptycell) /obj/mecha/proc/update_damage_alerts() if(occupant) var/integrity = health/initial(health)*100 switch(integrity) if(30 to 45) occupant.throw_alert("mech damage", /obj/screen/alert/low_mech_integrity, 1) if(15 to 35) occupant.throw_alert("mech damage", /obj/screen/alert/low_mech_integrity, 2) if(-INFINITY to 15) occupant.throw_alert("mech damage", /obj/screen/alert/low_mech_integrity, 3) else occupant.clear_alert("mech damage")