diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 04ca4db3688..ab07f2e6553 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -1242,9 +1242,15 @@ proc/pick_closest_path(value, list/matches = get_fancy_list_of_atom_types())
#define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE)
#define QDEL_NULL(item) qdel(item); item = null
#define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); }
+#define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/______qdel_list_wrapper, L), time, TIMER_STOPPABLE)
#define QDEL_LIST_ASSOC(L) if(L) { for(var/I in L) { qdel(L[I]); qdel(I); } L.Cut(); }
#define QDEL_LIST_ASSOC_VAL(L) if(L) { for(var/I in L) qel(L[I]); L.Cut(); }
+/proc/______qdel_list_wrapper(list/L) //the underscores are to encourage people not to use this directly.
+ QDEL_LIST(L)
+
+
+
/proc/random_nukecode()
var/val = rand(0, 99999)
var/str = "[val]"
diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm
index 784b5e0ee1b..2580e335e1c 100644
--- a/code/_onclick/observer.dm
+++ b/code/_onclick/observer.dm
@@ -67,8 +67,8 @@
user.forceMove(get_turf(com.locked))
/obj/effect/portal/attack_ghost(mob/user)
- if(target)
- user.forceMove(get_turf(target))
+ if(linked)
+ user.forceMove(get_turf(linked))
/obj/machinery/gateway/centerstation/attack_ghost(mob/user)
if(awaygate)
diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm
index 07ed28b575b..d6f679c3314 100644
--- a/code/datums/helper_datums/teleport.dm
+++ b/code/datums/helper_datums/teleport.dm
@@ -15,7 +15,6 @@
var/soundout //soundfile to play after teleportation
var/force_teleport = 1 //if false, teleport will use Move() proc (dense objects will prevent teleportation)
-
/datum/teleport/proc/start(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null)
if(!initTeleport(arglist(args)))
return 0
@@ -100,21 +99,7 @@
var/turf/destturf
var/turf/curturf = get_turf(teleatom)
- if(precision)
- var/list/posturfs = list()
- var/center = get_turf(destination)
- if(!center)
- center = destination
- for(var/turf/T in range(precision,center))
- if(T.is_transition_turf())
- continue // Avoid picking these.
- var/area/A = T.loc
- if(!A.noteleport)
- posturfs.Add(T)
-
- destturf = safepick(posturfs)
- else
- destturf = get_turf(destination)
+ destturf = get_teleport_turf(get_turf(destination), precision)
if(!destturf || !curturf || destturf.is_transition_turf())
return 0
@@ -124,13 +109,16 @@
return 0
playSpecials(curturf,effectin,soundin)
-
if(force_teleport)
teleatom.forceMove(destturf)
+ if(ismegafauna(teleatom))
+ message_admins("[teleatom] [ADMIN_FLW(teleatom)] has teleported from [ADMIN_COORDJMP(curturf)] to [ADMIN_COORDJMP(destturf)].")
playSpecials(destturf,effectout,soundout)
else
if(teleatom.Move(destturf))
playSpecials(destturf,effectout,soundout)
+ if(ismegafauna(teleatom))
+ message_admins("[teleatom] [ADMIN_FLW(teleatom)] has teleported from [ADMIN_COORDJMP(curturf)] to [ADMIN_COORDJMP(destturf)].")
return 1
/datum/teleport/proc/teleport()
@@ -225,3 +213,18 @@
// DING! You have passed the gauntlet, and are "probably" safe.
return F
+
+/proc/get_teleport_turfs(turf/center, precision = 0)
+ if(!precision)
+ return list(center)
+ var/list/posturfs = list()
+ for(var/turf/T in range(precision,center))
+ if(T.is_transition_turf())
+ continue // Avoid picking these.
+ var/area/A = T.loc
+ if(!A.noteleport)
+ posturfs.Add(T)
+ return posturfs
+
+/proc/get_teleport_turf(turf/center, precision = 0)
+ return safepick(get_teleport_turfs(center, precision))
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 6a61416366e..e8aa62ed85c 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -213,7 +213,8 @@
// Previously known as HasEntered()
// This is automatically called when something enters your square
-/atom/movable/Crossed(atom/movable/AM)
+//oldloc = old location on atom, inserted when forceMove is called and ONLY when forceMove is called!
+/atom/movable/Crossed(atom/movable/AM, oldloc)
return
/atom/movable/Bump(atom/A, yes) //the "yes" arg is to differentiate our Bump proc from byond's, without it every Bump() call would become a double Bump().
@@ -249,7 +250,7 @@
for(var/atom/movable/AM in destination)
if(AM == src)
continue
- AM.Crossed(src)
+ AM.Crossed(src, oldloc)
Moved(oldloc, 0)
return 1
diff --git a/code/game/mecha/equipment/tools/other_tools.dm b/code/game/mecha/equipment/tools/other_tools.dm
index 6074fc932e8..089cfdf6254 100644
--- a/code/game/mecha/equipment/tools/other_tools.dm
+++ b/code/game/mecha/equipment/tools/other_tools.dm
@@ -57,18 +57,12 @@
var/turf/target_turf = pick(L)
if(!target_turf)
return
- var/obj/effect/portal/P = new /obj/effect/portal(get_turf(target))
- P.target = target_turf
- P.creator = null
- P.icon = 'icons/obj/objects.dmi'
- P.icon_state = "anom"
- P.name = "wormhole"
- P.mech_sized = TRUE
+ var/list/obj/effect/portal/created = create_portal_pair(get_turf(src), target_turf, src, 300, 1, /obj/effect/portal/anom)
var/turf/T = get_turf(target)
message_admins("[ADMIN_LOOKUPFLW(chassis.occupant)] used a Wormhole Generator in [ADMIN_COORDJMP(T)]",0,1)
log_game("[key_name(chassis.occupant)] used a Wormhole Generator in [COORD(T)]")
src = null
- QDEL_IN(P, rand(150,300))
+ QDEL_LIST_IN(created, rand(150,300))
return 1
diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm
index 9e7dbe7fa9d..74556098373 100644
--- a/code/game/objects/effects/portals.dm
+++ b/code/game/objects/effects/portals.dm
@@ -1,25 +1,42 @@
+/proc/create_portal_pair(turf/source, turf/destination, _creator = null, _lifespan = 300, accuracy = 0, newtype = /obj/effect/portal)
+ if(!istype(source) || !istype(destination))
+ return
+ var/turf/actual_destination = get_teleport_turf(destination, accuracy)
+ var/obj/effect/portal/P1 = new newtype(source, _creator, _lifespan, null, FALSE)
+ var/obj/effect/portal/P2 = new newtype(actual_destination, _creator, _lifespan, P1, TRUE)
+ if(!istype(P1)||!istype(P2))
+ return
+ P1.link_portal(P2)
+ P1.hardlinked = TRUE
+ return list(P1, P2)
+
/obj/effect/portal
name = "portal"
desc = "Looks unstable. Best to test it with the clown."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "portal"
- density = 1
- var/obj/item/target = null
- var/creator = null
- anchored = 1
- var/precision = 1 // how close to the portal you will teleport. 0 = on the portal, 1 = adjacent
+ anchored = TRUE
var/mech_sized = FALSE
+ var/obj/effect/portal/linked
+ var/hardlinked = TRUE //Requires a linked portal at all times. Destroy if there's no linked portal, if there is destroy it when this one is deleted.
+ var/creator
+ var/turf/hard_target //For when a portal needs a hard target and isn't to be linked.
+ var/atmos_link = FALSE //Link source/destination atmos.
+ var/turf/open/atmos_source //Atmos link source
+ var/turf/open/atmos_destination //Atmos link destination
-/obj/effect/portal/Bumped(mob/M as mob|obj)
- teleport(M)
+/obj/effect/portal/anom
+ name = "wormhole"
+ icon = 'icons/obj/objects.dmi'
+ icon_state = "anom"
+ mech_sized = TRUE
-/obj/effect/portal/attack_tk(mob/user)
- return
-
-/obj/effect/portal/attack_hand(mob/user)
- if(Adjacent(user))
- teleport(user)
+/obj/effect/portal/Move(newloc)
+ for(var/T in newloc)
+ if(istype(T, /obj/effect/portal))
+ return FALSE
+ return ..()
/obj/effect/portal/attackby(obj/item/weapon/W, mob/user, params)
if(user && Adjacent(user))
@@ -28,42 +45,113 @@
/obj/effect/portal/make_frozen_visual()
return
-/obj/effect/portal/New(loc, turf/target, creator=null, lifespan=300)
- ..()
+/obj/effect/portal/Crossed(atom/movable/AM, oldloc)
+ if(get_turf(oldloc) == get_turf(linked))
+ return ..()
+ if(!teleport(AM))
+ return ..()
+
+/obj/effect/portal/attack_tk(mob/user)
+ return
+
+/obj/effect/portal/attack_hand(mob/user)
+ if(Adjacent(user))
+ teleport(user)
+
+/obj/effect/portal/Initialize(mapload, _creator, _lifespan = 300, obj/effect/portal/_linked, automatic_link = TRUE, hard_target_override, atmos_link_override)
+ . = ..()
GLOB.portals += src
- src.target = target
- src.creator = creator
+ if(!istype(_linked) && automatic_link)
+ . = INITIALIZE_HINT_QDEL
+ CRASH("Somebody fucked up.")
+ if(_lifespan > 0)
+ QDEL_IN(src, _lifespan)
+ if(!isnull(atmos_link_override))
+ atmos_link = atmos_link_override
+ link_portal(_linked)
+ hardlinked = automatic_link
+ creator = _creator
- var/area/A = get_area(target)
- if(A && A.noteleport) // No point in persisting if the target is unreachable.
- qdel(src)
- return
- if(lifespan > 0)
- QDEL_IN(src, lifespan)
+/obj/effect/portal/proc/link_portal(obj/effect/portal/newlink)
+ linked = newlink
+ if(atmos_link)
+ link_atmos()
-/obj/effect/portal/Destroy()
- GLOB.portals -= src
- if(istype(creator, /obj/item/weapon/hand_tele))
- var/obj/item/weapon/hand_tele/O = creator
- O.active_portals--
- else if(istype(creator, /obj/item/weapon/gun/energy/wormhole_projector))
- var/obj/item/weapon/gun/energy/wormhole_projector/P = creator
- P.portal_destroyed(src)
+/obj/effect/portal/proc/link_atmos()
+ if(atmos_source || atmos_destination)
+ unlink_atmos()
+ if(!isopenturf(get_turf(src)))
+ return FALSE
+ if(linked)
+ if(isopenturf(get_turf(linked)))
+ atmos_source = get_turf(src)
+ atmos_destination = get_turf(linked)
+ else if(hard_target)
+ if(isopenturf(hard_target))
+ atmos_source = get_turf(src)
+ atmos_destination = hard_target
+ else
+ return FALSE
+ if(!istype(atmos_source) || !istype(atmos_destination))
+ return FALSE
+ LAZYINITLIST(atmos_source.atmos_adjacent_turfs)
+ LAZYINITLIST(atmos_destination.atmos_adjacent_turfs)
+ if(atmos_source.atmos_adjacent_turfs[atmos_destination] || atmos_destination.atmos_adjacent_turfs[atmos_source]) //Already linked!
+ return FALSE
+ atmos_source.atmos_adjacent_turfs[atmos_destination] = TRUE
+ atmos_destination.atmos_adjacent_turfs[atmos_source] = TRUE
+ atmos_source.air_update_turf(FALSE)
+ atmos_destination.air_update_turf(FALSE)
+
+/obj/effect/portal/proc/unlink_atmos()
+ if(istype(atmos_source))
+ if(istype(atmos_destination) && !atmos_source.Adjacent(atmos_destination) && !CANATMOSPASS(atmos_destination, atmos_source))
+ LAZYREMOVE(atmos_source.atmos_adjacent_turfs, atmos_destination)
+ atmos_source = null
+ if(istype(atmos_destination))
+ if(istype(atmos_source) && !atmos_destination.Adjacent(atmos_source) && !CANATMOSPASS(atmos_source, atmos_destination))
+ LAZYREMOVE(atmos_destination.atmos_adjacent_turfs, atmos_source)
+ atmos_destination = null
+
+/obj/effect/portal/Destroy() //Calls on_portal_destroy(destroyed portal, location of destroyed portal) on creator if creator has such call.
+ if(creator && hascall(creator, "on_portal_destroy"))
+ call(creator, "on_portal_destroy")(src, src.loc)
creator = null
+ GLOB.portals -= src
+ unlink_atmos()
+ if(hardlinked && !QDELETED(linked))
+ QDEL_NULL(linked)
+ else
+ linked = null
return ..()
-/obj/effect/portal/proc/teleport(atom/movable/M as mob|obj)
- if(istype(M, /obj/effect)) //sparks don't teleport
+/obj/effect/portal/proc/teleport(atom/movable/M)
+ if(!istype(M) || istype(M, /obj/effect) || (istype(M, /obj/mecha) && !mech_sized) || (!isobj(M) && !ismob(M))) //Things that shouldn't teleport.
return
- if(M.anchored)
- if(!(istype(M, /obj/mecha) && mech_sized))
+ var/turf/real_target = get_link_target_turf()
+ if(!istype(real_target))
+ return FALSE
+ if(ismegafauna(M))
+ message_admins("[M] has used a portal at [ADMIN_COORDJMP(src)] made by [usr].")
+ if(do_teleport(M, real_target, 0))
+ if(istype(M, /obj/item/projectile))
+ var/obj/item/projectile/P = M
+ P.ignore_source_check = TRUE
+ return TRUE
+ return FALSE
+
+/obj/effect/portal/proc/get_link_target_turf()
+ var/turf/real_target
+ if(!istype(linked) || QDELETED(linked))
+ if(hardlinked)
+ qdel(src)
+ if(!istype(hard_target) || QDELETED(hard_target))
+ hard_target = null
return
- if (!( target ))
- qdel(src)
- return
- if (istype(M, /atom/movable))
- if(ismegafauna(M))
- message_admins("[M] [ADMIN_FLW(M)] has teleported through [src].")
- do_teleport(M, target, precision) ///You will appear adjacent to the beacon
-
+ else
+ real_target = hard_target
+ linked = null
+ else
+ real_target = get_turf(linked)
+ return real_target
diff --git a/code/game/objects/items/weapons/teleportation.dm b/code/game/objects/items/weapons/teleportation.dm
index 87254406ffa..ecdf443b63d 100644
--- a/code/game/objects/items/weapons/teleportation.dm
+++ b/code/game/objects/items/weapons/teleportation.dm
@@ -1,3 +1,7 @@
+
+#define SOURCE_PORTAL 1
+#define DESTINATION_PORTAL 2
+
/* Teleportation devices.
* Contains:
* Locator
@@ -136,7 +140,19 @@ Frequency:
origin_tech = "magnets=3;bluespace=4"
armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 30, bio = 0, rad = 0, fire = 100, acid = 100)
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
- var/active_portals = 0
+ var/list/active_portal_pairs
+ var/max_portal_pairs = 3
+
+/obj/item/weapon/hand_tele/Initialize()
+ . = ..()
+ active_portal_pairs = list()
+
+/obj/item/weapon/hand_tele/afterattack(atom/target, mob/user, proximity, params)
+ if(is_parent_of_portal(target))
+ qdel(target)
+ to_chat(user, "You dispel [target] remotely with \the [src]!")
+ return ..()
+
/obj/item/weapon/hand_tele/attack_self(mob/user)
var/turf/current_location = get_turf(user)//What turf is the user on?
@@ -169,7 +185,7 @@ Frequency:
var/t1 = input(user, "Please select a teleporter to lock in on.", "Hand Teleporter") as null|anything in L
if (!t1 || user.get_active_held_item() != src || user.incapacitated())
return
- if(active_portals >= 3)
+ if(active_portal_pairs.len >= max_portal_pairs)
user.show_message("\The [src] is recharging!")
return
var/atom/T = L[t1]
@@ -178,7 +194,22 @@ Frequency:
to_chat(user, "\The [src] is malfunctioning.")
return
user.show_message("Locked In.", 2)
- var/obj/effect/portal/P = new /obj/effect/portal(get_turf(src), T, src)
- try_move_adjacent(P)
- active_portals++
+ var/list/obj/effect/portal/created = create_portal_pair(current_location, get_teleport_turf(get_turf(T)), src, 300, 1)
+ if(!(LAZYLEN(created) == 2))
+ return
+ try_move_adjacent(created[1])
+ active_portal_pairs[created[1]] = created[2]
add_fingerprint(user)
+
+/obj/item/weapon/hand_tele/proc/on_portal_destroy(obj/effect/portal/P)
+ active_portal_pairs -= P //If this portal pair is made by us it'll be erased along with the other portal by the portal.
+
+/obj/item/weapon/hand_tele/proc/is_parent_of_portal(obj/effect/portal/P)
+ if(!istype(P))
+ return FALSE
+ if(active_portal_pairs[P])
+ return SOURCE_PORTAL
+ for(var/i in active_portal_pairs)
+ if(active_portal_pairs[i] == P)
+ return DESTINATION_PORTAL
+ return FALSE
diff --git a/code/modules/events/wormholes.dm b/code/modules/events/wormholes.dm
index f0c8b02ffff..09dc1b802dc 100644
--- a/code/modules/events/wormholes.dm
+++ b/code/modules/events/wormholes.dm
@@ -26,7 +26,7 @@
for(var/i = 1, i <= number_of_wormholes, i++)
var/turf/T = pick(pick_turfs)
- wormholes += new /obj/effect/portal/wormhole(T, null, null, -1)
+ wormholes += new /obj/effect/portal/wormhole(T, null, 300, null, FALSE)
/datum/round_event/wormholes/announce()
priority_announce("Space-time anomalies detected on the station. There is no additional data.", "Anomaly Alert", 'sound/ai/spanomalies.ogg')
@@ -36,14 +36,10 @@
for(var/obj/effect/portal/wormhole/O in wormholes)
var/turf/T = pick(pick_turfs)
if(T)
- O.loc = T
+ O.forceMove(T)
/datum/round_event/wormholes/end()
- GLOB.portals.Remove(wormholes)
- for(var/obj/effect/portal/wormhole/O in wormholes)
- O.loc = null
- wormholes.Cut()
-
+ QDEL_LIST(wormholes)
/obj/effect/portal/wormhole
name = "wormhole"
@@ -70,7 +66,7 @@
if(GLOB.portals.len)
var/obj/effect/portal/P = pick(GLOB.portals)
if(P && isturf(P.loc))
- target = P.loc
+ hard_target = P.loc
if(!target)
return
- do_teleport(M, target, 1, 1, 0, 0) ///You will appear adjacent to the beacon
\ No newline at end of file
+ do_teleport(M, hard_target, 1, 1, 0, 0) ///You will appear adjacent to the beacon
\ No newline at end of file
diff --git a/code/modules/mining/equipment/wormhole_jaunter.dm b/code/modules/mining/equipment/wormhole_jaunter.dm
index 3596125a2d6..59cb393c574 100644
--- a/code/modules/mining/equipment/wormhole_jaunter.dm
+++ b/code/modules/mining/equipment/wormhole_jaunter.dm
@@ -53,8 +53,7 @@
to_chat(user, "The [src.name] found no beacons in the world to anchor a wormhole to.")
return
var/chosen_beacon = pick(L)
- var/obj/effect/portal/wormhole/jaunt_tunnel/J = new /obj/effect/portal/wormhole/jaunt_tunnel(get_turf(src), chosen_beacon, lifespan=100)
- J.target = chosen_beacon
+ var/obj/effect/portal/wormhole/jaunt_tunnel/J = new /obj/effect/portal/wormhole/jaunt_tunnel(get_turf(src), src, lifespan=100, null, FALSE, get_turf(chosen_beacon))
try_move_adjacent(J)
playsound(src,'sound/effects/sparks4.ogg',50,1)
qdel(src)
@@ -98,7 +97,7 @@
return
if(istype(M, /atom/movable))
- if(do_teleport(M, target, 6))
+ if(do_teleport(M, hard_target, 6))
// KERPLUNK
playsound(M,'sound/weapons/resonator_blast.ogg',50,1)
if(iscarbon(M))
diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm
index 40c834ec1df..3c4d749678f 100644
--- a/code/modules/projectiles/guns/energy/special.dm
+++ b/code/modules/projectiles/guns/energy/special.dm
@@ -172,8 +172,8 @@
item_state = null
icon_state = "wormhole_projector"
origin_tech = "combat=4;bluespace=6;plasmatech=4;engineering=4"
- var/obj/effect/portal/blue
- var/obj/effect/portal/orange
+ var/obj/effect/portal/p_blue
+ var/obj/effect/portal/p_orange
/obj/item/weapon/gun/energy/wormhole_projector/update_icon()
icon_state = "[initial(icon_state)][select]"
@@ -184,30 +184,44 @@
..()
select_fire()
-/obj/item/weapon/gun/energy/wormhole_projector/proc/portal_destroyed(obj/effect/portal/P)
- if(P.icon_state == "portal")
- blue = null
- if(orange)
- orange.target = null
- else
- orange = null
- if(blue)
- blue.target = null
+/obj/item/weapon/gun/energy/wormhole_projector/proc/on_portal_destroy(obj/effect/portal/P)
+ if(P == p_blue)
+ p_blue = null
+ else if(P == p_orange)
+ p_orange = null
-/obj/item/weapon/gun/energy/wormhole_projector/proc/create_portal(obj/item/projectile/beam/wormhole/W)
- var/obj/effect/portal/P = new /obj/effect/portal(get_turf(W), null, src)
- P.precision = 0
- if(W.name == "bluespace beam")
- qdel(blue)
- blue = P
- else
- qdel(orange)
+/obj/item/weapon/gun/energy/wormhole_projector/proc/has_blue_portal()
+ if(istype(p_blue) && !QDELETED(p_blue))
+ return TRUE
+ return FALSE
+
+/obj/item/weapon/gun/energy/wormhole_projector/proc/has_orange_portal()
+ if(istype(p_orange) && !QDELETED(p_orange))
+ return TRUE
+ return FALSE
+
+/obj/item/weapon/gun/energy/wormhole_projector/proc/crosslink()
+ if(!has_blue_portal() && !has_orange_portal())
+ return
+ if(!has_blue_portal() && has_orange_portal())
+ p_orange.link_portal(null)
+ return
+ if(!has_orange_portal() && has_blue_portal())
+ p_blue.link_portal(null)
+ return
+ p_orange.link_portal(p_blue)
+ p_blue.link_portal(p_orange)
+
+/obj/item/weapon/gun/energy/wormhole_projector/proc/create_portal(obj/item/projectile/beam/wormhole/W, turf/target)
+ var/obj/effect/portal/P = new /obj/effect/portal(target, src, 300, null, FALSE, null)
+ if(istype(W, /obj/item/projectile/beam/wormhole/orange))
+ qdel(p_orange)
+ p_orange = P
P.icon_state = "portal1"
- orange = P
- if(orange && blue)
- blue.target = get_turf(orange)
- orange.target = get_turf(blue)
-
+ else
+ qdel(p_blue)
+ p_blue = P
+ crosslink()
/* 3d printer 'pseudo guns' for borgs */
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index b90129d9028..74d5018ed13 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -31,6 +31,7 @@
var/ricochets = 0
var/ricochets_max = 2
var/ricochet_chance = 30
+ var/ignore_source_check = FALSE
var/damage = 10
var/damage_type = BRUTE //BRUTE, BURN, TOX, OXY, CLONE are the only things that should be in here
@@ -138,8 +139,9 @@
if(check_ricochet() && check_ricochet_flag(A) && ricochets < ricochets_max)
ricochets++
if(A.handle_ricochet(src))
+ ignore_source_check = TRUE
return FALSE
- if(firer && !ricochets)
+ if(firer && !ignore_source_check)
if(A == firer || (A == firer.loc && istype(A, /obj/mecha))) //cannot shoot yourself or your mech
loc = A.loc
return FALSE
diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm
index 74a889c9fd9..1b00881106e 100644
--- a/code/modules/projectiles/projectile/special.dm
+++ b/code/modules/projectiles/projectile/special.dm
@@ -183,7 +183,7 @@
return ..()
if(!gun)
qdel(src)
- gun.create_portal(src)
+ gun.create_portal(src, get_turf(src))
/obj/item/projectile/bullet/frag12
name ="explosive slug"