Ship combat rework (#22566)

Makes ship combat significantly more destructive (and a bit deadlier),
especially with non-explosive projectiles.

**Additions:**
- A shell that penetrates a solid (dense) structure will cause spalling.
This spall does 2-30 damage, depending on range and has a high embed
chance. These embedded chunks are large enough to be ripped out by hand.
(But you probably shouldn't). The pilot's suit has 30 ballistic armor,
so a prepared bridge crew can survive a lot of spall.
- Adds a gun that shoots any ship weapon that admins can spawn. Useful
for testing, or for adminbus where they don't want to have to spawn a
whole overmap ship.
- Made several structures destructible, vending machines, computer
frames, grilles (these were just bugged) and watertanks (and their
children). These structures could just block infinite shots and were
common enough to serve as effective armor for the horizon. These now
spawn shrapnel (of their material if it's set), in addition to sheets of
steel when destroyed.
- Added support for negative maim chance. This allows chosen projectiles
to be prevented from decapitating. No more accidentally gibbing the
bridge crew's heads. (Unless they get hit with something really really
big).

**Balance:**
- All non-explosive anti ship rounds got significant buffs. Typically
2-3x Anti-Material numbers.
- Armour piercing anti-ship rounds got massively increased penetration
stats. Typically 4-8 from 1-2.
- Doors now get destroyed in less hits, if they are hit by anti-material
projectiles. Hits to destroy -= Antimaterial / 2
- Francisca frag shells now shoot less fragments but deal more damage.
394 projectiles for every shot was a bit laggy, now it's only 280.

**Shields:**
Shields now take more power. 13-14 megawatts for a 10 strength shield.
Just inside the ship's generation capacity with both the INDRA and
Supermatter running.

Instead of deleting any projectile they are hit by, shields now have
different failure modes.
- Firstly, a piercing projectile has it's penetration reduced by the
shield strength. Eg A shot that would go through 10 walls, only
penetrates 4 after going through a 6 strength shield.
- Secondly, a projectile has it's damage multiplied by 1 - shield
strength / 10, then has shield strength subtracted from it's damage. Eg.
A damage 100 projectile, hitting a 5 strength shield, has it's damaged
reduced to 50, (100 * 0.5), then to 45. (50-5)
- Thirdly, an explosive projectile (that does not penetrate per the
first step) does not explode, but instead deals it's full damage to the
shield.

Hits to the shield now also damage the field itself, not just the shield
tile. As such, a very big hit on the shield can bring the whole thing
down, requiring an engineer to reset it.

Shields can now be upgraded with research components. Additionally,
shield generators can now support more than one capacitor. The
roundstart capacitor on the Horizon is only good enough to support a 6
strength shield.
The upgrades increase efficiency, strength loss over time and charge
speed.

**Overmap Targetting**
Previously, shells would spawn within 20 tiles of their target and aim
directly forwards. This was bugged for shots with burst, which would
instead all aim inwards and mostly miss. This is the reason the Grauwolf
(in addition to the explosion bug below) and Fransisca felt so useless,
only one of every shell in a burst would hit.

Shots now spawn at the map edge, based on the orientation of the
projectile and ship. Diagonals are now accounted for.
Shots are then aimed directly at the target selected on the console,
with 3 degrees of dispersion.

**Bugfixes:**
- Fixes lots of explosions that didn't pass a turf to the explosions
subsystem. This caused the explosion not to happen if the thing hit got
qdeled before the subsystem could get the turf to spawn the explosion
on.
- Embedded shrapnel no longer gets the initial projectile name, this
prevents it getting named shrapnelshrapnel.
- Fixes a bunch of projectile piercing bugs, mostly with grilles and
windows. Shots now go past window frames without hitting them.
- Fixes shields not having a click delay when attacked.

---------

Signed-off-by: FenodyreeAv <fenodyree.av@gmail.com>
Co-authored-by: Matt Atlas <mattiathebest2000@hotmail.it>
This commit is contained in:
FenodyreeAv
2026-06-03 17:52:13 +01:00
committed by GitHub
parent fb3189f542
commit cf585ef505
49 changed files with 859 additions and 251 deletions
@@ -211,7 +211,7 @@
P.dir = S.dir
P.icon_state = overmap_icon_state
P.speed = get_speed()
P.entry_target = entry_point
P.submap_target = entry_point
forceMove(P)
log_and_message_admins("A projectile ([name]) has entered the Overmap! (<A href='byond://?_src_=holder;adminplayerobservecoodjump=1;X=[P.x];Y=[P.y];Z=[P.z]'>JMP</a>)")
return TRUE
@@ -227,11 +227,87 @@
anti_materiel_potential = 3
impact_sounds = list(BULLET_IMPACT_MEAT = SOUNDS_BULLET_MEAT, BULLET_IMPACT_METAL = SOUNDS_BULLET_METAL)
accuracy = 100
projectile_piercing = PASSMOB|PASSDOORS|PASSGLASS|PASSCLOSEDTURF|PASSWINDOW|PASSMACHINE|PASSBLOB|PASSFLAPS|PASSVEHICLE //It's a ship weapon let it try to penetrate everything.
projectile_piercing = PASSGLASS|PASSGRILLE|PASSBLOB|PASSMOB|PASSCLOSEDTURF|PASSMACHINE|PASSSTRUCTURE|PASSFLAPS|PASSDOORS|PASSVEHICLE|PASSWINDOW|PASSSHIELD //It's a ship weapon let it try to penetrate everything.
pierce_decay_damage = 0.95 //Ship weapon projectiles don't lose much damage on pierce by default, but this can be set per projectile.
///This is passed to explosion(), it is stored here for when projectiles hit shields and need to damage the shield as if they had exploded.
var/list/explosion_strength = list(0, 0, 0)
///The last thing this projectile pierced. Used for spalling effects in on_hit()
var/atom/last_thing_pierced = null
var/obj/item/ship_ammunition/ammo
var/primed = FALSE
var/hit_target = FALSE //First target we hit. Used to report if a hit was successful.
///First target we hit. Used to report if a hit was successful.
var/hit_target = FALSE
/obj/projectile/ship_ammo/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE) //When the projectile moves from a dense tile to a non-dense tile.
. = ..()
if(last_thing_pierced)
var/turf/entered_turf = isturf(loc) ? loc : get_turf(loc)
if(!(entered_turf.check_density(FALSE, TRUE))) //Checks for mobs so we don't spawn spalling inside the poor bridge crew, getting hit by the shell is bad enough.
handle_spalling(entered_turf, movement_dir, last_thing_pierced)
last_thing_pierced = null
/**
* This creates a cone of shrapnel whenever a projectile pierces something dense and then crosses into a non-dense turf.
* This means that it won't spawn all the shrapnel inside the next thing it penetrates, to simulate spalling.
* All of of the projectile stats are set custom here, in future it might be good to set them dynmically based on the material of the thing pierced.
* Too many things don't have a material set, so that will have to wait.
*/
/obj/projectile/ship_ammo/proc/handle_spalling(atom/source, shrapnel_dir, thing_pierced)
set waitfor = FALSE
src.visible_message(SPAN_DANGER("Huge chunks of shrapnel spray out from \the [thing_pierced] as \the [src] punches through!"))
var/turf/O = get_turf(src)
var/list/target_turfs = list()
target_turfs += get_step(O, shrapnel_dir)
target_turfs += get_step(O, turn(shrapnel_dir, -45))
target_turfs += get_step(O, turn(shrapnel_dir, 45))
target_turfs += get_step(get_step(O, turn(shrapnel_dir, -45)), shrapnel_dir)
target_turfs += get_step(get_step(O, turn(shrapnel_dir, 45)), shrapnel_dir)
for(var/turf/T in target_turfs)
var/obj/projectile/bullet/pellet/fragment/spall/P
if(istype(thing_pierced, /obj/structure/window))
P = new /obj/projectile/bullet/pellet/fragment/spall/glass(O)
else if(istype(thing_pierced, /obj/structure/grille) || istype(thing_pierced, /obj/structure/window_frame))
P = new /obj/projectile/bullet/pellet/fragment/spall/metalrod(O)
else if(istype(thing_pierced, /obj/structure/machinery/door/airlock))
var/obj/structure/machinery/door/airlock/D = thing_pierced
if(D.window_material && D.window_material == SSmaterials.get_material_by_name(MATERIAL_GLASS))
P = new /obj/projectile/bullet/pellet/fragment/spall/glass(O)
else
P = new /obj/projectile/bullet/pellet/fragment/spall(O)
else
P = new /obj/projectile/bullet/pellet/fragment/spall(O)
P.preparePixelProjectile(T, src)
P.firer = src
P.fired_from = thing_pierced
P.fire()
return TRUE
/obj/projectile/bullet/pellet/fragment/spall
damage = 2 //Between 30 and 3 damage, depending on how close you are. Pilot suits are 30 ballistic armour, so they should survive a lot of spall.
pellets = 15
armor_penetration = 10
range_step = 1
range = 15
embed_chance = 80 //Make the people hit pull big chunks of shrapnel out by hand.
maim_rate = -1 //We don't want this to decapitate people in the unlucky event they get hit by the shell (or shell explosion) directly, then more shrapnel afterwards.
anti_materiel_potential = 2 //Enough to break windows.
name = "spall"
shrapnel_type = /obj/item/material/shard/shrapnel/large
/obj/projectile/bullet/pellet/fragment/spall/glass
damage = 1
armor_penetration = 5
name = "glass"
shrapnel_type = /obj/item/material/shard
/obj/projectile/bullet/pellet/fragment/spall/metalrod
damage = 3
armor_penetration = 15
name = "metal rod"
shrapnel_type = /obj/item/stack/rods
/obj/projectile/ship_ammo/Destroy()
ammo = null
@@ -261,6 +337,14 @@
)
if(ammo && ammo.origin)
ammo.origin.signal_hit(hit_data)
if(istype(target, /turf/simulated/wall) || istype(target, /obj/structure/machinery/door)) //Stores the last thing we pierced for spalling purposes.
last_thing_pierced = target
else if (istype(target, /obj/structure)) //If it is a structure on a dense turf, that uses health,
if (target.should_use_health)
var/turf/T = get_turf(target)
if (T.check_density(FALSE, TRUE))
last_thing_pierced = target
return ..()
/obj/projectile/ship_ammo/proc/on_translate(var/turf/entry_turf, var/target_turf) //This proc is called when the projectile enters a new ship's overmap zlevel.
@@ -273,6 +357,5 @@
pellet.ammo.origin = ammo.origin
pellet.ammo.impact_type = ammo.impact_type
pellet.dir = dir
var/turf/front_turf = get_step(pellet, pellet.dir)
pellet.preparePixelProjectile(target_turf, front_turf)
pellet.preparePixelProjectile(target_turf, pellet, deviation = 3) //Deviation can be tuned. These spawn at the map edge so small deviations matter a lot.
pellet.fire()
@@ -9,8 +9,8 @@
var/obj/item/ship_ammunition/ammunition
/// The target is the actual overmap object we're hitting.
var/atom/target
/// The entry target is where the projectile itself is going to spawn in world.
var/obj/entry_target
/// The submap target is passed by the targetting console, it is the landmark selected on that console. This is the point on the submap (eg. The Horizon's Bridge) that the projectile will aim for when it enters.
var/obj/submap_target
var/range = OVERMAP_PROJECTILE_RANGE_MEDIUM
var/current_range_counter = 0
// A projectile with 0 speed does not move. Note that this is the 'lag' variable on walk_towards! Lower speed is better.
@@ -65,81 +65,132 @@
/obj/effect/overmap/projectile/Destroy()
ammunition = null
target = null
entry_target = null
walk(src, 0)
submap_target = null
return ..()
///Gets the turf the projectile should enter on. If it's diagnonal it spawns in the corner, if it's orthogonal it spawns on the map edge relative to the target landmark.
/obj/effect/overmap/projectile/proc/get_entry_turf(atom/target_landmark, direction)
var/turf/target = locate(target_landmark.x, target_landmark.y, target_landmark.z)
if(!target_landmark || !target)
return 0
switch(direction)
if(NORTH|WEST)
target = get_ranged_target_turf(locate(1, world.maxy, target.z), direction, max(8, 2 * ammunition.burst)) //We offset the target turf from the edge by 8 (one screen), or twice the burst size. Bursts get their positions randomized up to twice the burst size.
if(NORTH|EAST)
target = get_ranged_target_turf(locate(world.maxx, world.maxy, target.z), direction, max(8, 2 * ammunition.burst))
if(SOUTH|WEST)
target = get_ranged_target_turf(locate(1, 1, target.z), direction, max(8, 2 * ammunition.burst))
if(SOUTH|EAST)
target = get_ranged_target_turf(locate(world.maxx, 1, target.z), direction, max(8, 2 * ammunition.burst))
if(NORTH)
target = get_ranged_target_turf(locate(target.x, world.maxy, target.z), direction, max(8, 2 * ammunition.burst))
if(SOUTH)
target = get_ranged_target_turf(locate(target.x, 1, target.z), direction, max(8, 2 * ammunition.burst))
if(EAST)
target = get_ranged_target_turf(locate(world.maxx, target.y, target.z), direction, max(8, 2 * ammunition.burst))
if(WEST)
target = get_ranged_target_turf(locate(1, target.y, target.z), direction, max(8, 2 * ammunition.burst))
return target
/obj/effect/overmap/projectile/proc/prepare_for_entry()
moving = FALSE
entering = FALSE
walk(src, 0)
///Checks if we can hit the thing we just bumped into. If we can, do the initial setup for the projectile entering the submab and continue.
/obj/effect/overmap/projectile/proc/check_entry()
. = FALSE
if(!ammunition)
return
var/turf/T = get_turf(src)
for(var/obj/effect/overmap/A in T)
if(ammunition && A == ammunition.origin)
if(ammunition && A == ammunition.origin) //Don't collide with the thing that fired us.
continue
if(istype(A, /obj/effect/overmap/visitable))
var/obj/effect/overmap/visitable/V = A
if((V.check_ownership(entry_target)) || (V == target)) //Target spotted!
if((V.check_ownership(submap_target)) || (V == target)) //If the visitable is owned by the target landmark, or is the target itself, we can hit it.
var/turf/target_turf = get_turf(submap_target)
var/obj/projectile/ship_ammo/widowmaker = new ammunition.original_projectile.type
prepare_for_entry()
widowmaker.ammo = ammunition
qdel(ammunition.original_projectile) //No longer needed.
ammunition.original_projectile = widowmaker
widowmaker.primed = TRUE
if(istype(V, /obj/effect/overmap/visitable/sector/exoplanet) && (ammunition.overmap_behaviour & SHIP_AMMO_CAN_HIT_PLANETS))
. = TRUE
//Manually stopping because this proc needs to sleep for a bit.
prepare_for_entry()
var/obj/projectile/ship_ammo/widowmaker = new ammunition.original_projectile.type
widowmaker.ammo = ammunition
qdel(ammunition.original_projectile) //No longer needed.
var/turf/laze = get_turf(entry_target)
ammunition.original_projectile = widowmaker
playsound(laze, 'sound/weapons/gunshot/ship_weapons/orbital_travel.ogg', 60)
laze.visible_message(SPAN_DANGER("<font size=6>A bright star is getting closer from the sky...!</font>"))
sleep(11 SECONDS) //Let the sound play!
widowmaker.primed = TRUE
widowmaker.forceMove(entry_target)
widowmaker.on_hit(laze, is_landmark_hit = TRUE)
log_and_message_admins("A projectile ([name]) has entered a z-level at [entry_target.name]! (<A href='byond://?_src_=holder;adminplayerobservecoodjump=1;X=[widowmaker.x];Y=[widowmaker.y];Z=[widowmaker.z]'>JMP</a>)")
say_dead_direct("A projectile ([name]) has entered a z-level at [entry_target.name]!")
qdel(widowmaker)
qdel(src)
else if(istype(V, /obj/effect/overmap/visitable) && (ammunition.overmap_behaviour & SHIP_AMMO_CAN_HIT_VISITABLES))
. = TRUE
if(istype(V, /obj/effect/overmap/visitable/ship))
var/obj/effect/overmap/visitable/ship/VS = V
if(istype(ammunition.origin, /obj/effect/overmap/visitable/ship))
var/naval_heading = SSatlas.headings_to_naval["[VS.dir]"]["[ammunition.heading]"]
var/corrected_heading = SSatlas.naval_to_dir["[VS.fore_dir]"][naval_heading]
ammunition.heading = corrected_heading
else //if it's not a ship it doesn't have a fore direction, so we need to autocorrect
ammunition.heading = entry_target.dir
prepare_for_entry()
var/obj/projectile/ship_ammo/widowmaker = new ammunition.original_projectile.type
widowmaker.ammo = ammunition
qdel(ammunition.original_projectile) //No longer needed.
ammunition.original_projectile = widowmaker
widowmaker.primed = TRUE
var/turf/entry_turf_initial = get_ranged_target_turf(entry_target, REVERSE_DIR(entry_target.dir), 20)
var/entry_dir_choice = (dir & NORTH) || (dir & SOUTH) ? list(EAST, WEST) : list(NORTH, SOUTH)
var/turf/entry_turf = get_ranged_target_turf(entry_turf_initial, entry_dir_choice, 5)
widowmaker.forceMove(entry_turf)
widowmaker.dir = ammunition.heading
var/turf/target_turf = get_step(widowmaker, widowmaker.dir)
widowmaker.on_translate(entry_turf, target_turf)
log_and_message_admins("A projectile ([widowmaker.name]) has entered a z-level at [entry_target.name], with direction [dir2text(widowmaker.dir)]! (<A href='byond://?_src_=holder;adminplayerobservecoodjump=1;X=[widowmaker.x];Y=[widowmaker.y];Z=[widowmaker.z]'>JMP</a>)")
say_dead_direct("A projectile ([widowmaker.name]) has entered a z-level at [entry_target.name], with direction [dir2text(widowmaker.dir)]!")
widowmaker.preparePixelProjectile(target_turf, entry_turf)
widowmaker.fired_from = src
widowmaker.fire()
qdel(src)
if(istype(A, /obj/effect/overmap/event))
return check_entry_exoplanet(widowmaker, target_turf, target_turf)
else if(istype(V, /obj/effect/overmap/visitable/ship) && (ammunition.overmap_behaviour & SHIP_AMMO_CAN_HIT_VISITABLES))
return check_entry_ship(widowmaker, target_turf, V)
else if(ammunition.overmap_behaviour & SHIP_AMMO_CAN_HIT_VISITABLES)
return check_entry_visitable(widowmaker, target_turf)
if(istype(A, /obj/effect/overmap/event)) //Check hazards last, in case a ship is hiding in a hazard.
var/obj/effect/overmap/event/EV = A
if(EV.can_be_destroyed && (ammunition.overmap_behaviour & SHIP_AMMO_CAN_HIT_HAZARDS))
. = TRUE
qdel(EV)
qdel(src)
///Handle bombardment of planets. Spawn the shot in a random open tile near the target and fire the projectile at it. If there is no open tile, call on_hit directly. This allows non-explosive weapons to strafe targets.
/obj/effect/overmap/projectile/proc/check_entry_exoplanet(obj/projectile/ship_ammo/widowmaker, turf/entry_turf, turf/laze)
. = TRUE
playsound(laze, 'sound/weapons/gunshot/ship_weapons/orbital_travel.ogg', 60)
laze.visible_message(SPAN_DANGER("<font size=6>A bright star is getting closer from the sky...!</font>"))
sleep(11 SECONDS) //Let the sound play!
var/turf/nearest_valid_turf
if (!(nearest_valid_turf = get_random_turf_in_range(entry_turf, 1, 5, TRUE, FALSE))) //Get a random empty turf between 1 and 5 tiles away from the target, if this fails call on_hit directly on the target turf.
widowmaker.forceMove(submap_target)
widowmaker.on_hit(laze, is_landmark_hit = TRUE)
log_and_message_admins("A ([widowmaker.name]) landed at ([entry_turf.x], [entry_turf.y], [entry_turf.z]) aimed at [submap_target.name]! (<A href='byond://?_src_=holder;adminplayerobservecoodjump=1;X=[widowmaker.x];Y=[widowmaker.y];Z=[widowmaker.z]'>JMP</a>)")
say_dead_direct("A ([widowmaker.name]) landed at ([entry_turf.x], [entry_turf.y], [entry_turf.z]) aimed at [submap_target.name]!")
qdel(widowmaker)
qdel(src)
else //If there's an empty turf, spawn our projectile there and aim it at the target.
widowmaker.on_translate(entry_turf, nearest_valid_turf)
log_and_message_admins("A ([widowmaker.name]) landed at ([entry_turf.x], [entry_turf.y], [entry_turf.z]) aimed at [submap_target.name]! (<A href='byond://?_src_=holder;adminplayerobservecoodjump=1;X=[widowmaker.x];Y=[widowmaker.y];Z=[widowmaker.z]'>JMP</a>)")
say_dead_direct("A ([widowmaker.name]) landed at ([entry_turf.x], [entry_turf.y], [entry_turf.z]) aimed at [submap_target.name]!")
widowmaker.preparePixelProjectile(nearest_valid_turf, entry_turf)
widowmaker.fired_from = src
widowmaker.fire()
qdel(src)
//Handle hitting ships. This requires translating the projectile's direction to account for the relative rotation of the ship. The projectile then spawns at the edge of the map, aimed at the target landmark.
/obj/effect/overmap/projectile/proc/check_entry_ship(obj/projectile/ship_ammo/widowmaker, turf/target_turf, obj/effect/overmap/visitable/ship/VS)
var/shot_direction = src.dir
var/naval_heading = SSatlas.headings_to_naval["[VS.dir]"]["[shot_direction]"]
var/corrected_heading = SSatlas.naval_to_dir["[VS.fore_dir]"][naval_heading]
shot_direction = corrected_heading
var/turf/entry_turf = get_entry_turf(submap_target, REVERSE_DIR(shot_direction))
widowmaker.forceMove(entry_turf)
widowmaker.dir = shot_direction
widowmaker.on_translate(entry_turf, target_turf)
log_and_message_admins("A ([widowmaker.name]) arrived [naval_heading] of \the [target] at ([entry_turf.x], [entry_turf.y], [entry_turf.z]) aimed at [submap_target.name], with direction [dir2text(shot_direction)]! (<A href='byond://?_src_=holder;adminplayerobservecoodjump=1;X=[submap_target.x];Y=[submap_target.y];Z=[submap_target.z]'>JMP</a>)")
say_dead_direct("A ([widowmaker.name]) arrived [naval_heading] of \the [target] at ([entry_turf.x], [entry_turf.y], [entry_turf.z]) aimed at [submap_target.name], with direction [dir2text(shot_direction)]! (<A href='byond://?_src_=holder;adminplayerobservecoodjump=1;X=[submap_target.x];Y=[submap_target.y];Z=[submap_target.z]'>JMP</a>)")
widowmaker.preparePixelProjectile(target_turf, entry_turf)
widowmaker.fired_from = src
widowmaker.fire()
qdel(src)
return TRUE
///Handle hitting visitables that can't spin, such as asteroids and stations. Otherwise identical to hitting a ship.
/obj/effect/overmap/projectile/proc/check_entry_visitable(obj/projectile/ship_ammo/widowmaker, turf/target_turf)
var/shot_direction = src.dir
var/turf/entry_turf = get_entry_turf(submap_target, REVERSE_DIR(shot_direction))
widowmaker.forceMove(entry_turf)
widowmaker.dir = shot_direction
widowmaker.on_translate(entry_turf, target_turf)
log_and_message_admins("A ([widowmaker.name]) flew in at ([entry_turf.x], [entry_turf.y], [entry_turf.z]) aimed at [submap_target.name], with direction [dir2text(shot_direction)]! (<A href='byond://?_src_=holder;adminplayerobservecoodjump=1;X=[submap_target.x];Y=[submap_target.y];Z=[submap_target.z]'>JMP</a>)")
say_dead_direct("A ([widowmaker.name]) flew in at ([entry_turf.x], [entry_turf.y], [entry_turf.z]) aimed at [submap_target.name], with direction [dir2text(shot_direction)]! (<A href='byond://?_src_=holder;adminplayerobservecoodjump=1;X=[submap_target.x];Y=[submap_target.y];Z=[submap_target.z]'>JMP</a>)")
widowmaker.preparePixelProjectile(target_turf, entry_turf)
widowmaker.fired_from = src
widowmaker.fire()
qdel(src)
return TRUE
/obj/effect/overmap/projectile/proc/move_to()
if(isnull(target) || !speed)
walk(src, 0)
@@ -47,7 +47,7 @@
icon = 'icons/obj/guns/ship/ship_ammo_autocannon.dmi'
icon_state = "autocannon_frag"
overmap_icon_state = "cannon_salvo"
impact_type = SHIP_AMMO_IMPACT_HE
impact_type = SHIP_AMMO_IMPACT_FRAG
ammunition_flags = SHIP_AMMO_FLAG_INFLAMMABLE|SHIP_AMMO_FLAG_VERY_HEAVY
caliber = SHIP_CALIBER_60MM
burst = 8
@@ -59,7 +59,8 @@
icon_state = "small"
damage = 100
armor_penetration = 60
penetrating = 3
penetrating = 8
anti_materiel_potential = 6
/obj/projectile/ship_ammo/autocannon/he
name = "60mm HE shell"
@@ -67,6 +68,7 @@
damage = 80
armor_penetration = 30
penetrating = 0 // Explodes on the hull.
explosion_strength = list(0, 2, 3)
/obj/projectile/ship_ammo/autocannon/frag
name = "60mm fragmentation shell"
@@ -77,8 +79,8 @@
/obj/projectile/ship_ammo/autocannon/he/on_hit(atom/target, blocked, def_zone, is_landmark_hit)
. = ..()
explosion(target, 0, 2, 3)
explosion(get_turf(target), explosion_strength[1], explosion_strength[2], explosion_strength[3])
/obj/projectile/ship_ammo/autocannon/frag/on_hit(atom/target, blocked, def_zone, is_landmark_hit)
fragem(target, 70, 70, 1, 2, 15, 5, TRUE)
..()
fragem(src, 70, 70, 1, 2, 12, 4, TRUE, spread_range = 5) //Targets 5 tiles around the impact site and shoots a projectile at them.
. = ..()
@@ -27,14 +27,16 @@
damage = 10000
armor_penetration = 1000
penetrating = 1
explosion_strength = list(2, 5, 7)
/obj/projectile/ship_ammo/blaster/on_hit(atom/target, blocked, def_zone, is_landmark_hit)
. = ..()
var/turf/epicenter = get_turf(target)
if(ismob(target))
var/mob/M = target
M.visible_message(SPAN_DANGER("<font size=5>\The [src] disintegrates [M]'s chest and blasts them into pieces!</font>"))
if(isturf(target) || isobj(target))
explosion(target, 2, 5, 7)
explosion(epicenter, explosion_strength[1], explosion_strength[2], explosion_strength[3])
/obj/structure/machinery/ammunition_loader/blaster
name = "mining blaster loader"
@@ -119,10 +119,11 @@
damage = 150
armor_penetration = 75
penetrating = 0
explosion_strength = list(1, 3, 6)
/obj/projectile/ship_ammo/bruiser/he/on_hit(atom/target, blocked, def_zone, is_landmark_hit)
. = ..()
explosion(target, 1, 3, 6)
explosion(get_turf(target), explosion_strength[1], explosion_strength[2], explosion_strength[3])
/obj/projectile/ship_ammo/bruiser/real/he
name = "178mm shell"
@@ -130,6 +131,7 @@
damage = 350
armor_penetration = 125
penetrating = 0
explosion_strength = list(3, 6, 8)
/obj/projectile/ship_ammo/bruiser/real/ap
name = "178mm shell"
@@ -137,6 +139,7 @@
damage = 250
armor_penetration = 250
penetrating = 2
explosion_strength = list(0, 2, 4)
/obj/projectile/ship_ammo/bruiser/real/canister
damage = 40
@@ -157,10 +160,11 @@
/obj/projectile/ship_ammo/bruiser/real/on_hit(atom/target, blocked, def_zone, is_landmark_hit)
. = ..()
var/turf/epicenter = get_turf(target)
if(ammo.impact_type == SHIP_AMMO_IMPACT_HE)
explosion(target, 3, 6, 8)
explosion(epicenter, explosion_strength[1], explosion_strength[2], explosion_strength[3])
if(ammo.impact_type == SHIP_AMMO_IMPACT_AP)
explosion(target, 0, 2, 4)
explosion(epicenter, explosion_strength[1], explosion_strength[2], explosion_strength[3])
/obj/projectile/ship_ammo/bruiser/real/beehive/on_hit(atom/target, blocked, def_zone, is_landmark_hit)
. = ..()
@@ -27,14 +27,16 @@
damage = 10000
armor_penetration = 1000
penetrating = 50
explosion_strength = list(1, 5, 3)
/obj/projectile/ship_ammo/coilgun/on_hit(atom/target, blocked, def_zone, is_landmark_hit)
. = ..()
var/turf/epicenter = get_turf(target)
if(ismob(target))
var/mob/M = target
M.visible_message(SPAN_DANGER("<font size=5>\The [src] blows [M]'s chest apart and punches straight through!</font>"))
if(isturf(target) || isobj(target))
explosion(target, 1, 5, 3)
explosion(epicenter, explosion_strength[1], explosion_strength[2], explosion_strength[3])
/obj/structure/machinery/ammunition_loader/sol
icon_state = "ammo_loader_sol"
@@ -42,7 +42,7 @@
name_override = "40mm FRAG burst"
desc = "A box of fragmentation shells for use in a Francisca rotary gun."
icon_state = "box_inc"
impact_type = SHIP_AMMO_IMPACT_HE
impact_type = SHIP_AMMO_IMPACT_FRAG
projectile_type_override = /obj/projectile/ship_ammo/francisca/frag
/obj/projectile/ship_ammo/francisca
@@ -50,20 +50,24 @@
icon_state = "small"
damage = 50
armor_penetration = 50
penetrating = 2
penetrating = 3
anti_materiel_potential = 10
/obj/projectile/ship_ammo/francisca/ap
name = "40mm AP shell"
damage = 30
armor_penetration = 100
penetrating = 4
penetrating = 8
anti_materiel_potential = 8
/obj/projectile/ship_ammo/francisca/frag
name = "40mm FRAG shell"
icon_state = "small"
damage = 30
armor_penetration = 50
penetrating = 1
anti_materiel_potential = 6
/obj/projectile/ship_ammo/francisca/frag/on_hit(atom/target, blocked, def_zone, is_landmark_hit)
fragem(src, 70, 70, 1, 2, 10, 4, TRUE)
fragem(src, 70, 70, 1, 2, 12, 4, TRUE, spread_range = 5) //Targets 5 tiles around the impact site and shoots a projectile at them.
. = ..()
@@ -36,16 +36,21 @@
damage = 100
armor_penetration = 50
penetrating = 0
anti_materiel_potential = 6
explosion_strength = list(0, 2, 4)
/obj/projectile/ship_ammo/grauwolf/on_hit(atom/target, blocked, def_zone, is_landmark_hit)
. = ..()
var/turf/epicenter = get_turf(target)
if(ammo && ammo.impact_type == SHIP_AMMO_IMPACT_HE)
explosion(target, 0, 2, 4)
explosion(epicenter, explosion_strength[1], explosion_strength[2], explosion_strength[3])
else
explosion(target, 0, 1, 2)
explosion(epicenter, explosion_strength[1], explosion_strength[2], explosion_strength[3])
/obj/projectile/ship_ammo/grauwolf/ap
name = "armor-piercing flak"
damage = 50
armor_penetration = 50
penetrating = 2
penetrating = 8
anti_materiel_potential = 8
explosion_strength = list(0, 1, 2)
@@ -26,14 +26,16 @@
damage = 10000
armor_penetration = 1000
penetrating = 1
explosion_strength = list(3, 6, 8)
/obj/projectile/ship_ammo/lammergeier/on_hit(atom/target, blocked, def_zone, is_landmark_hit)
. = ..()
var/turf/epicenter = get_turf(target)
if(ismob(target))
var/mob/M = target
M.visible_message(SPAN_DANGER("<font size=5>\The [src] blows [M]'s chest apart and punches straight through!</font>"))
if(isturf(target) || isobj(target))
explosion(target, 3, 6, 8)
explosion(epicenter, explosion_strength[1], explosion_strength[2], explosion_strength[3])
/obj/structure/machinery/ammunition_loader/lammergeier
name = "typhoon cannon loader"
@@ -159,10 +159,10 @@
M.visible_message(SPAN_DANGER("<font size=6>[M] evaporates as they are engulfed by the beam!</font>"))
M.dust()
return
explosion(target, 6, 6, 6)
explosion(get_turf(target), 6, 6, 6)
else
target.visible_message(SPAN_DANGER("<font size=6>A giant, purple laser descends from the sky!</font>"))
explosion(target, 30, 30, 30)
explosion(get_turf(target), 30, 30, 30)
/obj/structure/machinery/zat_lever
name = "activation lever"
@@ -28,11 +28,13 @@
damage = 10000
armor_penetration = 1000
penetrating = 1
explosion_strength = list(2, 3, 4)
/obj/projectile/ship_ammo/coilgun/light/on_hit(atom/target, blocked, def_zone, is_landmark_hit)
. = ..()
var/turf/epicenter = get_turf(target)
if(ismob(target))
var/mob/M = target
M.visible_message(SPAN_DANGER("<font size=5>\The [src] blows [M]'s chest apart and punches straight through!</font>"))
if(isturf(target) || isobj(target))
explosion(target, 2, 3, 4)
explosion(epicenter, explosion_strength[1], explosion_strength[2], explosion_strength[3])
@@ -26,6 +26,7 @@
/obj/projectile/ship_ammo/longbow/on_hit(atom/target, blocked, def_zone, is_landmark_hit)
. = ..()
var/turf/epicenter = get_turf(target)
if(ismob(target))
var/mob/M = target
M.visible_message(SPAN_DANGER("<font size=5>\The [src] blows [M] apart and punches straight through!</font>"))
@@ -39,13 +40,13 @@
qdel(target)
penetrated = TRUE
else
explosion(target, 4, 8, 12)
explosion(epicenter, 4, 8, 12)
qdel(src)
if(SHIP_AMMO_IMPACT_HE)
explosion(target, 6, 8, 10)
explosion(epicenter, 6, 8, 10)
if(SHIP_AMMO_IMPACT_BUNKERBUSTER)
target.visible_message(SPAN_DANGER("<font size=5>\The [src] punches straight through \the [target]!</font>"))
explosion(target, 1, 2, 4)
explosion(epicenter, 1, 2, 4)
target.ex_act(1)
if(!QDELING(target) && target.density)
qdel(target)
@@ -50,6 +50,7 @@
damage = 40
armor_penetration = 40
penetrating = 0
explosion_strength = list(0, 2, 3)
/obj/projectile/ship_ammo/nephilim/ap
@@ -60,4 +61,4 @@
/obj/projectile/ship_ammo/nephilim/he/on_hit(atom/target, blocked, def_zone, is_landmark_hit)
. = ..()
explosion(target, 0, 2, 3)
explosion(get_turf(target), explosion_strength[1], explosion_strength[2], explosion_strength[3])