diff --git a/code/__HELPERS/maths.dm b/code/__HELPERS/maths.dm index b42364b59d..a05e06a05a 100644 --- a/code/__HELPERS/maths.dm +++ b/code/__HELPERS/maths.dm @@ -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) diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 99d12568bc..4c8d060708 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -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 diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm index f0ba15446c..6df0fea1bf 100644 --- a/code/_onclick/other_mobs.dm +++ b/code/_onclick/other_mobs.dm @@ -1,3 +1,4 @@ +<<<<<<< HEAD /* Humans: Adds an exception for gloves, to allow special glove types like the ninja ones. @@ -100,6 +101,110 @@ "[name] bites [ML]!") 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, "You look at your arm and sigh.") + 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("[name] bites [ML]!", \ + "[name] bites [ML]!") + 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) diff --git a/code/controllers/subsystem/processing/projectiles.dm b/code/controllers/subsystem/processing/projectiles.dm new file mode 100644 index 0000000000..ebf217c79a --- /dev/null +++ b/code/controllers/subsystem/processing/projectiles.dm @@ -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 diff --git a/code/datums/mutations.dm b/code/datums/mutations.dm index dea7a7308a..c19dc2c180 100644 --- a/code/datums/mutations.dm +++ b/code/datums/mutations.dm @@ -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() diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index a2d799752c..2ea1469481 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -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 diff --git a/code/game/gamemodes/clock_cult/clock_helpers/slab_abilities.dm b/code/game/gamemodes/clock_cult/clock_helpers/slab_abilities.dm index 3c067e0c17..4ca8f7f35b 100644 --- a/code/game/gamemodes/clock_cult/clock_helpers/slab_abilities.dm +++ b/code/game/gamemodes/clock_cult/clock_helpers/slab_abilities.dm @@ -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() diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm index 8f8202fe19..d879324228 100644 --- a/code/game/machinery/porta_turret/portable_turret.dm +++ b/code/game/machinery/porta_turret/portable_turret.dm @@ -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 diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm index 1e9616ae12..fea6873b90 100644 --- a/code/game/mecha/equipment/weapons/weapons.dm +++ b/code/game/mecha/equipment/weapons/weapons.dm @@ -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) diff --git a/code/game/objects/structures/manned_turret.dm b/code/game/objects/structures/manned_turret.dm index 52d9e38f6b..2deb2d2fde 100644 --- a/code/game/objects/structures/manned_turret.dm +++ b/code/game/objects/structures/manned_turret.dm @@ -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 diff --git a/code/game/objects/structures/reflector.dm b/code/game/objects/structures/reflector.dm index 5db90a60da..1f25c0dbbb 100644 --- a/code/game/objects/structures/reflector.dm +++ b/code/game/objects/structures/reflector.dm @@ -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, "Alt-click to adjust its direction.") +/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("[src] is hit by the [P]!") - 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.", \ - "You start to weld \the [src] to the floor...", \ - "You hear welding.") - if (do_after(user,20*W.toolspeed, target = src)) - if(!src || !WT.isOn()) - return - anchored = TRUE - to_chat(user, "You weld \the [src] to the floor.") - 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.", \ - "You start to cut \the [src] free from the floor...", \ - "You hear welding.") - if (do_after(user,20*W.toolspeed, target = src)) - if(!src || !WT.isOn()) - return - anchored = 0 - to_chat(user, "You cut \the [src] free from the floor.") + 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.", "You start to weld [src] to the floor...", "You hear welding.") + if (do_after(user,20*W.toolspeed, target = src)) + if(!WT.isOn()) + return + anchored = TRUE + to_chat(user, "You weld [src] to the floor.") + 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.", "You start to cut [src] free from the floor...", "You hear welding.") + if (do_after(user,20*W.toolspeed, target = src)) + if(!WT.isOn()) + return + anchored = FALSE + to_chat(user, "You cut [src] free from the floor.") //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, "You need five sheets of glass to create a reflector!") 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, "You need ten sheets of reinforced glass to create a double reflector!") 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, "It is fastened to the floor!") + 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, "You can't do that right now!") return - if (src.anchored) - to_chat(usr, "It is fastened to the floor!") - 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, "You can't do that right now!") 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 ..() diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 75f8aa7184..e6897d5e25 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -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) diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm index 20e292eaa2..5e65ca675b 100644 --- a/code/modules/mining/equipment/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher.dm @@ -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) diff --git a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm index 1390028282..1323112a53 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm @@ -214,8 +214,7 @@ Doesn't work on other aliens/AI.*/ user.visible_message("[user] spits neurotoxin!", "You spit neurotoxin.") 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) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 7be010b3ba..07bcb484a0 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -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 diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm index d04bd867ff..97b88d7715 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -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 diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm index 3cd66724e9..ddf4c93559 100644 --- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm +++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm @@ -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) diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm index a4b94340ee..5f216b7903 100644 --- a/code/modules/mob/living/simple_animal/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs.dm @@ -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 diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index d4c9632a03..6f3b526a03 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -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 diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm b/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm index 3e28a789ad..5b634f8ae1 100644 --- a/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm +++ b/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm @@ -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) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index 8b97fe5354..380248877e 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -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) diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index 45b6801355..fc06adfe78 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -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) diff --git a/code/modules/projectiles/firing.dm b/code/modules/projectiles/firing.dm index cce5c7e7c1..685b14f277 100644 --- a/code/modules/projectiles/firing.dm +++ b/code/modules/projectiles/firing.dm @@ -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) diff --git a/code/modules/projectiles/guns/beam_rifle.dm b/code/modules/projectiles/guns/beam_rifle.dm index 93e8ab6799..f715870f88 100644 --- a/code/modules/projectiles/guns/beam_rifle.dm +++ b/code/modules/projectiles/guns/beam_rifle.dm @@ -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 diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index ce3d9607bc..f7357207da 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -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() diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index d1d7b50cf5..2fa90152d8 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -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 diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index 179ce9ab6c..edb344fb12 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -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() diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm index 08b5872686..4ba8925df6 100644 --- a/code/modules/research/experimentor.dm +++ b/code/modules/research/experimentor.dm @@ -337,10 +337,7 @@ visible_message("[src] dangerously overheats, launching a flaming fuel orb!") investigate_log("Experimentor has launched a fireball 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("[src] malfunctions, melting [exp_on] and releasing a burst of flame!") diff --git a/code/modules/spells/spell_types/aimed.dm b/code/modules/spells/spell_types/aimed.dm index 94597de40f..997c83249a 100644 --- a/code/modules/spells/spell_types/aimed.dm +++ b/code/modules/spells/spell_types/aimed.dm @@ -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] diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi index 74f9df85a7..bd5c8ebcfe 100644 Binary files a/icons/obj/projectiles.dmi and b/icons/obj/projectiles.dmi differ diff --git a/tgstation.dme b/tgstation.dme index c2ee860831..1f02b5312f 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -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"