mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-25 14:08:31 +01:00
Holodeck refactor and Enchancements
Moves holodeck code to modules/holodeck. Repaths holodeck structures a little. Cleans holodeck code and prepares for additional features (multiple holodecks, etc). Should fix #4828. Necessary map bugfixen for Asteroid, Disc, Dream, Meta, and TG. Adds Lounge, Emergency Medical, pet center, and holdout room to the rec holodeck. All items spawned by the holodeck will do only stamina damage, except when emagged. This allows emergency medical to be staffed with scalpels and bonesaws and such. Emergency medical has some functioning equipment but the only drugs available are in the sleeper.
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
/proc/DuplicateObject(obj/original, var/perfectcopy = 0 , var/sameloc = 0, var/atom/newloc = null, var/nerf = 0)
|
||||
if(!original)
|
||||
return null
|
||||
var/obj/O
|
||||
|
||||
if(sameloc)
|
||||
O = new original.type(original.loc)
|
||||
else
|
||||
O = new original.type(newloc)
|
||||
|
||||
if(perfectcopy && O && original)
|
||||
var/global/list/forbidden_vars = list("type","loc","locs","vars", "parent","parent_type", "verbs","ckey","key","power_supply","contents","reagents","stat","x","y","z","group")
|
||||
|
||||
for(var/V in original.vars - forbidden_vars)
|
||||
if(istype(original.vars[V],/list))
|
||||
var/list/L = original.vars[V]
|
||||
O.vars[V] = L.Copy()
|
||||
else if(istype(original.vars[V],/datum))
|
||||
continue // this would reference the original's object, that will break when it is used or deleted.
|
||||
else
|
||||
O.vars[V] = original.vars[V]
|
||||
|
||||
if(istype(O))
|
||||
O.burn_state = -1 // holoitems do not burn
|
||||
if(nerf && istype(O,/obj/item))
|
||||
var/obj/item/I = O
|
||||
I.damtype = STAMINA // thou shalt not
|
||||
if(istype(O,/obj/machinery))
|
||||
var/obj/machinery/M = O
|
||||
M.power_change()
|
||||
O.update_icon()
|
||||
return O
|
||||
|
||||
|
||||
/area/proc/copy_contents_to(var/area/A , var/platingRequired = 0, var/nerf_weapons = 0 )
|
||||
//Takes: Area. Optional: If it should copy to areas that don't have plating
|
||||
//Returns: Nothing.
|
||||
//Notes: Attempts to move the contents of one area to another area.
|
||||
// Movement based on lower left corner. Tiles that do not fit
|
||||
// into the new area will not be moved.
|
||||
|
||||
if(!A || !src) return 0
|
||||
|
||||
var/list/turfs_src = get_area_turfs(src.type)
|
||||
var/list/turfs_trg = get_area_turfs(A.type)
|
||||
|
||||
var/src_min_x = 99999
|
||||
var/src_min_y = 99999
|
||||
var/list/refined_src = new/list()
|
||||
|
||||
for (var/turf/T in turfs_src)
|
||||
src_min_x = min(src_min_x,T.x)
|
||||
src_min_y = min(src_min_y,T.y)
|
||||
for (var/turf/T in turfs_src)
|
||||
refined_src[T] = "[T.x - src_min_x].[T.y - src_min_y]"
|
||||
|
||||
var/trg_min_x = 99999
|
||||
var/trg_min_y = 99999
|
||||
var/list/refined_trg = new/list()
|
||||
|
||||
for (var/turf/T in turfs_trg)
|
||||
trg_min_x = min(trg_min_x,T.x)
|
||||
trg_min_y = min(trg_min_y,T.y)
|
||||
for (var/turf/T in turfs_trg)
|
||||
refined_trg["[T.x - trg_min_x].[T.y - trg_min_y]"] = T
|
||||
|
||||
var/list/toupdate = new/list()
|
||||
|
||||
var/copiedobjs = list()
|
||||
|
||||
for (var/turf/T in refined_src)
|
||||
//var/datum/coords/C_src = refined_src[T]
|
||||
var/coordstring = refined_src[T]
|
||||
var/turf/B = refined_trg[coordstring]
|
||||
if(!istype(B))
|
||||
continue
|
||||
|
||||
if(platingRequired)
|
||||
if(istype(B, /turf/space))
|
||||
continue
|
||||
|
||||
var/old_dir1 = T.dir
|
||||
var/old_icon_state1 = T.icon_state
|
||||
var/old_icon1 = T.icon
|
||||
|
||||
var/turf/X = new T.type(B)
|
||||
X.dir = old_dir1
|
||||
X.icon = old_icon1
|
||||
X.icon_state = old_icon_state1
|
||||
|
||||
for(var/obj/O in T)
|
||||
var/obj/O2 = DuplicateObject(O , 1, newloc = X, nerf=nerf_weapons)
|
||||
if(!O2) continue
|
||||
copiedobjs += O2.contents + O2
|
||||
|
||||
for(var/mob/M in T)
|
||||
if(istype(M, /mob/camera)) continue // If we need to check for more mobs, I'll add a variable
|
||||
copiedobjs += DuplicateObject(M , 1, newloc = X)
|
||||
|
||||
var/global/list/forbidden_vars = list("type","stat","loc","locs","vars", "parent", "parent_type","verbs","ckey","key","x","y","z","contents", "luminosity")
|
||||
for(var/V in T.vars - forbidden_vars)
|
||||
X.vars[V] = T.vars[V]
|
||||
toupdate += X
|
||||
|
||||
if(toupdate.len)
|
||||
for(var/turf/simulated/T1 in toupdate)
|
||||
T1.CalculateAdjacentTurfs()
|
||||
SSair.add_to_active(T1,1)
|
||||
|
||||
|
||||
return copiedobjs
|
||||
@@ -0,0 +1,97 @@
|
||||
/area/holodeck
|
||||
name = "Holodeck"
|
||||
icon_state = "Holodeck"
|
||||
luminosity = 1
|
||||
lighting_use_dynamic = 0
|
||||
|
||||
var/obj/machinery/computer/holodeck/linked
|
||||
var/restricted = 0 // if true, program goes on emag list
|
||||
|
||||
/*
|
||||
Power tracking: Use the holodeck computer's power grid
|
||||
Asserts are to avoid the inevitable infinite loops
|
||||
*/
|
||||
|
||||
/area/holodeck/powered(var/chan)
|
||||
if(!master.requires_power)
|
||||
return 1
|
||||
if(master.always_unpowered)
|
||||
return 0
|
||||
if(!linked)
|
||||
return 0
|
||||
var/area/A = get_area(linked)
|
||||
ASSERT(!istype(A,/area/holodeck))
|
||||
return A.powered(chan)
|
||||
|
||||
/area/holodeck/usage(var/chan)
|
||||
if(!linked)
|
||||
return 0
|
||||
var/area/A = get_area(linked)
|
||||
ASSERT(!istype(A,/area/holodeck))
|
||||
return A.usage(chan)
|
||||
|
||||
/area/holodeck/addStaticPower(value, powerchannel)
|
||||
if(!linked)
|
||||
return
|
||||
var/area/A = get_area(linked)
|
||||
ASSERT(!istype(A,/area/holodeck))
|
||||
return A.addStaticPower(value,powerchannel)
|
||||
|
||||
/area/holodeck/use_power(var/amount, var/chan)
|
||||
if(!linked)
|
||||
return 0
|
||||
var/area/A = get_area(linked)
|
||||
ASSERT(!istype(A,/area/holodeck))
|
||||
return A.use_power(amount,chan)
|
||||
|
||||
|
||||
/*
|
||||
This is the standard holodeck. It is intended to allow you to
|
||||
blow off steam by doing stupid things like laying down, throwing
|
||||
spheres at holes, or bludgeoning people.
|
||||
*/
|
||||
/area/holodeck/rec_center
|
||||
name = "\improper Recreational Holodeck"
|
||||
|
||||
/area/holodeck/rec_center/offline
|
||||
name = "Holodeck - Offline"
|
||||
|
||||
/area/holodeck/rec_center/court
|
||||
name = "Holodeck - Empty Court"
|
||||
|
||||
/area/holodeck/rec_center/dodgeball
|
||||
name = "Holodeck - Dodgeball Court"
|
||||
|
||||
/area/holodeck/rec_center/basketball
|
||||
name = "Holodeck - Basketball Court"
|
||||
|
||||
/area/holodeck/rec_center/thunderdome
|
||||
name = "Holodeck - Thunderdome Court"
|
||||
|
||||
/area/holodeck/rec_center/beach
|
||||
name = "Holodeck - Beach"
|
||||
|
||||
/area/holodeck/rec_center/lounge
|
||||
name = "Holodeck - Lounge"
|
||||
|
||||
/area/holodeck/rec_center/medical
|
||||
name = "Holodeck - Emergency Medical"
|
||||
|
||||
/area/holodeck/rec_center/pet_lounge
|
||||
name = "Holodeck - Pet Playground"
|
||||
|
||||
|
||||
// Bad programs
|
||||
|
||||
/area/holodeck/rec_center/burn
|
||||
name = "Holodeck - Atmospheric Burn Test"
|
||||
restricted = 1
|
||||
|
||||
/area/holodeck/rec_center/wildlife
|
||||
name = "Holodeck - Wildlife Simulation"
|
||||
restricted = 1
|
||||
|
||||
/area/holodeck/rec_center/bunker
|
||||
name = "Holodeck - Holdout Bunker"
|
||||
restricted = 1
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
Holodeck Update
|
||||
|
||||
The on-station holodeck area is of type [holodeck_type].
|
||||
All subtypes of [program_type] are loaded into the program cache or emag programs list.
|
||||
If init_program is null, a random program will be loaded on startup.
|
||||
If you don't wish this, set it to the offline program or another of your choosing.
|
||||
|
||||
You can use this to add holodecks with minimal code:
|
||||
1) Define new areas for the holodeck programs
|
||||
2) Map them
|
||||
3) Create a new control console that uses those areas
|
||||
|
||||
Non-mapped areas should be skipped but you should probably comment them out anyway.
|
||||
The base of program_type will always be ignored; only subtypes will be loaded.
|
||||
*/
|
||||
|
||||
/obj/machinery/computer/holodeck
|
||||
name = "holodeck control console"
|
||||
desc = "A computer used to control a nearby holodeck."
|
||||
icon_screen = "holocontrol"
|
||||
idle_power_usage = 10
|
||||
active_power_usage = 50
|
||||
var/area/holodeck/linked
|
||||
var/area/holodeck/program
|
||||
var/area/holodeck/last_program
|
||||
var/area/offline_program = /area/holodeck/rec_center/offline
|
||||
|
||||
var/list/program_cache = list()
|
||||
var/list/emag_programs = list()
|
||||
|
||||
// Splitting this up allows two holodecks of the same size
|
||||
// to use the same source patterns. Y'know, if you want to.
|
||||
var/holodeck_type = /area/holodeck/rec_center // locate(this) to get the target holodeck
|
||||
var/program_type = /area/holodeck/rec_center // subtypes of this (but not this itself) are loadable programs
|
||||
|
||||
// set this if you want it to start with some particular program.
|
||||
var/init_program = null
|
||||
// or this to get anything
|
||||
var/random_program = 0
|
||||
|
||||
var/active = 0
|
||||
var/damaged = 0
|
||||
var/list/spawned = list()
|
||||
var/list/effects = list()
|
||||
var/last_change = 0
|
||||
|
||||
/obj/machinery/computer/holodeck/New()
|
||||
|
||||
if(ispath(holodeck_type,/area))
|
||||
linked = locate(holodeck_type)
|
||||
if(ispath(offline_program,/area))
|
||||
offline_program = locate(offline_program)
|
||||
// the following is necessary for power reasons
|
||||
var/area/AS = get_area(src)
|
||||
if(istype(AS,/area/holodeck))
|
||||
world.log << "### MAPPING ERROR"
|
||||
world.log << "Holodeck computer cannot be in a holodeck."
|
||||
world.log << "This would cause circular power dependency."
|
||||
qdel(src) // todo handle constructed computers
|
||||
return
|
||||
else
|
||||
linked.linked = src // todo detect multiple/constructed computers
|
||||
|
||||
if(ticker && ticker.current_state >= GAME_STATE_PLAYING)
|
||||
initialize()
|
||||
..()
|
||||
|
||||
/obj/machinery/computer/holodeck/initialize()
|
||||
program_cache = list()
|
||||
emag_programs = list()
|
||||
for(var/typekey in typesof(program_type) - program_type)
|
||||
var/area/holodeck/A = locate(typekey)
|
||||
if(!A || A == offline_program) continue
|
||||
if(A.contents.len == 0) continue // not loaded
|
||||
if(A.restricted)
|
||||
emag_programs += A
|
||||
else
|
||||
program_cache += A
|
||||
if(typekey == init_program)
|
||||
load_program(A,force=1)
|
||||
if(random_program && program_cache.len && init_program == null)
|
||||
load_program(pick(program_cache),force=1)
|
||||
else if(!program)
|
||||
load_program(offline_program)
|
||||
|
||||
/obj/machinery/computer/holodeck/power_change()
|
||||
..()
|
||||
toggle_power(!stat)
|
||||
|
||||
/obj/machinery/computer/holodeck/proc/toggle_power(toggleOn = 0)
|
||||
if(active == toggleOn) return
|
||||
|
||||
if(toggleOn)
|
||||
if(last_program && last_program != offline_program)
|
||||
load_program(last_program, delay = 1)
|
||||
active = 1
|
||||
else
|
||||
last_program = program
|
||||
load_program(offline_program,force=1)
|
||||
active = 0
|
||||
|
||||
/obj/machinery/computer/holodeck/proc/emergency_shutdown()
|
||||
last_program = program
|
||||
load_program(offline_program,1)
|
||||
active = 0
|
||||
|
||||
/obj/machinery/computer/holodeck/process()
|
||||
if(damaged)
|
||||
if(prob(10))
|
||||
for(var/turf/simulated/T in linked)
|
||||
if(prob(5))
|
||||
var/datum/effect_system/spark_spread/s = new
|
||||
s.set_up(2, 1, T)
|
||||
s.start()
|
||||
return
|
||||
|
||||
if(!..() || !active)
|
||||
return
|
||||
|
||||
if(!floorcheck())
|
||||
emergency_shutdown()
|
||||
damaged = 1
|
||||
for(var/mob/M in ultra_range(10,src))
|
||||
M.show_message("The holodeck overloads!")
|
||||
|
||||
for(var/turf/T in linked)
|
||||
if(prob(30))
|
||||
var/datum/effect_system/spark_spread/s = new
|
||||
s.set_up(2, 1, T)
|
||||
s.start()
|
||||
T.ex_act(3)
|
||||
T.hotspot_expose(1000,500,1)
|
||||
|
||||
if(!emagged)
|
||||
for(var/item in spawned)
|
||||
if(!(get_turf(item) in linked))
|
||||
derez(item, 0)
|
||||
for(var/obj/effect/holodeck_effect/HE in effects)
|
||||
HE.tick()
|
||||
|
||||
active_power_usage = 50 + spawned.len * 3 + effects.len * 5
|
||||
|
||||
/obj/machinery/computer/holodeck/proc/floorcheck()
|
||||
for(var/turf/T in linked)
|
||||
if(!T.intact || istype(T,/turf/space))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/obj/machinery/computer/holodeck/Topic(href, list/href_list)
|
||||
if(..())
|
||||
return
|
||||
if(Adjacent(usr) || istype(usr, /mob/living/silicon))
|
||||
usr.set_machine(src)
|
||||
src.add_fingerprint(usr)
|
||||
if(href_list["loadarea"])
|
||||
var/typepath = text2path(href_list["loadarea"])
|
||||
if(ispath(typepath,/area))
|
||||
var/area/target = locate(typepath)
|
||||
if(istype(target))
|
||||
load_program(target)
|
||||
else
|
||||
world.log << "area not ready: [typepath]"
|
||||
else
|
||||
world.log << "bad area argument: [href_list["loadarea"]]"
|
||||
else if("safety" in href_list)
|
||||
var/oe = emagged
|
||||
emagged = !text2num(href_list["safety"])
|
||||
if(oe == emagged) return
|
||||
if(program && !stat)
|
||||
if(!emagged && program.restricted)
|
||||
load_program(offline_program)
|
||||
else
|
||||
nerf(!emagged)
|
||||
src.updateUsrDialog()
|
||||
|
||||
/obj/machinery/computer/holodeck/proc/nerf(active)
|
||||
for(var/obj/item/I in spawned)
|
||||
I.damtype = (active? STAMINA : initial(I.damtype) )
|
||||
for(var/obj/effect/holodeck_effect/HE in effects)
|
||||
HE.safety(active)
|
||||
|
||||
/obj/machinery/computer/holodeck/emag_act(mob/user as mob)
|
||||
if(!emagged)
|
||||
if(!emag_programs.len)
|
||||
user << "[src] does not seem to have a card swipe port. It must be an inferior model."
|
||||
return
|
||||
playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
|
||||
emagged = 1
|
||||
user << "<span class='warning'>You vastly increase projector power and override the safety and security protocols.</span>"
|
||||
user << "Warning. Automatic shutoff and derezing protocols have been corrupted. Please call Nanotrasen maintenance and do not use the simulator."
|
||||
log_game("[key_name(user)] emagged the Holodeck Control Console")
|
||||
src.updateUsrDialog()
|
||||
nerf(!emagged)
|
||||
|
||||
/obj/machinery/computer/holodeck/Destroy()
|
||||
emergency_shutdown()
|
||||
..()
|
||||
|
||||
/obj/machinery/computer/holodeck/emp_act(severity)
|
||||
emergency_shutdown()
|
||||
..()
|
||||
|
||||
/obj/machinery/computer/holodeck/ex_act(severity, target)
|
||||
emergency_shutdown()
|
||||
..()
|
||||
|
||||
/obj/machinery/computer/holodeck/blob_act()
|
||||
emergency_shutdown()
|
||||
..()
|
||||
@@ -0,0 +1,109 @@
|
||||
/obj/machinery/computer/holodeck/attack_hand(var/mob/user as mob)
|
||||
user.set_machine(src)
|
||||
|
||||
var/dat = "<h3>Current Loaded Programs</h3>"
|
||||
dat += "<a href='?src=\ref[src];loadarea=[offline_program.type]'>Power Off</a><br>"
|
||||
for(var/area/A in program_cache)
|
||||
dat += "<a href='?src=\ref[src];loadarea=[A.type]'>[A.name]</a><br>"
|
||||
if(emagged && emag_programs.len)
|
||||
dat += "<span class='warning'>SUPERVISOR ACCESS - SAFETY PROTOCOLS DISABLED - CAUTION: EMITTER ANOMALY</span><br>"
|
||||
for(var/area/A in emag_programs)
|
||||
dat += "<a href='?src=\ref[src];loadarea=[A.type]'>[A.name]</a><br>"
|
||||
|
||||
var/datum/browser/popup = new(user, "computer", name, 400, 500)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
return
|
||||
|
||||
/obj/machinery/computer/holodeck/attack_ai(var/mob/user as mob)
|
||||
var/dat = "<h3>Current Loaded Programs</h3>"
|
||||
|
||||
dat += "<a href='?src=\ref[src];loadarea=[offline_program.type]'>Power Off</a><br>"
|
||||
for(var/area/A in program_cache)
|
||||
dat += "<a href='?src=\ref[src];loadarea=[A.type]'>[A.name]</a><br>"
|
||||
|
||||
if(emag_programs.len)
|
||||
dat += "<br>"
|
||||
if(emagged)
|
||||
dat += "Safety protocol: <span class='bad'>Offline</span> <a href='?\ref[src];safety=1'>Engage</a><br>"
|
||||
for(var/area/A in emag_programs)
|
||||
dat += "<a href='?src=\ref[src];loadarea=[A.type]'>[A.name]</a><br>"
|
||||
else
|
||||
dat += "Safety protocol: <span class='good'>Online</span> <a href='?\ref[src];safety=0'>Disengage</a><br>"
|
||||
|
||||
var/datum/browser/popup = new(user, "computer", name, 400, 500)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
|
||||
|
||||
/obj/machinery/computer/holodeck/proc/load_program(var/area/A, var/force = 0, var/delay = 0)
|
||||
if(stat)
|
||||
A = offline_program
|
||||
force = 1
|
||||
delay = 0
|
||||
if(program == A)
|
||||
return
|
||||
if(world.time < (last_change + 25 + (damaged?500:0)) && !force)
|
||||
if(delay)
|
||||
sleep(25)
|
||||
else
|
||||
if(world.time < (last_change + 15))//To prevent super-spam clicking, reduced process size and annoyance -Sieve
|
||||
return
|
||||
if(get_dist(usr,src) <= 3)
|
||||
usr << "<span class='warning'>ERROR. Recalibrating projection apparatus.</span>"
|
||||
return
|
||||
|
||||
last_change = world.time
|
||||
active = (A != offline_program)
|
||||
use_power = active+1
|
||||
|
||||
for(var/obj/effect/holodeck_effect/HE in effects)
|
||||
HE.deactivate(src)
|
||||
|
||||
for(var/item in spawned)
|
||||
derez(item, forced=force)
|
||||
|
||||
program = A
|
||||
// note nerfing does not yet work on guns, should
|
||||
// should also remove/limit/filter reagents?
|
||||
// this is an exercise left to others I'm afraid. -Sayu
|
||||
spawned = A.copy_contents_to(linked, 1, nerf_weapons = !emagged)
|
||||
effects = list()
|
||||
|
||||
spawn(30)
|
||||
for(var/obj/effect/holodeck_effect/HE in spawned)
|
||||
effects += HE
|
||||
spawned -= HE
|
||||
var/atom/x = HE.activate(src)
|
||||
if(istype(x) || islist(x))
|
||||
spawned += x // holocarp are not forever
|
||||
|
||||
/obj/machinery/computer/holodeck/proc/derez(var/obj/obj, var/silent = 1, var/forced = 0)
|
||||
// Emagging a machine creates an anomaly in the derez systems.
|
||||
if(obj && src.emagged && !src.stat && !forced)
|
||||
if((ismob(obj) || istype(obj.loc,/mob)) && prob(50))
|
||||
spawn(50) .(obj,silent) // may last a disturbingly long time
|
||||
return
|
||||
spawned.Remove(obj)
|
||||
|
||||
if(!obj)
|
||||
return
|
||||
var/turf/T = get_turf(obj)
|
||||
for(var/atom/movable/AM in obj.contents) // these should be derezed if they were generated
|
||||
AM.loc = T // otherwise make sure they are dropped
|
||||
|
||||
if(istype(obj))
|
||||
var/mob/M = obj.loc
|
||||
if(ismob(M))
|
||||
M.unEquip(obj, 1) //Holoweapons should always drop.
|
||||
|
||||
for(var/mob/M in obj.contents)
|
||||
M.loc = obj.loc
|
||||
silent = 0
|
||||
|
||||
if(!silent)
|
||||
var/obj/oldobj = obj
|
||||
visible_message("The [oldobj.name] fades away!")
|
||||
qdel(obj)
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
The holodeck activates these shortly after the program loads,
|
||||
and deactivates them immediately before changing or disabling the holodeck.
|
||||
|
||||
These remove snowflake code for special holodeck functions.
|
||||
*/
|
||||
/obj/effect/holodeck_effect
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
icon_state = "x2"
|
||||
invisibility = 101
|
||||
proc/activate(var/obj/machinery/computer/holodeck/HC)
|
||||
return
|
||||
proc/deactivate(var/obj/machinery/computer/holodeck/HC)
|
||||
qdel(src)
|
||||
return
|
||||
// Called by the holodeck computer as long as the program is running
|
||||
proc/tick(var/obj/machinery/computer/holodeck/HC)
|
||||
return
|
||||
|
||||
proc/safety(var/active)
|
||||
return
|
||||
|
||||
|
||||
// Generates a holodeck-tracked card deck
|
||||
/obj/effect/holodeck_effect/cards
|
||||
icon = 'icons/obj/toy.dmi'
|
||||
icon_state = "deck_nanotrasen_full"
|
||||
var/obj/item/toy/cards/deck/D
|
||||
|
||||
/obj/effect/holodeck_effect/cards/activate(var/obj/machinery/computer/holodeck/HC)
|
||||
D = new(loc)
|
||||
safety(!HC.emagged)
|
||||
D.holo = HC
|
||||
return D
|
||||
|
||||
/obj/effect/holodeck_effect/cards/safety(active)
|
||||
if(!D) return
|
||||
if(active)
|
||||
D.card_hitsound = null
|
||||
D.card_force = 0
|
||||
D.card_throwforce = 0
|
||||
D.card_throw_speed = 3
|
||||
D.card_throw_range = 7
|
||||
D.card_attack_verb = list("attacked")
|
||||
else
|
||||
D.card_hitsound = 'sound/weapons/bladeslice.ogg'
|
||||
D.card_force = 5
|
||||
D.card_throwforce = 10
|
||||
D.card_throw_speed = 3
|
||||
D.card_throw_range = 7
|
||||
D.card_attack_verb = list("attacked", "sliced", "diced", "slashed", "cut")
|
||||
|
||||
|
||||
/obj/effect/holodeck_effect/sparks/activate(var/obj/machinery/computer/holodeck/HC)
|
||||
var/turf/T = get_turf(src)
|
||||
if(T)
|
||||
var/datum/effect_system/spark_spread/s = new
|
||||
s.set_up(3, 1, T)
|
||||
s.start()
|
||||
T.temperature = 5000
|
||||
T.hotspot_expose(50000,50000,1)
|
||||
|
||||
|
||||
|
||||
/obj/effect/holodeck_effect/mobspawner
|
||||
var/mobtype = /mob/living/simple_animal/hostile/carp/holocarp
|
||||
var/mob/mob = null
|
||||
|
||||
/obj/effect/holodeck_effect/mobspawner/activate(var/obj/machinery/computer/holodeck/HC)
|
||||
if(islist(mobtype)) mobtype = pick(mobtype)
|
||||
mob = new mobtype(loc)
|
||||
|
||||
// these vars are not really standardized but all would theoretically create stuff on death
|
||||
for(var/v in list("butcher_results","corpse","weapon1","weapon2") & mob.vars)
|
||||
mob.vars[v] = null
|
||||
return mob
|
||||
|
||||
/obj/effect/holodeck_effect/mobspawner/deactivate(var/obj/machinery/computer/holodeck/HC)
|
||||
if(mob) HC.derez(mob)
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/holodeck_effect/mobspawner/pet
|
||||
mobtype = list(
|
||||
/mob/living/simple_animal/butterfly, /mob/living/simple_animal/chick/holo,
|
||||
/mob/living/simple_animal/pet/cat, /mob/living/simple_animal/pet/cat/kitten,
|
||||
/mob/living/simple_animal/pet/dog/corgi, /mob/living/simple_animal/pet/dog/corgi/puppy,
|
||||
/mob/living/simple_animal/pet/dog/pug, /mob/living/simple_animal/pet/fox)
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
Items, Structures, Machines
|
||||
*/
|
||||
|
||||
|
||||
//
|
||||
// Items
|
||||
//
|
||||
|
||||
/obj/item/weapon/holo
|
||||
damtype = STAMINA
|
||||
|
||||
/obj/item/weapon/holo/esword
|
||||
name = "holographic energy sword"
|
||||
desc = "May the force be with you. Sorta"
|
||||
icon_state = "sword0"
|
||||
force = 3.0
|
||||
throw_speed = 2
|
||||
throw_range = 5
|
||||
throwforce = 0
|
||||
w_class = 2.0
|
||||
hitsound = "swing_hit"
|
||||
flags = NOSHIELD
|
||||
var/active = 0
|
||||
|
||||
/obj/item/weapon/holo/esword/green
|
||||
New()
|
||||
item_color = "green"
|
||||
|
||||
/obj/item/weapon/holo/esword/red
|
||||
New()
|
||||
item_color = "red"
|
||||
|
||||
/obj/item/weapon/holo/esword/IsShield()
|
||||
if(active)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/item/weapon/holo/esword/attack(target as mob, mob/user as mob)
|
||||
..()
|
||||
|
||||
/obj/item/weapon/holo/esword/New()
|
||||
item_color = pick("red","blue","green","purple")
|
||||
|
||||
/obj/item/weapon/holo/esword/attack_self(mob/living/user as mob)
|
||||
active = !active
|
||||
if (active)
|
||||
force = 30
|
||||
icon_state = "sword[item_color]"
|
||||
w_class = 4
|
||||
hitsound = 'sound/weapons/blade1.ogg'
|
||||
playsound(user, 'sound/weapons/saberon.ogg', 20, 1)
|
||||
user << "<span class='warning'>[src] is now active.</span>"
|
||||
else
|
||||
force = 3
|
||||
icon_state = "sword0"
|
||||
w_class = 2
|
||||
hitsound = "swing_hit"
|
||||
playsound(user, 'sound/weapons/saberoff.ogg', 20, 1)
|
||||
user << "<span class='warning'>[src] can now be concealed.</span>"
|
||||
return
|
||||
|
||||
//BASKETBALL OBJECTS
|
||||
|
||||
/obj/item/toy/beach_ball/holoball
|
||||
name = "basketball"
|
||||
icon = 'icons/obj/basketball.dmi'
|
||||
icon_state = "basketball"
|
||||
item_state = "basketball"
|
||||
desc = "Here's your chance, do your dance at the Space Jam."
|
||||
w_class = 4 //Stops people from hiding it in their bags/pockets
|
||||
|
||||
/obj/item/toy/beach_ball/holoball/dodgeball
|
||||
name = "dodgeball"
|
||||
icon_state = "dodgeball"
|
||||
item_state = "dodgeball"
|
||||
desc = "Used for playing the most violent and degrading of childhood games."
|
||||
|
||||
/obj/item/toy/beach_ball/holoball/dodgeball/throw_impact(atom/hit_atom)
|
||||
..()
|
||||
if((ishuman(hit_atom)))
|
||||
var/mob/living/carbon/M = hit_atom
|
||||
playsound(src, 'sound/items/dodgeball.ogg', 50, 1)
|
||||
M.apply_damage(10, STAMINA)
|
||||
if(prob(5))
|
||||
M.Weaken(3)
|
||||
visible_message("<span class='danger'>[M] is knocked right off \his feet!</span>", 3)
|
||||
|
||||
//
|
||||
// Structures
|
||||
//
|
||||
|
||||
/obj/structure/stool/bed/chair/holo/attackby(obj/item/weapon/W as obj, mob/user as mob)
|
||||
if(istype(W,/obj/item/weapon/wrench))
|
||||
user << "It's holographic! There's no taking it apart."
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/structure/stool/bed/chair/holo/comfy
|
||||
name = "comfy chair"
|
||||
desc = "It looks comfy."
|
||||
icon_state = "comfychair"
|
||||
color = rgb(255,113,0)
|
||||
|
||||
/obj/structure/holohoop
|
||||
name = "basketball hoop"
|
||||
desc = "Boom, shakalaka!"
|
||||
icon = 'icons/obj/basketball.dmi'
|
||||
icon_state = "hoop"
|
||||
anchored = 1
|
||||
density = 1
|
||||
|
||||
/obj/structure/holohoop/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
|
||||
if (istype(W, /obj/item/weapon/grab) && get_dist(src,user)<2)
|
||||
var/obj/item/weapon/grab/G = W
|
||||
if(G.state < GRAB_AGGRESSIVE)
|
||||
user << "<span class='warning'>You need a better grip to do that!</span>"
|
||||
return
|
||||
G.affecting.loc = src.loc
|
||||
G.affecting.Weaken(5)
|
||||
visible_message("<span class='danger'>[G.assailant] dunks [G.affecting] into \the [src]!</span>", 3)
|
||||
qdel(W)
|
||||
return
|
||||
else if (istype(W, /obj/item) && get_dist(src,user)<2)
|
||||
user.drop_item(src)
|
||||
visible_message("<span class='warning'> [user] dunks [W] into \the [src]!</span>", 3)
|
||||
return
|
||||
|
||||
/obj/structure/holohoop/CanPass(atom/movable/mover, turf/target, height=0)
|
||||
if (istype(mover,/obj/item) && mover.throwing)
|
||||
var/obj/item/I = mover
|
||||
if(istype(I, /obj/item/projectile))
|
||||
return
|
||||
if(prob(50))
|
||||
I.loc = src.loc
|
||||
visible_message("<span class='warning'> Swish! \the [I] lands in \the [src].</span>", 3)
|
||||
else
|
||||
visible_message("<span class='danger'> \the [I] bounces off of \the [src]'s rim!</span>", 3)
|
||||
return 0
|
||||
else
|
||||
return ..(mover, target, height)
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Machines
|
||||
//
|
||||
|
||||
/obj/machinery/readybutton
|
||||
name = "ready declaration device"
|
||||
desc = "This device is used to declare ready. If all devices in an area are ready, the event will begin!"
|
||||
icon = 'icons/obj/monitors.dmi'
|
||||
icon_state = "auth_off"
|
||||
var/ready = 0
|
||||
var/area/currentarea = null
|
||||
var/eventstarted = 0
|
||||
|
||||
anchored = 1.0
|
||||
use_power = 1
|
||||
idle_power_usage = 2
|
||||
active_power_usage = 6
|
||||
power_channel = ENVIRON
|
||||
|
||||
/obj/machinery/readybutton/attack_ai(mob/user as mob)
|
||||
user << "The station AI is not to interact with these devices"
|
||||
return
|
||||
|
||||
/obj/machinery/readybutton/attack_paw(mob/user as mob)
|
||||
user << "<span class='warning'>You are too primitive to use this device!</span>"
|
||||
return
|
||||
|
||||
/obj/machinery/readybutton/New()
|
||||
..()
|
||||
|
||||
|
||||
/obj/machinery/readybutton/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
|
||||
user << "The device is a solid button, there's nothing you can do with it!"
|
||||
|
||||
/obj/machinery/readybutton/attack_hand(mob/user as mob)
|
||||
if(user.stat || stat & (NOPOWER|BROKEN))
|
||||
user << "<span class='warning'>This device is not powered!</span>"
|
||||
return
|
||||
|
||||
currentarea = get_area(src.loc)
|
||||
if(!currentarea)
|
||||
qdel(src)
|
||||
|
||||
if(eventstarted)
|
||||
usr << "<span class='warning'>The event has already begun!</span>"
|
||||
return
|
||||
|
||||
ready = !ready
|
||||
|
||||
update_icon()
|
||||
|
||||
var/numbuttons = 0
|
||||
var/numready = 0
|
||||
for(var/obj/machinery/readybutton/button in currentarea)
|
||||
numbuttons++
|
||||
if (button.ready)
|
||||
numready++
|
||||
|
||||
if(numbuttons == numready)
|
||||
begin_event()
|
||||
|
||||
/obj/machinery/readybutton/update_icon()
|
||||
if(ready)
|
||||
icon_state = "auth_on"
|
||||
else
|
||||
icon_state = "auth_off"
|
||||
|
||||
/obj/machinery/readybutton/proc/begin_event()
|
||||
|
||||
eventstarted = 1
|
||||
|
||||
for(var/obj/structure/window/holo/W in currentarea)
|
||||
qdel(W)
|
||||
|
||||
for(var/mob/M in currentarea)
|
||||
M << "FIGHT!"
|
||||
@@ -0,0 +1,68 @@
|
||||
/obj/structure/table/holo
|
||||
name = "table"
|
||||
frame = null
|
||||
buildstackamount = 0
|
||||
framestackamount = 0
|
||||
canSmoothWith = null
|
||||
|
||||
/obj/structure/table/holo/glass
|
||||
name = "glass table"
|
||||
icon = 'icons/obj/smooth_structures/glass_table.dmi'
|
||||
icon_state = "glass_table"
|
||||
|
||||
/obj/structure/table/holo/wood
|
||||
name = "wood table"
|
||||
icon = 'icons/obj/smooth_structures/wood_table.dmi'
|
||||
icon_state = "wood_table"
|
||||
canSmoothWith = list(/obj/structure/table/holo/wood, /obj/structure/table/holo/poker)
|
||||
|
||||
/obj/structure/table/holo/poker
|
||||
name = "poker table"
|
||||
icon = 'icons/obj/smooth_structures/poker_table.dmi'
|
||||
icon_state = "poker_table"
|
||||
canSmoothWith = list(/obj/structure/table/holo/wood, /obj/structure/table/holo/poker)
|
||||
|
||||
/obj/structure/table/holo/attack_paw(mob/user as mob)
|
||||
return
|
||||
|
||||
/obj/structure/table/holo/attack_alien(mob/user as mob)
|
||||
return
|
||||
|
||||
/obj/structure/table/holo/attack_animal(mob/living/simple_animal/user as mob)
|
||||
return
|
||||
|
||||
/obj/structure/table/holo/attack_hand(mob/user as mob)
|
||||
return
|
||||
/obj/structure/table/holo/attack_hulk()
|
||||
return
|
||||
|
||||
/obj/structure/table/holo/attackby(obj/item/I, mob/user, params)
|
||||
if (istype(I, /obj/item/weapon/grab))
|
||||
tablepush(I, user)
|
||||
return
|
||||
if (istype(I, /obj/item/weapon/storage/bag/tray))
|
||||
var/obj/item/weapon/storage/bag/tray/T = I
|
||||
if(T.contents.len > 0) // If the tray isn't empty
|
||||
var/list/obj/item/oldContents = T.contents.Copy()
|
||||
T.quick_empty()
|
||||
|
||||
for(var/obj/item/C in oldContents)
|
||||
C.loc = src.loc
|
||||
|
||||
user.visible_message("[user] empties [I] on [src].")
|
||||
return
|
||||
// If the tray IS empty, continue on (tray will be placed on the table like other items)
|
||||
|
||||
if(isrobot(user))
|
||||
return
|
||||
|
||||
if(!(I.flags & ABSTRACT)) //rip more parems rip in peace ;_;
|
||||
if(user.drop_item())
|
||||
I.Move(loc)
|
||||
var/list/click_params = params2list(params)
|
||||
//Center the icon where the user clicked.
|
||||
if(!click_params || !click_params["icon-x"] || !click_params["icon-y"])
|
||||
return
|
||||
//Clamp it so that the icon never moves more than 16 pixels in either direction (thus leaving the table turf)
|
||||
I.pixel_x = Clamp(text2num(click_params["icon-x"]) - 16, -(world.icon_size/2), world.icon_size/2)
|
||||
I.pixel_y = Clamp(text2num(click_params["icon-y"]) - 16, -(world.icon_size/2), world.icon_size/2)
|
||||
@@ -0,0 +1,80 @@
|
||||
/turf/simulated/floor/holofloor
|
||||
icon_state = "floor"
|
||||
thermal_conductivity = 0
|
||||
broken_states = list()
|
||||
burnt_states = list()
|
||||
|
||||
/turf/simulated/floor/holofloor/attackby(obj/item/weapon/W as obj, mob/user as mob)
|
||||
return // HOLOFLOOR DOES NOT GIVE A FUCK
|
||||
|
||||
/turf/simulated/floor/holofloor/plating
|
||||
name = "Holodeck Projector Floor"
|
||||
icon_state = "engine"
|
||||
|
||||
/turf/simulated/floor/holofloor/grass
|
||||
gender = PLURAL
|
||||
name = "lush grass"
|
||||
icon_state = "grass"
|
||||
|
||||
/turf/simulated/floor/holofloor/beach
|
||||
name = "sand"
|
||||
icon = 'icons/misc/beach.dmi' // what
|
||||
icon_state = "sand"
|
||||
/turf/simulated/floor/holofloor/beach/coast
|
||||
name = "coastline"
|
||||
icon = 'icons/misc/beach2.dmi' // why
|
||||
icon_state = "sandwater"
|
||||
/turf/simulated/floor/holofloor/beach/water
|
||||
name = "water"
|
||||
icon_state = "water"
|
||||
|
||||
/turf/simulated/floor/holofloor/asteroid
|
||||
name = "Asteroid"
|
||||
icon_state = "asteroid0"
|
||||
|
||||
/turf/simulated/floor/holofloor/asteroid/New()
|
||||
icon_state = "asteroid[pick(0,1,2,3,4,5,6,7,8,9,10,11,12)]"
|
||||
..()
|
||||
|
||||
/turf/simulated/floor/holofloor/space
|
||||
name = "Space"
|
||||
icon = 'icons/turf/space.dmi'
|
||||
icon_state = "0"
|
||||
|
||||
/turf/simulated/floor/holofloor/space/New()
|
||||
icon_state = "[((x + y) ^ ~(x * y) + z) % 25]" // so realistic
|
||||
..()
|
||||
|
||||
/turf/simulated/floor/holofloor/hyperspace
|
||||
name = "Hyperspace"
|
||||
icon = 'icons/turf/space.dmi'
|
||||
icon_state = "speedspace_ew_1"
|
||||
|
||||
/turf/simulated/floor/holofloor/hyperspace/New()
|
||||
icon_state = "speedspace_ew_[(x + 5*y + (y%2+1)*7)%15+1]"
|
||||
..()
|
||||
|
||||
/turf/simulated/floor/holofloor/hyperspace/ns/New()
|
||||
..()
|
||||
icon_state = "speedspace_ns_[(x + 5*y + (y%2+1)*7)%15+1]"
|
||||
|
||||
/turf/simulated/floor/holofloor/carpet
|
||||
name = "Carpet"
|
||||
desc = "Electrically inviting."
|
||||
icon = 'icons/turf/floors/carpet.dmi'
|
||||
icon_state = "carpet"
|
||||
floor_tile = /obj/item/stack/tile/carpet
|
||||
broken_states = list("damaged")
|
||||
smooth = SMOOTH_TRUE
|
||||
canSmoothWith = null
|
||||
|
||||
/turf/simulated/floor/holofloor/carpet/New()
|
||||
..()
|
||||
spawn(1)
|
||||
update_icon()
|
||||
|
||||
/turf/simulated/floor/holofloor/carpet/update_icon()
|
||||
if(!..())
|
||||
return 0
|
||||
if(intact)
|
||||
smooth_icon(src)
|
||||
@@ -0,0 +1,44 @@
|
||||
/obj/structure/window/holo
|
||||
name = "reinforced window"
|
||||
icon = 'icons/obj/structures.dmi'
|
||||
icon_state = "rwindow"
|
||||
desc = "A window. It has an electric hum to it."
|
||||
density = 1
|
||||
layer = 3.2//Just above doors
|
||||
pressure_resistance = 4*ONE_ATMOSPHERE
|
||||
anchored = 1.0
|
||||
flags = ON_BORDER
|
||||
maxhealth = 100
|
||||
disassembled = 1 // prevents noise on destruction
|
||||
|
||||
/obj/structure/window/holo/New()
|
||||
..()
|
||||
for(var/O in contents)
|
||||
qdel(O) // standard window code generates stuff no matter what we want
|
||||
|
||||
/obj/structure/window/holo/spawnfragments()
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
/obj/structure/window/holo/attackby(var/obj/item/I, var/mob/user)
|
||||
if(istype(I,/obj/item/weapon/screwdriver))
|
||||
user << "You see no screws to unfasten!"
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/structure/window/holo/hit(var/damage, var/sound_effect = 1)
|
||||
. = ..()
|
||||
spawn(50) // self-healing
|
||||
health += damage
|
||||
|
||||
/obj/structure/window/holo/opaque
|
||||
name = "tinted window"
|
||||
opacity = 1
|
||||
|
||||
/obj/structure/window/holo/fulltile
|
||||
dir = 5
|
||||
maxhealth = 250
|
||||
|
||||
/obj/structure/window/holo/opaque/fulltile
|
||||
dir = 5
|
||||
maxhealth = 250
|
||||
Reference in New Issue
Block a user