Pixel Projectiles (#4105)

Projectile hitscan effects moved to /effects/projectiles folder.
Most of projectile firing code and some of impact code refactored, and projectile hitscan effects refactored with ported TGcode (which is mostly TGcode I wrote anyways 😎)
Projectiles can now be fired at any angle, instead of just at the center of turfs!
Projectiles are now [in theory] 100% accurate down to 0.001 of a pixel.
This commit is contained in:
kevinz000
2018-03-10 07:13:24 -08:00
committed by Erki
parent 8723dce8b7
commit c000070a5b
58 changed files with 1105 additions and 861 deletions

View File

@@ -41,10 +41,11 @@
#define FLEXIBLEMATERIAL 0x20 // At the moment, masks with this flag will not prevent eating even if they are covering your face.
// Flags for pass_flags.
#define PASSTABLE 0x1
#define PASSGLASS 0x2
#define PASSGRILLE 0x4
#define PASSDOORHATCH 0x8
#define PASSTABLE 0x1
#define PASSGLASS 0x2
#define PASSGRILLE 0x4
#define PASSDOORHATCH 0x8
#define PASSMOB 0x10
// Bitmasks for the flags_inv variable. These determine when a piece of clothing hides another, i.e. a helmet hiding glasses.
// WARNING: The following flags apply only to the external suit!

View File

@@ -34,6 +34,7 @@
#define SS_PRIORITY_SMOOTHING 35 // Smooth turf generation.
#define SS_PRIORITY_ORBIT 30 // Orbit datum updates.
#define SS_PRIORITY_ICON_UPDATE 20 // Queued icon updates. Mostly used by APCs and tables.
#define SS_PRIORITY_PROJECTILES 10 // Projectile processing!
// Normal
#define SS_PRIORITY_TICKER 200 // Gameticker.
@@ -44,7 +45,7 @@
#define SS_PRIORITY_MACHINERY 95 // Machinery + powernet ticks.
#define SS_PRIORITY_CHEMISTRY 90 // Multi-tick chemical reactions.
#define SS_PRIORITY_SHUTTLE 85 // Shuttle movement.
#define SS_PRIORITY_CALAMITY 75 // Singularity, Tesla, Nar'sie, blob, etc.
#define SS_PRIORITY_CALAMITY 75 // Singularity, Tesla, Nar'sie, blob, etc.
#define SS_PRIORITY_EVENT 70
#define SS_PRIORITY_LIGHTING 65 // Queued lighting engine updates.
#define SS_PRIORITY_DISEASE 60 // Disease ticks.

View File

@@ -31,6 +31,9 @@
/proc/Ceiling(x, y=1)
return -round(-x / y) * y
/proc/Modulus(x, y)
return ( (x) - (y) * round((x) / (y)) )
// Greatest Common Divisor: Euclid's algorithm.
/proc/Gcd(a, b)
while (1)

View File

@@ -1,141 +0,0 @@
/*
plot_vector is a helper datum for plotting a path in a straight line towards a target turf.
This datum converts from world space (turf.x and turf.y) to pixel space, which the datum keeps track of itself. This
should work with any size turfs (i.e. 32x32, 64x64) as it references world.icon_size (note: not actually tested with
anything other than 32x32 turfs).
setup()
This should be called after creating a new instance of a plot_vector datum.
This does the initial setup and calculations. Since we are travelling in a straight line we only need to calculate
the vector and x/y steps once. x/y steps are capped to 1 full turf, whichever is further. If we are travelling along
the y axis each step will be +/- 1 y, and the x movement reduced based on the angle (tangent calculation). After
this every subsequent step will be incremented based on these calculations.
Inputs:
source - the turf the object is starting from
target - the target turf the object is travelling towards
xo - starting pixel_x offset, typically won't be needed, but included in case someone has a need for it later
yo - same as xo, but for the y_pixel offset
increment()
Adds the offset to the current location - incrementing it by one step along the vector.
return_angle()
Returns the direction (angle in degrees) the object is travelling in.
(N)
90<39>
^
|
(W) 180<38> <--+--> 0<> (E)
|
v
-90<39>
(S)
return_hypotenuse()
Returns the distance of travel for each step of the vector, relative to each full step of movement. 1 is a full turf
length. Currently used as a multiplier for scaling effects that should be contiguous, like laser beams.
return_location()
Returns a vector_loc datum containing the current location data of the object (see /datum/vector_loc). This includes
the turf it currently should be at, as well as the pixel offset from the centre of that turf. Typically increment()
would be called before this if you are going to move an object based on it's vector data.
*/
/datum/plot_vector
var/turf/source
var/turf/target
var/angle = 0 // direction of travel in degrees
var/loc_x = 0 // in pixels from the left edge of the map
var/loc_y = 0 // in pixels from the bottom edge of the map
var/loc_z = 0 // loc z is in world space coordinates (i.e. z level) - we don't care about measuring pixels for this
var/offset_x = 0 // distance to increment each step
var/offset_y = 0
/datum/plot_vector/proc/setup(var/turf/S, var/turf/T, var/xo = 0, var/yo = 0, var/angle_offset=0)
source = S
target = T
if(!istype(source))
source = get_turf(source)
if(!istype(target))
target = get_turf(target)
if(!istype(source) || !istype(target))
return
// convert coordinates to pixel space (default is 32px/turf, 8160px across for a size 255 map)
loc_x = source.x * world.icon_size + xo
loc_y = source.y * world.icon_size + yo
loc_z = source.z
// calculate initial x and y difference
var/dx = target.x - source.x
var/dy = target.y - source.y
// if we aren't moving anywhere; quit now
if(dx == 0 && dy == 0)
return
// calculate the angle
angle = Atan2(dx, dy) + angle_offset
// and some rounding to stop the increments jumping whole turfs - because byond favours certain angles
if(angle > -135 && angle < 45)
angle = Ceiling(angle)
else
angle = Floor(angle)
// calculate the offset per increment step
if(abs(angle) in list(0, 45, 90, 135, 180)) // check if the angle is a cardinal
if(abs(angle) in list(0, 45, 135, 180)) // if so we can skip the trigonometry and set these to absolutes as
offset_x = sign(dx) // they will always be a full step in one or more directions
if(abs(angle) in list(45, 90, 135))
offset_y = sign(dy)
else if(abs(dy) > abs(dx))
offset_x = Cot(abs(angle)) // otherwise set the offsets
offset_y = sign(dy)
else
offset_x = sign(dx)
offset_y = Tan(angle)
if(dx < 0)
offset_y = -offset_y
// multiply the offset by the turf pixel size
offset_x *= world.icon_size
offset_y *= world.icon_size
/datum/plot_vector/proc/increment()
loc_x += offset_x
loc_y += offset_y
/datum/plot_vector/proc/return_angle()
return angle
/datum/plot_vector/proc/return_hypotenuse()
return sqrt(((offset_x / 32) ** 2) + ((offset_y / 32) ** 2))
/datum/plot_vector/proc/return_location(var/datum/vector_loc/data)
if(!data)
data = new()
data.loc = locate(round(loc_x / world.icon_size, 1), round(loc_y / world.icon_size, 1), loc_z)
if(!data.loc)
return
data.pixel_x = loc_x - (data.loc.x * world.icon_size)
data.pixel_y = loc_y - (data.loc.y * world.icon_size)
return data
/*
vector_loc is a helper datum for returning precise location data from plot_vector. It includes the turf the object is in
as well as the pixel offsets.
return_turf()
Returns the turf the object should be currently located in.
*/
/datum/vector_loc
var/turf/loc
var/pixel_x
var/pixel_y
/datum/vector_loc/proc/return_turf()
return loc

12
code/_helpers/view.dm Normal file
View File

@@ -0,0 +1,12 @@
/proc/getviewsize(view)
var/viewX
var/viewY
if(isnum(view))
var/totalviewrange = 1 + 2 * view
viewX = totalviewrange
viewY = totalviewrange
else
var/list/viewrangelist = splittext(view,"x")
viewX = text2num(viewrangelist[1])
viewY = text2num(viewrangelist[2])
return list(viewX, viewY)

View File

@@ -77,7 +77,7 @@
if(!locate(/turf) in list(A, A.loc)) // Prevents inventory from being drilled
return
var/obj/mecha/M = loc
return M.click_action(A, src)
return M.click_action(A, src, params)
if(restrained())
setClickCooldown(10)
@@ -190,7 +190,7 @@
/mob/proc/RangedAttack(var/atom/A, var/params)
if(!mutations.len) return
if((LASER in mutations) && a_intent == I_HURT)
LaserEyes(A) // moved into a proc below
LaserEyes(A, params) // moved into a proc below
else if(TK in mutations)
switch(get_dist(src,A))
if(1 to 5) // not adjacent may mean blocked by window
@@ -292,21 +292,21 @@
Laser Eyes: as the name implies, handles this since nothing else does currently
face_atom: turns the mob towards what you clicked on
*/
/mob/proc/LaserEyes(atom/A)
/mob/proc/LaserEyes(atom/A, params)
return
/mob/living/LaserEyes(atom/A)
/mob/living/LaserEyes(atom/A, params)
setClickCooldown(4)
var/turf/T = get_turf(src)
src.visible_message("<span class='danger'>\The [src]'s eyes flare with ruby light!</span>")
var/obj/item/projectile/beam/LE = new (T)
LE.muzzle_type = /obj/effect/projectile/eyelaser/muzzle
LE.tracer_type = /obj/effect/projectile/eyelaser/tracer
LE.impact_type = /obj/effect/projectile/eyelaser/impact
LE.muzzle_type = /obj/effect/projectile/muzzle/eyelaser
LE.tracer_type = /obj/effect/projectile/tracer/eyelaser
LE.impact_type = /obj/effect/projectile/impact/eyelaser
playsound(usr.loc, 'sound/weapons/wave.ogg', 75, 1)
LE.launch(A)
LE.launch_projectile(A, zone_sel? zone_sel.selecting : null, src, params)
/mob/living/carbon/human/LaserEyes()
/mob/living/carbon/human/LaserEyes(atom/A, params)
if(nutrition>0)
..()
nutrition = max(nutrition - rand(1,5),0)

View File

@@ -0,0 +1,12 @@
var/datum/controller/subsystem/processing/projectiles/SSprojectiles
/datum/controller/subsystem/processing/projectiles
name = "Projectiles"
stat_tag = "PROJ"
priority = SS_PRIORITY_PROJECTILES
flags = SS_TICKER|SS_NO_INIT
wait = 1
var/global_max_tick_moves = 10
/datum/controller/subsystem/processing/projectiles/New()
NEW_SS_GLOBAL(SSprojectiles)

View File

@@ -0,0 +1,227 @@
//Designed for things that need precision trajectories like projectiles.
//Don't use this for anything that you don't absolutely have to use this with (like projectiles!) because it isn't worth using a datum unless you need accuracy down to decimal places in pixels.
//You might see places where it does - 16 - 1. This is intentionally 17 instead of 16, because of how byond's tiles work and how not doing it will result in rounding errors like things getting put on the wrong turf.
#define RETURN_PRECISE_POSITION(A) new /datum/position(A)
#define RETURN_PRECISE_POINT(A) new /datum/point(A)
#define RETURN_POINT_VECTOR(ATOM, ANGLE, SPEED) {new /datum/point/vector(ATOM, null, null, null, null, ANGLE, SPEED)}
#define RETURN_POINT_VECTOR_INCREMENT(ATOM, ANGLE, SPEED, AMT) {new /datum/point/vector(ATOM, null, null, null, null, ANGLE, SPEED, AMT)}
/datum/position //For positions with map x/y/z and pixel x/y so you don't have to return lists. Could use addition/subtraction in the future I guess.
var/x = 0
var/y = 0
var/z = 0
var/pixel_x = 0
var/pixel_y = 0
/datum/position/proc/valid()
return x && y && z && !isnull(pixel_x) && !isnull(pixel_y)
/datum/position/New(_x = 0, _y = 0, _z = 0, _pixel_x = 0, _pixel_y = 0) //first argument can also be a /datum/point.
if(istype(_x, /datum/point))
var/datum/point/P = _x
var/turf/T = P.return_turf()
_x = T.x
_y = T.y
_z = T.z
_pixel_x = P.return_px()
_pixel_y = P.return_py()
else if(istype(_x, /atom))
var/atom/A = _x
_x = A.x
_y = A.y
_z = A.z
_pixel_x = A.pixel_x
_pixel_y = A.pixel_y
x = _x
y = _y
z = _z
pixel_x = _pixel_x
pixel_y = _pixel_y
/datum/position/proc/return_turf()
return locate(x, y, z)
/datum/position/proc/return_px()
return pixel_x
/datum/position/proc/return_py()
return pixel_y
/datum/position/proc/return_point()
return new /datum/point(src)
/proc/point_midpoint_points(datum/point/a, datum/point/b) //Obviously will not support multiZ calculations! Same for the two below.
var/datum/point/P = new
P.x = a.x + (b.x - a.x) / 2
P.y = a.y + (b.y - a.y) / 2
P.z = a.z
return P
/proc/pixel_length_between_points(datum/point/a, datum/point/b)
return sqrt(((b.x - a.x) ** 2) + ((b.y - a.y) ** 2))
/proc/angle_between_points(datum/point/a, datum/point/b)
return Atan2((b.y - a.y), (b.x - a.x))
/datum/point //A precise point on the map in absolute pixel locations based on world.icon_size. Pixels are FROM THE EDGE OF THE MAP!
var/x = 0
var/y = 0
var/z = 0
/datum/point/proc/valid()
return x && y && z
/datum/point/proc/copy_to(datum/point/p = new)
p.x = x
p.y = y
p.z = z
return p
/datum/point/New(_x, _y, _z, _pixel_x = 0, _pixel_y = 0) //first argument can also be a /datum/position or /atom.
if(istype(_x, /datum/position))
var/datum/position/P = _x
_x = P.x
_y = P.y
_z = P.z
_pixel_x = P.pixel_x
_pixel_y = P.pixel_y
else if(istype(_x, /atom))
var/atom/A = _x
_x = A.x
_y = A.y
_z = A.z
_pixel_x = A.pixel_x
_pixel_y = A.pixel_y
initialize_location(_x, _y, _z, _pixel_x, _pixel_y)
/datum/point/proc/initialize_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0)
if(!isnull(tile_x))
x = ((tile_x - 1) * world.icon_size) + world.icon_size / 2 + p_x + 1
if(!isnull(tile_y))
y = ((tile_y - 1) * world.icon_size) + world.icon_size / 2 + p_y + 1
if(!isnull(tile_z))
z = tile_z
/datum/point/proc/debug_out()
var/turf/T = return_turf()
return "\ref[src] aX [x] aY [y] aZ [z] pX [return_px()] pY [return_py()] mX [T.x] mY [T.y] mZ [T.z]"
/datum/point/proc/move_atom_to_src(atom/movable/AM)
AM.forceMove(return_turf())
AM.pixel_x = return_px()
AM.pixel_y = return_py()
/datum/point/proc/return_turf()
return locate(Ceiling(x / world.icon_size, 1), Ceiling(y / world.icon_size, 1), z)
/datum/point/proc/return_coordinates() //[turf_x, turf_y, z]
return list(Ceiling(x / world.icon_size, 1), Ceiling(y / world.icon_size, 1), z)
/datum/point/proc/return_position()
return new /datum/position(src)
/datum/point/proc/return_px()
return Modulus(x, world.icon_size) - 16 - 1
/datum/point/proc/return_py()
return Modulus(y, world.icon_size) - 16 - 1
/datum/point/vector
var/speed = 32 //pixels per iteration
var/iteration = 0
var/angle = 0
var/mpx = 0 //calculated x/y movement amounts to prevent having to do trig every step.
var/mpy = 0
var/starting_x = 0 //just like before, pixels from EDGE of map! This is set in initialize_location().
var/starting_y = 0
var/starting_z = 0
/datum/point/vector/New(_x, _y, _z, _pixel_x = 0, _pixel_y = 0, _angle, _speed, initial_increment = 0)
..()
initialize_trajectory(_speed, _angle)
if(initial_increment)
increment(initial_increment)
/datum/point/vector/initialize_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0)
. = ..()
starting_x = x
starting_y = y
starting_z = z
/datum/point/vector/copy_to(datum/point/vector/v = new)
..(v)
v.speed = speed
v.iteration = iteration
v.angle = angle
v.mpx = mpx
v.mpy = mpy
v.starting_x = starting_x
v.starting_y = starting_y
v.starting_z = starting_z
return v
/datum/point/vector/proc/initialize_trajectory(pixel_speed, new_angle)
if(!isnull(pixel_speed))
speed = pixel_speed
set_angle(new_angle)
/datum/point/vector/proc/set_angle(new_angle) //calculations use "byond angle" where north is 0 instead of 90, and south is 180 instead of 270.
if(isnull(angle))
return
angle = new_angle
update_offsets()
/datum/point/vector/proc/update_offsets()
mpx = sin(angle) * speed
mpy = cos(angle) * speed
/datum/point/vector/proc/set_speed(new_speed)
if(isnull(new_speed) || speed == new_speed)
return
speed = new_speed
update_offsets()
/datum/point/vector/proc/increment(multiplier = 1)
iteration++
x += mpx * multiplier
y += mpy * multiplier
/datum/point/vector/proc/return_vector_after_increments(amount = 7, multiplier = 1, force_simulate = FALSE)
var/datum/point/vector/v = copy_to()
if(force_simulate)
for(var/i in 1 to amount)
v.increment(multiplier)
else
v.increment(multiplier * amount)
return v
/datum/point/vector/proc/on_z_change()
return
/datum/point/vector/processed //pixel_speed is per decisecond.
var/last_process = 0
var/last_move = 0
var/paused = FALSE
/datum/point/vector/processed/Destroy()
STOP_PROCESSING(SSprojectiles, src)
return ..()
/datum/point/vector/processed/proc/start()
last_process = world.time
last_move = world.time
START_PROCESSING(SSprojectiles, src)
/datum/point/vector/processed/process()
if(paused)
last_move += world.time - last_process
last_process = world.time
return
var/needed_time = world.time - last_move
last_process = world.time
last_move = world.time
increment(needed_time / SSprojectiles.wait)

View File

@@ -391,7 +391,7 @@
A = new /obj/item/projectile/beam/cult(loc)
playsound(loc, 'sound/weapons/laserdeep.ogg', 65, 1)
A.ignore = sacrificer
A.launch(target)
A.launch_projectile(target)
next_shot = world.time + shot_delay
A = null //So projectiles can GC
spawn(shot_delay+1)

View File

@@ -466,7 +466,7 @@
if(get_dist(src, L) > 7) //if it's too far away, why bother?
return TURRET_NOT_TARGET
if(!check_trajectory(L, src)) //check if we have true line of sight
if(!(L in check_trajectory(L, src))) //check if we have true line of sight
return TURRET_NOT_TARGET
if(emagged) // If emagged not even the dead get a rest
@@ -611,7 +611,7 @@
//If the target is grabbing someone then the turret smartly aims for extremities
var/def_zone = get_exposed_defense_zone(target)
//Shooting Code:
A.launch(target, def_zone)
A.launch_projectile(target, def_zone)
/datum/turret_checks
var/enabled

View File

@@ -80,7 +80,7 @@
return 0
return 1
/obj/item/mecha_parts/mecha_equipment/proc/action(atom/target)
/obj/item/mecha_parts/mecha_equipment/proc/action(atom/target, mob/user, params)
return
/obj/item/mecha_parts/mecha_equipment/proc/can_attach(obj/mecha/M as obj)

View File

@@ -18,7 +18,7 @@
return 0
return ..()
/obj/item/mecha_parts/mecha_equipment/weapon/action(atom/target)
/obj/item/mecha_parts/mecha_equipment/weapon/action(atom/target, mob/user, params)
if(!action_checks(target))
return
var/turf/curloc = chassis.loc
@@ -38,7 +38,7 @@
playsound(chassis, fire_sound, fire_volume, 1)
projectiles--
var/P = new projectile(curloc)
Fire(P, target)
Fire(P, target, user, params)
if(fire_cooldown)
sleep(fire_cooldown)
if(auto_rearm)
@@ -47,11 +47,11 @@
do_after_cooldown()
return
/obj/item/mecha_parts/mecha_equipment/weapon/proc/Fire(atom/A, atom/target)
/obj/item/mecha_parts/mecha_equipment/weapon/proc/Fire(atom/A, atom/target, mob/user, params)
var/obj/item/projectile/P = A
var/def_zone
if(chassis && istype(chassis.occupant,/mob/living/carbon/human))
var/mob/living/carbon/human/H = chassis.occupant
def_zone = H.zone_sel.selecting
H.setMoveCooldown(fire_time)
P.launch(target, def_zone)
P.launch_projectile(target, def_zone, user, params)

View File

@@ -298,7 +298,7 @@
////////////////////////////
///// Action processing ////
////////////////////////////
/obj/mecha/proc/click_action(atom/target,mob/user)
/obj/mecha/proc/click_action(atom/target,mob/user, params)
if(!src.occupant || src.occupant != user ) return
if(user.stat) return
if(state)
@@ -318,9 +318,9 @@
return
if(!target.Adjacent(src))
if(selected && selected.is_ranged())
selected.action(target)
selected.action(target, user, params)
else if(selected && selected.is_melee())
selected.action(target)
selected.action(target, user, params)
else
src.melee_action(target)
return

View File

@@ -0,0 +1,56 @@
/obj/effect/projectile
name = "pew"
icon = 'icons/obj/projectiles.dmi'
icon_state = "nothing"
layer = 4.5
anchored = TRUE
unacidable = TRUE
light_power = 1
light_range = 2
light_color = "#00ffff"
mouse_opacity = 0
appearance_flags = 0
/obj/effect/projectile/singularity_pull()
return
/obj/effect/projectile/singularity_act()
return
/obj/effect/projectile/proc/scale_to(nx,ny,override=TRUE)
var/matrix/M
if(!override)
M = transform
else
M = new
M.Scale(nx,ny)
transform = M
/obj/effect/projectile/proc/turn_to(angle,override=TRUE)
var/matrix/M
if(!override)
M = transform
else
M = new
M.Turn(angle)
transform = M
/obj/effect/projectile/New(angle_override, p_x, p_y, color_override, scaling = 1)
if(angle_override && p_x && p_y && color_override && scaling)
apply_vars(angle_override, p_x, p_y, color_override, scaling)
return ..()
/obj/effect/projectile/proc/apply_vars(angle_override, p_x = 0, p_y = 0, color_override, scaling = 1, new_loc, increment = 0)
var/mutable_appearance/look = new(src)
look.pixel_x = p_x
look.pixel_y = p_y
if(color_override)
look.color = color_override
appearance = look
scale_to(1,scaling, FALSE)
turn_to(angle_override, FALSE)
if(!isnull(new_loc)) //If you want to null it just delete it...
forceMove(new_loc)
for(var/i in 1 to increment)
pixel_x += round((sin(angle_override)+16*sin(angle_override)*2), 1)
pixel_y += round((cos(angle_override)+16*cos(angle_override)*2), 1)

View File

@@ -0,0 +1,52 @@
/obj/effect/projectile/impact
name = "beam impact"
icon = 'icons/effects/projectiles/impact.dmi'
/obj/effect/projectile/impact/laser
name = "laser impact"
icon_state = "impact_laser"
/obj/effect/projectile/impact/laser/blue
name = "laser impact"
icon_state = "impact_blue"
/obj/effect/projectile/impact/disabler
name = "disabler impact"
icon_state = "impact_omni"
/obj/effect/projectile/impact/xray
name = "xray impact"
icon_state = "impact_xray"
/obj/effect/projectile/impact/pulse
name = "pulse impact"
icon_state = "impact_u_laser"
/obj/effect/projectile/impact/plasma_cutter
name = "plasma impact"
icon_state = "impact_plasmacutter"
/obj/effect/projectile/impact/stun
name = "stun impact"
icon_state = "impact_stun"
/obj/effect/projectile/impact/heavy_laser
name = "heavy laser impact"
icon_state = "impact_beam_heavy"
/obj/effect/projectile/impact/cult
name = "arcane blast"
icon_state = "impact_cult"
/obj/effect/projectile/impact/cult/heavy
icon_state = "impact_hcult"
/obj/effect/projectile/impact/solar
name = "solar eruption"
icon_state = "impact_solar"
/obj/effect/projectile/impact/eyelaser
icon_state = "impact_eye"
/obj/effect/projectile/impact/emitter
icon_state = "impact_emitter"

View File

@@ -0,0 +1,46 @@
/obj/effect/projectile/muzzle
name = "muzzle flash"
icon = 'icons/effects/projectiles/muzzle.dmi'
/obj/effect/projectile/muzzle/laser
icon_state = "muzzle_laser"
/obj/effect/projectile/muzzle/laser/blue
icon_state = "muzzle_laser_blue"
/obj/effect/projectile/muzzle/disabler
icon_state = "muzzle_omni"
/obj/effect/projectile/muzzle/xray
icon_state = "muzzle_xray"
/obj/effect/projectile/muzzle/pulse
icon_state = "muzzle_u_laser"
/obj/effect/projectile/muzzle/plasma_cutter
icon_state = "muzzle_plasmacutter"
/obj/effect/projectile/muzzle/stun
icon_state = "muzzle_stun"
/obj/effect/projectile/muzzle/heavy_laser
icon_state = "muzzle_beam_heavy"
/obj/effect/projectile/muzzle/cult
name = "arcane flash"
icon_state = "muzzle_cult"
/obj/effect/projectile/muzzle/cult/heavy
icon_state = "muzzle_hcult"
/obj/effect/projectile/muzzle/solar
icon_state = "muzzle_solar"
/obj/effect/projectile/muzzle/eyelaser
icon_state = "muzzle_eye"
/obj/effect/projectile/muzzle/emitter
icon_state = "muzzle_emitter"
/obj/effect/projectile/muzzle/bullet
icon_state = "muzzle_bullet"

View File

@@ -0,0 +1,64 @@
/proc/generate_tracer_between_points(datum/point/starting, datum/point/ending, beam_type, color, qdel_in = 5) //Do not pass z-crossing points as that will not be properly (and likely will never be properly until it's absolutely needed) supported!
if(!istype(starting) || !istype(ending) || !ispath(beam_type))
return
if(starting.z != ending.z)
crash_with("Projectile tracer generation of cross-Z beam detected. This feature is not supported!") //Do it anyways though.
var/datum/point/midpoint = point_midpoint_points(starting, ending)
var/obj/effect/projectile/tracer/PB = new beam_type
PB.apply_vars(angle_between_points(starting, ending), midpoint.return_px(), midpoint.return_py(), color, pixel_length_between_points(starting, ending) / world.icon_size, midpoint.return_turf(), 0)
. = PB
if(qdel_in)
QDEL_IN(PB, qdel_in)
/obj/effect/projectile/tracer
name = "beam"
icon = 'icons/effects/projectiles/tracer.dmi'
/obj/effect/projectile/tracer/laser
name = "laser"
icon_state = "beam"
/obj/effect/projectile/tracer/laser/blue
icon_state = "beam_blue"
/obj/effect/projectile/tracer/disabler
name = "disabler"
icon_state = "beam_omni"
/obj/effect/projectile/tracer/xray
name = "xray laser"
icon_state = "xray"
/obj/effect/projectile/tracer/pulse
name = "pulse laser"
icon_state = "u_laser"
/obj/effect/projectile/tracer/plasma_cutter
name = "plasma blast"
icon_state = "plasmacutter"
/obj/effect/projectile/tracer/stun
name = "stun beam"
icon_state = "stun"
/obj/effect/projectile/tracer/heavy_laser
name = "heavy laser"
icon_state = "beam_heavy"
/obj/effect/projectile/tracer/cult/heavy
name = "heavy arcane beam"
icon_state = "hcult"
/obj/effect/projectile/tracer/cult
name = "arcane beam"
icon_state = "cult"
/obj/effect/projectile/tracer/solar
name = "solar energy"
icon_state = "solar"
/obj/effect/projectile/tracer/eyelaser
icon_state = "eye"
/obj/effect/projectile/tracer/emitter
icon_state = "emitter"

View File

@@ -15,7 +15,7 @@ proc/fragem(var/source,var/fragx,var/fragy,var/light_dam,var/flash_dam,var/p_dam
P.shot_from = source
P.name = "[source]'s shrapnel"
P.launch(T)
P.launch_projectile(T)
if(can_cover)
for(var/mob/living/M in O)

View File

@@ -97,10 +97,10 @@
// Find a turf near or on the original location to bounce to
var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
var/turf/curloc = get_turf(user)
// redirect the projectile
P.redirect(new_x, new_y, curloc, user)
P.firer = user
P.old_style_target(locate(new_x, new_y, P.z))
return PROJECTILE_CONTINUE // complete projectile permutation
else

View File

@@ -171,10 +171,10 @@
// Find a turf near or on the original location to bounce to
var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
var/turf/curloc = get_turf(user)
// redirect the projectile
P.redirect(new_x, new_y, curloc, user)
P.firer = user
P.old_style_target(locate(new_x, new_y, P.z))
return PROJECTILE_CONTINUE // complete projectile permutation
else

View File

@@ -10,7 +10,6 @@
temperature = T20C
thermal_conductivity = OPEN_HEAT_TRANSFER_COEFFICIENT
// heat_capacity = 700000 No.
is_hole = TRUE
@@ -29,14 +28,14 @@
for(var/atom/movable/AM as mob|obj in src)
src.Entered(AM)
turfs += src
if(dynamic_lighting)
luminosity = 0
else
luminosity = 1
return INITIALIZE_HINT_NORMAL
/turf/space/is_space()
@@ -91,7 +90,7 @@
return
else
user << "<span class='warning'>The plating is going to need some support.</span>"
..(C, user)
// Ported from unstable r355

View File

@@ -241,7 +241,7 @@
desc = "It looks like a pair of gloves, but it seems to have a small dial inside."
origin_tech = list(TECH_ILLEGAL = 3)
var/global/list/clothing_choices
/obj/item/clothing/gloves/chameleon/Initialize()
. = ..()
if(!clothing_choices)
@@ -374,7 +374,7 @@
P.icon_state = initial(copy_projectile.icon_state)
P.pass_flags = initial(copy_projectile.pass_flags)
P.hitscan = initial(copy_projectile.hitscan)
P.step_delay = initial(copy_projectile.step_delay)
P.range = initial(copy_projectile.range)
P.muzzle_type = initial(copy_projectile.muzzle_type)
P.tracer_type = initial(copy_projectile.tracer_type)
P.impact_type = initial(copy_projectile.impact_type)

View File

@@ -203,10 +203,10 @@
// Find a turf near or on the original location to bounce to
var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
var/turf/curloc = get_turf(user)
// redirect the projectile
P.redirect(new_x, new_y, curloc, user)
P.firer = user
P.old_style_target(locate(new_x, new_y, P.z))
return PROJECTILE_CONTINUE // complete projectile permutation

View File

@@ -154,10 +154,10 @@
// Find a turf near or on the original location to bounce to
var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
var/turf/curloc = get_turf(user)
// redirect the projectile
P.redirect(new_x, new_y, curloc, user)
P.firer = user
P.old_style_target(locate(new_x, new_y, P.z))
return PROJECTILE_CONTINUE // complete projectile permutation

View File

@@ -61,7 +61,7 @@
playsound(loc, emagged ? 'sound/weapons/Laser.ogg' : 'sound/weapons/Taser.ogg', 50, 1)
var/obj/item/projectile/P = new projectile(loc)
var/def_zone = get_exposed_defense_zone(A)
P.launch(A, def_zone)
P.launch_projectile(A, def_zone)
// Assembly
/obj/item/weapon/secbot_assembly/ed209_assembly

View File

@@ -175,7 +175,7 @@
visible_message("<span class='warning'>[src] spits neurotoxin at [target]!</span>", "<span class='alium'>You spit neurotoxin at [target].</span>")
var/obj/item/projectile/energy/neurotoxin/A = new /obj/item/projectile/energy/neurotoxin(usr.loc)
A.launch(target,get_organ_target())
A.launch_projectile(target,get_organ_target())
/mob/living/carbon/human/proc/resin() // -- TLE
set name = "Secrete Resin (75)"

View File

@@ -73,9 +73,9 @@
recharge_time = 5
sel_mode = 1
firemodes = list(
list(mode_name="semiauto", burst=1, fire_delay=0, move_delay=null, burst_accuracy=null, dispersion=null),
list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0)),
list(mode_name="short bursts", burst=5, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1,-2,-2), dispersion=list(0.6, 1.0, 1.0, 1.0, 1.2))
list(mode_name="semiauto", burst=1, fire_delay=0, move_delay=null, burst_accuracy=null, dispersion=list(0)),
list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0, 15, 15)),
list(mode_name="short bursts", burst=5, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1,-2,-2), dispersion=list(0, 15, 15, 18, 18, 20))
)
/obj/item/weapon/gun/energy/crossbow/cyborg

View File

@@ -146,10 +146,10 @@
if(P.starting)
var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)
var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)
var/turf/curloc = get_turf(src)
// redirect the projectile
P.redirect(new_x, new_y, curloc, src)
P.firer = user
P.old_style_target(locate(new_x, new_y, P.z))
return -1 // complete projectile permutation

View File

@@ -197,7 +197,7 @@
playsound(user, projectilesound, 100, 1)
if(!A) return
var/def_zone = get_exposed_defense_zone(target)
A.launch(target, def_zone)
A.launch_projectile(target, def_zone)
/mob/living/simple_animal/hostile/proc/DestroySurroundings()
if(prob(break_stuff_probability))

View File

@@ -55,9 +55,9 @@
check_armour = "energy"
damage = 5
muzzle_type = /obj/effect/projectile/stun/muzzle
tracer_type = /obj/effect/projectile/stun/tracer
impact_type = /obj/effect/projectile/stun/impact
muzzle_type = /obj/effect/projectile/muzzle/stun
tracer_type = /obj/effect/projectile/tracer/stun
impact_type = /obj/effect/projectile/impact/stun
/obj/item/projectile/beam/cavern/on_hit(var/atom/target, var/blocked = 0)
if(ishuman(target))

View File

@@ -183,14 +183,14 @@
if(!mob)
return // Moved here to avoid nullrefs below
if(mob.control_object)
if(mob.control_object)
Move_object(direct)
if(mob.incorporeal_move && isobserver(mob))
Process_Incorpmove(direct)
return
if(moving || world.time < move_delay)
if(moving || world.time < move_delay)
return 0
//This compensates for the inaccuracy of move ticks
@@ -210,7 +210,7 @@
if(mob.eyeobj)
return mob.EyeMove(n,direct)
if(mob.transforming)
if(mob.transforming)
return //This is sota the goto stop mobs from moving var
if(isliving(mob))
@@ -224,9 +224,9 @@
if(item.zoom)
item.zoom(mob)
break
// Only meaningful for living mobs.
if(Process_Grab())
if(Process_Grab())
return
if(!mob.canmove)

View File

@@ -147,7 +147,7 @@
var/obj/item/projectile/beam/emitter/A = new /obj/item/projectile/beam/emitter( src.loc )
A.damage = round(power_per_shot/EMITTER_DAMAGE_POWER_TRANSFER)
A.launch( get_step(src.loc, src.dir) )
A.launch_projectile(get_step(src, dir))
/obj/machinery/power/emitter/attackby(obj/item/W, mob/user)

View File

@@ -1,191 +0,0 @@
/obj/effect/projectile
icon = 'icons/effects/projectiles.dmi'
icon_state = "bolt"
layer = 20
/obj/effect/projectile/New(var/turf/location)
if(istype(location))
loc = location
/obj/effect/projectile/proc/set_transform(var/matrix/M)
if(istype(M))
transform = M
/obj/effect/projectile/proc/activate(var/kill_delay = 3)
QDEL_IN(src, kill_delay) //see effect_system.dm - sets loc to null and lets GC handle removing these effects
//----------------------------
// Laser beam
//----------------------------
/obj/effect/projectile/laser/tracer
icon_state = "beam"
/obj/effect/projectile/laser/muzzle
icon_state = "muzzle_laser"
/obj/effect/projectile/laser/impact
icon_state = "impact_laser"
//----------------------------
// Blue laser beam
//----------------------------
/obj/effect/projectile/laser_blue/tracer
icon_state = "beam_blue"
/obj/effect/projectile/laser_blue/muzzle
icon_state = "muzzle_blue"
/obj/effect/projectile/laser_blue/impact
icon_state = "impact_blue"
//----------------------------
// Omni laser beam
//----------------------------
/obj/effect/projectile/laser_omni/tracer
icon_state = "beam_omni"
/obj/effect/projectile/laser_omni/muzzle
icon_state = "muzzle_omni"
/obj/effect/projectile/laser_omni/impact
icon_state = "impact_omni"
//----------------------------
// Xray laser beam
//----------------------------
/obj/effect/projectile/xray/tracer
icon_state = "xray"
/obj/effect/projectile/xray/muzzle
icon_state = "muzzle_xray"
/obj/effect/projectile/xray/impact
icon_state = "impact_xray"
//----------------------------
// Heavy laser beam
//----------------------------
/obj/effect/projectile/laser_heavy/tracer
icon_state = "beam_heavy"
/obj/effect/projectile/laser_heavy/muzzle
icon_state = "muzzle_beam_heavy"
/obj/effect/projectile/laser_heavy/impact
icon_state = "impact_beam_heavy"
//----------------------------
// Pulse laser beam
//----------------------------
/obj/effect/projectile/laser_pulse/tracer
icon_state = "u_laser"
/obj/effect/projectile/laser_pulse/muzzle
icon_state = "muzzle_u_laser"
/obj/effect/projectile/laser_pulse/impact
icon_state = "impact_u_laser"
//----------------------------
// Pulse muzzle effect only
//----------------------------
/obj/effect/projectile/pulse/muzzle
icon_state = "muzzle_pulse"
//----------------------------
// Emitter beam
//----------------------------
/obj/effect/projectile/emitter/tracer
icon_state = "emitter"
/obj/effect/projectile/emitter/muzzle
icon_state = "muzzle_emitter"
/obj/effect/projectile/emitter/impact
icon_state = "impact_emitter"
//----------------------------
// Stun beam
//----------------------------
/obj/effect/projectile/stun/tracer
icon_state = "stun"
/obj/effect/projectile/stun/muzzle
icon_state = "muzzle_stun"
/obj/effect/projectile/stun/impact
icon_state = "impact_stun"
//----------------------------
// Eye beam
//----------------------------
/obj/effect/projectile/eyelaser/tracer
icon_state = "eye"
/obj/effect/projectile/eyelaser/muzzle
icon_state = "muzzle_eye"
/obj/effect/projectile/eyelaser/impact
icon_state = "impact_eye"
//----------------------------
// Bullet
//----------------------------
/obj/effect/projectile/bullet/muzzle
icon_state = "muzzle_bullet"
// Solar beam
//----------------------------
/obj/effect/projectile/solar/tracer
icon_state = "solar"
/obj/effect/projectile/solar/muzzle
icon_state = "muzzle_solar"
/obj/effect/projectile/solar/impact
icon_state = "impact_solar"
//----------------------------
// Treye beam
//----------------------------
/obj/effect/projectile/trilaser/tracer
icon_state = "plasmacutter"
/obj/effect/projectile/trilaser/muzzle
icon_state = "muzzle_plasmacutter"
/obj/effect/projectile/trilaser/impact
icon_state = "impact_plasmacutter"
//----------------------------
// Pulse muzzle
//----------------------------
/obj/effect/projectile/pulse_bullet/muzzle
icon_state = "muzzle_pulse"
//----------------------------
// Demonic Beam
//----------------------------
/obj/effect/projectile/cult/tracer
icon_state = "cult"
/obj/effect/projectile/cult/muzzle
icon_state = "muzzle_cult"
/obj/effect/projectile/cult/impact
icon_state = "impact_cult"
//----------------------------
// Empowered Demonic Beam
//----------------------------
/obj/effect/projectile/cult/heavy/tracer
icon_state = "hcult"
/obj/effect/projectile/cult/heavy/muzzle
icon_state = "muzzle_hcult"
/obj/effect/projectile/cult/heavy/impact
icon_state = "impact_hcult"

View File

@@ -177,8 +177,6 @@
return ..() //Pistolwhippin'
/obj/item/weapon/gun/proc/Fire(atom/target, mob/living/user, clickparams, pointblank=0, reflex=0)
if(!user || !target) return
add_fingerprint(user)
@@ -267,7 +265,7 @@
P.shot_from = src.name
P.silenced = silenced
P.launch(target)
P.launch_projectile(target)
if(silenced)
playsound(src, fire_sound, 10, 1)
@@ -300,7 +298,7 @@
return 2
//just assume we can shoot through glass and stuff. No big deal, the player can just choose to not target someone
//on the other side of a window if it makes a difference. Or if they run behind a window, too bad.
return check_trajectory(target, user)
return (target in check_trajectory(target, user))
//called if there was no projectile to shoot
/obj/item/weapon/gun/proc/handle_click_empty(mob/user)
@@ -384,27 +382,21 @@
P.accuracy += 2
//does the actual launching of the projectile
/obj/item/weapon/gun/proc/process_projectile(obj/projectile, mob/user, atom/target, var/target_zone, var/params=null)
/obj/item/weapon/gun/proc/process_projectile(obj/projectile, mob/user, atom/target, target_zone, params)
var/obj/item/projectile/P = projectile
if(!istype(P))
return 0 //default behaviour only applies to true projectiles
if(params)
P.set_clickpoint(params)
//shooting while in shock
var/x_offset = 0
var/y_offset = 0
var/added_spread = 0
if(istype(user, /mob/living/carbon))
var/mob/living/carbon/mob = user
if(mob.shock_stage > 120)
y_offset = rand(-2,2)
x_offset = rand(-2,2)
added_spread = 30
else if(mob.shock_stage > 70)
y_offset = rand(-1,1)
x_offset = rand(-1,1)
added_spread = 15
return !P.launch_from_gun(target, user, src, target_zone, x_offset, y_offset)
return !P.launch_from_gun(target, target_zone, user, params, null, added_spread, src)
//Suicide handling.
/obj/item/weapon/gun/var/mouthshoot = 0 //To stop people from suiciding twice... >.>

View File

@@ -157,7 +157,7 @@ obj/item/weapon/gun/energy/retro
burst_delay = 0
move_delay = 0
fire_delay = 2
dispersion = list(1.0, -1.0, 2.0, -2.0)
dispersion = list(10)
can_turret = 1
turret_sprite_set = "laser"

View File

@@ -36,7 +36,7 @@
burst = 3,
move_delay = 4,
accuracy = list(0,-1,-1,-2,-2),
dispersion = list(0.0, 0.6, 1.0),
dispersion = list(0, 10, 10),
projectile_type = /obj/item/projectile/bullet/pistol,
fire_sound = 'sound/weapons/Gunshot_smg.ogg'
),

View File

@@ -73,14 +73,14 @@
damage = 15
damage_type = BRUTE
check_armour = "bomb"
kill_count = 5
range = 5
var/pressure_decrease = 0.25
var/turf_aoe = FALSE
var/mob_aoe = 0
var/list/hit_overlays = list()
/obj/item/projectile/kinetic/launch_from_gun(atom/target, mob/user, obj/item/weapon/gun/launcher, var/target_zone, var/x_offset=0, var/y_offset=0)
/obj/item/projectile/kinetic/launch_from_gun(atom/target, target_zone, mob/user, params, angle_override, forced_spread, obj/item/weapon/gun/launcher)
if(istype(launcher, /obj/item/weapon/gun/energy/kinetic_accelerator))
var/obj/item/weapon/gun/energy/kinetic_accelerator/KA = launcher
for(var/A in KA.get_modkits())
@@ -189,7 +189,7 @@
cost = 24 //so you can fit four plus a tracer cosmetic
/obj/item/borg/upgrade/modkit/range/modify_projectile(obj/item/projectile/kinetic/K)
K.kill_count += modifier
K.range += modifier
//Damage
@@ -313,14 +313,15 @@
damage = 15
damage_type = BURN
check_armour = "laser"
kill_count = 5
range = 5
pass_flags = PASSTABLE
muzzle_type = /obj/effect/projectile/trilaser/muzzle
tracer_type = /obj/effect/projectile/trilaser/tracer
impact_type = /obj/effect/projectile/trilaser/impact
muzzle_type = /obj/effect/projectile/muzzle/plasma_cutter
tracer_type = /obj/effect/projectile/tracer/plasma_cutter
impact_type = /obj/effect/projectile/impact/plasma_cutter
maiming = 1
maim_rate = 3
/obj/item/projectile/beam/plasmacutter/on_impact(var/atom/A)
if(istype(A, /turf/simulated/mineral))
var/turf/simulated/mineral/M = A

View File

@@ -132,7 +132,7 @@
burst_delay = 1
move_delay = 3
fire_delay = 0
dispersion = list(0.0, 0.2, -0.2)
dispersion = list(0, 8)
/obj/item/weapon/gun/energy/mousegun
name = "\improper NT \"Arodentia\" Exterminator ray"
@@ -151,7 +151,7 @@
burst_delay = 1
move_delay = 0
fire_delay = 3
dispersion = list(0.0, 6,0, -6.0)
dispersion = list(0, 15, 15)
var/lightfail = 0
@@ -205,6 +205,9 @@
accuracy = 20
muzzle_flash = 10
#define GATLINGLASER_DISPERSION_CONCENTRATED list(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
#define GATLINGLASER_DISPERSION_SPRAY list(0, 5, 5, 10, 10, 15, 15, 20, 20, 25, 25, 30, 30, 35, 40, 45)
/obj/item/weapon/gun/energy/vaurca/gatlinglaser
name = "gatling laser"
desc = "A highly sophisticated rapid fire laser weapon."
@@ -222,11 +225,11 @@
burst = 10
burst_delay = 1
fire_delay = 10
dispersion = list(0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9)
dispersion = GATLINGLASER_DISPERSION_CONCENTRATED
firemodes = list(
list(mode_name="concentrated burst", burst=10, burst_delay = 1, fire_delay = 10, dispersion = list(0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9)),
list(mode_name="spray", burst=20, burst_delay = 1, move_delay = 5, fire_delay = 30, dispersion = list(0.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 3.0, 3.25))
list(mode_name="concentrated burst", burst=10, burst_delay = 1, fire_delay = 10, dispersion = GATLINGLASER_DISPERSION_CONCENTRATED),
list(mode_name="spray", burst=20, burst_delay = 1, move_delay = 5, fire_delay = 30, dispersion = GATLINGLASER_DISPERSION_SPRAY)
)
action_button_name = "Wield gatling laser"
@@ -411,14 +414,13 @@
recharge_time = 1
charge_meter = 1
charge_cost = 50
dispersion = list(0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9)
can_turret = 1
turret_sprite_set = "thermaldrill"
firemodes = list(
list(mode_name="2 second burst", burst=10, burst_delay = 1, fire_delay = 20),
list(mode_name="4 second burst", burst=20, burst_delay = 1, fire_delay = 40, dispersion = list(0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9)),
list(mode_name="6 second burst", burst=30, burst_delay = 1, fire_delay = 60, dispersion = list(0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 0.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8))
list(mode_name="4 second burst", burst=20, burst_delay = 1, fire_delay = 40, dispersion = list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)),
list(mode_name="6 second burst", burst=30, burst_delay = 1, fire_delay = 60, dispersion = list(0, 1.5, 3, 4.5, 6, 7.5, 9, 10.5, 12, 13.5, 15, 16.5, 18, 19.5, 21))
)
action_button_name = "Wield thermal drill"
@@ -482,7 +484,7 @@
charge_meter = 1
use_external_power = 1
charge_cost = 25
dispersion = list(0.0, 0.3, 0.6, 0.9, 1.2, 1.5, 1.8, 2.1, 2.4, 2.7, 3.0, 3.3, 3.6, 3.9, 4.2, 4.5, 4.8, 5.1, 5.4, 5.7, 6.0, 6.3, 6.6, 6.9, 7.2, 7.5, 7.8, 8.1, 8.4, 8.7)
dispersion = list(0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30)
/obj/item/weapon/gun/energy/vaurca/mountedthermaldrill/special_check(var/mob/user)
if(is_charging)

View File

@@ -15,8 +15,8 @@
firemodes = list(
list(mode_name="semiauto", burst=1, fire_delay=0, move_delay=null, burst_accuracy=null, dispersion=null),
list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0)),
list(mode_name="short bursts", burst=5, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1,-2,-2), dispersion=list(0.6, 1.0, 1.0, 1.0, 1.2))
list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0, 10, 15)),
list(mode_name="short bursts", burst=5, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1,-2,-2), dispersion=list(5, 10, 15, 20))
)
//Submachine guns and personal defence weapons, go.
@@ -101,8 +101,8 @@
firemodes = list(
list(mode_name="semiauto", burst=1, fire_delay=10, move_delay=null, burst_accuracy=null, dispersion=null),
list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0)),
list(mode_name="short bursts", burst=5, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1,-2,-2), dispersion=list(0.6, 1.0, 1.0, 1.0, 1.2))
list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0, 5, 10)),
list(mode_name="short bursts", burst=5, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1,-2,-2), dispersion=list(5, 5, 15))
)
//slower to regain aim, more inaccurate if not wielding
@@ -168,7 +168,7 @@
burst_delay = 4
firemodes = list(
list(mode_name="semiauto", burst=1, fire_delay=10, move_delay=null, use_launcher=null, burst_accuracy=null, dispersion=null),
list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=6, use_launcher=null, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 0.6)),
list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=6, use_launcher=null, burst_accuracy=list(0,-1,-1), dispersion=list(0, 7.5)),
list(mode_name="fire grenades", burst=null, fire_delay=null, move_delay=null, use_launcher=1, burst_accuracy=null, dispersion=null)
)
@@ -238,8 +238,8 @@
magazine_type = /obj/item/ammo_magazine/a762
firemodes = list(
list(mode_name="short bursts", burst=5, move_delay=6, burst_accuracy = list(0,-1,-1,-2,-2), dispersion = list(0.6, 1.0, 1.0, 1.0, 1.2)),
list(mode_name="long bursts", burst=8, move_delay=8, burst_accuracy = list(0,-1,-1,-2,-2,-2,-3,-3), dispersion = list(1.0, 1.0, 1.0, 1.0, 1.2))
list(mode_name="short bursts", burst=5, move_delay=6, burst_accuracy = list(0,-1,-1,-2,-2), dispersion = list(3, 6, 9)),
list(mode_name="long bursts", burst=8, move_delay=8, burst_accuracy = list(0,-1,-1,-2,-2,-2,-3,-3), dispersion = list(8))
)
var/cover_open = 0
@@ -324,7 +324,7 @@
firemodes = list(
list(mode_name="single coil", burst=1, fire_delay=0, move_delay=null, burst_accuracy=null, dispersion=null),
list(mode_name="dual coil", burst=2, move_delay=8, accuracy = list(-2,-3), dispersion = list(2.0, 3.0))
list(mode_name="dual coil", burst=2, move_delay=8, accuracy = list(-2,-3), dispersion = list(20))
)
@@ -348,8 +348,8 @@
firemodes = list(
list(mode_name="semiauto", burst=1, move_delay=null, burst_accuracy=null, dispersion=null),
list(mode_name="3-round bursts", burst=3, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0)),
list(mode_name="short bursts", burst=5, move_delay=4, burst_accuracy=list(0,-1,-1,-2,-2), dispersion=list(0.6, 1.0, 1.0, 1.0, 1.2))
list(mode_name="3-round bursts", burst=3, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0, 10, 15)),
list(mode_name="short bursts", burst=5, move_delay=4, burst_accuracy=list(0,-1,-1,-2,-2), dispersion=list(5, 10, 15))
)
@@ -414,7 +414,7 @@
firemodes = list(
list(mode_name="semiauto", burst=1, fire_delay= 10, move_delay=null, burst_accuracy=null, dispersion=null),
list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0))
list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0, 10, 15))
)
/obj/item/weapon/gun/projectile/automatic/rifle/shotgun/update_icon()

View File

@@ -5,7 +5,7 @@
sharp = 1
embed = 1 //the dart is shot fast enough to pierce space suits, so I guess splintering inside the target can be a thing. Should be rare due to low damage.
var/reagent_amount = 15
kill_count = 15 //shorter range
range = 15 //shorter range
muzzle_type = null

View File

@@ -227,7 +227,8 @@
burst = 3
burst_delay = 3
move_delay = 3
dispersion = list(1.0, -1.0, 2.0, -2.0)
fire_delay = 2
dispersion = list(5, 10, 15, 20)
jam_chance = 20
needspin = FALSE

View File

@@ -105,7 +105,7 @@
firemodes = list(
list(mode_name="semiauto", burst=1, fire_delay=0, move_delay=null, burst_accuracy=null, dispersion=null),
list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0))
list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0, 10))
)

View File

@@ -225,7 +225,7 @@
firemodes = list(
list(mode_name="semiauto", burst=1, fire_delay=0, move_delay=null, burst_accuracy=null, dispersion=null),
list(mode_name="2-round bursts", burst=2, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0))
list(mode_name="2-round bursts", burst=2, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0, 8))
)
/obj/item/weapon/gun/projectile/automatic/rifle/w556/verb/scope()

View File

@@ -1,37 +1,22 @@
/*
#define BRUTE "brute"
#define BURN "burn"
#define TOX "tox"
#define OXY "oxy"
#define CLONE "clone"
#define ADD "add"
#define SET "set"
*/
#define MUZZLE_EFFECT_PIXEL_INCREMENT 16 //How many pixels to move the muzzle flash up so your character doesn't look like they're shitting out lasers.
/obj/item/projectile
name = "projectile"
icon = 'icons/obj/projectiles.dmi'
icon_state = "bullet"
density = 1
unacidable = 1
anchored = 1 //There's a reason this is here, Mport. God fucking damn it -Agouri. Find&Fix by Pete. The reason this is here is to stop the curving of emitter shots.
density = TRUE
unacidable = TRUE
anchored = TRUE //There's a reason this is here, Mport. God fucking damn it -Agouri. Find&Fix by Pete. The reason this is here is to stop the curving of emitter shots.
pass_flags = PASSTABLE
mouse_opacity = 0
var/bumped = 0 //Prevents it from hitting more than one guy at once
animate_movement = 0 //Use SLIDE_STEPS in conjunction with legacy
var/projectile_type = /obj/item/projectile
var/def_zone = "" //Aiming at
var/mob/firer = null//Who shot it
var/silenced = 0 //Attack message
var/yo = null
var/xo = null
var/current = null
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
var/silenced = FALSE //Attack message
var/p_x = 16
var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center
var/shot_from = "" // name of the object which shot us
var/accuracy = 0
var/dispersion = 0.0
@@ -39,15 +24,14 @@
//used for shooting at blank range, you shouldn't be able to miss
var/can_miss = 0
var/damage = 10
var/damage_type = BRUTE //BRUTE, BURN, TOX, OXY, CLONE, HALLOSS are the only things that should be in here
var/nodamage = 0 //Determines if the projectile will skip any damage inflictions
var/taser_effect = 0 //If set then the projectile will apply it's agony damage using stun_effect_act() to mobs it hits, and other damage will be ignored
//Effects
var/damage = 10
var/damage_type = BRUTE //BRUTE, BURN, TOX, OXY, CLONE, HALLOSS are the only things that should be in here
var/nodamage = FALSE //Determines if the projectile will skip any damage inflictions
var/check_armour = "bullet" //Defines what armor to use when it hits things. Must be set to bullet, laser, energy,or bomb //Cael - bio and rad are also valid
var/projectile_type = /obj/item/projectile
var/penetrating = 0 //If greater than zero, the projectile will pass through dense objects as specified by on_penetrate()
var/kill_count = 50 //This will de-increment every process(). When 0, it will delete the projectile.
//Effects
var/stun = 0
var/weaken = 0
var/paralyze = 0
@@ -56,12 +40,13 @@
var/eyeblur = 0
var/drowsy = 0
var/agony = 0
var/incinerate = 0
var/embed = 0 // whether or not the projectile can embed itself in the mob
var/shrapnel_type //type of shrapnel the projectile leaves in its target.
var/hitscan = 0 // whether the projectile should be hitscan
var/step_delay = 1 // the delay between iterations if not a hitscan projectile
var/p_x = 16
var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center
//For Maim / Maiming.
var/maiming = 0 //Enables special limb dismemberment calculation; used primarily for ranged weapons that can maim, but do not do brute damage.
@@ -74,25 +59,51 @@
set maim_type to DROPLIMB_BLUNT to gib (Explode/Hamburger) the limb.
*/
// effect types to be used
var/muzzle_type
//Movement parameters
var/speed = 0.8 //Amount of deciseconds it takes for projectile to travel
var/pixel_speed = 33 //pixels per move - DO NOT FUCK WITH THIS UNLESS YOU ABSOLUTELY KNOW WHAT YOU ARE DOING OR UNEXPECTED THINGS /WILL/ HAPPEN!
var/Angle = 0
var/original_angle = 0 //Angle at firing
var/nondirectional_sprite = FALSE //Set TRUE to prevent projectiles from having their sprites rotated based on firing angle
var/yo = null
var/xo = null
var/atom/original // the target clicked (not necessarily where the projectile is headed). Should probably be renamed to 'target' or something.
var/turf/starting // the projectile's starting turf
var/list/permutated // we've passed through these atoms, don't try to hit them again
var/penetrating = 0 //If greater than zero, the projectile will pass through dense objects as specified by on_penetrate()
var/forcedodge = FALSE //to pass through everything
var/ignore_source_check = FALSE
//Fired processing vars
var/fired = FALSE //Have we been fired yet
var/paused = FALSE //for suspending the projectile midair
var/last_projectile_move = 0
var/last_process = 0
var/time_offset = 0
var/datum/point/vector/trajectory
var/trajectory_ignore_forcemove = FALSE //instructs forceMove to NOT reset our trajectory to the new location!
var/range = 50 //This will de-increment every step. When 0, it will deletze the projectile.
//Hitscan
var/hitscan = FALSE //Whether this is hitscan. If it is, speed is basically ignored.
var/list/beam_segments //assoc list of datum/point or datum/point/vector, start = end. Used for hitscan effect generation.
var/datum/point/beam_index
var/turf/hitscan_last //last turf touched during hitscanning.
var/tracer_type
var/muzzle_type
var/impact_type
var/datum/plot_vector/trajectory // used to plot the path of the projectile
var/datum/vector_loc/location // current location of the projectile in pixel space
var/matrix/effect_transform // matrix to rotate and scale projectile effects - putting it here so it doesn't
// have to be recreated multiple times
/obj/item/projectile/CanPass()
return TRUE
//TODO: make it so this is called more reliably, instead of sometimes by bullet_act() and sometimes not
/obj/item/projectile/proc/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null)
if(blocked >= 100)
return 0//Full block
if(blocked >= 100) //Full block
return FALSE
if(!isliving(target))
return 0
return FALSE
if(isanimal(target))
return 0
return FALSE
var/mob/living/L = target
var/splatter_color = "#A10808"
@@ -127,88 +138,42 @@
//called when the projectile stops flying because it collided with something
/obj/item/projectile/proc/on_impact(var/atom/A)
impact_effect(effect_transform) // generate an impact effect
return
//Checks if the projectile is eligible for embedding. Not that it necessarily will.
/obj/item/projectile/proc/can_embed()
//embed must be enabled and damage type must be brute
if(!embed || damage_type != BRUTE)
return 0
return 1
return FALSE
return TRUE
/obj/item/projectile/proc/get_structure_damage()
if(damage_type == BRUTE || damage_type == BURN)
return damage
return 0
return FALSE
//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
/obj/item/projectile/proc/check_fire(atom/target as mob, var/mob/living/user as mob) //Checks if you can hit them or not.
check_trajectory(target, user, pass_flags, flags)
//sets the click point of the projectile using mouse input params
/obj/item/projectile/proc/set_clickpoint(var/params)
var/list/mouse_control = params2list(params)
if(mouse_control["icon-x"])
p_x = text2num(mouse_control["icon-x"])
if(mouse_control["icon-y"])
p_y = text2num(mouse_control["icon-y"])
//randomize clickpoint a bit based on dispersion
if(dispersion)
var/radius = round((dispersion*0.443)*world.icon_size*0.8) //0.443 = sqrt(pi)/4 = 2a, where a is the side length of a square that shares the same area as a circle with diameter = dispersion
p_x = between(0, p_x + rand(-radius, radius), world.icon_size)
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, var/angle_offset=0)
var/turf/curloc = get_turf(src)
var/turf/targloc = get_turf(target)
if (!istype(targloc) || !istype(curloc))
return 1
if(targloc == curloc) //Shooting something in the same turf
target.bullet_act(src, target_zone)
on_impact(target)
qdel(src)
return 0
//return TRUE if the projectile should be allowed to pass through after all, FALSE if not.
/obj/item/projectile/proc/check_penetrate(atom/A)
return TRUE
/obj/item/projectile/proc/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0)
original = target
def_zone = target_zone
firer = user
var/direct_target
if(get_turf(target) == get_turf(src))
direct_target = target
spawn()
setup_trajectory(curloc, targloc, x_offset, y_offset, angle_offset) //plot the initial trajectory
process()
return 0
preparePixelProjectile(target, user? user : get_turf(src), params, forced_spread)
return fire(angle_override, direct_target)
//called to launch a projectile from a gun
/obj/item/projectile/proc/launch_from_gun(atom/target, mob/user, obj/item/weapon/gun/launcher, var/target_zone, var/x_offset=0, var/y_offset=0)
if(user == target) //Shooting yourself
user.bullet_act(src, target_zone)
qdel(src)
return 0
/obj/item/projectile/proc/launch_from_gun(atom/target, target_zone, mob/user, params, angle_override, forced_spread, obj/item/weapon/gun/launcher)
loc = get_turf(user) //move the projectile out into the world
firer = user
shot_from = launcher.name
silenced = launcher.silenced
return launch(target, target_zone, x_offset, y_offset)
//Used to change the direction of the projectile in flight.
/obj/item/projectile/proc/redirect(var/new_x, var/new_y, var/atom/starting_loc, var/mob/new_firer=null)
var/turf/new_target = locate(new_x, new_y, src.z)
original = new_target
if(new_firer)
firer = src
setup_trajectory(starting_loc, new_target)
return launch_projectile(target, target_zone, user, params, angle_override, forced_spread)
//Called when the projectile intercepts a mob. Returns 1 if the projectile hit the mob, 0 if it missed and should keep flying.
/obj/item/projectile/proc/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier=0)
@@ -227,7 +192,7 @@
if(result == PROJECTILE_FORCE_MISS && (can_miss == 0)) //if you're shooting at point blank you can't miss.
if(!silenced)
target_mob.visible_message("<span class='notice'>\The [src] misses [target_mob] narrowly!</span>")
return 0
return FALSE
//hit messages
if(silenced)
@@ -250,26 +215,27 @@
//sometimes bullet_act() will want the projectile to continue flying
if (result == PROJECTILE_CONTINUE)
return 0
return FALSE
return 1
return TRUE
/obj/item/projectile/Collide(atom/A, forced = 0)
/obj/item/projectile/Collide(atom/A)
. = ..()
if(A == src)
return 0 //no
return FALSE //no.
if(A == firer)
loc = A.loc
return 0 //cannot shoot yourself
if(A in permutated)
return FALSE
if((bumped && !forced) || (A in permutated))
return 0
if(firer && !ignore_source_check)
if(A == firer || (A == firer.loc && istype(A, /obj/mecha))) //cannot shoot yourself or your mech
trajectory_ignore_forcemove = TRUE
forceMove(get_turf(A))
trajectory_ignore_forcemove = FALSE
return FALSE
var/passthrough = 0 //if the projectile should continue flying
var/distance = get_dist(starting,loc)
bumped = 1
var/distance = get_dist(get_turf(A), starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations.
var/passthrough = FALSE //if the projectile should continue flying
if(ismob(A))
var/mob/M = A
if(istype(A, /mob/living))
@@ -277,12 +243,12 @@
var/obj/item/weapon/grab/G = locate() in M
if(G && G.state >= GRAB_NECK)
visible_message("<span class='danger'>\The [M] uses [G.affecting] as a shield!</span>")
if(Collide(G.affecting, forced = 1))
if(Collide(G.affecting))
return //If Collide() returns 0 (keep going) then we continue on to attack M.
passthrough = !attack_mob(M, distance)
else
passthrough = 1 //so ghosts don't stop bullets
passthrough = TRUE //so ghosts don't stop bullets
else
passthrough = (A.bullet_act(src, def_zone) == PROJECTILE_CONTINUE) //backwards compatibility
if(isturf(A))
@@ -294,211 +260,323 @@
//penetrating projectiles can pass through things that otherwise would not let them
if(!passthrough && penetrating > 0)
if(check_penetrate(A))
passthrough = 1
passthrough = TRUE
penetrating--
//the bullet passes through a dense object!
if(passthrough)
if(passthrough || forcedodge)
//move ourselves onto A so we can continue on our way.
if(A)
trajectory_ignore_forcemove = TRUE
if(istype(A, /turf))
loc = A
forceMove(A)
else
loc = A.loc
forceMove(get_turf(A))
trajectory_ignore_forcemove = FALSE
permutated.Add(A)
bumped = 0 //reset bumped variable!
return 0
return FALSE
//stop flying
on_impact(A)
density = 0
invisibility = 101
qdel(src)
return 1
return TRUE
/obj/item/projectile/ex_act()
return //explosions probably shouldn't delete projectiles
/obj/item/projectile/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
return 1
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/obj/item/projectile/proc/old_style_target(atom/target, atom/source)
if(!source)
source = get_turf(src)
setAngle(Get_Angle(source, target))
/obj/item/projectile/process()
var/first_step = 1
spawn while(src && src.loc)
if(kill_count-- < 1)
on_impact(src.loc) //for any final impact behaviours
qdel(src)
return
if((!( current ) || loc == current))
current = locate(min(max(x + xo, 1), world.maxx), min(max(y + yo, 1), world.maxy), z)
if((x == 1 || x == world.maxx || y == 1 || y == world.maxy))
/obj/item/projectile/proc/fire(angle, atom/direct_target)
//If no angle needs to resolve it from xo/yo!
if(direct_target)
direct_target.bullet_act(src, def_zone)
on_impact(direct_target)
qdel(src)
return
if(isnum(angle))
setAngle(angle)
// trajectory dispersion
var/turf/starting = get_turf(src)
if(isnull(Angle)) //Try to resolve through offsets if there's no angle set.
if(isnull(xo) || isnull(yo))
crash_with("WARNING: Projectile [type] deleted due to being unable to resolve a target after angle was null!")
qdel(src)
return
var/turf/target = locate(Clamp(starting + xo, 1, world.maxx), Clamp(starting + yo, 1, world.maxy), starting.z)
setAngle(Get_Angle(src, target))
if(dispersion)
setAngle(Angle + rand(-dispersion, dispersion))
original_angle = Angle
if(!nondirectional_sprite)
var/matrix/M = new
M.Turn(Angle)
transform = M
forceMove(starting)
trajectory = new(starting.x, starting.y, starting.z, 0, 0, Angle, pixel_speed)
last_projectile_move = world.time
fired = TRUE
if(hitscan)
return process_hitscan()
else
if(!isprocessing)
START_PROCESSING(SSprojectiles, src)
pixel_move(1) //move it now!
trajectory.increment() // increment the current location
location = trajectory.return_location(location) // update the locally stored location data
/obj/item/projectile/proc/preparePixelProjectile(atom/target, atom/source, params, angle_offset = 0)
var/turf/curloc = get_turf(source)
var/turf/targloc = get_turf(target)
forceMove(get_turf(source))
starting = get_turf(source)
original = target
if(targloc || !params)
yo = targloc.y - curloc.y
xo = targloc.x - curloc.x
setAngle(Get_Angle(src, targloc))
if(!location)
qdel(src) // if it's left the world... kill it
return
var/list/calculated = list(null,null,null)
if(isliving(source) && params)
calculated = calculate_projectile_angle_and_pixel_offsets(source, params)
p_x = calculated[2]
p_y = calculated[3]
setAngle(calculated[1])
before_move()
Move(location.return_turf())
if(!bumped && !isturf(original))
if(loc == get_turf(original))
if(!(original in permutated))
if(Collide(original))
return
if(first_step)
muzzle_effect(effect_transform)
first_step = 0
else if(!bumped)
tracer_effect(effect_transform)
if(!hitscan)
sleep(step_delay) //add delay between movement iterations if it's not a hitscan weapon
else if(targloc)
yo = targloc.y - curloc.y
xo = targloc.x - curloc.x
setAngle(Get_Angle(src, targloc))
else
crash_with("WARNING: Projectile [type] fired without either mouse parameters, or a target atom to aim at!")
qdel(src)
if(angle_offset)
setAngle(Angle + angle_offset)
/obj/item/projectile/proc/before_move()
return 0
/obj/item/projectile/proc/setup_trajectory(turf/startloc, turf/targloc, var/x_offset = 0, var/y_offset = 0)
// setup projectile state
starting = startloc
current = startloc
yo = targloc.y - startloc.y + y_offset
xo = targloc.x - startloc.x + x_offset
// trajectory dispersion
var/offset = 0
if(dispersion)
var/radius = round(dispersion*9, 1)
offset = rand(-radius, radius)
// plot the initial trajectory
trajectory = new()
trajectory.setup(starting, original, pixel_x, pixel_y, angle_offset=offset)
// generate this now since all visual effects the projectile makes can use it
effect_transform = new()
effect_transform.Scale(trajectory.return_hypotenuse(), 1)
effect_transform.Turn(-trajectory.return_angle()) //no idea why this has to be inverted, but it works
transform = turn(transform, -(trajectory.return_angle() + 90)) //no idea why 90 needs to be added, but it works
/obj/item/projectile/proc/muzzle_effect(var/matrix/T)
if(silenced)
return
if(ispath(muzzle_type))
var/obj/effect/projectile/M = new muzzle_type(get_turf(src))
if(istype(M))
M.set_transform(T)
M.pixel_x = location.pixel_x
M.pixel_y = location.pixel_y
M.activate()
/obj/item/projectile/proc/tracer_effect(var/matrix/M)
if(ispath(tracer_type))
var/obj/effect/projectile/P = new tracer_type(location.loc)
if(istype(P))
P.set_transform(M)
P.pixel_x = location.pixel_x
P.pixel_y = location.pixel_y
if(!hitscan)
P.activate(step_delay) //if not a hitscan projectile, remove after a single delay
else
P.activate()
/obj/item/projectile/proc/impact_effect(var/matrix/M)
if (!location)
return//Combat drones sometimes cause a runtime error with null location. Impact effect isnt important
if(ispath(tracer_type))
var/obj/effect/projectile/P = new impact_type(location.loc)
if(istype(P))
P.set_transform(M)
P.pixel_x = location.pixel_x
P.pixel_y = location.pixel_y
P.activate()
//"Tracing" projectile
/obj/item/projectile/test //Used to see if you can hit them.
invisibility = 101 //Nope! Can't see me!
yo = null
xo = null
var/result = 0 //To pass the message back to the gun.
/obj/item/projectile/test/Collide(atom/A)
if(A == firer)
loc = A.loc
return //cannot shoot yourself
if(istype(A, /obj/item/projectile))
return
if(istype(A, /mob/living) || istype(A, /obj/mecha) || istype(A, /obj/vehicle))
result = 2 //We hit someone, return 1!
return
result = 1
return
/obj/item/projectile/test/launch(atom/target)
var/turf/curloc = get_turf(src)
var/turf/targloc = get_turf(target)
if(!curloc || !targloc)
return 0
/obj/item/projectile/proc/after_move()
return
original = target
/obj/item/projectile/Crossed(atom/movable/AM) //A mob moving on a tile with a projectile is hit by it.
..()
if(isliving(AM) && (AM.density || AM == original) && !(pass_flags & PASSMOB))
Collide(AM)
//plot the initial trajectory
setup_trajectory(curloc, targloc)
return process(targloc)
/obj/item/projectile/Initialize()
. = ..()
permutated = list()
/obj/item/projectile/test/process(var/turf/targloc)
var/safety = 100 // We really never should need this to last longer than this number of iterations.
while(!QDELING(src) && safety > 0) //Loop on through!
if(result)
return (result - 1)
if((!( targloc ) || loc == targloc))
targloc = locate(min(max(x + xo, 1), world.maxx), min(max(y + yo, 1), world.maxy), z) //Finding the target turf at map edge
/obj/item/projectile/proc/pixel_move(moves, trajectory_multiplier = 1, hitscanning = FALSE)
if(!loc || !trajectory)
if(!QDELETED(src))
if(loc)
on_impact(loc)
qdel(src)
return
last_projectile_move = world.time
if(!nondirectional_sprite && !hitscanning)
var/matrix/M = new
M.Turn(Angle)
transform = M
trajectory.increment(trajectory_multiplier)
var/turf/T = trajectory.return_turf()
if(T.z != loc.z)
before_move()
before_z_change(loc, T)
trajectory_ignore_forcemove = TRUE
forceMove(T)
trajectory_ignore_forcemove = FALSE
after_move()
if(!hitscanning)
pixel_x = trajectory.return_px()
pixel_y = trajectory.return_py()
else
before_move()
step_towards(src, T)
after_move()
if(!hitscanning)
pixel_x = trajectory.return_px() - trajectory.mpx * trajectory_multiplier
pixel_y = trajectory.return_py() - trajectory.mpy * trajectory_multiplier
if(!hitscanning)
animate(src, pixel_x = trajectory.return_px(), pixel_y = trajectory.return_py(), time = 1, flags = ANIMATION_END_NOW)
if(isturf(loc))
hitscan_last = loc
if(can_hit_target(original, permutated))
Collide(original, TRUE)
Range()
trajectory.increment() // increment the current location
location = trajectory.return_location(location) // update the locally stored location data
//Returns true if the target atom is on our current turf and above the right layer
/obj/item/projectile/proc/can_hit_target(atom/target, var/list/passthrough)
return (target && ((target.layer >= TURF_LAYER + 0.3) || ismob(target)) && (loc == get_turf(target)) && (!(target in passthrough)))
Move(location.return_turf())
/proc/calculate_projectile_angle_and_pixel_offsets(mob/user, params)
var/list/mouse_control = params2list(params)
var/p_x = 0
var/p_y = 0
var/angle = 0
if(mouse_control["icon-x"])
p_x = text2num(mouse_control["icon-x"])
if(mouse_control["icon-y"])
p_y = text2num(mouse_control["icon-y"])
if(mouse_control["screen-loc"])
//Split screen-loc up into X+Pixel_X and Y+Pixel_Y
var/list/screen_loc_params = splittext(mouse_control["screen-loc"], ",")
var/mob/living/M = locate() in get_turf(src)
if(istype(M)) //If there is someting living...
return 1 //Return 1
else
M = locate() in get_step(src,targloc)
if(istype(M))
return 1
//Split X+Pixel_X up into list(X, Pixel_X)
var/list/screen_loc_X = splittext(screen_loc_params[1],":")
safety--
//Split Y+Pixel_Y up into list(Y, Pixel_Y)
var/list/screen_loc_Y = splittext(screen_loc_params[2],":")
var/x = text2num(screen_loc_X[1]) * 32 + text2num(screen_loc_X[2]) - 32
var/y = text2num(screen_loc_Y[1]) * 32 + text2num(screen_loc_Y[2]) - 32
if (safety < 0)
crash_with("test projectile process() maximum iterations exceeded, aborting!")
//Calculate the "resolution" of screen based on client's view and world's icon size. This will work if the user can view more tiles than average.
var/list/screenview = getviewsize(user.client.view)
var/screenviewX = screenview[1] * world.icon_size
var/screenviewY = screenview[2] * world.icon_size
//Helper proc to check if you can hit them or not.
/proc/check_trajectory(atom/target as mob|obj, atom/firer as mob|obj, var/pass_flags=PASSTABLE|PASSGLASS|PASSGRILLE, flags=null)
if(!istype(target) || !istype(firer))
return 0
var/ox = round(screenviewX/2) - user.client.pixel_x //"origin" x
var/oy = round(screenviewY/2) - user.client.pixel_y //"origin" y
angle = Atan2(y - oy, x - ox)
return list(angle, p_x, p_y)
var/obj/item/projectile/test/trace = new /obj/item/projectile/test(get_turf(firer)) //Making the test....
/obj/item/projectile/proc/Range()
range--
if(range <= 0 && loc)
on_range()
//Set the flags and pass flags to that of the real projectile...
if(!isnull(flags))
trace.flags = flags
trace.pass_flags = pass_flags
/obj/item/projectile/proc/on_range() //if we want there to be effects when they reach the end of their range
on_impact(loc)
qdel(src)
var/output = trace.launch(target) //Test it!
qdel(trace) //No need for it anymore
return output //Send it back to the gun!
/obj/item/projectile/proc/store_hitscan_collision(datum/point/pcache)
beam_segments[beam_index] = pcache
beam_index = pcache
beam_segments[beam_index] = null
/obj/item/projectile/proc/return_predicted_turf_after_moves(moves, forced_angle) //I say predicted because there's no telling that the projectile won't change direction/location in flight.
if(!trajectory && isnull(forced_angle) && isnull(Angle))
return FALSE
var/datum/point/vector/current = trajectory
if(!current)
var/turf/T = get_turf(src)
current = new(T.x, T.y, T.z, pixel_x, pixel_y, isnull(forced_angle)? Angle : forced_angle, pixel_speed)
var/datum/point/vector/v = current.return_vector_after_increments(moves)
return v.return_turf()
/obj/item/projectile/proc/return_pathing_turfs_in_moves(moves, forced_angle)
var/turf/current = get_turf(src)
var/turf/ending = return_predicted_turf_after_moves(moves, forced_angle)
return getline(current, ending)
/obj/item/projectile/proc/process_hitscan()
var/safety = range * 3
record_hitscan_start(RETURN_POINT_VECTOR_INCREMENT(src, Angle, MUZZLE_EFFECT_PIXEL_INCREMENT, 1))
while(loc && !QDELETED(src))
if(paused)
stoplag(1)
continue
if(safety-- <= 0)
qdel(src)
crash_with("WARNING: [type] projectile encountered infinite recursion during hitscanning in [__FILE__]/[__LINE__]!")
return //Kill!
pixel_move(1, 1, TRUE)
/obj/item/projectile/proc/record_hitscan_start(datum/point/pcache)
beam_segments = list() //initialize segment list with the list for the first segment
beam_index = pcache
beam_segments[beam_index] = null //record start.
/obj/item/projectile/proc/vol_by_damage()
if(src.damage)
return Clamp((src.damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then CLAMP the value between 30 and 100
else
return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume.
/obj/item/projectile/proc/before_z_change(turf/oldloc, turf/newloc)
var/datum/point/pcache = trajectory.copy_to()
if(hitscan)
store_hitscan_collision(pcache)
/obj/item/projectile/process()
last_process = world.time
if(!loc || !fired || !trajectory)
fired = FALSE
return PROCESS_KILL
if(paused || !isturf(loc))
last_projectile_move += world.time - last_process //Compensates for pausing, so it doesn't become a hitscan projectile when unpaused from charged up ticks.
return
var/elapsed_time_deciseconds = (world.time - last_projectile_move) + time_offset
time_offset = 0
var/required_moves = 0
if(speed > 0)
required_moves = Floor(elapsed_time_deciseconds / speed, 1)
if(required_moves > SSprojectiles.global_max_tick_moves)
var/overrun = required_moves - SSprojectiles.global_max_tick_moves
required_moves = SSprojectiles.global_max_tick_moves
time_offset += overrun * speed
time_offset += Modulus(elapsed_time_deciseconds, speed)
else
required_moves = SSprojectiles.global_max_tick_moves
if(!required_moves)
return
for(var/i in 1 to required_moves)
pixel_move(required_moves)
/obj/item/projectile/proc/setAngle(new_angle) //wrapper for overrides.
Angle = new_angle
if(!nondirectional_sprite)
var/matrix/M = new
M.Turn(Angle)
transform = M
if(trajectory)
trajectory.set_angle(new_angle)
return TRUE
/obj/item/projectile/forceMove(atom/target)
. = ..()
if(trajectory && !trajectory_ignore_forcemove && isturf(target))
trajectory.initialize_location(target.x, target.y, target.z, 0, 0)
/obj/item/projectile/Destroy()
if(hitscan)
if(loc && trajectory)
var/datum/point/pcache = trajectory.copy_to()
beam_segments[beam_index] = pcache
generate_hitscan_tracers()
STOP_PROCESSING(SSprojectiles, src)
return ..()
/obj/item/projectile/proc/generate_hitscan_tracers(cleanup = TRUE, duration = 3)
if(!length(beam_segments))
return
if(duration <= 0)
return
if(tracer_type)
for(var/datum/point/p in beam_segments)
generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration)
if(muzzle_type && !silenced)
var/datum/point/p = beam_segments[1]
var/atom/movable/thing = new muzzle_type
p.move_atom_to_src(thing)
var/matrix/M = new
M.Turn(original_angle)
thing.transform = M
QDEL_IN(thing, duration)
if(impact_type)
var/datum/point/p = beam_segments[beam_segments[beam_segments.len]]
var/atom/movable/thing = new impact_type
p.move_atom_to_src(thing)
var/matrix/M = new
M.Turn(Angle)
thing.transform = M
QDEL_IN(thing, duration)
if(cleanup)
for(var/i in beam_segments)
qdel(i)
beam_segments = null
QDEL_NULL(beam_index)

View File

@@ -10,9 +10,9 @@
hitscan = 1
invisibility = 101 //beam projectiles are invisible as they are rendered by the effect engine
muzzle_type = /obj/effect/projectile/laser/muzzle
tracer_type = /obj/effect/projectile/laser/tracer
impact_type = /obj/effect/projectile/laser/impact
muzzle_type = /obj/effect/projectile/muzzle/laser
tracer_type = /obj/effect/projectile/tracer/laser
impact_type = /obj/effect/projectile/impact/laser
/obj/item/projectile/beam/practice
name = "laser"
@@ -37,9 +37,9 @@
damage = 60
armor_penetration = 30
muzzle_type = /obj/effect/projectile/laser_heavy/muzzle
tracer_type = /obj/effect/projectile/laser_heavy/tracer
impact_type = /obj/effect/projectile/laser_heavy/impact
muzzle_type = /obj/effect/projectile/muzzle/heavy_laser
tracer_type = /obj/effect/projectile/tracer/heavy_laser
impact_type = /obj/effect/projectile/impact/heavy_laser
/obj/item/projectile/beam/xray
name = "xray beam"
@@ -47,9 +47,9 @@
damage = 25
armor_penetration = 50
muzzle_type = /obj/effect/projectile/xray/muzzle
tracer_type = /obj/effect/projectile/xray/tracer
impact_type = /obj/effect/projectile/xray/impact
muzzle_type = /obj/effect/projectile/muzzle/xray
tracer_type = /obj/effect/projectile/tracer/xray
impact_type = /obj/effect/projectile/impact/xray
/obj/item/projectile/beam/pulse
name = "pulse"
@@ -57,9 +57,9 @@
damage = 50
armor_penetration = 50
muzzle_type = /obj/effect/projectile/laser_pulse/muzzle
tracer_type = /obj/effect/projectile/laser_pulse/tracer
impact_type = /obj/effect/projectile/laser_pulse/impact
muzzle_type = /obj/effect/projectile/muzzle/pulse
tracer_type = /obj/effect/projectile/tracer/pulse
impact_type = /obj/effect/projectile/impact/pulse
/obj/item/projectile/beam/pulse/on_hit(var/atom/target, var/blocked = 0)
if(isturf(target))
@@ -82,9 +82,9 @@
icon_state = "emitter"
damage = 0 // The actual damage is computed in /code/modules/power/singularity/emitter.dm
muzzle_type = /obj/effect/projectile/emitter/muzzle
tracer_type = /obj/effect/projectile/emitter/tracer
impact_type = /obj/effect/projectile/emitter/impact
muzzle_type = /obj/effect/projectile/muzzle/emitter
tracer_type = /obj/effect/projectile/tracer/emitter
impact_type = /obj/effect/projectile/impact/emitter
/obj/item/projectile/beam/lastertag/blue
name = "lasertag beam"
@@ -95,9 +95,9 @@
damage_type = BURN
check_armour = "laser"
muzzle_type = /obj/effect/projectile/laser_blue/muzzle
tracer_type = /obj/effect/projectile/laser_blue/tracer
impact_type = /obj/effect/projectile/laser_blue/impact
muzzle_type = /obj/effect/projectile/muzzle/laser/blue
tracer_type = /obj/effect/projectile/tracer/laser/blue
impact_type = /obj/effect/projectile/impact/laser/blue
/obj/item/projectile/beam/lastertag/blue/on_hit(var/atom/target, var/blocked = 0)
if(istype(target, /mob/living/carbon/human))
@@ -130,9 +130,9 @@
damage_type = BURN
check_armour = "laser"
muzzle_type = /obj/effect/projectile/laser_omni/muzzle
tracer_type = /obj/effect/projectile/laser_omni/tracer
impact_type = /obj/effect/projectile/laser_omni/impact
muzzle_type = /obj/effect/projectile/muzzle/disabler
tracer_type = /obj/effect/projectile/tracer/disabler
impact_type = /obj/effect/projectile/impact/disabler
/obj/item/projectile/beam/lastertag/omni/on_hit(var/atom/target, var/blocked = 0)
if(istype(target, /mob/living/carbon/human))
@@ -150,9 +150,9 @@
weaken = 3
stutter = 3
muzzle_type = /obj/effect/projectile/xray/muzzle
tracer_type = /obj/effect/projectile/xray/tracer
impact_type = /obj/effect/projectile/xray/impact
muzzle_type = /obj/effect/projectile/muzzle/xray
tracer_type = /obj/effect/projectile/tracer/xray
impact_type = /obj/effect/projectile/impact/xray
/obj/item/projectile/beam/stun
name = "stun beam"
@@ -162,9 +162,9 @@
agony = 40
damage_type = HALLOSS
muzzle_type = /obj/effect/projectile/stun/muzzle
tracer_type = /obj/effect/projectile/stun/tracer
impact_type = /obj/effect/projectile/stun/impact
muzzle_type = /obj/effect/projectile/muzzle/stun
tracer_type = /obj/effect/projectile/tracer/stun
impact_type = /obj/effect/projectile/impact/stun
/obj/item/projectile/beam/gatlinglaser
name = "diffused laser"
@@ -172,9 +172,9 @@
damage = 10
no_attack_log = 1
muzzle_type = /obj/effect/projectile/laser_omni/muzzle
tracer_type = /obj/effect/projectile/laser_omni/tracer
impact_type = /obj/effect/projectile/laser_omni/impact
muzzle_type = /obj/effect/projectile/muzzle/disabler
tracer_type = /obj/effect/projectile/tracer/disabler
impact_type = /obj/effect/projectile/impact/disabler
/obj/item/projectile/beam/mousegun
name = "diffuse electrical arc"
@@ -182,9 +182,9 @@
nodamage = 1
damage_type = HALLOSS
muzzle_type = /obj/effect/projectile/stun/muzzle
tracer_type = /obj/effect/projectile/stun/tracer
impact_type = /obj/effect/projectile/stun/impact
muzzle_type = /obj/effect/projectile/muzzle/stun
tracer_type = /obj/effect/projectile/tracer/stun
impact_type = /obj/effect/projectile/impact/stun
/obj/item/projectile/beam/mousegun/on_impact(var/atom/A)
mousepulse(A, 1)
@@ -230,9 +230,9 @@
damage = 15
eyeblur = 4
muzzle_type = /obj/effect/projectile/laser/muzzle
tracer_type = /obj/effect/projectile/laser/tracer
impact_type = /obj/effect/projectile/laser/impact
muzzle_type = /obj/effect/projectile/muzzle/laser
tracer_type = /obj/effect/projectile/tracer/laser
impact_type = /obj/effect/projectile/impact/laser
/obj/item/projectile/beam/megaglaive
name = "thermal lance"
@@ -242,15 +242,9 @@
armor_penetration = 10
no_attack_log = 1
muzzle_type = /obj/effect/projectile/solar/muzzle
tracer_type = /obj/effect/projectile/solar/tracer
impact_type = /obj/effect/projectile/solar/impact
/obj/item/projectile/beam/megaglaive/New()
effect_transform = new()
effect_transform.Scale(2)
src.transform = effect_transform
..()
muzzle_type = /obj/effect/projectile/muzzle/solar
tracer_type = /obj/effect/projectile/tracer/solar
impact_type = /obj/effect/projectile/impact/solar
/obj/item/projectile/beam/megaglaive/on_impact(var/atom/A)
if(isturf(A))
@@ -275,9 +269,9 @@
damage = 1
no_attack_log = 1
muzzle_type = /obj/effect/projectile/solar/muzzle
tracer_type = /obj/effect/projectile/solar/tracer
impact_type = /obj/effect/projectile/solar/impact
muzzle_type = /obj/effect/projectile/muzzle/solar
tracer_type = /obj/effect/projectile/tracer/solar
impact_type = /obj/effect/projectile/impact/solar
/obj/item/projectile/beam/thermaldrill/on_impact(var/atom/A)
if(isturf(A))
@@ -306,9 +300,9 @@
eyeblur = 0 //Not bright or blinding
var/mob/living/ignore
muzzle_type = /obj/effect/projectile/cult/muzzle
tracer_type = /obj/effect/projectile/cult/tracer
impact_type = /obj/effect/projectile/cult/impact
muzzle_type = /obj/effect/projectile/muzzle/cult
tracer_type = /obj/effect/projectile/tracer/cult
impact_type = /obj/effect/projectile/impact/cult
/obj/item/projectile/beam/cult/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier=0)
//Harmlessly passes through cultists and constructs
@@ -324,9 +318,9 @@
damage = 10 //Stronger and better armor penetration, though still much weaker than a typical laser
armor_penetration = 10
muzzle_type = /obj/effect/projectile/cult/heavy/muzzle
tracer_type = /obj/effect/projectile/cult/heavy/tracer
impact_type = /obj/effect/projectile/cult/heavy/impact
muzzle_type = /obj/effect/projectile/muzzle/cult/heavy
tracer_type = /obj/effect/projectile/tracer/cult/heavy
impact_type = /obj/effect/projectile/impact/cult/heavy
/obj/item/projectile/beam/energy_net
name = "energy net projection"
@@ -334,9 +328,9 @@
nodamage = 1
damage_type = HALLOSS
muzzle_type = /obj/effect/projectile/xray/muzzle
tracer_type = /obj/effect/projectile/xray/tracer
impact_type = /obj/effect/projectile/xray/impact
muzzle_type = /obj/effect/projectile/muzzle/xray
tracer_type = /obj/effect/projectile/tracer/xray
impact_type = /obj/effect/projectile/impact/xray
/obj/item/projectile/beam/energy_net/on_hit(var/atom/netted)
do_net(netted)

View File

@@ -10,7 +10,7 @@
shrapnel_type = /obj/item/weapon/material/shard/shrapnel
var/mob_passthrough_check = 0
muzzle_type = /obj/effect/projectile/bullet/muzzle
muzzle_type = /obj/effect/projectile/muzzle/bullet
/obj/item/projectile/bullet/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null)
if (..(target, blocked, def_zone))
@@ -75,10 +75,6 @@
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/CollidedWith()
. = ..()
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/proc/get_pellets(var/distance)
var/pellet_loss = round((distance - 1)/range_step) //pellets lost due to distance
return max(pellets - pellet_loss, 1)
@@ -217,7 +213,7 @@
drowsy = 0
eyeblur = 0
damage_type = TOX
step_delay = 0.25
speed = 0.3
/obj/item/projectile/bullet/rifle/tranq/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null)
var/mob/living/L = target
@@ -313,7 +309,7 @@
sharp = 1
penetrating = 1
muzzle_type = /obj/effect/projectile/pulse_bullet/muzzle
muzzle_type = /obj/effect/projectile/muzzle/pulse
/obj/item/projectile/bullet/flechette/explosive
shrapnel_type = /obj/item/weapon/material/shard/shrapnel/flechette

View File

@@ -11,7 +11,7 @@
icon_state = "bullet"
damage = 5
agony = 10
kill_count = 15 //if the shell hasn't hit anything after travelling this far it just explodes.
range = 15 //if the shell hasn't hit anything after travelling this far it just explodes.
var/flash_range = 0
var/brightness = 7
var/light_duration = 5
@@ -106,9 +106,9 @@
damage = 60
damage_type = BRUTE
pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
kill_count = 100
range = 100
embed = 0
step_delay = 3
speed = 8
light_range = 4
light_color = "#b5ff5b"
@@ -125,63 +125,21 @@
src.transform = M
..()
/obj/item/projectile/energy/bfg/process()
var/first_step = 1
spawn while(src && src.loc)
if(kill_count-- < 1)
on_impact(src.loc) //for any final impact behaviours
qdel(src)
return
if((!( current ) || loc == current))
current = locate(min(max(x + xo, 1), world.maxx), min(max(y + yo, 1), world.maxy), z)
if((x == 1 || x == world.maxx || y == 1 || y == world.maxy))
qdel(src)
return
trajectory.increment() // increment the current location
location = trajectory.return_location(location) // update the locally stored location data
if(!location)
qdel(src) // if it's left the world... kill it
return
before_move()
Move(location.return_turf())
if(!bumped && !isturf(original))
if(loc == get_turf(original))
if(!(original in permutated))
if(Collide(original))
return
if(first_step)
muzzle_effect(effect_transform)
first_step = 0
else if(!bumped)
tracer_effect(effect_transform)
for(var/turf/T in range(1,src))
if(T.density)
T.ex_act(2)
playsound(src.loc, 'sound/magic/LightningShock.ogg', 75, 1)
for(var/obj/O in range(1,src))
if(O.density)
O.ex_act(2)
playsound(src.loc, 'sound/magic/LightningShock.ogg', 75, 1)
for(var/mob/living/M in range(1,src))
if(M == src.firer) //for the sake of courtesy we will not target our master)
continue
/obj/item/projectile/energy/bfg/after_move()
for(var/a in range(1, src))
if(isliving(a) && a != firer)
var/mob/living/M = a
if(M.stat == DEAD)
M.gib()
else
if(M.stat == DEAD)
M.gib()
else
M.apply_damage(60, BRUTE, "head")
playsound(src.loc, 'sound/magic/LightningShock.ogg', 75, 1)
if(!hitscan)
sleep(step_delay) //add delay between movement iterations if it's not a hitscan weapon
M.apply_damage(60, BRUTE, "head")
playsound(src, 'sound/magic/LightningShock.ogg', 75, 1)
else if(isturf(a) || isobj(a))
var/atom/A = a
if(!A.density)
continue
A.ex_act(2)
playsound(src, 'sound/magic/LightningShock.ogg', 75, 1)
/obj/item/projectile/energy/tesla
name = "tesla bolt"
@@ -190,9 +148,9 @@
damage = 10
damage_type = BURN
pass_flags = PASSTABLE | PASSGRILLE
kill_count = 40
range = 40
embed = 0
step_delay = 2
speed = 1.5
light_range = 5
light_color = "#b5ff5b"
@@ -203,14 +161,14 @@
/obj/item/projectile/energy/gravitydisabler
name = "gravity disabler"
icon = 'icons/effects/projectiles.dmi'
icon = 'icons/obj/projectiles.dmi'
icon_state = "bluespace"
damage = 0
damage_type = BRUTE
pass_flags = PASSTABLE | PASSGRILLE
kill_count = 10
range = 10
embed = 0
step_delay = 3
speed = 2
light_range = 4
light_color = "#b5ff5b"

View File

@@ -177,7 +177,7 @@
embed = 0 // nope
nodamage = 1
damage_type = HALLOSS
muzzle_type = /obj/effect/projectile/bullet/muzzle
muzzle_type = /obj/effect/projectile/muzzle/bullet
/obj/item/projectile/bullet/cannon
name ="armor-piercing shell"

View File

@@ -0,0 +1,38 @@
//Helper proc to check if you can hit them or not.
/proc/check_trajectory(atom/target as mob|obj, atom/firer as mob|obj, var/pass_flags=PASSTABLE|PASSGLASS|PASSGRILLE, flags=null)
if(!istype(target) || !istype(firer))
return 0
var/obj/item/projectile/test/trace = new /obj/item/projectile/test(get_turf(firer)) //Making the test....
//Set the flags and pass flags to that of the real projectile...
if(!isnull(flags))
trace.flags = flags
trace.pass_flags = pass_flags
return trace.launch_projectile(target) //Test it!
/obj/item/projectile/proc/_check_fire(atom/target as mob, var/mob/living/user as mob) //Checks if you can hit them or not.
check_trajectory(target, user, pass_flags, flags)
//"Tracing" projectile
/obj/item/projectile/test //Used to see if you can hit them.
invisibility = 101 //Nope! Can't see me!
hitscan = TRUE
nodamage = TRUE
damage = 0
var/list/hit = list()
/obj/item/projectile/test/process_hitscan()
. = ..()
if(!QDELING(src))
qdel(src)
return hit
/obj/item/projectile/test/Collide(atom/A)
if(A != src)
hit |= A
return ..()
/obj/item/projectile/test/attack_mob()
return

View File

@@ -7,7 +7,7 @@
var/spell/targeted/projectile/carried
penetrating = 0
kill_count = 10 //set by the duration of the spell
range = 10 //set by the duration of the spell
var/proj_trail = 0 //if it leaves a trail
var/proj_trail_lifespan = 0 //deciseconds

View File

@@ -29,11 +29,11 @@ If the spell_projectile is seeking, it will update its target every process and
projectile.shot_from = user //fired from the user
projectile.hitscan = !proj_step_delay
projectile.step_delay = proj_step_delay
projectile.speed = proj_step_delay
if(istype(projectile, /obj/item/projectile/spell_projectile))
var/obj/item/projectile/spell_projectile/SP = projectile
SP.carried = src //casting is magical
projectile.launch(target, target_zone="chest")
projectile.launch_projectile(target, target_zone="chest")
return
/spell/targeted/projectile/proc/choose_prox_targets(mob/user = usr, var/atom/movable/spell_holder)