Skip to content
This repository has been archived by the owner on Sep 12, 2021. It is now read-only.

Commit

Permalink
[READY] Re-works the Durand ability (#45144)
Browse files Browse the repository at this point in the history
* Woo shields

* reshuffle, adding a sound

* Re-organizing

* better defines for the signals I had

* new stuff

* fixes

* Replaced all instances of "defence" with defense"

and other small changes.

* oof

* spelling, and a forgotten check

* you, upgraded, improved, yes
  • Loading branch information
zxaber authored and Rob Bailey committed Jul 27, 2019
1 parent 7704552 commit aaaee29
Show file tree
Hide file tree
Showing 12 changed files with 209 additions and 40 deletions.
4 changes: 4 additions & 0 deletions code/__DEFINES/components.dm
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,10 @@
// /obj/item/projectile signals (sent to the firer)
#define COMSIG_PROJECTILE_ON_HIT "projectile_on_hit" // from base of /obj/item/projectile/proc/on_hit(): (atom/movable/firer, atom/target, Angle)
#define COMSIG_PROJECTILE_BEFORE_FIRE "projectile_before_fire" // from base of /obj/item/projectile/proc/fire(): (obj/item/projectile, atom/original_target)
#define COMSIG_PROJECTILE_PREHIT "com_proj_prehit" // sent to targets during the process_hit proc of projectiles

// /obj/mecha signals
#define COMSIG_MECHA_ACTION_ACTIVATE "mecha_action_activate" //sent from mecha action buttons to the mecha they're linked to

// /mob/living/carbon/human signals
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target)
Expand Down
187 changes: 187 additions & 0 deletions code/game/mecha/combat/durand.dm
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@
infra_luminosity = 8
force = 40
wreckage = /obj/structure/mecha_wreckage/durand
var/obj/durand_shield/shield

/obj/mecha/combat/durand/Initialize()
shield = new/obj/durand_shield
shield.chassis = src
shield.layer = layer
RegisterSignal(src, COMSIG_MECHA_ACTION_ACTIVATE, .proc/relay)
RegisterSignal(src, COMSIG_PROJECTILE_PREHIT, .proc/prehit)
. = ..()

/obj/mecha/combat/durand/Destroy()
if(shield)
qdel(shield)
. = ..()

/obj/mecha/combat/durand/GrantActions(mob/living/user, human_occupant = 0)
..()
Expand All @@ -20,3 +34,176 @@
..()
defense_action.Remove(user)

/obj/mecha/combat/durand/process()
. = ..()
if(defense_mode && !use_power(100))
defense_action.Activate(forced_state = TRUE)

/obj/mecha/combat/durand/domove(direction)
. = ..()
if(shield)
shield.forceMove(loc)
shield.dir = dir

/obj/mecha/combat/durand/forceMove(var/turf/T)
. = ..()
shield.forceMove(T)

/obj/mecha/combat/durand/go_out(forced, atom/newloc = loc)
if(defense_mode)
defense_action.Activate(forced_state = TRUE)
. = ..()

///Relays the signal from the action button to the shield, and creates a new shield if the old one is MIA.
/obj/mecha/combat/durand/proc/relay(datum/source, list/signal_args)
if(!shield) //if the shield somehow got deleted
shield = new/obj/durand_shield
shield.chassis = src
shield.layer = layer
shield.forceMove(loc)
shield.dir = 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/item/projectile/source, list/signal_args)
if(defense_check(source.loc) && shield)
signal_args[2] = shield


/**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(var/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/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))
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/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/mecha/combat/durand/attackby(obj/item/W as obj, mob/user as mob, params)
if(defense_check(user.loc))
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))
log_message("Impact with [AM] absorbed by defense field.", LOG_MECHA, color="orange")
shield.hitby(AM, skipcatch, hitpush, blocked, throwingdatum)
else
. = ..()

////////////////////////////
///// 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/mecha/durand_shield.dmi'
icon_state = "shield_null"
invisibility = INVISIBILITY_MAXIMUM //no showing on right-click
pixel_y = 4
max_integrity = 10000
obj_integrity = 10000
var/obj/mecha/combat/durand/chassis ///Our link back to the durand
var/switching = FALSE ///To keep track of things during the animation

/obj/durand_shield/Initialize()
. = ..()
RegisterSignal(src, COMSIG_MECHA_ACTION_ACTIVATE, .proc/activate)

/obj/durand_shield/Destroy()
if(chassis)
chassis.shield = null
. = ..()

/**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" variabe, 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)*/

/obj/durand_shield/proc/activate(datum/source, var/datum/action/innate/mecha/mech_defense_mode/button, list/signal_args)
if(!chassis || !chassis.occupant)
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.occupant_message("<span class='warn'>Insufficient power; cannot activate defense mode.</span>")
return
switching = TRUE
chassis.defense_mode = !chassis.defense_mode
chassis.defense_action.button_icon_state = "mech_defense_mode_[chassis.defense_mode ? "on" : "off"]" //This is backwards because we haven't changed the var yet
if(!signal_args[1])
chassis.occupant_message("<span class='notice'>Defense mode [chassis.defense_mode?"enabled":"disabled"].</span>")
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)
chassis.defense_action.UpdateButtonIcon()

if(chassis.defense_mode)
invisibility = 0
flick("shield_raise", src)
playsound(src, 'sound/mecha/mech_shield_raise.ogg', 50, FALSE)
set_light(l_range = MINIMUM_USEFUL_LIGHT_RANGE , l_power = 5, l_color = "#00FFFF")
sleep(3)
icon_state = "shield"
else
flick("shield_drop", src)
playsound(src, 'sound/mecha/mech_shield_drop.ogg', 50, FALSE)
sleep(5)
set_light(0)
icon_state = "shield_null"
invisibility = INVISIBILITY_MAXIMUM //no showing on right-click
switching = FALSE

/obj/durand_shield/take_damage()
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(!chassis.use_power((max_integrity - obj_integrity) * 100))
chassis.cell?.charge = 0
chassis.defense_action.Activate(forced_state = TRUE)
obj_integrity = 10000

/obj/durand_shield/play_attack_sound()
playsound(src, 'sound/mecha/mech_shield_deflect.ogg', 100, TRUE)

/obj/durand_shield/bullet_act()
play_attack_sound()
. = ..()
13 changes: 3 additions & 10 deletions code/game/mecha/mecha.dm
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
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/mech_toggle_thrusters/thrusters_action = new
var/datum/action/innate/mecha/mech_defence_mode/defense_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/effect_system/smoke_spread/smoke_system = new //not an action, but trigged by one
var/datum/action/innate/mecha/mech_smoke/smoke_action = new
Expand All @@ -109,8 +109,7 @@

//Action vars
var/thrusters_active = FALSE
var/defence_mode = FALSE
var/defence_mode_deflect_chance = 35
var/defense_mode = FALSE
var/leg_overload_mode = FALSE
var/leg_overload_coeff = 100
var/zoom_mode = FALSE
Expand Down Expand Up @@ -577,11 +576,6 @@
return 0
if(!has_charge(step_energy_drain))
return 0
if(defence_mode)
if(world.time - last_message > 20)
occupant_message("<span class='danger'>Unable to move while in defence mode</span>")
last_message = world.time
return 0
if(zoom_mode)
if(world.time - last_message > 20)
occupant_message("Unable to move while in zoom mode.")
Expand Down Expand Up @@ -1100,8 +1094,7 @@ GLOBAL_VAR_INIT(year_integer, text2num(year)) // = 2013???
return max(0, cell.charge)

/obj/mecha/proc/use_power(amount)
if(get_charge())
cell.use(amount)
if(get_charge() && cell.use(amount))
return 1
return 0

Expand Down
24 changes: 5 additions & 19 deletions code/game/mecha/mecha_actions.dm
Original file line number Diff line number Diff line change
Expand Up @@ -164,27 +164,13 @@
chassis.log_message("Toggled thrusters.", LOG_MECHA)
chassis.occupant_message("<font color='[chassis.thrusters_active ?"blue":"red"]'>Thrusters [chassis.thrusters_active ?"en":"dis"]abled.")


/datum/action/innate/mecha/mech_defence_mode
name = "Toggle Defence Mode"
/datum/action/innate/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"
var/image/def_overlay

/datum/action/innate/mecha/mech_defence_mode/Activate(forced_state = null)
if(!owner || !chassis || chassis.occupant != owner)
return
if(!isnull(forced_state))
chassis.defence_mode = forced_state
else
chassis.defence_mode = !chassis.defence_mode
button_icon_state = "mech_defense_mode_[chassis.defence_mode ? "on" : "off"]"
if(chassis.defence_mode)
chassis.deflect_chance = chassis.defence_mode_deflect_chance
chassis.occupant_message("<span class='notice'>You enable [chassis] defence mode.</span>")
else
chassis.deflect_chance = initial(chassis.deflect_chance)
chassis.occupant_message("<span class='danger'>You disable [chassis] defence mode.</span>")
chassis.log_message("Toggled defence mode.", LOG_MECHA)
UpdateButtonIcon()
/datum/action/innate/mecha/mech_defense_mode/Activate(forced_state = FALSE)
SEND_SIGNAL(chassis, COMSIG_MECHA_ACTION_ACTIVATE, args) ///Signal sent to the mech, to be handed to the shield. See durand.dm for more details

/datum/action/innate/mecha/mech_overload_mode
name = "Toggle leg actuators overload"
Expand Down
6 changes: 2 additions & 4 deletions code/game/mecha/mecha_defense.dm
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
if(.)
. *= booster_damage_modifier


/obj/mecha/attack_hand(mob/living/user)
. = ..()
if(.)
Expand All @@ -67,7 +66,6 @@
/obj/mecha/attack_paw(mob/user as mob)
return attack_hand(user)


/obj/mecha/attack_alien(mob/living/user)
log_message("Attack by alien. Attacker - [user].", LOG_MECHA, color="red")
playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1)
Expand All @@ -87,8 +85,8 @@
if(user.obj_damage)
animal_damage = user.obj_damage
animal_damage = min(animal_damage, 20*user.environment_smash)
attack_generic(user, animal_damage, user.melee_damage_type, "melee", play_soundeffect)
log_combat(user, src, "attacked")
attack_generic(user, animal_damage, user.melee_damage_type, "melee", play_soundeffect)
return 1


Expand All @@ -102,6 +100,7 @@
log_combat(user, src, "punched", "hulk powers")

/obj/mecha/blob_act(obj/structure/blob/B)
log_message("Attack by blob. Attacker - [B].", LOG_MECHA, color="red")
take_damage(30, BRUTE, "melee", 0, get_dir(src, B))

/obj/mecha/attack_tk()
Expand All @@ -111,7 +110,6 @@
log_message("Hit by [AM].", LOG_MECHA, color="red")
. = ..()


/obj/mecha/bullet_act(obj/item/projectile/Proj) //wrapper
if (!enclosed && occupant && !silicon_pilot && !Proj.force_hit && (Proj.def_zone == BODY_ZONE_HEAD || Proj.def_zone == BODY_ZONE_CHEST)) //allows bullets to hit the pilot of open-canopy mechs
occupant.bullet_act(Proj) //If the sides are open, the occupant can be hit
Expand Down
2 changes: 1 addition & 1 deletion code/game/mecha/mecha_topic.dm
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
<b>Cabin temperature: </b> [internal_tank?"[return_temperature()]&deg;K|[return_temperature() - T0C]&deg;C":"N/A"]<br>
[dna_lock?"<b>DNA-locked:</b><br> <span style='font-size:10px;letter-spacing:-1px;'>[dna_lock]</span> \[<a href='?src=[REF(src)];reset_dna=1'>Reset</a>\]<br>":""]<br>
[thrusters_action.owner ? "<b>Thrusters: </b> [thrusters_active ? "Enabled" : "Disabled"]<br>" : ""]
[defense_action.owner ? "<b>Defence Mode: </b> [defence_mode ? "Enabled" : "Disabled"]<br>" : ""]
[defense_action.owner ? "<b>Defense Mode: </b> [defense_mode ? "Enabled" : "Disabled"]<br>" : ""]
[overload_action.owner ? "<b>Leg Actuators Overload: </b> [leg_overload_mode ? "Enabled" : "Disabled"]<br>" : ""]
[smoke_action.owner ? "<b>Smoke: </b> [smoke]<br>" : ""]
[zoom_action.owner ? "<b>Zoom: </b> [zoom_mode ? "Enabled" : "Disabled"]<br>" : ""]
Expand Down
12 changes: 6 additions & 6 deletions code/modules/mob/living/simple_animal/hostile/mecha_pilot.dm
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

//Vars that control when the pilot uses their mecha's abilities (if the mecha has that ability)
var/threat_use_mecha_smoke = 5 //5 mobs is enough to engage crowd control
var/defence_mode_chance = 35 //Chance to engage Defence mode when damaged
var/defense_mode_chance = 35 //Chance to engage Defense mode when damaged
var/smoke_chance = 20 //Chance to deploy smoke for crowd control
var/retreat_chance = 40 //Chance to run away

Expand Down Expand Up @@ -226,19 +226,19 @@
if(mecha.smoke_action && mecha.smoke_action.owner && mecha.smoke)
mecha.smoke_action.Activate()

//Heavy damage - Defence Power or Retreat
//Heavy damage - Defense Power or Retreat
if(mecha.obj_integrity < mecha.max_integrity*0.25)
if(prob(defence_mode_chance))
if(mecha.defense_action && mecha.defense_action.owner && !mecha.defence_mode)
if(prob(defense_mode_chance))
if(mecha.defense_action && mecha.defense_action.owner && !mecha.defense_mode)
mecha.leg_overload_mode = 0
mecha.defense_action.Activate(TRUE)
addtimer(CALLBACK(mecha.defense_action, /datum/action/innate/mecha/mech_defence_mode.proc/Activate, FALSE), 100) //10 seconds of defence, then toggle off
addtimer(CALLBACK(mecha.defense_action, /datum/action/innate/mecha/mech_defense_mode.proc/Activate, FALSE), 100) //10 seconds of defense, then toggle off

else if(prob(retreat_chance))
//Speed boost if possible
if(mecha.overload_action && mecha.overload_action.owner && !mecha.leg_overload_mode)
mecha.overload_action.Activate(TRUE)
addtimer(CALLBACK(mecha.overload_action, /datum/action/innate/mecha/mech_defence_mode.proc/Activate, FALSE), 100) //10 seconds of speeeeed, then toggle off
addtimer(CALLBACK(mecha.overload_action, /datum/action/innate/mecha/mech_defense_mode.proc/Activate, FALSE), 100) //10 seconds of speeeeed, then toggle off

retreat_distance = 50
spawn(100)
Expand Down
1 change: 1 addition & 0 deletions code/modules/projectiles/projectile.dm
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@
permutated |= target //Make sure we're never hitting it again. If we ever run into weirdness with piercing projectiles needing to hit something multiple times.. well.. that's a to-do.
if(!prehit(target))
return process_hit(T, select_target(T), qdel_self, hit_something) //Hit whatever else we can since that didn't work.
SEND_SIGNAL(target, COMSIG_PROJECTILE_PREHIT, args)
var/result = target.bullet_act(src, def_zone)
if(result == BULLET_ACT_FORCE_PIERCE)
if(!CHECK_BITFIELD(movement_type, UNSTOPPABLE))
Expand Down
Binary file added icons/mecha/durand_shield.dmi
Binary file not shown.
Binary file added sound/mecha/mech_shield_deflect.ogg
Binary file not shown.
Binary file added sound/mecha/mech_shield_drop.ogg
Binary file not shown.
Binary file added sound/mecha/mech_shield_raise.ogg
Binary file not shown.

0 comments on commit aaaee29

Please sign in to comment.