Moves projectile processing to a subsystem

This commit is contained in:
kevinz000
2017-10-15 19:50:42 -07:00
committed by CitadelStationBot
parent be2dc49384
commit ed96224d3a
31 changed files with 486 additions and 333 deletions

View File

@@ -25,6 +25,9 @@ GLOBAL_LIST_INIT(sqrtTable, list(1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4,
#define Clamp(CLVALUE,CLMIN,CLMAX) ( max( (CLMIN), min((CLVALUE), (CLMAX)) ) )
/proc/Modulus(x, y) //Byond's modulus doesn't work with decimals.
return x - y * round(x / y)
// cotangent
/proc/Cot(x)
return 1 / Tan(x)

View File

@@ -385,13 +385,11 @@
Laser Eyes: as the name implies, handles this since nothing else does currently
face_atom: turns the mob towards what you clicked on
*/
/mob/proc/LaserEyes(atom/A)
/mob/proc/LaserEyes(atom/A, params)
return
/mob/living/LaserEyes(atom/A)
/mob/living/LaserEyes(atom/A, params)
changeNext_move(CLICK_CD_RANGE)
var/turf/T = get_turf(src)
var/turf/U = get_turf(A)
var/obj/item/projectile/beam/LE = new /obj/item/projectile/beam( loc )
LE.icon = 'icons/effects/genetics.dmi'
@@ -400,10 +398,7 @@
LE.firer = src
LE.def_zone = get_organ_target()
LE.original = A
LE.current = T
LE.yo = U.y - T.y
LE.xo = U.x - T.x
LE.preparePixelProjectile(A, src, params)
LE.fire()
// Simple helper to face what you clicked on, in case it should be needed in more than one place

View File

@@ -1,3 +1,4 @@
<<<<<<< HEAD
/*
Humans:
Adds an exception for gloves, to allow special glove types like the ninja ones.
@@ -100,6 +101,110 @@
"<span class='userdanger'>[name] bites [ML]!</span>")
if(armor >= 2)
return
=======
/*
Humans:
Adds an exception for gloves, to allow special glove types like the ninja ones.
Otherwise pretty standard.
*/
/mob/living/carbon/human/UnarmedAttack(atom/A, proximity)
if(!has_active_hand()) //can't attack without a hand.
to_chat(src, "<span class='notice'>You look at your arm and sigh.</span>")
return
// Special glove functions:
// If the gloves do anything, have them return 1 to stop
// normal attack_hand() here.
var/obj/item/clothing/gloves/G = gloves // not typecast specifically enough in defines
if(proximity && istype(G) && G.Touch(A,1))
return
var/override = 0
for(var/datum/mutation/human/HM in dna.mutations)
override += HM.on_attack_hand(src, A, proximity)
if(override)
return
A.attack_hand(src)
/atom/proc/attack_hand(mob/user)
return
/atom/proc/interact(mob/user)
return
/*
/mob/living/carbon/human/RestrainedClickOn(var/atom/A) ---carbons will handle this
return
*/
/mob/living/carbon/RestrainedClickOn(atom/A)
return 0
/mob/living/carbon/human/RangedAttack(atom/A, mouseparams)
if(gloves)
var/obj/item/clothing/gloves/G = gloves
if(istype(G) && G.Touch(A,0)) // for magic gloves
return
for(var/datum/mutation/human/HM in dna.mutations)
HM.on_ranged_attack(src, A, mouseparams)
if(isturf(A) && get_dist(src,A) <= 1)
src.Move_Pulled(A)
/*
Animals & All Unspecified
*/
/mob/living/UnarmedAttack(atom/A)
A.attack_animal(src)
/atom/proc/attack_animal(mob/user)
return
/mob/living/RestrainedClickOn(atom/A)
return
/*
Monkeys
*/
/mob/living/carbon/monkey/UnarmedAttack(atom/A)
A.attack_paw(src)
/atom/proc/attack_paw(mob/user)
return
/*
Monkey RestrainedClickOn() was apparently the
one and only use of all of the restrained click code
(except to stop you from doing things while handcuffed);
moving it here instead of various hand_p's has simplified
things considerably
*/
/mob/living/carbon/monkey/RestrainedClickOn(atom/A)
if(..())
return
if(a_intent != INTENT_HARM || !ismob(A))
return
if(is_muzzled())
return
var/mob/living/carbon/ML = A
if(istype(ML))
var/dam_zone = pick("chest", "l_hand", "r_hand", "l_leg", "r_leg")
var/obj/item/bodypart/affecting = null
if(ishuman(ML))
var/mob/living/carbon/human/H = ML
affecting = H.get_bodypart(ran_zone(dam_zone))
var/armor = ML.run_armor_check(affecting, "melee")
if(prob(75))
ML.apply_damage(rand(1,3), BRUTE, affecting, armor)
ML.visible_message("<span class='danger'>[name] bites [ML]!</span>", \
"<span class='userdanger'>[name] bites [ML]!</span>")
if(armor >= 2)
return
>>>>>>> 64851d8... Moves projectile processing to a subsystem (#30599)
for(var/thing in viruses)
var/datum/disease/D = thing
ML.ForceContractDisease(D)

View File

@@ -0,0 +1,7 @@
PROCESSING_SUBSYSTEM_DEF(projectiles)
name = "Projectiles"
priority = 25
wait = 1
stat_tag = "PP"
flags = SS_NO_INIT|SS_TICKER|SS_KEEP_TIMING
var/global_max_tick_moves = 10

View File

@@ -645,9 +645,9 @@ GLOBAL_LIST_EMPTY(mutations_list)
/datum/mutation/human/laser_eyes/get_visual_indicator(mob/living/carbon/human/owner)
return visual_indicators[1]
/datum/mutation/human/laser_eyes/on_ranged_attack(mob/living/carbon/human/owner, atom/target)
/datum/mutation/human/laser_eyes/on_ranged_attack(mob/living/carbon/human/owner, atom/target, mouseparams)
if(owner.a_intent == INTENT_HARM)
owner.LaserEyes(target)
owner.LaserEyes(target, mouseparams)
/mob/living/carbon/proc/update_mutations_overlay()

View File

@@ -433,11 +433,7 @@
playsound(spawn_turf, 'sound/effects/curse2.ogg', 80, 1, -1)
var/turf/ownerloc = get_turf(owner)
var/obj/item/projectile/curse_hand/C = new (spawn_turf)
C.current = spawn_turf
C.starting = spawn_turf
C.yo = ownerloc.y - spawn_turf.y
C.xo = ownerloc.x - spawn_turf.x
C.original = owner
C.preparePixelProjectile(ownerloc, spawn_turf)
C.fire()
/obj/effect/temp_visual/curse

View File

@@ -156,11 +156,7 @@
add_logs(ranged_ability_user, U, "fired at with Kindle")
playsound(ranged_ability_user, 'sound/magic/blink.ogg', 50, TRUE, frequency = 0.5)
var/obj/item/projectile/kindle/A = new(T)
A.original = target
A.starting = T
A.current = T
A.yo = U.y - T.y
A.xo = U.x - T.x
A.preparePixelProjectile(target, caller, params)
A.fire()
remove_ranged_ability()

View File

@@ -517,11 +517,7 @@
//Shooting Code:
A.original = target
A.starting = T
A.current = T
A.yo = U.y - T.y
A.xo = U.x - T.x
A.preparePixelProjectile(target, src)
A.fire()
return A

View File

@@ -35,18 +35,16 @@
var/obj/item/projectile/A = new projectile(curloc)
A.firer = chassis.occupant
A.original = target
A.current = curloc
if(!A.suppressed && firing_effect_type)
new firing_effect_type(get_turf(src), chassis.dir)
var/spread = 0
if(variance)
if(randomspread)
spread = round((rand() - 0.5) * variance)
else
spread = round((i / projectiles_per_shot - 0.5) * variance)
A.preparePixelProjectile(target, targloc, chassis.occupant, params, spread)
A.preparePixelProjectile(target, chassis.occupant, params, spread)
A.fire()
playsound(chassis, fire_sound, 50, 1)

View File

@@ -150,7 +150,6 @@
if(QDELETED(target))
target = target_turf
var/obj/item/projectile/P = new projectile_type(targets_from)
P.current = targets_from
P.starting = targets_from
P.firer = user
P.original = target

View File

@@ -2,45 +2,51 @@
name = "reflector frame"
icon = 'icons/obj/stock_parts.dmi'
icon_state = "box_0"
desc = "An angled mirror for reflecting lasers. This one does so at a 90 degree angle."
desc = "An angled mirror for reflecting lasers."
anchored = FALSE
density = TRUE
layer = BELOW_OBJ_LAYER
var/finished = 0
var/admin = 0 //Can't be rotated or deconstructed
var/finished = FALSE
var/admin = FALSE //Can't be rotated or deconstructed
var/framebuildstacktype = /obj/item/stack/sheet/metal
var/framebuildstackamount = 5
var/buildstacktype = /obj/item/stack/sheet/metal
var/buildstackamount = 0
var/list/allowed_projectile_typecache = list(/obj/item/projectile/beam)
var/rotation_angle = -1
/obj/structure/reflector/Initialize()
. = ..()
allowed_projectile_typecache = typecacheof(allowed_projectile_typecache)
if(rotation_angle == -1)
setAngle(dir2angle(dir))
else
setAngle(rotation_angle)
/obj/structure/reflector/examine(mob/user)
..()
to_chat(user, "<span class='notice'>Alt-click to adjust its direction.</span>")
/obj/structure/reflector/Moved()
setAngle(dir_map_to_angle(dir))
return ..()
/obj/structure/reflector/proc/dir_map_to_angle(dir)
return 0
/obj/structure/reflector/bullet_act(obj/item/projectile/P)
var/turf/reflector_turf = get_turf(src)
var/turf/reflect_turf
var/new_dir = get_reflection(src.dir,P.dir)
if(!istype(P, /obj/item/projectile/beam))
return..()
if(new_dir)
reflect_turf = get_step(reflect_turf, new_dir)
else
visible_message("<span class='notice'>[src] is hit by the [P]!</span>")
new_dir = 0
return ..() //Hits as normal, explodes or emps or whatever
reflect_turf = get_step(loc,new_dir)
P.original = reflect_turf
P.starting = reflector_turf
P.current = reflector_turf
P.yo = reflect_turf.y - reflector_turf.y
P.xo = reflect_turf.x - reflector_turf.x
P.range = initial(P.range) //Keep the projectile healthy as long as its bouncing off things
new_dir = 0
return - 1
var/pdir = P.dir
var/pangle = P.Angle
var/ploc = get_turf(P)
if(!finished || !allowed_projectile_typecache[P.type] || !(P.dir in GLOB.cardinals))
return ..()
if(auto_reflect(P, pdir, ploc, pangle) != -1)
return ..()
return -1
/obj/structure/reflector/proc/auto_reflect(obj/item/projectile/P, pdir, turf/ploc, pangle)
P.ignore_source_check = TRUE
return -1
/obj/structure/reflector/attackby(obj/item/W, mob/user, params)
if(admin)
@@ -56,83 +62,74 @@
qdel(src)
else if(istype(W, /obj/item/weldingtool))
var/obj/item/weldingtool/WT = W
switch(anchored)
if(0)
if (WT.remove_fuel(0,user))
playsound(src.loc, 'sound/items/welder2.ogg', 50, 1)
user.visible_message("[user.name] starts to weld [src] to the floor.", \
"<span class='notice'>You start to weld \the [src] to the floor...</span>", \
"<span class='italics'>You hear welding.</span>")
if (do_after(user,20*W.toolspeed, target = src))
if(!src || !WT.isOn())
return
anchored = TRUE
to_chat(user, "<span class='notice'>You weld \the [src] to the floor.</span>")
if(1)
if (WT.remove_fuel(0,user))
playsound(src.loc, 'sound/items/welder2.ogg', 50, 1)
user.visible_message("[user.name] starts to cut [src] free from the floor.", \
"<span class='notice'>You start to cut \the [src] free from the floor...</span>", \
"<span class='italics'>You hear welding.</span>")
if (do_after(user,20*W.toolspeed, target = src))
if(!src || !WT.isOn())
return
anchored = 0
to_chat(user, "<span class='notice'>You cut \the [src] free from the floor.</span>")
if(!anchored)
if (WT.remove_fuel(0,user))
playsound(src, 'sound/items/welder2.ogg', 50, 1)
user.visible_message("[user] starts to weld [src] to the floor.", "<span class='notice'>You start to weld [src] to the floor...</span>", "<span class='italics'>You hear welding.</span>")
if (do_after(user,20*W.toolspeed, target = src))
if(!WT.isOn())
return
anchored = TRUE
to_chat(user, "<span class='notice'>You weld [src] to the floor.</span>")
else
if (WT.remove_fuel(0,user))
playsound(src, 'sound/items/welder2.ogg', 50, 1)
user.visible_message("[user] starts to cut [src] free from the floor.", "<span class='notice'>You start to cut [src] free from the floor...</span>", "<span class='italics'>You hear welding.</span>")
if (do_after(user,20*W.toolspeed, target = src))
if(!WT.isOn())
return
anchored = FALSE
to_chat(user, "<span class='notice'>You cut [src] free from the floor.</span>")
//Finishing the frame
else if(istype(W, /obj/item/stack/sheet))
if(finished)
return
var/obj/item/stack/sheet/S = W
if(istype(W, /obj/item/stack/sheet/glass))
if(S.get_amount() < 5)
if(S.use(5))
new /obj/structure/reflector/single (loc)
qdel (src)
else
to_chat(user, "<span class='warning'>You need five sheets of glass to create a reflector!</span>")
return
else
S.use(5)
new /obj/structure/reflector/single (src.loc)
qdel (src)
if(istype(W, /obj/item/stack/sheet/rglass))
if(S.get_amount() < 10)
if(S.use(10))
new /obj/structure/reflector/double (loc)
qdel(src)
else
to_chat(user, "<span class='warning'>You need ten sheets of reinforced glass to create a double reflector!</span>")
return
else
S.use(10)
new /obj/structure/reflector/double (src.loc)
qdel(src)
if(istype(W, /obj/item/stack/sheet/mineral/diamond))
if(S.get_amount() >= 1)
S.use(1)
new /obj/structure/reflector/box (src.loc)
if(S.use(1))
new /obj/structure/reflector/box (loc)
qdel(src)
else
return ..()
/obj/structure/reflector/proc/get_reflection(srcdir,pdir)
return 0
/obj/structure/reflector/verb/rotate()
set name = "Rotate"
set category = "Object"
set src in oview(1)
if(usr.stat || !usr.canmove || usr.restrained())
/obj/structure/reflector/proc/rotate(mob/user)
if (anchored)
to_chat(user, "<span class='warning'>It is fastened to the floor!</span>")
return FALSE
var/new_angle = input(user, "Input a new angle for primary reflection face.", "Reflector Angle") as null|num
if(!user.canUseTopic(src, be_close=TRUE))
to_chat(user, "<span class='warning'>You can't do that right now!</span>")
return
if (src.anchored)
to_chat(usr, "<span class='warning'>It is fastened to the floor!</span>")
return 0
src.setDir(turn(src.dir, 270))
return 1
setAngle(NORM_ROT(new_angle))
return TRUE
/obj/structure/reflector/proc/setAngle(new_angle)
rotation_angle = new_angle
setDir(NORTH)
var/matrix/M = new
M.Turn(new_angle)
transform = M
/obj/structure/reflector/AltClick(mob/user)
..()
if(!user.canUseTopic(src, be_close=TRUE))
to_chat(user, "<span class='warning'>You can't do that right now!</span>")
return
else
rotate()
rotate(user)
//TYPES OF REFLECTORS, SINGLE, DOUBLE, BOX
@@ -144,25 +141,30 @@
icon = 'icons/obj/structures.dmi'
icon_state = "reflector"
desc = "A double sided angled mirror for reflecting lasers. This one does so at a 90 degree angle."
finished = 1
var/static/list/rotations = list("[NORTH]" = list("[SOUTH]" = WEST, "[EAST]" = NORTH),
"[EAST]" = list("[SOUTH]" = EAST, "[WEST]" = NORTH),
"[SOUTH]" = list("[NORTH]" = EAST, "[WEST]" = SOUTH),
"[WEST]" = list("[NORTH]" = WEST, "[EAST]" = SOUTH) )
finished = TRUE
buildstacktype = /obj/item/stack/sheet/glass
buildstackamount = 5
/obj/structure/reflector/single/anchored
anchored = TRUE
/obj/structure/reflector/single/get_reflection(srcdir,pdir)
var/new_dir = rotations["[srcdir]"]["[pdir]"]
return new_dir
/obj/structure/reflector/single/mapping
admin = 1
admin = TRUE
anchored = TRUE
/obj/structure/reflector/single/auto_reflect(obj/item/projectile/P, pdir, turf/ploc, pangle)
var/incidence = get_angle_of_incidence(rotation_angle, P.Angle)
var/incidence_norm = get_angle_of_incidence(rotation_angle, P.Angle, FALSE)
if((incidence_norm > -90) && (incidence_norm < 90))
return FALSE
var/new_angle_s = rotation_angle + incidence
while(new_angle_s > 180) // Translate to regular projectile degrees
new_angle_s -= 360
while(new_angle_s < -180)
new_angle_s += 360
P.Angle = new_angle_s
return ..()
//DOUBLE
/obj/structure/reflector/double
@@ -170,25 +172,31 @@
icon = 'icons/obj/structures.dmi'
icon_state = "reflector_double"
desc = "A double sided angled mirror for reflecting lasers. This one does so at a 90 degree angle."
finished = 1
var/static/list/double_rotations = list("[NORTH]" = list("[NORTH]" = WEST, "[EAST]" = SOUTH, "[SOUTH]" = EAST, "[WEST]" = NORTH),
"[EAST]" = list("[NORTH]" = EAST, "[WEST]" = SOUTH, "[SOUTH]" = WEST, "[EAST]" = NORTH),
"[SOUTH]" = list("[NORTH]" = EAST, "[WEST]" = SOUTH, "[SOUTH]" = WEST, "[EAST]" = NORTH),
"[WEST]" = list("[NORTH]" = WEST, "[EAST]" = SOUTH, "[SOUTH]" = EAST, "[WEST]" = NORTH) )
finished = TRUE
buildstacktype = /obj/item/stack/sheet/rglass
buildstackamount = 10
/obj/structure/reflector/double/anchored
anchored = TRUE
/obj/structure/reflector/double/get_reflection(srcdir,pdir)
var/new_dir = double_rotations["[srcdir]"]["[pdir]"]
return new_dir
/obj/structure/reflector/double/mapping
admin = 1
admin = TRUE
anchored = TRUE
/obj/structure/reflector/double/auto_reflect(obj/item/projectile/P, pdir, turf/ploc, pangle)
var/incidence = get_angle_of_incidence(rotation_angle, P.Angle)
var/incidence_norm = get_angle_of_incidence(rotation_angle, P.Angle, FALSE)
var/invert = ((incidence_norm > -90) && (incidence_norm < 90))
var/new_angle_s = rotation_angle + incidence
if(invert)
new_angle_s += 180
while(new_angle_s > 180) // Translate to regular projectile degrees
new_angle_s -= 360
while(new_angle_s < -180)
new_angle_s += 360
P.Angle = new_angle_s
return ..()
//BOX
/obj/structure/reflector/box
@@ -196,35 +204,32 @@
icon = 'icons/obj/structures.dmi'
icon_state = "reflector_box"
desc = "A box with an internal set of mirrors that reflects all laser fire in a single direction."
finished = 1
var/static/list/box_rotations = list("[NORTH]" = list("[SOUTH]" = NORTH, "[EAST]" = NORTH, "[WEST]" = NORTH, "[NORTH]" = NORTH),
"[EAST]" = list("[SOUTH]" = EAST, "[EAST]" = EAST, "[WEST]" = EAST, "[NORTH]" = EAST),
"[SOUTH]" = list("[SOUTH]" = SOUTH, "[EAST]" = SOUTH, "[WEST]" = SOUTH, "[NORTH]" = SOUTH),
"[WEST]" = list("[SOUTH]" = WEST, "[EAST]" = WEST, "[WEST]" = WEST, "[NORTH]" = WEST) )
finished = TRUE
buildstacktype = /obj/item/stack/sheet/mineral/diamond
buildstackamount = 1
/obj/structure/reflector/box/anchored
anchored = TRUE
/obj/structure/reflector/box/get_reflection(srcdir,pdir)
var/new_dir = box_rotations["[srcdir]"]["[pdir]"]
return new_dir
/obj/structure/reflector/box/mapping
admin = 1
admin = TRUE
anchored = TRUE
/obj/structure/reflector/box/auto_reflect(obj/item/projectile/P)
P.Angle = rotation_angle
return ..()
/obj/structure/reflector/ex_act()
if(admin)
return
else
..()
return ..()
/obj/structure/reflector/dir_map_to_angle(dir)
return dir2angle(dir)
/obj/structure/reflector/singularity_act()
if(admin)
return
else
..()
return ..()

View File

@@ -1141,11 +1141,7 @@ GLOBAL_LIST_INIT(hallucinations_major, list(
var/obj/item/projectile/hallucination/H = new proj_type(start)
target.playsound_local(start, H.hal_fire_sound, 60, 1)
H.hal_target = target
H.current = start
H.starting = start
H.yo = target.y - start.y
H.xo = target.x - start.x
H.original = target
H.preparePixelProjectile(target, start)
H.fire()
qdel(src)

View File

@@ -65,7 +65,7 @@
if(!QDELETED(C) && !QDELETED(target))
C.total_damage += target_health - target.health //we did some damage, but let's not assume how much we did
/obj/item/twohanded/required/kinetic_crusher/afterattack(atom/target, mob/living/user, proximity_flag)
/obj/item/twohanded/required/kinetic_crusher/afterattack(atom/target, mob/living/user, proximity_flag, clickparams)
if(!proximity_flag && charged)//Mark a target, or mine a tile.
var/turf/proj_turf = user.loc
if(!isturf(proj_turf))
@@ -74,7 +74,7 @@
for(var/t in trophies)
var/obj/item/crusher_trophy/T = t
T.on_projectile_fire(D, user)
D.preparePixelProjectile(target, get_turf(target), user)
D.preparePixelProjectile(target, user, clickparams)
D.firer = user
D.hammer_synced = src
playsound(user, 'sound/weapons/plasma_cutter.ogg', 100, 1)

View File

@@ -214,8 +214,7 @@ Doesn't work on other aliens/AI.*/
user.visible_message("<span class='danger'>[user] spits neurotoxin!", "<span class='alertalien'>You spit neurotoxin.</span>")
var/obj/item/projectile/bullet/neurotoxin/A = new /obj/item/projectile/bullet/neurotoxin(user.loc)
A.current = U
A.preparePixelProjectile(target, get_turf(target), user, params)
A.preparePixelProjectile(target, user, params)
A.fire()
user.newtonian_move(get_dir(U, T))
user.adjustPlasma(-p_cost)

View File

@@ -62,7 +62,6 @@
// redirect the projectile
P.original = locate(new_x, new_y, P.z)
P.starting = curloc
P.current = curloc
P.firer = src
P.yo = new_y - curloc.y
P.xo = new_x - curloc.x

View File

@@ -360,16 +360,9 @@
if(P.starting)
var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
var/turf/curloc = get_turf(H)
var/turf/target = get_turf(P.starting)
// redirect the projectile
P.original = locate(new_x, new_y, P.z)
P.starting = curloc
P.current = curloc
P.firer = H
P.yo = new_y - curloc.y
P.xo = new_x - curloc.x
P.Angle = null
P.preparePixelProjectile(locate(Clamp(target.x + new_x, 1, world.maxx), Clamp(target.y + new_y, 1, world.maxy), H.z), H)
return -1
return 0

View File

@@ -444,9 +444,7 @@ Auto Patrol[]"},
var/obj/item/projectile/A = new projectile (loc)
playsound(loc, shoot_sound, 50, 1)
A.current = U
A.yo = U.y - T.y
A.xo = U.x - T.x
A.preparePixelProjectile(target, src)
A.fire()
/mob/living/simple_animal/bot/ed209/attack_alien(mob/living/carbon/alien/user)

View File

@@ -145,7 +145,6 @@
// redirect the projectile
P.original = locate(new_x, new_y, P.z)
P.starting = curloc
P.current = curloc
P.firer = src
P.yo = new_y - curloc.y
P.xo = new_x - curloc.x

View File

@@ -362,7 +362,6 @@
else if(projectiletype)
var/obj/item/projectile/P = new projectiletype(startloc)
playsound(src, projectilesound, 100, 1)
P.current = startloc
P.starting = startloc
P.firer = src
P.yo = targeted_atom.y - startloc.y
@@ -370,6 +369,7 @@
if(AIStatus != AI_ON)//Don't want mindless mobs to have their movement screwed up firing in space
newtonian_move(get_dir(targeted_atom, targets_from))
P.original = targeted_atom
P.preparePixelProjectile(targeted_atom, src)
P.fire()
return P

View File

@@ -182,12 +182,7 @@
return
var/turf/our_turf = get_turf(src)
var/obj/item/projectile/seedling/readied_shot = new /obj/item/projectile/seedling(our_turf)
readied_shot.current = our_turf
readied_shot.starting = our_turf
readied_shot.firer = src
readied_shot.original = target
readied_shot.yo = target.y - our_turf.y + rand(-1,1)
readied_shot.xo = target.x - our_turf.x + rand(-1,1)
readied_shot.preparePixelProjectile(target, src, null, rand(-10, 10))
readied_shot.fire()
playsound(src, projectilesound, 100, 1)

View File

@@ -164,7 +164,6 @@ Difficulty: Very Hard
return
var/turf/startloc = get_turf(src)
var/obj/item/projectile/P = new /obj/item/projectile/colossus(startloc)
P.current = startloc
P.starting = startloc
P.firer = src
if(marker)

View File

@@ -33,7 +33,7 @@
// The following 3 vars are mostly for the prototype
var/manual = FALSE
var/charge = 0
var/atom/target
var/last_projectile_params
/obj/machinery/power/emitter/anchored
anchored = TRUE
@@ -185,7 +185,7 @@
charge+=5
if(!check_delay() || manual == TRUE)
return FALSE
fire_beam(target)
fire_beam()
/obj/machinery/power/emitter/proc/check_delay()
if((src.last_shot + src.fire_delay) <= world.time)
@@ -201,46 +201,18 @@
add_load(active_power_usage)
fire_beam()
/obj/machinery/power/emitter/proc/fire_beam(atom/targeted_atom, mob/user)
var/turf/targets_from = get_turf(src)
if(targeted_atom && (targeted_atom == user || targeted_atom == targets_from || targeted_atom == src))
return
var/obj/item/projectile/P = new projectile_type(targets_from)
playsound(src.loc, projectile_sound, 50, 1)
/obj/machinery/power/emitter/proc/fire_beam(mob/user)
var/obj/item/projectile/P = new projectile_type(get_turf(src))
playsound(get_turf(src), projectile_sound, 50, 1)
if(prob(35))
sparks.start()
switch(dir)
if(NORTH)
P.yo = 20
P.xo = 0
if(NORTHEAST)
P.yo = 20
P.xo = 20
if(EAST)
P.yo = 0
P.xo = 20
if(SOUTHEAST)
P.yo = -20
P.xo = 20
if(WEST)
P.yo = 0
P.xo = -20
if(SOUTHWEST)
P.yo = -20
P.xo = -20
if(NORTHWEST)
P.yo = 20
P.xo = -20
else // Any other
P.yo = -20
P.xo = 0
if(target)
P.yo = targeted_atom.y - targets_from.y
P.xo = targeted_atom.x - targets_from.x
P.current = targets_from
P.starting = targets_from
P.firer = src
P.original = targeted_atom
P.firer = user? user : src
if(last_projectile_params)
P.p_x = last_projectile_params[2]
P.p_y = last_projectile_params[3]
P.fire(last_projectile_params[1])
else
P.fire(dir2angle(dir))
if(!manual)
last_shot = world.time
if(shot_number < 3)
@@ -249,12 +221,6 @@
else
fire_delay = rand(minimum_fire_delay,maximum_fire_delay)
shot_number = 0
if(!target)
P.setDir(src.dir)
P.starting = loc
else
if(QDELETED(target))
target = null
P.fire()
return P
@@ -449,7 +415,7 @@
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF | NOBLUDGEON_1
var/delay = 0
/obj/item/turret_control/afterattack(atom/targeted_atom, mob/user)
/obj/item/turret_control/afterattack(atom/targeted_atom, mob/user, proxflag, clickparams)
..()
var/obj/machinery/power/emitter/E = user.buckled
E.setDir(get_dir(E,targeted_atom))
@@ -488,10 +454,11 @@
user.pixel_x = 8
user.pixel_y = -12
E.last_projectile_params = calculate_projectile_angle_and_pixel_offsets(user, clickparams)
if(E.charge >= 10 && world.time > delay)
E.charge -= 10
E.target = targeted_atom
E.fire_beam(targeted_atom, user)
E.fire_beam(user)
delay = world.time + 10
else if (E.charge < 10)
playsound(get_turf(user),'sound/machines/buzz-sigh.ogg', 50, 1)

View File

@@ -1,3 +1,4 @@
<<<<<<< HEAD
/obj/item/ammo_casing/proc/fire_casing(atom/target, mob/living/user, params, distro, quiet, zone_override, spread)
distro += variance
for (var/i = max(1, pellets), i > 0, i--)
@@ -60,3 +61,67 @@
var/dx = abs(target.x - current.x)
var/dy = abs(target.y - current.y)
return locate(target.x + round(gaussian(0, distro) * (dy+2)/8, 1), target.y + round(gaussian(0, distro) * (dx+2)/8, 1), target.z)
=======
/obj/item/ammo_casing/proc/fire_casing(atom/target, mob/living/user, params, distro, quiet, zone_override, spread)
distro += variance
for (var/i = max(1, pellets), i > 0, i--)
var/targloc = get_turf(target)
ready_proj(target, user, quiet, zone_override)
if(distro) //We have to spread a pixel-precision bullet. throw_proj was called before so angles should exist by now...
if(randomspread)
spread = round((rand() - 0.5) * distro)
else //Smart spread
spread = round((i / pellets - 0.5) * distro)
if(!throw_proj(target, targloc, user, params, spread))
return 0
if(i > 1)
newshot()
if(click_cooldown_override)
user.changeNext_move(click_cooldown_override)
else
user.changeNext_move(CLICK_CD_RANGE)
user.newtonian_move(get_dir(target, user))
update_icon()
return 1
/obj/item/ammo_casing/proc/ready_proj(atom/target, mob/living/user, quiet, zone_override)
if (!BB)
return
BB.original = target
BB.firer = user
if (zone_override)
BB.def_zone = zone_override
else
BB.def_zone = user.zone_selected
BB.suppressed = quiet
if(reagents && BB.reagents)
reagents.trans_to(BB, reagents.total_volume) //For chemical darts/bullets
qdel(reagents)
/obj/item/ammo_casing/proc/throw_proj(atom/target, turf/targloc, mob/living/user, params, spread)
var/turf/curloc = get_turf(user)
if (!istype(targloc) || !istype(curloc) || !BB)
return 0
var/firing_dir
if(BB.firer)
firing_dir = BB.firer.dir
if(!BB.suppressed && firing_effect_type)
new firing_effect_type(get_turf(src), firing_dir)
var/direct_target
if(targloc == curloc)
if(target) //if the target is right on our location we'll skip the travelling code in the proj's fire()
direct_target = target
if(!direct_target)
BB.preparePixelProjectile(target, user, params, spread)
BB.fire(null, direct_target)
BB = null
return 1
/obj/item/ammo_casing/proc/spread(turf/target, turf/current, distro)
var/dx = abs(target.x - current.x)
var/dy = abs(target.y - current.y)
return locate(target.x + round(gaussian(0, distro) * (dy+2)/8, 1), target.y + round(gaussian(0, distro) * (dx+2)/8, 1), target.z)
>>>>>>> 64851d8... Moves projectile processing to a subsystem (#30599)

View File

@@ -38,9 +38,14 @@
var/aiming_time = 12
var/aiming_time_fire_threshold = 5
var/aiming_time_left = 12
<<<<<<< HEAD
var/aiming_time_increase_user_movement = 6
=======
var/aiming_time_increase_user_movement = 3
>>>>>>> 64851d8... Moves projectile processing to a subsystem (#30599)
var/scoped_slow = 1
var/aiming_time_increase_angle_multiplier = 0.3
var/last_process = 0
var/lastangle = 0
var/aiming_lastangle = 0
@@ -204,7 +209,7 @@
/obj/item/gun/energy/beam_rifle/Initialize()
. = ..()
START_PROCESSING(SSfastprocess, src)
START_PROCESSING(SSprojectiles, src)
zoom_speed_action = new(src)
zoom_lock_action = new(src)
@@ -240,7 +245,7 @@
if(!istype(curloc))
return
targloc = get_turf_in_angle(lastangle, curloc, 10)
P.preparePixelProjectile(targloc, targloc, current_user, current_user.client.mouseParams, 0)
P.preparePixelProjectile(targloc, current_user, current_user.client.mouseParams, 0)
P.fire(lastangle)
/obj/item/gun/energy/beam_rifle/process()
@@ -248,9 +253,15 @@
return
check_user()
handle_zooming()
<<<<<<< HEAD
aiming_time_left = min(0, aiming_time_left - (world.time - lastprocess))
aiming_beam(TRUE)
lastprocess = world.time
=======
aiming_time_left = max(0, aiming_time_left - (world.time - last_process))
aiming_beam(TRUE)
last_process = world.time
>>>>>>> 64851d8... Moves projectile processing to a subsystem (#30599)
/obj/item/gun/energy/beam_rifle/proc/check_user(automatic_cleanup = TRUE)
if(!istype(current_user) || !isturf(current_user.loc) || !(src in current_user.held_items) || current_user.incapacitated()) //Doesn't work if you're not holding it!
@@ -442,7 +453,7 @@
firing_dir = BB.firer.dir
if(!BB.suppressed && firing_effect_type)
new firing_effect_type(get_turf(src), firing_dir)
BB.preparePixelProjectile(target, targloc, user, params, spread)
BB.preparePixelProjectile(target, user, params, spread)
BB.fire(gun? gun.lastangle : null, null)
BB = null
return TRUE
@@ -638,10 +649,6 @@
while(loc)
if(++safety > (range * 3)) //If it's looping for way, way too long...
return //Kill!
if((!( current ) || loc == current))
current = locate(Clamp(x+xo,1,world.maxx),Clamp(y+yo,1,world.maxy),z)
if(!Angle)
Angle=round(Get_Angle(src,current))
if(spread)
Angle += (rand() - 0.5) * spread
var/matrix/M = new

View File

@@ -1,3 +1,6 @@
#define MOVES_HITSCAN -1 //Not actually hitscan but close as we get.
/obj/item/projectile
name = "projectile"
icon = 'icons/obj/projectiles.dmi'
@@ -16,18 +19,31 @@
var/suppressed = FALSE //Attack message
var/yo = null
var/xo = null
var/current = 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/paused = FALSE //for suspending the projectile midair
var/p_x = 16
var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center
//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/old_pixel_x = 0
var/old_pixel_y = 0
var/pixel_x_increment = 0
var/pixel_y_increment = 0
var/pixel_x_offset = 0
var/pixel_y_offset = 0
var/new_x = 0
var/new_y = 0
var/speed = 0.8 //Amount of deciseconds it takes for projectile to travel
var/Angle = 0
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
var/legacy = 0 //legacy projectile system
animate_movement = 0 //Use SLIDE_STEPS in conjunction with legacy
var/ricochets = 0
var/ricochets_max = 2
@@ -146,7 +162,7 @@
return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume
/obj/item/projectile/Collide(atom/A)
if(check_ricochet() && check_ricochet_flag(A) && ricochets < ricochets_max)
if(check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max)
ricochets++
if(A.handle_ricochet(src))
ignore_source_check = TRUE
@@ -218,9 +234,33 @@
return FALSE
/obj/item/projectile/Process_Spacemove(var/movement_dir = 0)
return 1 //Bullets don't drift in space
return TRUE //Bullets don't drift in space
/obj/item/projectile/proc/fire(setAngle, atom/direct_target)
/obj/item/projectile/process()
last_process = world.time
if(!loc || !fired)
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) : 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(required_moves)
/obj/item/projectile/proc/fire(angle, atom/direct_target)
//If no angle needs to resolve it from xo/yo!
if(!log_override && firer && original)
add_logs(firer, original, "fired at", src, " [get_area(src)]")
if(direct_target)
@@ -228,84 +268,70 @@
direct_target.bullet_act(src, def_zone)
qdel(src)
return
if(isnum(setAngle))
Angle = setAngle
var/old_pixel_x = pixel_x
var/old_pixel_y = pixel_y
if(!legacy) //new projectiles
set waitfor = 0
var/next_run = world.time
while(loc)
if(paused)
next_run = world.time
stoplag(1)
continue
if(isnum(angle))
setAngle(angle)
if(spread)
setAngle(Angle + ((rand() - 0.5) * spread))
if(isnull(Angle)) //Try to resolve through offsets if there's no angle set.
var/turf/starting = get_turf(src)
var/turf/target = locate(Clamp(starting + xo, 1, world.maxx), Clamp(starting + yo, 1, world.maxy), starting.z)
setAngle(Get_Angle(src, target))
if(!nondirectional_sprite)
var/matrix/M = new
M.Turn(Angle)
transform = M
old_pixel_x = pixel_x
old_pixel_y = pixel_y
last_projectile_move = world.time
fired = TRUE
if(!isprocessing)
START_PROCESSING(SSprojectiles, src)
if((!( current ) || loc == current))
current = locate(Clamp(x+xo,1,world.maxx),Clamp(y+yo,1,world.maxy),z)
/obj/item/projectile/proc/setAngle(new_angle) //wrapper for overrides.
Angle = new_angle
return TRUE
if(!Angle)
Angle=round(Get_Angle(src,current))
if(spread)
Angle += (rand() - 0.5) * spread
if(!nondirectional_sprite)
var/matrix/M = new
M.Turn(Angle)
transform = M
/obj/item/projectile/proc/pixel_move(moves)
if(!nondirectional_sprite)
var/matrix/M = new
M.Turn(Angle)
transform = M
var/Pixel_x=round((sin(Angle)+16*sin(Angle)*2), 1) //round() is a floor operation when only one argument is supplied, we don't want that here
var/Pixel_y=round((cos(Angle)+16*cos(Angle)*2), 1)
var/pixel_x_offset = old_pixel_x + Pixel_x
var/pixel_y_offset = old_pixel_y + Pixel_y
var/new_x = x
var/new_y = y
pixel_x_increment=round((sin(Angle)+16*sin(Angle)*2), 1) //round() is a floor operation when only one argument is supplied, we don't want that here
pixel_y_increment=round((cos(Angle)+16*cos(Angle)*2), 1)
pixel_x_offset = old_pixel_x + pixel_x_increment
pixel_y_offset = old_pixel_y + pixel_y_increment
new_x = x
new_y = y
while(pixel_x_offset > 16)
pixel_x_offset -= 32
old_pixel_x -= 32
new_x++// x++
while(pixel_x_offset < -16)
pixel_x_offset += 32
old_pixel_x += 32
new_x--
while(pixel_y_offset > 16)
pixel_y_offset -= 32
old_pixel_y -= 32
new_y++
while(pixel_y_offset < -16)
pixel_y_offset += 32
old_pixel_y += 32
new_y--
while(pixel_x_offset > 16)
pixel_x_offset -= 32
old_pixel_x -= 32
new_x++// x++
while(pixel_x_offset < -16)
pixel_x_offset += 32
old_pixel_x += 32
new_x--
while(pixel_y_offset > 16)
pixel_y_offset -= 32
old_pixel_y -= 32
new_y++
while(pixel_y_offset < -16)
pixel_y_offset += 32
old_pixel_y += 32
new_y--
pixel_x = old_pixel_x
pixel_y = old_pixel_y
step_towards(src, locate(new_x, new_y, z))
next_run += max(world.tick_lag, speed)
var/delay = next_run - world.time
if(delay <= world.tick_lag*2)
pixel_x = pixel_x_offset
pixel_y = pixel_y_offset
else
animate(src, pixel_x = pixel_x_offset, pixel_y = pixel_y_offset, time = max(1, (delay <= 3 ? delay - 1 : delay)), flags = ANIMATION_END_NOW)
old_pixel_x = pixel_x_offset
old_pixel_y = pixel_y_offset
if(can_hit_target(original, permutated))
Collide(original)
Range()
if (delay > 0)
sleep(delay)
else //old projectile system
set waitfor = 0
while(loc)
if(!paused)
if((!( current ) || loc == current))
current = locate(Clamp(x+xo,1,world.maxx),Clamp(y+yo,1,world.maxy),z)
step_towards(src, current)
if(can_hit_target(original, permutated))
Collide(original)
Range()
sleep(CONFIG_GET(number/run_delay) * 0.9)
step_towards(src, locate(new_x, new_y, z))
pixel_x = old_pixel_x
pixel_y = old_pixel_y
//var/animation_time = ((SSprojectiles.flags & SS_TICKER? (SSprojectiles.wait * world.tick_lag) : SSprojectiles.wait) / moves)
animate(src, pixel_x = pixel_x_offset, pixel_y = pixel_y_offset, time = 1, flags = ANIMATION_END_NOW)
old_pixel_x = pixel_x_offset
old_pixel_y = pixel_y_offset
if(can_hit_target(original, permutated))
Collide(original)
Range()
last_projectile_move = world.time
//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)
@@ -315,21 +341,26 @@
return TRUE
return FALSE
/obj/item/projectile/proc/preparePixelProjectile(atom/target, var/turf/targloc, mob/living/user, params, spread)
var/turf/curloc = get_turf(user)
forceMove(get_turf(user))
starting = get_turf(user)
current = curloc
/obj/item/projectile/proc/preparePixelProjectile(atom/target, atom/source, params, spread = 0)
var/turf/curloc = get_turf(source)
var/turf/targloc = get_turf(target)
forceMove(get_turf(source))
starting = get_turf(source)
original = target
yo = targloc.y - curloc.y
xo = targloc.x - curloc.x
var/list/calculated = calculate_projectile_angle_and_pixel_offsets(user, params)
Angle = calculated[1]
p_x = calculated[2]
p_y = calculated[3]
if(isliving(source) && params)
var/list/calculated = calculate_projectile_angle_and_pixel_offsets(source, params)
p_x = calculated[2]
p_y = calculated[3]
if(spread)
src.Angle += spread
if(spread)
setAngle(calculated[1] + spread)
else
setAngle(calculated[1])
else
setAngle(Get_Angle(src, targloc))
/proc/calculate_projectile_angle_and_pixel_offsets(mob/user, params)
var/list/mouse_control = params2list(params)
@@ -366,6 +397,7 @@
Collide(AM)
/obj/item/projectile/Destroy()
STOP_PROCESSING(SSprojectiles, src)
return ..()
/obj/item/projectile/experience_pressure_difference()

View File

@@ -95,8 +95,6 @@
name = "emitter beam"
icon_state = "emitter"
damage = 30
legacy = 1
animate_movement = SLIDE_STEPS
impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser
light_color = LIGHT_COLOR_GREEN

View File

@@ -8,12 +8,6 @@
hitsound_wall = "ricochet"
impact_effect_type = /obj/effect/temp_visual/impact_effect
/obj/item/projectile/bullet/pellet/shotgun_buckshot/Range()
..()
damage -= 0.75
if(damage < 0)
qdel(src)
/obj/item/projectile/bullet/incendiary
damage = 20
var/fire_stacks = 4
@@ -39,7 +33,7 @@
damage = 60
// 7.62 (Nagant Rifle)
/obj/item/projectile/bullet/a762
name = "7.62 bullet"
damage = 60
@@ -156,7 +150,7 @@
/obj/item/projectile/bullet/p50
name =".50 bullet"
speed = 0 //360 alwaysscope.
speed = 0.4
damage = 70
knockdown = 100
dismemberment = 50
@@ -272,15 +266,30 @@
explosion(target, -1, 0, 1)
return TRUE
/obj/item/projectile/bullet/pellet
var/tile_dropoff = 0.75
var/tile_dropoff_s = 1.25
/obj/item/projectile/bullet/pellet/shotgun_buckshot
name = "buckshot pellet"
damage = 12.5
/obj/item/projectile/bullet/pellet/shotgun_rubbershot
name = "rubbershot pellet"
damage = 3
stamina = 25
/obj/item/projectile/bullet/pellet/Range()
..()
if(damage > 0)
damage -= tile_dropoff
if(stamina > 0)
stamina -= tile_dropoff_s
if(damage < 0 && stamina < 0)
qdel(src)
/obj/item/projectile/bullet/pellet/shotgun_improvised
tile_dropoff = 0.55 //Come on it does 6 damage don't be like that.
damage = 6
/obj/item/projectile/bullet/pellet/shotgun_improvised/Initialize()

View File

@@ -337,10 +337,7 @@
visible_message("<span class='danger'>[src] dangerously overheats, launching a flaming fuel orb!</span>")
investigate_log("Experimentor has launched a <font color='red'>fireball</font> at [M]!", INVESTIGATE_EXPERIMENTOR)
var/obj/item/projectile/magic/aoe/fireball/FB = new /obj/item/projectile/magic/aoe/fireball(start)
FB.original = MT
FB.current = start
FB.yo = MT.y - start.y
FB.xo = MT.x - start.x
FB.preparePixelProjectile(MT, start)
FB.fire()
else if(prob(EFFECT_PROB_LOW-badThingCoeff))
visible_message("<span class='danger'>[src] malfunctions, melting [exp_on] and releasing a burst of flame!</span>")

View File

@@ -65,9 +65,8 @@
/obj/effect/proc_holder/spell/aimed/proc/fire_projectile(mob/living/user, atom/target)
current_amount--
var/obj/item/projectile/P = new projectile_type(user.loc)
P.current = get_turf(user)
P.firer = user
P.preparePixelProjectile(target, get_turf(target), user)
P.preparePixelProjectile(target, user)
for(var/V in projectile_var_overrides)
if(P.vars[V])
P.vars[V] = projectile_var_overrides[V]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 111 KiB

View File

@@ -263,6 +263,7 @@
#include "code\controllers\subsystem\processing\obj.dm"
#include "code\controllers\subsystem\processing\overlays.dm"
#include "code\controllers\subsystem\processing\processing.dm"
#include "code\controllers\subsystem\processing\projectiles.dm"
#include "code\datums\action.dm"
#include "code\datums\ai_laws.dm"
#include "code\datums\beam.dm"