From eba1e8f3015290cb06283a5026e21713a009e169 Mon Sep 17 00:00:00 2001 From: Timothy Teakettle <59849408+timothyteakettle@users.noreply.github.com> Date: Mon, 15 Jun 2020 23:29:49 +0100 Subject: [PATCH] fixes the merge conflicts --- code/__DEFINES/_flags/_flags.dm | 5 + code/__DEFINES/combat.dm | 22 ++++- code/__DEFINES/traits.dm | 2 + code/_globalvars/bitfields.dm | 4 + code/_globalvars/traits.dm | 3 +- code/game/atoms.dm | 26 ++++- code/game/objects/items/shrapnel.dm | 4 - code/game/turfs/simulated/walls.dm | 11 --- .../antagonists/blob/blob/blobs/shield.dm | 11 --- code/modules/mob/living/living_defense.dm | 2 +- .../projectiles/boxes_magazines/ammo_boxes.dm | 13 ++- code/modules/projectiles/projectile.dm | 97 +++++++++++++------ 12 files changed, 136 insertions(+), 64 deletions(-) diff --git a/code/__DEFINES/_flags/_flags.dm b/code/__DEFINES/_flags/_flags.dm index dae3df6f0b..b1324000b8 100644 --- a/code/__DEFINES/_flags/_flags.dm +++ b/code/__DEFINES/_flags/_flags.dm @@ -141,3 +141,8 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 // melee_attack_chain() attackchain_flags /// The attack is from a parry counterattack. #define ATTACKCHAIN_PARRY_COUNTERATTACK (1<<0) + +/// If the thing can reflect light (lasers/energy) +#define RICOCHET_SHINY (1<<0) +/// If the thing can reflect matter (bullets/bomb shrapnel) +#define RICOCHET_HARD (1<<1) \ No newline at end of file diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index bf053635af..3cb60b1143 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -163,9 +163,8 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( /obj/item/gun))) -//Combat object defines - //Embedded objects + #define EMBEDDED_PAIN_CHANCE 15 //Chance for embedded objects to cause pain (damage user) #define EMBEDDED_ITEM_FALLOUT 5 //Chance for embedded object to fall out (causing pain but removing the object) #define EMBED_CHANCE 45 //Chance for an object to embed into somebody when thrown (if it's sharp) @@ -174,7 +173,16 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( #define EMBEDDED_IMPACT_PAIN_MULTIPLIER 4 //Coefficient of multiplication for the damage the item does when it first embeds (this*item.w_class) #define EMBED_THROWSPEED_THRESHOLD 4 //The minimum value of an item's throw_speed for it to embed (Unless it has embedded_ignore_throwspeed_threshold set to 1) #define EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER 8 //Coefficient of multiplication for the damage the item does when removed without a surgery (this*item.w_class) -#define EMBEDDED_UNSAFE_REMOVAL_TIME 150 //A Time in ticks, total removal time = (this/item.w_class) +#define EMBEDDED_UNSAFE_REMOVAL_TIME 30 //A Time in ticks, total removal time = (this*item.w_class) +#define EMBEDDED_JOSTLE_CHANCE 5 //Chance for embedded objects to cause pain every time they move (jostle) +#define EMBEDDED_JOSTLE_PAIN_MULTIPLIER 1 //Coefficient of multiplication for the damage the item does while +#define EMBEDDED_PAIN_STAM_PCT 0.0 //This percentage of all pain will be dealt as stam damage rather than brute (0-1) +#define EMBED_CHANCE_TURF_MOD -15 //You are this many percentage points less likely to embed into a turf (good for things glass shards and spears vs walls) + +#define EMBED_HARMLESS list("pain_mult" = 0, "jostle_pain_mult" = 0, "ignore_throwspeed_threshold" = TRUE) +#define EMBED_HARMLESS_SUPERIOR list("pain_mult" = 0, "jostle_pain_mult" = 0, "ignore_throwspeed_threshold" = TRUE, "embed_chance" = 100, "fall_chance" = 0.1) +#define EMBED_POINTY list("ignore_throwspeed_threshold" = TRUE) +#define EMBED_POINTY_SUPERIOR list("embed_chance" = 100, "ignore_throwspeed_threshold" = TRUE) //Gun weapon weight #define WEAPON_LIGHT 1 @@ -189,6 +197,14 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( #define EGUN_SELFCHARGE 1 #define EGUN_SELFCHARGE_BORG 2 +//Gun suppression +#define SUPPRESSED_NONE 0 +#define SUPPRESSED_QUIET 1 ///standard suppressed +#define SUPPRESSED_VERY 2 /// no message + +//Nice shot bonus +#define NICE_SHOT_RICOCHET_BONUS 10 //if the shooter has the NICE_SHOT trait and they fire a ricocheting projectile, add this to the ricochet chance and auto aim angle + ///Time to spend without clicking on other things required for your shots to become accurate. #define GUN_AIMING_TIME (2 SECONDS) diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index f3f9f1e8b5..3977b3c085 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -196,6 +196,7 @@ #define TRAIT_NO_ALCOHOL "alcohol_intolerance" #define TRAIT_MUTATION_STASIS "mutation_stasis" //Prevents processed genetics mutations from processing. #define TRAIT_FAST_PUMP "fast_pump" +#define TRAIT_NICE_SHOT "nice_shot" //hnnnnnnnggggg..... you're pretty good.... // mobility flag traits // IN THE FUTURE, IT WOULD BE NICE TO DO SOMETHING SIMILAR TO https://github.com/tgstation/tgstation/pull/48923/files (ofcourse not nearly the same because I have my.. thoughts on it) @@ -301,3 +302,4 @@ #define ACTIVE_BLOCK_TRAIT "active_block" /// This trait is added by the parry system. #define ACTIVE_PARRY_TRAIT "active_parry" +#define STICKY_NODROP "sticky-nodrop" //sticky nodrop sounds like a bad soundcloud rapper's name \ No newline at end of file diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index c3004a4501..85f5e4fd9b 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -140,6 +140,10 @@ GLOBAL_LIST_INIT(bitfields, list( "BLOCK_FACE_ATOM_1" = BLOCK_FACE_ATOM_1, "PREVENT_CONTENTS_EXPLOSION_1" = PREVENT_CONTENTS_EXPLOSION_1 ), + "flags_ricochet" = list( + "RICOCHET_SHINY" = RICOCHET_SHINY, + "RICOCHET_HARD" = RICOCHET_HARD + ), "clothing_flags" = list( "LAVAPROTECT" = LAVAPROTECT, "STOPSPRESSUREDAMAGE" = STOPSPRESSUREDAMAGE, diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index 3c28e50ea3..8d8b8820b2 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -117,7 +117,8 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_MASO" = TRAIT_MASO, "TRAIT_HIGH_BLOOD" = TRAIT_HIGH_BLOOD, "TRAIT_EMPATH" = TRAIT_EMPATH, - "TRAIT_FRIENDLY" = TRAIT_FRIENDLY + "TRAIT_FRIENDLY" = TRAIT_FRIENDLY, + "TRAIT_NICE_SHOT" = TRAIT_NICE_SHOT ), /obj/item/bodypart = list( "TRAIT_PARALYSIS" = TRAIT_PARALYSIS diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 5875334ed6..99c3379d77 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -8,6 +8,13 @@ var/interaction_flags_atom = NONE var/datum/reagents/reagents = null + var/flags_ricochet = NONE + + ///When a projectile tries to ricochet off this atom, the projectile ricochet chance is multiplied by this + var/ricochet_chance_mod = 1 + ///When a projectile ricochets off this atom, it deals the normal damage * this modifier to this atom + var/ricochet_damage_mod = 0.33 + //This atom's HUD (med/sec, etc) images. Associative list. var/list/image/hud_list = null //HUD images that this atom can provide. @@ -148,11 +155,20 @@ /atom/proc/check_projectile_ricochet(obj/item/projectile/P) return (flags_1 & DEFAULT_RICOCHET_1)? PROJECTILE_RICOCHET_YES : PROJECTILE_RICOCHET_NO -/** - * Handle a projectile ricochet. Return TRUE if we did something to the projectile like reflecting it/whatnot. - */ -/atom/proc/handle_projectile_ricochet(obj/item/projectile/P) - return FALSE +/atom/proc/handle_ricochet(obj/item/projectile/P) + var/turf/p_turf = get_turf(P) + var/face_direction = get_dir(src, p_turf) + var/face_angle = dir2angle(face_direction) + var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180)) + var/a_incidence_s = abs(incidence_s) + if(a_incidence_s > 90 && a_incidence_s < 270) + return FALSE + if((P.flag in list("bullet", "bomb")) && P.ricochet_incidence_leeway) + if((a_incidence_s < 90 && a_incidence_s < 90 - P.ricochet_incidence_leeway) || (a_incidence_s > 270 && a_incidence_s -270 > P.ricochet_incidence_leeway)) + return + var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) + P.setAngle(new_angle_s) + return TRUE /atom/proc/CanPass(atom/movable/mover, turf/target) return !density diff --git a/code/game/objects/items/shrapnel.dm b/code/game/objects/items/shrapnel.dm index d3ffe62af8..7108080ecc 100644 --- a/code/game/objects/items/shrapnel.dm +++ b/code/game/objects/items/shrapnel.dm @@ -36,7 +36,6 @@ ricochet_chance = 40 shrapnel_type = /obj/item/shrapnel ricochet_incidence_leeway = 60 - hit_stunned_targets = TRUE /obj/item/projectile/bullet/shrapnel/mega name = "flying shrapnel hunk" @@ -63,6 +62,3 @@ name = "megastingball pellet" ricochets_max = 6 ricochet_chance = 110 - -/obj/item/projectile/bullet/pellet/stingball/on_ricochet(atom/A) - hit_stunned_targets = TRUE // ducking will save you from the first wave, but not the rebounds diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index da5d3c643d..b01a1df2c0 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -41,17 +41,6 @@ /turf/closed/wall/attack_tk() return -/turf/closed/wall/handle_projectile_ricochet(obj/item/projectile/P) //A huge pile of shitcode! - var/turf/p_turf = get_turf(P) - var/face_direction = get_dir(src, p_turf) - var/face_angle = dir2angle(face_direction) - var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180)) - if(abs(incidence_s) > 90 && abs(incidence_s) < 270) - return FALSE - var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) - P.setAngle(new_angle_s) - return TRUE - /turf/closed/wall/proc/dismantle_wall(devastated=0, explode=0) if(devastated) devastate_wall() diff --git a/code/modules/antagonists/blob/blob/blobs/shield.dm b/code/modules/antagonists/blob/blob/blobs/shield.dm index 384df935f5..a2a6ce94d3 100644 --- a/code/modules/antagonists/blob/blob/blobs/shield.dm +++ b/code/modules/antagonists/blob/blob/blobs/shield.dm @@ -53,14 +53,3 @@ /obj/structure/blob/shield/reflective/check_projectile_ricochet(obj/item/projectile/P) return PROJECTILE_RICOCHET_FORCE -/obj/structure/blob/shield/reflective/handle_projectile_ricochet(obj/item/projectile/P) - var/turf/p_turf = get_turf(P) - var/face_direction = get_dir(src, p_turf) - var/face_angle = dir2angle(face_direction) - var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180)) - if(abs(incidence_s) > 90 && abs(incidence_s) < 270) - return FALSE - var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) - P.setAngle(new_angle_s) - visible_message("[P] reflects off [src]!") - return TRUE diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 2ca1b63b14..3df07a6101 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -126,7 +126,7 @@ skipcatch = TRUE blocked = TRUE total_damage = block_calculate_resultant_damage(total_damage, block_return) - else if(I && I.throw_speed >= EMBED_THROWSPEED_THRESHOLD && can_embed(I, src) && prob(I.embedding.embed_chance) && !HAS_TRAIT(src, TRAIT_PIERCEIMMUNE) && (!HAS_TRAIT(src, TRAIT_AUTO_CATCH_ITEM) || incapacitated() || get_active_held_item())) + else if(I && I.throw_speed >= EMBED_THROWSPEED_THRESHOLD && can_embed(I, src) && prob(I.embedding["embed_chance"]) && !HAS_TRAIT(src, TRAIT_PIERCEIMMUNE) && (!HAS_TRAIT(src, TRAIT_AUTO_CATCH_ITEM) || incapacitated() || get_active_held_item())) embed_item(I) hitpush = FALSE skipcatch = TRUE //can't catch the now embedded item diff --git a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm index 4009027d25..86d66ec354 100644 --- a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm +++ b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm @@ -50,12 +50,23 @@ desc = "Designed to quickly reload revolvers. DumDum bullets shatter on impact and shred the target's innards, likely getting caught inside." ammo_type = /obj/item/ammo_casing/c38/dumdum - /obj/item/ammo_box/c38/match name = "speed loader (.38 Match)" desc = "Designed to quickly reload revolvers. These rounds are manufactured within extremely tight tolerances, making them easy to show off trickshots with." ammo_type = /obj/item/ammo_casing/c38/match +/obj/item/ammo_box/c32mm + name = "ammo box (.32 acp)" + desc = "Lethal .32 acp bullets, there's forty in the box." + ammo_type = /obj/item/ammo_casing/c32acp + max_ammo = 40 + +/obj/item/ammo_box/r32mm + name = "ammo box (rubber .32 acp)" + desc = "Non-lethal .32 acp bullets, there's forty in the box." + ammo_type = /obj/item/ammo_casing/r32acp + max_ammo = 40 + /obj/item/ammo_box/c9mm name = "ammo box (9mm)" icon_state = "9mmbox" diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index d2643eeeb7..4658e9375d 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -58,9 +58,23 @@ var/nondirectional_sprite = FALSE //Set TRUE to prevent projectiles from having their sprites rotated based on firing angle var/spread = 0 //amount (in degrees) of projectile spread animate_movement = 0 //Use SLIDE_STEPS in conjunction with legacy + + // how many times we've ricochet'd so far (instance variable, not a stat) var/ricochets = 0 - var/ricochets_max = 2 - var/ricochet_chance = 30 + /// how many times we can ricochet max + var/ricochets_max = 0 + /// 0-100, the base chance of ricocheting, before being modified by the atom we shoot and our chance decay + var/ricochet_chance = 0 + /// 0-1 (or more, I guess) multiplier, the ricochet_chance is modified by multiplying this after each ricochet + var/ricochet_decay_chance = 0.7 + /// 0-1 (or more, I guess) multiplier, the projectile's damage is modified by multiplying this after each ricochet + var/ricochet_decay_damage = 0.7 + /// On ricochet, if nonzero, we consider all mobs within this range of our projectile at the time of ricochet to home in on like Revolver Ocelot, as governed by ricochet_auto_aim_angle + var/ricochet_auto_aim_range = 0 + /// On ricochet, if ricochet_auto_aim_range is nonzero, we'll consider any mobs within this range of the normal angle of incidence to home in on, higher = more auto aim + var/ricochet_auto_aim_angle = 30 + /// the angle of impact must be within this many degrees of the struck surface, set to 0 to allow any angle + var/ricochet_incidence_leeway = 40 //Hitscan var/hitscan = FALSE //Whether this is hitscan. If it is, speed is basically ignored. @@ -133,6 +147,9 @@ var/temporary_unstoppable_movement = FALSE + ///If defined, on hit we create an item of this type then call hitby() on the hit target with this + var/shrapnel_type + /obj/item/projectile/Initialize() . = ..() permutated = list() @@ -259,6 +276,25 @@ else return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume +/obj/item/projectile/proc/on_ricochet(atom/A) + if(!ricochet_auto_aim_angle || !ricochet_auto_aim_range) + return + + var/mob/living/unlucky_sob + var/best_angle = ricochet_auto_aim_angle + if(firer && HAS_TRAIT(firer, TRAIT_NICE_SHOT)) + best_angle += NICE_SHOT_RICOCHET_BONUS + for(var/mob/living/L in range(ricochet_auto_aim_range, src.loc)) + if(L.stat == DEAD || !isInSight(src, L)) + continue + var/our_angle = abs(closer_angle_difference(Angle, Get_Angle(src.loc, L.loc))) + if(our_angle < best_angle) + best_angle = our_angle + unlucky_sob = L + + if(unlucky_sob) + setAngle(Get_Angle(src, unlucky_sob.loc)) + /obj/item/projectile/proc/store_hitscan_collision(datum/point/pcache) beam_segments[beam_index] = pcache beam_index = pcache @@ -281,42 +317,46 @@ if(PROJECTILE_RICOCHET_NO) return FALSE if(PROJECTILE_RICOCHET_YES) - return prob(ricochet_chance) + var/chance = ricochet_chance * A.ricochet_chance_mod + if(firer && HAS_TRAIT(firer, TRAIT_NICE_SHOT)) + chance += NICE_SHOT_RICOCHET_BONUS + return prob(chance) else CRASH("Invalid return value for projectile ricochet check from [A].") -/** - * Handles ricocheting off of something. - * By default, also asks the thing to handle it, but because we're called first, we get final say. - */ -/obj/item/projectile/proc/handle_ricochet(atom/A) - ricochets++ - ignore_source_check = TRUE - decayedRange = max(0, decayedRange - reflect_range_decrease) - pixel_move_interrupted = TRUE - range = decayedRange - return A.handle_projectile_ricochet(src) - -/obj/item/projectile/Bump(atom/A) - if(!trajectory) - return - var/turf/T = get_turf(A) - if(check_ricochet(A)) - var/datum/point/pcache = trajectory.copy_to() - if(hitscan) - store_hitscan_collision(pcache) - handle_ricochet(A) +/obj/item/projectile/proc/check_ricochet_flag(atom/A) + if((flag in list("energy", "laser")) && (A.flags_ricochet & RICOCHET_SHINY)) return TRUE + if((flag in list("bomb", "bullet")) && (A.flags_ricochet & RICOCHET_HARD)) + return TRUE + + return FALSE + +/obj/item/projectile/Bump(atom/A) + var/datum/point/pcache = trajectory.copy_to() + var/turf/T = get_turf(A) + if(ricochets < ricochets_max && check_ricochet_flag(A) && check_ricochet(A)) + ricochets++ + if(A.handle_ricochet(src)) + on_ricochet(A) + ignore_source_check = TRUE + decayedRange = max(0, decayedRange - reflect_range_decrease) + ricochet_chance *= ricochet_decay_chance + damage *= ricochet_decay_damage + range = decayedRange + if(hitscan) + store_hitscan_collision(pcache) + return TRUE + var/distance = get_dist(T, starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations. - if(def_zone && check_zone(def_zone) != BODY_ZONE_CHEST) - def_zone = ran_zone(def_zone, max(100-(7*distance), 5) * zone_accuracy_factor) //Lower accurancy/longer range tradeoff. 7 is a balanced number to use. + def_zone = ran_zone(def_zone, max(100-(7*distance), 5)) //Lower accurancy/longer range tradeoff. 7 is a balanced number to use. if(isturf(A) && hitsound_wall) var/volume = clamp(vol_by_damage() + 20, 0, 100) if(suppressed) volume = 5 - playsound(loc, hitsound_wall, volume, 1, -1) + playsound(loc, hitsound_wall, volume, TRUE, -1) return process_hit(T, select_target(T, A)) @@ -418,6 +458,9 @@ /obj/item/projectile/proc/fire(angle, atom/direct_target) if(fired_from) SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_BEFORE_FIRE, src, original) //If no angle needs to resolve it from xo/yo! + //If no angle needs to resolve it from xo/yo! + if(shrapnel_type) + AddElement(/datum/element/embed, projectile_payload = shrapnel_type) if(!log_override && firer && original) log_combat(firer, original, "fired at", src, "from [get_area_name(src, TRUE)]") if(direct_target)