diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index 589103d4b03..2bde3b1f0c6 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -223,6 +223,33 @@ Turf and target are seperate in case you want to teleport some distance from a t line+=locate(px,py,M.z) return line +#define LOCATE_COORDS(X, Y, Z) locate(between(1, X, world.maxx), between(1, Y, world.maxy), Z) +/proc/getcircle(turf/center, var/radius) //Uses a fast Bresenham rasterization algorithm to return the turfs in a thin circle. + if(!radius) return list(center) + + var/x = 0 + var/y = radius + var/p = 3 - 2 * radius + + . = list() + while(y >= x) // only formulate 1/8 of circle + + . += LOCATE_COORDS(center.x - x, center.y - y, center.z) //upper left left + . += LOCATE_COORDS(center.x - y, center.y - x, center.z) //upper upper left + . += LOCATE_COORDS(center.x + y, center.y - x, center.z) //upper upper right + . += LOCATE_COORDS(center.x + x, center.y - y, center.z) //upper right right + . += LOCATE_COORDS(center.x - x, center.y + y, center.z) //lower left left + . += LOCATE_COORDS(center.x - y, center.y + x, center.z) //lower lower left + . += LOCATE_COORDS(center.x + y, center.y + x, center.z) //lower lower right + . += LOCATE_COORDS(center.x + x, center.y + y, center.z) //lower right right + + if(p < 0) + p += 4*x++ + 6; + else + p += 4*(x++ - y--) + 10; + +#undef LOCATE_COORDS + //Returns whether or not a player is a guest using their ckey as an input /proc/IsGuestKey(key) if (findtext(key, "Guest-", 1, 7) != 1) //was findtextEx diff --git a/code/game/objects/items/weapons/grenades/fragmentation.dm b/code/game/objects/items/weapons/grenades/fragmentation.dm index 7ed4080085b..6ae37db22dd 100644 --- a/code/game/objects/items/weapons/grenades/fragmentation.dm +++ b/code/game/objects/items/weapons/grenades/fragmentation.dm @@ -1,95 +1,57 @@ //Fragmentation grenade projectile /obj/item/projectile/bullet/pellet/fragment - damage = 6 + damage = 15 range_step = 2 - - base_spread = 0 //hit any target zone randomly - spread_step = 0 - silenced = 1 + base_spread = 0 //causes it to be treated as a shrapnel explosion instead of cone + spread_step = 20 + + //silenced = 1 //embedding messages are still produced so it's kind of weird when enabled. + move_delay = 10 muzzle_type = null /obj/item/weapon/grenade/frag name = "fragmentation grenade" desc = "A military fragmentation grenade, designed to explode in a deadly shower of fragments." - var/num_fragments = 7 //fragments per projectile + var/num_fragments = 120 //total number of fragments produced by the grenade var/fragment_damage = 15 - var/damage_step = 1 //projectiles lose a fragment each time they travel this distance. Can be a non-integer. + var/damage_step = 2 //projectiles lose a fragment each time they travel this distance. Can be a non-integer. + var/explosion_size = 2 //size of the center explosion - var/silenced = 0 - - //Higher values means projectile spread is more even but more projectiles are created. - //The actual number of projectile objects is spread_range*8 - var/spread_range = 4 //32 projectiles. With spread_range=4 and num_fragments=7, a frag grenade produces 224 fragments. + //The radius of the circle used to launch projectiles. Lower values mean less projectiles are used but if set too low gaps may appear in the spread pattern + var/spread_range = 7 -#define LOCATE_COORDS(X, Y, Z) locate(between(1, X, world.maxx), between(1, Y, world.maxy), Z) /obj/item/weapon/grenade/frag/prime() ..() var/turf/O = get_turf(src) if(!O) return - explosion(O, -1, 1, 2, 1, 0) + if(explosion_size) + explosion(O, -1, round(explosion_size/2), explosion_size, round(explosion_size/2), 0) - for(var/i in -spread_range to spread_range) - var/turf/T1 = LOCATE_COORDS(O.x+i, O.y+spread_range, O.z) - launch_projectile(O, T1) - - var/turf/T2 = LOCATE_COORDS(O.x+i, O.y-spread_range, O.z) - launch_projectile(O, T2) + var/list/target_turfs = getcircle(O, spread_range) + var/fragments_per_projectile = round(num_fragments/target_turfs.len) - for(var/j in 1-spread_range to spread_range-1) - var/turf/T1 = LOCATE_COORDS(O.x+spread_range, O.y+j, O.z) - launch_projectile(O, T1) - - var/turf/T2 = LOCATE_COORDS(O.x-spread_range, O.y+j, O.z) - launch_projectile(O, T2) - - qdel(src) + for(var/turf/T in target_turfs) + var/obj/item/projectile/bullet/pellet/fragment/P = new (O) -/obj/item/weapon/grenade/frag/proc/launch_projectile(var/turf/source, var/turf/target) - var/obj/item/projectile/bullet/pellet/fragment/P = new(src) - - P.damage = fragment_damage - P.pellets = num_fragments - P.range_step = damage_step - P.silenced = silenced - P.shot_from = src.name - - var/location = loc //store our current loc since qdel will move the grenade to nullspace - spawn(0) - P.launch(target) - /* - if(!isturf(location)) - P.Bump(location) - for(var/atom/movable/AM in source) - P.Bump(AM) - */ + P.damage = fragment_damage + P.pellets = fragments_per_projectile + P.range_step = damage_step + P.shot_from = src.name + + P.launch(T) + + var/cone = new /obj/item/weapon/caution/cone (T) + spawn(100) qdel(cone) + + //Make sure to hit any mobs in the source turf + for(var/mob/living/M in O) + if(M.lying) + P.attack_mob(M, 0, 0) //lying on a frag grenade causes you to tank most of it. + else + P.attack_mob(M, 0, 100) //otherwise, allow a decent amount of fragments to pass - /* - //the vector system doesn't play well with very large angle offsets, so generate projectiles along each of the 8 directions - var/subarc_width = 45/(num_projectiles*2) - var/fragments_per_projectile = round(num_fragments / (num_projectiles*8)) - - for(var/launch_dir in alldirs) - var/turf/target = get_edge_target_turf(T, launch_dir) - for(var/i in 1 to num_projectiles) - //keep the trajectory centered in the 45 deg arc - var/launch_angle = (i-0.5)*subarc_width - 22.5 - - var/obj/item/projectile/bullet/pellet/fragment/P = new(src) - - if(dispersion) - P.dispersion = (subarc_width)/9 //randomly disperse within the projectile arc, so that there aren't any predictable blind spots - P.damage = fragment_damage - P.pellets = fragments_per_projectile - P.range_step = damage_step - P.silenced = silenced - - P.shot_from = src.name - spawn(0) - P.Move(T) //so that we hit people in the starting turf - P.launch(target, ran_zone(), angle_offset = launch_angle) - */ qdel(src) diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index 76a84b19e92..14cbd72d605 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -77,7 +77,7 @@ . = ..() bumped = 0 //can hit all mobs in a tile. pellets is decremented inside attack_mob so this should be fine. -/obj/item/projectile/bullet/pellet/attack_mob(var/mob/living/target_mob, var/distance, var/prone = 0) +/obj/item/projectile/bullet/pellet/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier) if (pellets < 0) return 1 var/pellet_loss = round((distance - 1)/range_step) //pellets lost due to distance @@ -89,8 +89,6 @@ if(!base_spread) prone_chance = max(spread_step*(distance - 2), 0) - world << "[src]: attacking [target_mob] > target_mob.lying = [target_mob.lying], original = [original], prone_chance = [prone_chance]" - var/hits = 0 for (var/i in 1 to total_pellets) if(target_mob.lying && target_mob != original && prob(prone_chance)) @@ -114,8 +112,9 @@ //If this is a shrapnel explosion, allow mobs that are prone to get hit, too if(. && !base_spread && isturf(loc)) for(var/mob/living/M in loc) - if(M.lying && Bump(M)) //Bump will make sure we don't hit a mob multiple times - return + if(M.lying || !M.CanPass(src, loc)) //Bump if lying or if we would normally Bump. + if(Bump(M)) //Bump will make sure we don't hit a mob multiple times + return /* short-casing projectiles, like the kind used in pistols or SMGs */