Merge pull request #14482 from silicons/combat_v7

Combat v7 - Sprint removal, automatic block/parry, turns combat mode into a pure UI/interaction toggle with no side effects, and a truckload of other stuff.
This commit is contained in:
Lin
2021-06-27 13:04:29 -07:00
committed by GitHub
76 changed files with 854 additions and 392 deletions

View File

@@ -19,10 +19,62 @@
icon = 'icons/obj/chempuff.dmi'
pass_flags = PASSTABLE | PASSGRILLE
layer = FLY_LAYER
var/stream = FALSE
var/speed = 1
var/range = 3
var/hits_left = 3
var/range_left = 3
/obj/effect/decal/chempuff/blob_act(obj/structure/blob/B)
return
/obj/effect/decal/chempuff/Initialize(mapload, stream_mode, speed, range, hits_left)
. = ..()
stream = stream_mode
src.speed = speed
src.range = src.range_left = range
src.hits_left = hits_left
/obj/effect/decal/chempuff/proc/hit_thing(atom/A)
if(A == src || A.invisibility)
return
if(!hits_left)
return
if(stream)
if(ismob(A))
var/mob/M = A
if(!M.lying || !range_left)
reagents.reaction(M, VAPOR)
hits_left--
else
if(!range_left)
reagents.reaction(A, VAPOR)
else
reagents.reaction(A)
if(ismob(A))
hits_left--
/obj/effect/decal/chempuff/Crossed(atom/movable/AM, oldloc)
. = ..()
hit_thing(AM)
/obj/effect/decal/chempuff/proc/run_puff(atom/target)
set waitfor = FALSE
for(var/i in 1 to range)
range_left--
if(!isturf(loc))
break
for(var/atom/T in loc)
hit_thing(T)
if(!hits_left || !isturf(loc))
break
if(hits_left && isturf(loc) && (!stream || !range_left))
reagents.reaction(loc, VAPOR)
hits_left--
if(!hits_left)
break
qdel(src)
/obj/effect/decal/fakelattice
name = "lattice"
desc = "A lightweight support lattice."

View File

@@ -461,6 +461,21 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_ITEM_PICKUP, user)
item_flags |= IN_INVENTORY
if(item_flags & (ITEM_CAN_BLOCK | ITEM_CAN_PARRY) && user.client && !(type in user.client.block_parry_hinted))
var/list/dat = list("<span class='boldnotice'>You have picked up an item that can be used to block and/or parry:</span>")
// cit change - parry/block feedback
var/datum/block_parry_data/data = return_block_parry_datum(block_parry_data)
if(item_flags & ITEM_CAN_BLOCK)
dat += "[src] can be used to block damage using directional block. Press your active block keybind to use it."
if(data.block_automatic_enabled)
dat += "[src] is also capable of automatically blocking damage, if you are facing the right direction (usually towards your attacker)!"
if(item_flags & ITEM_CAN_PARRY)
dat += "[src] can be used to parry damage using active parry. Pressed your active parry keybind to initiate a timed parry sequence."
if(data.parry_automatic_enabled)
dat += "[src] is also capable of automatically parrying an incoming attack, if your mouse is over your attacker at the time if you being hit in a direct, melee attack."
dat += "Examine [src] to get a full readout of its block/parry stats."
to_chat(user, dat.Join("<br>"))
user.client.block_parry_hinted |= type
// called when "found" in pockets and storage items. Returns 1 if the search should end.
/obj/item/proc/on_found(mob/finder)
@@ -504,12 +519,13 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
*/
/obj/item/proc/equipped(mob/user, slot, initial = FALSE)
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_ITEM_EQUIPPED, user, slot)
var/signal_flags = SEND_SIGNAL(src, COMSIG_ITEM_EQUIPPED, user, slot)
current_equipped_slot = slot
for(var/X in actions)
var/datum/action/A = X
if(item_action_slot_check(slot, user, A)) //some items only give their actions buttons when in a specific slot.
A.Grant(user)
if(!(signal_flags & COMPONENT_NO_GRANT_ACTIONS))
for(var/X in actions)
var/datum/action/A = X
if(item_action_slot_check(slot, user, A)) //some items only give their actions buttons when in a specific slot.
A.Grant(user)
item_flags |= IN_INVENTORY
// if(!initial)
// if(equip_sound && (slot_flags & slot))

View File

@@ -78,22 +78,29 @@ effective or pretty fucking useless.
var/ui_x = 320
var/ui_y = 335
/obj/item/healthanalyzer/rad_laser/Initialize()
. = ..()
AddComponent(/datum/component/identification/syndicate, ID_COMPONENT_DEL_ON_IDENTIFY, ID_COMPONENT_EFFECT_NO_ACTIONS, ID_COMPONENT_IDENTIFY_WITH_DECONSTRUCTOR)
/obj/item/healthanalyzer/rad_laser/attack(mob/living/M, mob/living/user)
if(!stealth || !irradiate)
..()
return ..()
var/knowledge = SEND_SIGNAL(src, COMSIG_IDENTIFICATION_KNOWLEDGE_CHECK, user) == ID_COMPONENT_KNOWLEDGE_FULL
if(!irradiate)
return
if(!used)
log_combat(user, M, "irradiated", src)
log_combat(user, M, "[knowledge? "" : "unknowingly "]irradiated", src)
var/cooldown = get_cooldown()
used = TRUE
icon_state = "health1"
addtimer(VARSET_CALLBACK(src, used, FALSE), cooldown)
addtimer(VARSET_CALLBACK(src, icon_state, "health"), cooldown)
to_chat(user, "<span class='warning'>Successfully irradiated [M].</span>")
if(knowledge)
to_chat(user, "<span class='warning'>Successfully irradiated [M].</span>")
addtimer(CALLBACK(src, .proc/radiation_aftereffect, M, intensity), (wavelength+(intensity*4))*5)
else
to_chat(user, "<span class='warning'>The radioactive microlaser is still recharging.</span>")
if(knowledge)
to_chat(user, "<span class='warning'>The radioactive microlaser is still recharging.</span>")
/obj/item/healthanalyzer/rad_laser/proc/radiation_aftereffect(mob/living/M, passed_intensity)
if(QDELETED(M) || !ishuman(M) || HAS_TRAIT(M, TRAIT_RADIMMUNE))
@@ -109,7 +116,9 @@ effective or pretty fucking useless.
interact(user)
/obj/item/healthanalyzer/rad_laser/interact(mob/user)
ui_interact(user)
var/knowledge = SEND_SIGNAL(src, COMSIG_IDENTIFICATION_KNOWLEDGE_CHECK, user) == ID_COMPONENT_KNOWLEDGE_FULL
if(knowledge)
ui_interact(user)
/obj/item/healthanalyzer/rad_laser/ui_state(mob/user)
return GLOB.hands_state

View File

@@ -41,7 +41,7 @@
/datum/block_parry_data/dual_esword // please run at the man going apeshit with his funny doublesword
can_block_directions = BLOCK_DIR_NORTH | BLOCK_DIR_NORTHEAST | BLOCK_DIR_NORTHWEST | BLOCK_DIR_WEST | BLOCK_DIR_EAST
block_damage_absorption = 2
block_damage_absorption = 5
block_damage_multiplier = 0.15
block_damage_multiplier_override = list(
ATTACK_TYPE_MELEE = 0.25
@@ -59,7 +59,7 @@
)
parry_time_windup = 0
parry_time_active = 8
parry_time_active = 12
parry_time_spindown = 0
// we want to signal to players the most dangerous phase, the time when automatic counterattack is a thing.
parry_time_windup_visual_override = 1
@@ -69,12 +69,10 @@
parry_time_perfect = 2 // first ds isn't perfect
parry_time_perfect_leeway = 1
parry_imperfect_falloff_percent = 10
parry_efficiency_to_counterattack = 100
parry_efficiency_considered_successful = 25 // VERY generous
parry_failed_stagger_duration = 3 SECONDS
parry_failed_clickcd_duration = CLICK_CD_MELEE
/obj/item/dualsaber/active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction)
/obj/item/dualsaber/directional_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction)
if((attack_type & ATTACK_TYPE_PROJECTILE) && is_energy_reflectable_projectile(object))
block_return[BLOCK_RETURN_REDIRECT_METHOD] = REDIRECT_METHOD_RETURN_TO_SENDER
return BLOCK_SUCCESS | BLOCK_REDIRECTED | BLOCK_SHOULD_REDIRECT
@@ -83,7 +81,7 @@
/obj/item/dualsaber/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/block_return, parry_efficiency, parry_time)
. = ..()
if(parry_efficiency >= 90) // perfect parry
block_return[BLOCK_RETURN_REDIRECT_METHOD] = REDIRECT_METHOD_RETURN_TO_SENDER
block_return[BLOCK_RETURN_REDIRECT_METHOD] = REDIRECT_METHOD_DEFLECT
. |= BLOCK_SHOULD_REDIRECT
/obj/item/dualsaber/Initialize()
@@ -369,14 +367,12 @@
parry_time_perfect = 1
parry_time_perfect_leeway = 1
parry_imperfect_falloff_percent = 7.5
parry_efficiency_to_counterattack = 100
parry_efficiency_considered_successful = 80
parry_efficiency_perfect = 120
parry_efficiency_perfect_override = list(
TEXT_ATTACK_TYPE_PROJECTILE = 30,
)
parry_failed_stagger_duration = 3 SECONDS
parry_failed_clickcd_duration = 2 SECONDS
/obj/item/dualsaber/hypereutactic/chaplain/ComponentInitialize()
. = ..()

View File

@@ -45,15 +45,15 @@
// no attacking while blocking
block_lock_attacking = TRUE
parry_time_windup = 1
parry_time_active = 5
parry_time_windup = 0
parry_time_active = 7
parry_time_spindown = 0
parry_time_spindown_visual_override = 1
parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK | PARRY_LOCK_ATTACKING // no attacking while parrying
parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK // no attacking while parrying
parry_time_perfect = 0
parry_time_perfect_leeway = 0.5
parry_efficiency_perfect = 100
parry_imperfect_falloff_percent = 1
parry_efficiency_perfect = 85
parry_imperfect_falloff_percent = 10
parry_imperfect_falloff_percent_override = list(
TEXT_ATTACK_TYPE_PROJECTILE = 45 // really crappy vs projectiles
)
@@ -61,9 +61,7 @@
TEXT_ATTACK_TYPE_PROJECTILE = 1 // extremely harsh window for projectiles
)
// not extremely punishing to fail, but no spamming the parry.
parry_cooldown = 2.5 SECONDS
parry_failed_stagger_duration = 1.5 SECONDS
parry_failed_clickcd_duration = 1 SECONDS
/obj/item/electrostaff/Initialize(mapload)
. = ..()

View File

@@ -121,11 +121,14 @@
parry_time_perfect = 2.5 // first ds isn't perfect
parry_time_perfect_leeway = 1.5
parry_imperfect_falloff_percent = 5
parry_efficiency_to_counterattack = 100
parry_efficiency_to_counterattack = INFINITY
parry_efficiency_considered_successful = 65 // VERY generous
parry_efficiency_perfect = 100
parry_failed_stagger_duration = 4 SECONDS
parry_cooldown = 0.5 SECONDS
parry_automatic_enabled = TRUE
autoparry_single_efficiency = 65
autoparry_cooldown_absolute = 3 SECONDS
/obj/item/melee/transforming/energy/sword/Initialize(mapload)
. = ..()
@@ -149,8 +152,8 @@
/obj/item/melee/transforming/energy/sword/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/block_return, parry_efficiency, parry_time)
. = ..()
if(parry_efficiency >= 80) // perfect parry
block_return[BLOCK_RETURN_REDIRECT_METHOD] = REDIRECT_METHOD_RETURN_TO_SENDER
if(parry_efficiency >= 100) // perfect parry
block_return[BLOCK_RETURN_REDIRECT_METHOD] = REDIRECT_METHOD_DEFLECT
. |= BLOCK_SHOULD_REDIRECT
/obj/item/melee/transforming/energy/sword/cyborg

View File

@@ -72,15 +72,17 @@
block_parry_data = /datum/block_parry_data/captain_saber
/datum/block_parry_data/captain_saber
parry_time_windup = 0.5
parry_time_active = 4
parry_time_spindown = 1
parry_time_windup = 0
parry_time_active = 6
parry_time_spindown = 0
parry_time_perfect = 0.75
parry_time_perfect_leeway = 0.75
parry_imperfect_falloff_percent = 30
parry_efficiency_perfect = 100
parry_failed_stagger_duration = 3 SECONDS
parry_failed_clickcd_duration = 2 SECONDS
parry_failed_clickcd_duration = 1 SECONDS
parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK
parry_automatic_enabled = TRUE
/obj/item/melee/sabre/Initialize()
. = ..()
@@ -176,30 +178,24 @@
// Fast, efficient parry.
/datum/block_parry_data/traitor_rapier
parry_time_windup = 0.5
parry_time_active = 5
parry_time_windup = 0
parry_time_active = 6
parry_time_spindown = 0
parry_time_active_visual_override = 3
parry_time_spindown_visual_override = 2
parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK | PARRY_LOCK_ATTACKING
parry_time_perfect = 0
parry_time_perfect_leeway = 3
parry_time_perfect = 1
parry_time_perfect_leeway = 1
parry_time_perfect_leeway_override = list(
TEXT_ATTACK_TYPE_PROJECTILE = 1
)
parry_imperfect_falloff_percent_override = list(
TEXT_ATTACK_TYPE_PROJECTILE = 50 // useless after 3rd decisecond
)
parry_imperfect_falloff_percent = 30
parry_efficiency_to_counterattack = 100
parry_efficiency_to_counterattack = INFINITY
parry_efficiency_considered_successful = 1
parry_efficiency_perfect = 100
parry_data = list(
PARRY_DISARM_ATTACKER = TRUE,
PARRY_KNOCKDOWN_ATTACKER = 10
)
parry_stamina_cost = 5
parry_failed_stagger_duration = 2 SECONDS
parry_failed_clickcd_duration = CLICK_CD_RANGE
parry_automatic_enabled = TRUE
parry_cooldown = 0
/obj/item/melee/rapier/active_parry_reflex_counter(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list, parry_efficiency, list/effect_text)
@@ -259,6 +255,8 @@
var/stam_dmg = 30
var/cooldown_check = 0 // Used internally, you don't want to modify
var/cooldown = 13 // Default wait time until can stun again.
/// block mitigation needed to prevent knockdown/disarms
var/block_percent_to_counter = 50
var/stun_time_silicon = 60 // How long it stuns silicons for - 6 seconds.
var/affect_silicon = FALSE // Does it stun silicons.
var/on_sound // "On" sound, played when switching between able to stun or not.
@@ -356,7 +354,8 @@
if(cooldown_check < world.time)
if(!UseStaminaBufferStandard(user, STAM_COST_BATON_MOB_MULT, warn = TRUE))
return DISCARD_LAST_ACTION
if(target.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, null) & BLOCK_SUCCESS)
var/list/block_return = list()
if(target.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, block_return) & BLOCK_SUCCESS)
playsound(target, 'sound/weapons/genhit.ogg', 50, 1)
return
if(ishuman(target))
@@ -367,7 +366,8 @@
if(stun_animation)
user.do_attack_animation(target)
playsound(get_turf(src), on_stun_sound, 75, 1, -1)
target.DefaultCombatKnockdown(softstun_ds, TRUE, FALSE, hardstun_ds, stam_dmg)
var/countered = block_return[BLOCK_RETURN_MITIGATION_PERCENT] > block_percent_to_counter
target.DefaultCombatKnockdown(softstun_ds, TRUE, FALSE, countered? 0 : hardstun_ds, stam_dmg, !countered)
additional_effects_carbon(target, user)
log_combat(user, target, "stunned", src)
add_fingerprint(user)

View File

@@ -681,7 +681,7 @@ as performing this in action() will cause the upgrade to end up in the borg inst
action_icon_state = "Chevron_State_0"
var/currentState = 0
var/maxReduction = 1
var/maxReduction = 0.5
/obj/effect/proc_holder/silicon/cyborg/vtecControl/Trigger(mob/living/silicon/robot/user)

View File

@@ -34,6 +34,10 @@
block_damage_absorption = 5
block_resting_stamina_penalty_multiplier = 2
block_projectile_mitigation = 75
block_damage_absorption_override = list(
TEXT_ATTACK_TYPE_TACKLE = INFINITY,
TEXT_ATTACK_TYPE_THROWN = 10
)
/obj/item/shield/examine(mob/user)
. = ..()
@@ -120,8 +124,6 @@
return TRUE
/obj/item/shield/proc/user_shieldbash(mob/living/user, atom/target, harmful)
if(!SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) //Combat mode has to be enabled for shield bashing
return FALSE
if(!(shield_flags & SHIELD_CAN_BASH))
to_chat(user, "<span class='warning'>[src] can't be used to shield bash!</span>")
return FALSE
@@ -376,11 +378,10 @@
/obj/item/shield/riot/flash/on_shield_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
. = ..()
if (. && !embedded_flash.crit_fail)
if (. && damage && !embedded_flash.crit_fail)
embedded_flash.activate()
update_icon()
/obj/item/shield/riot/flash/attackby(obj/item/W, mob/user)
if(istype(W, /obj/item/assembly/flash/handheld))
var/obj/item/assembly/flash/handheld/flash = W
@@ -564,7 +565,7 @@
return BLOCK_SUCCESS | BLOCK_REDIRECTED | BLOCK_SHOULD_REDIRECT
return ..()
/obj/item/shield/energy/active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction)
/obj/item/shield/energy/directional_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction)
if((attack_type & ATTACK_TYPE_PROJECTILE) && is_energy_reflectable_projectile(object))
block_return[BLOCK_RETURN_REDIRECT_METHOD] = REDIRECT_METHOD_DEFLECT
return BLOCK_SUCCESS | BLOCK_REDIRECTED | BLOCK_SHOULD_REDIRECT

View File

@@ -19,6 +19,8 @@
var/stamina_loss_amount = 35
var/turned_on = FALSE
var/knockdown = TRUE
/// block percent needed to prevent knockdown/disarm
var/block_percent_to_counter = 50
var/obj/item/stock_parts/cell/cell
var/hitcost = 750
var/throw_hit_chance = 35

View File

@@ -332,6 +332,12 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
bare_wound_bonus = 0
wound_bonus = 0
/obj/item/melee/bokken/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/block_return, parry_efficiency, parry_time)
. = ..()
if(!istype(object, /obj/item/melee/bokken))
// no counterattack.
block_return[BLOCK_RETURN_FORCE_NO_PARRY_COUNTERATTACK] = TRUE
/datum/block_parry_data/bokken // fucked up parry data, emphasizing quicker, shorter parries
parry_stamina_cost = 10 // be wise about when you parry, though, else you won't be able to fight enough to make it count
parry_time_windup = 0
@@ -358,7 +364,6 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
parry_time_perfect = 2.5 // however...
parry_time_perfect_leeway = 2 // the entire time, the parry is perfect
parry_failed_stagger_duration = 1 SECONDS
parry_failed_clickcd_duration = 1 SECONDS // more forgiving punishments for missed parries
// still, don't fucking miss your parries or you're down stamina and staggered to shit
/datum/block_parry_data/bokken/quick_parry/proj
@@ -475,7 +480,6 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
parry_time_perfect = 1
parry_time_perfect_leeway = 1
parry_failed_stagger_duration = 1 SECONDS
parry_failed_clickcd_duration = 1 SECONDS
/datum/block_parry_data/bokken/waki/quick_parry/proj
parry_efficiency_perfect_override = list()

View File

@@ -348,7 +348,7 @@
parry_time_perfect = 1.5
parry_time_perfect_leeway = 1
parry_imperfect_falloff_percent = 7.5
parry_efficiency_to_counterattack = 100
parry_efficiency_to_counterattack = INFINITY
parry_efficiency_considered_successful = 50
parry_efficiency_perfect = 120
parry_efficiency_perfect_override = list(