diff --git a/code/__DEFINES/combat/block.dm b/code/__DEFINES/combat/block.dm index bc85947d71..9be4aaa2bf 100644 --- a/code/__DEFINES/combat/block.dm +++ b/code/__DEFINES/combat/block.dm @@ -63,6 +63,8 @@ #define BLOCK_RETURN_MITIGATION_PERCENT "partial_mitigation" /// Used internally by run_parry proc, use on an on_active_parry() proc to override parrying efficiency. #define BLOCK_RETURN_OVERRIDE_PARRY_EFFICIENCY "override_parry_efficiency" +/// Used internally by run_parry proc, use on an on_active_parry() proc to prevent counterattacks +#define BLOCK_RETURN_FORCE_NO_PARRY_COUNTERATTACK "no_parry_counterattack" /// Always set to 100 by run_block() if BLOCK_SUCCESS is in return value. Otherwise, defaults to mitigation percent if not set. Used by projectile/proc/on_hit(). #define BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE "projectile_block_percentage" diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index e8ab7899db..5f8745ca8e 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -570,3 +570,6 @@ // twitch plays /// Returns direction: (wipe_votes) #define COMSIG_TWITCH_PLAYS_MOVEMENT_DATA "twitch_plays_movement_data" + +// /datum/component/identification signals +#define COMSIG_IDENTIFICATION_KNOWLEDGE_CHECK "id_knowledge_check" // (mob/user) - returns a value from ID_COMPONENT_KNOWLEDGE_NONE to ID_COMPONENT_KNOWLEDGE_FULL diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 86fdd23d0b..14382eb09c 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -9,6 +9,9 @@ #define TEXT_EAST "[EAST]" #define TEXT_WEST "[WEST]" +/// yeah yeah i'm a lazy asshole who can't debug yeah yeah +#define DEBUG_LINE message_admins("DEBUG: [__FILE__] [__LINE__] executing!") + /// world.icon_size #define PIXELS 32 diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm index 1098a07b39..72543970ba 100644 --- a/code/__DEFINES/sound.dm +++ b/code/__DEFINES/sound.dm @@ -25,7 +25,11 @@ ///Percentage of sound's range where no falloff is applied #define SOUND_DEFAULT_FALLOFF_DISTANCE 1 //For a normal sound this would be 1 tile of no falloff ///The default exponent of sound falloff -#define SOUND_FALLOFF_EXPONENT 6 +#define SOUND_FALLOFF_EXPONENT 7.5 +/// Default distance multiplier for sounds +#define SOUND_DEFAULT_DISTANCE_MULTIPLIER 2.5 +/// Default range at which sound distance multiplier applies +#define SOUND_DEFAULT_MULTIPLIER_EFFECT_RANGE 7 //THIS SHOULD ALWAYS BE THE LOWEST ONE! //KEEP IT UPDATED diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index a047020999..5c6c8b143c 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -105,8 +105,6 @@ #define STATUS_EFFECT_FAKE_VIRUS /datum/status_effect/fake_virus //gives you fluff messages for cough, sneeze, headache, etc but without an actual virus -#define STATUS_EFFECT_NO_COMBAT_MODE /datum/status_effect/no_combat_mode //Wont allow combat mode and will disable it - #define STATUS_EFFECT_STASIS /datum/status_effect/grouped/stasis //Halts biological functions like bleeding, chemical processing, blood regeneration, walking, etc #define STATUS_EFFECT_MESMERIZE /datum/status_effect/mesmerize //Just reskinned no_combat_mode diff --git a/code/__HELPERS/do_after.dm b/code/__HELPERS/do_after.dm index c65cd80394..c0c07e3620 100644 --- a/code/__HELPERS/do_after.dm +++ b/code/__HELPERS/do_after.dm @@ -1,23 +1,24 @@ /** - * Higher overhead "advanced" version of do_after. - * @params - * - atom/user is the atom doing the action or the "physical" user - * - delay is time in deciseconds - * - atom/target is the atom the action is being done to, defaults to user - * - do_after_flags see __DEFINES/flags/do_after.dm for details. - * - datum/callback/extra_checks - Every time this ticks, extra_checks() is invoked with (user, delay, target, time_left, do_after_flags, required_mobility_flags, required_combat_flags, mob_redirect, stage, initially_held_item, tool). - * Stage can be DO_AFTER_STARTING, DO_AFTER_PROGRESSING, DO_AFTER_FINISHING - * If it returns DO_AFTER_STOP, this breaks. - * If it returns nothing, all other checks are done. - * If it returns DO_AFTER_PROCEED, all other checks are ignored. - * - required_mobility_flags is checked with CHECK_ALL_MOBILITY. Will immediately fail if the user isn't a mob. - * - requried_combat_flags is checked with CHECK_MULTIPLE_BITFIELDS. Will immediately fail if the user isn't a mob. - * - mob/living/mob_redirect - advanced option: If this is specified, movement and mobility/combat flag checks will use this instead of user. Progressbars will also go to this. - * - obj/item/tool - The tool we're using. See do_after flags for details. - */ -#define INVOKE_CALLBACK cb_return = extra_checks?.Invoke(user, delay, target, world.time - starttime, do_after_flags, required_mobility_flags, required_combat_flags, mob_redirect, stage, initially_held_item, tool) + * Higher overhead "advanced" version of do_after. + * @params + * - atom/user is the atom doing the action or the "physical" user + * - delay is time in deciseconds + * - atom/target is the atom the action is being done to, defaults to user + * - do_after_flags see __DEFINES/flags/do_after.dm for details. + * - datum/callback/extra_checks - Every time this ticks, extra_checks() is invoked with (user, delay, target, time_left, do_after_flags, required_mobility_flags, required_combat_flags, mob_redirect, stage, initially_held_item, tool, passed_in). + * Stage can be DO_AFTER_STARTING, DO_AFTER_PROGRESSING, DO_AFTER_FINISHING + * If it returns DO_AFTER_STOP, this breaks. + * If it returns nothing, all other checks are done. + * If it returns DO_AFTER_PROCEED, all other checks are ignored. + * passed_in is a list[PROGRESS_MULTIPLIER], for modification. + * - required_mobility_flags is checked with CHECK_ALL_MOBILITY. Will immediately fail if the user isn't a mob. + * - requried_combat_flags is checked with CHECK_MULTIPLE_BITFIELDS. Will immediately fail if the user isn't a mob. + * - mob/living/mob_redirect - advanced option: If this is specified, movement and mobility/combat flag checks will use this instead of user. Progressbars will also go to this. + * - obj/item/tool - The tool we're using. See do_after flags for details. + */ +#define INVOKE_CALLBACK cb_return = extra_checks?.Invoke(user, delay, target, timeleft, do_after_flags, required_mobility_flags, required_combat_flags, mob_redirect, stage, initially_held_item, tool, passed_in) #define CHECK_FLAG_FAILURE ((required_mobility_flags || required_combat_flags) && (!living_user || (required_mobility_flags && !CHECK_ALL_MOBILITY(living_user, required_mobility_flags)) || (required_combat_flags && !CHECK_MULTIPLE_BITFIELDS(living_user.combat_flags, required_combat_flags)))) -#define TIMELEFT (world.time - starttime) +#define TIMELEFT (timeleft) /proc/do_after_advanced(atom/user, delay, atom/target, do_after_flags, datum/callback/extra_checks, required_mobility_flags, required_combat_flags, mob/living/mob_redirect, obj/item/tool) // CHECK AND SET VARIABLES if(!user) @@ -40,8 +41,7 @@ return FALSE if(!(do_after_flags & DO_AFTER_NO_COEFFICIENT) && living_user) delay *= living_user.cached_multiplicative_actions_slowdown - var/starttime = world.time - var/endtime = world.time + delay + var/timeleft = delay var/obj/item/initially_held_item = mob_redirect?.get_active_held_item() var/atom/movable/AM_user = ismovable(user) && user var/drifting = AM_user?.Process_Spacemove(NONE) && AM_user.inertia_dir @@ -51,6 +51,7 @@ var/dy = initial_dy // DO OUR STARTING CHECKS var/cb_return + var/list/passed_in = list(1) INVOKE_CALLBACK if(cb_return == DO_AFTER_STOP) return FALSE @@ -70,13 +71,17 @@ var/locchanged var/ctu var/ctt - while(world.time < endtime) + var/tick_time = world.time + while(timeleft > 0) stoplag(1) + var/timepassed = world.time - tick_time + timepassed = world.time progbar?.update(TIMELEFT) if(QDELETED(user) || QDELETED(target) || (user.loc == null) || (target.loc == null)) . = FALSE break INVOKE_CALLBACK + timeleft -= timepassed * passed_in[1] if(cb_return == DO_AFTER_STOP) . = FALSE break diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index b9f180ac4a..745b12e84f 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -270,6 +270,20 @@ SEND_SIGNAL(A, COMSIG_ATOM_HEARER_IN_VIEW, processing, .) processing += A.contents +/proc/get_hearers_in_range(R, atom/source) + var/turf/T = get_turf(source) + . = list() + if(!T) + return + var/list/processing = range(R, source) + var/i = 0 + while(i < length(processing)) + var/atom/A = processing[++i] + if(A.flags_1 & HEAR_1) + . += A + SEND_SIGNAL(A, COMSIG_ATOM_HEARER_IN_VIEW, processing, .) + processing += A.contents + //viewers() but with a signal, for blacklisting. /proc/fov_viewers(depth = world.view, atom/center) if(!center) diff --git a/code/__HELPERS/yelling.dm b/code/__HELPERS/yelling.dm new file mode 100644 index 0000000000..4383a245d4 --- /dev/null +++ b/code/__HELPERS/yelling.dm @@ -0,0 +1,150 @@ +/datum/yelling_wavefill + var/stop = FALSE + var/list/atom/collected + +/datum/yelling_wavefill/Destroy(force, ...) + stop = TRUE + collected = null // don't cut it, something else is probably using it now! + return ..() + +/datum/yelling_wavefill/proc/run_wavefill(atom/source, dist = 50) + collected = list() + do_run(source, dist) + // to_chat(world, "DEBUG: collected [english_list(collected)]") + +// blatantly copied from wave explosion code +// check explosion2.dm for what this does and how it works. +/datum/yelling_wavefill/proc/do_run(atom/source, dist) + source = get_turf(source) + var/list/edges = list() + edges[source] = (NORTH|SOUTH|EAST|WEST) + collected += typecache_filter_list(source.contents, GLOB.typecache_living) + var/list/powers = list() + powers[source] = dist + var/list/processed = list() + var/turf/T + var/turf/expanding + var/power + var/dir + var/returned +#define RUN_YELL(_T, _P, _D) \ + returned = max(_P - max(_T.get_yelling_resistance(_P), 0) - 1, 0); \ + processed[_T] = returned; + // _T.maptext = "[returned]"; + + var/list/turf/edges_next + var/list/turf/powers_next + var/list/turf/powers_returned + var/list/turf/diagonals + var/list/turf/diagonal_powers + var/list/turf/diagonal_powers_max + var/safety = 1000 + +#define CALCULATE_DIAGONAL_POWER(existing, adding, maximum) min(maximum, existing + adding) +#define CALCULATE_DIAGONAL_CROSS_POWER(existing, adding) max(existing, adding) +#define CARDINAL_MARK(ndir, cdir, edir) \ + if(edir & cdir) { \ + expanding = get_step(T,ndir); \ + if(expanding && (isnull(processed[expanding]) || (processed[expanding] < (power - 3)))) { \ + powers_next[expanding] = max(powers_next[expanding], returned); \ + edges_next[expanding] = (cdir | edges_next[expanding]); \ + }; \ + }; + +#define DIAGONAL_SUBSTEP(ndir, cdir, edir) \ + expanding = get_step(T,ndir); \ + if(expanding && (isnull(processed[expanding]) || (processed[expanding] < (power - 3)))) { \ + if(!edges_next[expanding]) { \ + diagonal_powers_max[expanding] = max(diagonal_powers_max[expanding], returned, powers[T]); \ + diagonal_powers[expanding] = CALCULATE_DIAGONAL_POWER(diagonal_powers[expanding], returned, diagonal_powers_max[expanding]); \ + diagonals[expanding] = (cdir | diagonals[expanding]); \ + }; \ + else { \ + powers_next[expanding] = CALCULATE_DIAGONAL_CROSS_POWER(powers_next[expanding], returned); \ + }; \ + }; + +#define DIAGONAL_MARK(ndir, cdir, edir) \ + if(edir & cdir) { \ + DIAGONAL_SUBSTEP(turn(ndir, 90), turn(cdir, 90), edir); \ + DIAGONAL_SUBSTEP(turn(ndir, -90), turn(cdir, -90), edir); \ + }; + + while(edges.len) + edges_next = list() + powers_next = list() + powers_returned = list() + diagonals = list() + diagonal_powers = list() + diagonal_powers_max = list() + // to_chat(world, "DEBUG: cycle start edges [english_list_assoc(edges)]") + + // process cardinals + for(var/i in edges) + T = i + power = powers[T] + dir = edges[T] + RUN_YELL(T, power, dir) + powers_returned[T] = returned + if(returned >= 1) + collected |= typecache_filter_list(T.contents, GLOB.typecache_living) + else + continue + + CARDINAL_MARK(NORTH, NORTH, dir) + CARDINAL_MARK(SOUTH, SOUTH, dir) + CARDINAL_MARK(EAST, EAST, dir) + CARDINAL_MARK(WEST, WEST, dir) + + // to_chat(world, "DEBUG: cycle mid edges_next [english_list_assoc(edges_next)]") + + // Sweep after cardinals for diagonals + for(var/i in edges) + T = i + power = powers[T] + dir = edges[T] + returned = powers_returned[T] + DIAGONAL_MARK(NORTH, NORTH, dir) + DIAGONAL_MARK(SOUTH, SOUTH, dir) + DIAGONAL_MARK(EAST, EAST, dir) + DIAGONAL_MARK(WEST, WEST, dir) + + // to_chat(world, "DEBUG: cycle mid diagonals [english_list_assoc(diagonals)]") + + // Process diagonals: + for(var/i in diagonals) + T = i + power = diagonal_powers[T] + dir = diagonals[T] + RUN_YELL(T, power, dir) + if(returned >= 1) + collected |= typecache_filter_list(T.contents, GLOB.typecache_living) + else + continue + CARDINAL_MARK(NORTH, NORTH, dir) + CARDINAL_MARK(SOUTH, SOUTH, dir) + CARDINAL_MARK(EAST, EAST, dir) + CARDINAL_MARK(WEST, WEST, dir) + + // to_chat(world, "DEBUG: cycle end edges_next [english_list_assoc(edges_next)]") + + // flush lists + edges = edges_next + powers = powers_next + + // sleep(2.5) + if(!--safety) + CRASH("Yelling ran out of safety.") + +#undef RUN_YELL +#undef DIAGONAL_SUBSTEP +#undef CALCULATE_DIAGONAL_POWER +#undef CALCULATE_DIAGONAL_CROSS_POWER +#undef DIAGONAL_MARK +#undef CARDINAL_MARK + +/proc/yelling_wavefill(atom/source, dist = 50) + var/datum/yelling_wavefill/Y = new + Y.run_wavefill(source, dist) + . = Y.collected + qdel(Y) diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 935fbce301..d24aa06806 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -288,6 +288,9 @@ return /atom/proc/ShiftClick(mob/user) + attempt_examinate(user) + +/atom/proc/attempt_examinate(mob/user) var/flags = SEND_SIGNAL(src, COMSIG_CLICK_SHIFT, user) | SEND_SIGNAL(user, COMSIG_MOB_CLICKED_SHIFT_ON, src) if(!(flags & COMPONENT_DENY_EXAMINATE) && user.client && (user.client.eye == user || user.client.eye == user.loc || flags & COMPONENT_ALLOW_EXAMINATE)) user.examinate(src) diff --git a/code/_onclick/drag_drop.dm b/code/_onclick/drag_drop.dm index 8e2e0c3fb5..43451bd056 100644 --- a/code/_onclick/drag_drop.dm +++ b/code/_onclick/drag_drop.dm @@ -94,6 +94,10 @@ mouseControlObject = control if(mob) SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_MOUSEMOVE, object, location, control, params) + // god forgive me for i have sinned - used for autoparry. currently at 5 objects. + moused_over_objects[object] = world.time + if(moused_over_objects.len > 7) + moused_over_objects.Cut(1, 2) ..() /client/MouseDrag(src_object,atom/over_object,src_location,over_location,src_control,over_control,params) @@ -115,7 +119,6 @@ if(active_mousedown_item) active_mousedown_item.onMouseDrag(src_object, over_object, src_location, over_location, params, mob) - /obj/item/proc/onMouseDrag(src_object, over_object, src_location, over_location, params, mob) return diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index faf911e55b..1618e351ec 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -257,7 +257,7 @@ if(!isnull(stagger_force)) return stagger_force /// totally not an untested, arbitrary equation. - return clamp((1.5 + (w_class/5)) * ((force_override || force) / 1.5), 0, 10 SECONDS) + return clamp((1.5 + (w_class/5)) * ((force_override || force) / 1.5), 0, 10 SECONDS) * CONFIG_GET(number/melee_stagger_factor) /obj/item/proc/do_stagger_action(mob/living/target, mob/living/user, force_override) if(!CHECK_BITFIELD(target.status_flags, CANSTAGGER)) diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm index 76a5f5060c..08989469f5 100644 --- a/code/controllers/configuration/entries/game_options.dm +++ b/code/controllers/configuration/entries/game_options.dm @@ -269,6 +269,15 @@ /mob/living/carbon/alien/humanoid/royal/queen = 2 ) +/datum/config_entry/number/sprintless_stagger_slowdown + config_entry_value = 0 + +/datum/config_entry/number/melee_stagger_factor + config_entry_value = 1 + +/datum/config_entry/number/sprintless_off_balance_slowdown + config_entry_value = 0.85 + /datum/config_entry/number/movedelay //Used for modifying movement speed for mobs. abstract_type = /datum/config_entry/number/movedelay integer = FALSE diff --git a/code/controllers/configuration/entries/stamina_combat.dm b/code/controllers/configuration/entries/stamina_combat.dm index 65580899f1..5c2c9d7ec5 100644 --- a/code/controllers/configuration/entries/stamina_combat.dm +++ b/code/controllers/configuration/entries/stamina_combat.dm @@ -12,11 +12,7 @@ /// Base regeneration per second /datum/config_entry/number/stamina_combat/base_regeneration - config_entry_value = 0.5 - -/// Combat mode regeneration per second -/datum/config_entry/number/stamina_combat/combat_regeneration - config_entry_value = 5 + config_entry_value = 3.5 /// After out_of_combat_timer elapses, additionally regenerate this percent of total stamina per second. Unaffected by combat mode. /datum/config_entry/number/stamina_combat/percent_regeneration_out_of_combat diff --git a/code/datums/components/combat_mode.dm b/code/datums/components/combat_mode.dm index 29d90fd509..ac18becb67 100644 --- a/code/datums/components/combat_mode.dm +++ b/code/datums/components/combat_mode.dm @@ -90,7 +90,6 @@ source.playsound_local(source, 'sound/misc/ui_toggle.ogg', 50, FALSE, pressure_affected = FALSE) //Sound from interbay! RegisterSignal(source, COMSIG_MOB_CLIENT_MOUSEMOVE, .proc/onMouseMove) RegisterSignal(source, COMSIG_MOVABLE_MOVED, .proc/on_move) - RegisterSignal(source, COMSIG_MOB_CLIENT_MOVE, .proc/on_client_move) if(hud_icon) hud_icon.combat_on = TRUE hud_icon.update_icon() @@ -115,7 +114,7 @@ to_chat(source, self_message) if(playsound) source.playsound_local(source, 'sound/misc/ui_toggleoff.ogg', 50, FALSE, pressure_affected = FALSE) //Slightly modified version of the toggleon sound! - UnregisterSignal(source, list(COMSIG_MOB_CLIENT_MOUSEMOVE, COMSIG_MOVABLE_MOVED, COMSIG_MOB_CLIENT_MOVE)) + UnregisterSignal(source, list(COMSIG_MOB_CLIENT_MOUSEMOVE, COMSIG_MOVABLE_MOVED)) if(hud_icon) hud_icon.combat_on = FALSE hud_icon.update_icon() @@ -128,11 +127,6 @@ if((mode_flags & COMBAT_MODE_ACTIVE) && L.client) L.setDir(lastmousedir, ismousemovement = TRUE) -/// Added movement delay if moving backward. -/datum/component/combat_mode/proc/on_client_move(mob/source, client/client, direction, n, oldloc, added_delay) - if(oldloc != n && direction == REVERSE_DIR(source.dir)) - client.move_delay += added_delay*0.5 - ///Changes the user direction to (try) match the pointer. /datum/component/combat_mode/proc/onMouseMove(mob/source, object, location, control, params) if(source.client.show_popup_menus) diff --git a/code/datums/components/identification.dm b/code/datums/components/identification.dm index f7dc211504..59b98b57ac 100644 --- a/code/datums/components/identification.dm +++ b/code/datums/components/identification.dm @@ -24,6 +24,7 @@ identification_method_flags = id_method_flags /datum/component/identification/RegisterWithParent() + RegisterSignal(parent, COMSIG_IDENTIFICATION_KNOWLEDGE_CHECK, .proc/check_knowledge) RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/on_examine) if(identification_effect_flags & ID_COMPONENT_EFFECT_NO_ACTIONS) RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/on_equip) diff --git a/code/datums/explosion2.dm b/code/datums/explosion2.dm index 75f65e4b23..9ebdc8a326 100644 --- a/code/datums/explosion2.dm +++ b/code/datums/explosion2.dm @@ -283,14 +283,11 @@ // insanity define to mark the next set of cardinals. #define CARDINAL_MARK(ndir, cdir, edir) \ if(edir & cdir) { \ - CARDINAL_MARK_NOCHECK(ndir, cdir, edir); \ - }; - -#define CARDINAL_MARK_NOCHECK(ndir, cdir, edir) \ - expanding = get_step(T,ndir); \ - if(expanding && !exploded_last[expanding] && !edges[expanding]) { \ - powers_next[expanding] = max(powers_next[expanding], returned); \ - edges_next[expanding] = (cdir | edges_next[expanding]); \ + expanding = get_step(T,ndir); \ + if(expanding && !exploded_last[expanding] && !edges[expanding]) { \ + powers_next[expanding] = max(powers_next[expanding], returned); \ + edges_next[expanding] = (cdir | edges_next[expanding]); \ + }; \ }; // insanity define to do diagonal marking as 2 substeps @@ -308,24 +305,15 @@ }; // insanity define to mark the diagonals that would otherwise be missed -#define DIAGONAL_MARK(ndir, cdir, edir) \ - if(edir & cdir) { \ - DIAGONAL_MARK_NOCHECK(ndir, cdir, edir); \ - }; - // this only works because right now, WEX_DIR_X is the same as a byond dir // and we know we're only passing in one dir at a time. // if this ever stops being the case, and explosions break when you touch this, now you know why. -#define DIAGONAL_MARK_NOCHECK(ndir, cdir, edir) \ - DIAGONAL_SUBSTEP(turn(ndir, 90), turn(cdir, 90), edir); \ - DIAGONAL_SUBSTEP(turn(ndir, -90), turn(cdir, -90), edir); - - // mark -#define MARK(ndir, cdir, edir) \ +#define DIAGONAL_MARK(ndir, cdir, edir) \ if(edir & cdir) { \ - CARDINAL_MARK_NOCHECK(ndir, cdir, edir); \ - DIAGONAL_MARK_NOCHECK(ndir, cdir, edir); \ + DIAGONAL_SUBSTEP(turn(ndir, 90), turn(cdir, 90), edir); \ + DIAGONAL_SUBSTEP(turn(ndir, -90), turn(cdir, -90), edir); \ }; + CARDINAL_MARK(NORTH, WEX_DIR_NORTH, dir) CARDINAL_MARK(SOUTH, WEX_DIR_SOUTH, dir) CARDINAL_MARK(EAST, WEX_DIR_EAST, dir) @@ -372,7 +360,9 @@ #undef WEX_ACT +#undef CALCULATE_DIAGONAL_POWER +#undef CALCULATE_DIAGONAL_CROSS_POWER + #undef DIAGONAL_SUBSTEP #undef DIAGONAL_MARK #undef CARDINAL_MARK -#undef MARK diff --git a/code/datums/martial/_martial.dm b/code/datums/martial/_martial.dm index bc48e82395..19cfc7691d 100644 --- a/code/datums/martial/_martial.dm +++ b/code/datums/martial/_martial.dm @@ -43,12 +43,8 @@ /datum/martial_art/proc/damage_roll(mob/living/carbon/human/A, mob/living/carbon/human/D) //Here we roll for our damage to be added into the damage var in the various attack procs. This is changed depending on whether we are in combat mode, lying down, or if our target is in combat mode. var/damage = rand(A.dna.species.punchdamagelow, A.dna.species.punchdamagehigh) - if(SEND_SIGNAL(D, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) - damage *= 1.2 if(!CHECK_MOBILITY(A, MOBILITY_STAND)) damage *= 0.7 - if(SEND_SIGNAL(A, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) - damage *= 0.8 return damage /datum/martial_art/proc/teach(mob/living/carbon/human/H, make_temporary = FALSE) diff --git a/code/datums/martial/sleeping_carp.dm b/code/datums/martial/sleeping_carp.dm index 9b9ea3c86e..a55e947709 100644 --- a/code/datums/martial/sleeping_carp.dm +++ b/code/datums/martial/sleeping_carp.dm @@ -144,14 +144,16 @@ parry_time_active_visual_override = 3 parry_time_spindown_visual_override = 12 parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK //can attack while - parry_time_perfect = 2.5 // first ds isn't perfect - parry_time_perfect_leeway = 1.5 + parry_time_perfect = 2.5 + parry_time_perfect_leeway = 2.5 parry_imperfect_falloff_percent = 5 parry_efficiency_to_counterattack = 100 parry_efficiency_considered_successful = 65 // VERY generous parry_efficiency_perfect = 100 parry_failed_stagger_duration = 4 SECONDS + parry_failed_cooldown_duration = 2 SECONDS parry_cooldown = 0.5 SECONDS + parry_flags = NONE /mob/living/carbon/human/UseStaminaBuffer(amount, warn = FALSE, considered_action = TRUE) amount *= physiology? physiology.stamina_buffer_mod : 1 diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index 1c49f61996..7fc7da2ef7 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -95,21 +95,31 @@ /datum/status_effect/staggered/on_creation(mob/living/new_owner, set_duration) if(isnum(set_duration)) duration = set_duration + if(!CONFIG_GET(flag/sprint_enabled)) + new_owner.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/status_effect/stagger, TRUE, CONFIG_GET(number/sprintless_stagger_slowdown)) + return ..() + +/datum/status_effect/staggered/on_remove() + owner.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/stagger) return ..() /datum/status_effect/off_balance id = "offbalance" + blocks_sprint = TRUE alert_type = null /datum/status_effect/off_balance/on_creation(mob/living/new_owner, set_duration) if(isnum(set_duration)) duration = set_duration + if(!CONFIG_GET(flag/sprint_enabled)) + new_owner.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/status_effect/off_balance, TRUE, CONFIG_GET(number/sprintless_off_balance_slowdown)) return ..() /datum/status_effect/off_balance/on_remove() var/active_item = owner.get_active_held_item() if(active_item) owner.visible_message("[owner.name] regains their grip on \the [active_item]!", "You regain your grip on \the [active_item]", null, COMBAT_MESSAGE_RANGE) + owner.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/off_balance) return ..() /obj/screen/alert/status_effect/asleep @@ -117,24 +127,14 @@ desc = "You've fallen asleep. Wait a bit and you should wake up. Unless you don't, considering how helpless you are." icon_state = "asleep" + /datum/status_effect/grouped/stasis id = "stasis" duration = -1 tick_interval = 10 var/last_dead_time -/datum/status_effect/no_combat_mode - id = "no_combat_mode" - alert_type = null - status_type = STATUS_EFFECT_REPLACE - blocks_combatmode = TRUE - -/datum/status_effect/no_combat_mode/on_creation(mob/living/new_owner, set_duration) - if(isnum(set_duration)) - duration = set_duration - . = ..() - -/datum/status_effect/no_combat_mode/robotic_emp +/datum/status_effect/robotic_emp id = "emp_no_combat_mode" /datum/status_effect/mesmerize @@ -144,13 +144,11 @@ /datum/status_effect/mesmerize/on_creation(mob/living/new_owner, set_duration) . = ..() ADD_TRAIT(owner, TRAIT_MUTE, "mesmerize") - ADD_TRAIT(owner, TRAIT_COMBAT_MODE_LOCKED, "mesmerize") owner.add_movespeed_modifier(/datum/movespeed_modifier/status_effect/mesmerize) /datum/status_effect/mesmerize/on_remove() . = ..() REMOVE_TRAIT(owner, TRAIT_MUTE, "mesmerize") - REMOVE_TRAIT(owner, TRAIT_COMBAT_MODE_LOCKED, "mesmerize") owner.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/mesmerize) /datum/status_effect/mesmerize/on_creation(mob/living/new_owner, set_duration) @@ -168,7 +166,7 @@ id = "tased" alert_type = null var/movespeed_mod = /datum/movespeed_modifier/status_effect/tased - var/stamdmg_per_ds = 0 //a 20 duration would do 20 stamdmg, disablers do 24 or something + var/stamdmg_per_ds = 1 //a 20 duration would do 20 stamdmg, disablers do 24 or something var/last_tick = 0 //fastprocess processing speed is a goddamn sham, don't trust it. /datum/status_effect/electrode/on_creation(mob/living/new_owner, set_duration) @@ -199,9 +197,9 @@ /datum/status_effect/electrode/no_combat_mode id = "tased_strong" movespeed_mod = /datum/movespeed_modifier/status_effect/tased/no_combat_mode - blocks_combatmode = TRUE stamdmg_per_ds = 1 + //OTHER DEBUFFS /datum/status_effect/his_wrath //does minor damage over time unless holding His Grace id = "his_wrath" diff --git a/code/datums/status_effects/status_effect.dm b/code/datums/status_effects/status_effect.dm index 9dbbc1c469..b38b755f28 100644 --- a/code/datums/status_effects/status_effect.dm +++ b/code/datums/status_effects/status_effect.dm @@ -11,8 +11,6 @@ var/on_remove_on_mob_delete = FALSE //if we call on_remove() when the mob is deleted var/examine_text //If defined, this text will appear when the mob is examined - to use he, she etc. use "SUBJECTPRONOUN" and replace it in the examines themselves var/alert_type = /obj/screen/alert/status_effect //the alert thrown by the status effect, contains name and description - /// If this is TRUE, the user will have combt mode forcefully disabled while this is active. - var/blocks_combatmode = FALSE /// If this is TRUE, the user will have sprint forcefully disabled while this is active. var/blocks_sprint = FALSE var/obj/screen/alert/status_effect/linked_alert = null //the alert itself, if it exists @@ -61,8 +59,6 @@ /datum/status_effect/proc/on_apply() //Called whenever the buff is applied; returning FALSE will cause it to autoremove itself. SHOULD_CALL_PARENT(TRUE) - if(blocks_combatmode) - ADD_TRAIT(owner, TRAIT_COMBAT_MODE_LOCKED, src) if(blocks_sprint) ADD_TRAIT(owner, TRAIT_SPRINT_LOCKED, src) return TRUE @@ -74,8 +70,6 @@ /datum/status_effect/proc/on_remove() //Called whenever the buff expires or is removed; do note that at the point this is called, it is out of the owner's status_effects but owner is not yet null SHOULD_CALL_PARENT(TRUE) - if(blocks_combatmode) - REMOVE_TRAIT(owner, TRAIT_COMBAT_MODE_LOCKED, src) if(blocks_sprint) REMOVE_TRAIT(owner, TRAIT_SPRINT_LOCKED, src) return TRUE @@ -83,8 +77,6 @@ /datum/status_effect/proc/be_replaced() //Called instead of on_remove when a status effect is replaced by itself or when a status effect with on_remove_on_mob_delete = FALSE has its mob deleted owner.clear_alert(id) LAZYREMOVE(owner.status_effects, src) - if(blocks_combatmode) - REMOVE_TRAIT(owner, TRAIT_COMBAT_MODE_LOCKED, src) if(blocks_sprint) REMOVE_TRAIT(owner, TRAIT_SPRINT_LOCKED, src) owner = null diff --git a/code/datums/wounds/pierce.dm b/code/datums/wounds/pierce.dm index 56c60b31d7..b26728c377 100644 --- a/code/datums/wounds/pierce.dm +++ b/code/datums/wounds/pierce.dm @@ -128,7 +128,7 @@ initial_flow = 1.5 gauzed_clot_rate = 0.8 internal_bleeding_chance = 30 - internal_bleeding_coefficient = 1.25 + internal_bleeding_coefficient = 1 threshold_minimum = 40 threshold_penalty = 15 status_effect_type = /datum/status_effect/wound/pierce/moderate @@ -142,10 +142,10 @@ occur_text = "looses a violent spray of blood, revealing a pierced wound" sound_effect = 'sound/effects/wounds/pierce2.ogg' severity = WOUND_SEVERITY_SEVERE - initial_flow = 2.25 + initial_flow = 2 gauzed_clot_rate = 0.6 internal_bleeding_chance = 60 - internal_bleeding_coefficient = 1.5 + internal_bleeding_coefficient = 1.25 threshold_minimum = 60 threshold_penalty = 25 status_effect_type = /datum/status_effect/wound/pierce/severe @@ -159,10 +159,10 @@ occur_text = "blasts apart, sending chunks of viscera flying in all directions" sound_effect = 'sound/effects/wounds/pierce3.ogg' severity = WOUND_SEVERITY_CRITICAL - initial_flow = 3 + initial_flow = 2.7 gauzed_clot_rate = 0.4 internal_bleeding_chance = 80 - internal_bleeding_coefficient = 1.75 + internal_bleeding_coefficient = 1.5 threshold_minimum = 110 threshold_penalty = 40 status_effect_type = /datum/status_effect/wound/pierce/critical diff --git a/code/datums/wounds/slash.dm b/code/datums/wounds/slash.dm index 9044835272..6a631ab8f4 100644 --- a/code/datums/wounds/slash.dm +++ b/code/datums/wounds/slash.dm @@ -253,7 +253,7 @@ occur_text = "is cut open, slowly leaking blood" sound_effect = 'sound/effects/wounds/blood1.ogg' severity = WOUND_SEVERITY_MODERATE - initial_flow = 1.5 + initial_flow = 1.25 minimum_flow = 0.375 max_per_type = 3 clot_rate = 0.12 @@ -270,8 +270,8 @@ occur_text = "is ripped open, veins spurting blood" sound_effect = 'sound/effects/wounds/blood2.ogg' severity = WOUND_SEVERITY_SEVERE - initial_flow = 2.4375 - minimum_flow = 2.0625 + initial_flow = 2 + minimum_flow = 1.75 clot_rate = 0.07 max_per_type = 4 threshold_minimum = 60 @@ -288,8 +288,8 @@ occur_text = "is torn open, spraying blood wildly" sound_effect = 'sound/effects/wounds/blood3.ogg' severity = WOUND_SEVERITY_CRITICAL - initial_flow = 3.1875 - minimum_flow = 3 + initial_flow = 2.75 + minimum_flow = 2.5 clot_rate = -0.05 // critical cuts actively get worse instead of better max_per_type = 5 threshold_minimum = 90 diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 098f1a2ea0..155a462295 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -57,7 +57,10 @@ /// Should we use tooltips, if the thing does not have the code implemented `get_tooltip_data()`, it will default to examine(src) var/tooltips = FALSE - + /// How loudly we yell + var/yell_power = 50 + /// last time we yelled + var/last_yell = 0 /atom/movable/Initialize(mapload) . = ..() diff --git a/code/game/objects/effects/decals/misc.dm b/code/game/objects/effects/decals/misc.dm index 8f46d5cc46..6b90cfd62c 100644 --- a/code/game/objects/effects/decals/misc.dm +++ b/code/game/objects/effects/decals/misc.dm @@ -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." diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 5089d26c88..cbfd5ef5a0 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -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("You have picked up an item that can be used to block and/or parry:") + // 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("
")) + 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)) diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm index 8c36703855..ac79b3b887 100644 --- a/code/game/objects/items/devices/traitordevices.dm +++ b/code/game/objects/items/devices/traitordevices.dm @@ -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, "Successfully irradiated [M].") + if(knowledge) + to_chat(user, "Successfully irradiated [M].") addtimer(CALLBACK(src, .proc/radiation_aftereffect, M, intensity), (wavelength+(intensity*4))*5) else - to_chat(user, "The radioactive microlaser is still recharging.") + if(knowledge) + to_chat(user, "The radioactive microlaser is still recharging.") /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 diff --git a/code/game/objects/items/dualsaber.dm b/code/game/objects/items/dualsaber.dm index 3d62a98ddf..eea189c3be 100644 --- a/code/game/objects/items/dualsaber.dm +++ b/code/game/objects/items/dualsaber.dm @@ -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() . = ..() diff --git a/code/game/objects/items/electrostaff.dm b/code/game/objects/items/electrostaff.dm index a1c06f8d23..70a25bc9f9 100644 --- a/code/game/objects/items/electrostaff.dm +++ b/code/game/objects/items/electrostaff.dm @@ -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) . = ..() diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/melee/energy.dm index 55546a7f9c..1be570b62b 100644 --- a/code/game/objects/items/melee/energy.dm +++ b/code/game/objects/items/melee/energy.dm @@ -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 diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index 5b243a51a3..8a9b6a5235 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -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) diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index 291fc30aa2..ed92623a37 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -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) diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index 84faf27242..9aed545509 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -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, "[src] can't be used to shield bash!") 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 diff --git a/code/game/objects/items/stunbaton.dm b/code/game/objects/items/stunbaton.dm index 7721de42ad..a733c282c6 100644 --- a/code/game/objects/items/stunbaton.dm +++ b/code/game/objects/items/stunbaton.dm @@ -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 diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index 3c077863c0..fe63e313de 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -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() diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm index 76ab086eb2..89f018f291 100644 --- a/code/game/objects/structures/beds_chairs/chair.dm +++ b/code/game/objects/structures/beds_chairs/chair.dm @@ -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( diff --git a/code/game/sound.dm b/code/game/sound.dm index 1816336bd1..3a65d47f3f 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -43,7 +43,8 @@ falloff_distance - Distance at which falloff begins. Sound is at peak volume (in */ -/proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff_exponent = SOUND_FALLOFF_EXPONENT, frequency = null, channel = 0, pressure_affected = TRUE, ignore_walls = TRUE, falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, envwet = -10000, envdry = 0) +/proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff_exponent = SOUND_FALLOFF_EXPONENT, frequency = null, channel = 0, pressure_affected = TRUE, ignore_walls = TRUE, + falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, envwet = -10000, envdry = 0, distance_multiplier = SOUND_DEFAULT_DISTANCE_MULTIPLIER, distance_multiplier_min_range = SOUND_DEFAULT_MULTIPLIER_EFFECT_RANGE) if(isarea(source)) CRASH("playsound(): source is an area") @@ -83,11 +84,11 @@ falloff_distance - Distance at which falloff begins. Sound is at peak volume (in for(var/P in listeners) var/mob/M = P if(get_dist(M, turf_source) <= maxdistance) - M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, null, envwet, envdry) + M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, get_dist(M, turf_source) <= distance_multiplier_min_range? 1 : distance_multiplier, envwet, envdry) for(var/P in SSmobs.dead_players_by_zlevel[source_z]) var/mob/M = P if(get_dist(M, turf_source) <= maxdistance) - M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, null, envwet, envdry) + M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, get_dist(M, turf_source) <= distance_multiplier_min_range? 1 : distance_multiplier, envwet, envdry) /*! playsound @@ -108,7 +109,8 @@ distance_multiplier - Can be used to multiply the distance at which the sound is */ -/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff_exponent = SOUND_FALLOFF_EXPONENT, channel = 0, pressure_affected = TRUE, sound/S, max_distance, falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, distance_multiplier = 1, envwet = -10000, envdry = 0) +/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff_exponent = SOUND_FALLOFF_EXPONENT, channel = 0, pressure_affected = TRUE, sound/S, max_distance, + falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, distance_multiplier = SOUND_DEFAULT_DISTANCE_MULTIPLIER, envwet = -10000, envdry = 0) if(!client || !can_hear()) return diff --git a/code/game/turfs/closed.dm b/code/game/turfs/closed.dm index 38f9df4925..a3ec6c6276 100644 --- a/code/game/turfs/closed.dm +++ b/code/game/turfs/closed.dm @@ -8,6 +8,10 @@ rad_insulation = RAD_MEDIUM_INSULATION wave_explosion_block = 10 wave_explosion_multiply = 0.75 + /// How much we block yelling + var/yelling_resistance = 10 + /// how much of inbound yelling to dampen + var/yelling_dampen = 0.5 /turf/closed/Initialize() . = ..() @@ -212,3 +216,6 @@ icon = 'icons/turf/walls.dmi' icon_state = "ice" canSmoothWith = list(/turf/closed/indestructible/rock/glacierrock/blue) + +/turf/closed/get_yelling_resistance(power) + return yelling_resistance + (power * yelling_dampen) diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index 1c09e00557..3ed3891fdf 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -276,6 +276,8 @@ destination_y = dest_y destination_z = dest_z +/turf/open/space/get_yelling_resistance(power) + return INFINITY // no sound through space for crying out loud /turf/open/space/transparent baseturfs = /turf/open/space/transparent/openspace diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 50d18c1b40..d949158850 100755 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -625,3 +625,16 @@ GLOBAL_LIST_EMPTY(station_turfs) . = ..() if(. != BULLET_ACT_FORCE_PIERCE) . = BULLET_ACT_TURF + +/turf/proc/get_yelling_resistance(power) + . = 0 + // don't bother checking fulltile, we don't need accuracy + var/obj/window = locate(/obj/structure/window) in src + if(!window) + window = locate(/obj/machinery/door/window) in src + if(window) + . += 4 // windows are minimally resistant + // if there's more than one someone fucked up as that shouldn't happen + var/obj/machinery/door/D = locate() in src + if(D?.density) + . += D.opacity? 29 : 19 // glass doors are slightly more resistant to screaming diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm index 3857d1206c..f4b74c5567 100644 --- a/code/modules/antagonists/changeling/powers/mutations.dm +++ b/code/modules/antagonists/changeling/powers/mutations.dm @@ -466,7 +466,7 @@ block_return[BLOCK_RETURN_BLOCK_CAPACITY] = (block_return[BLOCK_RETURN_BLOCK_CAPACITY] || 0) + remaining_uses return ..() -/obj/item/shield/changeling/active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) +/obj/item/shield/changeling/directional_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) . = ..() if(--remaining_uses < 1) if(ishuman(loc)) diff --git a/code/modules/antagonists/clockcult/clock_items/clock_weapons/ratvarian_spear.dm b/code/modules/antagonists/clockcult/clock_items/clock_weapons/ratvarian_spear.dm index aa69478217..b702b19566 100644 --- a/code/modules/antagonists/clockcult/clock_items/clock_weapons/ratvarian_spear.dm +++ b/code/modules/antagonists/clockcult/clock_items/clock_weapons/ratvarian_spear.dm @@ -90,7 +90,6 @@ parry_time_perfect = 2 parry_efficiency_perfect = 110 //Very low leeway for counterattacks... parry_efficiency_considered_successful = 0.8 - parry_efficiency_to_counterattack = 1 + parry_efficiency_to_counterattack = 110 parry_cooldown = 15 //But also very low cooldown.. parry_failed_stagger_duration = 2 SECONDS //And relatively small penalties for failing. - parry_failed_clickcd_duration = 1 SECONDS diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index 241cd6be93..a4d94e994c 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -183,3 +183,7 @@ //world.time of when the crew manifest can be accessed var/crew_manifest_delay + /// Should go in persistent round player data sometime. This tracks what items have already warned the user on pickup that they can block/parry. + var/list/block_parry_hinted = list() + /// moused over objects, currently capped at 7. this is awful, and should be replaced with a component to track it using signals for parrying at some point. + var/list/moused_over_objects = list() diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm index 86468a0269..8c8400a45d 100644 --- a/code/modules/clothing/gloves/miscellaneous.dm +++ b/code/modules/clothing/gloves/miscellaneous.dm @@ -236,10 +236,9 @@ parry_efficiency_considered_successful = 0.01 parry_efficiency_to_counterattack = INFINITY // no auto counter parry_max_attacks = INFINITY - parry_failed_cooldown_duration = 2.25 SECONDS - parry_failed_stagger_duration = 2.25 SECONDS + parry_failed_cooldown_duration = 1.5 SECONDS + parry_failed_stagger_duration = 1.5 SECONDS parry_cooldown = 0 - parry_failed_clickcd_duration = 0 /obj/item/clothing/gloves/fingerless/pugilist/mauler name = "mauler gauntlets" diff --git a/code/modules/keybindings/keybind/mob.dm b/code/modules/keybindings/keybind/mob.dm index 257ccc37e6..15911d831e 100644 --- a/code/modules/keybindings/keybind/mob.dm +++ b/code/modules/keybindings/keybind/mob.dm @@ -76,3 +76,15 @@ else user.mob.dropItemToGround(I) return TRUE + +/datum/keybinding/mob/examine_immediate + hotkey_keys = list() + classic_keys = list() + name = "examine_immediate" + full_name = "Examine (Immediate)" + description = "Immediately examine anything you're hovering your mouse over." + +/datum/keybinding/mob/examine_immediate/down(client/user) + var/atom/A = user.mouseObject + if(A) + A.attempt_examinate(user.mob) diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm index 859ea58d26..ddec9232ce 100644 --- a/code/modules/mining/equipment/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher.dm @@ -196,7 +196,6 @@ parry_imperfect_falloff_percent = 20 parry_efficiency_to_counterattack = 100 // perfect parry or you're cringe parry_failed_stagger_duration = 1.5 SECONDS // a good time to reconsider your actions... - parry_failed_clickcd_duration = 1.5 SECONDS // or your failures /obj/item/kinetic_crusher/glaive/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 you're dumb enough to go for a parry... var/turf/proj_turf = owner.loc // destabilizer bolt, ignoring cooldown diff --git a/code/modules/mining/equipment/survival_pod.dm b/code/modules/mining/equipment/survival_pod.dm index f780d889ea..80dec5bc29 100644 --- a/code/modules/mining/equipment/survival_pod.dm +++ b/code/modules/mining/equipment/survival_pod.dm @@ -307,7 +307,7 @@ /obj/item/energy_katana, /obj/item/hierophant_club, /obj/item/his_grace, - /obj/item/gun/ballistic/minigun, + /obj/item/gun/energy/minigun, /obj/item/gun/ballistic/automatic/l6_saw, /obj/item/gun/magic/staff/chaos, /obj/item/gun/magic/staff/spellblade, diff --git a/code/modules/mob/living/carbon/alien/status_procs.dm b/code/modules/mob/living/carbon/alien/status_procs.dm index 71d61cab25..e20b20ef3d 100644 --- a/code/modules/mob/living/carbon/alien/status_procs.dm +++ b/code/modules/mob/living/carbon/alien/status_procs.dm @@ -2,7 +2,7 @@ //The effects include: stun, knockdown, unconscious, sleeping, resting, jitteriness, dizziness, ear damage, // eye damage, eye_blind, eye_blurry, druggy, TRAIT_BLIND trait, and TRAIT_NEARSIGHT trait. -/mob/living/carbon/alien/DefaultCombatKnockdown(amount, updating = TRUE, ignore_canknockdown = FALSE, override_hardstun, override_stamdmg) +/mob/living/carbon/alien/DefaultCombatKnockdown(amount, updating = TRUE, ignore_canknockdown = FALSE, override_hardstun, override_stamdmg, knocktofloor) return //no /////////////////////////////////// STUN //////////////////////////////////// diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index bfa57269a6..e4d6f5253e 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -358,10 +358,11 @@ return I.item_flags |= BEING_REMOVED breakouttime = I.breakouttime + var/datum/cuffbreak_checker/cuffbreak_checker = new(get_turf(src)) if(!cuff_break) visible_message("[src] attempts to remove [I]!") to_chat(src, "You attempt to remove [I]... (This will take around [DisplayTimeText(breakouttime)] and you need to stand still.)") - if(do_after(src, breakouttime, 0, target = src, required_mobility_flags = MOBILITY_RESIST)) + if(do_after_advanced(src, breakouttime, src, NONE, CALLBACK(cuffbreak_checker, /datum/cuffbreak_checker.proc/check_movement), required_mobility_flags = MOBILITY_RESIST)) clear_cuffs(I, cuff_break) else to_chat(src, "You fail to remove [I]!") @@ -370,15 +371,30 @@ breakouttime = 50 visible_message("[src] is trying to break [I]!") to_chat(src, "You attempt to break [I]... (This will take around 5 seconds and you need to stand still.)") - if(do_after(src, breakouttime, 0, target = src, required_mobility_flags = MOBILITY_RESIST)) + if(do_after_advanced(src, breakouttime, src, NONE, CALLBACK(cuffbreak_checker, /datum/cuffbreak_checker.proc/check_movement), required_mobility_flags = MOBILITY_RESIST)) clear_cuffs(I, cuff_break) else to_chat(src, "You fail to break [I]!") else if(cuff_break == INSTANT_CUFFBREAK) clear_cuffs(I, cuff_break) + + QDEL_NULL(cuffbreak_checker) I.item_flags &= ~BEING_REMOVED +/datum/cuffbreak_checker + var/turf/last + +/datum/cuffbreak_checker/New(turf/initial_turf) + last = initial_turf + +/datum/cuffbreak_checker/proc/check_movement(atom/user, delay, atom/target, time_left, do_after_flags, required_mobility_flags, required_combat_flags, mob_redirect, stage, initially_held_item, tool, list/passed_in) + if(get_turf(user) != last) + last = get_turf(user) + passed_in[1] = 0.5 + else + passed_in[1] = 1 + /mob/living/carbon/proc/uncuff() if (handcuffed) var/obj/item/W = handcuffed @@ -459,10 +475,6 @@ if(HAS_TRAIT(src, TRAIT_CLUMSY)) modifier -= 40 //Clumsy people are more likely to hit themselves -Honk! - //CIT CHANGES START HERE - else if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) - modifier -= 50 - if(modifier < 100) dropItemToGround(I) //END OF CIT CHANGES diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index ce311039fd..a0088ead04 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1055,10 +1055,10 @@ return if(!HAS_TRAIT(src, TRAIT_IGNOREDAMAGESLOWDOWN)) //if we want to ignore slowdown from damage, but not from equipment var/scaling = maxHealth / 100 - var/health_deficiency = ((maxHealth / scaling) - (health / scaling) + (getStaminaLoss()*0.75))//CIT CHANGE - reduces the impact of staminaloss and makes stamina buffer influence it + var/health_deficiency = max(((maxHealth / scaling) - (health / scaling)), (getStaminaLoss()*0.75)) if(health_deficiency >= 40) - add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown, TRUE, (health_deficiency-39) / 75) - add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying, TRUE, (health_deficiency-39) / 25) + add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown, TRUE, (health_deficiency - 15) / 75) + add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying, TRUE, (health_deficiency - 15) / 25) else remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown) remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index a74afc10f5..1174fcba70 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -409,7 +409,7 @@ return var/informed = FALSE if(isrobotic(src)) - apply_status_effect(/datum/status_effect/no_combat_mode/robotic_emp, severity / 20) + apply_status_effect(/datum/status_effect/robotic_emp, severity / 20) severity *= 0.5 var/do_not_stun = FALSE if(HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM)) diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 102f3df65d..6f1fe57df3 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -143,7 +143,7 @@ ) parry_efficiency_considered_successful = 0.01 - parry_efficiency_to_counterattack = 0.01 + parry_efficiency_to_counterattack = INFINITY // no counterattacks parry_max_attacks = INFINITY parry_failed_cooldown_duration = 1.5 SECONDS parry_failed_stagger_duration = 1 SECONDS diff --git a/code/modules/mob/living/carbon/human/innate_abilities/blobform.dm b/code/modules/mob/living/carbon/human/innate_abilities/blobform.dm index f2ffaec84c..c8f870bf2a 100644 --- a/code/modules/mob/living/carbon/human/innate_abilities/blobform.dm +++ b/code/modules/mob/living/carbon/human/innate_abilities/blobform.dm @@ -56,7 +56,6 @@ ADD_TRAIT(H, TRAIT_MOBILITY_NOPICKUP, SLIMEPUDDLE_TRAIT) ADD_TRAIT(H, TRAIT_MOBILITY_NOUSE, SLIMEPUDDLE_TRAIT) ADD_TRAIT(H, TRAIT_SPRINT_LOCKED, SLIMEPUDDLE_TRAIT) - ADD_TRAIT(H, TRAIT_COMBAT_MODE_LOCKED, SLIMEPUDDLE_TRAIT) ADD_TRAIT(H, TRAIT_MOBILITY_NOREST, SLIMEPUDDLE_TRAIT) ADD_TRAIT(H, TRAIT_ARMOR_BROKEN, SLIMEPUDDLE_TRAIT) H.update_disabled_bodyparts(silent = TRUE) //silently update arms to be paralysed @@ -99,7 +98,6 @@ REMOVE_TRAIT(H, TRAIT_MOBILITY_NOPICKUP, SLIMEPUDDLE_TRAIT) REMOVE_TRAIT(H, TRAIT_MOBILITY_NOUSE, SLIMEPUDDLE_TRAIT) REMOVE_TRAIT(H, TRAIT_SPRINT_LOCKED, SLIMEPUDDLE_TRAIT) - REMOVE_TRAIT(H, TRAIT_COMBAT_MODE_LOCKED, SLIMEPUDDLE_TRAIT) REMOVE_TRAIT(H, TRAIT_MOBILITY_NOREST, SLIMEPUDDLE_TRAIT) REMOVE_TRAIT(H, TRAIT_ARMOR_BROKEN, SLIMEPUDDLE_TRAIT) REMOVE_TRAIT(H, TRAIT_HUMAN_NO_RENDER, SLIMEPUDDLE_TRAIT) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 96a44792a8..3eb7771025 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1589,12 +1589,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/punchedbrute = target.getBruteLoss() //CITADEL CHANGES - makes resting and disabled combat mode reduce punch damage, makes being out of combat mode result in you taking more damage - if(!SEND_SIGNAL(target, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) - damage *= 1.2 if(!CHECK_MOBILITY(user, MOBILITY_STAND)) damage *= 0.65 - if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) - damage *= 0.8 //END OF CITADEL CHANGES var/obj/item/bodypart/affecting = target.get_bodypart(ran_zone(user.zone_selected)) @@ -1718,12 +1714,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) else user.do_attack_animation(target, ATTACK_EFFECT_DISARM) - if(HAS_TRAIT(user, TRAIT_PUGILIST))//CITADEL CHANGE - makes disarmspam cause staminaloss, pugilists can do it almost effortlessly - if(!user.UseStaminaBuffer(1, warn = TRUE)) - return - else - if(!user.UseStaminaBuffer(1, warn = TRUE)) - return + if(!user.UseStaminaBuffer(1, warn = TRUE)) + return if(attacker_style && attacker_style.disarm_act(user,target)) return TRUE @@ -1741,12 +1733,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) log_combat(user, target, "disarmed out of grab from") return var/randn = rand(1, 100) - if(SEND_SIGNAL(target, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) // CITADEL CHANGE - randn += -10 //CITADEL CHANGE - being out of combat mode makes it easier for you to get disarmed if(!CHECK_MOBILITY(user, MOBILITY_STAND)) //CITADEL CHANGE randn += 100 //CITADEL CHANGE - No kosher disarming if you're resting - if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) //CITADEL CHANGE - randn += 25 //CITADEL CHANGE - Makes it harder to disarm outside of combat mode if(user.pulling == target) randn -= 20 //If you have the time to get someone in a grab, you should have a greater chance at snatching the thing in their hand. Will be made completely obsolete by the grab rework but i've got a poor track record for releasing big projects on time so w/e i guess if(HAS_TRAIT(user, TRAIT_PUGILIST)) @@ -1951,9 +1939,6 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) if(IS_STAMCRIT(user)) to_chat(user, "You're too exhausted for that.") return - if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) - to_chat(user, "You need combat mode to be active to that!") - return if(user.IsKnockdown() || user.IsParalyzed() || user.IsStun()) to_chat(user, "You can't seem to force yourself up right now!") return @@ -1993,6 +1978,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) if(CHECK_MOBILITY(target, MOBILITY_STAND)) target.adjustStaminaLoss(5) + else + target.adjustStaminaLoss(target.getStaminaLoss() > 75? 5 : 75) if(target.is_shove_knockdown_blocked()) return @@ -2035,7 +2022,6 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) target.visible_message("[user.name] shoves [target.name]!", "[user.name] shoves you!", null, COMBAT_MESSAGE_RANGE, null, user, "You shove [target.name]!") - target.Stagger(SHOVE_STAGGER_DURATION) var/obj/item/target_held_item = target.get_active_held_item() if(!target.has_status_effect(STATUS_EFFECT_OFF_BALANCE)) if(target_held_item) diff --git a/code/modules/mob/living/carbon/human/status_procs.dm b/code/modules/mob/living/carbon/human/status_procs.dm index 7892380d8b..ec961da4dd 100644 --- a/code/modules/mob/living/carbon/human/status_procs.dm +++ b/code/modules/mob/living/carbon/human/status_procs.dm @@ -3,7 +3,7 @@ amount = dna.species.spec_stun(src,amount) return ..() -/mob/living/carbon/human/DefaultCombatKnockdown(amount, updating = TRUE, ignore_canknockdown = FALSE, override_hardstun, override_stamdmg) +/mob/living/carbon/human/DefaultCombatKnockdown(amount, updating = TRUE, ignore_canknockdown = FALSE, override_hardstun, override_stamdmg, knocktofloor) amount = dna.species.spec_stun(src,amount) return ..() diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index d70196a78c..0b12f3884e 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -512,9 +512,8 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put //this updates all special effects: stun, sleeping, knockdown, druggy, stuttering, etc.. /mob/living/carbon/handle_status_effects() ..() - var/combat_mode = SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE) if(getStaminaLoss() && !HAS_TRAIT(src, TRAIT_NO_STAMINA_REGENERATION)) - adjustStaminaLoss((!CHECK_MOBILITY(src, MOBILITY_STAND) ? ((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) ? STAM_RECOVERY_STAM_CRIT : STAM_RECOVERY_RESTING) : STAM_RECOVERY_NORMAL) * (combat_mode? 0.25 : 1)) + adjustStaminaLoss((!CHECK_MOBILITY(src, MOBILITY_STAND) ? ((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) ? STAM_RECOVERY_STAM_CRIT : STAM_RECOVERY_RESTING) : STAM_RECOVERY_NORMAL)) if(!(combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && incomingstammult != 1) incomingstammult = max(0.01, incomingstammult) diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index db472df388..9ccaeb6c59 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -48,25 +48,23 @@ animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN) /mob/living/proc/continue_starting_active_block() - if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) - return DO_AFTER_STOP return (combat_flags & COMBAT_FLAG_ACTIVE_BLOCK_STARTING)? DO_AFTER_CONTINUE : DO_AFTER_STOP /mob/living/get_standard_pixel_x_offset() . = ..() if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING)) if(dir & EAST) - . += 8 + . += 4 if(dir & WEST) - . -= 8 + . -= 4 /mob/living/get_standard_pixel_y_offset() . = ..() if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING)) if(dir & NORTH) - . += 8 + . += 4 if(dir & SOUTH) - . -= 8 + . -= 4 /** * Proc called by keybindings to toggle active blocking. @@ -100,11 +98,6 @@ if(!I.can_active_block()) to_chat(src, "[I] is either not capable of being used to actively block, or is not currently in a state that can! (Try wielding it if it's twohanded, for example.)") return - // QOL: Attempt to toggle on combat mode if it isn't already - SEND_SIGNAL(src, COMSIG_ENABLE_COMBAT_MODE) - if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) - to_chat(src, "You must be in combat mode to actively block!") - return FALSE var/datum/block_parry_data/data = I.get_block_parry_data() var/delay = data.block_start_delay combat_flags |= COMBAT_FLAG_ACTIVE_BLOCK_STARTING @@ -147,7 +140,7 @@ /** * Calculates FINAL ATTACK DAMAGE after mitigation */ -/obj/item/proc/active_block_calculate_final_damage(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) +/obj/item/proc/active_block_calculate_final_damage(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, passive = FALSE) var/datum/block_parry_data/data = get_block_parry_data() var/absorption = data.attack_type_list_scan(data.block_damage_absorption_override, attack_type) var/efficiency = data.attack_type_list_scan(data.block_damage_multiplier_override, attack_type) @@ -156,7 +149,7 @@ if(isnull(absorption)) absorption = data.block_damage_absorption if(isnull(efficiency)) - efficiency = data.block_damage_multiplier + efficiency = data.block_damage_multiplier * (passive? (1 / data.block_automatic_mitigation_multiplier) : 1) if(isnull(limit)) limit = data.block_damage_limit // now we calculate damage to reduce. @@ -172,7 +165,7 @@ return final_damage /// Amount of stamina from damage blocked. Note that the damage argument is damage_blocked. -/obj/item/proc/active_block_stamina_cost(mob/living/owner, atom/object, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) +/obj/item/proc/active_block_stamina_cost(mob/living/owner, atom/object, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, passive = FALSE) var/datum/block_parry_data/data = get_block_parry_data() var/efficiency = data.attack_type_list_scan(data.block_stamina_efficiency_override, attack_type) if(isnull(efficiency)) @@ -182,7 +175,7 @@ multiplier = data.attack_type_list_scan(data.block_resting_stamina_penalty_multiplier_override, attack_type) if(isnull(multiplier)) multiplier = data.block_resting_stamina_penalty_multiplier - return (damage_blocked / efficiency) * multiplier + return (damage_blocked / efficiency) * multiplier * (passive? data.block_automatic_stamina_multiplier : 1) /// Apply the stamina damage to our user, notice how damage argument is stamina_amount. /obj/item/proc/active_block_do_stamina_damage(mob/living/owner, atom/object, stamina_amount, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) @@ -214,6 +207,18 @@ return /obj/item/proc/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) + return directional_block(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return, override_direction) + +/obj/item/proc/can_passive_block() + if(!block_parry_data || !(item_flags & ITEM_CAN_BLOCK)) + return FALSE + var/datum/block_parry_data/data = return_block_parry_datum(block_parry_data) + return data.block_automatic_enabled + +/obj/item/proc/passive_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) + return directional_block(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return, override_direction, TRUE) + +/obj/item/proc/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, passive = FALSE) if(!can_active_block()) return BLOCK_NONE var/datum/block_parry_data/data = get_block_parry_data() @@ -228,12 +233,12 @@ incoming_direction = get_dir(get_turf(attacker) || get_turf(object), src) if(!CHECK_MOBILITY(owner, MOBILITY_STAND) && !(data.block_resting_attack_types_anydir & attack_type) && (!(data.block_resting_attack_types_directional & attack_type) || !can_block_direction(owner.dir, incoming_direction))) return BLOCK_NONE - else if(!can_block_direction(owner.dir, incoming_direction)) + else if(!can_block_direction(owner.dir, incoming_direction, passive)) return BLOCK_NONE block_return[BLOCK_RETURN_ACTIVE_BLOCK] = TRUE - var/final_damage = active_block_calculate_final_damage(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + var/final_damage = active_block_calculate_final_damage(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return, passive) var/damage_blocked = damage - final_damage - var/stamina_cost = active_block_stamina_cost(owner, object, damage_blocked, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + var/stamina_cost = active_block_stamina_cost(owner, object, damage_blocked, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return, passive) active_block_do_stamina_damage(owner, object, stamina_cost, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) block_return[BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATED] = damage - final_damage block_return[BLOCK_RETURN_SET_DAMAGE_TO] = final_damage @@ -261,9 +266,9 @@ /** * Gets the block direction bitflags of what we can block. */ -/obj/item/proc/blockable_directions() +/obj/item/proc/blockable_directions(passive = FALSE) var/datum/block_parry_data/data = get_block_parry_data() - return data.can_block_directions + return (!isnull(data.block_automatic_directions) && passive)? data.block_automatic_directions : data.can_block_directions /** * Checks if we can block from a specific direction from our direction. @@ -272,14 +277,14 @@ * * our_dir - our direction. * * their_dir - their direction. Must be a single direction, or NONE for an attack from the same tile. This is incoming direction. */ -/obj/item/proc/can_block_direction(our_dir, their_dir) +/obj/item/proc/can_block_direction(our_dir, their_dir, passive = FALSE) their_dir = turn(their_dir, 180) if(our_dir != NORTH) var/turn_angle = dir2angle(our_dir) // dir2angle(), ss13 proc is clockwise so dir2angle(EAST) == 90 // turn(), byond proc is counterclockwise so turn(NORTH, 90) == WEST their_dir = turn(their_dir, turn_angle) - return (DIR2BLOCKDIR(their_dir) & blockable_directions()) + return (DIR2BLOCKDIR(their_dir) & blockable_directions(passive)) /** * can_block_direction but for "compound" directions to check all of them and return the number of directions that were blocked. diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 10f8aaf2f4..e34c5ce053 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -14,18 +14,58 @@ /** * Initiates a parrying sequence. */ -/mob/living/proc/initiate_parry_sequence() +/mob/living/proc/initiate_parry_sequence(silent = FALSE, list/override_method_data) if(parrying) - return // already parrying + return FALSE // already parrying if(!(mobility_flags & MOBILITY_USE)) - to_chat(src, "You can't move your arms!") - return + if(!silent) + to_chat(src, "You can't move your arms!") + return FALSE if(!(combat_flags & COMBAT_FLAG_PARRY_CAPABLE)) - to_chat(src, "You are not something that can parry attacks.") - return + if(!silent) + to_chat(src, "You are not something that can parry attacks.") + return FALSE if(!(mobility_flags & MOBILITY_STAND)) - to_chat(src, "You aren't able to parry without solid footing!") - return + if(!silent) + to_chat(src, "You aren't able to parry without solid footing!") + return FALSE + var/datum/block_parry_data/data + var/list/determined = override_method_data || determine_parry_method(FALSE, FALSE) + if(!islist(determined)) + return FALSE + var/method = determined[1] + data = return_block_parry_datum(determined[2]) + var/datum/tool = determined[3] + var/full_parry_duration = data.parry_time_windup + data.parry_time_active + data.parry_time_spindown + // no system in place to "fallback" if out of the 3 the top priority one can't parry due to constraints but something else can. + // can always implement it later, whatever. + if((data.parry_respect_clickdelay && !CheckActionCooldown()) || ((parry_end_time_last + data.parry_cooldown) > world.time)) + if(!silent) + to_chat(src, "You are not ready to parry (again)!") + return FALSE + // Point of no return, make sure everything is set. + parrying = method + if(method == ITEM_PARRY) + active_parry_item = tool + if(!UseStaminaBuffer(data.parry_stamina_cost, TRUE)) + return FALSE + parry_start_time = world.time + successful_parries = list() + successful_parry_counterattacks = list() + addtimer(CALLBACK(src, .proc/end_parry_sequence), full_parry_duration) + if(data.parry_flags & PARRY_LOCK_ATTACKING) + ADD_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_PARRY_TRAIT) + if(data.parry_flags & PARRY_LOCK_SPRINTING) + ADD_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_PARRY_TRAIT) + handle_parry_starting_effects(data) + return TRUE + +/** + * Massive snowflake proc for getting something to parry with. + * + * @return list of [method, data, tool], where method is the parry method define, data is the block_parry_data var that must be run through return_block_parry_data, and tool is the concept/object/martial art/etc used. + */ +/mob/living/proc/determine_parry_method(silent = TRUE, autoparry = FALSE) // Prioritize item, then martial art, then unarmed. // yanderedev else if time var/obj/item/using_item = get_active_held_item() @@ -55,7 +95,8 @@ var/list/other_items = list() var/list/override = list() if(SEND_SIGNAL(src, COMSIG_LIVING_ACTIVE_PARRY_START, method, tool, other_items, override) & COMPONENT_PREVENT_PARRY_START) - to_chat(src, "Something is preventing you from parrying!") + if(!silent) + to_chat(src, "Something is preventing you from parrying!") return if(length(override)) var/datum/thing = override[1] @@ -72,35 +113,10 @@ method = ITEM_PARRY data = using_item.block_parry_data if(!method) - to_chat(src, "You have nothing to parry with!") + if(!silent) + to_chat(src, "You have nothing to parry with!") return FALSE - //QOL: Try to enable combat mode if it isn't already - SEND_SIGNAL(src, COMSIG_ENABLE_COMBAT_MODE) - if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) - to_chat(src, "You must be in combat mode to parry!") - return FALSE - data = return_block_parry_datum(data) - var/full_parry_duration = data.parry_time_windup + data.parry_time_active + data.parry_time_spindown - // no system in place to "fallback" if out of the 3 the top priority one can't parry due to constraints but something else can. - // can always implement it later, whatever. - if((data.parry_respect_clickdelay && !CheckActionCooldown()) || ((parry_end_time_last + data.parry_cooldown) > world.time)) - to_chat(src, "You are not ready to parry (again)!") - return FALSE - // Point of no return, make sure everything is set. - parrying = method - if(method == ITEM_PARRY) - active_parry_item = using_item - if(!UseStaminaBuffer(data.parry_stamina_cost, TRUE)) - return FALSE - parry_start_time = world.time - successful_parries = list() - addtimer(CALLBACK(src, .proc/end_parry_sequence), full_parry_duration) - if(data.parry_flags & PARRY_LOCK_ATTACKING) - ADD_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_PARRY_TRAIT) - if(data.parry_flags & PARRY_LOCK_SPRINTING) - ADD_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_PARRY_TRAIT) - handle_parry_starting_effects(data) - return TRUE + return list(method, data, tool) /** * Tries to find a backup parry item. @@ -146,6 +162,7 @@ parry_start_time = 0 parry_end_time_last = world.time + (successful? 0 : data.parry_failed_cooldown_duration) successful_parries = null + successful_parry_counterattacks = null /** * Handles starting effects for parrying. @@ -178,17 +195,17 @@ /** * Called when an attack is parried using this, whether or not the parry was successful. */ -/obj/item/proc/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) +/obj/item/proc/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, autoparry = FALSE) /** * Called when an attack is parried innately, whether or not the parry was successful. */ -/mob/living/proc/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) +/mob/living/proc/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, autoparry = FALSE) /** * Called when an attack is parried using this, whether or not the parry was successful. */ -/datum/martial_art/proc/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) +/datum/martial_art/proc/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, autoparry = FALSE) /** * Called when an attack is parried and block_parra_data indicates to use a proc to handle counterattack. @@ -205,6 +222,94 @@ */ /datum/martial_art/proc/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) +/** + * Attempts to automatically parry an attacker. + */ +/mob/living/proc/attempt_auto_parry(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list()) + // determine how we'll parry + var/list/determined = determine_parry_method(TRUE, TRUE) + if(!islist(determined)) + return BLOCK_NONE + var/datum/block_parry_data/data = return_block_parry_datum(determined[2]) + if(!data.parry_automatic_enabled || (last_autoparry > (world.time - data.autoparry_cooldown_absolute))) + return BLOCK_NONE + if(attack_type && !(attack_type & data.parry_attack_types)) + return BLOCK_NONE + // before doing anything, check if the user moused over them properly + if(!client) + return BLOCK_NONE + var/found = attacker == client.mouseObject + if(!found) + for(var/i in client.moused_over_objects) + if(i == object) + if((client.moused_over_objects[i] + (data.autoparry_mouse_delay_maximum)) >= world.time) + found = TRUE + break + if(!found) + return BLOCK_NONE + + // if that works, try to start parry + // first, check cooldowns + + // now, depending on if we're doing a single simulation or a full sequence + last_autoparry = world.time + if(data.autoparry_sequence_simulation) + // for full sequence simulation + initiate_parry_sequence(TRUE, determined) + if(data.autoparry_sequence_start_time == -1) + parry_start_time = world.time - data.parry_time_windup + else + parry_start_time = world.time - data.autoparry_sequence_start_time + // recurse back to original + return run_parry(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, FALSE) + else + // yes, this is mostly a copypaste of run_parry. + var/efficiency = data.attack_type_list_scan(data.autoparry_single_efficiency_override, attack_type) + if(isnull(efficiency)) + efficiency = data.autoparry_single_efficiency + var/method = determined[1] + switch(method) + if(ITEM_PARRY) + var/obj/item/I = determined[3] + . = I.on_active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, null, TRUE) + if(UNARMED_PARRY) + . = on_active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, null, TRUE) + if(MARTIAL_PARRY) + . = mind.martial_art.on_active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, null, TRUE) + if(!isnull(return_list[BLOCK_RETURN_OVERRIDE_PARRY_EFFICIENCY])) // one of our procs overrode + efficiency = return_list[BLOCK_RETURN_OVERRIDE_PARRY_EFFICIENCY] + if(efficiency <= 0) // Do not allow automatically handled/standardized parries that increase damage for now. + return + . |= BLOCK_SHOULD_PARTIAL_MITIGATE + if(efficiency >= data.parry_efficiency_perfect) + . |= data.perfect_parry_block_return_flags + if(data.perfect_parry_block_return_list) + return_list |= data.perfect_parry_block_return_list + else if(efficiency >= data.parry_efficiency_considered_successful) + . |= data.imperfect_parry_block_return_flags + if(data.imperfect_parry_block_return_list) + return_list |= data.imperfect_parry_block_return_list + else + . |= data.failed_parry_block_return_flags + if(data.failed_parry_block_return_list) + return_list |= data.failed_parry_block_return_list + if(isnull(return_list[BLOCK_RETURN_MITIGATION_PERCENT])) // if one of the on_active_parry procs overrode. We don't have to worry about interference since parries are the first thing checked in the [do_run_block()] sequence. + return_list[BLOCK_RETURN_MITIGATION_PERCENT] = clamp(efficiency, 0, 100) // do not allow > 100% or < 0% for now. + if((return_list[BLOCK_RETURN_MITIGATION_PERCENT] >= 100) || (damage <= 0)) + . |= BLOCK_SUCCESS + var/list/effect_text + var/pacifist_counter_check = TRUE + if(HAS_TRAIT(src, TRAIT_PACIFISM)) + switch(parrying) + if(ITEM_PARRY) + pacifist_counter_check = (!active_parry_item.force || active_parry_item.damtype == STAMINA) + else + pacifist_counter_check = FALSE //Both martial and unarmed counter attacks generally are harmful, so no need to have the same line twice. + if(efficiency >= data.parry_efficiency_to_counterattack && pacifist_counter_check && !return_list[BLOCK_RETURN_FORCE_NO_PARRY_COUNTERATTACK] && (!(attacker in successful_parry_counterattacks) && !data.parry_allow_repeated_counterattacks)) + effect_text = run_parry_countereffects(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, data) + if(data.parry_flags & PARRY_DEFAULT_HANDLE_FEEDBACK) + handle_parry_feedback(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, effect_text, data) + /** * Gets the stage of our parry sequence we're currently in. */ @@ -235,12 +340,20 @@ return world.time - parry_start_time /// same return values as normal blocking, called with absolute highest priority in the block "chain". -/mob/living/proc/run_parry(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list()) +/mob/living/proc/run_parry(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), allow_auto = TRUE) var/stage = get_parry_stage() + if(attack_type & ATTACK_TYPE_PARRY_COUNTERATTACK) + return BLOCK_NONE // don't infinite loop if(stage != PARRY_ACTIVE) - return BLOCK_NONE + // If they're not currently parrying, attempt auto parry + if(stage == NOT_PARRYING) + if(!allow_auto || SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) + return BLOCK_NONE + return attempt_auto_parry(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list) + else + return BLOCK_NONE var/datum/block_parry_data/data = get_parry_data() - if(attack_type && (!(attack_type & data.parry_attack_types) || (attack_type & ATTACK_TYPE_PARRY_COUNTERATTACK))) // if this attack is from a parry do not parry it lest we infinite loop. + if(attack_type && !(attack_type & data.parry_attack_types)) return BLOCK_NONE var/efficiency = data.get_parry_efficiency(attack_type, get_parry_time()) switch(parrying) @@ -281,7 +394,7 @@ pacifist_counter_check = (!active_parry_item.force || active_parry_item.damtype == STAMINA) else pacifist_counter_check = FALSE //Both martial and unarmed counter attacks generally are harmful, so no need to have the same line twice. - if(efficiency >= data.parry_efficiency_to_counterattack && pacifist_counter_check) + if(efficiency >= data.parry_efficiency_to_counterattack && pacifist_counter_check && !return_list[BLOCK_RETURN_FORCE_NO_PARRY_COUNTERATTACK] && (!(attacker in successful_parry_counterattacks) && !data.parry_allow_repeated_counterattacks)) effect_text = run_parry_countereffects(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency) if(data.parry_flags & PARRY_DEFAULT_HANDLE_FEEDBACK) handle_parry_feedback(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, effect_text) @@ -289,8 +402,9 @@ if(length(successful_parries) >= data.parry_max_attacks) end_parry_sequence() -/mob/living/proc/handle_parry_feedback(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), parry_efficiency, list/effect_text) - var/datum/block_parry_data/data = get_parry_data() +/mob/living/proc/handle_parry_feedback(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), parry_efficiency, list/effect_text, datum/block_parry_data/data) + if(!data) + data = get_parry_data() var/knockdown_check = FALSE if(data.parry_data[PARRY_KNOCKDOWN_ATTACKER] && parry_efficiency >= data.parry_efficiency_to_counterattack) knockdown_check = TRUE @@ -299,12 +413,14 @@ visible_message("[src] parries [attack_text][length(effect_text)? ", [english_list(effect_text)] [attacker]" : ""][length(effect_text) && knockdown_check? " and" : ""][knockdown_check? " knocking them to the ground" : ""]!") /// Run counterattack if any -/mob/living/proc/run_parry_countereffects(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), parry_efficiency) +/mob/living/proc/run_parry_countereffects(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), parry_efficiency, datum/block_parry_data/data) if(!isliving(attacker)) return var/mob/living/L = attacker - var/datum/block_parry_data/data = get_parry_data() + if(!data) + data = get_parry_data() var/list/effect_text = list() + successful_parry_counterattacks |= attacker // Always proc so items can override behavior easily switch(parrying) if(ITEM_PARRY) diff --git a/code/modules/mob/living/living_block.dm b/code/modules/mob/living/living_block.dm index d32265e478..99e74916da 100644 --- a/code/modules/mob/living/living_block.dm +++ b/code/modules/mob/living/living_block.dm @@ -37,6 +37,8 @@ var/results if(I == active_block_item) results = I.active_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list, attack_direction) + else if(I.can_passive_block() && !SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) + results = I.passive_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list, attack_direction) else results = I.run_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) . |= results diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 9e974177e5..0ccc03982a 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -75,7 +75,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/block_stamina_buffer_ratio = 1 /// Stamina dealt directly via UseStaminaBuffer() per SECOND of block. - var/block_stamina_cost_per_second = 1.5 + var/block_stamina_cost_per_second = 1 /// Prevent stamina buffer regeneration while block? var/block_no_stambuffer_regeneration = TRUE /// Prevent stamina regeneration while block? @@ -93,20 +93,31 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Sounds for blocking var/list/block_sounds = list('sound/block_parry/block_metal1.ogg' = 1, 'sound/block_parry/block_metal1.ogg' = 1) + // Autoblock + // Other than for overrides, this mostly just reads from the above vars + /// Can this item automatically block? + var/block_automatic_enabled = TRUE + /// Directions that you can autoblock in. Null to default to normal directions. + var/block_automatic_directions = null + /// Effectiveness multiplier for automated block. Only applies to efficiency, absorption and limits stay the same! + var/block_automatic_mitigation_multiplier = 0.33 + /// Stamina cost multiplier for automated block + var/block_automatic_stamina_multiplier = 1 + /////////// PARRYING //////////// - /// Prioriry for [mob/do_run_block()] while we're being used to parry. + /// Priority for [mob/do_run_block()] while we're being used to parry. // None - Parry is always highest priority! /// Parry doesn't work if you aren't able to otherwise attack due to clickdelay - var/parry_respect_clickdelay = TRUE + var/parry_respect_clickdelay = FALSE /// Parry stamina cost var/parry_stamina_cost = 5 /// Attack types we can block var/parry_attack_types = ALL /// Parry flags - var/parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK | PARRY_LOCK_ATTACKING + var/parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK /// Parry windup duration in deciseconds. 0 to this is windup, afterwards is main stage. - var/parry_time_windup = 2 + var/parry_time_windup = 0 /// Parry spindown duration in deciseconds. main stage end to this is the spindown stage, afterwards the parry fully ends. var/parry_time_spindown = 3 /// Main parry window in deciseconds. This is between [parry_time_windup] and [parry_time_spindown] @@ -139,7 +150,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Efficiency must be at least this to be considered successful var/parry_efficiency_considered_successful = 0.1 /// Efficiency must be at least this to run automatic counterattack - var/parry_efficiency_to_counterattack = 0.1 + var/parry_efficiency_to_counterattack = INFINITY /// Maximum attacks to parry successfully or unsuccessfully (but not efficiency < 0) during active period, hitting this immediately ends the sequence. var/parry_max_attacks = INFINITY /// Visual icon state override for parrying @@ -153,7 +164,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) /// Stagger duration post-parry if you fail to parry an attack var/parry_failed_stagger_duration = 3.5 SECONDS /// Clickdelay duration post-parry if you fail to parry an attack - var/parry_failed_clickcd_duration = 2 SECONDS + var/parry_failed_clickcd_duration = 0 SECONDS /// Parry cooldown post-parry if failed. This is ADDED to parry_cooldown!!! var/parry_failed_cooldown_duration = 0 SECONDS @@ -166,6 +177,29 @@ GLOBAL_LIST_EMPTY(block_parry_data) var/perfect_parry_block_return_list var/imperfect_parry_block_return_list var/failed_parry_block_return_list + /// Allow multiple counterattacks per parry sequence. Bad idea. + var/parry_allow_repeated_counterattacks = FALSE + + // Auto parry + // Anything not specified like cooldowns/clickdelay respecting is pulled from above. + /// Can this data automatically parry? This is off by default because this is something that requires thought to balance. + var/parry_automatic_enabled = FALSE + /// Hard autoparry cooldown + var/autoparry_cooldown_absolute = 7.5 SECONDS + /// Autoparry : Simulate a parry sequence starting at a certain tick, or simply simulate a single attack parry? + var/autoparry_sequence_simulation = FALSE + // Single attack simulation: + /// Single attack autoparry - efficiency + var/autoparry_single_efficiency = 75 + /// Single attack autoparry - efficiency overrides by attack type, see above + var/list/autoparry_single_efficiency_override + // Parry sequence simulation: + /// Decisecond of sequence to start on. -1 to start to 0th tick of active parry window. + var/autoparry_sequence_start_time = -1 + // Clickdelay/cooldown settings not included, as well as whether or not to lock attack/sprinting/etc. They will be pulled from the above. + + /// ADVANCED - Autoparry requirement for time since last moused over for a specific object + var/autoparry_mouse_delay_maximum = 0.35 SECONDS /** * Quirky proc to get average of flags in list that are in attack_type because why is attack_type a flag. @@ -308,6 +342,7 @@ GLOBAL_LIST_EMPTY(block_parry_data) RENDER_VARIABLE_SIMPLE(parry_cooldown, "Deciseconds it has to be since the last time a parry sequence ended for you before you can parry again.") RENDER_VARIABLE_SIMPLE(parry_failed_stagger_duration, "Deciseconds you are staggered for at the of the parry sequence if you do not successfully parry anything.") RENDER_VARIABLE_SIMPLE(parry_failed_clickcd_duration, "Deciseconds you are put on attack cooldown at the end of the parry sequence if you do not successfully parry anything.") + dat += "" dat += "" return dat.Join("") #undef RENDER_VARIABLE_SIMPLE diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index f5919f389c..3b53eec64f 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -50,8 +50,12 @@ var/obj/effect/abstract/parry/parry_visual_effect /// world.time of last parry end var/parry_end_time_last = 0 + /// Last autoparry + var/last_autoparry = 0 /// Successful parries within the current parry cycle. It's a list of efficiency percentages. var/list/successful_parries + /// Current parry counterattacks. Makes sure we can only counterattack someone once per parry. + var/list/successful_parry_counterattacks var/confused = 0 //Makes the mob move in random directions. diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 7ca2234081..1a925e8eaf 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -267,16 +267,10 @@ GLOBAL_LIST_INIT(department_radio_keys, list( eavesdrop_range = EAVESDROP_EXTRA_RANGE var/list/listening = get_hearers_in_view(message_range+eavesdrop_range, source) var/list/the_dead = list() - var/list/yellareas //CIT CHANGE - adds the ability for yelling to penetrate walls and echo throughout areas - if(!eavesdrop_range && say_test(message) == "2") //CIT CHANGE - ditto - yellareas = get_areas_in_range(message_range*0.5, source) //CIT CHANGE - ditto + for(var/_M in GLOB.player_list) var/mob/M = _M if(M.stat != DEAD) //not dead, not important - if(yellareas) //CIT CHANGE - see above. makes yelling penetrate walls - var/area/A = get_area(M) //CIT CHANGE - ditto - if(istype(A) && A.ambientsounds != SPACE && (A in yellareas)) //CIT CHANGE - ditto - listening |= M //CIT CHANGE - ditto continue if(!M.client || !client) //client is so that ghosts don't have to listen to mice continue @@ -303,6 +297,9 @@ GLOBAL_LIST_INIT(department_radio_keys, list( AM.Hear(rendered, src, message_language, message, null, spans, message_mode, source) SEND_GLOBAL_SIGNAL(COMSIG_GLOB_LIVING_SAY_SPECIAL, src, message) + if(!eavesdrop_range && say_test(message) == "2") // Yell hook + process_yelling(listening, rendered, src, message_language, message, spans, message_mode, source) + //speech bubble var/list/speech_bubble_recipients = list() for(var/mob/M in listening) @@ -312,6 +309,30 @@ GLOBAL_LIST_INIT(department_radio_keys, list( I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, speech_bubble_recipients, 30) +/atom/movable/proc/process_yelling(list/already_heard, rendered, atom/movable/speaker, datum/language/message_language, message, list/spans, message_mode, obj/source) + if(last_yell > (world.time - 10)) + to_chat(src, "Your voice doesn't project as far as you try to yell in such quick succession.") // yeah no, no spamming an expensive floodfill. + return + last_yell = world.time + var/list/overhearing = list() + var/list/overhearing_text = list() + overhearing = yelling_wavefill(src, yell_power) + if(!overhearing.len) + overhearing_text = "none" + else + for(var/mob/M as anything in overhearing) + overhearing_text += key_name(M) + overhearing_text = english_list(overhearing_text) + log_say("YELL: [ismob(src)? key_name(src) : src] yelled [message] with overhearing mobs [overhearing_text]") + // overhearing = get_hearers_in_view(35, src) | get_hearers_in_range(5, src) + overhearing -= already_heard + if(!overhearing.len) + return + // to_chat(world, "DEBUG: overhearing [english_list(overhearing)]") + for(var/_AM in overhearing) + var/atom/movable/AM = _AM + AM.Hear(rendered, speaker, message_language, message, null, spans, message_mode, source) + /mob/proc/binarycheck() return FALSE diff --git a/code/modules/mob/living/stamina_buffer.dm b/code/modules/mob/living/stamina_buffer.dm index a410df582b..91811e048a 100644 --- a/code/modules/mob/living/stamina_buffer.dm +++ b/code/modules/mob/living/stamina_buffer.dm @@ -35,11 +35,10 @@ return CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/out_of_combat_timer, out_of_combat_timer) CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/base_regeneration, base_regeneration) - CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/combat_regeneration, combat_regeneration) CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/percent_regeneration_out_of_combat, percent_regeneration_out_of_combat) CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/post_action_penalty_delay, post_action_penalty_delay) CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/post_action_penalty_factor, post_action_penalty_factor) - var/base_regen = (SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))? base_regeneration : combat_regeneration + var/base_regen = base_regeneration var/time_since_last_action = world.time - stamina_buffer_last_use var/action_penalty = ((time_since_last_action) < (post_action_penalty_delay * 10))? post_action_penalty_factor : 1 var/out_of_combat_bonus = (time_since_last_action < (out_of_combat_timer * 10))? 0 : ((buffer_max * percent_regeneration_out_of_combat * 0.01)) diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index 87fd0cf609..389f5ee5b5 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -4,7 +4,8 @@ // ignore_castun = same logic as Paralyze() in general // override_duration = If this is set, does Paralyze() for this duration. // override_stam = If this is set, does this amount of stamina damage. -/mob/living/proc/DefaultCombatKnockdown(amount, updating = TRUE, ignore_canknockdown = FALSE, override_hardstun, override_stamdmg) +// knocktofloor - whether to knock them to the ground +/mob/living/proc/DefaultCombatKnockdown(amount, updating = TRUE, ignore_canknockdown = FALSE, override_hardstun, override_stamdmg, knocktofloor = TRUE) if(!iscarbon(src)) return Paralyze(amount, updating, ignore_canknockdown) if(!ignore_canknockdown && !(status_flags & CANKNOCKDOWN)) @@ -13,7 +14,8 @@ buckled.unbuckle_mob(src) var/drop_items = amount > 80 //80 is cutoff for old item dropping behavior var/stamdmg = isnull(override_stamdmg)? (amount * 0.25) : override_stamdmg - KnockToFloor(drop_items, TRUE, updating) + if(knocktofloor) + KnockToFloor(drop_items, TRUE, updating) adjustStaminaLoss(stamdmg) if(!isnull(override_hardstun)) Paralyze(override_hardstun) diff --git a/code/modules/movespeed/modifiers/status_effects.dm b/code/modules/movespeed/modifiers/status_effects.dm index 1a017c25b0..1adfe8b4be 100644 --- a/code/modules/movespeed/modifiers/status_effects.dm +++ b/code/modules/movespeed/modifiers/status_effects.dm @@ -45,3 +45,9 @@ /datum/movespeed_modifier/status_effect/mkultra multiplicative_slowdown = -2 blacklisted_movetypes= FLYING|FLOATING + +/datum/movespeed_modifier/status_effect/stagger + variable = TRUE + +/datum/movespeed_modifier/status_effect/off_balance + variable = TRUE diff --git a/code/modules/ninja/suit/suit.dm b/code/modules/ninja/suit/suit.dm index b46f30b4fb..ed711771bf 100644 --- a/code/modules/ninja/suit/suit.dm +++ b/code/modules/ninja/suit/suit.dm @@ -118,7 +118,7 @@ Contents: ADD_TRAIT(n_hood, TRAIT_NODROP, NINJA_SUIT_TRAIT) n_shoes = H.shoes ADD_TRAIT(n_shoes, TRAIT_NODROP, NINJA_SUIT_TRAIT) - n_shoes.slowdown-- + n_shoes.slowdown -= 0.5 n_gloves = H.gloves ADD_TRAIT(n_gloves, TRAIT_NODROP, NINJA_SUIT_TRAIT) return TRUE @@ -139,7 +139,7 @@ Contents: REMOVE_TRAIT(n_hood, TRAIT_NODROP, NINJA_SUIT_TRAIT) if(n_shoes) REMOVE_TRAIT(n_shoes, TRAIT_NODROP, NINJA_SUIT_TRAIT) - n_shoes.slowdown++ + n_shoes.slowdown += 0.5 if(n_gloves) n_gloves.icon_state = "s-ninja" n_gloves.item_state = "s-ninja" diff --git a/code/modules/projectiles/ammunition/energy/laser.dm b/code/modules/projectiles/ammunition/energy/laser.dm index f6eca57e29..5b727d76de 100644 --- a/code/modules/projectiles/ammunition/energy/laser.dm +++ b/code/modules/projectiles/ammunition/energy/laser.dm @@ -2,6 +2,9 @@ projectile_type = /obj/item/projectile/beam/laser select_name = "kill" +/obj/item/ammo_casing/energy/laser/minigun + click_cooldown_override = 2 + /obj/item/ammo_casing/energy/lasergun projectile_type = /obj/item/projectile/beam/laser e_cost = 83 diff --git a/code/modules/projectiles/ammunition/energy/stun.dm b/code/modules/projectiles/ammunition/energy/stun.dm index f063672654..cb7f80d949 100644 --- a/code/modules/projectiles/ammunition/energy/stun.dm +++ b/code/modules/projectiles/ammunition/energy/stun.dm @@ -9,6 +9,10 @@ projectile_type = /obj/item/projectile/energy/electrode/security e_cost = 100 +/obj/item/ammo_casing/energy/electrode/hos + projectile_type = /obj/item/projectile/energy/electrode/security/hos + e_cost = 100 + /obj/item/ammo_casing/energy/electrode/spec e_cost = 100 @@ -16,7 +20,6 @@ fire_sound = 'sound/weapons/gunshot.ogg' e_cost = 100 - /obj/item/ammo_casing/energy/electrode/old e_cost = 1000 diff --git a/code/modules/projectiles/boxes_magazines/internal/misc.dm b/code/modules/projectiles/boxes_magazines/internal/misc.dm index 2b87557b39..76f3c88381 100644 --- a/code/modules/projectiles/boxes_magazines/internal/misc.dm +++ b/code/modules/projectiles/boxes_magazines/internal/misc.dm @@ -3,9 +3,3 @@ ammo_type = /obj/item/ammo_casing/caseless/magspear caliber = "speargun" max_ammo = 1 - -/obj/item/ammo_box/magazine/internal/minigun - name = "gatling gun fusion core" - ammo_type = /obj/item/ammo_casing/caseless/laser/gatling - caliber = "gatling" - max_ammo = 5000 diff --git a/code/modules/projectiles/guns/energy/energy_gun.dm b/code/modules/projectiles/guns/energy/energy_gun.dm index 45bc3a79dc..294040b040 100644 --- a/code/modules/projectiles/guns/energy/energy_gun.dm +++ b/code/modules/projectiles/guns/energy/energy_gun.dm @@ -48,12 +48,24 @@ /obj/item/gun/energy/e_gun/hos name = "\improper X-01 MultiPhase Energy Gun" - desc = "This is an expensive, modern recreation of an antique laser gun. This gun has several unique firemodes, but lacks the ability to recharge over time in exchange for inbuilt advanced firearm EMP shielding." + desc = "This is an expensive, modern recreation of an antique laser gun. This gun has several unique firemodes, but lacks the ability to recharge over time in exchange for inbuilt advanced firearm EMP shielding. Right click in combat mode to fire a taser shot with a cooldown." icon_state = "hoslaser" force = 10 - ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser/hos, /obj/item/ammo_casing/energy/ion/hos) + ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser/hos, /obj/item/ammo_casing/energy/ion/hos, /obj/item/ammo_casing/energy/electrode/hos) ammo_x_offset = 4 resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + var/last_altfire = 0 + var/altfire_delay = 0 + +/obj/item/gun/energy/e_gun/hos/altafterattack(atom/target, mob/user, proximity_flag, params) + . = TRUE + if(last_altfire + altfire_delay > world.time) + return + var/current_index = current_firemode_index + set_firemode_to_type(/obj/item/ammo_casing/energy/electrode) + process_afterattack(target, user, proximity_flag, params) + set_firemode_index(current_index) + last_altfire = world.time /obj/item/gun/energy/e_gun/hos/emp_act(severity) return diff --git a/code/modules/projectiles/guns/ballistic/laser_gatling.dm b/code/modules/projectiles/guns/energy/laser_gatling.dm similarity index 81% rename from code/modules/projectiles/guns/ballistic/laser_gatling.dm rename to code/modules/projectiles/guns/energy/laser_gatling.dm index 244bc5b124..716cc6f31d 100644 --- a/code/modules/projectiles/guns/ballistic/laser_gatling.dm +++ b/code/modules/projectiles/guns/energy/laser_gatling.dm @@ -11,19 +11,19 @@ righthand_file = 'icons/mob/inhands/equipment/backpack_righthand.dmi' slot_flags = ITEM_SLOT_BACK w_class = WEIGHT_CLASS_HUGE - var/obj/item/gun/ballistic/minigun/gun + var/obj/item/gun/energy/minigun/gun var/armed = 0 //whether the gun is attached, 0 is attached, 1 is the gun is wielded. var/overheat = 0 - var/overheat_max = 40 + var/overheat_max = 50 var/heat_diffusion = 1 /obj/item/minigunpack/Initialize() . = ..() gun = new(src) - START_PROCESSING(SSobj, src) + START_PROCESSING(SSfastprocess, src) /obj/item/minigunpack/Destroy() - STOP_PROCESSING(SSobj, src) + STOP_PROCESSING(SSfastprocess, src) return ..() /obj/item/minigunpack/process() @@ -72,7 +72,6 @@ var/obj/screen/inventory/hand/H = over_object M.putItemFromInventoryInHandIfPossible(src, H.held_index) - /obj/item/minigunpack/update_icon_state() if(armed) icon_state = "notholstered" @@ -91,8 +90,7 @@ update_icon() user.update_inv_back() - -/obj/item/gun/ballistic/minigun +/obj/item/gun/energy/minigun name = "laser gatling gun" desc = "An advanced laser cannon with an incredible rate of fire. Requires a bulky backpack power source to use." icon = 'icons/obj/guns/minigun.dmi' @@ -103,17 +101,20 @@ slot_flags = null w_class = WEIGHT_CLASS_HUGE custom_materials = null - burst_size = 3 - automatic = 0 - fire_delay = 1 + automatic = 0.5 + fire_delay = 2 + ammo_type = list( + /obj/item/ammo_casing/energy/laser + ) + weapon_weight = WEAPON_HEAVY fire_sound = 'sound/weapons/laser.ogg' - mag_type = /obj/item/ammo_box/magazine/internal/minigun - casing_ejector = FALSE + charge_sections = 0 + shaded_charge = 0 item_flags = NEEDS_PERMIT | SLOWS_WHILE_IN_HAND var/obj/item/minigunpack/ammo_pack -/obj/item/gun/ballistic/minigun/Initialize() +/obj/item/gun/energy/minigun/Initialize() if(istype(loc, /obj/item/minigunpack)) //We should spawn inside an ammo pack so let's use that one. ammo_pack = loc else @@ -121,29 +122,29 @@ return ..() -/obj/item/gun/ballistic/minigun/attack_self(mob/living/user) +/obj/item/gun/energy/minigun/attack_self(mob/living/user) return -/obj/item/gun/ballistic/minigun/dropped(mob/user) +/obj/item/gun/energy/minigun/dropped(mob/user) . = ..() if(ammo_pack) ammo_pack.attach_gun(user) else qdel(src) -/obj/item/gun/ballistic/minigun/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0, stam_cost = 0) +/obj/item/gun/energy/minigun/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0, stam_cost = 0) if(ammo_pack) if(ammo_pack.overheat < ammo_pack.overheat_max) - ammo_pack.overheat += burst_size + ammo_pack.overheat++ ..() else to_chat(user, "The gun's heat sensor locked the trigger to prevent lens damage.") -/obj/item/gun/ballistic/minigun/afterattack(atom/target, mob/living/user, flag, params) +/obj/item/gun/energy/minigun/afterattack(atom/target, mob/living/user, flag, params) if(!ammo_pack || ammo_pack.loc != user) to_chat(user, "You need the backpack power source to fire the gun!") . = ..() -/obj/item/gun/ballistic/minigun/dropped(mob/living/user) +/obj/item/gun/energy/minigun/dropped(mob/living/user) . = ..() ammo_pack.attach_gun(user) diff --git a/code/modules/projectiles/projectile/energy/stun.dm b/code/modules/projectiles/projectile/energy/stun.dm index 83b753e0a3..6f272dab40 100644 --- a/code/modules/projectiles/projectile/energy/stun.dm +++ b/code/modules/projectiles/projectile/energy/stun.dm @@ -37,15 +37,17 @@ ..() /obj/item/projectile/energy/electrode/security - tase_duration = 30 + tase_duration = 40 knockdown = 0 - stamina = 10 + stamina = 0 knockdown_stamoverride = 0 knockdown_stam_max = 0 strong_tase = FALSE /obj/item/projectile/energy/electrode/security/hos - knockdown = 100 - knockdown_stamoverride = 30 - knockdown_stam_max = null - tase_duration = 10 + tase_duration = 40 + knockdown = 0 + stamina = 0 + knockdown_stamoverride = 0 + knockdown_stam_max = 0 + strong_tase = FALSE diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index eb3b80b693..b90815d543 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -18,7 +18,8 @@ var/spray_range = 3 //the range of tiles the sprayer will reach when in spray mode. var/stream_range = 1 //the range of tiles the sprayer will reach when in stream mode. var/stream_amount = 10 //the amount of reagents transfered when in stream mode. - var/spray_delay = 3 //The amount of sleep() delay between each chempuff step. + /// Amount of time it takes for a spray to completely travel. + var/spray_delay = 8 /// Last world.time of spray var/last_spray = 0 /// Spray cooldown @@ -72,58 +73,20 @@ if((last_spray + spray_cooldown) > world.time) return var/range = clamp(get_dist(src, A), 1, current_range) - var/obj/effect/decal/chempuff/D = new /obj/effect/decal/chempuff(get_turf(src)) - D.create_reagents(amount_per_transfer_from_this, NONE, NO_REAGENTS_VALUE) - var/puff_reagent_left = range //how many turf, mob or dense objet we can react with before we consider the chem puff consumed - if(stream_mode) - reagents.trans_to(D, amount_per_transfer_from_this) - puff_reagent_left = 1 - else - reagents.trans_to(D, amount_per_transfer_from_this, 1/range) - D.color = mix_color_from_reagents(D.reagents.reagent_list) + var/wait_step = CEILING(spray_delay * INVERSE(range), world.tick_lag) + var/obj/effect/decal/chempuff/D = new /obj/effect/decal/chempuff(get_turf(src), stream_mode, wait_step, range, stream_mode? 1 : range) var/turf/T = get_turf(src) if(!T) return log_reagent("SPRAY: [key_name(usr)] fired [src] ([REF(src)]) [COORD(T)] at [A] ([REF(A)]) [COORD(A)] (chempuff: [D.reagents.log_list()])") - var/wait_step = max(round(2+ spray_delay * INVERSE(range)), 2) + D.create_reagents(amount_per_transfer_from_this, NONE, NO_REAGENTS_VALUE) + if(stream_mode) + reagents.trans_to(D, amount_per_transfer_from_this) + else + reagents.trans_to(D, amount_per_transfer_from_this, 1/range) + D.color = mix_color_from_reagents(D.reagents.reagent_list) last_spray = world.time - INVOKE_ASYNC(src, .proc/do_spray, A, wait_step, D, range, puff_reagent_left) - return TRUE - -/obj/item/reagent_containers/spray/proc/do_spray(atom/A, wait_step, obj/effect/decal/chempuff/D, range, puff_reagent_left) - var/range_left = range - for(var/i=0, i 0 && (!stream_mode || !range_left)) - D.reagents.reaction(get_turf(D), VAPOR) - puff_reagent_left -= 1 - - if(puff_reagent_left <= 0) // we used all the puff so we delete it. - qdel(D) - return - qdel(D) + D.run_puff(A) /obj/item/reagent_containers/spray/attack_self(mob/user) stream_mode = !stream_mode @@ -207,7 +170,7 @@ righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi' volume = 40 stream_range = 4 - spray_delay = 1 + spray_delay = 2 amount_per_transfer_from_this = 5 list_reagents = list(/datum/reagent/consumable/condensedcapsaicin = 40) diff --git a/code/modules/smithing/finished_items.dm b/code/modules/smithing/finished_items.dm index 20eaee8a08..27be8c034d 100644 --- a/code/modules/smithing/finished_items.dm +++ b/code/modules/smithing/finished_items.dm @@ -209,7 +209,7 @@ parry_time_perfect = 2 parry_time_perfect_leeway = 0.75 parry_imperfect_falloff_percent = 7.5 - parry_efficiency_to_counterattack = 100 + parry_efficiency_to_counterattack = INFINITY parry_efficiency_considered_successful = 80 parry_efficiency_perfect = 120 parry_failed_stagger_duration = 3 SECONDS @@ -266,14 +266,15 @@ parry_time_perfect = 2 parry_time_perfect_leeway = 2 parry_failed_stagger_duration = 3 SECONDS - parry_failed_clickcd_duration = 3 SECONDS parry_time_windup = 0 parry_time_spindown = 0 parry_imperfect_falloff_percent = 0 - parry_efficiency_to_counterattack = 100 + parry_efficiency_to_counterattack = INFINITY parry_efficiency_considered_successful = 120 parry_efficiency_perfect = 120 parry_data = list(PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 4) + parry_automatic_enabled = TRUE + autoparry_single_efficiency = 75 //unique hammers /obj/item/melee/smith/hammer/toolbox diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index d595c494b5..e24268cc45 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -32,6 +32,8 @@ var/max_stamina_damage = 0 var/incoming_stam_mult = 1 //Multiplier for incoming staminaloss, decreases when taking staminaloss when the limb is disabled, resets back to 1 when limb is no longer disabled. var/max_damage = 0 + /// Threshold at which we are disabled. Defaults to max_damage if unset. + var/disable_threshold var/stam_heal_tick = 0 //per Life(). Defaults to 0 due to citadel changes var/brute_reduction = 0 //Subtracted to brute damage taken @@ -505,6 +507,12 @@ return set_disabled(is_disabled(silent), silent) +/** + * Gets the damage at which point we're disabled. + */ +/obj/item/bodypart/proc/get_disable_threshold() + return isnull(disable_threshold)? max_damage : disable_threshold + /obj/item/bodypart/proc/is_disabled(silent = FALSE) if(!owner) return @@ -514,15 +522,16 @@ var/datum/wound/W = i if(W.disabling) return BODYPART_DISABLED_WOUND + var/disable_threshold = get_disable_threshold() if(can_dismember() && !HAS_TRAIT(owner, TRAIT_NODISMEMBER)) . = disabled //inertia, to avoid limbs healing 0.1 damage and being re-enabled - if(get_damage(TRUE) >= max_damage * (HAS_TRAIT(owner, TRAIT_EASYLIMBDISABLE) ? 0.6 : 1)) //Easy limb disable disables the limb at 40% health instead of 0% + if(get_damage(TRUE) >= disable_threshold * (HAS_TRAIT(owner, TRAIT_EASYLIMBDISABLE) ? 0.6 : 1)) //Easy limb disable disables the limb at 40% health instead of 0% if(!last_maxed && !silent) owner.emote("scream") last_maxed = TRUE - if(!is_organic_limb(FALSE) || stamina_dam >= max_damage) + if(!is_organic_limb(FALSE) || stamina_dam >= disable_threshold) return BODYPART_DISABLED_DAMAGE - else if(disabled && (get_damage(TRUE) <= (max_damage * 0.8))) // reenabled at 80% now instead of 50% as of wounds update + else if(disabled && (get_damage(TRUE) <= (disable_threshold * 0.8))) // reenabled at 80% now instead of 50% as of wounds update last_maxed = FALSE return BODYPART_NOT_DISABLED else diff --git a/code/modules/surgery/bodyparts/parts.dm b/code/modules/surgery/bodyparts/parts.dm index 86288564ae..8c7553d8e3 100644 --- a/code/modules/surgery/bodyparts/parts.dm +++ b/code/modules/surgery/bodyparts/parts.dm @@ -59,12 +59,13 @@ one though." icon_state = "default_human_l_arm" attack_verb = list("slapped", "punched") - max_damage = 50 + max_damage = 150 + disable_threshold = 75 max_stamina_damage = 50 body_zone = BODY_ZONE_L_ARM body_part = ARM_LEFT aux_icons = list(BODY_ZONE_PRECISE_L_HAND = HANDS_PART_LAYER, "l_hand_behind" = BODY_BEHIND_LAYER) - body_damage_coeff = 0.75 + body_damage_coeff = 0.25 held_index = 1 px_x = -6 px_y = 0 @@ -120,11 +121,12 @@ among humans missing their right arm." icon_state = "default_human_r_arm" attack_verb = list("slapped", "punched") - max_damage = 50 + max_damage = 150 + disable_threshold = 75 body_zone = BODY_ZONE_R_ARM body_part = ARM_RIGHT aux_icons = list(BODY_ZONE_PRECISE_R_HAND = HANDS_PART_LAYER, "r_hand_behind" = BODY_BEHIND_LAYER) - body_damage_coeff = 0.75 + body_damage_coeff = 0.25 held_index = 2 px_x = 6 px_y = 0 @@ -182,10 +184,11 @@ luck. In this instance, it probably would not have helped." icon_state = "default_human_l_leg" attack_verb = list("kicked", "stomped") - max_damage = 50 + max_damage = 150 + disable_threshold = 75 body_zone = BODY_ZONE_L_LEG body_part = LEG_LEFT - body_damage_coeff = 0.75 + body_damage_coeff = 0.25 px_x = -2 px_y = 12 stam_heal_tick = STAM_RECOVERY_LIMB @@ -240,10 +243,11 @@ // alternative spellings of 'pokey' are availible icon_state = "default_human_r_leg" attack_verb = list("kicked", "stomped") - max_damage = 50 + max_damage = 150 + disable_threshold = 75 body_zone = BODY_ZONE_R_LEG body_part = LEG_RIGHT - body_damage_coeff = 0.75 + body_damage_coeff = 0.25 px_x = 2 px_y = 12 max_stamina_damage = 50 diff --git a/tgstation.dme b/tgstation.dme index 35ad674ec0..ed85c32cc4 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -201,6 +201,7 @@ #include "code\__HELPERS\vector.dm" #include "code\__HELPERS\verbs.dm" #include "code\__HELPERS\view.dm" +#include "code\__HELPERS\yelling.dm" #include "code\__HELPERS\sorts\__main.dm" #include "code\__HELPERS\sorts\InsertSort.dm" #include "code\__HELPERS\sorts\MergeSort.dm" @@ -3127,7 +3128,6 @@ #include "code\modules\projectiles\guns\ballistic\automatic.dm" #include "code\modules\projectiles\guns\ballistic\bow.dm" #include "code\modules\projectiles\guns\ballistic\derringer.dm" -#include "code\modules\projectiles\guns\ballistic\laser_gatling.dm" #include "code\modules\projectiles\guns\ballistic\launchers.dm" #include "code\modules\projectiles\guns\ballistic\magweapon.dm" #include "code\modules\projectiles\guns\ballistic\pistol.dm" @@ -3138,6 +3138,7 @@ #include "code\modules\projectiles\guns\energy\energy_gun.dm" #include "code\modules\projectiles\guns\energy\kinetic_accelerator.dm" #include "code\modules\projectiles\guns\energy\laser.dm" +#include "code\modules\projectiles\guns\energy\laser_gatling.dm" #include "code\modules\projectiles\guns\energy\megabuster.dm" #include "code\modules\projectiles\guns\energy\mounted.dm" #include "code\modules\projectiles\guns\energy\plasma_cit.dm"