mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-14 17:45:02 +01:00
4fe16acd0b
## About The Pull Request `shock` was copy pasted across a bunch of base types I needed the behavior unified and it was fairly trivial to do So now we have `/obj/proc/shock` which all the old implementations call ## Changelog 🆑 Melbert refactor: Made some minor changes to how things like airlocks, vendors, and autolathes shock you. Report any wierdness with that /🆑
1025 lines
39 KiB
Plaintext
1025 lines
39 KiB
Plaintext
/***************** WELCOME TO MECHA.DM, ENJOY YOUR STAY *****************/
|
|
|
|
/**
|
|
* Mechs are now (finally) vehicles, this means you can make them multicrew
|
|
* They can also grant select ability buttons based on occupant bitflags
|
|
*
|
|
* Movement is handled through vehicle_move() which is called by relaymove
|
|
* Clicking is done by way of signals registering to the entering mob
|
|
* NOTE: MMIS are NOT mobs but instead contain a brain that is, so you need special checks
|
|
* AI also has special checks becaus it gets in and out of the mech differently
|
|
* Always call remove_occupant(mob) when leaving the mech so the mob is removed properly
|
|
*
|
|
* For multi-crew, you need to set how the occupants receive ability bitflags corresponding to their status on the vehicle(i.e: driver, gunner etc)
|
|
* Abilities can then be set to only apply for certain bitflags and are assigned as such automatically
|
|
*
|
|
* Clicks are wither translated into mech_melee_attack (see mech_melee_attack.dm)
|
|
* Or are used to call action() on equipped gear
|
|
* Cooldown for gear is on the mech because exploits
|
|
* Cooldown for melee is on mech_melee_attack also because exploits
|
|
*/
|
|
/obj/vehicle/sealed/mecha
|
|
name = "exosuit"
|
|
desc = "Exosuit"
|
|
icon = 'icons/mob/rideables/mecha.dmi'
|
|
resistance_flags = FIRE_PROOF | ACID_PROOF
|
|
max_integrity = 300
|
|
armor_type = /datum/armor/sealed_mecha
|
|
force = 5
|
|
movedelay = 1 SECONDS
|
|
move_force = MOVE_FORCE_VERY_STRONG
|
|
move_resist = MOVE_FORCE_EXTREMELY_STRONG
|
|
light_system = OVERLAY_LIGHT_DIRECTIONAL
|
|
light_on = FALSE
|
|
light_range = 6
|
|
generic_canpass = FALSE
|
|
hud_possible = list(DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_TRACK_HUD, DIAG_CAMERA_HUD)
|
|
mouse_pointer = 'icons/effects/mouse_pointers/mecha_mouse.dmi'
|
|
/// Significantly heavier than humans
|
|
inertia_force_weight = 5
|
|
///How much energy the mech will consume each time it moves. this is the current active energy consumed
|
|
var/step_energy_drain = 0.008 * STANDARD_CELL_CHARGE
|
|
///How much energy we drain each time we mechpunch someone
|
|
var/melee_energy_drain = 0.015 * STANDARD_CELL_CHARGE
|
|
///Power we use to have the lights on
|
|
var/light_power_drain = 0.002 * STANDARD_CELL_RATE
|
|
///Modifiers for directional damage reduction
|
|
var/list/facing_modifiers = list(MECHA_FRONT_ARMOUR = 0.5, MECHA_SIDE_ARMOUR = 1, MECHA_BACK_ARMOUR = 1.5)
|
|
///if we cant use our equipment(such as due to EMP)
|
|
var/equipment_disabled = FALSE
|
|
/// Keeps track of the mech's cell
|
|
var/obj/item/stock_parts/power_store/cell
|
|
/// Keeps track of the mech's scanning module
|
|
var/obj/item/stock_parts/scanning_module/scanmod
|
|
/// Keeps track of the mech's capacitor
|
|
var/obj/item/stock_parts/capacitor/capacitor
|
|
/// Keeps track of the mech's servo motor
|
|
var/obj/item/stock_parts/servo/servo
|
|
///Contains flags for the mecha
|
|
var/mecha_flags = CAN_STRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE | BEACON_TRACKABLE | AI_COMPATIBLE | BEACON_CONTROLLABLE
|
|
|
|
///Spark effects are handled by this datum
|
|
var/datum/effect_system/basic/spark_spread/spark_system
|
|
///How powerful our lights are
|
|
var/lights_power = 6
|
|
///Just stop the mech from doing anything
|
|
var/completely_disabled = FALSE
|
|
///Whether this mech is allowed to move diagonally
|
|
var/allow_diagonal_movement = TRUE
|
|
///Whether this mech moves into a direct as soon as it goes to move. Basically, turn and step in the same key press.
|
|
var/pivot_step = FALSE
|
|
///Whether or not the mech destroys walls by running into it.
|
|
var/bumpsmash = FALSE
|
|
|
|
///////////ATMOS
|
|
///Whether the cabin exchanges gases with the environment
|
|
var/cabin_sealed = FALSE
|
|
///Internal air mix datum
|
|
var/datum/gas_mixture/cabin_air
|
|
///Volume of the cabin
|
|
var/cabin_volume = TANK_STANDARD_VOLUME * 3
|
|
|
|
///List of installed remote tracking beacons, including AI control beacons
|
|
var/list/trackers = list()
|
|
///Camera installed into the mech
|
|
var/obj/machinery/camera/exosuit/chassis_camera
|
|
|
|
var/max_temperature = 25000
|
|
|
|
///Bitflags for internal damage
|
|
var/internal_damage = NONE
|
|
/// % chance for internal damage to occur
|
|
var/internal_damage_probability = 20
|
|
/// list of possibly dealt internal damage for this mech type
|
|
var/possible_int_damage = MECHA_INT_FIRE|MECHA_INT_TEMP_CONTROL|MECHA_CABIN_AIR_BREACH|MECHA_INT_CONTROL_LOST|MECHA_INT_SHORT_CIRCUIT
|
|
/// damage threshold above which we take component damage
|
|
var/component_damage_threshold = 10
|
|
|
|
///Stores the DNA enzymes of a carbon so tht only they can access the mech
|
|
var/dna_lock
|
|
/// A list of all granted accesses
|
|
var/list/accesses = list()
|
|
/// If the mech should require ALL or only ONE of the listed accesses
|
|
var/one_access = TRUE
|
|
|
|
///Typepath for the wreckage it spawns when destroyed
|
|
var/wreckage
|
|
///single flag for the type of this mech, determines what kind of equipment can be attached to it
|
|
var/mech_type
|
|
|
|
///assoc list: key-typepathlist before init, key-equipmentlist after
|
|
var/list/equip_by_category = list(
|
|
MECHA_L_ARM = null,
|
|
MECHA_R_ARM = null,
|
|
MECHA_UTILITY = list(),
|
|
MECHA_POWER = list(),
|
|
MECHA_ARMOR = list(),
|
|
)
|
|
///assoc list: max equips for modules key-count
|
|
var/list/max_equip_by_category = list(
|
|
MECHA_L_ARM = 1,
|
|
MECHA_R_ARM = 1,
|
|
MECHA_UTILITY = 2,
|
|
MECHA_POWER = 1,
|
|
MECHA_ARMOR = 0,
|
|
)
|
|
///flat equipment for iteration
|
|
var/list/flat_equipment
|
|
|
|
///Handles an internal ore box for mining mechs
|
|
var/obj/structure/ore_box/ore_box
|
|
|
|
///Whether our steps are silent due to no gravity
|
|
var/step_silent = FALSE
|
|
///Sound played when the mech moves
|
|
var/stepsound = 'sound/vehicles/mecha/mechstep.ogg'
|
|
///Sound played when the mech walks
|
|
var/turnsound = 'sound/vehicles/mecha/mechturn.ogg'
|
|
///Sounds for types of melee attack
|
|
var/brute_attack_sound = 'sound/items/weapons/punch4.ogg'
|
|
var/burn_attack_sound = 'sound/items/tools/welder.ogg'
|
|
var/tox_attack_sound = 'sound/effects/spray2.ogg'
|
|
///Sound on wall destroying
|
|
var/destroy_wall_sound = 'sound/effects/meteorimpact.ogg'
|
|
|
|
///Melee attack verb
|
|
var/list/attack_verbs = list("hit", "hits", "hitting")
|
|
|
|
///Cooldown duration between melee punches
|
|
var/melee_cooldown = CLICK_CD_SLOW
|
|
|
|
///TIme taken to leave the mech
|
|
var/exit_delay = 2 SECONDS
|
|
///Time you get slept for if you get forcible ejected by the mech exploding
|
|
var/destruction_sleep_duration = 2 SECONDS
|
|
///In case theres a different iconstate for AI/MMI pilot(currently only used for ripley)
|
|
var/silicon_icon_state = null
|
|
///Currently ejecting, and unable to do things
|
|
var/is_currently_ejecting = FALSE
|
|
///Safety for weapons. Won't fire if enabled, and toggled by middle click.
|
|
var/weapons_safety = FALSE
|
|
///Don't play standard sound when set safety if TRUE.
|
|
var/safety_sound_custom = FALSE
|
|
|
|
var/datum/effect_system/fluid_spread/smoke/smoke_system
|
|
|
|
////Action vars
|
|
///Ref to any active thrusters we might have
|
|
var/obj/item/mecha_parts/mecha_equipment/thrusters/active_thrusters
|
|
|
|
///Bool for energy shield on/off
|
|
var/defense_mode = FALSE
|
|
|
|
///Bool for leg overload on/off
|
|
VAR_FINAL/overclock_mode = FALSE
|
|
///Whether it is possible to toggle overclocking from the cabin
|
|
var/can_use_overclock = FALSE
|
|
///Speed and energy usage modifier for leg overload
|
|
var/overclock_coeff = 1.5
|
|
///Current leg actuator temperature. Increases when overloaded, decreases when not.
|
|
var/overclock_temp = 0
|
|
///Temperature threshold at which actuators may start causing internal damage
|
|
var/overclock_temp_danger = 15
|
|
///Whether the mech has an option to enable safe overclocking
|
|
var/overclock_safety_available = FALSE
|
|
///Whether the overclocking turns off automatically when overheated
|
|
var/overclock_safety = FALSE
|
|
/// Action type for overclocking, if null - no action
|
|
var/overclock_action_type = /datum/action/vehicle/sealed/mecha/mech_overclock
|
|
/// Name of the overclock action
|
|
var/overclock_name = "overclock"
|
|
|
|
//Bool for zoom on/off
|
|
var/zoom_mode = FALSE
|
|
|
|
///Remaining smoke charges
|
|
var/smoke_charges = 5
|
|
///Cooldown between using smoke
|
|
var/smoke_cooldown = 10 SECONDS
|
|
|
|
///check for phasing, if it is set to text (to describe how it is phasing: "flying", "phasing") it will let the mech walk through walls.
|
|
var/phasing = ""
|
|
///Power we use every time we phaze through something
|
|
var/phasing_energy_drain = 0.2 * STANDARD_CELL_CHARGE
|
|
///icon_state for flick() when phazing
|
|
var/phase_state = ""
|
|
|
|
///Wether we are strafing
|
|
var/strafe = FALSE
|
|
|
|
///Bool for whether this mech can only be used on lavaland
|
|
var/lavaland_only = FALSE
|
|
|
|
/// ref to screen object that displays in the middle of the UI
|
|
var/atom/movable/screen/map_view/ui_view
|
|
|
|
/// Theme of the mech TGUI
|
|
var/ui_theme = "ntos"
|
|
/// Module selected by default when mech UI is opened
|
|
var/ui_selected_module_index
|
|
|
|
/datum/armor/sealed_mecha
|
|
melee = 20
|
|
bullet = 10
|
|
bomb = 10
|
|
fire = 100
|
|
acid = 100
|
|
|
|
/obj/vehicle/sealed/mecha/Initialize(mapload, built_manually)
|
|
. = ..()
|
|
ui_view = new()
|
|
ui_view.generate_view("mech_view_[REF(src)]")
|
|
RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
|
|
RegisterSignal(src, COMSIG_LIGHT_EATER_ACT, PROC_REF(on_light_eater))
|
|
|
|
spark_system = new(src, 2, FALSE)
|
|
spark_system.attach(src)
|
|
smoke_system = new(src, 3, holder = src)
|
|
|
|
cabin_air = new(cabin_volume)
|
|
|
|
if(!built_manually)
|
|
populate_parts()
|
|
update_access()
|
|
set_wires(new /datum/wires/mecha(src))
|
|
START_PROCESSING(SSobj, src)
|
|
SSpoints_of_interest.make_point_of_interest(src)
|
|
log_message("[src.name] created.", LOG_MECHA)
|
|
GLOB.mechas_list += src //global mech list
|
|
prepare_huds()
|
|
var/datum/atom_hud/data/diagnostic/diag_hud = GLOB.huds[DATA_HUD_DIAGNOSTIC]
|
|
diag_hud.add_atom_to_hud(src)
|
|
diag_hud_set_mechhealth()
|
|
diag_hud_set_mechcell()
|
|
diag_hud_set_mechstat()
|
|
update_appearance()
|
|
|
|
AddElement(/datum/element/atmos_sensitive, mapload)
|
|
become_hearing_sensitive(trait_source = ROUNDSTART_TRAIT)
|
|
add_traits(list(TRAIT_ASHSTORM_IMMUNE, TRAIT_SNOWSTORM_IMMUNE), ROUNDSTART_TRAIT) //stormy weather (keeps rainin' all the time)
|
|
for(var/key in equip_by_category)
|
|
if(key == MECHA_L_ARM || key == MECHA_R_ARM)
|
|
var/path = equip_by_category[key]
|
|
if(!path)
|
|
continue
|
|
var/obj/item/mecha_parts/mecha_equipment/thing = new path
|
|
thing.attach(src, key == MECHA_R_ARM)
|
|
continue
|
|
for(var/path in equip_by_category[key])
|
|
var/obj/item/mecha_parts/mecha_equipment/thing = new path
|
|
thing.attach(src, FALSE)
|
|
equip_by_category[key] -= path
|
|
|
|
AddElement(/datum/element/falling_hazard, damage = 80, wound_bonus = 10, hardhat_safety = FALSE, crushes = TRUE)
|
|
AddElement(/datum/element/hostile_machine)
|
|
|
|
/obj/vehicle/sealed/mecha/Destroy()
|
|
/// If the former occupants get polymorphed, mutated, chestburstered,
|
|
/// or otherwise replaced by another mob, that mob is no longer in .occupants
|
|
/// and gets deleted with the mech. However, they do remain in .contents
|
|
var/list/potential_occupants = contents | occupants
|
|
for(var/mob/buggy_ejectee in potential_occupants)
|
|
mob_exit(buggy_ejectee, silent = TRUE, forced = TRUE)
|
|
|
|
if(LAZYLEN(flat_equipment))
|
|
for(var/obj/item/mecha_parts/mecha_equipment/equip as anything in flat_equipment)
|
|
equip.detach(loc)
|
|
qdel(equip)
|
|
|
|
STOP_PROCESSING(SSobj, src)
|
|
LAZYCLEARLIST(flat_equipment)
|
|
|
|
QDEL_NULL(ore_box)
|
|
|
|
QDEL_NULL(cell)
|
|
QDEL_NULL(scanmod)
|
|
QDEL_NULL(capacitor)
|
|
QDEL_NULL(servo)
|
|
QDEL_NULL(cabin_air)
|
|
QDEL_NULL(spark_system)
|
|
QDEL_NULL(smoke_system)
|
|
QDEL_NULL(ui_view)
|
|
QDEL_LIST(trackers)
|
|
QDEL_NULL(chassis_camera)
|
|
|
|
GLOB.mechas_list -= src //global mech list
|
|
var/datum/atom_hud/data/diagnostic/diag_hud = GLOB.huds[DATA_HUD_DIAGNOSTIC]
|
|
diag_hud.remove_atom_from_hud(src) //YEET
|
|
return ..()
|
|
|
|
///Add parts on mech spawning. Skipped in manual construction.
|
|
/obj/vehicle/sealed/mecha/proc/populate_parts()
|
|
cell = new /obj/item/stock_parts/power_store/cell/high(src)
|
|
scanmod = new /obj/item/stock_parts/scanning_module(src)
|
|
capacitor = new /obj/item/stock_parts/capacitor(src)
|
|
servo = new /obj/item/stock_parts/servo(src)
|
|
update_part_values()
|
|
|
|
/obj/vehicle/sealed/mecha/proc/locate_parts()
|
|
cell = locate(/obj/item/stock_parts/power_store) in contents
|
|
diag_hud_set_mechcell()
|
|
scanmod = locate(/obj/item/stock_parts/scanning_module) in contents
|
|
capacitor = locate(/obj/item/stock_parts/capacitor) in contents
|
|
servo = locate(/obj/item/stock_parts/servo) in contents
|
|
update_part_values()
|
|
|
|
/obj/vehicle/sealed/mecha/atom_destruction()
|
|
spark_system?.start()
|
|
loc.assume_air(cabin_air)
|
|
|
|
var/mob/living/silicon/ai/unlucky_ai
|
|
for(var/mob/living/occupant as anything in occupants)
|
|
if(isAI(occupant))
|
|
//FIXME: Nothiing about this block works
|
|
var/mob/living/silicon/ai/ai = occupant
|
|
if(!ai.linked_core && !ai.can_shunt) // we probably shouldnt gib AIs with a core or shunting abilities
|
|
unlucky_ai = occupant
|
|
ai.investigate_log("has been gibbed by having their mech destroyed.", INVESTIGATE_DEATHS)
|
|
ai.gib(DROP_ALL_REMAINS) //No wreck, no AI to recover
|
|
else
|
|
mob_exit(ai, silent = TRUE, forced = TRUE) // so we dont ghost the AI
|
|
continue
|
|
else
|
|
mob_exit(occupant, forced = TRUE)
|
|
if(!isbrain(occupant)) // who would win.. 1 brain vs 1 sleep proc..
|
|
occupant.SetSleeping(destruction_sleep_duration)
|
|
|
|
if(wreckage)
|
|
var/obj/structure/mecha_wreckage/WR = new wreckage(loc, unlucky_ai)
|
|
for(var/obj/item/mecha_parts/mecha_equipment/E in flat_equipment)
|
|
if(E.detachable && prob(30))
|
|
WR.crowbar_salvage += E
|
|
E.detach(WR) //detaches from src into WR
|
|
E.active = TRUE
|
|
else
|
|
E.detach(loc)
|
|
qdel(E)
|
|
if(cell)
|
|
WR.crowbar_salvage += cell
|
|
cell.forceMove(WR)
|
|
cell.use(rand(0, cell.charge), TRUE)
|
|
cell = null
|
|
return ..()
|
|
|
|
|
|
/obj/vehicle/sealed/mecha/update_icon_state()
|
|
icon_state = get_mecha_occupancy_state()
|
|
return ..()
|
|
|
|
/**
|
|
* Toggles Weapons Safety
|
|
*
|
|
* Handles enabling or disabling the safety function.
|
|
*/
|
|
/obj/vehicle/sealed/mecha/proc/set_safety(mob/user)
|
|
weapons_safety = !weapons_safety
|
|
if(!safety_sound_custom)
|
|
SEND_SOUND(user, sound('sound/machines/beep/beep.ogg', volume = 25))
|
|
balloon_alert(user, "equipment [weapons_safety ? "safe" : "ready"]")
|
|
set_mouse_pointer()
|
|
SEND_SIGNAL(src, COMSIG_MECH_SAFETIES_TOGGLE, user, weapons_safety)
|
|
|
|
/**
|
|
* Updates the pilot's mouse cursor override.
|
|
*
|
|
* If the mech's weapons safety is enabled, there should be no override, and the user gets their regular mouse cursor. If safety
|
|
* is off but the mech's equipment is disabled (such as by EMP), the cursor should be the red disabled version. Otherwise, if
|
|
* safety is off and the equipment is functional, the cursor should be the regular green cursor. This proc sets the cursor.
|
|
* correct and then updates it for each mob in the occupants list.
|
|
*/
|
|
/obj/vehicle/sealed/mecha/proc/set_mouse_pointer()
|
|
if(weapons_safety)
|
|
mouse_pointer = ""
|
|
else
|
|
if(equipment_disabled)
|
|
mouse_pointer = 'icons/effects/mouse_pointers/mecha_mouse-disable.dmi'
|
|
else
|
|
mouse_pointer = 'icons/effects/mouse_pointers/mecha_mouse.dmi'
|
|
|
|
for(var/mob/mob_occupant as anything in occupants)
|
|
mob_occupant.update_mouse_pointer()
|
|
|
|
//override this proc if you need to split up mecha control between multiple people (see savannah_ivanov.dm)
|
|
/obj/vehicle/sealed/mecha/auto_assign_occupant_flags(mob/M)
|
|
if(driver_amount() < max_drivers)
|
|
add_control_flags(M, FULL_MECHA_CONTROL)
|
|
|
|
/obj/vehicle/sealed/mecha/generate_actions()
|
|
initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_eject)
|
|
if(mecha_flags & IS_ENCLOSED)
|
|
initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/mech_toggle_cabin_seal, VEHICLE_CONTROL_SETTINGS)
|
|
if(can_use_overclock && overclock_action_type)
|
|
initialize_passenger_action_type(overclock_action_type)
|
|
initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/mech_toggle_lights, VEHICLE_CONTROL_SETTINGS)
|
|
initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/mech_toggle_safeties, VEHICLE_CONTROL_SETTINGS)
|
|
initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/mech_view_stats, VEHICLE_CONTROL_SETTINGS)
|
|
initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/strafe, VEHICLE_CONTROL_DRIVE)
|
|
|
|
/obj/vehicle/sealed/mecha/add_occupant(mob/M, control_flags, forced)
|
|
if(..())
|
|
generate_equipment_actions(M)
|
|
|
|
/obj/vehicle/sealed/mecha/remove_occupant(mob/M)
|
|
remove_all_equipment_actions(M)
|
|
return ..()
|
|
|
|
///Generates action buttons for all eligible equipment and grants them to the occupant with VEHICLE_CONTROL_SETTINGS flag.
|
|
/obj/vehicle/sealed/mecha/proc/generate_equipment_actions(mob/occupant)
|
|
if(!(occupant in occupants) || !(occupants[occupant] & VEHICLE_CONTROL_SETTINGS))
|
|
return
|
|
for(var/obj/item/mecha_parts/mecha_equipment/equipment in flat_equipment)
|
|
if(!is_equipment_valid_for_action(equipment))
|
|
continue
|
|
|
|
grant_equipment_action(occupant, equipment)
|
|
|
|
///Removes all equipment actions from a specific occupant.
|
|
/obj/vehicle/sealed/mecha/proc/remove_all_equipment_actions(mob/occupant)
|
|
var/list/actions = LAZYACCESS(occupant_actions, occupant)
|
|
if(!actions)
|
|
return
|
|
|
|
for(var/equipment_type in actions)
|
|
if(!ispath(equipment_type, /obj/item/mecha_parts/mecha_equipment))
|
|
continue
|
|
|
|
remove_action_type_from_mob(equipment_type, occupant)
|
|
|
|
/**
|
|
* Grants a specific equipment action to an occupant.
|
|
* Creates a new action, sets up the chassis and equipment references, and grants it to the mob.
|
|
*/
|
|
/obj/vehicle/sealed/mecha/proc/grant_equipment_action(mob/occupant, obj/item/mecha_parts/mecha_equipment/equipment)
|
|
var/datum/action/vehicle/sealed/mecha/equipment/action = new equipment.action_type // We cannot use grant_action_type_to_mob() because:
|
|
action.set_chassis(src) // 1. grant_action_type_to_mob() works with a single predefined action type
|
|
action.set_equipment(equipment) // 2. We create unique action instances for each equipment with specific equipment references
|
|
|
|
action.Grant(occupant)
|
|
LAZYINITLIST(occupant_actions[occupant])
|
|
// Use equipment type as actiontype for remove_action_type_from_mob() compatibility
|
|
occupant_actions[occupant][equipment.type] = action
|
|
|
|
/**
|
|
* Called when equipment is attached to the mecha.
|
|
* Grants equipment actions to current occupants with VEHICLE_CONTROL_SETTINGS flag.
|
|
*/
|
|
/obj/vehicle/sealed/mecha/proc/on_equipment_attach(obj/item/mecha_parts/mecha_equipment/equipment)
|
|
if(!is_equipment_valid_for_action(equipment))
|
|
return
|
|
|
|
for(var/mob/occupant in occupants)
|
|
if(!(occupants[occupant] & VEHICLE_CONTROL_SETTINGS))
|
|
continue
|
|
grant_equipment_action(occupant, equipment)
|
|
|
|
/**
|
|
* Called when equipment is detached from the mecha.
|
|
* Removes equipment actions from all current occupants.
|
|
*/
|
|
/obj/vehicle/sealed/mecha/proc/on_equipment_detach(obj/item/mecha_parts/mecha_equipment/equipment)
|
|
for(var/mob/occupant in occupants)
|
|
remove_action_type_from_mob(equipment.type, occupant)
|
|
|
|
/// Create actions only for equipment that can be toggled or triggered, excluding air tanks.
|
|
/obj/vehicle/sealed/mecha/proc/is_equipment_valid_for_action(obj/item/mecha_parts/mecha_equipment/equipment)
|
|
if(!(equipment.can_be_toggled || equipment.can_be_triggered))
|
|
return FALSE
|
|
|
|
if(istype(equipment, /obj/item/mecha_parts/mecha_equipment/air_tank)) // this thing has its own button
|
|
return FALSE
|
|
|
|
return TRUE
|
|
|
|
/obj/vehicle/sealed/mecha/proc/get_mecha_occupancy_state()
|
|
if((mecha_flags & SILICON_PILOT) && silicon_icon_state)
|
|
return silicon_icon_state
|
|
if(LAZYLEN(occupants))
|
|
return base_icon_state
|
|
return "[base_icon_state]-open"
|
|
|
|
/obj/vehicle/sealed/mecha/CanPassThrough(atom/blocker, movement_dir, blocker_opinion)
|
|
if(!phasing || get_charge() <= phasing_energy_drain || throwing)
|
|
return ..()
|
|
if(phase_state)
|
|
flick(phase_state, src)
|
|
var/turf/destination_turf = get_step(loc, movement_dir)
|
|
if(!check_teleport_valid(src, destination_turf) || SSmapping.level_trait(destination_turf.z, ZTRAIT_NOPHASE))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/vehicle/sealed/mecha/get_cell()
|
|
return cell
|
|
|
|
/obj/vehicle/sealed/mecha/rust_heretic_act()
|
|
take_damage(500, BRUTE)
|
|
|
|
/obj/vehicle/sealed/mecha/proc/restore_equipment()
|
|
equipment_disabled = FALSE
|
|
for(var/occupant in occupants)
|
|
var/mob/mob_occupant = occupant
|
|
SEND_SOUND(mob_occupant, sound('sound/items/timer.ogg', volume=50))
|
|
to_chat(mob_occupant, span_notice("Equipment control unit has been rebooted successfully."))
|
|
set_mouse_pointer()
|
|
|
|
/obj/vehicle/sealed/mecha/proc/update_part_values() ///Updates the values given by scanning module and capacitor tier, called when a part is removed or inserted.
|
|
update_energy_drain()
|
|
|
|
if(capacitor)
|
|
overclock_temp_danger = initial(overclock_temp_danger) * capacitor.rating
|
|
else
|
|
overclock_temp_danger = initial(overclock_temp_danger)
|
|
|
|
/obj/vehicle/sealed/mecha/examine(mob/user)
|
|
. = ..()
|
|
if(LAZYLEN(flat_equipment))
|
|
. += span_notice("It's equipped with:")
|
|
for(var/obj/item/mecha_parts/mecha_equipment/ME as anything in flat_equipment)
|
|
if(istype(ME, /obj/item/mecha_parts/mecha_equipment/concealed_weapon_bay))
|
|
continue
|
|
. += span_notice("[icon2html(ME, user)] \A [ME].")
|
|
if(mecha_flags & PANEL_OPEN)
|
|
if(servo)
|
|
. += span_notice("Servo reduces movement power usage by [100 - round(100 / servo.rating)]%")
|
|
else
|
|
. += span_warning("It's missing a servo.")
|
|
if(capacitor)
|
|
. += span_notice("Capacitor increases armor against energy attacks by [capacitor.rating * 5].")
|
|
else
|
|
. += span_warning("It's missing a capacitor.")
|
|
if(!scanmod)
|
|
. += span_warning("It's missing a scanning module.")
|
|
if(!(mecha_flags & IS_ENCLOSED))
|
|
if(mecha_flags & SILICON_PILOT)
|
|
. += span_notice("[src] appears to be piloting itself...")
|
|
else
|
|
for(var/occupante in occupants)
|
|
. += span_notice("You can see [occupante] inside.")
|
|
if(ishuman(user))
|
|
var/mob/living/carbon/human/H = user
|
|
for(var/held_item in H.held_items)
|
|
if(!isgun(held_item))
|
|
continue
|
|
. += span_warning("It looks like you can hit the pilot directly if you target the center or above.")
|
|
break //in case user is holding two guns
|
|
. += span_notice("It has a <a href='byond://?src=[REF(src)];list_armor=1'>tag</a> listing its protection classes.")
|
|
|
|
/obj/vehicle/sealed/mecha/Topic(href, href_list)
|
|
. = ..()
|
|
|
|
if(href_list["list_armor"])
|
|
var/list/readout = list()
|
|
|
|
var/datum/armor/armor = get_armor()
|
|
var/added_damage_header = FALSE
|
|
for(var/damage_key in ARMOR_LIST_DAMAGE)
|
|
var/rating = armor.get_rating(damage_key)
|
|
if(!rating)
|
|
continue
|
|
if(!added_damage_header)
|
|
readout += "<b><u>ARMOR (I-X)</u></b>"
|
|
added_damage_header = TRUE
|
|
readout += "[armor_to_protection_name(damage_key)] [armor_to_protection_class(rating)]"
|
|
|
|
var/added_durability_header = FALSE
|
|
for(var/durability_key in ARMOR_LIST_DURABILITY)
|
|
var/rating = armor.get_rating(durability_key)
|
|
if(!rating)
|
|
continue
|
|
if(!added_durability_header)
|
|
readout += "<b><u>DURABILITY (I-X)</u></b>"
|
|
added_durability_header = TRUE
|
|
readout += "[armor_to_protection_name(durability_key)] [armor_to_protection_class(rating)]"
|
|
|
|
readout += "It can withstand temperatures up to [max_temperature]K."
|
|
if(mecha_flags & IS_ENCLOSED)
|
|
readout += "It fully encloses its occupants, protecting them from the atmosphere or lack thereof."
|
|
|
|
var/formatted_readout = span_notice("<b>PROTECTION CLASSES</b><hr>[jointext(readout, "\n")]")
|
|
to_chat(usr, boxed_message(formatted_readout))
|
|
|
|
/obj/vehicle/sealed/mecha/generate_integrity_message()
|
|
var/examine_text = ""
|
|
var/integrity = atom_integrity*100/max_integrity
|
|
|
|
switch(integrity)
|
|
if(85 to 100)
|
|
examine_text = "It's fully intact."
|
|
if(65 to 85)
|
|
examine_text = "It's slightly damaged."
|
|
if(45 to 65)
|
|
examine_text = "It's badly damaged."
|
|
if(25 to 45)
|
|
examine_text = "It's heavily damaged."
|
|
else
|
|
examine_text = "It's falling apart."
|
|
|
|
return examine_text
|
|
|
|
///Locate an internal tack in the utility modules
|
|
/obj/vehicle/sealed/mecha/proc/get_internal_tank()
|
|
var/obj/item/mecha_parts/mecha_equipment/air_tank/module = locate(/obj/item/mecha_parts/mecha_equipment/air_tank) in equip_by_category[MECHA_UTILITY]
|
|
return module?.internal_tank
|
|
|
|
//processing internal damage, temperature, air regulation, alert updates, lights power use.
|
|
/obj/vehicle/sealed/mecha/process(seconds_per_tick)
|
|
if(overclock_mode || overclock_temp > 0)
|
|
process_overclock_effects(seconds_per_tick)
|
|
if(internal_damage)
|
|
process_internal_damage_effects(seconds_per_tick)
|
|
if(cabin_sealed)
|
|
process_cabin_air(seconds_per_tick)
|
|
if(length(occupants))
|
|
process_occupants(seconds_per_tick)
|
|
process_constant_power_usage(seconds_per_tick)
|
|
//Diagnostic HUD updates
|
|
diag_hud_set_mechhealth()
|
|
diag_hud_set_mechcell()
|
|
diag_hud_set_mechstat()
|
|
|
|
/obj/vehicle/sealed/mecha/proc/process_overclock_effects(seconds_per_tick)
|
|
if(!overclock_mode && overclock_temp > 0)
|
|
overclock_temp -= seconds_per_tick
|
|
return
|
|
var/temp_gain = seconds_per_tick * (1 + 1 / movedelay)
|
|
overclock_temp = min(overclock_temp + temp_gain, overclock_temp_danger * 2)
|
|
if(overclock_temp < overclock_temp_danger)
|
|
return
|
|
if(overclock_temp >= overclock_temp_danger && overclock_safety)
|
|
toggle_overclock(FALSE)
|
|
return
|
|
var/damage_chance = 100 * ((overclock_temp - overclock_temp_danger) / (overclock_temp_danger * 2))
|
|
if(SPT_PROB(damage_chance, seconds_per_tick))
|
|
do_sparks(5, TRUE, src)
|
|
try_deal_internal_damage(damage_chance)
|
|
take_damage(seconds_per_tick, BURN, 0, 0)
|
|
|
|
/obj/vehicle/sealed/mecha/proc/process_internal_damage_effects(seconds_per_tick)
|
|
if(internal_damage & MECHA_INT_FIRE)
|
|
if(!(internal_damage & MECHA_INT_TEMP_CONTROL) && SPT_PROB(2.5, seconds_per_tick))
|
|
clear_internal_damage(MECHA_INT_FIRE)
|
|
if(cabin_air && cabin_sealed && cabin_air.return_volume()>0)
|
|
if(cabin_air.return_pressure() > (PUMP_DEFAULT_PRESSURE * 30) && !(internal_damage & MECHA_CABIN_AIR_BREACH))
|
|
set_internal_damage(MECHA_CABIN_AIR_BREACH)
|
|
cabin_air.temperature = min(6000+T0C, cabin_air.temperature+rand(5,7.5)*seconds_per_tick)
|
|
if(cabin_air.return_temperature() > max_temperature/2)
|
|
take_damage(seconds_per_tick*2/round(max_temperature/cabin_air.return_temperature(),0.1), BURN, 0, 0)
|
|
|
|
if(internal_damage & MECHA_CABIN_AIR_BREACH && cabin_air && cabin_sealed) //remove some air from cabin_air
|
|
var/datum/gas_mixture/leaked_gas = cabin_air.remove_ratio(SPT_PROB_RATE(0.05, seconds_per_tick))
|
|
if(loc)
|
|
loc.assume_air(leaked_gas)
|
|
else
|
|
qdel(leaked_gas)
|
|
|
|
if(internal_damage & MECHA_INT_SHORT_CIRCUIT && get_charge())
|
|
spark_system.start()
|
|
var/damage_energy_consumption = 0.005 * STANDARD_CELL_CHARGE * seconds_per_tick
|
|
use_energy(damage_energy_consumption)
|
|
cell.maxcharge -= min(damage_energy_consumption, cell.maxcharge)
|
|
|
|
/obj/vehicle/sealed/mecha/proc/process_cabin_air(seconds_per_tick)
|
|
if(!(internal_damage & MECHA_INT_TEMP_CONTROL) && cabin_air && cabin_air.return_volume() > 0)
|
|
var/heat_capacity = cabin_air.heat_capacity()
|
|
var/required_energy = abs(T20C - cabin_air.temperature) * heat_capacity
|
|
required_energy = min(required_energy, 1000)
|
|
if(required_energy < 1)
|
|
return
|
|
var/delta_temperature = required_energy / heat_capacity
|
|
if(delta_temperature)
|
|
if(cabin_air.temperature < T20C)
|
|
cabin_air.temperature += delta_temperature
|
|
else
|
|
cabin_air.temperature -= delta_temperature
|
|
|
|
/obj/vehicle/sealed/mecha/proc/process_occupants(seconds_per_tick)
|
|
for(var/mob/living/occupant as anything in occupants)
|
|
if(!(mecha_flags & IS_ENCLOSED) && occupant?.incapacitated) //no sides mean it's easy to just sorta fall out if you're incapacitated.
|
|
mob_exit(occupant, randomstep = TRUE) //bye bye
|
|
continue
|
|
if(cell && cell.maxcharge)
|
|
var/cellcharge = cell.charge/cell.maxcharge
|
|
switch(cellcharge)
|
|
if(0.75 to INFINITY)
|
|
occupant.clear_alert(ALERT_CHARGE)
|
|
if(0.5 to 0.75)
|
|
occupant.throw_alert(ALERT_CHARGE, /atom/movable/screen/alert/lowcell/mech, 1)
|
|
if(0.25 to 0.5)
|
|
occupant.throw_alert(ALERT_CHARGE, /atom/movable/screen/alert/lowcell/mech, 2)
|
|
if(0.01 to 0.25)
|
|
occupant.throw_alert(ALERT_CHARGE, /atom/movable/screen/alert/lowcell/mech, 3)
|
|
else
|
|
occupant.throw_alert(ALERT_CHARGE, /atom/movable/screen/alert/emptycell/mech)
|
|
else
|
|
occupant.throw_alert(ALERT_CHARGE, /atom/movable/screen/alert/nocell)
|
|
var/integrity = atom_integrity/max_integrity*100
|
|
switch(integrity)
|
|
if(30 to 45)
|
|
occupant.throw_alert(ALERT_MECH_DAMAGE, /atom/movable/screen/alert/low_mech_integrity, 1)
|
|
if(15 to 35)
|
|
occupant.throw_alert(ALERT_MECH_DAMAGE, /atom/movable/screen/alert/low_mech_integrity, 2)
|
|
if(-INFINITY to 15)
|
|
occupant.throw_alert(ALERT_MECH_DAMAGE, /atom/movable/screen/alert/low_mech_integrity, 3)
|
|
else
|
|
occupant.clear_alert(ALERT_MECH_DAMAGE)
|
|
var/atom/checking = occupant.loc
|
|
// recursive check to handle all cases regarding very nested occupants,
|
|
// such as brainmob inside brainitem inside MMI inside mecha
|
|
while(!isnull(checking))
|
|
if(isturf(checking))
|
|
// hit a turf before hitting the mecha, seems like they have been moved out
|
|
occupant.clear_alert(ALERT_CHARGE)
|
|
occupant.clear_alert(ALERT_MECH_DAMAGE)
|
|
occupant = null
|
|
break
|
|
else if (checking == src)
|
|
break // all good
|
|
checking = checking.loc
|
|
|
|
/obj/vehicle/sealed/mecha/proc/process_constant_power_usage(seconds_per_tick)
|
|
if(mecha_flags & LIGHTS_ON && !use_energy(light_power_drain * seconds_per_tick))
|
|
mecha_flags &= ~LIGHTS_ON
|
|
set_light_on(mecha_flags & LIGHTS_ON)
|
|
playsound(src,'sound/machines/clockcult/brass_skewer.ogg', 40, TRUE)
|
|
log_message("Toggled lights off due to the lack of power.", LOG_MECHA)
|
|
|
|
///Called when a driver clicks somewhere. Handles everything like equipment, punches, etc.
|
|
/obj/vehicle/sealed/mecha/proc/on_mouseclick(mob/user, atom/target, list/modifiers)
|
|
SIGNAL_HANDLER
|
|
if(LAZYACCESS(modifiers, MIDDLE_CLICK))
|
|
set_safety(user)
|
|
return COMSIG_MOB_CANCEL_CLICKON
|
|
if(weapons_safety)
|
|
return
|
|
if(isAI(user)) //For AIs: If safeties are off, use mech functions. If safeties are on, use AI functions.
|
|
. = COMSIG_MOB_CANCEL_CLICKON
|
|
if(modifiers[SHIFT_CLICK]) //Allows things to be examined.
|
|
return
|
|
if(!isturf(target) && !isturf(target.loc)) // Prevents inventory from being drilled
|
|
return
|
|
if(completely_disabled || is_currently_ejecting || (mecha_flags & CANNOT_INTERACT))
|
|
return
|
|
if(phasing)
|
|
balloon_alert(user, "not while [phasing]!")
|
|
return
|
|
if(user.incapacitated)
|
|
return
|
|
if(!get_charge())
|
|
return
|
|
if(src == target)
|
|
return
|
|
var/dir_to_target = get_dir(src,target)
|
|
if(!(mecha_flags & OMNIDIRECTIONAL_ATTACKS) && dir_to_target && !(dir_to_target & dir))//wrong direction
|
|
return
|
|
if(internal_damage & MECHA_INT_CONTROL_LOST)
|
|
target = pick(view(3,target))
|
|
var/mob/living/livinguser = user
|
|
if(!(livinguser in return_controllers_with_flag(VEHICLE_CONTROL_EQUIPMENT)))
|
|
balloon_alert(user, "wrong seat for equipment!")
|
|
return
|
|
var/obj/item/mecha_parts/mecha_equipment/selected
|
|
if(modifiers[BUTTON] == RIGHT_CLICK)
|
|
selected = equip_by_category[MECHA_R_ARM]
|
|
else
|
|
selected = equip_by_category[MECHA_L_ARM]
|
|
if(selected)
|
|
if(!Adjacent(target) && (selected.range & MECHA_RANGED))
|
|
if(HAS_TRAIT(livinguser, TRAIT_PACIFISM) && selected.harmful)
|
|
to_chat(livinguser, span_warning("You don't want to harm other living beings!"))
|
|
return
|
|
if(SEND_SIGNAL(src, COMSIG_MECHA_EQUIPMENT_CLICK, livinguser, target) & COMPONENT_CANCEL_EQUIPMENT_CLICK)
|
|
return
|
|
INVOKE_ASYNC(selected, TYPE_PROC_REF(/obj/item/mecha_parts/mecha_equipment, action), user, target, modifiers)
|
|
return
|
|
if(Adjacent(target) && (selected.range & MECHA_MELEE))
|
|
if(isliving(target) && selected.harmful && HAS_TRAIT(livinguser, TRAIT_PACIFISM))
|
|
to_chat(livinguser, span_warning("You don't want to harm other living beings!"))
|
|
return
|
|
if(SEND_SIGNAL(src, COMSIG_MECHA_EQUIPMENT_CLICK, livinguser, target) & COMPONENT_CANCEL_EQUIPMENT_CLICK)
|
|
return
|
|
INVOKE_ASYNC(selected, TYPE_PROC_REF(/obj/item/mecha_parts/mecha_equipment, action), user, target, modifiers)
|
|
return
|
|
if(!(livinguser in return_controllers_with_flag(VEHICLE_CONTROL_MELEE)))
|
|
to_chat(livinguser, span_warning("You're in the wrong seat to interact with your hands."))
|
|
return
|
|
var/on_cooldown = TIMER_COOLDOWN_RUNNING(src, COOLDOWN_MECHA_MELEE_ATTACK)
|
|
var/adjacent = Adjacent(target)
|
|
if(SEND_SIGNAL(src, COMSIG_MECHA_MELEE_CLICK, livinguser, target, on_cooldown, adjacent) & COMPONENT_CANCEL_MELEE_CLICK)
|
|
return
|
|
if(on_cooldown || !adjacent)
|
|
return
|
|
if(internal_damage & MECHA_INT_CONTROL_LOST)
|
|
target = pick(oview(1,src))
|
|
|
|
if(!has_charge(melee_energy_drain))
|
|
return
|
|
use_energy(melee_energy_drain)
|
|
|
|
SEND_SIGNAL(user, COMSIG_MOB_USED_CLICK_MECH_MELEE, src)
|
|
if(target.mech_melee_attack(src, user))
|
|
TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_MELEE_ATTACK, melee_cooldown)
|
|
|
|
/// Driver alt clicks anything while in mech
|
|
/obj/vehicle/sealed/mecha/proc/on_click_alt(mob/user, atom/target, params)
|
|
SIGNAL_HANDLER
|
|
|
|
. = COMSIG_MOB_CANCEL_CLICKON // Cancel base_click_alt
|
|
|
|
if(target != src)
|
|
return
|
|
|
|
if(!(user in occupants))
|
|
return
|
|
|
|
if(!(user in return_controllers_with_flag(VEHICLE_CONTROL_DRIVE)))
|
|
to_chat(user, span_warning("You're in the wrong seat to control movement."))
|
|
return
|
|
|
|
toggle_strafe()
|
|
|
|
|
|
/// middle mouse click signal wrapper for AI users
|
|
/obj/vehicle/sealed/mecha/proc/on_middlemouseclick(mob/user, atom/target, params)
|
|
SIGNAL_HANDLER
|
|
if(isAI(user))
|
|
on_mouseclick(user, target, params)
|
|
|
|
///Displays a special speech bubble when someone inside the mecha speaks
|
|
/obj/vehicle/sealed/mecha/proc/display_speech_bubble(datum/source, list/speech_args)
|
|
SIGNAL_HANDLER
|
|
var/list/speech_bubble_recipients = list()
|
|
for(var/mob/listener in get_hearers_in_view(7, src))
|
|
if(listener.client)
|
|
speech_bubble_recipients += listener.client
|
|
|
|
var/image/mech_speech = image('icons/mob/effects/talk.dmi', src, "machine[say_test(speech_args[SPEECH_MESSAGE])]",MOB_LAYER+1)
|
|
INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(flick_overlay_global), mech_speech, speech_bubble_recipients, 3 SECONDS)
|
|
|
|
/////////////////////////////////////
|
|
//////// Atmospheric stuff ////////
|
|
/////////////////////////////////////
|
|
|
|
/obj/vehicle/sealed/mecha/remove_air(amount)
|
|
if((mecha_flags & IS_ENCLOSED) && cabin_sealed)
|
|
return cabin_air.remove(amount)
|
|
return ..()
|
|
|
|
/obj/vehicle/sealed/mecha/return_air()
|
|
if((mecha_flags & IS_ENCLOSED) && cabin_sealed)
|
|
return cabin_air
|
|
return ..()
|
|
|
|
/obj/vehicle/sealed/mecha/return_analyzable_air()
|
|
return cabin_air
|
|
|
|
///fetches pressure of the gas mixture we are using
|
|
/obj/vehicle/sealed/mecha/proc/return_pressure()
|
|
var/datum/gas_mixture/air = return_air()
|
|
return air?.return_pressure()
|
|
|
|
///fetches temp of the gas mixture we are using
|
|
/obj/vehicle/sealed/mecha/return_temperature()
|
|
var/datum/gas_mixture/air = return_air()
|
|
return air?.return_temperature()
|
|
|
|
///makes cabin unsealed, dumping cabin air outside or airtight filling the cabin with external air mix
|
|
/obj/vehicle/sealed/mecha/proc/set_cabin_seal(mob/user, cabin_sealed)
|
|
if(!(mecha_flags & IS_ENCLOSED))
|
|
balloon_alert(user, "cabin can't be sealed!")
|
|
log_message("Tried to seal cabin. This mech can't be airtight.", LOG_MECHA)
|
|
return
|
|
if(TIMER_COOLDOWN_RUNNING(src, COOLDOWN_MECHA_CABIN_SEAL))
|
|
balloon_alert(user, "on cooldown!")
|
|
return
|
|
TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_CABIN_SEAL, 1 SECONDS)
|
|
|
|
src.cabin_sealed = cabin_sealed
|
|
|
|
var/datum/gas_mixture/environment_air = loc.return_air()
|
|
if(!isnull(environment_air))
|
|
if(cabin_sealed)
|
|
// Fill cabin with air
|
|
environment_air.pump_gas_to(cabin_air, environment_air.return_pressure())
|
|
else
|
|
// Dump cabin air
|
|
var/datum/gas_mixture/removed_gases = cabin_air.remove_ratio(1)
|
|
if(loc)
|
|
loc.assume_air(removed_gases)
|
|
else
|
|
qdel(removed_gases)
|
|
|
|
var/obj/item/mecha_parts/mecha_equipment/air_tank/tank = locate(/obj/item/mecha_parts/mecha_equipment/air_tank) in equip_by_category[MECHA_UTILITY]
|
|
for(var/mob/occupant as anything in occupants)
|
|
var/datum/action/action = locate(/datum/action/vehicle/sealed/mecha/mech_toggle_cabin_seal) in occupant.actions
|
|
if(!isnull(tank) && cabin_sealed && tank.auto_pressurize_on_seal)
|
|
if(!tank.active)
|
|
tank.set_active(TRUE)
|
|
else
|
|
action.button_icon_state = "mech_cabin_pressurized"
|
|
action.build_all_button_icons()
|
|
else
|
|
action.button_icon_state = "mech_cabin_[cabin_sealed ? "closed" : "open"]"
|
|
action.build_all_button_icons()
|
|
|
|
balloon_alert(occupant, "cabin [cabin_sealed ? "sealed" : "unsealed"]")
|
|
log_message("Cabin [cabin_sealed ? "sealed" : "unsealed"].", LOG_MECHA)
|
|
playsound(src, 'sound/machines/airlock/airlock.ogg', 50, TRUE)
|
|
|
|
/// Special light eater handling
|
|
/obj/vehicle/sealed/mecha/proc/on_light_eater(obj/vehicle/sealed/source, datum/light_eater)
|
|
SIGNAL_HANDLER
|
|
if(mecha_flags & HAS_LIGHTS)
|
|
visible_message(span_danger("[src]'s lights burn out!"))
|
|
mecha_flags &= ~HAS_LIGHTS
|
|
set_light_on(FALSE)
|
|
for(var/occupant in occupants)
|
|
remove_action_type_from_mob(/datum/action/vehicle/sealed/mecha/mech_toggle_lights, occupant)
|
|
return COMPONENT_BLOCK_LIGHT_EATER
|
|
|
|
/obj/vehicle/sealed/mecha/on_saboteur(datum/source, disrupt_duration)
|
|
. = ..()
|
|
if((mecha_flags & HAS_LIGHTS) && light_on)
|
|
set_light_on(FALSE)
|
|
return TRUE
|
|
|
|
/// Apply corresponding accesses
|
|
/obj/vehicle/sealed/mecha/proc/update_access()
|
|
req_access = one_access ? list() : accesses
|
|
req_one_access = one_access ? accesses : list()
|
|
|
|
/// Electrocute user from power celll
|
|
/obj/vehicle/sealed/mecha/shock(mob/living/shocking, chance = 100, shock_source, siemens_coeff)
|
|
if(get_charge() < 1)
|
|
return FALSE
|
|
if(isnull(siemens_coeff))
|
|
siemens_coeff = 0.7
|
|
return ..()
|
|
|
|
/// Toggle mech overclock with a button or by hacking
|
|
/obj/vehicle/sealed/mecha/proc/toggle_overclock(forced_state = null)
|
|
if(!isnull(forced_state))
|
|
if(overclock_mode == forced_state)
|
|
return FALSE
|
|
overclock_mode = forced_state
|
|
else
|
|
overclock_mode = !overclock_mode
|
|
log_message("Toggled [overclock_name].", LOG_MECHA)
|
|
for(var/mob/occupant as anything in occupants)
|
|
balloon_alert(occupant, "[overclock_name] [overclock_mode ? "on":"off"]")
|
|
var/datum/action/act = locate(/datum/action/vehicle/sealed/mecha/mech_overclock) in occupant.actions
|
|
act?.build_all_button_icons(UPDATE_BUTTON_ICON)
|
|
if(overclock_mode)
|
|
movedelay = movedelay / overclock_coeff
|
|
visible_message(span_notice("[src] starts heating up, making humming sounds."))
|
|
else
|
|
movedelay = initial(movedelay)
|
|
visible_message(span_notice("[src] cools down and the humming stops."))
|
|
update_energy_drain()
|
|
return TRUE
|
|
|
|
/// Update the energy drain according to parts and status
|
|
/obj/vehicle/sealed/mecha/proc/update_energy_drain()
|
|
if(servo)
|
|
step_energy_drain = initial(step_energy_drain) / servo.rating
|
|
else
|
|
step_energy_drain = 2 * initial(step_energy_drain)
|
|
if(overclock_mode)
|
|
step_energy_drain *= overclock_coeff
|
|
|
|
if(capacitor)
|
|
phasing_energy_drain = initial(phasing_energy_drain) / capacitor.rating
|
|
melee_energy_drain = initial(melee_energy_drain) / capacitor.rating
|
|
light_power_drain = initial(light_power_drain) / capacitor.rating
|
|
else
|
|
phasing_energy_drain = initial(phasing_energy_drain)
|
|
melee_energy_drain = initial(melee_energy_drain)
|
|
light_power_drain = initial(light_power_drain)
|
|
|
|
/// Toggle lights on/off
|
|
/obj/vehicle/sealed/mecha/proc/toggle_lights(forced_state = null, mob/user)
|
|
if(!(mecha_flags & HAS_LIGHTS))
|
|
if(user)
|
|
balloon_alert(user, "mech has no lights!")
|
|
return
|
|
if((!(mecha_flags & LIGHTS_ON) && forced_state != FALSE) && get_charge() < power_to_energy(light_power_drain, scheduler = SSobj))
|
|
if(user)
|
|
balloon_alert(user, "no power for lights!")
|
|
return
|
|
mecha_flags ^= LIGHTS_ON
|
|
set_light_on(mecha_flags & LIGHTS_ON)
|
|
playsound(src,'sound/machines/clockcult/brass_skewer.ogg', 40, TRUE)
|
|
log_message("Toggled lights [(mecha_flags & LIGHTS_ON)?"on":"off"].", LOG_MECHA)
|
|
for(var/mob/occupant as anything in occupants)
|
|
var/datum/action/act = locate(/datum/action/vehicle/sealed/mecha/mech_toggle_lights) in occupant.actions
|
|
if(mecha_flags & LIGHTS_ON)
|
|
act.button_icon_state = "mech_lights_on"
|
|
else
|
|
act.button_icon_state = "mech_lights_off"
|
|
balloon_alert(occupant, "lights [mecha_flags & LIGHTS_ON ? "on":"off"]")
|
|
act.build_all_button_icons()
|
|
|
|
/obj/vehicle/sealed/mecha/proc/melee_attack_effect(mob/living/victim, heavy)
|
|
if(heavy)
|
|
victim.Unconscious(2 SECONDS)
|
|
else
|
|
victim.Knockdown(4 SECONDS)
|