diff --git a/code/modules/holodeck/HolodeckControl.dm b/code/modules/holodeck/HolodeckControl.dm new file mode 100644 index 0000000000..9431c4685f --- /dev/null +++ b/code/modules/holodeck/HolodeckControl.dm @@ -0,0 +1,385 @@ +var/global/list/holodeck_programs = list( + "emptycourt" = /area/holodeck/source_emptycourt, \ + "boxingcourt" = /area/holodeck/source_boxingcourt, \ + "basketball" = /area/holodeck/source_basketball, \ + "thunderdomecourt" = /area/holodeck/source_thunderdomecourt, \ + "beach" = /area/holodeck/source_beach, \ + "desert" = /area/holodeck/source_desert, \ + "space" = /area/holodeck/source_space, \ + "picnicarea" = /area/holodeck/source_picnicarea, \ + "snowfield" = /area/holodeck/source_snowfield, \ + "theatre" = /area/holodeck/source_theatre, \ + "meetinghall" = /area/holodeck/source_meetinghall, \ + "courtroom" = /area/holodeck/source_courtroom, \ + "burntest" = /area/holodeck/source_burntest, \ + "wildlifecarp" = /area/holodeck/source_wildlife, \ + "turnoff" = /area/holodeck/source_plating \ + ) + +/obj/machinery/computer/HolodeckControl + name = "holodeck control console" + desc = "A computer used to control a nearby holodeck." + icon_state = "holocontrol" + + use_power = 1 + active_power_usage = 8000 //8kW for the scenery + 500W per holoitem + var/item_power_usage = 500 + + var/area/linkedholodeck = null + var/area/target = null + var/active = 0 + var/list/holographic_items = list() + var/list/holographic_mobs = list() + var/damaged = 0 + var/safety_disabled = 0 + var/mob/last_to_emag = null + var/last_change = 0 + var/last_gravity_change = 0 + var/list/supported_programs = list( \ + "Empty Court" = "emptycourt", \ + "Basketball Court" = "basketball", \ + "Thunderdome Court" = "thunderdomecourt", \ + "Boxing Ring"="boxingcourt", \ + "Beach" = "beach", \ + "Desert" = "desert", \ + "Space" = "space", \ + "Picnic Area" = "picnicarea", \ + "Snow Field" = "snowfield", \ + "Theatre" = "theatre", \ + "Meeting Hall" = "meetinghall", \ + "Courtroom" = "courtroom" \ + ) + var/list/restricted_programs = list("Atmospheric Burn Simulation" = "burntest", "Wildlife Simulation" = "wildlifecarp") + +/obj/machinery/computer/HolodeckControl/attack_ai(var/mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/computer/HolodeckControl/attack_hand(var/mob/user as mob) + + if(..()) + return + user.set_machine(src) + var/dat + + dat += "Holodeck Control System
" + dat += "
Current Loaded Programs:
" + for(var/prog in supported_programs) + dat += "([prog])
" + + dat += "
" + dat += "(Turn Off)
" + + dat += "
" + dat += "Please ensure that only holographic weapons are used in the holodeck if a combat simulation has been loaded.
" + + if(issilicon(user)) + dat += "
" + if(safety_disabled) + if (emagged) + dat += "ERROR: Cannot re-enable Safety Protocols.
" + else + dat += "(Re-Enable Safety Protocols?)
" + else + dat += "(Override Safety Protocols?)
" + + dat += "
" + + if(safety_disabled) + for(var/prog in restricted_programs) + dat += "(Begin [prog])
" + dat += "Ensure the holodeck is empty before testing.
" + dat += "
" + dat += "Safety Protocols are DISABLED
" + else + dat += "Safety Protocols are ENABLED
" + + if(linkedholodeck.has_gravity) + dat += "Gravity is (ON)
" + else + dat += "Gravity is (OFF)
" + + user << browse(dat, "window=computer;size=400x500") + onclose(user, "computer") + + return + + +/obj/machinery/computer/HolodeckControl/Topic(href, href_list) + if(..()) + return + if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) + usr.set_machine(src) + + if(href_list["program"]) + var/prog = href_list["program"] + if(prog in holodeck_programs) + target = locate(holodeck_programs[prog]) + if(target) + loadProgram(target) + + else if(href_list["AIoverride"]) + if(!issilicon(usr)) + return + + if(safety_disabled && emagged) + return //if a traitor has gone through the trouble to emag the thing, let them keep it. + + safety_disabled = !safety_disabled + update_projections() + if(safety_disabled) + message_admins("[key_name_admin(usr)] overrode the holodeck's safeties") + log_game("[key_name(usr)] overrided the holodeck's safeties") + else + message_admins("[key_name_admin(usr)] restored the holodeck's safeties") + log_game("[key_name(usr)] restored the holodeck's safeties") + + else if(href_list["gravity"]) + toggleGravity(linkedholodeck) + + src.add_fingerprint(usr) + src.updateUsrDialog() + return + +/obj/machinery/computer/HolodeckControl/attackby(var/obj/item/weapon/D as obj, var/mob/user as mob) + if(istype(D, /obj/item/weapon/card/emag)) + playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1) + last_to_emag = user //emag again to change the owner + if (!emagged) + emagged = 1 + safety_disabled = 1 + update_projections() + user << "You vastly increase projector power and override the safety and security protocols." + user << "Warning. Automatic shutoff and derezing protocols have been corrupted. Please call Nanotrasen maintenance and do not use the simulator." + log_game("[key_name(usr)] emagged the Holodeck Control Computer") + src.updateUsrDialog() + return + +/obj/machinery/computer/HolodeckControl/proc/update_projections() + if (safety_disabled) + item_power_usage = 2500 + for(var/obj/item/weapon/holo/esword/H in linkedholodeck) + H.damtype = BRUTE + else + item_power_usage = initial(item_power_usage) + for(var/obj/item/weapon/holo/esword/H in linkedholodeck) + H.damtype = initial(H.damtype) + + for(var/mob/living/simple_animal/hostile/carp/holodeck/C in holographic_mobs) + C.set_safety(!safety_disabled) + if (last_to_emag) + C.friends = list(last_to_emag) + +/obj/machinery/computer/HolodeckControl/New() + ..() + linkedholodeck = locate(/area/holodeck/alphadeck) + //if(linkedholodeck) + // target = locate(/area/holodeck/source_emptycourt) + // if(target) + // loadProgram(target) + +//This could all be done better, but it works for now. +/obj/machinery/computer/HolodeckControl/Del() + emergencyShutdown() + ..() + +/obj/machinery/computer/HolodeckControl/meteorhit(var/obj/O as obj) + emergencyShutdown() + ..() + + +/obj/machinery/computer/HolodeckControl/emp_act(severity) + emergencyShutdown() + ..() + + +/obj/machinery/computer/HolodeckControl/ex_act(severity) + emergencyShutdown() + ..() + + +/obj/machinery/computer/HolodeckControl/blob_act() + emergencyShutdown() + ..() + +/obj/machinery/computer/HolodeckControl/power_change() + var/oldstat + ..() + if (stat != oldstat && active && (stat & NOPOWER)) + emergencyShutdown() + +/obj/machinery/computer/HolodeckControl/process() + for(var/item in holographic_items) // do this first, to make sure people don't take items out when power is down. + if(!(get_turf(item) in linkedholodeck)) + derez(item, 0) + + if (!safety_disabled) + for(var/mob/living/simple_animal/hostile/carp/holodeck/C in holographic_mobs) + if (get_area(C.loc) != linkedholodeck) + holographic_mobs -= C + C.derez() + + if(!..()) + return + if(active) + use_power(item_power_usage * (holographic_items.len + holographic_mobs.len)) + + if(!checkInteg(linkedholodeck)) + damaged = 1 + target = locate(/area/holodeck/source_plating) + if(target) + loadProgram(target) + active = 0 + use_power = 1 + for(var/mob/M in range(10,src)) + M.show_message("The holodeck overloads!") + + + for(var/turf/T in linkedholodeck) + if(prob(30)) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(2, 1, T) + s.start() + T.ex_act(3) + T.hotspot_expose(1000,500,1) + +/obj/machinery/computer/HolodeckControl/proc/derez(var/obj/obj , var/silent = 1) + holographic_items.Remove(obj) + + if(obj == null) + return + + if(isobj(obj)) + var/mob/M = obj.loc + if(ismob(M)) + M.u_equip(obj) + M.update_icons() //so their overlays update + + if(!silent) + var/obj/oldobj = obj + visible_message("The [oldobj.name] fades away!") + del(obj) + +/obj/machinery/computer/HolodeckControl/proc/checkInteg(var/area/A) + for(var/turf/T in A) + if(istype(T, /turf/space)) + return 0 + + return 1 + +//Why is it called toggle if it doesn't toggle? +/obj/machinery/computer/HolodeckControl/proc/togglePower(var/toggleOn = 0) + + if(toggleOn) + var/area/targetsource = locate(/area/holodeck/source_emptycourt) + holographic_items = targetsource.copy_contents_to(linkedholodeck) + + spawn(30) + for(var/obj/effect/landmark/L in linkedholodeck) + if(L.name=="Atmospheric Test Start") + spawn(20) + var/turf/T = get_turf(L) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(2, 1, T) + s.start() + if(T) + T.temperature = 5000 + T.hotspot_expose(50000,50000,1) + + active = 1 + use_power = 2 + else + for(var/item in holographic_items) + derez(item) + if(!linkedholodeck.has_gravity) + linkedholodeck.gravitychange(1,linkedholodeck) + + var/area/targetsource = locate(/area/holodeck/source_plating) + targetsource.copy_contents_to(linkedholodeck , 1) + active = 0 + use_power = 1 + + +/obj/machinery/computer/HolodeckControl/proc/loadProgram(var/area/A) + + if(world.time < (last_change + 25)) + if(world.time < (last_change + 15))//To prevent super-spam clicking, reduced process size and annoyance -Sieve + return + for(var/mob/M in range(3,src)) + M.show_message("\b ERROR. Recalibrating projection apparatus.") + last_change = world.time + return + + last_change = world.time + active = 1 + use_power = 2 + + for(var/item in holographic_items) + derez(item) + + for(var/mob/living/simple_animal/hostile/carp/holodeck/C in holographic_mobs) + holographic_mobs -= C + C.derez() + + for(var/obj/effect/decal/cleanable/blood/B in linkedholodeck) + del(B) + + holographic_items = A.copy_contents_to(linkedholodeck , 1) + + spawn(30) + for(var/obj/effect/landmark/L in linkedholodeck) + if(L.name=="Atmospheric Test Start") + spawn(20) + var/turf/T = get_turf(L) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(2, 1, T) + s.start() + if(T) + T.temperature = 5000 + T.hotspot_expose(50000,50000,1) + if(L.name=="Holocarp Spawn") + holographic_mobs += new /mob/living/simple_animal/hostile/carp/holodeck(L.loc) + + if(L.name=="Holocarp Spawn Random") + if (prob(4)) //With 4 spawn points, carp should only appear 15% of the time. + holographic_mobs += new /mob/living/simple_animal/hostile/carp/holodeck(L.loc) + + update_projections() + + +/obj/machinery/computer/HolodeckControl/proc/toggleGravity(var/area/A) + if(world.time < (last_gravity_change + 25)) + if(world.time < (last_gravity_change + 15))//To prevent super-spam clicking + return + for(var/mob/M in range(3,src)) + M.show_message("\b ERROR. Recalibrating gravity field.") + last_change = world.time + return + + last_gravity_change = world.time + active = 1 + use_power = 1 + + if(A.has_gravity) + A.gravitychange(0,A) + else + A.gravitychange(1,A) + +/obj/machinery/computer/HolodeckControl/proc/emergencyShutdown() + //Get rid of any items + for(var/item in holographic_items) + derez(item) + for(var/mob/living/simple_animal/hostile/carp/holodeck/C in holographic_mobs) + holographic_mobs -= C + C.derez() + //Turn it back to the regular non-holographic room + target = locate(/area/holodeck/source_plating) + if(target) + loadProgram(target) + + if(!linkedholodeck.has_gravity) + linkedholodeck.gravitychange(1,linkedholodeck) + + var/area/targetsource = locate(/area/holodeck/source_plating) + targetsource.copy_contents_to(linkedholodeck , 1) + active = 0 + use_power = 1 \ No newline at end of file diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm new file mode 100644 index 0000000000..fda07a2ddb --- /dev/null +++ b/code/modules/holodeck/HolodeckObjects.dm @@ -0,0 +1,422 @@ +// Holographic Items! + +/turf/simulated/floor/holofloor/ + thermal_conductivity = 0 + +/turf/simulated/floor/holofloor/grass + name = "Lush Grass" + icon_state = "grass1" + floor_type = /obj/item/stack/tile/grass + + New() + icon_state = "grass[pick("1","2","3","4")]" + ..() + spawn(4) + update_icon() + for(var/direction in cardinal) + if(istype(get_step(src,direction),/turf/simulated/floor)) + var/turf/simulated/floor/FF = get_step(src,direction) + FF.update_icon() //so siding get updated properly + +/turf/simulated/floor/holofloor/space + icon = 'icons/turf/space.dmi' + name = "\proper space" + icon_state = "0" + +/turf/simulated/floor/holofloor/space/New() + icon_state = "[((x + y) ^ ~(x * y) + z) % 25]" + +/turf/simulated/floor/holofloor/desert + name = "desert sand" + desc = "Uncomfortably gritty for a hologram." + icon_state = "asteroid" + +/turf/simulated/floor/holofloor/desert/New() + ..() + if(prob(10)) + overlays += "asteroid[rand(0,9)]" + +/turf/simulated/floor/holofloor/attackby(obj/item/weapon/W as obj, mob/user as mob) + return + // HOLOFLOOR DOES NOT GIVE A FUCK + +/obj/structure/table/holotable + name = "table" + desc = "A square piece of metal standing on four metal legs. It can not move." + icon = 'icons/obj/structures.dmi' + icon_state = "table" + density = 1 + anchored = 1.0 + layer = 2.8 + throwpass = 1 //You can throw objects over this, despite it's density. + +/obj/structure/table/holotable/attack_hand(mob/user as mob) + return // HOLOTABLE DOES NOT GIVE A FUCK + + +/obj/structure/table/holotable/attackby(obj/item/weapon/W as obj, mob/user as mob) + if (istype(W, /obj/item/weapon/wrench)) + user << "It's a holotable! There are no bolts!" + return + + if(isrobot(user)) + return + + ..() + +/obj/structure/table/woodentable/holotable + name = "table" + desc = "A square piece of wood standing on four wooden legs. It can not move." + icon = 'icons/obj/structures.dmi' + icon_state = "wood_table" + +/obj/structure/holostool + name = "stool" + desc = "Apply butt." + icon = 'icons/obj/objects.dmi' + icon_state = "stool" + anchored = 1.0 + pressure_resistance = 15 + + +/obj/item/clothing/gloves/boxing/hologlove + name = "boxing gloves" + desc = "Because you really needed another excuse to punch your crewmates." + icon_state = "boxing" + item_state = "boxing" + +/obj/structure/window/reinforced/holowindow/Del() + ..() + +/obj/structure/window/reinforced/holowindow/attackby(obj/item/W as obj, mob/user as mob) + if(!istype(W)) return//I really wish I did not need this + if (istype(W, /obj/item/weapon/grab) && get_dist(src,user)<2) + var/obj/item/weapon/grab/G = W + if(istype(G.affecting,/mob/living)) + var/mob/living/M = G.affecting + var/state = G.state + del(W) //gotta delete it here because if window breaks, it won't get deleted + switch (state) + if(1) + M.visible_message("[user] slams [M] against \the [src]!") + M.apply_damage(7) + hit(10) + if(2) + M.visible_message("[user] bashes [M] against \the [src]!") + if (prob(50)) + M.Weaken(1) + M.apply_damage(10) + hit(25) + if(3) + M.visible_message("[user] crushes [M] against \the [src]!") + M.Weaken(5) + M.apply_damage(20) + hit(50) + return + + if(W.flags & NOBLUDGEON) return + + if(istype(W, /obj/item/weapon/screwdriver)) + user << ("It's a holowindow, you can't unfasten it!") + else if(istype(W, /obj/item/weapon/crowbar) && reinf && state <= 1) + user << ("It's a holowindow, you can't pry it!") + else if(istype(W, /obj/item/weapon/wrench) && !anchored && (!state || !reinf)) + user << ("It's a holowindow, you can't dismantle it!") + else + if(W.damtype == BRUTE || W.damtype == BURN) + hit(W.force) + if(health <= 7) + anchored = 0 + update_nearby_icons() + step(src, get_dir(user, src)) + else + playsound(loc, 'sound/effects/Glasshit.ogg', 75, 1) + ..() + return + +/obj/structure/window/reinforced/holowindow/shatter(var/display_message = 1) + playsound(src, "shatter", 70, 1) + if(display_message) + visible_message("[src] fades away as it shatters!") + del(src) + return + +/obj/structure/window/reinforced/holowindow/disappearing/Del() + ..() + +/obj/machinery/door/window/holowindoor/Del() + ..() + +/obj/machinery/door/window/holowindoor/attackby(obj/item/weapon/I as obj, mob/user as mob) + + if (src.operating == 1) + return + + if(src.density && istype(I, /obj/item/weapon) && !istype(I, /obj/item/weapon/card)) + var/aforce = I.force + playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1) + visible_message("\red [src] was hit by [I].") + if(I.damtype == BRUTE || I.damtype == BURN) + take_damage(aforce) + return + + src.add_fingerprint(user) + if (!src.requiresID()) + user = null + + if (src.allowed(user)) + if (src.density) + open() + else + close() + + else if (src.density) + flick(text("[]deny", src.base_state), src) + + return + +/obj/machinery/door/window/holowindoor/shatter(var/display_message = 1) + src.density = 0 + playsound(src, "shatter", 70, 1) + if(display_message) + visible_message("[src] fades away as it shatters!") + del(src) + +/obj/structure/stool/bed/chair/holochair/Del() + ..() + +/obj/structure/stool/bed/chair/holochair/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/wrench)) + user << ("It's a holochair, you can't dismantle it!") + return + +/obj/item/weapon/holo + damtype = HALLOSS + +/obj/item/weapon/holo/esword + desc = "May the force be within you. Sorta." + icon_state = "sword0" + force = 3.0 + throw_speed = 1 + throw_range = 5 + throwforce = 0 + w_class = 2.0 + flags = NOSHIELD | NOBLOODY + 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 + playsound(user, 'sound/weapons/saberon.ogg', 50, 1) + user << "[src] is now active." + else + force = 3 + icon_state = "sword0" + w_class = 2 + playsound(user, 'sound/weapons/saberoff.ogg', 50, 1) + user << "[src] can now be concealed." + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + + add_fingerprint(user) + return + +//BASKETBALL OBJECTS + +/obj/item/weapon/beach_ball/holoball + icon = 'icons/obj/basketball.dmi' + icon_state = "basketball" + name = "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/structure/holohoop + name = "basketball hoop" + desc = "Boom, Shakalaka!" + icon = 'icons/obj/basketball.dmi' + icon_state = "hoop" + anchored = 1 + density = 1 + throwpass = 1 + +/obj/structure/holohoop/attackby(obj/item/weapon/W as obj, mob/user as mob) + if (istype(W, /obj/item/weapon/grab) && get_dist(src,user)<2) + var/obj/item/weapon/grab/G = W + if(G.state<2) + user << "You need a better grip to do that!" + return + G.affecting.loc = src.loc + G.affecting.Weaken(5) + visible_message("[G.assailant] dunks [G.affecting] into the [src]!", 3) + del(W) + return + else if (istype(W, /obj/item) && get_dist(src,user)<2) + user.drop_item(src) + visible_message("[user] dunks [W] into the [src]!", 3) + return + +/obj/structure/holohoop/CanPass(atom/movable/mover, turf/target, height=0, air_group=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("Swish! \the [I] lands in \the [src].", 3) + else + visible_message("\The [I] bounces off of \the [src]'s rim!", 3) + return 0 + else + return ..(mover, target, height, air_group) + + +/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/New() + ..() + + +/obj/machinery/readybutton/attackby(obj/item/weapon/W as obj, mob/user as mob) + 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 << "This device is not powered." + return + + if(!user.IsAdvancedToolUser()) + return 0 + + currentarea = get_area(src.loc) + if(!currentarea) + del(src) + + if(eventstarted) + usr << "The event has already begun!" + 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/reinforced/holowindow/disappearing/W in currentarea) + del(W) + + for(var/mob/M in currentarea) + M << "FIGHT!" + +//Holorack + +/obj/structure/table/rack/holorack + name = "rack" + desc = "Different from the Middle Ages version." + icon = 'icons/obj/objects.dmi' + icon_state = "rack" + +/obj/structure/table/rack/holorack/attack_hand(mob/user as mob) + return + +/obj/structure/table/rack/holorack/attackby(obj/item/weapon/W as obj, mob/user as mob) + if (istype(W, /obj/item/weapon/wrench)) + user << "It's a holorack! You can't unwrench it!" + return + +//Holocarp + +/mob/living/simple_animal/hostile/carp/holodeck + icon = 'icons/mob/AI.dmi' + icon_state = "holo4" + icon_living = "holo4" + icon_dead = "holo4" + icon_gib = null + meat_amount = 0 + meat_type = null + +/mob/living/simple_animal/hostile/carp/holodeck/proc/set_safety(var/safe) + if (safe) + faction = "neutral" + melee_damage_lower = 0 + melee_damage_upper = 0 + wall_smash = 0 + destroy_surroundings = 0 + else + faction = "carp" + melee_damage_lower = initial(melee_damage_lower) + melee_damage_upper = initial(melee_damage_upper) + wall_smash = initial(wall_smash) + destroy_surroundings = initial(destroy_surroundings) + +/mob/living/simple_animal/hostile/carp/holodeck/gib() + derez() //holograms can't gib + +/mob/living/simple_animal/hostile/carp/holodeck/death() + ..() + derez() + +/mob/living/simple_animal/hostile/carp/holodeck/proc/derez() + visible_message("\The [src] fades away!") + del(src)