mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-24 04:27:35 +01:00
Ports hitscan from TG, adds it to *SELECT* things (#22754)
* this was just going to be a plasma cutter change but no had to make it look good, whats next, ai sat turrets (yes) * ai turret * Coca cola, don't forget the ice. Or the pulse * almost done, pull master than map edit * we ball * ah, chasms. * it's for the best. Fuck should not sleep checker though * hitscan reflection limiting system * Apply suggestions from code review Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com> * c-c-c-changes --------- Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com> Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com>
This commit is contained in:
co-authored by
Henri215
S34N
parent
49c9d6f65a
commit
357c62b6d7
@@ -75,6 +75,7 @@
|
||||
#define LYING_MOB_LAYER 3.8
|
||||
//#define MOB_LAYER 4 //For easy recordkeeping; this is a byond define
|
||||
#define ABOVE_MOB_LAYER 4.1
|
||||
#define HITSCAN_LAYER 4.2
|
||||
#define WALL_OBJ_LAYER 4.25
|
||||
#define EDGED_TURF_LAYER 4.3
|
||||
#define ON_EDGED_TURF_LAYER 4.35
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
var/BT = get_turf(B)
|
||||
if(AT == BT)
|
||||
return 1
|
||||
var/list/line = getline(A, B)
|
||||
var/list/line = get_line(A, B)
|
||||
for(var/turf/T in line)
|
||||
if(T == AT || T == BT)
|
||||
break
|
||||
|
||||
+41
-35
@@ -173,43 +173,49 @@ Turf and target are seperate in case you want to teleport some distance from a t
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Gets the turfs which are between the two given atoms. Including their positions
|
||||
* Only works for atoms on the same Z level which is not 0. So an atom located in a non turf won't work
|
||||
* Arguments:
|
||||
* * M - The source atom
|
||||
* * N - The target atom
|
||||
* Get a list of turfs in a line from `starting_atom` to `ending_atom`.
|
||||
*
|
||||
* Uses the ultra-fast [Bresenham Line-Drawing Algorithm](https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm).
|
||||
*/
|
||||
/proc/getline(atom/M, atom/N)//Ultra-Fast Bresenham Line-Drawing Algorithm
|
||||
if(!M.z || M.z != N.z) // Same Z level and not 0. Else all below breaks
|
||||
return list()
|
||||
var/px=M.x //starting x
|
||||
var/py=M.y
|
||||
var/line[] = list(locate(px,py,M.z))
|
||||
var/dx=N.x-px //x distance
|
||||
var/dy=N.y-py
|
||||
var/dxabs=abs(dx)//Absolute value of x distance
|
||||
var/dyabs=abs(dy)
|
||||
var/sdx=SIGN(dx) //Sign of x distance (+ or -)
|
||||
var/sdy=SIGN(dy)
|
||||
var/x=dxabs>>1 //Counters for steps taken, setting to distance/2
|
||||
var/y=dyabs>>1 //Bit-shifting makes me l33t. It also makes getline() unnessecarrily fast.
|
||||
var/j //Generic integer for counting
|
||||
if(dxabs>=dyabs) //x distance is greater than y
|
||||
for(j=0;j<dxabs;j++)//It'll take dxabs steps to get there
|
||||
y+=dyabs
|
||||
if(y>=dxabs) //Every dyabs steps, step once in y direction
|
||||
y-=dxabs
|
||||
py+=sdy
|
||||
px+=sdx //Step on in x direction
|
||||
line+=locate(px,py,M.z)//Add the turf to the list
|
||||
/proc/get_line(atom/starting_atom, atom/ending_atom)
|
||||
var/current_x_step = starting_atom.x//start at x and y, then add 1 or -1 to these to get every turf from starting_atom to ending_atom
|
||||
var/current_y_step = starting_atom.y
|
||||
var/starting_z = starting_atom.z
|
||||
|
||||
var/list/line = list(get_step(starting_atom, 0))//get_turf(atom) is faster than locate(x, y, z) //Get turf isn't defined yet so we use get step
|
||||
|
||||
var/x_distance = ending_atom.x - current_x_step //x distance
|
||||
var/y_distance = ending_atom.y - current_y_step
|
||||
|
||||
var/abs_x_distance = abs(x_distance)//Absolute value of x distance
|
||||
var/abs_y_distance = abs(y_distance)
|
||||
|
||||
var/x_distance_sign = SIGN(x_distance) //Sign of x distance (+ or -)
|
||||
var/y_distance_sign = SIGN(y_distance)
|
||||
|
||||
var/x = abs_x_distance >> 1 //Counters for steps taken, setting to distance/2
|
||||
var/y = abs_y_distance >> 1 //Bit-shifting makes me l33t. It also makes get_line() unnessecarrily fast.
|
||||
|
||||
if(abs_x_distance >= abs_y_distance) //x distance is greater than y
|
||||
for(var/distance_counter in 0 to (abs_x_distance - 1))//It'll take abs_x_distance steps to get there
|
||||
y += abs_y_distance
|
||||
|
||||
if(y >= abs_x_distance) //Every abs_y_distance steps, step once in y direction
|
||||
y -= abs_x_distance
|
||||
current_y_step += y_distance_sign
|
||||
|
||||
current_x_step += x_distance_sign //Step on in x direction
|
||||
line += locate(current_x_step, current_y_step, starting_z)//Add the turf to the list
|
||||
else
|
||||
for(j=0;j<dyabs;j++)
|
||||
x+=dxabs
|
||||
if(x>=dyabs)
|
||||
x-=dyabs
|
||||
px+=sdx
|
||||
py+=sdy
|
||||
line+=locate(px,py,M.z)
|
||||
for(var/distance_counter in 0 to (abs_y_distance - 1))
|
||||
x += abs_x_distance
|
||||
|
||||
if(x >= abs_y_distance)
|
||||
x -= abs_y_distance
|
||||
current_x_step += x_distance_sign
|
||||
|
||||
current_y_step += y_distance_sign
|
||||
line += locate(current_x_step, current_y_step, starting_z)
|
||||
return line
|
||||
|
||||
//Same as the thing below just for density and without support for atoms.
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
//Checks for obstacles from A to B
|
||||
var/obj/dummy = new(source.loc)
|
||||
dummy.pass_flags |= PASSTABLE
|
||||
for(var/turf/turf as anything in getline(source, target))
|
||||
for(var/turf/turf as anything in get_line(source, target))
|
||||
for(var/atom/movable/AM in turf)
|
||||
if(!AM.CanPass(dummy, turf, 1))
|
||||
qdel(dummy)
|
||||
|
||||
@@ -335,11 +335,11 @@
|
||||
unlock_sound = 'sound/items/rped.ogg'
|
||||
|
||||
/datum/AI_Module/upgrade_turrets/upgrade(mob/living/silicon/ai/AI)
|
||||
for(var/obj/machinery/porta_turret/turret in GLOB.machines)
|
||||
for(var/obj/machinery/porta_turret/ai_turret/turret in GLOB.machines)
|
||||
var/turf/T = get_turf(turret)
|
||||
if(is_station_level(T.z))
|
||||
turret.health += 30
|
||||
turret.eprojectile = /obj/item/projectile/beam/laser/heavylaser //Once you see it, you will know what it means to FEAR.
|
||||
turret.eprojectile = /obj/item/projectile/beam/laser/ai_turret/heavylaser //Once you see it, you will know what it means to FEAR.
|
||||
turret.eshot_sound = 'sound/weapons/lasercannonfire.ogg'
|
||||
|
||||
//Hostile Station Lockdown: Locks, bolts, and electrifies every airlock on the station. After 90 seconds, the doors reset.
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
return FALSE
|
||||
playsound(T, 'sound/magic/lightningshock.ogg', 50, TRUE)
|
||||
O.Beam(target, icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 1 SECONDS)
|
||||
for(var/turf/working in getline(O, T))
|
||||
for(var/turf/working in get_line(O, T))
|
||||
for(var/mob/living/L in working)
|
||||
if(!electrocute_mob(L, C.powernet, user)) // give a little bit of non-lethal counterplay against insuls
|
||||
L.Jitter(5 SECONDS)
|
||||
|
||||
@@ -67,6 +67,10 @@
|
||||
var/has_cover = TRUE //Hides the cover
|
||||
/// Deployment override to allow turret popup on/under dense turfs/objects, for admin/CC turrets
|
||||
var/deployment_override = FALSE
|
||||
/// What lethal mode projectile with the turret start with?
|
||||
var/initial_eprojectile = null
|
||||
/// What non-lethal mode projectile with the turret start with?
|
||||
var/initial_projectile = null
|
||||
|
||||
|
||||
/obj/machinery/porta_turret/Initialize(mapload)
|
||||
@@ -142,8 +146,12 @@
|
||||
egun = 1
|
||||
|
||||
if(/obj/item/gun/energy/pulse/turret)
|
||||
eprojectile = /obj/item/projectile/beam/pulse
|
||||
eprojectile = /obj/item/projectile/beam/pulse/hitscan
|
||||
eshot_sound = 'sound/weapons/pulse.ogg'
|
||||
if(initial_eprojectile)
|
||||
eprojectile = initial_eprojectile
|
||||
if(initial_projectile)
|
||||
projectile = initial_projectile
|
||||
|
||||
GLOBAL_LIST_EMPTY(turret_icons)
|
||||
/obj/machinery/porta_turret/update_icon_state()
|
||||
@@ -761,6 +769,13 @@ GLOBAL_LIST_EMPTY(turret_icons)
|
||||
A.throw_at(target, scan_range, 1)
|
||||
return A
|
||||
|
||||
/obj/machinery/porta_turret/ai_turret
|
||||
initial_eprojectile = /obj/item/projectile/beam/laser/ai_turret
|
||||
|
||||
/obj/machinery/porta_turret/ai_turret/disable
|
||||
name = "hallway turret"
|
||||
initial_projectile = /obj/item/projectile/beam/disabler
|
||||
|
||||
/obj/machinery/porta_turret/centcom
|
||||
name = "\improper Centcomm turret"
|
||||
enabled = FALSE
|
||||
|
||||
@@ -156,17 +156,18 @@
|
||||
icon_state = "mecha_pulse"
|
||||
energy_drain = 120
|
||||
origin_tech = "materials=3;combat=6;powerstorage=4"
|
||||
projectile = /obj/item/projectile/beam/pulse/heavy
|
||||
projectile = /obj/item/projectile/beam/pulse/hitscan/heavy
|
||||
fire_sound = 'sound/weapons/marauder.ogg'
|
||||
harmful = TRUE
|
||||
|
||||
|
||||
/obj/item/projectile/beam/pulse/heavy
|
||||
/obj/item/projectile/beam/pulse/hitscan/heavy
|
||||
name = "heavy pulse laser"
|
||||
icon_state = "pulse1_bl"
|
||||
var/life = 20
|
||||
|
||||
/obj/item/projectile/beam/pulse/heavy/Bump(atom/A)
|
||||
|
||||
/obj/item/projectile/beam/pulse/hitscan/heavy/Bump(atom/A)
|
||||
A.bullet_act(src, def_zone)
|
||||
life -= 10
|
||||
if(ismob(A))
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
//These are directional impact effects used by hitscan projectiles. Unlike current impact effect, they rotate to face the point the hitscan beam came from.
|
||||
|
||||
/obj/effect/projectile/impact
|
||||
name = "beam impact"
|
||||
icon = 'icons/obj/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 = "\improper X-ray 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/wormhole
|
||||
icon_state = "wormhole_g"
|
||||
|
||||
/obj/effect/projectile/impact/laser/emitter
|
||||
name = "emitter impact"
|
||||
icon_state = "impact_emitter"
|
||||
|
||||
/obj/effect/projectile/impact/solar
|
||||
name = "solar impact"
|
||||
icon_state = "impact_solar"
|
||||
|
||||
/obj/effect/projectile/impact/sniper
|
||||
icon_state = "sniper"
|
||||
|
||||
/obj/effect/projectile/impact/death
|
||||
icon_state = "impact_hcult"
|
||||
@@ -0,0 +1,48 @@
|
||||
//These are directional muzzle effects used by hitscan projectiles. Unlike current muzzle effect, they rotate to face the point the hitscan beam came from.
|
||||
|
||||
/obj/effect/projectile/muzzle
|
||||
name = "muzzle flash"
|
||||
icon = 'icons/obj/projectiles_muzzle.dmi'
|
||||
|
||||
/obj/effect/projectile/muzzle/laser
|
||||
icon_state = "muzzle_laser"
|
||||
|
||||
/obj/effect/projectile/muzzle/laser/blue
|
||||
icon_state = "muzzle_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/wormhole
|
||||
icon_state = "wormhole_g"
|
||||
|
||||
/obj/effect/projectile/muzzle/laser/emitter
|
||||
name = "emitter flash"
|
||||
icon_state = "muzzle_emitter"
|
||||
|
||||
/obj/effect/projectile/muzzle/solar
|
||||
icon_state = "muzzle_solar"
|
||||
|
||||
/obj/effect/projectile/muzzle/sniper
|
||||
icon_state = "sniper"
|
||||
|
||||
/obj/effect/projectile/muzzle/bullet
|
||||
icon_state = "muzzle_bullet"
|
||||
|
||||
/obj/effect/projectile/muzzle/death
|
||||
icon_state = "muzzle_hcult"
|
||||
@@ -0,0 +1,66 @@
|
||||
//This is the parent of all projectile effects, impact muzzle and tracer.
|
||||
|
||||
/obj/effect/projectile
|
||||
name = "pew"
|
||||
icon = 'icons/obj/projectiles.dmi'
|
||||
icon_state = "nothing"
|
||||
layer = HITSCAN_LAYER
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
appearance_flags = LONG_GLIDE
|
||||
|
||||
/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.appearance_flags = RESET_COLOR | RESET_ALPHA
|
||||
look.pixel_x = p_x
|
||||
look.pixel_y = p_y
|
||||
if(color_override)
|
||||
look.color = color_override
|
||||
look.appearance_flags = RESET_ALPHA
|
||||
else
|
||||
look.appearance_flags = RESET_COLOR | RESET_ALPHA
|
||||
icon_state = null
|
||||
add_overlay(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)
|
||||
|
||||
/obj/effect/projectile_lighting
|
||||
var/owner
|
||||
|
||||
/obj/effect/projectile_lighting/Initialize(mapload, color, range, intensity, owner_key)
|
||||
. = ..()
|
||||
set_light(range, intensity, color)
|
||||
owner = owner_key
|
||||
@@ -0,0 +1,99 @@
|
||||
//These are the visual tracers, the beams you see with hitscan effects. This is the proc that generates said tracer
|
||||
|
||||
/proc/generate_tracer_between_points(datum/position/starting, datum/position/ending, beam_type, color, qdel_in = 5, light_range = 2, light_color_override, light_intensity = 1, instance_key) //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(isnull(starting))
|
||||
return
|
||||
if(isnull(ending))
|
||||
return
|
||||
if(isnull(beam_type))
|
||||
return
|
||||
var/datum/position/midpoint = point_midpoint_points(starting, ending)
|
||||
var/performance_counter
|
||||
for(var/obj/effect/projectile/tracer/counter in midpoint.return_turf())
|
||||
performance_counter++
|
||||
if(performance_counter >= 3)
|
||||
return //Damage still happens, this should stop engineers with looping emitters doing shenanigins that makes the server cry
|
||||
|
||||
var/obj/effect/projectile/tracer/PB = new beam_type
|
||||
if(isnull(light_color_override))
|
||||
light_color_override = color
|
||||
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(light_range > 0 && light_intensity > 0)
|
||||
var/list/turf/line = get_line(starting.return_turf(), ending.return_turf())
|
||||
tracing_line:
|
||||
for(var/i in line)
|
||||
var/turf/T = i
|
||||
for(var/obj/effect/projectile_lighting/PL in T)
|
||||
if(PL.owner == locateUID(instance_key))
|
||||
continue tracing_line
|
||||
QDEL_IN(new /obj/effect/projectile_lighting(T, light_color_override, light_range, light_intensity, instance_key), qdel_in > 0? qdel_in : 5)
|
||||
line = null
|
||||
if(qdel_in)
|
||||
QDEL_IN(PB, qdel_in)
|
||||
|
||||
/obj/effect/projectile/tracer
|
||||
name = "beam"
|
||||
icon = 'icons/obj/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 = "\improper X-ray 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/solar
|
||||
name = "solar beam"
|
||||
icon_state = "solar"
|
||||
|
||||
/obj/effect/projectile/tracer/solar/thin
|
||||
icon_state = "solar_thin"
|
||||
|
||||
/obj/effect/projectile/tracer/solar/thinnest
|
||||
icon_state = "solar_thinnest"
|
||||
|
||||
//BEAM RIFLE
|
||||
/obj/effect/projectile/tracer/tracer/beam_rifle
|
||||
icon_state = "tracer_beam"
|
||||
|
||||
/obj/effect/projectile/tracer/tracer/aiming
|
||||
icon_state = "pixelbeam_greyscale"
|
||||
plane = ABOVE_LIGHTING_PLANE
|
||||
|
||||
/obj/effect/projectile/tracer/wormhole
|
||||
icon_state = "wormhole_g"
|
||||
|
||||
/obj/effect/projectile/tracer/laser/emitter
|
||||
name = "emitter beam"
|
||||
icon_state = "emitter"
|
||||
|
||||
/obj/effect/projectile/tracer/sniper
|
||||
icon_state = "sniper"
|
||||
|
||||
/obj/effect/projectile/tracer/death
|
||||
icon_state = "hcult"
|
||||
@@ -89,7 +89,7 @@
|
||||
if(user.get_active_hand() == src) // Make sure our user is still holding us
|
||||
var/turf/target_turf = get_turf(target)
|
||||
if(target_turf)
|
||||
var/turflist = getline(user, target_turf)
|
||||
var/turflist = get_line(user, target_turf)
|
||||
add_attack_logs(user, target, "Flamethrowered at [target.x],[target.y],[target.z]")
|
||||
flame_turf(turflist)
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
/obj/effect/spawner,
|
||||
/obj/structure/railing,
|
||||
/obj/machinery/atmospherics/pipe/simple,
|
||||
/obj/effect/projectile,
|
||||
/obj/effect/projectile_lighting,
|
||||
/mob/living/simple_animal/hostile/megafauna //failsafe
|
||||
))
|
||||
var/drop_x = 1
|
||||
|
||||
@@ -195,7 +195,7 @@
|
||||
should_recharge_after_cast = TRUE
|
||||
return
|
||||
var/wall_count
|
||||
for(var/turf/T in getline(target_turf, start_turf))
|
||||
for(var/turf/T in get_line(target_turf, start_turf))
|
||||
if(max_walls <= wall_count)
|
||||
break
|
||||
new /obj/structure/blood_barrier(T)
|
||||
|
||||
@@ -368,7 +368,7 @@
|
||||
alert_admins_on_craft = TRUE
|
||||
|
||||
/datum/crafting_recipe/pulseslug
|
||||
name = "Pulse Slug Shell"
|
||||
name = "Proto Pulse Slug Shell"
|
||||
result = list(/obj/item/ammo_casing/shotgun/pulseslug)
|
||||
reqs = list(/obj/item/ammo_casing/shotgun/techshell = 1,
|
||||
/obj/item/stock_parts/capacitor/adv = 2,
|
||||
|
||||
@@ -305,7 +305,7 @@
|
||||
if(STAGE_ATTACK)
|
||||
|
||||
if(!eating)
|
||||
for(var/I in getline(src, get_turf(H)))
|
||||
for(var/I in get_line(src, get_turf(H)))
|
||||
var/turf/T = I
|
||||
for(var/obj/structure/O in T)
|
||||
if(istype(O, /obj/structure/closet))
|
||||
|
||||
@@ -370,7 +370,7 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/CheckFriendlyFire(atom/A)
|
||||
if(check_friendly_fire)
|
||||
for(var/turf/T in getline(src,A)) // Not 100% reliable but this is faster than simulating actual trajectory
|
||||
for(var/turf/T in get_line(src,A)) // Not 100% reliable but this is faster than simulating actual trajectory
|
||||
for(var/mob/living/L in T)
|
||||
if(L == src || L == A)
|
||||
continue
|
||||
|
||||
@@ -239,7 +239,7 @@ Difficulty: Medium
|
||||
turf_dist_to_target += get_dist(dash_target, O)
|
||||
if(get_dist(src, O) >= MINER_DASH_RANGE && turf_dist_to_target <= self_dist_to_target && !islava(O) && !ischasm(O))
|
||||
var/valid = TRUE
|
||||
for(var/turf/T in getline(own_turf, O))
|
||||
for(var/turf/T in get_line(own_turf, O))
|
||||
if(is_blocked_turf(T, TRUE))
|
||||
valid = FALSE
|
||||
continue
|
||||
|
||||
@@ -267,7 +267,7 @@ Difficulty: Medium
|
||||
if(!check)
|
||||
break
|
||||
T = check
|
||||
return (getline(src, T) - get_turf(src))
|
||||
return (get_line(src, T) - get_turf(src))
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_line(list/turfs)
|
||||
SLEEP_CHECK_DEATH(0)
|
||||
|
||||
@@ -168,7 +168,7 @@ Difficulty: Medium
|
||||
/mob/living/simple_animal/hostile/megafauna/legion/proc/fire_disintegration_laser(location)
|
||||
playsound(loc, 'sound/weapons/marauder.ogg', 200, TRUE)
|
||||
Beam(location, icon_state = "death_laser", time = 2 SECONDS, maxdistance = INFINITY, beam_type = /obj/effect/ebeam/disintegration)
|
||||
for(var/turf/t in getline(src, location))
|
||||
for(var/turf/t in get_line(src, location))
|
||||
if(ismineralturf(t))
|
||||
var/turf/simulated/mineral/M = t
|
||||
M.gets_drilled(src)
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
health = 200
|
||||
maxHealth = 200
|
||||
speed = 8
|
||||
projectiletype = /obj/item/projectile/beam/immolator/weak
|
||||
projectiletype = /obj/item/projectile/beam/immolator/weak/hitscan
|
||||
projectilesound = 'sound/weapons/laser3.ogg'
|
||||
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
||||
minbodytemp = 0
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
for(var/mob/living/L in view(grasp_range, src))
|
||||
if(L == src || faction_check_mob(L) || (L in grasping) || L == target)
|
||||
continue
|
||||
for(var/t in getline(src,L))
|
||||
for(var/t in get_line(src,L))
|
||||
for(var/a in t)
|
||||
var/atom/A = a
|
||||
if(A.density && A != L)
|
||||
@@ -107,7 +107,7 @@
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/venus_human_trap/OpenFire(atom/the_target)
|
||||
for(var/turf/T in getline(src,target))
|
||||
for(var/turf/T in get_line(src,target))
|
||||
if (T.density)
|
||||
return
|
||||
for(var/obj/O in T)
|
||||
|
||||
@@ -282,7 +282,7 @@
|
||||
L.forceMove(locate(locx,locy,mobloc.z))
|
||||
spawn(0)
|
||||
var/limit = 2//For only two trailing shadows.
|
||||
for(var/turf/T in getline(mobloc, L.loc))
|
||||
for(var/turf/T in get_line(mobloc, L.loc))
|
||||
new /obj/effect/temp_visual/dir_setting/ninja/shadow(T, L.dir)
|
||||
limit--
|
||||
if(limit<=0)
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
/// Locked by an ID card
|
||||
var/locked = FALSE
|
||||
|
||||
var/projectile_type = /obj/item/projectile/beam/emitter
|
||||
var/projectile_type = /obj/item/projectile/beam/emitter/hitscan
|
||||
var/projectile_sound = 'sound/weapons/emitter.ogg'
|
||||
var/datum/effect_system/spark_spread/sparks
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@
|
||||
projectile_type = /obj/item/projectile/bullet/meteorshot
|
||||
|
||||
/obj/item/ammo_casing/shotgun/pulseslug
|
||||
name = "pulse slug"
|
||||
name = "proto pulse slug"
|
||||
desc = "A delicate device which can be loaded into a shotgun. The primer acts as a button which triggers the gain medium and fires a powerful \
|
||||
energy blast. While the heat and power drain limit it to one use, it can still allow an operator to engage targets that ballistic ammunition \
|
||||
would have difficulty with."
|
||||
|
||||
@@ -42,14 +42,14 @@
|
||||
fire_sound = 'sound/weapons/lasercannonfire.ogg'
|
||||
|
||||
/obj/item/ammo_casing/energy/laser/pulse
|
||||
projectile_type = /obj/item/projectile/beam/pulse
|
||||
projectile_type = /obj/item/projectile/beam/pulse/hitscan
|
||||
muzzle_flash_color = LIGHT_COLOR_DARKBLUE
|
||||
e_cost = 200
|
||||
select_name = "DESTROY"
|
||||
fire_sound = 'sound/weapons/pulse.ogg'
|
||||
|
||||
/obj/item/ammo_casing/energy/laser/scatter/pulse
|
||||
projectile_type = /obj/item/projectile/beam/pulse
|
||||
projectile_type = /obj/item/projectile/beam/pulse/hitscan
|
||||
e_cost = 200
|
||||
select_name = "ANNIHILATE"
|
||||
fire_sound = 'sound/weapons/pulse.ogg'
|
||||
@@ -286,7 +286,7 @@
|
||||
select_name = "clown"
|
||||
|
||||
/obj/item/ammo_casing/energy/emitter
|
||||
projectile_type = /obj/item/projectile/beam/emitter
|
||||
projectile_type = /obj/item/projectile/beam/emitter/hitscan
|
||||
muzzle_flash_color = LIGHT_COLOR_GREEN
|
||||
fire_sound = 'sound/weapons/emitter.ogg'
|
||||
e_cost = 100
|
||||
|
||||
@@ -417,42 +417,11 @@
|
||||
reset_overloaded()
|
||||
do_sparks(2, 1, src)
|
||||
update_icon()
|
||||
if(prob(25))
|
||||
visible_message("<span class='danger'>[src] vents heated plasma!</span>")
|
||||
var/turf/simulated/T = get_turf(src)
|
||||
if(istype(T))
|
||||
T.atmos_spawn_air(LINDA_SPAWN_TOXINS|LINDA_SPAWN_20C,15)
|
||||
return
|
||||
if(prob(50))
|
||||
var/list/mob_targets = list()
|
||||
for(var/mob/living/M in oview(get_turf(src), 7))
|
||||
mob_targets += M
|
||||
if(length(mob_targets))
|
||||
var/mob/living/target = pick(mob_targets)
|
||||
shootAt(target)
|
||||
visible_message("<span class='danger'>[src] discharges a plasma bolt!</span>")
|
||||
return
|
||||
visible_message("<span class='danger'>[src] vents heated plasma!</span>")
|
||||
var/turf/simulated/T = get_turf(src)
|
||||
if(istype(T))
|
||||
T.atmos_spawn_air(LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS|LINDA_SPAWN_20C, 20)
|
||||
|
||||
visible_message("<span class='danger'>[src] discharges a plasma bolt!</span>")
|
||||
var/list/turf_targets = list()
|
||||
for(var/turf/T in orange(get_turf(src), 7))
|
||||
turf_targets += T
|
||||
if(length(turf_targets))
|
||||
var/turf/target = pick(turf_targets)
|
||||
shootAt(target)
|
||||
|
||||
|
||||
/obj/item/gun/energy/plasma_pistol/proc/shootAt(atom/movable/target)
|
||||
var/turf/T = get_turf(src)
|
||||
var/turf/U = get_turf(target)
|
||||
if(!T || !U)
|
||||
return
|
||||
var/obj/item/projectile/energy/charged_plasma/O = new /obj/item/projectile/energy/charged_plasma(T)
|
||||
playsound(get_turf(src), 'sound/weapons/marauder.ogg', 75, 1)
|
||||
O.current = T
|
||||
O.yo = U.y - T.y
|
||||
O.xo = U.x - T.x
|
||||
O.fire()
|
||||
|
||||
/obj/item/gun/energy/bsg
|
||||
name = "\improper B.S.G"
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
return FALSE
|
||||
var/obj/dummy = new(user_turf)
|
||||
dummy.pass_flags |= PASSTABLE | PASSGLASS | PASSGRILLE | PASSFENCE //Grille/Glass so it can be used through common windows
|
||||
for(var/turf/turf in getline(user_turf,target))
|
||||
for(var/turf/turf in get_line(user_turf,target))
|
||||
if(turf.density)
|
||||
qdel(dummy)
|
||||
return FALSE
|
||||
|
||||
@@ -33,6 +33,39 @@
|
||||
icon_state = "heavylaser"
|
||||
damage = 40
|
||||
|
||||
/obj/item/projectile/beam/laser/ai_turret
|
||||
damage = 17.5 //Slight loss in damage because hitscan
|
||||
forcedodge = 1
|
||||
hitscan = TRUE
|
||||
muzzle_type = /obj/effect/projectile/muzzle/laser
|
||||
tracer_type = /obj/effect/projectile/tracer/laser
|
||||
impact_type = /obj/effect/projectile/impact/laser
|
||||
impact_effect_type = null
|
||||
hitscan_light_intensity = 3
|
||||
hitscan_light_range = 0.75
|
||||
hitscan_light_color_override = LIGHT_COLOR_DARKRED
|
||||
muzzle_flash_intensity = 6
|
||||
muzzle_flash_range = 2
|
||||
muzzle_flash_color_override = LIGHT_COLOR_DARKRED
|
||||
impact_light_intensity = 7
|
||||
impact_light_range = 2.5
|
||||
impact_light_color_override = LIGHT_COLOR_DARKRED
|
||||
|
||||
/obj/item/projectile/beam/laser/ai_turret/prehit(atom/target)
|
||||
if(isAI(target))
|
||||
damage = 0 //cheater cheater I don't want AI to die to stupid placement eater
|
||||
nodamage = 1
|
||||
if(isliving(target))
|
||||
forcedodge = 0 //no peircing after hitting a mob to avoid rooms destroying itself. If someone does monkey chair on AI sat though, I swear to god
|
||||
..()
|
||||
|
||||
/obj/item/projectile/beam/laser/ai_turret/heavylaser
|
||||
damage = 35
|
||||
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/practice
|
||||
name = "practice laser"
|
||||
damage = 0
|
||||
@@ -73,12 +106,31 @@
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser
|
||||
light_color = LIGHT_COLOR_DARKBLUE
|
||||
|
||||
/obj/item/projectile/beam/pulse/hitscan
|
||||
impact_effect_type = null
|
||||
light_color = null
|
||||
hitscan = TRUE
|
||||
muzzle_type = /obj/effect/projectile/muzzle/pulse
|
||||
tracer_type = /obj/effect/projectile/tracer/pulse
|
||||
impact_type = /obj/effect/projectile/impact/pulse
|
||||
impact_effect_type = null
|
||||
hitscan_light_intensity = 3
|
||||
hitscan_light_range = 0.75
|
||||
hitscan_light_color_override = LIGHT_COLOR_DARKBLUE
|
||||
muzzle_flash_intensity = 6
|
||||
muzzle_flash_range = 2
|
||||
muzzle_flash_color_override = LIGHT_COLOR_DARKBLUE
|
||||
impact_light_intensity = 7
|
||||
impact_light_range = 2.5
|
||||
impact_light_color_override = LIGHT_COLOR_DARKBLUE
|
||||
|
||||
/obj/item/projectile/beam/pulse/on_hit(atom/target, blocked = 0)
|
||||
if(isturf(target) || isstructure(target) || ismachinery(target))
|
||||
target.ex_act(2)
|
||||
..()
|
||||
|
||||
/obj/item/projectile/beam/pulse/shot
|
||||
name = "proto pulse"
|
||||
damage = 40
|
||||
|
||||
/obj/item/projectile/beam/emitter
|
||||
@@ -88,6 +140,22 @@
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser
|
||||
light_color = LIGHT_COLOR_GREEN
|
||||
|
||||
/obj/item/projectile/beam/emitter/hitscan
|
||||
hitscan = TRUE
|
||||
muzzle_type = /obj/effect/projectile/muzzle/laser/emitter
|
||||
tracer_type = /obj/effect/projectile/tracer/laser/emitter
|
||||
impact_type = /obj/effect/projectile/impact/laser/emitter
|
||||
impact_effect_type = null
|
||||
hitscan_light_intensity = 3
|
||||
hitscan_light_range = 0.75
|
||||
hitscan_light_color_override = LIGHT_COLOR_GREEN
|
||||
muzzle_flash_intensity = 6
|
||||
muzzle_flash_range = 2
|
||||
muzzle_flash_color_override = LIGHT_COLOR_GREEN
|
||||
impact_light_intensity = 7
|
||||
impact_light_range = 2.5
|
||||
impact_light_color_override = LIGHT_COLOR_GREEN
|
||||
|
||||
/obj/item/projectile/beam/emitter/singularity_pull()
|
||||
return //don't want the emitters to miss
|
||||
|
||||
@@ -141,6 +209,23 @@
|
||||
damage = 8
|
||||
icon_state = "scatterlaser"
|
||||
|
||||
/obj/item/projectile/beam/immolator/weak/hitscan
|
||||
color = LIGHT_COLOR_FIRE
|
||||
hitscan = TRUE
|
||||
muzzle_type = /obj/effect/projectile/muzzle/laser
|
||||
tracer_type = /obj/effect/projectile/tracer/laser
|
||||
impact_type = /obj/effect/projectile/impact/laser
|
||||
impact_effect_type = null
|
||||
hitscan_light_intensity = 3
|
||||
hitscan_light_range = 0.75
|
||||
hitscan_light_color_override = LIGHT_COLOR_FIRE
|
||||
muzzle_flash_intensity = 6
|
||||
muzzle_flash_range = 2
|
||||
muzzle_flash_color_override = LIGHT_COLOR_FIRE
|
||||
impact_light_intensity = 7
|
||||
impact_light_range = 2.5
|
||||
impact_light_color_override = LIGHT_COLOR_FIRE
|
||||
|
||||
/obj/item/projectile/beam/immolator/on_hit(atom/target, blocked = 0)
|
||||
. = ..()
|
||||
if(isliving(target))
|
||||
|
||||
@@ -9,7 +9,20 @@
|
||||
|
||||
/obj/item/projectile/magic/death
|
||||
name = "bolt of death"
|
||||
icon_state = "pulse1_bl"
|
||||
icon_state = null
|
||||
hitscan = TRUE
|
||||
muzzle_type = /obj/effect/projectile/muzzle/death
|
||||
tracer_type = /obj/effect/projectile/tracer/death
|
||||
impact_type = /obj/effect/projectile/impact/death
|
||||
hitscan_light_intensity = 3
|
||||
hitscan_light_range = 0.75
|
||||
hitscan_light_color_override = LIGHT_COLOR_PURPLE
|
||||
muzzle_flash_intensity = 6
|
||||
muzzle_flash_range = 2
|
||||
muzzle_flash_color_override = LIGHT_COLOR_PURPLE
|
||||
impact_light_intensity = 7
|
||||
impact_light_range = 2.5
|
||||
impact_light_color_override = LIGHT_COLOR_PURPLE
|
||||
|
||||
/obj/item/projectile/magic/fireball
|
||||
name = "bolt of fireball"
|
||||
|
||||
@@ -240,7 +240,20 @@
|
||||
range = 3
|
||||
dismemberment = 20
|
||||
sharp = TRUE
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser
|
||||
hitscan = TRUE
|
||||
muzzle_type = /obj/effect/projectile/muzzle/plasma_cutter
|
||||
tracer_type = /obj/effect/projectile/tracer/plasma_cutter
|
||||
impact_type = /obj/effect/projectile/impact/plasma_cutter
|
||||
impact_effect_type = null
|
||||
hitscan_light_intensity = 3
|
||||
hitscan_light_range = 0.75
|
||||
hitscan_light_color_override = LIGHT_COLOR_CYAN
|
||||
muzzle_flash_intensity = 6
|
||||
muzzle_flash_range = 2
|
||||
muzzle_flash_color_override = LIGHT_COLOR_CYAN
|
||||
impact_light_intensity = 7
|
||||
impact_light_range = 2.5
|
||||
impact_light_color_override = LIGHT_COLOR_CYAN
|
||||
|
||||
/obj/item/projectile/plasma/prehit(atom/target)
|
||||
. = ..()
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/// Is this a hitscan projectile or not, if so move like one
|
||||
#define MOVES_HITSCAN -1
|
||||
/// How many pixels to move the muzzle flash up so your character doesn't look like they're shitting out lasers.
|
||||
#define MUZZLE_EFFECT_PIXEL_INCREMENT 17
|
||||
|
||||
/obj/item/projectile
|
||||
name = "projectile"
|
||||
icon = 'icons/obj/projectiles.dmi'
|
||||
@@ -26,6 +31,7 @@
|
||||
var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center
|
||||
var/speed = 1 //Amount of deciseconds it takes for projectile to travel
|
||||
var/Angle = null
|
||||
var/original_angle = null //Angle at firing
|
||||
var/spread = 0 //amount (in degrees) of projectile spread
|
||||
animate_movement = 0
|
||||
|
||||
@@ -80,6 +86,32 @@
|
||||
var/shield_buster = FALSE
|
||||
var/forced_accuracy = FALSE
|
||||
|
||||
///Has the projectile been fired?
|
||||
var/has_been_fired = FALSE
|
||||
|
||||
//Hitscan
|
||||
var/hitscan = FALSE //Whether this is hitscan. If it is, speed is basically ignored.
|
||||
var/list/beam_segments //assoc list of datum/point_precise or datum/point_precise/vector, start = end. Used for hitscan effect generation.
|
||||
/// Last turf an angle was changed in for hitscan projectiles.
|
||||
var/turf/last_angle_set_hitscan_store
|
||||
var/datum/point_precise/beam_index
|
||||
var/turf/hitscan_last //last turf touched during hitscanning.
|
||||
var/tracer_type
|
||||
var/muzzle_type
|
||||
var/impact_type
|
||||
|
||||
//Fancy hitscan lighting effects!
|
||||
var/hitscan_light_intensity = 1.5
|
||||
var/hitscan_light_range = 0.75
|
||||
var/hitscan_light_color_override
|
||||
var/muzzle_flash_intensity = 3
|
||||
var/muzzle_flash_range = 1.5
|
||||
var/muzzle_flash_color_override
|
||||
var/impact_light_intensity = 3
|
||||
var/impact_light_range = 2
|
||||
var/impact_light_color_override
|
||||
var/hitscan_duration = 0.3 SECONDS
|
||||
|
||||
/obj/item/projectile/New()
|
||||
return ..()
|
||||
|
||||
@@ -112,7 +144,7 @@
|
||||
hity = target.pixel_y + rand(-8, 8)
|
||||
if(!nodamage && (damage_type == BRUTE || damage_type == BURN) && iswallturf(target_loca) && prob(75))
|
||||
var/turf/simulated/wall/W = target_loca
|
||||
if(impact_effect_type)
|
||||
if(impact_effect_type && !hitscan)
|
||||
new impact_effect_type(target_loca, hitx, hity)
|
||||
|
||||
W.add_dent(WALL_DENT_SHOT, hitx, hity)
|
||||
@@ -120,7 +152,7 @@
|
||||
if(alwayslog)
|
||||
add_attack_logs(firer, target, "Shot with a [type]")
|
||||
if(!isliving(target))
|
||||
if(impact_effect_type)
|
||||
if(impact_effect_type && !hitscan)
|
||||
new impact_effect_type(target_loca, hitx, hity)
|
||||
return 0
|
||||
var/mob/living/L = target
|
||||
@@ -152,7 +184,7 @@
|
||||
M.bloody_hands(H)
|
||||
/* Uncomment when bloody_body stops randomly not transferring blood colour.
|
||||
M.bloody_body(H) */
|
||||
else if(impact_effect_type)
|
||||
else if(impact_effect_type && !hitscan)
|
||||
new impact_effect_type(target_loca, hitx, hity)
|
||||
var/organ_hit_text = ""
|
||||
if(L.has_limbs)
|
||||
@@ -199,11 +231,18 @@
|
||||
else
|
||||
return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume
|
||||
|
||||
/obj/item/projectile/proc/store_hitscan_collision(datum/point_precise/point_cache)
|
||||
beam_segments[beam_index] = point_cache
|
||||
beam_index = point_cache
|
||||
beam_segments[beam_index] = null
|
||||
|
||||
/obj/item/projectile/Bump(atom/A, yes)
|
||||
if(!yes) //prevents double bumps.
|
||||
return
|
||||
|
||||
if(check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max && is_reflectable(REFLECTABILITY_PHYSICAL))
|
||||
if(hitscan && ricochets_max > 10)
|
||||
ricochets_max = 10 //I do not want a chucklefuck editing this higher, sorry.
|
||||
ricochets++
|
||||
if(A.handle_ricochet(src))
|
||||
on_ricochet(A)
|
||||
@@ -265,7 +304,9 @@
|
||||
return
|
||||
var/elapsed_time_deciseconds = (world.time - last_projectile_move) + time_offset
|
||||
time_offset = 0
|
||||
var/required_moves = FLOOR(elapsed_time_deciseconds / speed, 1)
|
||||
var/required_moves = hitscan ? MOVES_HITSCAN : FLOOR(elapsed_time_deciseconds / speed, 1)
|
||||
if(required_moves == MOVES_HITSCAN)
|
||||
required_moves = SSprojectiles.global_max_tick_moves
|
||||
if(required_moves > SSprojectiles.global_max_tick_moves)
|
||||
var/overrun = required_moves - SSprojectiles.global_max_tick_moves
|
||||
required_moves = SSprojectiles.global_max_tick_moves
|
||||
@@ -275,14 +316,15 @@
|
||||
for(var/i in 1 to required_moves)
|
||||
pixel_move(1)
|
||||
|
||||
/obj/item/projectile/proc/pixel_move(trajectory_multiplier)
|
||||
/obj/item/projectile/proc/pixel_move(trajectory_multiplier, hitscanning = FALSE)
|
||||
if(!loc || !trajectory)
|
||||
return
|
||||
last_projectile_move = world.time
|
||||
// Keep on course
|
||||
var/matrix/M = new
|
||||
M.Turn(Angle)
|
||||
transform = M
|
||||
if(!hitscanning)
|
||||
var/matrix/M = new
|
||||
M.Turn(Angle)
|
||||
transform = M
|
||||
// Iterate
|
||||
var/forcemoved = FALSE
|
||||
for(var/i in 1 to SSprojectiles.global_iterations_per_move)
|
||||
@@ -297,17 +339,20 @@
|
||||
trajectory_ignore_forcemove = TRUE
|
||||
forceMove(T)
|
||||
trajectory_ignore_forcemove = FALSE
|
||||
pixel_x = trajectory.return_px()
|
||||
pixel_y = trajectory.return_py()
|
||||
if(!hitscanning)
|
||||
pixel_x = trajectory.return_px()
|
||||
pixel_y = trajectory.return_py()
|
||||
forcemoved = TRUE
|
||||
hitscan_last = loc
|
||||
else if(T != loc)
|
||||
step_towards(src, T)
|
||||
hitscan_last = loc
|
||||
if(original && (original.layer >= PROJECTILE_HIT_THRESHHOLD_LAYER || ismob(original)))
|
||||
if(loc == get_turf(original) && !(original in permutated))
|
||||
Bump(original, TRUE)
|
||||
if(QDELETED(src)) //deleted on last move
|
||||
return
|
||||
if(!forcemoved)
|
||||
if(!hitscanning && !forcemoved)
|
||||
pixel_x = trajectory.return_px() - trajectory.mpx * trajectory_multiplier * SSprojectiles.global_iterations_per_move
|
||||
pixel_y = trajectory.return_py() - trajectory.mpy * trajectory_multiplier * SSprojectiles.global_iterations_per_move
|
||||
animate(src, pixel_x = trajectory.return_px(), pixel_y = trajectory.return_py(), time = 1, flags = ANIMATION_END_NOW)
|
||||
@@ -320,6 +365,7 @@
|
||||
current = locate(clamp(x + xo, 1, world.maxx), clamp(y + yo, 1, world.maxy), z)
|
||||
if(isnull(Angle))
|
||||
Angle = round(get_angle(src, current))
|
||||
original_angle = Angle
|
||||
if(spread)
|
||||
Angle += (rand() - 0.5) * spread
|
||||
// Turn right away
|
||||
@@ -329,6 +375,9 @@
|
||||
// Start flying
|
||||
trajectory = new(x, y, z, pixel_x, pixel_y, Angle, SSprojectiles.global_pixel_speed)
|
||||
last_projectile_move = world.time
|
||||
has_been_fired = TRUE
|
||||
if(hitscan)
|
||||
process_hitscan()
|
||||
START_PROCESSING(SSprojectiles, src)
|
||||
pixel_move(1, FALSE)
|
||||
|
||||
@@ -358,6 +407,8 @@
|
||||
Bump(AM, 1)
|
||||
|
||||
/obj/item/projectile/Destroy()
|
||||
if(hitscan)
|
||||
finalize_hitscan_and_generate_tracers()
|
||||
STOP_PROCESSING(SSprojectiles, src)
|
||||
ammo_casing = null
|
||||
firer_source_atom = null
|
||||
@@ -386,11 +437,22 @@
|
||||
..()
|
||||
Angle = new_angle
|
||||
trajectory.set_angle(new_angle)
|
||||
if(has_been_fired && hitscan && isloc(loc) && (loc != last_angle_set_hitscan_store))
|
||||
last_angle_set_hitscan_store = loc
|
||||
var/datum/point/point_cache = new (src)
|
||||
point_cache = trajectory.copy_to()
|
||||
store_hitscan_collision(point_cache)
|
||||
|
||||
/obj/item/projectile/proc/set_angle_centered(new_angle)
|
||||
set_angle(new_angle)
|
||||
var/list/coordinates = trajectory.return_coordinates()
|
||||
trajectory.set_location(coordinates[1], coordinates[2], coordinates[3]) // Sets the trajectory to the center of the tile it bounced at
|
||||
if(has_been_fired && hitscan && isloc(loc))// Handles hitscan projectiles
|
||||
last_angle_set_hitscan_store = loc
|
||||
var/datum/point_precise/point_cache = new (src)
|
||||
point_cache.initialize_location(coordinates[1], coordinates[2], coordinates[3]) // Take the center of the hitscan collision tile
|
||||
store_hitscan_collision(point_cache)
|
||||
return TRUE
|
||||
|
||||
/obj/item/projectile/experience_pressure_difference()
|
||||
return
|
||||
@@ -400,7 +462,11 @@
|
||||
if(QDELETED(src)) // we coulda bumped something
|
||||
return
|
||||
if(trajectory && !trajectory_ignore_forcemove && isturf(target))
|
||||
if(hitscan)
|
||||
finalize_hitscan_and_generate_tracers(FALSE)
|
||||
trajectory.initialize_location(target.x, target.y, target.z, 0, 0)
|
||||
if(hitscan)
|
||||
record_hitscan_start(RETURN_PRECISE_POINT(src))
|
||||
|
||||
/obj/item/projectile/proc/is_reflectable(desired_reflectability_level)
|
||||
if(reflectability == REFLECTABILITY_NEVER) //You'd trust coders not to try and override never reflectable things, but heaven help us I do not
|
||||
@@ -408,3 +474,67 @@
|
||||
if(reflectability < desired_reflectability_level)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/projectile/proc/record_hitscan_start(datum/point_precise/point_cache)
|
||||
if(point_cache)
|
||||
beam_segments = list()
|
||||
beam_index = point_cache
|
||||
beam_segments[beam_index] = null //record start.
|
||||
|
||||
/obj/item/projectile/proc/process_hitscan()
|
||||
//Safety here is to make hitscan stop if something goes wrong. Why is it equal to range * 10, when range is the maximum amount of tiles it can go? No clue.
|
||||
var/safety = range * 10
|
||||
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)
|
||||
if(loc)
|
||||
Bump(loc)
|
||||
if(!QDELETED(src))
|
||||
qdel(src)
|
||||
return //Kill!
|
||||
pixel_move(1, TRUE)
|
||||
|
||||
/obj/item/projectile/proc/finalize_hitscan_and_generate_tracers(impacting = TRUE)
|
||||
if(trajectory && beam_index)
|
||||
var/datum/point_precise/point_cache = trajectory.copy_to()
|
||||
beam_segments[beam_index] = point_cache
|
||||
generate_hitscan_tracers(null, hitscan_duration, impacting)
|
||||
|
||||
/obj/item/projectile/proc/generate_hitscan_tracers(cleanup = TRUE, duration = 3, impacting = TRUE)
|
||||
if(!length(beam_segments))
|
||||
return
|
||||
if(tracer_type)
|
||||
var/tempuid = UID()
|
||||
for(var/datum/point_precise/p in beam_segments)
|
||||
generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity, tempuid)
|
||||
if(muzzle_type && duration > 0)
|
||||
var/datum/point_precise/p = beam_segments[1]
|
||||
var/atom/movable/thing = new muzzle_type
|
||||
p.move_atom_to_src(thing)
|
||||
var/matrix/matrix = new
|
||||
matrix.Turn(original_angle)
|
||||
thing.transform = matrix
|
||||
thing.color = color
|
||||
thing.set_light(muzzle_flash_range, muzzle_flash_intensity, muzzle_flash_color_override? muzzle_flash_color_override : color)
|
||||
QDEL_IN(thing, duration)
|
||||
if(impacting && impact_type && duration > 0)
|
||||
var/datum/point_precise/p = beam_segments[beam_segments[beam_segments.len]]
|
||||
var/atom/movable/thing = new impact_type
|
||||
p.move_atom_to_src(thing)
|
||||
var/matrix/matrix = new
|
||||
matrix.Turn(Angle)
|
||||
thing.transform = matrix
|
||||
thing.color = color
|
||||
thing.set_light(impact_light_range, impact_light_intensity, impact_light_color_override? impact_light_color_override : color)
|
||||
QDEL_IN(thing, duration)
|
||||
if(cleanup)
|
||||
cleanup_beam_segments()
|
||||
|
||||
/obj/item/projectile/proc/cleanup_beam_segments()
|
||||
QDEL_LIST_ASSOC(beam_segments)
|
||||
|
||||
#undef MOVES_HITSCAN
|
||||
#undef MUZZLE_EFFECT_PIXEL_INCREMENT
|
||||
|
||||
@@ -214,7 +214,7 @@
|
||||
|
||||
/obj/machinery/bsa/full/proc/fire(mob/user, turf/bullseye, target)
|
||||
var/turf/point = get_front_turf()
|
||||
for(var/turf/T in getline(get_step(point,dir),get_target_turf()))
|
||||
for(var/turf/T in get_line(get_step(point,dir),get_target_turf()))
|
||||
T.ex_act(1)
|
||||
for(var/atom/A in T)
|
||||
A.ex_act(1)
|
||||
|
||||
@@ -154,7 +154,7 @@
|
||||
var/kill_range = 14
|
||||
|
||||
/obj/machinery/satellite/meteor_shield/proc/space_los(meteor)
|
||||
for(var/turf/T in getline(src,meteor))
|
||||
for(var/turf/T in get_line(src,meteor))
|
||||
if(!isspaceturf(T))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
Reference in New Issue
Block a user