mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-24 13:38:41 +01:00
+37






![tgstation-ci[bot] <179393467+tgstation-ci[bot]@users.noreply.github.com>](/assets/img/avatar_default.png)


Alexis
GitHub
tgstation-ci[bot] <179393467+tgstation-ci[bot]@users.noreply.github.com>
ArcaneMusic
MrMelbert
Rhials
rageguy505
LemonInTheDark
John Willard
Aliceee2ch
Time-Green
Tsar-Salat
SmArtKar
Maxipat
SyncIt21
deltanedas
SimplyLogan
loganuk
Leland Kemble
FalloutFalcon
Roxy
Lucy
siliconOpossum
Isratosh
TheRyeGuyWhoWillNowDie
Neocloudy
Alexander V.
ElGitificador
Twaticus
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bloop
Cameron Lennox
Tim
Iamgoofball
Layzu666
Arturlang
_0Steven
mrmanlikesbt
Ben10Omintrix
John F. Kennedy
Cursor
Josh
Josh Powell
Yobrocharlie
Hardly3D
shayoki
LT3
21b4095dfd
Upstream 04/17/2026 fixes https://github.com/Bubberstation/Bubberstation/issues/5549 --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: tgstation-ci[bot] <179393467+tgstation-ci[bot]@users.noreply.github.com> Co-authored-by: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com> Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Co-authored-by: Rhials <28870487+Rhials@users.noreply.github.com> Co-authored-by: rageguy505 <54517726+rageguy505@users.noreply.github.com> Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> Co-authored-by: Aliceee2ch <160794176+Aliceee2ch@users.noreply.github.com> Co-authored-by: Time-Green <7501474+Time-Green@users.noreply.github.com> Co-authored-by: Tsar-Salat <62388554+Tsar-Salat@users.noreply.github.com> Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com> Co-authored-by: Maxipat <108554989+Maxipat112@users.noreply.github.com> Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com> Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com> Co-authored-by: SimplyLogan <47579821+loganuk@users.noreply.github.com> Co-authored-by: loganuk <fakeemail123@aol.com> Co-authored-by: Leland Kemble <70413276+lelandkemble@users.noreply.github.com> Co-authored-by: FalloutFalcon <86381784+FalloutFalcon@users.noreply.github.com> Co-authored-by: Roxy <75404941+TealSeer@users.noreply.github.com> Co-authored-by: Lucy <lucy@absolucy.moe> Co-authored-by: siliconOpossum <138069572+siliconOpossum@users.noreply.github.com> Co-authored-by: Isratosh <Isratosh@hotmail.com> Co-authored-by: TheRyeGuyWhoWillNowDie <70169560+TheRyeGuyWhoWillNowDie@users.noreply.github.com> Co-authored-by: Neocloudy <88008002+Neocloudy@users.noreply.github.com> Co-authored-by: Alexander V. <volas@ya.ru> Co-authored-by: ElGitificador <168473461+ElGitificador@users.noreply.github.com> Co-authored-by: Twaticus <46540570+Twaticus@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bloop <13398309+vinylspiders@users.noreply.github.com> Co-authored-by: Cameron Lennox <killer65311@gmail.com> Co-authored-by: Tim <timothymtorres@gmail.com> Co-authored-by: Iamgoofball <iamgoofball@gmail.com> Co-authored-by: Layzu666 <121319428+Layzu666@users.noreply.github.com> Co-authored-by: Arturlang <24881678+Arturlang@users.noreply.github.com> Co-authored-by: _0Steven <42909981+00-Steven@users.noreply.github.com> Co-authored-by: mrmanlikesbt <99309552+mrmanlikesbt@users.noreply.github.com> Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com> Co-authored-by: John F. Kennedy <54908920+MacaroniCritter@users.noreply.github.com> Co-authored-by: Cursor <102828457+theselfish@users.noreply.github.com> Co-authored-by: Josh <josh.adam.powell@gmail.com> Co-authored-by: Josh Powell <josh.powell@softwire.com> Co-authored-by: Yobrocharlie <Charliemiller5617@gmail.com> Co-authored-by: Hardly3D <66234359+Hardly3D@users.noreply.github.com> Co-authored-by: shayoki <96078776+shayoki@users.noreply.github.com> Co-authored-by: LT3 <83487515+lessthnthree@users.noreply.github.com>
292 lines
11 KiB
Plaintext
292 lines
11 KiB
Plaintext
/obj/vehicle/sealed/mecha/durand
|
|
desc = "An aging combat exosuit utilized by the Nanotrasen corporation. Originally developed to combat hostile alien lifeforms."
|
|
name = "\improper Durand"
|
|
icon_state = "durand"
|
|
base_icon_state = "durand"
|
|
movedelay = 4
|
|
max_integrity = 400
|
|
accesses = list(ACCESS_MECH_SCIENCE, ACCESS_MECH_SECURITY)
|
|
armor_type = /datum/armor/mecha_durand
|
|
max_temperature = 30000
|
|
force = 40
|
|
destruction_sleep_duration = 40
|
|
exit_delay = 40
|
|
wreckage = /obj/structure/mecha_wreckage/durand
|
|
mech_type = EXOSUIT_MODULE_DURAND
|
|
max_equip_by_category = list(
|
|
MECHA_L_ARM = 1,
|
|
MECHA_R_ARM = 1,
|
|
MECHA_UTILITY = 3,
|
|
MECHA_POWER = 1,
|
|
MECHA_ARMOR = 1,
|
|
)
|
|
var/obj/durand_shield/shield
|
|
|
|
/datum/armor/mecha_durand
|
|
melee = 40
|
|
bullet = 35
|
|
laser = 15
|
|
energy = 10
|
|
bomb = 20
|
|
fire = 100
|
|
acid = 100
|
|
|
|
/obj/vehicle/sealed/mecha/durand/Initialize(mapload)
|
|
. = ..()
|
|
shield = new /obj/durand_shield(loc, src, plane, layer, dir)
|
|
RegisterSignal(src, COMSIG_MECHA_ACTION_TRIGGER, PROC_REF(relay))
|
|
|
|
/obj/vehicle/sealed/mecha/durand/Destroy()
|
|
if(shield)
|
|
QDEL_NULL(shield)
|
|
return ..()
|
|
|
|
|
|
/obj/vehicle/sealed/mecha/durand/generate_actions()
|
|
. = ..()
|
|
initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_defense_mode)
|
|
|
|
/obj/vehicle/sealed/mecha/durand/process()
|
|
. = ..()
|
|
if(defense_mode && !use_energy(0.01 * STANDARD_CELL_CHARGE)) //Defence mode can only be on with a occupant so we check if one of them can toggle it and toggle
|
|
for(var/O in occupants)
|
|
var/mob/living/occupant = O
|
|
var/datum/action/action = LAZYACCESSASSOC(occupant_actions, occupant, /datum/action/vehicle/sealed/mecha/mech_defense_mode)
|
|
if(action)
|
|
action.Trigger()
|
|
break
|
|
|
|
/obj/vehicle/sealed/mecha/durand/Move(direction)
|
|
. = ..()
|
|
if(shield)
|
|
shield.forceMove(loc)
|
|
shield.setDir(dir)
|
|
|
|
/obj/vehicle/sealed/mecha/durand/forceMove(turf/T)
|
|
. = ..()
|
|
shield.forceMove(T)
|
|
|
|
/obj/vehicle/sealed/mecha/durand/mob_exit(mob/M, silent = FALSE, randomstep = FALSE, forced = FALSE)
|
|
if(defense_mode)
|
|
var/datum/action/action = LAZYACCESSASSOC(occupant_actions, M, /datum/action/vehicle/sealed/mecha/mech_defense_mode)
|
|
if(action)
|
|
INVOKE_ASYNC(action, TYPE_PROC_REF(/datum/action, Trigger), null, NONE, FALSE)
|
|
return ..()
|
|
|
|
///Relays the signal from the action button to the shield, and creates a new shield if the old one is MIA.
|
|
/obj/vehicle/sealed/mecha/durand/proc/relay(datum/source, mob/owner, list/signal_args)
|
|
SIGNAL_HANDLER
|
|
if(!shield) //if the shield somehow got deleted
|
|
stack_trace("Durand triggered relay without a shield")
|
|
shield = new /obj/durand_shield(loc, src, layer)
|
|
shield.setDir(dir)
|
|
|
|
//Redirects projectiles to the shield if defense_check decides they should be blocked and returns true.
|
|
/obj/vehicle/sealed/mecha/durand/projectile_hit(obj/projectile/hitting_projectile, def_zone, piercing_hit, blocked)
|
|
if(defense_check(hitting_projectile.loc) && shield)
|
|
return shield.projectile_hit(hitting_projectile, def_zone, piercing_hit, blocked)
|
|
return ..()
|
|
|
|
/**Checks if defense mode is enabled, and if the attacker is standing in an area covered by the shield.
|
|
Expects a turf. Returns true if the attack should be blocked, false if not.*/
|
|
/obj/vehicle/sealed/mecha/durand/proc/defense_check(turf/aloc)
|
|
if (!defense_mode || !shield || shield.switching)
|
|
return FALSE
|
|
. = FALSE
|
|
switch(dir)
|
|
if (1)
|
|
if(abs(x - aloc.x) <= (y - aloc.y) * -2)
|
|
. = TRUE
|
|
if (2)
|
|
if(abs(x - aloc.x) <= (y - aloc.y) * 2)
|
|
. = TRUE
|
|
if (4)
|
|
if(abs(y - aloc.y) <= (x - aloc.x) * -2)
|
|
. = TRUE
|
|
if (8)
|
|
if(abs(y - aloc.y) <= (x - aloc.x) * 2)
|
|
. = TRUE
|
|
return
|
|
|
|
/obj/vehicle/sealed/mecha/durand/attack_generic(mob/user, damage_amount = 0, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, armor_penetration = 0)
|
|
if(defense_check(user.loc))
|
|
log_message("Attack absorbed by defense field. Attacker - [user].", LOG_MECHA, color="orange")
|
|
return shield.attack_generic(user, damage_amount, damage_type, damage_flag, sound_effect, armor_penetration)
|
|
return ..()
|
|
|
|
/obj/vehicle/sealed/mecha/durand/blob_act(obj/structure/blob/B)
|
|
if(defense_check(B.loc))
|
|
log_message("Attack by blob. Attacker - [B].", LOG_MECHA, color="red")
|
|
log_message("Attack absorbed by defense field.", LOG_MECHA, color="orange")
|
|
shield.blob_act(B)
|
|
else
|
|
. = ..()
|
|
|
|
/obj/vehicle/sealed/mecha/durand/attackby(obj/item/W as obj, mob/user as mob, list/modifiers, list/attack_modifiers)
|
|
if(defense_check(user.loc))
|
|
log_message("Attack absorbed by defense field. Attacker - [user], with [W]", LOG_MECHA, color="orange")
|
|
shield.attackby(W, user, modifiers)
|
|
else
|
|
. = ..()
|
|
|
|
/obj/vehicle/sealed/mecha/durand/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
|
|
if(defense_check(AM.loc))
|
|
log_message("Impact with [AM] absorbed by defense field.", LOG_MECHA, color="orange")
|
|
shield.hitby(AM, skipcatch, hitpush, blocked, throwingdatum)
|
|
else
|
|
. = ..()
|
|
|
|
/datum/action/vehicle/sealed/mecha/mech_defense_mode
|
|
name = "Toggle an energy shield that blocks all attacks from the faced direction at a heavy power cost."
|
|
button_icon_state = "mech_defense_mode_off"
|
|
|
|
/datum/action/vehicle/sealed/mecha/mech_defense_mode/Trigger(mob/clicker, trigger_flags, forced_state = FALSE)
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
if(!chassis || !(owner in chassis.occupants))
|
|
return
|
|
SEND_SIGNAL(chassis, COMSIG_MECHA_ACTION_TRIGGER, owner, args) //Signal sent to the mech, to be handed to the shield. See durand.dm for more details
|
|
|
|
////////////////////////////
|
|
///// Shield processing ////
|
|
////////////////////////////
|
|
|
|
/**An object to take the hit for us when using the Durand's defense mode.
|
|
It is spawned in during the durand's initilization, and always stays on the same tile.
|
|
Normally invisible, until defense mode is actvated. When the durand detects an attack that should be blocked, the
|
|
attack is passed to the shield. The shield takes the damage, uses it to calculate charge cost, and then sets its
|
|
own integrity back to max. Shield is automatically dropped if we run out of power or the user gets out.*/
|
|
|
|
/obj/durand_shield //projectiles get passed to this when defense mode is enabled
|
|
name = "defense grid"
|
|
icon = 'icons/mob/effects/durand_shield.dmi'
|
|
icon_state = "shield_null"
|
|
invisibility = INVISIBILITY_MAXIMUM //no showing on right-click
|
|
pixel_y = 4
|
|
max_integrity = 10000
|
|
anchored = TRUE
|
|
light_system = OVERLAY_LIGHT
|
|
light_range = 2.8
|
|
light_power = 1
|
|
light_color = LIGHT_COLOR_FAINT_CYAN
|
|
light_on = FALSE
|
|
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF //The shield should not take damage from fire, lava, or acid; that's the mech's job.
|
|
///Our link back to the durand
|
|
var/obj/vehicle/sealed/mecha/durand/chassis
|
|
///To keep track of things during the animation
|
|
var/switching = FALSE
|
|
|
|
/obj/durand_shield/Initialize(mapload, chassis, plane, layer, dir)
|
|
. = ..()
|
|
src.chassis = chassis
|
|
src.layer = ABOVE_MOB_LAYER
|
|
SET_PLANE_IMPLICIT(src, plane)
|
|
setDir(dir)
|
|
RegisterSignal(chassis, COMSIG_MECHA_ACTION_TRIGGER, PROC_REF(activate))
|
|
RegisterSignal(chassis, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, PROC_REF(shield_glide_size_update))
|
|
|
|
/obj/durand_shield/Destroy()
|
|
UnregisterSignal(src, COMSIG_MECHA_ACTION_TRIGGER)
|
|
if(chassis)
|
|
UnregisterSignal(chassis, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE)
|
|
chassis.shield = null
|
|
chassis = null
|
|
return ..()
|
|
|
|
/obj/durand_shield/proc/shield_glide_size_update(datum/source, target)
|
|
SIGNAL_HANDLER
|
|
glide_size = target
|
|
|
|
/**
|
|
* Handles activating and deactivating the shield.
|
|
*
|
|
* This proc is called by a signal sent from the mech's action button and
|
|
* relayed by the mech itself. The "forced" variable, `signal_args[1]`, will
|
|
* skip the to-pilot text and is meant for when the shield is disabled by
|
|
* means other than the action button (like running out of power).
|
|
*
|
|
* Arguments:
|
|
* * source: the shield
|
|
* * owner: mob that activated the shield
|
|
* * signal_args: whether it's forced
|
|
*/
|
|
/obj/durand_shield/proc/activate(datum/source, mob/owner, list/signal_args)
|
|
SIGNAL_HANDLER
|
|
if(!LAZYLEN(chassis?.occupants))
|
|
return
|
|
if(switching && !signal_args[1])
|
|
return
|
|
if(!chassis.defense_mode && (!chassis.cell || chassis.cell.charge < 100)) //If it's off, and we have less than 100 units of power
|
|
chassis.balloon_alert(owner, "insufficient power")
|
|
return
|
|
switching = TRUE
|
|
chassis.defense_mode = !chassis.defense_mode
|
|
if(!signal_args[1])
|
|
chassis.balloon_alert(owner, "shield [chassis.defense_mode?"enabled":"disabled"]")
|
|
chassis.log_message("User has toggled defense mode -- now [chassis.defense_mode?"enabled":"disabled"].", LOG_MECHA)
|
|
else
|
|
chassis.log_message("defense mode state changed -- now [chassis.defense_mode?"enabled":"disabled"].", LOG_MECHA)
|
|
for(var/occupant in chassis.occupants)
|
|
var/datum/action/button = chassis.occupant_actions[occupant][/datum/action/vehicle/sealed/mecha/mech_defense_mode]
|
|
button.button_icon_state = "mech_defense_mode_[chassis.defense_mode ? "on" : "off"]"
|
|
button.build_all_button_icons()
|
|
|
|
set_light_on(chassis.defense_mode)
|
|
|
|
if(chassis.defense_mode)
|
|
SetInvisibility(INVISIBILITY_NONE, id=type)
|
|
flick("shield_raise", src)
|
|
playsound(src, 'sound/vehicles/mecha/mech_shield_raise.ogg', 50, FALSE)
|
|
icon_state = "shield"
|
|
resetdir(chassis, dir, dir) // to set the plane for the shield properly when it's turned on
|
|
RegisterSignal(chassis, COMSIG_ATOM_DIR_CHANGE, PROC_REF(resetdir))
|
|
else
|
|
flick("shield_drop", src)
|
|
playsound(src, 'sound/vehicles/mecha/mech_shield_drop.ogg', 50, FALSE)
|
|
icon_state = "shield_null"
|
|
addtimer(CALLBACK(src, PROC_REF(make_invisible)), 1 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE)
|
|
UnregisterSignal(chassis, COMSIG_ATOM_DIR_CHANGE)
|
|
switching = FALSE
|
|
|
|
/**
|
|
* Sets invisibility to INVISIBILITY_MAXIMUM if defense mode is disabled
|
|
*
|
|
* We need invisibility set to higher than 25 for the shield to not appear
|
|
* in the right-click context menu, but if we do it too early, we miss the
|
|
* deactivate animation. Hense, timer and this proc.
|
|
*/
|
|
/obj/durand_shield/proc/make_invisible()
|
|
if(!chassis.defense_mode)
|
|
RemoveInvisibility(type)
|
|
|
|
/obj/durand_shield/proc/resetdir(datum/source, olddir, newdir)
|
|
SIGNAL_HANDLER
|
|
|
|
setDir(newdir)
|
|
|
|
/obj/durand_shield/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", sound_effect = TRUE, attack_dir, armour_penetration = 0)
|
|
if(!chassis)
|
|
qdel(src)
|
|
return
|
|
if(!chassis.defense_mode) //if defense mode is disabled, we're taking damage that we shouldn't be taking
|
|
return
|
|
. = ..()
|
|
flick("shield_impact", src)
|
|
if(!.)
|
|
return
|
|
if(!chassis.use_energy(. * (STANDARD_CELL_CHARGE / 150)))
|
|
chassis.cell?.charge = 0
|
|
for(var/O in chassis.occupants)
|
|
var/mob/living/occupant = O
|
|
var/datum/action/action = LAZYACCESSASSOC(chassis.occupant_actions, occupant, /datum/action/vehicle/sealed/mecha/mech_defense_mode)
|
|
action.Trigger()
|
|
atom_integrity = 10000
|
|
|
|
/obj/durand_shield/play_attack_sound()
|
|
playsound(src, 'sound/vehicles/mecha/mech_shield_deflect.ogg', 100, TRUE)
|
|
|
|
/obj/durand_shield/bullet_act()
|
|
play_attack_sound()
|
|
. = ..()
|