mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-01-03 05:51:56 +00:00
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:
@@ -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!
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
12
code/_helpers/view.dm
Normal 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)
|
||||
@@ -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)
|
||||
|
||||
12
code/controllers/subsystems/processing/projectiles.dm
Normal file
12
code/controllers/subsystems/processing/projectiles.dm
Normal 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)
|
||||
227
code/datums/position_point_vector.dm
Normal file
227
code/datums/position_point_vector.dm
Normal 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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
56
code/game/objects/effects/projectile/projectile_effects.dm
Normal file
56
code/game/objects/effects/projectile/projectile_effects.dm
Normal 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)
|
||||
52
code/game/objects/effects/projectile/projectile_impact.dm
Normal file
52
code/game/objects/effects/projectile/projectile_impact.dm
Normal 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"
|
||||
46
code/game/objects/effects/projectile/projectile_muzzle.dm
Normal file
46
code/game/objects/effects/projectile/projectile_muzzle.dm
Normal 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"
|
||||
64
code/game/objects/effects/projectile/projectile_tracer.dm
Normal file
64
code/game/objects/effects/projectile/projectile_tracer.dm
Normal 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"
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -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... >.>
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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'
|
||||
),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
38
code/modules/projectiles/projectile/trace.dm
Normal file
38
code/modules/projectiles/projectile/trace.dm
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user