mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-20 18:47:20 +01:00
Makes Space Hotel Great Again (#4700)
This commit is contained in:
@@ -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.")
|
||||
@@ -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
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user