Merge pull request #10672 from mwerezak/frag-grenade

Adds fragmentation grenades
This commit is contained in:
Zuhayr
2015-09-07 14:12:27 +09:30
30 changed files with 178 additions and 72 deletions

View File

@@ -707,6 +707,7 @@
#include "code\game\objects\items\weapons\grenades\chem_grenade.dm"
#include "code\game\objects\items\weapons\grenades\emgrenade.dm"
#include "code\game\objects\items\weapons\grenades\flashbang.dm"
#include "code\game\objects\items\weapons\grenades\fragmentation.dm"
#include "code\game\objects\items\weapons\grenades\grenade.dm"
#include "code\game\objects\items\weapons\grenades\smokebomb.dm"
#include "code\game\objects\items\weapons\grenades\spawnergrenade.dm"

View File

@@ -226,6 +226,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

View File

@@ -79,8 +79,7 @@
..()
/obj/machinery/camera/bullet_act(var/obj/item/projectile/P)
if(P.damage_type == BRUTE || P.damage_type == BURN)
take_damage(P.damage)
take_damage(P.get_structure_damage())
/obj/machinery/camera/ex_act(severity)
if(src.invuln)

View File

@@ -56,10 +56,7 @@
return
/obj/machinery/computer/bullet_act(var/obj/item/projectile/Proj)
if(!(Proj.damage_type == BRUTE || Proj.damage_type == BURN))
return
if(prob(Proj.damage))
if(prob(Proj.get_structure_damage()))
set_broken()
..()

View File

@@ -156,12 +156,10 @@
/obj/machinery/door/bullet_act(var/obj/item/projectile/Proj)
..()
//Tasers and the like should not damage doors. Nor should TOX, OXY, CLONE, etc damage types
if(!(Proj.damage_type == BRUTE || Proj.damage_type == BURN))
return
var/damage = Proj.get_structure_damage()
// Emitter Blasts - these will eventually completely destroy the door, given enough time.
if (Proj.damage > 90)
if (damage > 90)
destroy_hits--
if (destroy_hits <= 0)
visible_message("<span class='danger'>\The [src.name] disintegrates!</span>")
@@ -173,9 +171,9 @@
new /obj/effect/decal/cleanable/ash(src.loc) // Turn it to ashes!
qdel(src)
if(Proj.damage)
if(damage)
//cap projectile damage so that there's still a minimum number of hits required to break the door
take_damage(min(Proj.damage, 100))
take_damage(min(damage, 100))

View File

@@ -375,7 +375,9 @@ var/list/turret_icons
die() //the death process :(
/obj/machinery/porta_turret/bullet_act(obj/item/projectile/Proj)
if(Proj.damage_type == HALLOSS)
var/damage = Proj.get_structure_damage()
if(!damage)
return
if(enabled)
@@ -387,8 +389,7 @@ var/list/turret_icons
..()
if((Proj.damage_type == BRUTE || Proj.damage_type == BURN))
take_damage(Proj.damage)
take_damage(damage)
/obj/machinery/porta_turret/emp_act(severity)
if(enabled)

View File

@@ -48,7 +48,7 @@
/obj/item/mecha_parts/mecha_equipment/weapon/proc/Fire(atom/A, atom/target, turf/aimloc)
var/obj/item/projectile/P = A
P.shot_from = src
P.shot_from = src.name
P.original = target
P.starting = P.loc
P.current = P.loc

View File

@@ -602,7 +602,7 @@
if(prob(15))
break //give a chance to exit early
Proj.on_hit(src)
Proj.on_hit(src) //on_hit just returns if it's argument is not a living mob so does this actually do anything?
return
/obj/mecha/ex_act(severity)

View File

@@ -40,7 +40,7 @@
/obj/effect/spider/bullet_act(var/obj/item/projectile/Proj)
..()
health -= Proj.damage
health -= Proj.get_structure_damage()
healthcheck()
/obj/effect/spider/proc/healthcheck()

View File

@@ -0,0 +1,60 @@
//Fragmentation grenade projectile
/obj/item/projectile/bullet/pellet/fragment
damage = 15
range_step = 2
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.
no_attack_log = 1
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."
icon_state = "frag"
var/num_fragments = 200 //total number of fragments produced by the grenade
var/fragment_damage = 15
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
//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
/obj/item/weapon/grenade/frag/prime()
..()
var/turf/O = get_turf(src)
if(!O) return
if(explosion_size)
explosion(O, -1, round(explosion_size/2), explosion_size, round(explosion_size/2), 0)
var/list/target_turfs = getcircle(O, spread_range)
var/fragments_per_projectile = round(num_fragments/target_turfs.len)
for(var/turf/T in target_turfs)
var/obj/item/projectile/bullet/pellet/fragment/P = new (O)
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)
//lying on a frag grenade while the grenade is on the ground causes you to absorb most of the shrapnel.
//you will most likely be dead, but others nearby will be spared the fragments that hit you instead.
if(M.lying && isturf(src.loc))
P.attack_mob(M, 0, 0)
else
P.attack_mob(M, 0, 100) //otherwise, allow a decent amount of fragments to pass
qdel(src)

View File

@@ -205,7 +205,7 @@
qdel(src)
/obj/effect/energy_net/bullet_act(var/obj/item/projectile/Proj)
health -= Proj.damage
health -= Proj.get_structure_damage()
healthcheck()
return 0

View File

@@ -190,11 +190,12 @@
qdel(src)
/obj/structure/closet/bullet_act(var/obj/item/projectile/Proj)
if(!(Proj.damage_type == BRUTE || Proj.damage_type == BURN))
var/proj_damage = Proj.get_structure_damage()
if(!proj_damage)
return
..()
damage(Proj.damage)
damage(proj_damage)
return

View File

@@ -86,7 +86,7 @@
shatter(M)
/obj/structure/closet/statue/bullet_act(var/obj/item/projectile/Proj)
health -= Proj.damage
health -= Proj.get_structure_damage()
check_health()
return

View File

@@ -29,7 +29,7 @@
/obj/structure/displaycase/bullet_act(var/obj/item/projectile/Proj)
health -= Proj.damage
health -= Proj.get_structure_damage()
..()
src.healthcheck()
return

View File

@@ -28,11 +28,10 @@
if(Proj.original != src && !prob(cover))
return PROJECTILE_CONTINUE //pass through
//Tasers and the like should not damage girders.
if(!(Proj.damage_type == BRUTE || Proj.damage_type == BURN))
var/damage = Proj.get_structure_damage()
if(!damage)
return
var/damage = Proj.damage
if(!istype(Proj, /obj/item/projectile/beam))
damage *= 0.4 //non beams do reduced damage

View File

@@ -65,14 +65,12 @@
/obj/structure/grille/bullet_act(var/obj/item/projectile/Proj)
if(!Proj) return
//Tasers and the like should not damage grilles.
if(!(Proj.damage_type == BRUTE || Proj.damage_type == BURN))
return
//Flimsy grilles aren't so great at stopping projectiles. However they can absorb some of the impact
var/damage = Proj.damage
var/damage = Proj.get_structure_damage()
var/passthrough = 0
if(!damage) return
//20% chance that the grille provides a bit more cover than usual. Support structure for example might take up 20% of the grille's area.
//If they click on the grille itself then we assume they are aiming at the grille itself and the extra cover behaviour is always used.
switch(Proj.damage_type)

View File

@@ -38,10 +38,10 @@
return 0
/obj/structure/inflatable/bullet_act(var/obj/item/projectile/Proj)
if(!(Proj.damage_type == BRUTE || Proj.damage_type == BURN))
return
var/proj_damage = Proj.get_structure_damage()
if(!proj_damage) return
health -= Proj.damage
health -= proj_damage
..()
if(health <= 0)
deflate(1)

View File

@@ -30,10 +30,8 @@
/obj/structure/mirror/bullet_act(var/obj/item/projectile/Proj)
if(!(Proj.damage_type == BRUTE || Proj.damage_type == BURN))
return
if(prob(Proj.damage * 2))
if(prob(Proj.get_structure_damage() * 2))
if(!shattered)
shatter()
else

View File

@@ -101,12 +101,11 @@
/obj/structure/window/bullet_act(var/obj/item/projectile/Proj)
//Tasers and the like should not damage windows.
if(!(Proj.damage_type == BRUTE || Proj.damage_type == BURN))
return
var/proj_damage = Proj.get_structure_damage()
if(!proj_damage) return
..()
take_damage(Proj.damage)
take_damage(proj_damage)
return

View File

@@ -54,12 +54,10 @@
else if(istype(Proj,/obj/item/projectile/ion))
burn(500)
// Tasers and stuff? No thanks. Also no clone or tox damage crap.
if(!(Proj.damage_type == BRUTE || Proj.damage_type == BURN))
return
var/proj_damage = Proj.get_structure_damage()
//cap the amount of damage, so that things like emitters can't destroy walls in one hit.
var/damage = min(Proj.damage, 100)
var/damage = min(proj_damage, 100)
take_damage(damage)
return

View File

@@ -25,7 +25,7 @@
var/yo = null
var/xo = null
var/current = null
var/obj/shot_from = null // the object which shot us
var/shot_from = "" // name of the object which shot us
var/atom/original = null // the target clicked (not necessarily where the projectile is headed). Should probably be renamed to 'target' or something.
var/turf/starting = null // the projectile's starting turf
var/list/permutated = list() // we've passed through these atoms, don't try to hit them again
@@ -89,6 +89,11 @@
return 0
return 1
/obj/item/projectile/proc/get_structure_damage()
if(damage_type == BRUTE || damage_type == BURN)
return damage
return 0
//return 1 if the projectile should be allowed to pass through after all, 0 if not.
/obj/item/projectile/proc/check_penetrate(var/atom/A)
return 1
@@ -111,7 +116,7 @@
p_y = between(0, p_y + rand(-radius, radius), world.icon_size)
//called to launch a projectile
/obj/item/projectile/proc/launch(atom/target, var/target_zone, var/x_offset=0, var/y_offset=0)
/obj/item/projectile/proc/launch(atom/target, var/target_zone, var/x_offset=0, var/y_offset=0, var/angle_offset=0)
var/turf/curloc = get_turf(src)
var/turf/targloc = get_turf(target)
if (!istype(targloc) || !istype(curloc))
@@ -127,7 +132,7 @@
def_zone = target_zone
spawn()
setup_trajectory(curloc, targloc, x_offset, y_offset) //plot the initial trajectory
setup_trajectory(curloc, targloc, x_offset, y_offset, angle_offset) //plot the initial trajectory
process()
return 0
@@ -143,7 +148,7 @@
loc = get_turf(user) //move the projectile out into the world
firer = user
shot_from = launcher
shot_from = launcher.name
silenced = launcher.silenced
return launch(target, target_zone, x_offset, y_offset)
@@ -173,6 +178,7 @@
result = target_mob.bullet_act(src, def_zone)
if(result == PROJECTILE_FORCE_MISS)
if(!silenced)
visible_message("<span class='notice'>\The [src] misses [target_mob] narrowly!</span>")
return 0
@@ -264,12 +270,10 @@
qdel(src)
return 1
/obj/item/projectile/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(air_group || (height==0)) return 1
/obj/item/projectile/ex_act()
return //explosions probably shouldn't delete projectiles
if(istype(mover, /obj/item/projectile))
return prob(95) //ha
else
/obj/item/projectile/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
return 1
/obj/item/projectile/process()

View File

@@ -69,22 +69,34 @@
damage = 20
//icon_state = "bullet" //TODO: would be nice to have it's own icon state
var/pellets = 4 //number of pellets
var/range_step = 2 //effective pellet count decreases every few tiles
var/base_spread = 90 //lower means the pellets spread more across body parts
var/range_step = 2 //projectile will lose a fragment each time it travels this distance. Can be a non-integer.
var/base_spread = 90 //lower means the pellets spread more across body parts. If zero then this is considered a shrapnel explosion instead of a shrapnel cone
var/spread_step = 10 //higher means the pellets spread more across body parts with distance
/obj/item/projectile/bullet/pellet/Bumped()
. = ..()
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)
/obj/item/projectile/bullet/pellet/proc/get_pellets(var/distance)
var/pellet_loss = round((distance - 1)/range_step) //pellets lost due to distance
return max(pellets - pellet_loss, 1)
/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
var/total_pellets = max(pellets - pellet_loss, 1)
var/total_pellets = get_pellets(distance)
var/spread = max(base_spread - (spread_step*distance), 0)
//shrapnel explosions miss prone mobs with a chance that increases with distance
var/prone_chance = 0
if(!base_spread)
prone_chance = max(spread_step*(distance - 2), 0)
var/hits = 0
for (var/i in 1 to total_pellets)
if(target_mob.lying && target_mob != original && prob(prone_chance))
continue
//pellet hits spread out across different zones, but 'aim at' the targeted zone with higher probability
//whether the pellet actually hits the def_zone or a different zone should still be determined by the parent using get_zone_with_miss_chance().
var/old_zone = def_zone
@@ -97,6 +109,20 @@
return 1
return 0
/obj/item/projectile/bullet/pellet/get_structure_damage()
var/distance = get_dist(loc, starting)
return ..() * get_pellets(distance)
/obj/item/projectile/bullet/pellet/Move()
. = ..()
//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 || !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 */
/obj/item/projectile/bullet/pistol

View File

@@ -146,7 +146,7 @@
/obj/structure/reagent_dispensers/fueltank/bullet_act(var/obj/item/projectile/Proj)
if(Proj.damage_type == BRUTE || Proj.damage_type == BURN)
if(Proj.get_structure_damage())
if(istype(Proj.firer))
message_admins("[key_name_admin(Proj.firer)] shot fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[loc.x];Y=[loc.y];Z=[loc.z]'>JMP</a>).")
log_game("[key_name(Proj.firer)] shot fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]).")

View File

@@ -9,7 +9,7 @@
reagents.add_reagent("coolant",1000)
/obj/structure/reagent_dispensers/coolanttank/bullet_act(var/obj/item/projectile/Proj)
if(Proj.damage_type == BRUTE || Proj.damage_type == BURN)
if(Proj.get_structure_damage())
if(!istype(Proj ,/obj/item/projectile/beam/lastertag) && !istype(Proj ,/obj/item/projectile/beam/practice) )
explode()

View File

@@ -60,7 +60,7 @@
..()
/obj/machinery/shield/bullet_act(var/obj/item/projectile/Proj)
health -= Proj.damage
health -= Proj.get_structure_damage()
..()
check_failure()
opacity = 1

View File

@@ -25,7 +25,7 @@
Stress(0.5 + severity)
/obj/effect/energy_field/bullet_act(var/obj/item/projectile/Proj)
Stress(Proj.damage / 10)
Stress(Proj.get_structure_damage() / 10)
/obj/effect/energy_field/proc/Stress(var/severity)
strength -= severity

View File

@@ -214,7 +214,7 @@
..()
/obj/machinery/shieldwallgen/bullet_act(var/obj/item/projectile/Proj)
storedpower -= 400 * Proj.damage
storedpower -= 400 * Proj.get_structure_damage()
..()
return
@@ -285,7 +285,7 @@
G = gen_primary
else
G = gen_secondary
G.storedpower -= 400 * Proj.damage
G.storedpower -= 400 * Proj.get_structure_damage()
..()
return

View File

@@ -278,10 +278,11 @@
// Then bring it inside to explode instantly upon landing on a valid turf.
var/proj_damage = Proj.get_structure_damage()
if(istype(Proj, /obj/item/projectile/beam))
power += Proj.damage * config_bullet_energy * CHARGING_FACTOR / POWER_FACTOR
power += proj_damage * config_bullet_energy * CHARGING_FACTOR / POWER_FACTOR
else
damage += Proj.damage * config_bullet_energy
damage += proj_damage * config_bullet_energy
return 0
/obj/machinery/power/supermatter/attack_robot(mob/user as mob)

View File

@@ -112,8 +112,7 @@
..()
/obj/vehicle/bullet_act(var/obj/item/projectile/Proj)
if (Proj.damage_type == BRUTE || Proj.damage_type == BURN)
health -= Proj.damage
health -= Proj.get_structure_damage()
..()
healthcheck()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB