diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm
index 507b656ed1a..664cdb695f4 100644
--- a/code/game/machinery/machinery.dm
+++ b/code/game/machinery/machinery.dm
@@ -113,6 +113,7 @@ Class Procs:
var/custom_aghost_alerts=0
var/panel_open = 0
var/myArea
+ var/interact_offline = 0 // Can the machine be interacted with while de-powered.
/obj/machinery/New()
addAtProcessing()
@@ -185,7 +186,7 @@ Class Procs:
/obj/machinery/Topic(href, href_list)
..()
- if(stat & (NOPOWER|BROKEN))
+ if(!interact_offline && stat & (NOPOWER|BROKEN))
return 1
if(usr.restrained() || usr.lying || usr.stat)
return 1
@@ -230,7 +231,7 @@ Class Procs:
return src.attack_hand(user)
/obj/machinery/attack_hand(mob/user as mob)
- if(stat & (NOPOWER|BROKEN|MAINT))
+ if(!interact_offline && stat & (NOPOWER|BROKEN|MAINT))
return 1
if(user.lying || user.stat)
return 1
diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm
index e86715366d9..c952d0735d3 100644
--- a/code/modules/power/gravitygenerator.dm
+++ b/code/modules/power/gravitygenerator.dm
@@ -1,144 +1,392 @@
-// It.. uses a lot of power. Everything under power is engineering stuff, at least.
-/obj/machinery/computer/gravity_control_computer
- name = "Gravity Generator Control"
- desc = "A computer to control a local gravity generator. Qualified personnel only."
- icon = 'icons/obj/computer.dmi'
- icon_state = "airtunnel0e"
- anchored = 1
- density = 1
- var/obj/machinery/gravity_generator = null
+//
+// Gravity Generator
+//
+var/list/gravity_generators = list() // We will keep track of this by adding new gravity generators to the list, and keying it with the z level.
-/obj/machinery/gravity_generator/
- name = "Gravitational Generator"
+var/const/POWER_IDLE = 0
+var/const/POWER_UP = 1
+var/const/POWER_DOWN = 2
+
+var/const/GRAV_NEEDS_SCREWDRIVER = 0
+var/const/GRAV_NEEDS_WELDING = 1
+var/const/GRAV_NEEDS_PLASTEEL = 2
+var/const/GRAV_NEEDS_WRENCH = 3
+
+//
+// Abstract Generator
+//
+
+/obj/machinery/gravity_generator
+ name = "gravitational generator"
desc = "A device which produces a gravaton field when set up."
- icon = 'icons/obj/singularity.dmi'
- icon_state = "TheSingGen"
+ icon = 'icons/obj/machines/gravity_generator.dmi'
anchored = 1
density = 1
+ use_power = 0
+ unacidable = 1
+ var/sprite_number = 0
+
+/obj/machinery/gravity_generator/ex_act(severity)
+ if(severity == 1) // Very sturdy.
+ set_broken()
+
+/obj/machinery/gravity_generator/meteorhit()
+ return
+
+/obj/machinery/gravity_generator/update_icon()
+ ..()
+ icon_state = "[get_status()]_[sprite_number]"
+
+/obj/machinery/gravity_generator/proc/get_status()
+ return "off"
+
+// You aren't allowed to move.
+/obj/machinery/gravity_generator/Move()
+ ..()
+ qdel(src)
+
+/obj/machinery/gravity_generator/proc/set_broken()
+ stat |= BROKEN
+
+/obj/machinery/gravity_generator/proc/set_fix()
+ stat &= ~BROKEN
+
+/obj/machinery/gravity_generator/part/Destroy()
+ set_broken()
+ if(main_part)
+ qdel(main_part)
+ ..()
+
+//
+// Part generator which is mostly there for looks
+//
+
+/obj/machinery/gravity_generator/part
+ var/obj/machinery/gravity_generator/main/main_part = null
+
+/obj/machinery/gravity_generator/part/attackby(obj/item/I as obj, mob/user as mob)
+ return main_part.attackby(I, user)
+
+/obj/machinery/gravity_generator/part/get_status()
+ return main_part.get_status()
+
+/obj/machinery/gravity_generator/part/attack_hand(mob/user as mob)
+ return main_part.attack_hand(user)
+
+/obj/machinery/gravity_generator/part/set_broken()
+ ..()
+ if(main_part && !(main_part.stat & BROKEN))
+ main_part.set_broken()
+
+//
+// Generator which spawns with the station.
+//
+
+/obj/machinery/gravity_generator/main/station/initialize()
+ setup_parts()
+ middle.overlays += "activated"
+ update_list()
+
+//
+// Generator an admin can spawn
+//
+
+/obj/machinery/gravity_generator/main/station/admin/New()
+ ..()
+ initialize()
+
+//
+// Main Generator with the main code
+//
+
+/obj/machinery/gravity_generator/main
+ icon_state = "on_8"
+ idle_power_usage = 0
+ active_power_usage = 3000
+ power_channel = ENVIRON
+ sprite_number = 8
use_power = 1
- idle_power_usage = 200
- active_power_usage = 1000
+ interact_offline = 1
var/on = 1
- var/list/localareas = list()
- var/effectiverange = 25
+ var/breaker = 1
+ var/list/parts = list()
+ var/obj/middle = null
+ var/charging_state = POWER_IDLE
+ var/charge_count = 100
+ var/current_overlay = null
+ var/broken_state = 0
- // Borrows code from cloning computer
-/obj/machinery/computer/gravity_control_computer/New()
+/obj/machinery/gravity_generator/main/Destroy() // If we somehow get deleted, remove all of our other parts.
+ investigate_log("was destroyed!", "gravity")
+ on = 0
+ update_list()
+ for(var/obj/machinery/gravity_generator/part/O in parts)
+ O.main_part = null
+ qdel(O)
..()
- spawn(5)
- updatemodules()
- return
- return
-/obj/machinery/gravity_generator/New()
+/obj/machinery/gravity_generator/main/proc/setup_parts()
+ var/turf/our_turf = get_turf(src)
+ // 9x9 block obtained from the bottom middle of the block
+ var/list/spawn_turfs = block(locate(our_turf.x - 1, our_turf.y + 2, our_turf.z), locate(our_turf.x + 1, our_turf.y, our_turf.z))
+ var/count = 10
+ for(var/turf/T in spawn_turfs)
+ count--
+ if(T == our_turf) // Skip our turf.
+ continue
+ var/obj/machinery/gravity_generator/part/part = new(T)
+ if(count == 5) // Middle
+ middle = part
+ part.sprite_number = count;
+ part.main_part = src
+ parts += part
+ part.update_icon()
+
+/obj/machinery/gravity_generator/main/proc/connected_parts()
+ return parts.len == 8
+
+/obj/machinery/gravity_generator/main/set_broken()
..()
- spawn(5)
- locatelocalareas()
- return
- return
+ for(var/obj/machinery/gravity_generator/M in parts)
+ if(!(M.stat & BROKEN))
+ M.set_broken()
+ middle.overlays.Cut()
+ charge_count = 0
+ breaker = 0
+ set_power()
+ set_state(0)
+ investigate_log("has broken down.", "gravity")
+/obj/machinery/gravity_generator/main/set_fix()
+ ..()
+ for(var/obj/machinery/gravity_generator/M in parts)
+ if(M.stat & BROKEN)
+ M.set_fix()
+ broken_state = 0
+ update_icon()
+ set_power()
+// Interaction
-/obj/machinery/computer/gravity_control_computer/proc/updatemodules()
- src.gravity_generator = findgenerator()
-
-
-
-/obj/machinery/gravity_generator/proc/locatelocalareas()
- for(var/area/A in range(src,effectiverange))
- if(A.name == "Space")
- continue // No (de)gravitizing space.
- if(A.master && !( A.master in localareas) )
- localareas += A.master
-
-/obj/machinery/computer/gravity_control_computer/proc/findgenerator()
- var/obj/machinery/gravity_generator/foundgenerator = null
- for(dir in list(NORTH,EAST,SOUTH,WEST))
- //world << "SEARCHING IN [dir]"
- foundgenerator = locate(/obj/machinery/gravity_generator/, get_step(src, dir))
- if (!isnull(foundgenerator))
- //world << "FOUND"
- break
- return foundgenerator
-
-
-/obj/machinery/computer/gravity_control_computer/attack_paw(mob/user as mob)
- return attack_hand(user)
-
-/obj/machinery/computer/gravity_control_computer/attack_ai(mob/user as mob)
- return attack_hand(user)
-
-/obj/machinery/computer/gravity_control_computer/attack_hand(mob/user as mob)
- user.set_machine(src)
- add_fingerprint(user)
-
- if(stat & (BROKEN|NOPOWER))
- return
-
- updatemodules()
-
- var/dat = "
Generator Control System
"
- //dat += "Refresh"
- if(gravity_generator)
- if(gravity_generator:on)
- dat += "
Gravity Status: ON
"
+// Fixing the gravity generator.
+/obj/machinery/gravity_generator/main/attackby(obj/item/I as obj, mob/user as mob)
+ var/old_broken_state = broken_state
+ switch(broken_state)
+ if(GRAV_NEEDS_SCREWDRIVER)
+ if(istype(I, /obj/item/weapon/screwdriver))
+ user << "You secure the screws of the framework."
+ playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
+ broken_state++
+ if(GRAV_NEEDS_WELDING)
+ if(istype(I, /obj/item/weapon/weldingtool))
+ var/obj/item/weapon/weldingtool/WT = I
+ if(WT.remove_fuel(1, user))
+ user << "You mend the damaged framework."
+ playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1)
+ broken_state++
+ if(GRAV_NEEDS_PLASTEEL)
+ if(istype(I, /obj/item/stack/sheet/plasteel))
+ var/obj/item/stack/sheet/plasteel/PS = I
+ if(PS.amount >= 10)
+ PS.use(10)
+ user << "You add the plating to the framework."
+ playsound(src.loc, 'sound/machines/click.ogg', 75, 1)
+ broken_state++
+ else
+ user << "You need 10 sheets of plasteel."
+ if(GRAV_NEEDS_WRENCH)
+ if(istype(I, /obj/item/weapon/wrench))
+ user << "You secure the plating to the framework."
+ playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1)
+ set_fix()
else
- dat += "
Gravity Status: OFF
"
+ ..()
+ if(old_broken_state != broken_state)
+ update_icon()
- dat += "
Currently Supplying Gravitons To:
"
-
- for(var/area/A in gravity_generator:localareas)
- if(A.has_gravity && gravity_generator:on)
- dat += "[A]
"
-
- else if (A.has_gravity)
- dat += "[A]
"
-
- else
- dat += "[A]
"
-
- dat += "
Maintainence Functions:
"
- if(gravity_generator:on)
- dat += " TURN GRAVITY GENERATOR OFF. "
- else
- dat += " TURN GRAVITY GENERATOR ON. "
+/obj/machinery/gravity_generator/main/attack_hand(mob/user as mob)
+ if(!..())
+ return interact(user)
+/obj/machinery/gravity_generator/main/interact(mob/user as mob)
+ if(stat & BROKEN)
+ return
+ var/dat = "Gravity Generator Breaker: "
+ if(breaker)
+ dat += "ON OFF"
else
- dat += "No local gravity generator detected!"
+ dat += "ON OFF "
- user << browse(dat, "window=gravgen")
- onclose(user, "gravgen")
+ dat += "
Generator Status:
"
+ if(charging_state != POWER_IDLE)
+ dat += "WARNING Radiation Detected.
[charging_state == POWER_UP ? "Charging..." : "Discharging..."]"
+ else if(on)
+ dat += "Powered."
+ else
+ dat += "Unpowered."
+
+ dat += "
Gravity Charge: [charge_count]%
"
+
+ var/datum/browser/popup = new(user, "gravgen", name)
+ popup.set_content(dat)
+ popup.open()
-/obj/machinery/computer/gravity_control_computer/Topic(href, href_list)
- //set background = 1
- ..()
+/obj/machinery/gravity_generator/main/Topic(href, href_list)
- if ( (get_dist(src, usr) > 1 ))
- if (!istype(usr, /mob/living/silicon))
- usr.unset_machine()
- usr << browse(null, "window=air_alarm")
- return
+ if(..())
+ return
if(href_list["gentoggle"])
- if(gravity_generator:on)
- gravity_generator:on = 0
-
- for(var/area/A in gravity_generator:localareas)
- var/obj/machinery/gravity_generator/G
- for(G in machines)
- if((A.master in G.localareas) && (G.on))
- break
- if(!G)
- A.gravitychange(0,A)
-
-
- else
- for(var/area/A in gravity_generator:localareas)
- gravity_generator:on = 1
- A.gravitychange(1,A)
-
+ breaker = !breaker
+ investigate_log("was toggled [breaker ? "ON" : "OFF"] by [usr.key].", "gravity")
+ set_power()
src.updateUsrDialog()
- return
\ No newline at end of file
+
+// Power and Icon States
+
+/obj/machinery/gravity_generator/main/power_change()
+ ..()
+ investigate_log("has [stat & NOPOWER ? "lost" : "regained"] power.", "gravity")
+ set_power()
+
+/obj/machinery/gravity_generator/main/get_status()
+ if(stat & BROKEN)
+ return "fix[min(broken_state, 3)]"
+ return on || charging_state != POWER_IDLE ? "on" : "off"
+
+/obj/machinery/gravity_generator/main/update_icon()
+ ..()
+ for(var/obj/O in parts)
+ O.update_icon()
+
+// Set the charging state based on power/breaker.
+/obj/machinery/gravity_generator/main/proc/set_power()
+ var/new_state = 0
+ if(stat & (NOPOWER|BROKEN) || !breaker)
+ new_state = 0
+ else if(breaker)
+ new_state = 1
+
+ charging_state = new_state ? POWER_UP : POWER_DOWN // Startup sequence animation.
+ investigate_log("is now [charging_state == POWER_UP ? "charging" : "discharging"].", "gravity")
+ update_icon()
+
+// Set the state of the gravity.
+/obj/machinery/gravity_generator/main/proc/set_state(var/new_state)
+ charging_state = POWER_IDLE
+ on = new_state
+ use_power = on ? 2 : 1
+ // Sound the alert if gravity was just enabled or disabled.
+ var/alert = 0
+ var/area/area = get_area(src)
+ if(new_state) // If we turned on
+ if(gravity_in_level() == 0)
+ alert = 1
+ investigate_log("was brought online and is now producing gravity for this level.", "gravity")
+ message_admins("The gravity generator was brought online. ([area.name])")
+ else
+ if(gravity_in_level() == 1)
+ alert = 1
+ investigate_log("was brought offline and there is now no gravity for this level.", "gravity")
+ message_admins("The gravity generator was brought offline with no backup generator. ([area.name])")
+
+ update_icon()
+ update_list()
+ src.updateUsrDialog()
+ if(alert)
+ shake_everyone()
+
+// Charge/Discharge and turn on/off gravity when you reach 0/100 percent.
+// Also emit radiation and handle the overlays.
+/obj/machinery/gravity_generator/main/process()
+ if(stat & BROKEN)
+ return
+ if(charging_state != POWER_IDLE)
+ if(charging_state == POWER_UP && charge_count >= 100)
+ set_state(1)
+ else if(charging_state == POWER_DOWN && charge_count <= 0)
+ set_state(0)
+ else
+ if(charging_state == POWER_UP)
+ charge_count += 2
+ else if(charging_state == POWER_DOWN)
+ charge_count -= 2
+
+ if(charge_count % 4 == 0 && prob(75)) // Let them know it is charging/discharging.
+ playsound(src.loc, 'sound/effects/EMPulse.ogg', 100, 1)
+
+ updateDialog()
+ if(prob(25)) // To help stop "Your clothes feel warm" spam.
+ pulse_radiation()
+
+ var/overlay_state = null
+ switch(charge_count)
+ if(0 to 20)
+ overlay_state = null
+ if(21 to 40)
+ overlay_state = "startup"
+ if(41 to 60)
+ overlay_state = "idle"
+ if(61 to 80)
+ overlay_state = "activating"
+ if(81 to 100)
+ overlay_state = "activated"
+
+ if(overlay_state != current_overlay)
+ if(middle)
+ middle.overlays.Cut()
+ if(overlay_state)
+ middle.overlays += overlay_state
+ current_overlay = overlay_state
+
+
+/obj/machinery/gravity_generator/main/proc/pulse_radiation()
+ for(var/mob/living/L in view(7, src))
+ L.apply_effect(20, IRRADIATE)
+
+// Shake everyone on the z level to let them know that gravity was enagaged/disenagaged.
+/obj/machinery/gravity_generator/main/proc/shake_everyone()
+ var/turf/our_turf = get_turf(src)
+ for(var/mob/M in player_list)
+ var/turf/their_turf = get_turf(M)
+ if(M.client && their_turf.z == our_turf.z)
+ shake_camera(M, 15, 1)
+ M.playsound_local(our_turf, 'sound/effects/alert.ogg', 100, 1, 0.5)
+
+/obj/machinery/gravity_generator/main/proc/gravity_in_level()
+ var/turf/T = get_turf(src)
+ if(!T)
+ return 0
+ if(gravity_generators["[T.z]"])
+ return length(gravity_generators["[T.z]"])
+ return 0
+
+/obj/machinery/gravity_generator/main/proc/update_list()
+ var/turf/T = get_turf(src.loc)
+ if(T)
+ if(!gravity_generators["[T.z]"])
+ gravity_generators["[T.z]"] = list()
+ if(on)
+ gravity_generators["[T.z]"] |= src
+ else
+ gravity_generators["[T.z]"] -= src
+
+// Misc
+
+/obj/item/weapon/paper/gravity_gen
+ name = "paper- 'Generate your own gravity!'"
+ info = {"Gravity Generator Instructions For Dummies
+ Surprisingly, gravity isn't that hard to make! All you have to do is inject deadly radioactive minerals into a ball of
+ energy and you have yourself gravity! You can turn the machine on or off when required but you must remember that the generator
+ will EMIT RADIATION when charging or discharging, you can tell it is charging or discharging by the noise it makes, so please WEAR PROTECTIVE CLOTHING.
+
+ It blew up!
+ Don't panic! The gravity generator was designed to be easily repaired. If, somehow, the sturdy framework did not survive then
+ please proceed to panic; otherwise follow these steps.
+ - Secure the screws of the framework with a screwdriver.
+ - Mend the damaged framework with a welding tool.
+ - Add additional plasteel plating.
+ - Secure the additional plating with a wrench.
"}
diff --git a/icons/obj/machines/gravity_generator.dmi b/icons/obj/machines/gravity_generator.dmi
new file mode 100644
index 00000000000..69395034dc8
Binary files /dev/null and b/icons/obj/machines/gravity_generator.dmi differ