mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-11 10:43:20 +00:00
Merge pull request #4719 from VOREStation/upstream-merge-5636
[MIRROR] [READY]Ports /tg/station pixel projectiles, processing subsystems, timer subsystems, and some misc stuff to make it all work
This commit is contained in:
@@ -1,312 +0,0 @@
|
||||
/obj/effect/projectile
|
||||
icon = 'icons/effects/projectiles.dmi'
|
||||
icon_state = "bolt"
|
||||
plane = ABOVE_PLANE
|
||||
|
||||
/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 = 5)
|
||||
update_light()
|
||||
spawn(kill_delay)
|
||||
qdel(src) //see effect_system.dm - sets loc to null and lets GC handle removing these effects
|
||||
|
||||
return
|
||||
|
||||
//----------------------------
|
||||
// Laser beam
|
||||
//----------------------------
|
||||
/obj/effect/projectile/laser/tracer
|
||||
icon_state = "beam"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#FF0D00"
|
||||
|
||||
/obj/effect/projectile/laser/muzzle
|
||||
icon_state = "muzzle_laser"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#FF0D00"
|
||||
|
||||
/obj/effect/projectile/laser/impact
|
||||
icon_state = "impact_laser"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#FF0D00"
|
||||
|
||||
//----------------------------
|
||||
// Blue laser beam
|
||||
//----------------------------
|
||||
/obj/effect/projectile/laser_blue/tracer
|
||||
icon_state = "beam_blue"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#0066FF"
|
||||
|
||||
/obj/effect/projectile/laser_blue/muzzle
|
||||
icon_state = "muzzle_blue"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#0066FF"
|
||||
|
||||
/obj/effect/projectile/laser_blue/impact
|
||||
icon_state = "impact_blue"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#0066FF"
|
||||
|
||||
//----------------------------
|
||||
// Omni laser beam
|
||||
//----------------------------
|
||||
/obj/effect/projectile/laser_omni/tracer
|
||||
icon_state = "beam_omni"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00C6FF"
|
||||
|
||||
/obj/effect/projectile/laser_omni/muzzle
|
||||
icon_state = "muzzle_omni"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00C6FF"
|
||||
|
||||
/obj/effect/projectile/laser_omni/impact
|
||||
icon_state = "impact_omni"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00C6FF"
|
||||
|
||||
//----------------------------
|
||||
// Xray laser beam
|
||||
//----------------------------
|
||||
/obj/effect/projectile/xray/tracer
|
||||
icon_state = "xray"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00CC33"
|
||||
|
||||
/obj/effect/projectile/xray/muzzle
|
||||
icon_state = "muzzle_xray"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00CC33"
|
||||
|
||||
/obj/effect/projectile/xray/impact
|
||||
icon_state = "impact_xray"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00CC33"
|
||||
|
||||
//----------------------------
|
||||
// Heavy laser beam
|
||||
//----------------------------
|
||||
/obj/effect/projectile/laser_heavy/tracer
|
||||
icon_state = "beam_heavy"
|
||||
light_range = 3
|
||||
light_power = 1
|
||||
light_color = "#FF0D00"
|
||||
|
||||
/obj/effect/projectile/laser_heavy/muzzle
|
||||
icon_state = "muzzle_beam_heavy"
|
||||
light_range = 3
|
||||
light_power = 1
|
||||
light_color = "#FF0D00"
|
||||
|
||||
/obj/effect/projectile/laser_heavy/impact
|
||||
icon_state = "impact_beam_heavy"
|
||||
light_range = 3
|
||||
light_power = 1
|
||||
light_color = "#FF0D00"
|
||||
|
||||
//----------------------------
|
||||
// Pulse laser beam
|
||||
//----------------------------
|
||||
/obj/effect/projectile/laser_pulse/tracer
|
||||
icon_state = "u_laser"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#0066FF"
|
||||
|
||||
/obj/effect/projectile/laser_pulse/muzzle
|
||||
icon_state = "muzzle_u_laser"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#0066FF"
|
||||
|
||||
/obj/effect/projectile/laser_pulse/impact
|
||||
icon_state = "impact_u_laser"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#0066FF"
|
||||
|
||||
//----------------------------
|
||||
// Pulse muzzle effect only
|
||||
//----------------------------
|
||||
/obj/effect/projectile/pulse/muzzle
|
||||
icon_state = "muzzle_pulse"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#0066FF"
|
||||
|
||||
//----------------------------
|
||||
// Emitter beam
|
||||
//----------------------------
|
||||
/obj/effect/projectile/emitter/tracer
|
||||
icon_state = "emitter"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00CC33"
|
||||
|
||||
/obj/effect/projectile/emitter/muzzle
|
||||
icon_state = "muzzle_emitter"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00CC33"
|
||||
|
||||
/obj/effect/projectile/emitter/impact
|
||||
icon_state = "impact_emitter"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00CC33"
|
||||
|
||||
//----------------------------
|
||||
// Stun beam
|
||||
//----------------------------
|
||||
/obj/effect/projectile/stun/tracer
|
||||
icon_state = "stun"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#FFFFFF"
|
||||
|
||||
/obj/effect/projectile/stun/muzzle
|
||||
icon_state = "muzzle_stun"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#FFFFFF"
|
||||
|
||||
/obj/effect/projectile/stun/impact
|
||||
icon_state = "impact_stun"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#FFFFFF"
|
||||
|
||||
//----------------------------
|
||||
// Bullet
|
||||
//----------------------------
|
||||
/obj/effect/projectile/bullet/muzzle
|
||||
icon_state = "muzzle_bullet"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#FFFFFF"
|
||||
|
||||
//----------------------------
|
||||
// Lightning beam
|
||||
//----------------------------
|
||||
/obj/effect/projectile/lightning/tracer
|
||||
icon_state = "lightning"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00C6FF"
|
||||
|
||||
/obj/effect/projectile/lightning/muzzle
|
||||
icon_state = "muzzle_lightning"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00C6FF"
|
||||
|
||||
/obj/effect/projectile/lightning/impact
|
||||
icon_state = "impact_lightning"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00C6FF"
|
||||
|
||||
//----------------------------
|
||||
// Dark matter stun
|
||||
//----------------------------
|
||||
|
||||
/obj/effect/projectile/darkmatterstun/tracer
|
||||
icon_state = "darkt"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#8837A3"
|
||||
|
||||
/obj/effect/projectile/darkmatterstun/muzzle
|
||||
icon_state = "muzzle_darkt"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#8837A3"
|
||||
|
||||
/obj/effect/projectile/darkmatterstun/impact
|
||||
icon_state = "impact_darkt"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#8837A3"
|
||||
|
||||
//----------------------------
|
||||
// Dark matter
|
||||
//----------------------------
|
||||
|
||||
/obj/effect/projectile/darkmatter/tracer
|
||||
icon_state = "darkb"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#8837A3"
|
||||
|
||||
/obj/effect/projectile/darkmatter/muzzle
|
||||
icon_state = "muzzle_darkb"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#8837A3"
|
||||
|
||||
/obj/effect/projectile/darkmatter/impact
|
||||
icon_state = "impact_darkb"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#8837A3"
|
||||
|
||||
//----------------------------
|
||||
// Inversion / Cult
|
||||
//----------------------------
|
||||
/obj/effect/projectile/inversion/tracer
|
||||
icon_state = "invert"
|
||||
light_range = 2
|
||||
light_power = -2
|
||||
light_color = "#FFFFFF"
|
||||
|
||||
/obj/effect/projectile/inversion/muzzle
|
||||
icon_state = "muzzle_invert"
|
||||
light_range = 2
|
||||
light_power = -2
|
||||
light_color = "#FFFFFF"
|
||||
|
||||
/obj/effect/projectile/inversion/impact
|
||||
icon_state = "impact_invert"
|
||||
light_range = 2
|
||||
light_power = -2
|
||||
light_color = "#FFFFFF"
|
||||
|
||||
//----------------------------
|
||||
// Magnetohydronamic Howitzer
|
||||
//----------------------------
|
||||
/obj/effect/projectile/tungsten/tracer
|
||||
icon_state = "mhd_laser"
|
||||
light_range = 4
|
||||
light_power = 3
|
||||
light_color = "#3300ff"
|
||||
|
||||
/obj/effect/projectile/tungsten/muzzle
|
||||
icon_state = "muzzle_mhd_laser"
|
||||
light_range = 4
|
||||
light_power = 3
|
||||
light_color = "#3300ff"
|
||||
|
||||
/obj/effect/projectile/tungsten/impact
|
||||
icon_state = "impact_mhd_laser"
|
||||
light_range = 4
|
||||
light_power = 3
|
||||
light_color = "#3300ff"
|
||||
@@ -470,7 +470,8 @@
|
||||
P.shot_from = src.name
|
||||
P.silenced = silenced
|
||||
|
||||
P.launch(target)
|
||||
P.old_style_target(target)
|
||||
P.fire()
|
||||
|
||||
last_shot = world.time
|
||||
|
||||
@@ -645,24 +646,17 @@
|
||||
/obj/item/weapon/gun/proc/process_projectile(obj/projectile, mob/user, atom/target, var/target_zone, var/params=null)
|
||||
var/obj/item/projectile/P = projectile
|
||||
if(!istype(P))
|
||||
return 0 //default behaviour only applies to true projectiles
|
||||
|
||||
if(params)
|
||||
P.set_clickpoint(params)
|
||||
return FALSE //default behaviour only applies to true projectiles
|
||||
|
||||
//shooting while in shock
|
||||
var/x_offset = 0
|
||||
var/y_offset = 0
|
||||
var/forcespread
|
||||
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)
|
||||
forcespread = rand(50, 50)
|
||||
else if(mob.shock_stage > 70)
|
||||
y_offset = rand(-1,1)
|
||||
x_offset = rand(-1,1)
|
||||
|
||||
var/launched = !P.launch_from_gun(target, user, src, target_zone, x_offset, y_offset)
|
||||
forcespread = rand(-25, 25)
|
||||
var/launched = !P.launch_from_gun(target, target_zone, user, params, null, forcespread, src)
|
||||
|
||||
if(launched)
|
||||
play_fire_sound(user, P)
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
damage = 32
|
||||
damage_type = BRUTE
|
||||
check_armour = "bomb"
|
||||
kill_count = 3 // Our "range" var is named "kill_count". Yes it is.
|
||||
range = 3 // Our "range" var is named "kill_count". Yes it is.
|
||||
|
||||
var/pressure_decrease = 0.25
|
||||
var/turf_aoe = FALSE
|
||||
@@ -219,7 +219,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
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
icon_state = "dart"
|
||||
damage = 5
|
||||
var/reagent_amount = 15
|
||||
kill_count = 15 //shorter range
|
||||
range = 15 //shorter range
|
||||
|
||||
muzzle_type = null
|
||||
|
||||
|
||||
@@ -79,9 +79,9 @@
|
||||
damage_type = HALLOSS
|
||||
light_color = "#8837A3"
|
||||
|
||||
muzzle_type = /obj/effect/projectile/darkmatterstun/muzzle
|
||||
tracer_type = /obj/effect/projectile/darkmatterstun/tracer
|
||||
impact_type = /obj/effect/projectile/darkmatterstun/impact
|
||||
muzzle_type = /obj/effect/projectile/muzzle/darkmatterstun
|
||||
tracer_type = /obj/effect/projectile/tracer/darkmatterstun
|
||||
impact_type = /obj/effect/projectile/impact/darkmatterstun
|
||||
|
||||
/obj/item/projectile/beam/darkmatter
|
||||
name = "dark matter bolt"
|
||||
@@ -95,9 +95,9 @@
|
||||
|
||||
embed_chance = 0
|
||||
|
||||
muzzle_type = /obj/effect/projectile/darkmatter/muzzle
|
||||
tracer_type = /obj/effect/projectile/darkmatter/tracer
|
||||
impact_type = /obj/effect/projectile/darkmatter/impact
|
||||
muzzle_type = /obj/effect/projectile/muzzle/darkmatter
|
||||
tracer_type = /obj/effect/projectile/tracer/darkmatter
|
||||
impact_type = /obj/effect/projectile/impact/darkmatter
|
||||
|
||||
/obj/item/projectile/energy/darkmatter
|
||||
name = "dark matter pellet"
|
||||
|
||||
@@ -1,37 +1,84 @@
|
||||
/*
|
||||
#define BRUTE "brute"
|
||||
#define BURN "burn"
|
||||
#define TOX "tox"
|
||||
#define OXY "oxy"
|
||||
#define CLONE "clone"
|
||||
|
||||
#define ADD "add"
|
||||
#define SET "set"
|
||||
*/
|
||||
#define MOVES_HITSCAN -1 //Not actually hitscan but close as we get without actual hitscan.
|
||||
#define MUZZLE_EFFECT_PIXEL_INCREMENT 17 //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 = FALSE
|
||||
anchored = TRUE
|
||||
unacidable = TRUE
|
||||
pass_flags = PASSTABLE
|
||||
mouse_opacity = 0
|
||||
var/bumped = 0 //Prevents it from hitting more than one guy at once
|
||||
|
||||
////TG PROJECTILE SYTSEM
|
||||
//Projectile stuff
|
||||
var/range = 50
|
||||
var/originalRange
|
||||
|
||||
//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/speed = 0.8 //Amount of deciseconds it takes for projectile to travel
|
||||
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/spread = 0 //amount (in degrees) of projectile spread
|
||||
animate_movement = 0 //Use SLIDE_STEPS in conjunction with legacy
|
||||
var/ricochets = 0
|
||||
var/ricochets_max = 2
|
||||
var/ricochet_chance = 30
|
||||
|
||||
//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
|
||||
|
||||
//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
|
||||
|
||||
//Homing
|
||||
var/homing = FALSE
|
||||
var/atom/homing_target
|
||||
var/homing_turn_speed = 10 //Angle per tick.
|
||||
var/homing_inaccuracy_min = 0 //in pixels for these. offsets are set once when setting target.
|
||||
var/homing_inaccuracy_max = 0
|
||||
var/homing_offset_x = 0
|
||||
var/homing_offset_y = 0
|
||||
|
||||
//Targetting
|
||||
var/yo = null
|
||||
var/xo = null
|
||||
var/atom/original = null // the original target clicked
|
||||
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/p_x = 16
|
||||
var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center
|
||||
|
||||
//Misc/Polaris variables
|
||||
|
||||
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/p_x = 16
|
||||
var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center
|
||||
|
||||
var/accuracy = 0
|
||||
var/dispersion = 0.0
|
||||
@@ -45,7 +92,6 @@
|
||||
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/incendiary = 0 //1 for ignite on hit, 2 for trail of fire. 3 maybe later for burst of fire around the impact point. - Mech
|
||||
var/flammability = 0 //Amount of fire stacks to add for the above.
|
||||
@@ -64,178 +110,365 @@
|
||||
|
||||
embed_chance = 0 //Base chance for a projectile to embed
|
||||
|
||||
var/hitscan = 0 // whether the projectile should be hitscan
|
||||
var/step_delay = 1 // the delay between iterations if not a hitscan projectile
|
||||
|
||||
// effect types to be used
|
||||
var/muzzle_type
|
||||
var/tracer_type
|
||||
var/impact_type
|
||||
|
||||
var/fire_sound = 'sound/weapons/Gunshot_old.ogg' // Can be overriden in gun.dm's fire_sound var. It can also be null but I don't know why you'd ever want to do that. -Ace
|
||||
|
||||
var/vacuum_traversal = 1 //Determines if the projectile can exist in vacuum, if false, the projectile will be deleted if it enters vacuum.
|
||||
var/vacuum_traversal = TRUE //Determines if the projectile can exist in vacuum, if false, the projectile will be deleted if it enters vacuum.
|
||||
|
||||
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/proc/Range()
|
||||
range--
|
||||
if(range <= 0 && loc)
|
||||
on_range()
|
||||
|
||||
//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(!isliving(target)) return 0
|
||||
// if(isanimal(target)) return 0
|
||||
var/mob/living/L = target
|
||||
L.apply_effects(stun, weaken, paralyze, irradiate, stutter, eyeblur, drowsy, agony, blocked, incendiary, flammability) // add in AGONY!
|
||||
if(modifier_type_to_apply)
|
||||
L.add_modifier(modifier_type_to_apply, modifier_duration)
|
||||
return 1
|
||||
/obj/item/projectile/proc/on_range() //if we want there to be effects when they reach the end of their range
|
||||
qdel(src)
|
||||
|
||||
//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 impact effect
|
||||
if(damage && damage_type == BURN)
|
||||
var/turf/T = get_turf(A)
|
||||
if(T)
|
||||
T.hotspot_expose(700, 5)
|
||||
/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, SSprojectiles.global_pixel_speed)
|
||||
var/datum/point/vector/v = current.return_vector_after_increments(moves * SSprojectiles.global_iterations_per_move)
|
||||
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/set_pixel_speed(new_speed)
|
||||
if(trajectory)
|
||||
trajectory.set_speed(new_speed)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/item/projectile/proc/record_hitscan_start(datum/point/pcache)
|
||||
if(pcache)
|
||||
beam_segments = list()
|
||||
beam_index = pcache
|
||||
beam_segments[beam_index] = null //record start.
|
||||
|
||||
/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)
|
||||
if(loc)
|
||||
Bump(loc)
|
||||
if(!QDELETED(src))
|
||||
qdel(src)
|
||||
return //Kill!
|
||||
pixel_move(1, TRUE)
|
||||
|
||||
/obj/item/projectile/proc/pixel_move(trajectory_multiplier, hitscanning = FALSE)
|
||||
if(!loc || !trajectory)
|
||||
return
|
||||
last_projectile_move = world.time
|
||||
if(homing)
|
||||
process_homing()
|
||||
var/forcemoved = FALSE
|
||||
for(var/i in 1 to SSprojectiles.global_iterations_per_move)
|
||||
if(QDELETED(src))
|
||||
return
|
||||
trajectory.increment(trajectory_multiplier)
|
||||
var/turf/T = trajectory.return_turf()
|
||||
if(!istype(T))
|
||||
qdel(src)
|
||||
return
|
||||
if(T.z != loc.z)
|
||||
var/old = loc
|
||||
before_z_change(loc, T)
|
||||
trajectory_ignore_forcemove = TRUE
|
||||
forceMove(T)
|
||||
trajectory_ignore_forcemove = FALSE
|
||||
after_z_change(old, loc)
|
||||
if(!hitscanning)
|
||||
pixel_x = trajectory.return_px()
|
||||
pixel_y = trajectory.return_py()
|
||||
forcemoved = TRUE
|
||||
hitscan_last = loc
|
||||
else if(T != loc)
|
||||
before_move()
|
||||
step_towards(src, T)
|
||||
hitscan_last = loc
|
||||
after_move()
|
||||
if(can_hit_target(original, permutated))
|
||||
Bump(original)
|
||||
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)
|
||||
Range()
|
||||
|
||||
/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))
|
||||
Bump(AM)
|
||||
|
||||
/obj/item/projectile/proc/process_homing() //may need speeding up in the future performance wise.
|
||||
if(!homing_target)
|
||||
return FALSE
|
||||
var/datum/point/PT = RETURN_PRECISE_POINT(homing_target)
|
||||
PT.x += CLAMP(homing_offset_x, 1, world.maxx)
|
||||
PT.y += CLAMP(homing_offset_y, 1, world.maxy)
|
||||
var/angle = closer_angle_difference(Angle, angle_between_points(RETURN_PRECISE_POINT(src), PT))
|
||||
setAngle(Angle + CLAMP(angle, -homing_turn_speed, homing_turn_speed))
|
||||
|
||||
/obj/item/projectile/proc/set_homing_target(atom/A)
|
||||
if(!A || (!isturf(A) && !isturf(A.loc)))
|
||||
return FALSE
|
||||
homing = TRUE
|
||||
homing_target = A
|
||||
homing_offset_x = rand(homing_inaccuracy_min, homing_inaccuracy_max)
|
||||
homing_offset_y = rand(homing_inaccuracy_min, homing_inaccuracy_max)
|
||||
if(prob(50))
|
||||
homing_offset_x = -homing_offset_x
|
||||
if(prob(50))
|
||||
homing_offset_y = -homing_offset_y
|
||||
|
||||
/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 = speed > 0? FLOOR(elapsed_time_deciseconds / speed, 1) : MOVES_HITSCAN //Would be better if a 0 speed made hitscan but everyone hates those so I can't make it a universal system :<
|
||||
if(required_moves == MOVES_HITSCAN)
|
||||
required_moves = SSprojectiles.global_max_tick_moves
|
||||
else
|
||||
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)
|
||||
|
||||
for(var/i in 1 to required_moves)
|
||||
pixel_move(1, FALSE)
|
||||
|
||||
/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(!isloc(target) || !isloc(loc) || !z)
|
||||
return ..()
|
||||
var/zc = target.z != z
|
||||
var/old = loc
|
||||
if(zc)
|
||||
before_z_change(old, target)
|
||||
. = ..()
|
||||
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))
|
||||
if(zc)
|
||||
after_z_change(old, target)
|
||||
|
||||
/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)
|
||||
qdel(src)
|
||||
return
|
||||
if(isnum(angle))
|
||||
setAngle(angle)
|
||||
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
|
||||
trajectory_ignore_forcemove = TRUE
|
||||
forceMove(starting)
|
||||
trajectory_ignore_forcemove = FALSE
|
||||
trajectory = new(starting.x, starting.y, starting.z, pixel_x, pixel_y, Angle, SSprojectiles.global_pixel_speed)
|
||||
last_projectile_move = world.time
|
||||
permutated = list()
|
||||
originalRange = range
|
||||
fired = TRUE
|
||||
if(hitscan)
|
||||
process_hitscan()
|
||||
START_PROCESSING(SSprojectiles, src)
|
||||
pixel_move(1, FALSE) //move it now!
|
||||
|
||||
/obj/item/projectile/proc/after_z_change(atom/olcloc, atom/newloc)
|
||||
|
||||
/obj/item/projectile/proc/before_z_change(atom/oldloc, atom/newloc)
|
||||
|
||||
/obj/item/projectile/proc/before_move()
|
||||
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_chance == 0 || damage_type != BRUTE)
|
||||
return 0
|
||||
return 1
|
||||
/obj/item/projectile/proc/after_move()
|
||||
return
|
||||
|
||||
/obj/item/projectile/proc/get_structure_damage()
|
||||
if(damage_type == BRUTE || damage_type == BURN)
|
||||
return damage
|
||||
return 0
|
||||
/obj/item/projectile/proc/store_hitscan_collision(datum/point/pcache)
|
||||
beam_segments[beam_index] = pcache
|
||||
beam_index = pcache
|
||||
beam_segments[beam_index] = null
|
||||
|
||||
//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
|
||||
//Spread is FORCED!
|
||||
/obj/item/projectile/proc/preparePixelProjectile(atom/target, atom/source, params, spread = 0)
|
||||
var/turf/curloc = get_turf(source)
|
||||
var/turf/targloc = get_turf(target)
|
||||
trajectory_ignore_forcemove = TRUE
|
||||
forceMove(get_turf(source))
|
||||
trajectory_ignore_forcemove = FALSE
|
||||
starting = get_turf(source)
|
||||
original = target
|
||||
if(targloc || !params)
|
||||
yo = targloc.y - curloc.y
|
||||
xo = targloc.x - curloc.x
|
||||
setAngle(Get_Angle(src, targloc) + spread)
|
||||
|
||||
/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)
|
||||
if(isliving(source) && params)
|
||||
var/list/calculated = calculate_projectile_angle_and_pixel_offsets(source, params)
|
||||
p_x = calculated[2]
|
||||
p_y = calculated[3]
|
||||
|
||||
//sets the click point of the projectile using mouse input params
|
||||
/obj/item/projectile/proc/set_clickpoint(var/params)
|
||||
setAngle(calculated[1] + spread)
|
||||
else if(targloc)
|
||||
yo = targloc.y - curloc.y
|
||||
xo = targloc.x - curloc.x
|
||||
setAngle(Get_Angle(src, targloc) + spread)
|
||||
else
|
||||
crash_with("WARNING: Projectile [type] fired without either mouse parameters, or a target atom to aim at!")
|
||||
qdel(src)
|
||||
|
||||
/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"], ",")
|
||||
|
||||
//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)
|
||||
//Split X+Pixel_X up into list(X, Pixel_X)
|
||||
var/list/screen_loc_X = splittext(screen_loc_params[1],":")
|
||||
|
||||
//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
|
||||
//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(combustion)
|
||||
curloc.hotspot_expose(700, 5)
|
||||
//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 = user.client? getviewsize(user.client.view) : world.view
|
||||
var/screenviewX = screenview[1] * world.icon_size
|
||||
var/screenviewY = screenview[2] * world.icon_size
|
||||
|
||||
if(targloc == curloc) //Shooting something in the same turf
|
||||
target.bullet_act(src, target_zone)
|
||||
on_impact(target)
|
||||
qdel(src)
|
||||
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)
|
||||
|
||||
/obj/item/projectile/proc/redirect(x, y, starting, source)
|
||||
old_style_target(locate(x, y, z), starting? get_turf(starting) : get_turf(source))
|
||||
|
||||
/obj/item/projectile/proc/old_style_target(atom/target, atom/source)
|
||||
if(!source)
|
||||
source = get_turf(src)
|
||||
starting = source
|
||||
original = target
|
||||
def_zone = target_zone
|
||||
setAngle(Get_Angle(source, target))
|
||||
|
||||
spawn()
|
||||
setup_trajectory(curloc, targloc, x_offset, y_offset, angle_offset) //plot the initial trajectory
|
||||
process()
|
||||
/obj/item/projectile/Destroy()
|
||||
if(hitscan)
|
||||
finalize_hitscan_and_generate_tracers()
|
||||
STOP_PROCESSING(SSprojectiles, src)
|
||||
cleanup_beam_segments()
|
||||
qdel(trajectory)
|
||||
return ..()
|
||||
|
||||
return 0
|
||||
/obj/item/projectile/proc/cleanup_beam_segments()
|
||||
QDEL_LIST_ASSOC(beam_segments)
|
||||
beam_segments = list()
|
||||
qdel(beam_index)
|
||||
|
||||
//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)
|
||||
on_impact(user)
|
||||
qdel(src)
|
||||
return 0
|
||||
|
||||
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)
|
||||
|
||||
//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)
|
||||
if(!istype(target_mob))
|
||||
return
|
||||
|
||||
//roll to-hit
|
||||
miss_modifier = max(15*(distance-2) - accuracy + miss_modifier + target_mob.get_evasion(), 0)
|
||||
var/hit_zone = get_zone_with_miss_chance(def_zone, target_mob, miss_modifier, ranged_attack=(distance > 1 || original != target_mob)) //if the projectile hits a target we weren't originally aiming at then retain the chance to miss
|
||||
|
||||
var/result = PROJECTILE_FORCE_MISS
|
||||
if(hit_zone)
|
||||
def_zone = hit_zone //set def_zone, so if the projectile ends up hitting someone else later (to be implemented), it is more likely to hit the same part
|
||||
result = target_mob.bullet_act(src, def_zone)
|
||||
|
||||
if(result == PROJECTILE_FORCE_MISS)
|
||||
if(!silenced)
|
||||
visible_message("<span class='notice'>\The [src] misses [target_mob] narrowly!</span>")
|
||||
return 0
|
||||
|
||||
//hit messages
|
||||
if(silenced)
|
||||
to_chat(target_mob, "<span class='danger'>You've been hit in the [parse_zone(def_zone)] by \the [src]!</span>")
|
||||
/obj/item/projectile/proc/vol_by_damage()
|
||||
if(damage)
|
||||
return CLAMP((damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then CLAMP the value between 30 and 100
|
||||
else
|
||||
visible_message("<span class='danger'>\The [target_mob] is hit by \the [src] in the [parse_zone(def_zone)]!</span>")//X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter
|
||||
return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume.
|
||||
|
||||
//admin logs
|
||||
if(!no_attack_log)
|
||||
if(istype(firer, /mob) && istype(target_mob))
|
||||
add_attack_logs(firer,target_mob,"Shot with \a [src.type] projectile")
|
||||
/obj/item/projectile/proc/finalize_hitscan_and_generate_tracers(impacting = TRUE)
|
||||
if(trajectory && beam_index)
|
||||
var/datum/point/pcache = trajectory.copy_to()
|
||||
beam_segments[beam_index] = pcache
|
||||
generate_hitscan_tracers(null, null, impacting)
|
||||
|
||||
//sometimes bullet_act() will want the projectile to continue flying
|
||||
if (result == PROJECTILE_CONTINUE)
|
||||
return 0
|
||||
/obj/item/projectile/proc/generate_hitscan_tracers(cleanup = TRUE, duration = 3, impacting = TRUE)
|
||||
if(!length(beam_segments))
|
||||
return
|
||||
if(tracer_type)
|
||||
var/tempref = "\ref[src]"
|
||||
for(var/datum/point/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, tempref)
|
||||
if(muzzle_type && duration > 0)
|
||||
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
|
||||
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/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
|
||||
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()
|
||||
|
||||
return 1
|
||||
//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 >= TABLE_LAYER) || ismob(target)) && (loc == get_turf(target)) && (!(target in passthrough)))
|
||||
|
||||
/obj/item/projectile/Bump(atom/A as mob|obj|turf|area, forced=0)
|
||||
if(A == src)
|
||||
return 0 //no
|
||||
/obj/item/projectile/Bump(atom/A)
|
||||
if(A in permutated)
|
||||
return FALSE
|
||||
if(firer && !reflected)
|
||||
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
|
||||
|
||||
if(A == firer)
|
||||
loc = A.loc
|
||||
return 0 //cannot shoot yourself
|
||||
var/distance = get_dist(starting, get_turf(src))
|
||||
var/turf/target_turf = get_turf(A)
|
||||
var/passthrough = FALSE
|
||||
|
||||
if((bumped && !forced) || (A in permutated))
|
||||
return 0
|
||||
|
||||
var/passthrough = 0 //if the projectile should continue flying
|
||||
var/distance = get_dist(starting,loc)
|
||||
|
||||
bumped = 1
|
||||
if(ismob(A))
|
||||
var/mob/M = A
|
||||
if(istype(A, /mob/living))
|
||||
@@ -246,13 +479,13 @@
|
||||
var/shield_chance = min(80, (30 * (M.mob_size / 10))) //Small mobs have a harder time keeping a dead body as a shield than a human-sized one. Unathi would have an easier job, if they are made to be SIZE_LARGE in the future. -Mech
|
||||
if(prob(shield_chance))
|
||||
visible_message("<span class='danger'>\The [M] uses [G.affecting] as a shield!</span>")
|
||||
if(Bump(G.affecting, forced=1))
|
||||
if(Bump(G.affecting))
|
||||
return
|
||||
else
|
||||
visible_message("<span class='danger'>\The [M] tries to use [G.affecting] as a shield, but fails!</span>")
|
||||
else
|
||||
visible_message("<span class='danger'>\The [M] uses [G.affecting] as a shield!</span>")
|
||||
if(Bump(G.affecting, forced=1))
|
||||
if(Bump(G.affecting))
|
||||
return //If Bump() returns 0 (keep going) then we continue on to attack M.
|
||||
|
||||
passthrough = !attack_mob(M, distance)
|
||||
@@ -269,234 +502,113 @@
|
||||
//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)
|
||||
//move ourselves onto A so we can continue on our way.
|
||||
if(A)
|
||||
if(istype(A, /turf))
|
||||
loc = A
|
||||
else
|
||||
loc = A.loc
|
||||
permutated.Add(A)
|
||||
bumped = 0 //reset bumped variable!
|
||||
return 0
|
||||
|
||||
//stop flying
|
||||
on_impact(A)
|
||||
|
||||
density = 0
|
||||
invisibility = 101
|
||||
trajectory_ignore_forcemove = TRUE
|
||||
forceMove(target_turf)
|
||||
permutated.Add(A)
|
||||
trajectory_ignore_forcemove = FALSE
|
||||
return FALSE
|
||||
|
||||
if(A)
|
||||
on_impact(A)
|
||||
qdel(src)
|
||||
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(atom/target, blocked = 0, def_zone)
|
||||
if(blocked >= 100) return 0//Full block
|
||||
if(!isliving(target)) return 0
|
||||
// if(isanimal(target)) return 0
|
||||
var/mob/living/L = target
|
||||
L.apply_effects(stun, weaken, paralyze, irradiate, stutter, eyeblur, drowsy, agony, blocked, incendiary, flammability) // add in AGONY!
|
||||
if(modifier_type_to_apply)
|
||||
L.add_modifier(modifier_type_to_apply, modifier_duration)
|
||||
return 1
|
||||
|
||||
/obj/item/projectile/ex_act()
|
||||
return //explosions probably shouldn't delete projectiles
|
||||
//called when the projectile stops flying because it Bump'd with something
|
||||
/obj/item/projectile/proc/on_impact(atom/A)
|
||||
if(damage && damage_type == BURN)
|
||||
var/turf/T = get_turf(A)
|
||||
if(T)
|
||||
T.hotspot_expose(700, 5)
|
||||
|
||||
/obj/item/projectile/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
|
||||
//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_chance == 0 || damage_type != BRUTE)
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/obj/item/projectile/process()
|
||||
var/first_step = 1
|
||||
/obj/item/projectile/proc/get_structure_damage()
|
||||
if(damage_type == BRUTE || damage_type == BURN)
|
||||
return damage
|
||||
return 0
|
||||
|
||||
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
|
||||
//return 1 if the projectile should be allowed to pass through after all, 0 if not.
|
||||
/obj/item/projectile/proc/check_penetrate(atom/A)
|
||||
return 1
|
||||
|
||||
trajectory.increment() // increment the current location
|
||||
location = trajectory.return_location(location) // update the locally stored location data
|
||||
update_light() //energy projectiles will look glowy and fun
|
||||
/obj/item/projectile/proc/check_fire(atom/target as mob, mob/living/user as mob) //Checks if you can hit them or not.
|
||||
check_trajectory(target, user, pass_flags, flags)
|
||||
|
||||
if(!location)
|
||||
qdel(src) // if it's left the world... kill it
|
||||
return
|
||||
/obj/item/projectile/CanPass()
|
||||
return TRUE
|
||||
|
||||
if (is_below_sound_pressure(get_turf(src)) && !vacuum_traversal) //Deletes projectiles that aren't supposed to bein vacuum if they leave pressurised areas
|
||||
qdel(src)
|
||||
return
|
||||
//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(mob/living/target_mob, distance, miss_modifier = 0)
|
||||
if(!istype(target_mob))
|
||||
return
|
||||
|
||||
before_move()
|
||||
Move(location.return_turf())
|
||||
after_move()
|
||||
//roll to-hit
|
||||
miss_modifier = max(15*(distance-2) - accuracy + miss_modifier + target_mob.get_evasion(), 0)
|
||||
var/hit_zone = get_zone_with_miss_chance(def_zone, target_mob, miss_modifier, ranged_attack=(distance > 1 || original != target_mob)) //if the projectile hits a target we weren't originally aiming at then retain the chance to miss
|
||||
|
||||
if(!bumped && !isturf(original))
|
||||
if(loc == get_turf(original))
|
||||
if(!(original in permutated))
|
||||
if(Bump(original))
|
||||
return
|
||||
var/result = PROJECTILE_FORCE_MISS
|
||||
if(hit_zone)
|
||||
def_zone = hit_zone //set def_zone, so if the projectile ends up hitting someone else later (to be implemented), it is more likely to hit the same part
|
||||
result = target_mob.bullet_act(src, def_zone)
|
||||
|
||||
if(first_step)
|
||||
muzzle_effect(effect_transform)
|
||||
first_step = 0
|
||||
else if(!bumped)
|
||||
tracer_effect(effect_transform)
|
||||
if(result == PROJECTILE_FORCE_MISS)
|
||||
if(!silenced)
|
||||
visible_message("<span class='notice'>\The [src] misses [target_mob] narrowly!</span>")
|
||||
return FALSE
|
||||
|
||||
if(incendiary >= 2) //This should cover the bases of 'Why is there fuel here?' in a much cleaner way than previous.
|
||||
if(src && src.loc) //Safety.
|
||||
if(!src.loc.density)
|
||||
var/trail_volume = (flammability * 0.20)
|
||||
new /obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel(src.loc, trail_volume, src.dir)
|
||||
|
||||
if(!hitscan)
|
||||
sleep(step_delay) //add delay between movement iterations if it's not a hitscan weapon
|
||||
|
||||
/obj/item/projectile/proc/before_move()
|
||||
return
|
||||
|
||||
/obj/item/projectile/proc/after_move()
|
||||
return
|
||||
|
||||
/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)
|
||||
//hit messages
|
||||
if(silenced)
|
||||
return
|
||||
to_chat(target_mob, "<span class='danger'>You've been hit in the [parse_zone(def_zone)] by \the [src]!</span>")
|
||||
else
|
||||
visible_message("<span class='danger'>\The [target_mob] is hit by \the [src] in the [parse_zone(def_zone)]!</span>")//X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter
|
||||
|
||||
if(ispath(muzzle_type))
|
||||
var/obj/effect/projectile/M = new muzzle_type(get_turf(src))
|
||||
//admin logs
|
||||
if(!no_attack_log)
|
||||
if(istype(firer, /mob) && istype(target_mob))
|
||||
add_attack_logs(firer,target_mob,"Shot with \a [src.type] projectile")
|
||||
|
||||
if(istype(M))
|
||||
M.set_transform(T)
|
||||
M.pixel_x = location.pixel_x
|
||||
M.pixel_y = location.pixel_y
|
||||
M.update_light()
|
||||
M.activate()
|
||||
//sometimes bullet_act() will want the projectile to continue flying
|
||||
if (result == PROJECTILE_CONTINUE)
|
||||
return FALSE
|
||||
|
||||
/obj/item/projectile/proc/tracer_effect(var/matrix/M)
|
||||
if(ispath(tracer_type))
|
||||
var/obj/effect/projectile/P = new tracer_type(location.loc)
|
||||
return TRUE
|
||||
|
||||
if(istype(P))
|
||||
P.set_transform(M)
|
||||
P.pixel_x = location.pixel_x
|
||||
P.pixel_y = location.pixel_y
|
||||
P.update_light()
|
||||
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(ispath(tracer_type) && location)
|
||||
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.update_light()
|
||||
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.
|
||||
var/atom/movable/result_ref = null // The thing that got hit that made the check return true.
|
||||
|
||||
/obj/item/projectile/test/Bump(atom/A as mob|obj|turf|area)
|
||||
if(A == firer)
|
||||
loc = A.loc
|
||||
return //cannot shoot yourself
|
||||
if(istype(A, /obj/item/projectile))
|
||||
return
|
||||
if(istype(A, /obj/structure/foamedmetal)) //Turrets can detect through foamed metal, but will have to blast through it. Similar to windows, if someone runs behind it, a person should probably just not shoot.
|
||||
return
|
||||
if(istype(A, /obj/structure/girder)) //They see you there.
|
||||
return
|
||||
if(istype(A, /obj/structure/door_assembly)) //And through there.
|
||||
return
|
||||
if(istype(A, /obj/structure)) //Unanchored things you can shove around will still keep the turret or other firing at your position. Aim intent still functions.
|
||||
var/obj/structure/S = A
|
||||
if(!S.anchored)
|
||||
return
|
||||
if(istype(A, /mob/living) || istype(A, /obj/mecha) || istype(A, /obj/vehicle))
|
||||
result_ref = A
|
||||
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/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0)
|
||||
original = target
|
||||
def_zone = check_zone(target_zone)
|
||||
firer = user
|
||||
var/direct_target
|
||||
if(get_turf(target) == get_turf(src))
|
||||
direct_target = target
|
||||
|
||||
//plot the initial trajectory
|
||||
setup_trajectory(curloc, targloc)
|
||||
return process(targloc)
|
||||
preparePixelProjectile(target, user? user : get_turf(src), params, forced_spread)
|
||||
return fire(angle_override, direct_target)
|
||||
|
||||
/obj/item/projectile/test/process(var/turf/targloc)
|
||||
while(src) //Loop on through!
|
||||
if(result)
|
||||
return result_ref
|
||||
// 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
|
||||
//called to launch a projectile from a gun
|
||||
/obj/item/projectile/proc/launch_from_gun(atom/target, target_zone, mob/user, params, angle_override, forced_spread, obj/item/weapon/gun/launcher)
|
||||
|
||||
trajectory.increment() // increment the current location
|
||||
location = trajectory.return_location(location) // update the locally stored location data
|
||||
shot_from = launcher.name
|
||||
silenced = launcher.silenced
|
||||
|
||||
Move(location.return_turf())
|
||||
|
||||
var/mob/living/M = locate() in get_turf(src)
|
||||
if(istype(M)) //If there is someting living...
|
||||
result_ref = M
|
||||
return result_ref //Return 1
|
||||
else
|
||||
M = locate() in get_step(src,targloc)
|
||||
if(istype(M))
|
||||
result_ref = M
|
||||
return result_ref
|
||||
|
||||
//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
|
||||
|
||||
var/output = trace.launch(target) //Test it!
|
||||
qdel(trace) //No need for it anymore
|
||||
return output //Send it back to the gun!
|
||||
return launch_projectile(target, target_zone, user, params, angle_override, forced_spread)
|
||||
|
||||
@@ -9,12 +9,16 @@
|
||||
/obj/item/projectile/arc
|
||||
name = "arcing shot"
|
||||
icon_state = "fireball" // WIP
|
||||
step_delay = 2 // Travel a bit slower, to really sell the arc visuals.
|
||||
speed = 2 // Travel a bit slower, to really sell the arc visuals.
|
||||
movement_type = UNSTOPPABLE
|
||||
plane = ABOVE_PLANE // Since projectiles are 'in the air', they might visually overlap mobs while in flight, so the projectile needs to be above their plane.
|
||||
var/target_distance = null // How many tiles the impact site is.
|
||||
var/fired_dir = null // Which direction was the projectile fired towards. Needed to invert the projectile turning based on if facing left or right.
|
||||
var/obj/effect/projectile_shadow/shadow = null // Visual indicator for the projectile's 'true' position. Needed due to being bound to two dimensions in reality.
|
||||
|
||||
/obj/item/projectile/arc/Bump()
|
||||
return
|
||||
|
||||
/obj/item/projectile/arc/Initialize()
|
||||
shadow = new(get_turf(src))
|
||||
return ..()
|
||||
@@ -23,33 +27,32 @@
|
||||
QDEL_NULL(shadow)
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/arc/Bump(atom/A, forced=0)
|
||||
return 0
|
||||
// if(get_turf(src) != original)
|
||||
// return 0
|
||||
// else
|
||||
// return ..()
|
||||
|
||||
// This is a test projectile in the sense that its testing the code to make sure it works,
|
||||
// as opposed to a 'can I hit this thing' projectile.
|
||||
/obj/item/projectile/arc/test/on_impact(turf/T)
|
||||
new /obj/effect/explosion(T)
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/arc/launch(atom/target, target_zone, x_offset=0, y_offset=0, angle_offset=0)
|
||||
var/expected_distance = get_dist(target, loc)
|
||||
kill_count = expected_distance // So the projectile "hits the ground."
|
||||
/obj/item/projectile/arc/old_style_target(target, source)
|
||||
var/source_loc = get_turf(source) || get_turf(src)
|
||||
var/expected_distance = get_dist(target, source_loc)
|
||||
range = expected_distance // So the projectile "hits the ground."
|
||||
target_distance = expected_distance
|
||||
fired_dir = get_dir(loc, target)
|
||||
..() // Does the regular launching stuff.
|
||||
fired_dir = get_dir(source_loc, target)
|
||||
..()
|
||||
if(fired_dir & EAST)
|
||||
transform = turn(transform, -45)
|
||||
else if(fired_dir & WEST)
|
||||
transform = turn(transform, 45)
|
||||
|
||||
/obj/item/projectile/arc/on_range()
|
||||
on_impact(loc)
|
||||
return ..()
|
||||
|
||||
// Visuals.
|
||||
/obj/item/projectile/arc/after_move()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
// Handle projectile turning in flight.
|
||||
// This won't turn if fired north/south, as it looks weird.
|
||||
var/turn_per_step = 90 / target_distance
|
||||
@@ -73,7 +76,7 @@
|
||||
var/projectile_position = arc_progress / target_distance
|
||||
var/sine_position = projectile_position * 180
|
||||
var/pixel_z_position = arc_max_height * sin(sine_position)
|
||||
animate(src, pixel_z = pixel_z_position, time = step_delay)
|
||||
animate(src, pixel_z = pixel_z_position, time = speed)
|
||||
|
||||
// Update our shadow.
|
||||
shadow.forceMove(loc)
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
light_power = 0.5
|
||||
light_color = "#FF0D00"
|
||||
|
||||
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"
|
||||
@@ -54,9 +54,9 @@
|
||||
light_power = 1
|
||||
light_color = "#FF0D00"
|
||||
|
||||
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/laser_heavy
|
||||
tracer_type = /obj/effect/projectile/tracer/laser_heavy
|
||||
impact_type = /obj/effect/projectile/impact/laser_heavy
|
||||
|
||||
/obj/item/projectile/beam/heavylaser/fakeemitter
|
||||
name = "emitter beam"
|
||||
@@ -64,9 +64,9 @@
|
||||
fire_sound = 'sound/weapons/emitter.ogg'
|
||||
light_color = "#00CC33"
|
||||
|
||||
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/heavylaser/cannon
|
||||
damage = 80
|
||||
@@ -81,9 +81,9 @@
|
||||
armor_penetration = 50
|
||||
light_color = "#00CC33"
|
||||
|
||||
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/cyan
|
||||
name = "cyan beam"
|
||||
@@ -91,9 +91,9 @@
|
||||
damage = 40
|
||||
light_color = "#00C6FF"
|
||||
|
||||
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/laser_omni
|
||||
tracer_type = /obj/effect/projectile/tracer/laser_omni
|
||||
impact_type = /obj/effect/projectile/impact/laser_omni
|
||||
|
||||
/obj/item/projectile/beam/pulse
|
||||
name = "pulse"
|
||||
@@ -103,9 +103,9 @@
|
||||
armor_penetration = 100
|
||||
light_color = "#0066FF"
|
||||
|
||||
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/laser_pulse
|
||||
tracer_type = /obj/effect/projectile/tracer/laser_pulse
|
||||
impact_type = /obj/effect/projectile/impact/laser_pulse
|
||||
|
||||
/obj/item/projectile/beam/pulse/on_hit(var/atom/target, var/blocked = 0)
|
||||
if(isturf(target))
|
||||
@@ -119,9 +119,9 @@
|
||||
damage = 0 // The actual damage is computed in /code/modules/power/singularity/emitter.dm
|
||||
light_color = "#00CC33"
|
||||
|
||||
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"
|
||||
@@ -134,9 +134,9 @@
|
||||
|
||||
combustion = FALSE
|
||||
|
||||
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))
|
||||
@@ -173,9 +173,9 @@
|
||||
|
||||
combustion = FALSE
|
||||
|
||||
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/laser_omni
|
||||
tracer_type = /obj/effect/projectile/tracer/laser_omni
|
||||
impact_type = /obj/effect/projectile/impact/laser_omni
|
||||
|
||||
/obj/item/projectile/beam/lastertag/omni/on_hit(var/atom/target, var/blocked = 0)
|
||||
if(istype(target, /mob/living/carbon/human))
|
||||
@@ -192,9 +192,9 @@
|
||||
armor_penetration = 10
|
||||
light_color = "#00CC33"
|
||||
|
||||
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"
|
||||
@@ -208,9 +208,9 @@
|
||||
|
||||
combustion = FALSE
|
||||
|
||||
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/stun/weak
|
||||
name = "weak stun beam"
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
damage_type = HALLOSS
|
||||
light_color = "#00CECE"
|
||||
|
||||
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/laser_omni
|
||||
tracer_type = /obj/effect/projectile/tracer/laser_omni
|
||||
impact_type = /obj/effect/projectile/impact/laser_omni
|
||||
|
||||
/obj/item/projectile/beam/stun
|
||||
agony = 35
|
||||
@@ -22,9 +22,9 @@
|
||||
damage_type = HALLOSS
|
||||
light_color = "#00CC33"
|
||||
|
||||
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)
|
||||
@@ -38,6 +38,6 @@
|
||||
icon_state = "bluelaser"
|
||||
light_color = "#0066FF"
|
||||
|
||||
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
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
sharp = 1
|
||||
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)
|
||||
if (..(target, blocked))
|
||||
@@ -257,12 +257,12 @@
|
||||
incendiary = 2
|
||||
flammability = 4
|
||||
agony = 30
|
||||
kill_count = 4
|
||||
range = 4
|
||||
vacuum_traversal = 0
|
||||
|
||||
/obj/item/projectile/bullet/incendiary/flamethrower/large
|
||||
damage = 15
|
||||
kill_count = 6
|
||||
range = 6
|
||||
|
||||
/* Practice rounds and blanks */
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
name = "chemical shell"
|
||||
icon_state = "bullet"
|
||||
damage = 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.
|
||||
flash_strength = 15
|
||||
brightness = 15
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
icon_state = "bullet"
|
||||
fire_sound = 'sound/weapons/gunshot_pathetic.ogg'
|
||||
damage = 5
|
||||
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_colour = "#ffffff"
|
||||
@@ -165,7 +165,7 @@
|
||||
icon_state = "plasma_stun"
|
||||
fire_sound = 'sound/weapons/blaster.ogg'
|
||||
armor_penetration = 10
|
||||
kill_count = 4
|
||||
range = 4
|
||||
damage = 5
|
||||
agony = 55
|
||||
damage_type = BURN
|
||||
@@ -212,25 +212,25 @@
|
||||
light_color = "#0000FF"
|
||||
|
||||
embed_chance = 0
|
||||
muzzle_type = /obj/effect/projectile/pulse/muzzle
|
||||
muzzle_type = /obj/effect/projectile/muzzle/pulse
|
||||
|
||||
/obj/item/projectile/energy/phase
|
||||
name = "phase wave"
|
||||
icon_state = "phase"
|
||||
kill_count = 6
|
||||
range = 6
|
||||
damage = 5
|
||||
SA_bonus_damage = 45 // 50 total on animals
|
||||
SA_vulnerability = SA_ANIMAL
|
||||
|
||||
/obj/item/projectile/energy/phase/light
|
||||
kill_count = 4
|
||||
range = 4
|
||||
SA_bonus_damage = 35 // 40 total on animals
|
||||
|
||||
/obj/item/projectile/energy/phase/heavy
|
||||
kill_count = 8
|
||||
range = 8
|
||||
SA_bonus_damage = 55 // 60 total on animals
|
||||
|
||||
/obj/item/projectile/energy/phase/heavy/cannon
|
||||
kill_count = 10
|
||||
range = 10
|
||||
damage = 15
|
||||
SA_bonus_damage = 60 // 75 total on animals
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
var/beam_state = "b_beam"
|
||||
|
||||
damage = 5
|
||||
step_delay = 2
|
||||
speed = 2
|
||||
damage_type = BURN
|
||||
check_armour = "energy"
|
||||
armor_penetration = 15
|
||||
@@ -27,9 +27,9 @@
|
||||
var/list/help_messages = list("slaps", "pokes", "nudges", "bumps", "pinches")
|
||||
var/done_mob_unique = FALSE // Has the projectile already done something to a mob?
|
||||
|
||||
/obj/item/projectile/energy/hook/launch(atom/target, target_zone, x_offset=0, y_offset=0, angle_offset=0)
|
||||
/obj/item/projectile/energy/hook/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0)
|
||||
var/expected_distance = get_dist(target, loc)
|
||||
kill_count = expected_distance // So the hook hits the ground if no mob is hit.
|
||||
range = expected_distance // So the hook hits the ground if no mob is hit.
|
||||
target_distance = expected_distance
|
||||
if(firer) // Needed to ensure later checks in impact and on hit function.
|
||||
launcher_intent = firer.a_intent
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
penetrating = 2
|
||||
embed_chance = 0
|
||||
armor_penetration = 40
|
||||
kill_count = 20
|
||||
range = 20
|
||||
|
||||
var/searing = 0 //Does this fuelrod ignore shields?
|
||||
var/detonate_travel = 0 //Will this fuelrod explode when it reaches maximum distance?
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
if(energetic_impact)
|
||||
var/eye_coverage = 0
|
||||
for(var/mob/living/carbon/M in viewers(world.view, location))
|
||||
for(var/mob/living/carbon/M in viewers(world.view, get_turf(src)))
|
||||
eye_coverage = 0
|
||||
if(iscarbon(M))
|
||||
eye_coverage = M.eyecheck()
|
||||
@@ -103,7 +103,7 @@
|
||||
armor_penetration = 100
|
||||
penetrating = 100 //Theoretically, this shouldn't stop flying for a while, unless someone lines it up with a wall or fires it into a mountain.
|
||||
irradiate = 120
|
||||
kill_count = 75
|
||||
range = 75
|
||||
searing = 1
|
||||
detonate_travel = 1
|
||||
detonate_mob = 1
|
||||
@@ -127,7 +127,7 @@
|
||||
penetrating = 0
|
||||
check_armour = "melee"
|
||||
irradiate = 20
|
||||
kill_count = 6
|
||||
range = 6
|
||||
|
||||
/obj/item/projectile/bullet/magnetic/bore/Bump(atom/A, forced=0)
|
||||
if(istype(A, /turf/simulated/mineral))
|
||||
|
||||
@@ -9,10 +9,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/Bumped()
|
||||
. = ..()
|
||||
bumped = 0 //can hit all mobs in a tile. pellets is decremented inside attack_mob so this should be fine.
|
||||
|
||||
/obj/item/projectile/bullet/pellet/proc/get_pellets(var/distance)
|
||||
var/pellet_loss = round((distance - 1)/range_step) //pellets lost due to distance
|
||||
return max(pellets - pellet_loss, 1)
|
||||
|
||||
@@ -219,7 +219,7 @@
|
||||
embed_chance = 0 // nope
|
||||
nodamage = 1
|
||||
damage_type = HALLOSS
|
||||
muzzle_type = /obj/effect/projectile/bullet/muzzle
|
||||
muzzle_type = /obj/effect/projectile/muzzle/bullet
|
||||
|
||||
/obj/item/projectile/bola
|
||||
name = "bola"
|
||||
|
||||
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/Bump(atom/A)
|
||||
if(A != src)
|
||||
hit |= A
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/test/attack_mob()
|
||||
return
|
||||
Reference in New Issue
Block a user