From a67f8798a3f678a4ecc9a6ee8995eb4a7f275f5c Mon Sep 17 00:00:00 2001
From: ShiftyRail <31417754+ShiftyRail@users.noreply.github.com>
Date: Wed, 25 Mar 2020 20:20:29 +0100
Subject: [PATCH] Tracker for projectiles (#26026)
---
code/datums/datumvars.dm | 43 +++++++++++++++++++++++
code/game/atoms_movable.dm | 42 +++++++++++++++++++++++
code/modules/admin/admin.dm | 14 ++++++++
code/modules/admin/admin_verbs.dm | 11 +++++-
code/modules/admin/topic.dm | 24 +++++++++++++
code/modules/events/immovablerod.dm | 36 ++++++++++++++++++++
code/modules/projectiles/projectile.dm | 47 ++++++++++++++++++++++++++
7 files changed, 216 insertions(+), 1 deletion(-)
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index d86c38401b3..b3f5a8021c4 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -133,6 +133,7 @@
if(istype(D,/atom/movable))
body += ""
+ body += ""
if(istype(D,/atom))
body += ""
@@ -834,6 +835,48 @@ function loadPage(list) {
A.alpha = 0
animate(A, alpha = 255, time = stealthy_level)
+ else if(href_list["throw_a_fucking_rod_at_it"])
+ if(!check_rights(R_FUN))
+ to_chat(usr, "You do not have sufficient permissions to do this.")
+ return
+
+ var/atom/movable/A = locate(href_list["throw_a_fucking_rod_at_it"])
+ if(!istype(A))
+ to_chat(usr, "This can only be done to instances of movable atoms.")
+ return
+
+ var/rod_size = input("What type of rod do you want to throw?","Throwing an Immovable Rod",null) as null|anything in list("Normal", "Pillar", "Monolith")
+ var/rod_type
+
+ if (!rod_size)
+ return
+
+ switch (rod_size)
+ if ("Normal")
+ rod_type = /obj/item/projectile/immovablerod
+ if ("Pillar")
+ rod_type = /obj/item/projectile/immovablerod/big
+ if ("Monolith")
+ rod_type = /obj/item/projectile/immovablerod/hyper
+
+ if(alert("Are you sure you want to do this?","Confirm","Yes","No") != "Yes")
+ return
+
+ var/obj/item/projectile/immovablerod/rod = new rod_type(random_start_turf(A.z))
+ rod.tracking = TRUE
+ rod.throw_at(A)
+
+ var/log_data = "[A]"
+ if (ismob(A))
+ var/mob/M = A
+ if (M.client)
+ log_data += " ([M.client.ckey])"
+
+ log_admin("[key_name(usr)] threw a rod at [log_data].")
+ message_admins("[key_name(usr)] threw a rod at [log_data].")
+ to_chat(usr, "If you changed your mind, you can always stop the tracking by using the verb 'VIEW-ALL-RODS' and click the 'Untrack' link.")
+ return
+
else if(href_list["teleport_to"])
if(!check_rights(0))
return
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 038e8109d06..8984057f892 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -57,6 +57,8 @@
var/last_explosion_push = 0
+ var/list/datum/tracker/trackers = list()
+
/atom/movable/New()
. = ..()
if((flags & HEAR) && !ismob(src))
@@ -1060,3 +1062,43 @@
forceMove(F)
return TRUE
return FALSE
+
+// -- trackers
+
+/atom/movable/proc/add_tracker(var/datum/tracker/T)
+ on_moved.Add(T, "recieve_position")
+
+/datum/tracker
+ var/name = "Tracker"
+ var/active = TRUE
+ var/changed = FALSE
+
+ var/turf/target
+
+ var/tick_refresh = 5 // The number of moved events before we update the position.
+ var/current_tick = 1
+
+ var/lost_position_probability = 0 // Probability of losing the target
+ var/lost_position_distance = 0 // Distance at which the tracker loses the target
+
+/datum/tracker/proc/recieve_position(var/list/loc)
+
+ ASSERT(loc)
+
+ if (!active)
+ return
+ if (current_tick < tick_refresh)
+ current_tick++
+ return
+
+ if (prob(lost_position_probability))
+ active = FALSE
+ return
+
+ var/target_loc = loc["loc"]
+ if (target != target_loc)
+ changed = TRUE
+
+ target = get_turf(target_loc)
+
+ current_tick = 1
\ No newline at end of file
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 134c38dec89..fd79cbe0429 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -1618,3 +1618,17 @@ proc/formatPlayerPanel(var/mob/U,var/text="PP")
dat += "Max [T.max_per_turf] per turf. Lasts up to [T.max_age] rounds.
"
usr << browse(dat, "window=persistencepanel;size=350x600")
+
+/datum/admins/proc/ViewAllRods()
+ if(!check_rights(0))
+ return
+
+ var/dat = "View all active rods
"
+
+ for (var/obj/item/projectile/immovablerod/rod in all_rods)
+ dat += "[rod] in z = [rod.z] (\[VV\])"
+ if (rod.tracking)
+ dat += "- (UNTRACK)"
+ dat += "
"
+
+ usr << browse(dat, "window=rodswindow;size=350x300")
\ No newline at end of file
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 6021036ab35..e3cfd047251 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -128,7 +128,8 @@ var/list/admin_verbs_fun = list(
/client/proc/makepAI,
/client/proc/set_blob_looks,
/client/proc/set_teleport_pref,
- /client/proc/deadchat_singularity
+ /client/proc/deadchat_singularity,
+ /client/proc/view_all_rods,
)
var/list/admin_verbs_spawn = list(
/datum/admins/proc/spawn_atom, // Allows us to spawn instances
@@ -1265,3 +1266,11 @@ var/list/admin_verbs_mod = list(
holder.PersistencePanel()
feedback_add_details("admin_verb","PEP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
+
+/client/proc/view_all_rods()
+ set name = "VIEW-ALL-RODS"
+ set category = "Fun"
+ if(holder)
+ holder.ViewAllRods()
+ feedback_add_details("admin_verb","V-ROD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ return
\ No newline at end of file
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index fb04e4d30be..993fc7abd90 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -5375,6 +5375,30 @@
PersistencePanel() //refresh!
+ // --- Rod tracking
+
+ else if (href_list["rod_to_untrack"])
+ if(!check_rights(R_FUN))
+ return
+ var/obj/item/projectile/P = locate(href_list["rod_to_untrack"])
+
+ if (!P)
+ return
+
+ P.tracking = FALSE
+ P.tracker_datum = null
+ qdel(P.tracker_datum)
+
+ var/log_data = "[P.original]"
+ if (ismob(P.original))
+ var/mob/M = P.original
+ if (M.client)
+ log_data += " (M.client.ckey)"
+
+ log_admin("[key_name(usr)] stopped a rod thrown at [log_data].")
+ message_admins("[key_name(usr)] stopped a rod thrown at [log_data].")
+
+ ViewAllRods()
// ----- Religion and stuff
else if(href_list["ashpaper"])
diff --git a/code/modules/events/immovablerod.dm b/code/modules/events/immovablerod.dm
index fade845ff5f..d3cba83952e 100644
--- a/code/modules/events/immovablerod.dm
+++ b/code/modules/events/immovablerod.dm
@@ -4,6 +4,8 @@
//As well as hurting all dense mobs
//Recoded as a projectile for better movement/appearance
+var/list/all_rods = list()
+
/datum/event/immovable_rod
announceWhen = 1
@@ -71,6 +73,14 @@
lock_angle = 1
clongSound = 'sound/effects/immovablerod_clong.ogg'
+/obj/item/projectile/immovablerod/New()
+ all_rods += src
+ ..()
+
+/obj/item/projectile/immovablerod/Destroy()
+ all_rods -= src
+ ..()
+
/obj/item/projectile/immovablerod/hyper/New()
..()
var/image/I = image('icons/obj/objects_96x96.dmi',"immrod_bottom")
@@ -188,3 +198,29 @@
for (var/mob/M in range(loc,20))
to_chat(M,"CLANG!")
M.playsound_local(loc, clongSound, 100 - (get_dist(src,M)*5), 1)
+
+/proc/random_start_turf(var/z)
+ var/startx
+ var/starty
+ var/chosen_dir = pick(NORTH, SOUTH, EAST, WEST)
+
+ switch(chosen_dir)
+
+ if(NORTH) //North, along the y = max edge
+ starty = world.maxy - (TRANSITIONEDGE + 2)
+ startx = rand((TRANSITIONEDGE + 2), world.maxx - (TRANSITIONEDGE + 2))
+
+ if(SOUTH) //South, along the y = 0 edge
+ starty = (TRANSITIONEDGE + 2)
+ startx = rand((TRANSITIONEDGE + 2), world.maxx - (TRANSITIONEDGE + 2))
+
+ if(EAST) //East, along the x = max edge
+ starty = rand((TRANSITIONEDGE + 2), world.maxy - (TRANSITIONEDGE + 2))
+ startx = world.maxx - (TRANSITIONEDGE + 2)
+
+ if(WEST) //West, along the x = 0 edge
+ starty = rand((TRANSITIONEDGE + 2), world.maxy - (TRANSITIONEDGE + 2))
+ startx = (TRANSITIONEDGE + 2)
+
+ var/turf/T = locate(startx, starty, z)
+ return T
\ No newline at end of file
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 7084b6e5660..b776b53ac5a 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -71,6 +71,9 @@ var/list/impact_master = list()
var/inaccurate = 0
var/turf/target = null
+ var/datum/tracker/tracker_datum = null
+ var/tracking = FALSE
+
var/dist_x = 0
var/dist_y = 0
var/dx = 0
@@ -115,6 +118,10 @@ var/list/impact_master = list()
X.apply_effects(stun, weaken, paralyze, irradiate, stutter, eyeblur, drowsy, agony, blocked)
/obj/item/projectile/proc/on_hit(var/atom/atarget, var/blocked = 0)
+
+ qdel(tracker_datum)
+ tracker_datum = null
+
if(blocked >= 100)
return 0//Full block
if(!isliving(atarget))
@@ -362,6 +369,16 @@ var/list/impact_master = list()
/obj/item/projectile/proc/OnFired(var/proj_target = original) //if assigned, allows for code when the projectile gets fired
target = get_turf(proj_target)
+
+ if (tracking)
+ if (istype(proj_target, /atom/movable))
+ var/atom/movable/the_target = proj_target
+ var/datum/tracker/T = new
+ T.name = "[src] tracker on [proj_target]"
+ T.target = target
+ src.tracker_datum = T
+ the_target.add_tracker(T)
+
dist_x = abs(target.x - starting.x)
dist_y = abs(target.y - starting.y)
@@ -419,6 +436,36 @@ var/list/impact_master = list()
bumped = 0
+ if (tracker_datum && tracker_datum.changed)
+ tracker_datum.changed = FALSE
+ var/dist = get_dist(tracker_datum.target, src)
+ if (tracker_datum.lost_position_distance && (dist > tracker_datum.lost_position_distance))
+ tracker_datum.active = FALSE
+ else
+ target = tracker_datum.target
+ var/turf/current = get_turf(src)
+
+ // recalculate trajectory based on new tracker data
+ if (target.x > current.x)
+ dx = EAST
+ else
+ dx = WEST
+
+ if (target.y > current.y)
+ dy = NORTH
+ else
+ dy = SOUTH
+
+ dist_x = abs(target.x - current.x)
+ dist_y = abs(target.y - current.y)
+
+ if(dist_x > dist_y)
+ error = dist_x/2 - dist_y
+ else
+ error = dist_y/2 - dist_x
+ if(rotate)
+ target_angle = round(Get_Angle(current,target))
+
sleep(projectile_speed)