Skip to content

Commit

Permalink
Inteq Gygax and Durand Paladin shield backlash. Fixes Durand shield b…
Browse files Browse the repository at this point in the history
…locking bullets from any direction. (shiptest-ss13#3382)

<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may
not be viewable. -->
<!-- You can view Contributing.MD for a detailed description of the pull
request process. -->

## About The Pull Request

<!-- Describe The Pull Request. Please be sure every change is
documented or this can delay review and even discourage maintainers from
merging your PR! -->

Adds a new mech variant, the IRMG Basenji Gygax. Featuring a much more
powerful overclock, capable of charging through walls and obstacles in
short bursts of intense speed.


![image](https://github.com/user-attachments/assets/5daae4b1-2d9f-4f6f-8237-e861f0e44254)


Implements the backlash function for the Paladin Durand's shield. Coming
into contact with it will apply a nasty shock to any attackers. The
modification however prevents it from blocking bullets or projectiles.

Fixes the Durand shield being able to block bullets from any direction.
The proc didnt have the right arguments, and was using the Durand itself
as the projectile it was supposed to defend itself against.

Inteq Gygax sprite done by Rye-Rice, modified from sprites by
INFRARED_BARON

## Why It's Good For The Game

<!-- Please add a short description of why you think these changes would
benefit the game. If you can't justify it in words, it might not be
worth adding. -->

New mechs are neat, and implements an intended feature that was wanted
for the Paladin. Bug fixes are good too I think

## Changelog

:cl: Gristlebee, Rye-Rice, INFRARED_BARON
add: Inteq Gygax and mech charges
add: Paladin shield backlash
fix: Durand shield blocking all projectiles
imageadd: Inteq Gygax sprites
/:cl:

<!-- Both :cl:'s are required for the changelog to work! You can put
your name to the right of the first :cl: if you want to overwrite your
GitHub username as author ingame. -->
<!-- You can use multiple of the same prefix (they're only used for the
icon ingame) and delete the unneeded ones. Despite some of the tags,
changelogs should generally represent how a player might be affected by
the changes rather than a summary of the PR's contents. -->

---------

Signed-off-by: Gristlebee <[email protected]>
Co-authored-by: Theos <[email protected]>
  • Loading branch information
2 people authored and MysticalFaceLesS committed Oct 6, 2024
1 parent e5c9788 commit d4c2964
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 19 deletions.
87 changes: 69 additions & 18 deletions code/game/mecha/combat/durand.dm
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,24 @@
force = 40
wreckage = /obj/structure/mecha_wreckage/durand
var/obj/durand_shield/shield
var/shield_type = /obj/durand_shield
var/shield_passive_drain = 300



/obj/mecha/combat/durand/clip
desc = "An aging combat exosuit appropriated from abandoned Nanotrasen facilities, now supplied to the CMM-BARD anti-xenofauna division."
desc = "An aging combat exosuit appropriated from abandoned Nanotrasen facilities, now supplied to the CMM-BARD anti-xenofauna division. The defence grid has been modified to disperse controlled electric shocks on contact, at the cost of its ability to block ranged projectiles."
name = "\improper Paladin"
icon_state = "clipdurand"
wreckage = /obj/structure/mecha_wreckage/durand/clip
armor = list("melee" = 40, "bullet" = 35, "laser" = 15, "energy" = 10, "bomb" = 20, "bio" = 0, "rad" = 50, "fire" = 100, "acid" = 100)

//TODO: Custom melee backlash shield with no projectile protection
shield_passive_drain = 0
shield_type = /obj/durand_shield/clip

/obj/mecha/combat/durand/Initialize()
. = ..()
shield = new /obj/durand_shield(loc, src, layer, dir)
shield = new shield_type(loc, src, layer, dir)
RegisterSignal(src, COMSIG_MECHA_ACTION_ACTIVATE, PROC_REF(relay))
RegisterSignal(src, COMSIG_PROJECTILE_PREHIT, PROC_REF(prehit))


/obj/mecha/combat/durand/Destroy()
Expand Down Expand Up @@ -71,22 +72,21 @@

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 = new shield_type(loc, src, layer)
shield.setDir(dir)
SEND_SIGNAL(shield, COMSIG_MECHA_ACTION_ACTIVATE, source, signal_args)

//Redirects projectiles to the shield if defense_check decides they should be blocked and returns true.
/obj/mecha/combat/durand/proc/prehit(obj/projectile/source, list/signal_args)
SIGNAL_HANDLER

if(defense_check(source.loc) && shield)
signal_args[2] = shield

/obj/mecha/combat/durand/bullet_act(obj/projectile/source)
if(defense_check(source.loc, shield.ranged_pass))
shield.bullet_act(source)
else
. = ..()

/**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/mecha/combat/durand/proc/defense_check(turf/aloc)
if (!defense_mode || !shield || shield.switching)
Expects a turf. Returns true if the attack should be blocked, false if not. Skip defence will make the proc return false and the attack will go through*/
/obj/mecha/combat/durand/proc/defense_check(turf/aloc, skip_defence = FALSE)
if (!defense_mode || !shield || shield.switching || skip_defence)
return FALSE
. = FALSE
switch(dir)
Expand All @@ -105,26 +105,38 @@ Expects a turf. Returns true if the attack should be blocked, false if not.*/
return

/obj/mecha/combat/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))
if(defense_check(user.loc, shield.melee_pass))
log_message("Attack absorbed by defense field. Attacker - [user].", LOG_MECHA, color="orange")
shield.attack_generic(user, damage_amount, damage_type, damage_flag, sound_effect, armor_penetration)
else
. = ..()

/obj/mecha/combat/durand/attackby(obj/item/W as obj, mob/user as mob, params)
if(defense_check(user.loc))
if(defense_check(user.loc, shield.melee_pass))
log_message("Attack absorbed by defense field. Attacker - [user], with [W]", LOG_MECHA, color="orange")
shield.attackby(W, user, params)
else
. = ..()

/obj/mecha/combat/durand/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
if(defense_check(AM.loc))
if(defense_check(AM.loc, shield.ranged_pass))
log_message("Impact with [AM] absorbed by defense field.", LOG_MECHA, color="orange")
shield.hitby(AM, skipcatch, hitpush, blocked, throwingdatum)
else
. = ..()

// Walking into the Paladin's shield shocks you.

/obj/mecha/combat/durand/clip/Bump(atom/obstacle)
. = ..()
if(defense_check(obstacle.loc) && isliving(obstacle))
shield.contact(obstacle)

/obj/mecha/combat/durand/clip/Bumped(atom/movable/AM)
. = ..()
if(defense_check(AM.loc) && isliving(AM))
shield.contact(AM)

////////////////////////////
///// Shield processing ////
////////////////////////////
Expand All @@ -151,7 +163,14 @@ own integrity back to max. Shield is automatically dropped if we run out of powe
light_on = FALSE
var/obj/mecha/combat/durand/chassis ///Our link back to the durand
var/switching = FALSE ///To keep track of things during the animation
/// if this shield lets melee attacks pass and hit the mech directly
var/melee_pass = FALSE
/// if this shield lets projectiles pass and hit the mech directly
var/ranged_pass = FALSE

/obj/durand_shield/clip
name = "electric repulsion grid"
ranged_pass = TRUE

/obj/durand_shield/Initialize(mapload, _chassis, _layer, _dir)
. = ..()
Expand Down Expand Up @@ -230,3 +249,35 @@ the shield is disabled by means other than the action button (like running out o
/obj/durand_shield/bullet_act()
play_attack_sound()
. = ..()

/// a mob has bumped into the shield
/obj/durand_shield/proc/contact(mob/living/contactor)
return

/// Clippy shield
/obj/durand_shield/clip/attack_generic(mob/user, damage_amount, damage_type, damage_flag, sound_effect, armor_penetration)
. = ..()
apply_shock(user)

/obj/durand_shield/clip/attackby(obj/item/I, mob/living/user, params)
. = ..()
apply_shock(user)

/obj/durand_shield/clip/contact(mob/living/contactor)
. = ..()
apply_shock(contactor)

/obj/durand_shield/clip/proc/apply_shock(mob/attacker)
var/did_shock = FALSE
if(iscarbon(attacker))
var/mob/living/carbon/victim = attacker
if(electrocute_mob(victim, chassis.cell, src, 1, FALSE, FALSE))
did_shock = TRUE
else if(isliving(attacker))
var/mob/living/victim = attacker
if(victim.apply_damage_type(20,BURN))
to_chat(victim,span_userdanger("You're shocked by \the [src]!"))
did_shock = TRUE
if(did_shock)
visible_message(span_bolddanger("\The [src] repels \the [attacker] on contact, shocking [attacker.p_them()]."))
do_sparks(5,TRUE,src)
16 changes: 16 additions & 0 deletions code/game/mecha/combat/gygax.dm
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@
return
cell = new /obj/item/stock_parts/cell/bluespace(src)

/obj/mecha/combat/gygax/inteq
name = "\improper Basenji"
desc = "A lightweight security exosuit, modified to IRMG standards. The leg actuators have been maxed out, allowing for powerful short ranged charges."
icon_state = "inteqgygax"
charge_break_walls = TRUE
charge_toss_structures = TRUE
charge_toss_mobs = TRUE

/obj/mecha/combat/gygax/GrantActions(mob/living/user, human_occupant = 0)
..()
Expand All @@ -67,3 +74,12 @@
/obj/mecha/combat/gygax/RemoveActions(mob/living/user, human_occupant = 0)
..()
overload_action.Remove(user)

/obj/mecha/combat/gygax/inteq/GrantActions(mob/living/user, human_occupant = 0)
..()
overload_action.Remove(user)
charge_action.Grant(user,src)

/obj/mecha/combat/gygax/inteq/RemoveActions(mob/living/user, human_occupant)
. = ..()
charge_action.Remove(user)
7 changes: 7 additions & 0 deletions code/game/mecha/equipment/tools/work_tools.dm
Original file line number Diff line number Diff line change
Expand Up @@ -575,3 +575,10 @@
icon_state = "clipupgrade"
source_mech = list(/obj/mecha/combat/durand)
result_mech = /obj/mecha/combat/durand/clip

/obj/item/mecha_parts/mecha_equipment/conversion_kit/inteq_gygax
name = "IRMG Basenji Conversion Kit"
desc = "An IRMG-custom conversion kit for a Gygax combat exosuit, to convert it to the specialized Pyrnese breaching exosuit."
source_mech = list(/obj/mecha/combat/gygax,/obj/mecha/combat/gygax/dark)
result_mech = /obj/mecha/combat/gygax/inteq

60 changes: 60 additions & 0 deletions code/game/mecha/mecha.dm
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@
var/last_user_hud = 1 // used to show/hide the mecha hud while preserving previous preference
var/completely_disabled = FALSE //stops the mech from doing anything

///Vars for mech charges
var/charging = FALSE
var/charge_ready = TRUE
var/charge_cooldown = 50
var/charge_power_consume = 200
var/charge_distance = 5
var/charge_break_walls = FALSE
var/charge_toss_structures = FALSE
var/charge_toss_mobs = FALSE

var/bumpsmash = 0 //Whether or not the mech destroys walls by running into it.
//inner atmos
var/use_internal_tank = 0
Expand Down Expand Up @@ -90,6 +100,7 @@
var/datum/action/innate/mecha/mech_view_stats/stats_action = new
var/datum/action/innate/mecha/mech_defense_mode/defense_action = new
var/datum/action/innate/mecha/mech_overload_mode/overload_action = new
var/datum/action/innate/mecha/mech_charge_mode/charge_action = new
var/datum/effect_system/smoke_spread/smoke_system = new //not an action, but trigged by one
var/datum/action/innate/mecha/mech_smoke/smoke_action = new
var/datum/action/innate/mecha/mech_zoom/zoom_action = new
Expand Down Expand Up @@ -643,6 +654,7 @@
step_silent = FALSE

/obj/mecha/Bump(atom/obstacle)
var/atom/throw_target = get_edge_target_turf(obstacle, dir)
if(phasing && get_charge() >= phasing_energy_drain && !throwing)
if(!can_move)
return
Expand All @@ -654,6 +666,27 @@
forceMove(get_step(src,dir))
use_power(phasing_energy_drain)
addtimer(VARSET_CALLBACK(src, can_move, TRUE), step_in*3)
else if(charging)
if(charge_break_walls && iswallturf(obstacle))
var/turf/closed/wall/crushed = obstacle
playsound(src, 'sound/effects/meteorimpact.ogg', 100, TRUE)
visible_message(span_danger("[src] smashes through [obstacle]"))
crushed.dismantle_wall(TRUE)
if(isobj(obstacle))
var/obj/object = obstacle
obstacle.mech_melee_attack(src)
if(!(object.resistance_flags & INDESTRUCTIBLE) && charge_toss_structures)
object.throw_at(throw_target, 4, 3)
visible_message(span_danger("[src] crashes into [obstacle]!"))
playsound(src, 'sound/effects/bang.ogg', 50, TRUE)
if(ishuman(obstacle))
var/mob/living/carbon/human/H = obstacle
H.throw_at(throw_target,4,3)
visible_message(span_danger("[src] slams into \the [obstacle], sending [obstacle.p_them()] flying!"))
playsound(H, 'sound/effects/bang.ogg', 100, FALSE, -1)
H.Paralyze(20)
H.adjustStaminaLoss(30)
H.apply_damage(rand(20,35), BRUTE)
else
if(..()) //mech was thrown
return
Expand Down Expand Up @@ -1212,3 +1245,30 @@ GLOBAL_VAR_INIT(year_integer, text2num(year)) // = 2013???
else
to_chat(user, "<span class='notice'>None of the equipment on this exosuit can use this ammo!</span>")
return FALSE


///////////////////////
////// Charging /////
///////////////////////

/obj/mecha/proc/start_charge()
Shake(15, 15, 1 SECONDS)
var/obj/effect/temp_visual/decoy/new_decoy = new /obj/effect/temp_visual/decoy(loc,src)
animate(new_decoy, alpha = 0, color = "#5a5858", transform = matrix()*2, time = 2)
addtimer(CALLBACK(src,PROC_REF(handle_charge)),0.5 SECONDS, TIMER_STOPPABLE)

/obj/mecha/proc/handle_charge()
var/turf/mecha_loc = get_turf(src)
charging = TRUE
var/turf/charge_target = get_ranged_target_turf(mecha_loc,dir,charge_distance)
if(!charge_target)
charging = FALSE
return
cell.use(charge_power_consume)
walk_towards(src, charge_target, 0.7)
sleep(get_dist(src, charge_target) * 0.7)
charge_end()

/obj/mecha/proc/charge_end()
walk(src,0)
charging = FALSE
14 changes: 14 additions & 0 deletions code/game/mecha/mecha_actions.dm
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,20 @@
chassis.occupant_message("<span class='notice'>You disable leg actuators overload.</span>")
UpdateButtonIcon()

/datum/action/innate/mecha/mech_charge_mode
name = "Charge"
button_icon_state = "mech_overload_off"

/datum/action/innate/mecha/mech_charge_mode/Activate()
if(!owner || !chassis || chassis.occupant != owner)
return
if(chassis.charge_ready && !chassis.charging)
chassis.start_charge()
chassis.charge_ready = FALSE
addtimer(VARSET_CALLBACK(chassis, charge_ready, TRUE), chassis.charge_cooldown)
else
chassis.occupant_message(span_warning("The leg actuators are still recharging!"))

/datum/action/innate/mecha/mech_smoke
name = "Smoke"
button_icon_state = "mech_smoke"
Expand Down
3 changes: 2 additions & 1 deletion code/modules/power/power.dm
Original file line number Diff line number Diff line change
Expand Up @@ -377,8 +377,9 @@
//source is an object caused electrocuting (airlock, grille, etc)
//siemens_coeff - layman's terms, conductivity
//dist_check - set to only shock mobs within 1 of source (vendors, airlocks, etc.)
//drain_energy - whether the shock will drain power from the mech. Enabled by default.
//No animations will be performed by this proc.
/proc/electrocute_mob(mob/living/carbon/victim, power_source, obj/source, siemens_coeff = 1, dist_check = FALSE)
/proc/electrocute_mob(mob/living/carbon/victim, power_source, obj/source, siemens_coeff = 1, dist_check = FALSE, drain_energy = TRUE)
if(!istype(victim) || ismecha(victim.loc))
return FALSE //feckin mechs are dumb

Expand Down
Binary file added icons/mecha/inteq_gygax.dmi
Binary file not shown.
Binary file modified icons/mecha/mecha.dmi
Binary file not shown.

0 comments on commit d4c2964

Please sign in to comment.