Makes Space Hotel Great Again (#4700)

This commit is contained in:
Tastyfish
2016-06-30 20:42:29 -04:00
committed by Fox McCloud
parent ec4534302b
commit 4dd64607dd
37 changed files with 2354 additions and 331 deletions
@@ -0,0 +1,312 @@
#define PAY_INTERVAL 6000 // amount of ticks between payment
/area/awaymission/spacehotel
name = "Deep Space Hotel 419"
requires_power = 0
/area/awaymission/spacehotel/kitchen
name = "Hotel Kitchen"
icon_state = "kitchen"
/area/awaymission/spacehotel/reception
name = "Hotel Reception"
icon_state = "entry"
// "Directional" map template loader for N or S hotel room
/obj/effect/landmark/map_loader/hotel_room
icon = 'icons/testing/turf_analysis.dmi'
icon_state = "arrow"
/obj/item/weapon/paper/crumpled/hotel_scrap_1
name = "paper scrap"
info = "I can't believe this shitty hotel assigned me a purple-themed room. <i>Why does the shower dump grape drink everywhere??</i>"
/obj/item/weapon/paper/hotel_scrap_2
name = "Journal Entry #2"
info = "<h3>Day 7:</h3>Today, I stepped into my bathroom, only to discover that it had become a mountainside wilderness. I tried to wash my hands with snow, but it didn't particularly pan out.<br>I contacting the front desk regarding the issue, but the receptionist just stared at me with dead eyes and spouted unrepeatable curses at me. However, I convinced them to reassign me to an upgraded suite."
/obj/item/weapon/paper/hotel_scrap_3
name = "Journal Entry #3"
info = "<h3>Day 26:</h3>Well, that sucked royally. The queen-sized bed was nice, but the hottub in my room was, well, my wife described it as \"a portal to hell\". I don't know, I guess the lava and weird blood gave it that impression, but it was pretty nicely warm.<br>However, <i>\"we\"</i> decided to get assigned to a new room. Whatever."
/obj/item/weapon/paper/hotel_scrap_4
name = "Diary Page"
info = "<b>Day 7 of Bad Vacation</b><br>Thanks Qzxor this shit vacation is ending today. The novelty of the trashy pool wore off 2 days in and I'm not allowed in the bar. The tiny box they called a room that, we had to cram into, just had a poster and a couple of beds. Only one bedsheet to spare. Shower had brown water and sink just dumped its water back out the ceiling, somehow.<br><br>PS: The receptionist isn't paying attention to us when we try to check out. Apparently, we're staying another day."
/obj/item/weapon/paper/hotel_scrap_5
name = "Journal Entry #5"
info = "<h3>Day 185:</h3>I feel like the restaurant's chef is just making random dishes to mess with me. I request something from the menu, and he throws out something nobody asked for on the counter. The bartender isn't much better. At least both of them can make some nice food and drinks. I would complain to the management... but they never seem to care."
/obj/item/weapon/paper/crumpled/hotel_scrap_6
name = "Journal Entry #6"
info = "<h3>Day 386:</h3><span style='font-family: wingdings; color: red'>AMBGAT MEZLBNU</span>. Something wrong. Can't stop hugging plushies."
/obj/item/weapon/paper/crumpled/hotel_scrap_7
name = "Journal Entry #8"
info = "<span style='font-family: wingdings; color: red'>ASGATDHU NAMSPA KER OKS O OKDOFLI OSDSFKO OASKDAFO AOSDKF OINAIS ISJDDEF OSKFEREAODIK OSI ODIFOSA OEKRRO ASODFKAO PSSDOF SDFK OSDKF OSDKFSASPODFIOSASD UHGU DFYG FPO ASDFOS DFOASSD FAPSO FSPDFOIER OPWASDSA PS DODIOF OSDI</span> pizza <span style='font-family: wingdings; color: red'>OKSDFO AL OKEWORK CVBUASO SDFO AOE RAOWEIK SODDFI</span>"
/obj/item/weapon/paper/hotel_scrap_8
name = "Mysterious Note"
icon_state = "paper_talisman"
info = "<div style='text-align: center; color: red; font: 24pt comic sans ms'>There is only one way to leave.</div>"
/obj/item/weapon/paper/pamphlet/hotel
name = "space hotel pamphlet"
info = "<h3>Welcome to Deep Space Hotel 419!</h3>Thank you for choosing our hotel. Simply hand your credit or debit card to the concierge and get your room key! To check out, hand your credit card back.<small><h4>Conditions:</h4><ul><li>The hotel is not responsible for any losses due to time or space anomalies.<li>The hotel is not responsible for events that occur outside of the hotel station, including, but not limited to, events that occur inside of dimensional pockets.<li>The hotel is not responsible for overcharging your account.<li>The hotel is not responsible for missing persons.<li>The hotel is not responsible for mind-altering effects due to drugs, magic, demons, or space worms.</ul></small>"
/obj/effect/landmark/map_loader/hotel_room/New()
..()
// load and randomly assign rooms
var/global/list/south_room_templates = list()
var/global/list/north_room_templates = list()
var/static/path = "_maps/map_files/templates/spacehotel/"
var/global/loaded = 0
if(!loaded)
loaded = 1
for(var/map in flist(path))
var/datum/map_template/T = new(path = "[path][map]", rename = "[map]")
if(copytext(map, 1, 3) == "n_")
north_room_templates += T
else if(copytext(map, 1, 3) == "s_")
south_room_templates += T
else
// omnidirectional rooms are randomly assigned
if(prob(50))
north_room_templates += T
else
south_room_templates += T
var/datum/map_template/M = safepick(dir == NORTH ? north_room_templates : south_room_templates)
if(M)
template = M
load(M)
if(dir == NORTH)
north_room_templates -= M
else
south_room_templates -= M
// The door to a hotel room, but also metadata for the room itself
/obj/machinery/door/unpowered/hotel_door
name = "Room Door"
icon = 'icons/obj/doors/Doorsand.dmi'
icon_state = "door_closed"
autoclose = 1
atom_say_verb = "beeps"
var/doorOpen = 'sound/machines/airlock.ogg'
var/doorClose = 'sound/machines/airlock.ogg'
var/doorDeni = 'sound/machines/DeniedBeep.ogg'
var/id // the room number, eg 101
var/obj/item/weapon/card/hotel_card/card// room's key card
var/mob/living/occupant = null // the current room occupant
var/datum/money_account/account // Account we're pulling from
var/roomtimer // timer PS handle for updating room
/obj/machinery/door/unpowered/hotel_door/New()
..()
if(id)
name = "Room [id]"
// in case we spawned after hotel
var/obj/effect/hotel_controller/H
H = H.controller
if(H)
H.add_room(src)
/obj/machinery/door/unpowered/hotel_door/Destroy()
if(roomtimer)
deltimer(roomtimer)
roomtimer = null
qdel(card)
card = null
return ..()
/obj/machinery/door/unpowered/hotel_door/examine(mob/user)
..()
to_chat(user, "This room is currently [occupant ? "" : "un"]occupied.")
/obj/machinery/door/unpowered/hotel_door/allowed(mob/living/carbon/user)
for(var/obj/item/weapon/card/hotel_card/C in user.get_all_slots())
if(C == card && occupant)
atom_say("Welcome, [occupant.real_name]!")
return 1
return 0
/obj/machinery/door/unpowered/hotel_door/update_icon()
overlays.Cut()
if(density)
icon_state = "door_closed"
else
icon_state = "door_open"
/obj/machinery/door/unpowered/hotel_door/do_animate(animation)
switch(animation)
if("opening")
playsound(loc, doorOpen, 30, 1)
flick("door_opening", src)
if("closing")
playsound(loc, doorClose, 30, 1)
flick("door_closing", src)
if("deny")
playsound(src.loc, doorDeni, 50, 0, 3)
flick("door_deny", src)
/obj/machinery/door/unpowered/hotel_door/autoclose()
if(!density && !operating && autoclose)
close()
/obj/machinery/door/unpowered/hotel_door/emag_act(mob/user)
if(isliving(user) && density)
var/obj/effect/hotel_controller/H
if(H.controller)
H.controller.deploy_sec(user)
..()
/obj/item/weapon/card/hotel_card
name = "Key Card"
icon_state = "guest"
color = "#0CF"
var/id
/obj/item/weapon/card/hotel_card/New(loc, ID)
..()
if(ID)
id = ID
if(id)
name = "Key Card - [id]"
desc = "A key card to room [id]. Use it to open the door."
/obj/item/weapon/card/hotel_card/Destroy()
var/mob/user = get(loc, /mob)
if(user)
to_chat(user, "\The [src] suddenly disappears in a flash!")
return ..()
/obj/effect/hotel_controller
var/global/obj/effect/hotel_controller/controller
name = "Deep Space Hotel 419"
icon = 'icons/mob/screen_gen.dmi'
icon_state = "x"
invisibility = 101
anchored = 1
density = 0
opacity = 0
var/list/room_doors[0] // assoc list of [room id]=hotel_door
var/list/vacant_rooms[0] // list of vacant room doors
var/list/guests[0] // assoc list of [guest mob]=room id
var/obj/item/device/radio/radio // for shouting at deadbeats
/obj/effect/hotel_controller/New()
..()
if(controller)
qdel(src)
controller = src
radio = new()
radio.broadcasting = 0
radio.listening = 0
// get room doors
for(var/obj/machinery/door/unpowered/hotel_door/D in get_area(src))
add_room(D)
/obj/effect/hotel_controller/proc/add_room(obj/machinery/door/unpowered/hotel_door/D)
room_doors["[D.id]"] = D
vacant_rooms += D
/obj/effect/hotel_controller/Destroy()
room_doors.Cut()
vacant_rooms.Cut()
guests.Cut()
if(radio)
qdel(radio)
radio = null
return ..()
// to check a person into a room; no financial stuff; returns the keycard
/obj/effect/hotel_controller/proc/checkin(roomid, mob/living/carbon/occupant, obj/item/weapon/card/id/id)
if(!istype(occupant))
world << "no occupant"
return null
var/obj/machinery/door/unpowered/hotel_door/D = room_doors["[roomid]"]
if(!D || D.occupant || (occupant in guests))
world << "room occupied"
return null
D.account = get_card_account(id, occupant)
if(!D.account)
world << "no account"
return null
if(!D.account.charge(100, transaction_purpose = "10 minutes", dest_name = name))
world << "xaction failed"
return null
D.occupant = occupant
D.roomtimer = addtimer(src, "process_room", PAY_INTERVAL, 0, roomid)
vacant_rooms -= D
guests[occupant] = roomid
var/obj/item/weapon/card/hotel_card/C = new(ID = roomid)
D.card = C
return C
/obj/effect/hotel_controller/proc/process_room(roomid)
var/obj/machinery/door/unpowered/hotel_door/D = room_doors["[roomid]"]
if(!D || !D.occupant)
return
if(D.account.charge(100, transaction_purpose = "10 minutes", dest_name = name))
D.roomtimer = addtimer(src, "process_room", PAY_INTERVAL, 0, roomid)
else
force_checkout(roomid)
// Checks a person out of a room; no financial stuff
/obj/effect/hotel_controller/proc/checkout(roomid)
var/obj/machinery/door/unpowered/hotel_door/D = room_doors["[roomid]"]
if(!D || !D.occupant)
return 0
guests -= D.occupant
D.occupant = null
D.account = null
deltimer(D.roomtimer)
D.roomtimer = null
qdel(D.card)
vacant_rooms += D
return 1
// The person's card bounced mid-stay
/obj/effect/hotel_controller/proc/force_checkout(roomid)
var/obj/machinery/door/unpowered/hotel_door/D = room_doors["[roomid]"]
if(!D || !D.occupant)
return 0
var/mob/deadbeat = D.occupant
radio.autosay("[deadbeat], your card has been rejected. You have 30 seconds to check out.", name, zlevel = list(z))
spawn(300)
if(D.occupant == deadbeat)
// they still haven't checked out...
deploy_sec(deadbeat)
checkout(roomid)
/obj/effect/hotel_controller/proc/deploy_sec(mob/living/target)
if(!istype(target) || !istype(get_area(target), /area/awaymission/spacehotel))
return
var/list/secs[0]
for(var/mob/living/carbon/human/interactive/away/hotel/guard/M in get_area(src))
if(!M.retal)
secs += M
var/mob/living/carbon/human/interactive/away/hotel/guard/S = safepick(secs)
if(!S)
return
S.retal_target = target
S.retal = 1
#undef CHECKOUT_TIME
@@ -0,0 +1,118 @@
/mob/living/carbon/human/interactive/away/hotel
away_area = /area/awaymission/spacehotel
override_under = /obj/item/clothing/under/mafia
chattyness = SNPC_CHANCE_TALK / 4
/mob/living/carbon/human/interactive/away/hotel/New(loc)
..(loc, "Skrell")
/mob/living/carbon/human/interactive/away/hotel/doSetup()
..()
MYID.access += access_syndicate
RPID.access += access_syndicate
/mob/living/carbon/human/interactive/away/hotel/guard
default_job = /datum/job/officer
/mob/living/carbon/human/interactive/away/hotel/guard/doSetup()
..("Guard")
/mob/living/carbon/human/interactive/away/hotel/chef
default_job = /datum/job/chef
away_area = /area/awaymission/spacehotel/kitchen
override_under = /obj/item/clothing/under/mafia/vest
/mob/living/carbon/human/interactive/away/hotel/bartender
default_job = /datum/job/bartender
override_under = /obj/item/clothing/under/mafia/vest
/mob/living/carbon/human/interactive/away/hotel/concierge
override_under = /obj/item/clothing/under/mafia/white
away_area = /area/awaymission/spacehotel/reception
var/list/known_guests[0]
var/obj/effect/hotel_controller/hotel
var/obj/item/weapon/card/id/last_seen_id = null
/mob/living/carbon/human/interactive/away/hotel/concierge/random()
..()
equip_or_collect(new /obj/item/weapon/clipboard(src), slot_l_hand)
/mob/living/carbon/human/interactive/away/hotel/concierge/doSetup()
..("Concierge")
/mob/living/carbon/human/interactive/away/hotel/concierge/setup_job()
favoured_types = list(/obj/item/weapon/paper, /obj/item/weapon/clipboard)
functions += list("stamping", "concierge")
restrictedJob = 1
/mob/living/carbon/human/interactive/away/hotel/concierge/proc/concierge()
if(!hotel)
hotel = hotel.controller
if(!hotel)
return
var/verbs_use = pick_list(speak_file, "verbs_use")
var/verbs_touch = pick_list(speak_file, "verbs_touch")
var/verbs_move = pick_list(speak_file, "verbs_move")
var/nouns_generic = pick_list(speak_file, "nouns_generic")
var/adjective_insult = pick_list(speak_file, "adjective_insult")
var/adjective_objects = pick_list(speak_file, "adjective_objects")
var/adjective_generic = pick_list(speak_file, "adjective_generic")
var/curse_words = pick_list(speak_file, "curse_words")
var/found = 0
for(var/mob/living/carbon/human/H in nearby - known_guests)
pointed(H)
known_guests += H
found = 1
if(found)
say("Welcome to [hotel], [nouns_generic]! Please check in by [ing_verb(verbs_move)] and [ing_verb(verbs_use)] your [adjective_objects] ID onto the table.")
return
var/obj/item/weapon/card/id
var/id_seen = 0
for(var/obj/item/weapon/card/id/I in get_area(src))
id_seen = 1
if(I != last_seen_id)
id = I
if(!id_seen)
last_seen_id = null
return
if(!id)
return
var/turf/idloc = get_turf(id)
if(!Adjacent(idloc))
tryWalk(idloc)
else
// is the last jerk that touched it over here?
var/mob/id_owner
for(var/mob/living/carbon/human/H in nearby)
if(H.ckey == id.fingerprintslast)
id_owner = H
break
if(!id_owner)
say("Whose card is this?")
pointed(id)
return
last_seen_id = id
// Checking in or out?
if(id_owner in hotel.guests)
// Check out
say ("Hope you enjoyed your [adjective_objects] stay at our [adjective_generic] hotel!")
hotel.checkout(hotel.guests[id_owner])
else
// pick a room
var/obj/machinery/door/unpowered/hotel_door/D = safepick(hotel.vacant_rooms)
if(!D)
say("Sorry, all the [adjective_objects] are occupied currently.")
else
// Check in
say("$100 per 10 minutes. The cost will be automatically [past_verb(verbs_touch)] while you're checked in.")
var/obj/item/weapon/card/hotel_card/K = hotel.checkin(D.id, id_owner, id)
if(K)
K.forceMove(idloc)
else
say("Your [adjective_insult] [curse_words] card was rejected.")
+83
View File
@@ -0,0 +1,83 @@
/mob/living/carbon/human/interactive/away
var/away_area = /area/awaymission // their overriden job area
var/override_under = null // optional type for uniform, else default for job
var/squad_member = 0 // was spawned by squad
var/home_z
/mob/living/carbon/human/interactive/away/New()
..()
TRAITS |= TRAIT_ROBUST
faction += "away"
/mob/living/carbon/human/interactive/away/random()
if(ispath(override_under, /obj/item/clothing/under))
equip_to_slot(new override_under(src), slot_w_uniform)
..()
/mob/living/carbon/human/interactive/away/doSetup()
..()
var/datum/data/pda/app/messenger/M = MYPDA.find_program(/datum/data/pda/app/messenger)
M.toff = 1
var/datum/data/pda/app/chatroom/C = MYPDA.find_program(/datum/data/pda/app/chatroom)
C.toff = 1
/mob/living/carbon/human/interactive/away/job2area()
return away_area
/mob/living/carbon/human/interactive/away/doProcess()
if(!home_z)
home_z = get_turf(z)
if(home_z != get_turf(z))
gib()
..()
/obj/effect/spawner/snpc_squad
name = "squad spawner"
icon = 'icons/mob/screen_gen.dmi'
icon_state = "x"
density = 0
opacity = 0
invisibility = 101
var/squad_type = /mob/living/carbon/human/interactive/away
var/squad_size = 3
var/global/list/squads[0]
var/list/squad
/obj/effect/spawner/snpc_squad/New()
processing_objects += src
squad = squads[squad_type]
if(!squad)
squad = list()
squads[squad_type] = squad
squad += src
/obj/effect/spawner/snpc_squad/Destroy()
squad = null
processing_objects -= src
return ..()
/obj/effect/spawner/snpc_squad/process()
// check squad memebers
var/living = 0
for(var/mob/living/carbon/human/interactive/away/A in squad)
if(!A.stat)
living++
else
// see if anyone's looking, if not, despawn
var/can_despawn = 1
for(var/mob/living/M in viewers(A, world.view + 1))
if(M.client)
living++ // dead guy that can be seen still takes up a slot
can_despawn = 0
break
if(can_despawn)
squad -= A
qdel(A)
// spawn new ones
if(living < squad_size && !length(viewers(src, world.view)))
var/mob/living/carbon/human/interactive/away/A = new squad_type(loc)
squad += A
A.squad_member = 1
+364
View File
@@ -0,0 +1,364 @@
/area/awaymission/upperlevel
name = "Open Space"
color = "#888"
lighting_use_dynamic = 0
requires_power = 0
// Used by /turf/unsimulated/floor/upperlevel as a reference for where the other floor is
/obj/effect/levelref
name = "level reference"
icon = 'icons/mob/screen_gen.dmi'
icon_state = "x2"
invisibility = 101
var/id = null
var/obj/effect/levelref/other = null
var/offset_x
var/offset_y
var/offset_z
var/global/list/levels[0]
/obj/effect/levelref/New()
..()
levels += src
for(var/obj/effect/levelref/O in levels)
if(id == O.id && O != src)
other = O
update_offset()
O.other = src
O.update_offset()
for(var/turf/unsimulated/floor/upperlevel/U in get_area(loc))
U.init(src)
return
/obj/effect/levelref/Destroy()
levels -= src
return ..()
/obj/effect/levelref/proc/update_offset()
offset_x = other.x - x
offset_y = other.y - y
offset_z = other.z - z
// Used by /turf/unsimulated/floor/upperlevel and /obj/effect/view_portal/visual
// to know if the world changed on the remote side
/obj/effect/portal_sensor
invisibility = 101
var/light_hash = -1
var/triggered_this_tick = 0
var/datum/owner // owner that receive signals
var/list/params[0] // what to send to the main object to indicate which sensor
var/trigger_limit = 5 // number of time we're allowed to trigger per ptick
/obj/effect/portal_sensor/New(loc, o, ...)
..()
owner = o
if(args.len >= 3)
params = args.Copy(3)
processing_objects += src
trigger()
/obj/effect/portal_sensor/Destroy()
processing_objects -= src
return ..()
/obj/effect/portal_sensor/Crossed(A)
trigger()
/obj/effect/portal_sensor/Uncrossed(A)
trigger()
/obj/effect/portal_sensor/process()
check_light()
if(triggered_this_tick >= trigger_limit)
call(owner, "trigger")(arglist(params))
triggered_this_tick = 0
/obj/effect/portal_sensor/proc/trigger()
triggered_this_tick++
if(triggered_this_tick < trigger_limit)
call(owner, "trigger")(arglist(params))
/obj/effect/portal_sensor/proc/check_light()
var/turf/T = loc
if(istype(T) && T.lighting_overlay && !T.lighting_overlay.needs_update)
var/atom/movable/lighting_overlay/O = T.lighting_overlay
var/hash = O.lum_r + O.lum_g + O.lum_b
if(hash != light_hash)
light_hash = hash
trigger()
else
if(light_hash != -1)
light_hash = -1
trigger()
// for second floor showing floor below
/turf/unsimulated/floor/upperlevel
icon = 'icons/turf/areas.dmi'
icon_state = "dark128"
layer = AREA_LAYER + 0.5
appearance_flags = TILE_BOUND | KEEP_TOGETHER
var/turf/lower_turf
var/obj/effect/portal_sensor/sensor
/turf/unsimulated/floor/upperlevel/New()
..()
var/obj/effect/levelref/R = locate() in get_area(src)
if(R)
init(R)
/turf/unsimulated/floor/upperlevel/Destroy()
qdel(sensor)
sensor = null
return ..()
/turf/unsimulated/floor/upperlevel/proc/init(var/obj/effect/levelref/R)
lower_turf = locate(x + R.offset_x, y + R.offset_y, z + R.offset_z)
if(lower_turf)
sensor = new(lower_turf, src)
/turf/unsimulated/floor/upperlevel/Entered(atom/movable/AM, atom/OL, ignoreRest = 0)
if(isliving(AM) || istype(AM, /obj))
if(isliving(AM))
var/mob/living/M = AM
M.emote("scream")
M.SpinAnimation(5, 1)
AM.forceMove(lower_turf)
/turf/unsimulated/floor/upperlevel/attack_ghost(mob/user)
user.forceMove(lower_turf)
/turf/unsimulated/floor/upperlevel/proc/trigger()
name = lower_turf.name
desc = lower_turf.desc
// render each atom
underlays.Cut()
for(var/X in list(lower_turf) + lower_turf.contents)
var/atom/A = X
if(A && A.invisibility <= SEE_INVISIBLE_LIVING)
var/image/I = image(A, layer = AREA_LAYER + A.layer * 0.01, dir = A.dir)
I.pixel_x = A.pixel_x
I.pixel_y = A.pixel_y
underlays += I
// remote end of narnia portal
/obj/effect/view_portal
name = "portal target"
icon = 'icons/mob/screen_gen.dmi'
icon_state = "x2"
invisibility = 101
anchored = 1
var/id = null // id of other portal turf we connect to
var/obj/effect/view_portal/other = null
var/global/list/portals[0]
/obj/effect/view_portal/New()
..()
portals += src
if(id)
for(var/obj/effect/view_portal/O in portals)
if(id == O.id && O != src && can_link(O))
other = O
O.other = src
linkup()
O.linkup()
if(other)
return
/obj/effect/view_portal/Destroy()
portals -= src
return ..()
/obj/effect/view_portal/proc/can_link(obj/effect/view_portal/P)
return P.type == /obj/effect/view_portal/visual && !P.other
/obj/effect/view_portal/proc/linkup()
// allow it to link to multiple visual nodes
other = null
// near end of nania portal
/obj/effect/view_portal/visual
name = "???"
desc = "You'll have to get closer to clearly see what this is."
icon = 'icons/turf/floors.dmi'
icon_state = "loadingarea"
opacity = 1
density = 1
invisibility = 0
appearance_flags = TILE_BOUND | KEEP_TOGETHER
var/dist = 6 // dist that we render out
var/radius = 3 // dist we render on other axis, in each direction
var/frustrum = 0 // if 1, get wider and wider at each step outward
var/teleport = 1 // should teleport?
var/list/render_block
var/list/sensors[0]
var/list/tiles[0]
var/list/near_render_block
var/turf/near_viewpoint
/obj/effect/view_portal/visual/Destroy()
for(var/T in sensors)
qdel(sensors[T])
sensors.Cut()
sensors = null
for(var/T in tiles)
qdel(tiles[T])
tiles.Cut()
tiles = null
render_block = null
near_render_block = null
near_viewpoint = null
return ..()
/obj/effect/view_portal/visual/can_link(obj/effect/view_portal/P)
return P.type == /obj/effect/view_portal
/obj/effect/view_portal/visual/linkup()
icon = null
icon_state = null
var/turf/Tloc = get_turf(loc)
if(Tloc)
Tloc.icon = null
Tloc.icon_state = null
Tloc.dynamic_lighting = 0
layer = AREA_LAYER + 0.5
// setup references
var/crossdir = angle2dir((dir2angle(dir) + 90) % 360)
near_viewpoint = get_step(get_turf(src), GetOppositeDir(dir))
// setup far turfs
var/turf/T1 = get_turf(other)
var/turf/T2 = T1
for(var/i in 1 to radius)
T1 = get_step(T1, crossdir)
T2 = get_step(T2, GetOppositeDir(crossdir))
if(frustrum)
// make a trapazoid, with length dist, short end radius*2 long,
// and 45 degree angles
render_block = block(T1, T2)
for(var/i in 1 to dist)
T1 = get_step(get_step(T1, dir), crossdir)
T2 = get_step(get_step(T2, dir), GetOppositeDir(crossdir))
render_block += block(T1, T2)
else
// else make a box dist x radius*2
for(var/i in 1 to dist)
T2 = get_step(T2, dir)
render_block = block(T1, T2)
for(var/turf/T in render_block)
sensors[T] = new /obj/effect/portal_sensor(T, src, 0, T)
// setup turfs on this side of the portal to cover the map streaming
// has to be done later for view() to be correct (so it happens when the walls exist)
/obj/effect/view_portal/visual/proc/setup_near()
var/nvs = dir & (EAST|WEST) ? near_viewpoint.x - x : near_viewpoint.y - y
if(nvs)
nvs = SIGN(nvs)
// need a mob for view() to work correctly
var/mob/M = new(near_viewpoint)
M.see_invisible = SEE_INVISIBLE_LIVING
near_render_block = view(M, world.view)
qdel(M)
for(var/A in near_render_block)
var/turf/T = A
if(istype(T))
var/ts = dir & (EAST|WEST) ? T.x - x : T.y - y
if(ts)
ts = SIGN(ts)
if(nvs == ts)
sensors[T] = new /obj/effect/portal_sensor(T, src, 1, T)
else
near_render_block -= T
else
near_render_block -= T
/obj/effect/view_portal/visual/Bumped(atom/movable/thing)
if((istype(thing, /obj) || isliving(thing)) && other && teleport)
if(!near_render_block)
setup_near()
var/mob/living/M = thing
// make the person glide onto the dest, giving a smooth transition
var/ox = thing.x - x
var/oy = thing.y - y
if(istype(M) && M.client)
M.notransform = 1
// cover up client-side map loading
M.screen_loc = "CENTER"
M.client.screen += M
for(var/T in tiles)
M.client.screen += tiles[T]
// wait a tick for the screen to replicate across network
// or this whole exercise of covering the transition is pointless
spawn(1)
thing.forceMove(locate(other.x + ox, other.y + oy, other.z))
sleep(1)
if(istype(M) && M.client)
for(var/T in tiles)
M.client.screen -= tiles[T]
M.client.screen -= M
M.screen_loc = initial(M.screen_loc)
thing.forceMove(get_turf(other.loc))
if(istype(M) && M.client)
M.notransform = 0
/obj/effect/view_portal/visual/attack_ghost(mob/user)
user.forceMove(get_turf(other.loc))
/obj/effect/view_portal/visual/proc/trigger(near, turf/T)
var/obj/effect/view_portal_dummy/D = tiles[T]
if(D)
D.overlays.Cut()
else
D = new(src, near, T)
tiles[T] = D
// render atoms to overlays of a dummy object
if(D.name != T.name)
D.name = T.name
D.desc = T.desc
for(var/AX in list(T) + T.contents)
var/atom/A = AX
if(A && A.invisibility <= SEE_INVISIBLE_LIVING)
var/image/I = image(A, layer = D.layer + A.layer * 0.01, dir = A.dir)
I.pixel_x = A.pixel_x
I.pixel_y = A.pixel_y
D.overlays += I
// tile of rendered other side for narnia portal
/obj/effect/view_portal_dummy
var/obj/effect/view_portal/visual/owner
/obj/effect/view_portal_dummy/New(obj/effect/view_portal/visual/V, near, turf/T)
..()
if(!near)
loc = V.loc
owner = V
var/ox
var/oy
if(near)
ox = (T.x - V.near_viewpoint.x)
oy = (T.y - V.near_viewpoint.y)
layer = AREA_LAYER + 0.4
else
ox = T.x - V.other.x + V.x - V.near_viewpoint.x
oy = T.y - V.other.y + V.y - V.near_viewpoint.y
pixel_x = 32 * (T.x - V.other.x)
pixel_y = 32 * (T.y - V.other.y)
layer = AREA_LAYER + 0.5
if(abs(ox) <= world.view && abs(oy) <= world.view)
screen_loc = "CENTER[ox >= 0 ? "+" : ""][ox],CENTER[oy >= 0 ? "+" : ""][oy]"
/obj/effect/view_portal_dummy/attack_ghost(mob/user)
owner.attack_ghost(user)