Merge pull request #4039 from Woodratt/10092017_WRBreaksEverything

Porting see down through open spaces
This commit is contained in:
Anewbe
2017-10-12 22:48:19 -05:00
committed by GitHub
9 changed files with 419 additions and 23 deletions

View File

@@ -302,6 +302,21 @@ proc/get_radio_key_from_channel(var/channel)
var/image/speech_bubble = image('icons/mob/talk.dmi',src,"[speech_type][speech_bubble_test]")
spawn(30) qdel(speech_bubble)
// VOREStation Edit - Attempt Multi-Z Talking
var/mob/above = src.shadow
while(!QDELETED(above))
var/turf/ST = get_turf(above)
if(ST)
var/list/results = get_mobs_and_objs_in_view_fast(ST, world.view)
var/image/z_speech_bubble = image('icons/mob/talk.dmi', above, "h[speech_bubble_test]")
spawn(30) qdel(z_speech_bubble)
for(var/item in results["mobs"])
if(item != above && !(item in listening))
listening[item] = z_speech_bubble
listening_obj |= results["objs"]
above = above.shadow
// VOREStation Edit End
//Main 'say' and 'whisper' message delivery
for(var/mob/M in listening)
spawn(0) //Using spawns to queue all the messages for AFTER this proc is done, and stop runtimes
@@ -310,12 +325,12 @@ proc/get_radio_key_from_channel(var/channel)
var/dst = get_dist(get_turf(M),get_turf(src))
if(dst <= message_range || (M.stat == DEAD && !forbid_seeing_deadchat)) //Inside normal message range, or dead with ears (handled in the view proc)
M << speech_bubble
M << (listening[M] || speech_bubble) // VOREStation Edit - Send the image attached to shadow mob if available
M.hear_say(message, verb, speaking, alt_name, italics, src, speech_sound, sound_vol)
if(whispering) //Don't even bother with these unless whispering
if(dst > message_range && dst <= w_scramble_range) //Inside whisper scramble range
M << speech_bubble
M << (listening[M] || speech_bubble) // VOREStation Edit - Send the image attached to shadow mob if available
M.hear_say(stars(message), verb, speaking, alt_name, italics, src, speech_sound, sound_vol*0.2)
if(dst > w_scramble_range && dst <= world.view) //Inside whisper 'visible' range
M.show_message("<span class='game say'><span class='name'>[src.name]</span> [w_not_heard].</span>", 2)

View File

@@ -0,0 +1,111 @@
//
// Controller handling icon updates of open space turfs
//
/var/global/open_space_initialised = FALSE
/var/global/datum/controller/process/open_space/OS_controller = null
/var/global/image/over_OS_darkness = image('icons/turf/open_space.dmi', "black_open")
/datum/controller/process/open_space
var/list/turfs_to_process = list() // List of turfs queued for update.
var/list/turfs_to_process_old = null // List of turfs currently being updated.
/datum/controller/process/open_space/setup()
. = ..()
name = "openspace"
schedule_interval = world.tick_lag // every second
start_delay = 30 SECONDS
OS_controller = src
over_OS_darkness.plane = OVER_OPENSPACE_PLANE
over_OS_darkness.layer = MOB_LAYER
initialize_open_space()
// Pre-process open space once once before the round starts. Wait 20 seconds so other stuff has time to finish.
spawn(200)
doWork(1)
/datum/controller/process/open_space/copyStateFrom(var/datum/controller/process/open_space/other)
. = ..()
OS_controller = src
/datum/controller/process/open_space/doWork()
// We use a different list so any additions to the update lists during a delay from scheck()
// don't cause things to be cut from the list without being updated.
turfs_to_process_old = turfs_to_process
turfs_to_process = list()
for(last_object in turfs_to_process_old)
var/turf/T = last_object
if(!QDELETED(T))
update_turf(T)
SCHECK
/datum/controller/process/open_space/proc/update_turf(var/turf/T)
for(var/atom/movable/A in T)
A.fall()
T.update_icon()
/datum/controller/process/open_space/proc/add_turf(var/turf/T, var/recursive = 0)
ASSERT(isturf(T))
turfs_to_process += T
if(recursive > 0)
var/turf/above = GetAbove(T)
if(above && isopenspace(above))
add_turf(above, recursive)
// Do the initial updates of open space turfs when the game starts. This will lag!
/datum/controller/process/open_space/proc/initialize_open_space()
// Do initial setup from bottom to top.
for(var/zlevel = 1 to world.maxz)
for(var/turf/simulated/open/T in block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel)))
add_turf(T)
open_space_initialised = TRUE
/turf/simulated/open/initialize()
. = ..()
if(open_space_initialised)
// log_debug("[src] ([x],[y],[z]) queued for update for initialize()")
OS_controller.add_turf(src)
/turf/Entered(atom/movable/AM)
. = ..()
if(open_space_initialised && !AM.invisibility && isobj(AM))
var/turf/T = GetAbove(src)
if(isopenspace(T))
// log_debug("[T] ([T.x],[T.y],[T.z]) queued for update for [src].Entered([AM])")
OS_controller.add_turf(T, 1)
/turf/Exited(atom/movable/AM)
. = ..()
if(open_space_initialised && !AM.invisibility && isobj(AM))
var/turf/T = GetAbove(src)
if(isopenspace(T))
// log_debug("[T] ([T.x],[T.y],[T.z]) queued for update for [src].Exited([AM])")
OS_controller.add_turf(T, 1)
/obj/update_icon()
. = ..()
if(open_space_initialised && !invisibility && isturf(loc))
var/turf/T = GetAbove(src)
if(isopenspace(T))
// log_debug("[T] ([T.x],[T.y],[T.z]) queued for update for [src].update_icon()")
OS_controller.add_turf(T, 1)
// Ouch... this is painful. But is there any other way?
/* - No for now
/obj/New()
. = ..()
if(open_space_initialised && !invisibility)
var/turf/T = GetAbove(src)
if(isopenspace(T))
// log_debug("[T] ([T.x],[T.y],[T.z]) queued for update for [src]New()")
OS_controller.add_turf(T, 1)
*/
// Just as New() we probably should hook Destroy() If we can think of something more efficient, lets hear it.
/obj/Destroy()
if(open_space_initialised && !invisibility && isturf(loc))
var/turf/T = GetAbove(src)
if(isopenspace(T))
OS_controller.add_turf(T, 1)
. = ..() // Important that this be at the bottom, or we will have been moved to nullspace.

View File

@@ -13,13 +13,19 @@
/turf/space/CanZPass(atom, direction)
return 1
//
// Open Space - "empty" turf that lets stuff fall thru it to the layer below
//
/turf/simulated/open
name = "open space"
icon = 'icons/turf/space.dmi'
icon_state = ""
layer = 0
desc = "\..."
density = 0
plane = OPENSPACE_PLANE_START
pathweight = 100000 //Seriously, don't try and path over this one numbnuts
dynamic_lighting = 0 // Someday lets do proper lighting z-transfer. Until then we are leaving this off so it looks nicer.
var/turf/below
@@ -42,42 +48,75 @@
AM.fall()
/turf/simulated/open/proc/update()
plane = OPENSPACE_PLANE + src.z
below = GetBelow(src)
turf_changed_event.register(below, src, /turf/simulated/open/update_icon)
var/turf/simulated/T = get_step(src,NORTH)
if(T)
turf_changed_event.register(T, src, /turf/simulated/open/update_icon)
levelupdate()
for(var/atom/movable/A in src)
A.fall()
update_icon()
OS_controller.add_turf(src, 1)
// override to make sure nothing is hidden
/turf/simulated/open/levelupdate()
for(var/obj/O in src)
O.hide(0)
/turf/simulated/open/examine(mob/user, distance, infix, suffix)
if(..(user, 2))
var/depth = 1
for(var/T = GetBelow(src); isopenspace(T); T = GetBelow(T))
depth += 1
to_chat(user, "It is about [depth] levels deep.")
/**
* Update icon and overlays of open space to be that of the turf below, plus any visible objects on that turf.
*/
/turf/simulated/open/update_icon()
overlays = list() // Edit - Overlays are being crashy when modified.
update_icon_edge()// Add - Get grass into open spaces and whatnot.
var/turf/below = GetBelow(src)
if(below)
underlays = list(image(icon = below.icon, icon_state = below.icon_state))
underlays += below.overlays.Copy()
var/below_is_open = isopenspace(below)
var/list/noverlays = list()
if(!istype(below,/turf/space))
noverlays += image(icon =icon, icon_state = "empty", layer = 2.2)
if(below_is_open)
underlays = below.underlays
else
var/image/bottom_turf = image(icon = below.icon, icon_state = below.icon_state, dir=below.dir, layer=below.layer)
bottom_turf.plane = src.plane
bottom_turf.color = below.color
underlays = list(bottom_turf)
// VOREStation Edit - Hack workaround to byond crash bug - Include the magic overlay holder object.
overlays += below.overlays
// if(below.overlay_holder)
// overlays += (below.overlays + below.overlay_holder.overlays)
// else
// overlays += below.overlays
// VOREStation Edit End
var/turf/simulated/T = get_step(src,NORTH)
if(istype(T) && !istype(T,/turf/simulated/open))
noverlays += image(icon ='icons/turf/cliff.dmi', icon_state = "metal", layer = 2.2)
// get objects (not mobs, they are handled by /obj/zshadow)
var/list/o_img = list()
for(var/obj/O in below)
if(O.invisibility) continue // Ignore objects that have any form of invisibility
if(O.loc != below) continue // Ignore multi-turf objects not directly below
var/image/temp2 = image(O, dir = O.dir, layer = O.layer)
temp2.plane = src.plane
temp2.color = O.color
temp2.overlays += O.overlays
// TODO Is pixelx/y needed?
o_img += temp2
var/overlays_pre = overlays.len
overlays += o_img
var/overlays_post = overlays.len
if(overlays_post != (overlays_pre + o_img.len)) //Here we go!
world.log << "Corrupted openspace turf at [x],[y],[z] being replaced. Pre: [overlays_pre], Post: [overlays_post]"
new /turf/simulated/open(src)
return //Let's get out of here.
var/obj/structure/stairs/S = locate() in below
if(S && S.loc == below)
var/image/I = image(icon = S.icon, icon_state = "below", dir = S.dir, layer = 2.2)
I.pixel_x = S.pixel_x
I.pixel_y = S.pixel_y
noverlays += I
if(!below_is_open)
overlays += over_OS_darkness
overlays = noverlays
return 0
return PROCESS_KILL
// Straight copy from space.
/turf/simulated/open/attackby(obj/item/C as obj, mob/user as mob)
@@ -115,4 +154,8 @@
//Most things use is_plating to test if there is a cover tile on top (like regular floors)
/turf/simulated/open/is_plating()
return 1
return TRUE
/turf/simulated/open/is_space()
var/turf/below = GetBelow(src)
return !below || below.is_space()

View File

@@ -0,0 +1,133 @@
/mob // TODO: rewrite as obj.
var/mob/zshadow/shadow
/mob/zshadow
plane = OVER_OPENSPACE_PLANE
name = "shadow"
desc = "Z-level shadow"
status_flags = GODMODE
anchored = 1
unacidable = 1
density = 0
opacity = 0 // Don't trigger lighting recalcs gah! TODO - consider multi-z lighting.
auto_init = FALSE // We do not need to be initialize()d
var/mob/owner = null // What we are a shadow of.
/mob/zshadow/can_fall()
return FALSE
/mob/zshadow/New(var/mob/L)
if(!istype(L))
qdel(src)
return
..() // I'm cautious about this, but its the right thing to do.
owner = L
sync_icon(L)
/mob/Destroy()
if(shadow)
qdel(shadow)
shadow = null
. = ..()
/mob/zshadow/examine(mob/user, distance, infix, suffix)
return owner.examine(user, distance, infix, suffix)
// Relay some stuff they hear
/mob/zshadow/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol)
if(speaker && speaker.z != src.z)
return // Only relay speech on our acutal z, otherwise we might relay sounds that were themselves relayed up!
if(isliving(owner))
verb += " from above"
return owner.hear_say(message, verb, language, alt_name, italics, speaker, speech_sound, sound_vol)
/mob/zshadow/proc/sync_icon(var/mob/M)
name = M.name
icon = M.icon
icon_state = M.icon_state
//color = M.color
color = "#848484"
overlays = M.overlays
transform = M.transform
dir = M.dir
if(shadow)
shadow.sync_icon(src)
/mob/living/Move()
. = ..()
check_shadow()
/mob/living/forceMove()
. = ..()
check_shadow()
/mob/living/on_mob_jump()
// We're about to be admin-jumped.
// Unfortuantely loc isn't set until after this proc is called. So we must spawn() so check_shadow executes with the new loc.
. = ..()
if(shadow)
spawn(0)
check_shadow()
/mob/living/proc/check_shadow()
var/mob/M = src
if(isturf(M.loc))
var/turf/simulated/open/OS = GetAbove(src)
while(OS && istype(OS))
if(!M.shadow)
M.shadow = new /mob/zshadow(M)
M.shadow.forceMove(OS)
M = M.shadow
OS = GetAbove(M)
// The topmost level does not need a shadow!
if(M.shadow)
qdel(M.shadow)
M.shadow = null
//
// Handle cases where the owner mob might have changed its icon or overlays.
//
/mob/living/update_icons()
. = ..()
if(shadow)
shadow.sync_icon(src)
// WARNING - the true carbon/human/update_icons does not call ..(), therefore we must sideways override this.
// But be careful, we don't want to screw with that proc. So lets be cautious about what we do here.
/mob/living/carbon/human/update_icons()
. = ..()
if(shadow)
shadow.sync_icon(src)
/mob/set_dir(new_dir)
. = ..()
if(shadow)
shadow.set_dir(new_dir)
// Transfer messages about what we are doing to upstairs
/mob/visible_message(var/message, var/self_message, var/blind_message)
. = ..()
if(shadow)
shadow.visible_message(message, self_message, blind_message)
// We should show the typing indicator so people above us can tell we're about to talk.
/mob/set_typing_indicator(var/state)
var/old_typing = src.typing
. = ..()
if(shadow && old_typing != src.typing)
shadow.set_typing_indicator(state) // Okay the real proc changed something! That means we should handle things too
/mob/zshadow/set_typing_indicator(var/state)
if(!typing_indicator)
typing_indicator = new
typing_indicator.icon = 'icons/mob/talk.dmi' //VOREStation Edit - Looks better on the right with job icons.
typing_indicator.icon_state = "typing"
if(state && !typing)
overlays += typing_indicator
typing = 1
else if(!state && typing)
overlays -= typing_indicator
typing = 0
if(shadow)
shadow.set_typing_indicator(state)