diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 1439ede2906..622667a9ac7 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -2925,6 +2925,7 @@ message_admins("[key_name_admin(usr)] fixed all lights", 1) for(var/obj/machinery/light/L in GLOB.machines) L.fix() + L.switchcount = 0 if("floorlava") feedback_inc("admin_secrets_fun_used", 1) feedback_add_details("admin_secrets_fun_used", "LF") diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 9305faf6637..561b0e88c07 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -1,7 +1,6 @@ -// The lighting system +// The Lighting System // -// consists of light fixtures (/obj/machinery/light) and light tube/bulb items (/obj/item/light) - +// Consists of light fixtures (/obj/machinery/light) and light tube/bulb items (/obj/item/light) // status values shared between lighting fixtures and items #define LIGHT_OK 0 @@ -9,18 +8,29 @@ #define LIGHT_BROKEN 2 #define LIGHT_BURNED 3 +/** + * # Light fixture frame + * + * Incomplete light tube fixture + * + * Becomes a [Light fixture] when completed + */ /obj/machinery/light_construct name = "light fixture frame" desc = "A light fixture under construction." icon = 'icons/obj/lighting.dmi' icon_state = "tube-construct-stage1" - anchored = 1 + anchored = TRUE layer = 5 max_integrity = 200 armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) + /// Construction stage (1 = Empty frame | 2 = Wired frame | 3 = Completed frame) var/stage = 1 + /// Light bulb type var/fixture_type = "tube" + /// How many metal sheets get given after deconstruction var/sheets_refunded = 2 + /// Holder for the completed fixture var/obj/machinery/light/newlight = null /obj/machinery/light_construct/New() @@ -43,12 +53,11 @@ . = TRUE switch(stage) if(1) - to_chat(user, "You begin deconstructing [src].") + to_chat(user, "You begin to dismantle [src].") if(!I.use_tool(src, user, 30, volume = I.tool_volume)) return new /obj/item/stack/sheet/metal(get_turf(loc), sheets_refunded) - user.visible_message("[user] deconstructs [src].", \ - "You deconstruct [src].", "You hear a noise.") + TOOL_DISMANTLE_SUCCESS_MESSAGE qdel(src) if(2) to_chat(user, "You have to remove the wires first.") @@ -58,10 +67,8 @@ /obj/machinery/light_construct/wirecutter_act(mob/living/user, obj/item/I) if(stage != 2) return - . = TRUE - if(!I.use_tool(src, user, 0)) + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) return - playsound(loc, I.usesound, 100, 1) . = TRUE stage = 1 switch(fixture_type) @@ -70,14 +77,13 @@ if("bulb") icon_state = "bulb-construct-stage1" new /obj/item/stack/cable_coil(get_turf(loc), 1, paramcolor = COLOR_RED) - user.visible_message("[user] removes the wiring from [src].", \ - "You remove the wiring from [src].", "You hear a noise.") + WIRECUTTER_SNIP_MESSAGE /obj/machinery/light_construct/screwdriver_act(mob/living/user, obj/item/I) if(stage != 2) return . = TRUE - if(!I.use_tool(src, user, 0)) + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) return switch(fixture_type) if("tube") @@ -86,8 +92,7 @@ icon_state = "bulb-empty" stage = 3 user.visible_message("[user] closes [src]'s casing.", \ - "You close [src]'s casing.", "You hear a noise.") - playsound(loc, I.usesound, 75, 1) + "You close [src]'s casing.", "You hear a screwdriver.") switch(fixture_type) if("tube") @@ -115,7 +120,7 @@ stage = 2 playsound(loc, coil.usesound, 50, 1) user.visible_message("[user.name] adds wires to [src].", \ - "You add wires to [src].") + "You add wires to [src].", "You hear a noise.") return return ..() @@ -129,58 +134,90 @@ new /obj/item/stack/sheet/metal(loc, sheets_refunded) qdel(src) +/** + * # Small light fixture frame + * + * Incomplete light bulb fixture + * + * Becomes a [Small light fixture] when completed + */ /obj/machinery/light_construct/small name = "small light fixture frame" desc = "A small light fixture under construction." icon = 'icons/obj/lighting.dmi' icon_state = "bulb-construct-stage1" - anchored = 1 + anchored = TRUE layer = 5 stage = 1 fixture_type = "bulb" sheets_refunded = 1 -// the standard tube light fixture +/** + * # Light fixture + * + * The standard light tube fixture + */ /obj/machinery/light name = "light fixture" icon = 'icons/obj/lighting.dmi' - var/base_state = "tube" // base description and icon_state + var/base_state = "tube" // Base description and icon_state icon_state = "tube1" desc = "A lighting fixture." anchored = 1 - layer = 5 // They were appearing under mobs which is a little weird - Ostaf + layer = 5 max_integrity = 100 use_power = ACTIVE_POWER_USE idle_power_usage = 2 active_power_usage = 20 power_channel = LIGHT //Lights are calc'd via area so they dont need to be in the machine list - var/on = FALSE // 1 if on, 0 if off - var/on_gs = 0 + /// Is the light on or off? + var/on = FALSE + /// If the light state has changed since the last 'update()', also update the power requirements + var/power_state = FALSE + /// How much power does it use? var/static_power_used = 0 - var/brightness_range = 8 // luminosity when on, also used in power calculation + /// Light range (Also used in power calculation) + var/brightness_range = 8 + /// Light intensity var/brightness_power = 1 + /// Light colour when on var/brightness_color = "#FFFFFF" - var/status = LIGHT_OK // LIGHT_OK, _EMPTY, _BURNED or _BROKEN - var/flickering = 0 - var/light_type = /obj/item/light/tube // the type of light item + /// Light fixture status (LIGHT_OK | LIGHT_EMPTY | LIGHT_BURNED | LIGHT_BROKEN) + var/status = LIGHT_OK + /// Is the light currently flickering? + var/flickering = FALSE + + /// Item type of the light bulb + var/light_type = /obj/item/light/tube + /// Type of light bulb that goes into the fixture var/fitting = "tube" - var/switchcount = 0 // count of number of times switched on/off - // this is used to calc the probability the light burns out + /// How many times has the light been switched on/off? (This is used to calc the probability the light burns out) + var/switchcount = 0 + /// Is the light rigged to explode? + var/rigged = FALSE + /// Materials the light is made of + var/lightmaterials = list(MAT_GLASS=100) - var/rigged = 0 // true if rigged to explode - var/lightmaterials = list(MAT_GLASS=100) //stores the materials the light is made of to stop infinite glass exploit - - var/nightshift_enabled = FALSE //Currently in night shift mode? - var/nightshift_allowed = TRUE //Set to FALSE to never let this light get switched to night mode. + /// Currently in night shift mode? + var/nightshift_enabled = FALSE + /// Allowed to be switched to night shift mode? + var/nightshift_allowed = TRUE + /// Light range when in night shift mode var/nightshift_light_range = 8 + /// Light intensity when in night shift mode var/nightshift_light_power = 0.45 + /// The colour of the light while it's in night shift mode var/nightshift_light_color = "#FFDDCC" + /// The colour of the light while it's in emergency mode + var/bulb_emergency_colour = "#FF3232" - var/bulb_emergency_colour = "#FF3232" // determines the colour of the light while it's in emergency mode - -// the smaller bulb light fixture +/** + * # Small light fixture + * + * The smaller light bulb fixture + */ /obj/machinery/light/small icon_state = "bulb1" base_state = "bulb" @@ -198,36 +235,32 @@ brightness_range = 12 brightness_power = 4 -/obj/machinery/light/built/New() +/obj/machinery/light/built/Initialize(mapload) status = LIGHT_EMPTY - update(0) ..() -/obj/machinery/light/small/built/New() +/obj/machinery/light/small/built/Initialize(mapload) status = LIGHT_EMPTY - update(0) ..() // create a new lighting fixture -/obj/machinery/light/New() - ..() - spawn(2) - var/area/A = get_area(src) - if(A && !A.requires_power) - on = 1 +/obj/machinery/light/Initialize(mapload) + . = ..() + var/area/A = get_area(src) + if(A && !A.requires_power) + on = TRUE - switch(fitting) - if("tube") - brightness_range = 8 - if(prob(2)) - break_light_tube(1) - if("bulb") - brightness_range = 4 - brightness_color = "#a0a080" - if(prob(5)) - break_light_tube() - spawn(1) - update(0) + switch(fitting) + if("tube") + brightness_range = 8 + if(prob(2)) + break_light_tube(TRUE) + if("bulb") + brightness_range = 4 + brightness_color = "#a0a080" + if(prob(5)) + break_light_tube(TRUE) + update(FALSE) /obj/machinery/light/Destroy() var/area/A = get_area(src) @@ -259,8 +292,15 @@ /obj/machinery/light/get_spooked() flicker() -// update the icon_state and luminosity of the light depending on its state -/obj/machinery/light/proc/update(var/trigger = TRUE) +/** + * Updates the light's properties + * + * Updates the icon_state, luminosity, colour, and power usage of the light. + * Also handles rigged light bulbs exploding. + * Arguments: + * * trigger - Should this update make the light explode/burn out? (Defaults to TRUE) + */ +/obj/machinery/light/proc/update(trigger = TRUE) switch(status) if(LIGHT_BROKEN, LIGHT_BURNED, LIGHT_EMPTY) on = FALSE @@ -287,12 +327,12 @@ log_admin("LOG: Rigged light explosion, last touched by [fingerprintslast]") message_admins("LOG: Rigged light explosion, last touched by [fingerprintslast]") explode() - else if(prob(min(60, switchcount * switchcount * 0.01))) + // Whichever number is smallest gets set as the prob + // Each spook adds a 0.5% to 1% chance of burnout + else if(prob(min(40, switchcount / 10))) if(status == LIGHT_OK && trigger) - status = LIGHT_BURNED - icon_state = "[base_state]-burned" - on = FALSE - set_light(0) + burnout() + else use_power = ACTIVE_POWER_USE set_light(BR, PO, CO) @@ -301,18 +341,28 @@ set_light(0) active_power_usage = (brightness_range * 10) - if(on != on_gs) - on_gs = on + if(on != power_state) // Light was turned on/off, so update the power usage + power_state = on if(on) static_power_used = brightness_range * 20 //20W per unit luminosity addStaticPower(static_power_used, STATIC_LIGHT) else removeStaticPower(static_power_used, STATIC_LIGHT) +/obj/machinery/light/proc/burnout() + status = LIGHT_BURNED + icon_state = "[base_state]-burned" + + visible_message("[src] burns out!") + do_sparks(2, 1, src) + + on = FALSE + set_light(0) + // attempt to set the light's on/off status // will not switch on if broken/burned/empty -/obj/machinery/light/proc/seton(var/s) - on = (s && status == LIGHT_OK) +/obj/machinery/light/proc/seton(S) + on = (S && status == LIGHT_OK) update() // examine verb @@ -321,13 +371,13 @@ if(in_range(user, src)) switch(status) if(LIGHT_OK) - . += "[desc] It is turned [on? "on" : "off"]." + . += "It is turned [on? "on" : "off"]." if(LIGHT_EMPTY) - . += "[desc] The [fitting] has been removed." + . += "The [fitting] has been removed." if(LIGHT_BURNED) - . += "[desc] The [fitting] is burnt out." + . += "The [fitting] is burnt out." if(LIGHT_BROKEN) - . += "[desc] The [fitting] has been smashed." + . += "The [fitting] has been smashed." @@ -343,13 +393,13 @@ // attempt to insert light else if(istype(W, /obj/item/light)) if(status != LIGHT_EMPTY) - to_chat(user, "There is a [fitting] already inserted.") + to_chat(user, "There is a [fitting] already inserted.") else - src.add_fingerprint(user) + add_fingerprint(user) var/obj/item/light/L = W if(istype(L, light_type)) status = L.status - to_chat(user, "You insert the [L.name].") + to_chat(user, "You insert [L].") switchcount = L.switchcount rigged = L.rigged brightness_range = L.brightness_range @@ -369,7 +419,7 @@ explode() else - to_chat(user, "This type of light requires a [fitting].") + to_chat(user, "This type of light requires a [fitting].") return // attempt to break the light @@ -378,36 +428,36 @@ else if(status != LIGHT_BROKEN && status != LIGHT_EMPTY) user.do_attack_animation(src) - if(prob(1+W.force * 5)) + if(prob(1 + W.force * 5)) - to_chat(user, "You hit the light, and it smashes!") - for(var/mob/M in viewers(src)) - if(M == user) - continue - M.show_message("[user.name] smashed the light!", 3, "You hear a tinkle of breaking glass", 2) + user.visible_message("[user] smashed the light!", "You hit the light, and it smashes!", \ + "You hear the tinkle of breaking glass.") if(on && (W.flags & CONDUCT)) if(prob(12)) electrocute_mob(user, get_area(src), src, 0.3, TRUE) break_light_tube() else - user.visible_message("[user.name] hits the light.") - playsound(src.loc, 'sound/effects/glasshit.ogg', 75, 1) + user.visible_message("[user] hits the light.", "You hit the light.", \ + "You hear someone hitting a light.") + playsound(loc, 'sound/effects/glasshit.ogg', 75, 1) // attempt to stick weapon into light socket else if(status == LIGHT_EMPTY) if(istype(W, /obj/item/screwdriver)) //If it's a screwdriver open it. - playsound(src.loc, W.usesound, 75, 1) - user.visible_message("[user.name] opens [src]'s casing.", \ - "You open [src]'s casing.", "You hear a noise.") + playsound(loc, W.usesound, W.tool_volume, 1) + user.visible_message("[user] opens [src]'s casing.", \ + "You open [src]'s casing.", "You hear a screwdriver.") deconstruct() return - to_chat(user, "You stick \the [W] into the light socket!") if(has_power() && (W.flags & CONDUCT)) do_sparks(3, 1, src) - if(prob(75)) + if(prob(75)) // If electrocuted electrocute_mob(user, get_area(src), src, rand(0.7, 1), TRUE) + to_chat(user, "You are electrocuted by [src]!") + else // If not electrocuted + to_chat(user, "You stick [W] into the light socket!") else return ..() @@ -419,13 +469,13 @@ cur_stage = 1 switch(fitting) if("tube") - newlight = new /obj/machinery/light_construct(src.loc) + newlight = new /obj/machinery/light_construct(loc) newlight.icon_state = "tube-construct-stage2" if("bulb") - newlight = new /obj/machinery/light_construct/small(src.loc) + newlight = new /obj/machinery/light_construct/small(loc) newlight.icon_state = "bulb-construct-stage2" - newlight.setDir(src.dir) + newlight.setDir(dir) newlight.stage = cur_stage if(!disassembled) newlight.obj_integrity = newlight.max_integrity * 0.5 @@ -461,7 +511,7 @@ else playsound(loc, 'sound/effects/glasshit.ogg', 90, TRUE) if(BURN) - playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) + playsound(loc, 'sound/items/welder.ogg', 100, TRUE) // returns whether this light has power // true if area has power and lightswitch is on @@ -469,23 +519,25 @@ var/area/A = get_area(src) return A.lightswitch && A.power_light -/obj/machinery/light/proc/flicker(var/amount = rand(10, 20)) - if(flickering) return - flickering = 1 - spawn(0) - if(on && status == LIGHT_OK) - for(var/i = 0; i < amount; i++) - if(status != LIGHT_OK) break - on = !on - update(0) - sleep(rand(5, 15)) - on = (status == LIGHT_OK) - update(0) - flickering = 0 +/obj/machinery/light/proc/flicker(amount = rand(10, 20)) + if(flickering) + return + if(on) + flickering = TRUE + for(var/i in 1 to amount) + if(status != LIGHT_OK) + break + on = !on + update() // No param means that ghosts can burn out lights + sleep(rand(5, 15)) + if(status == LIGHT_OK) + on = TRUE + update(FALSE) + flickering = FALSE // ai attack - make lights flicker, because why not /obj/machinery/light/attack_ai(mob/user) - src.flicker(1) + flicker(1) // attack with hand - remove tube/bulb // if hands aren't protected and the light is on, burn the player @@ -495,7 +547,7 @@ add_fingerprint(user) if(status == LIGHT_EMPTY) - to_chat(user, "There is no [fitting] in this light.") + to_chat(user, "There is no [fitting] in this light.") return // make it burn hands if not wearing fire-insulated gloves @@ -512,23 +564,23 @@ prot = 1 if(prot > 0 || (HEATRES in user.mutations)) - to_chat(user, "You remove the light [fitting]") + to_chat(user, "You remove the light [fitting]") else if(TK in user.mutations) - to_chat(user, "You telekinetically remove the light [fitting].") + to_chat(user, "You telekinetically remove the light [fitting].") else if(user.a_intent == INTENT_DISARM || user.a_intent == INTENT_GRAB) - to_chat(user, "You try to remove the light [fitting], but you burn your hand on it!") + to_chat(user, "You try to remove the light [fitting], but you burn your hand on it!") var/obj/item/organ/external/affecting = H.get_organ("[user.hand ? "l" : "r" ]_hand") - if(affecting.receive_damage( 0, 5 )) // 5 burn damage + if(affecting.receive_damage(0, 5)) // 5 burn damage H.UpdateDamageIcon() H.updatehealth() return else - to_chat(user, "You try to remove the light [fitting], but it's too hot to touch!") + to_chat(user, "You try to remove the light [fitting], but it's too hot to touch!") return else - to_chat(user, "You remove the light [fitting].") + to_chat(user, "You remove the light [fitting]") // create a light tube/bulb item and put it in the user's hand drop_light_tube(user) @@ -555,7 +607,7 @@ user.put_in_active_hand(L) status = LIGHT_EMPTY - update() + update(FALSE) return L /obj/machinery/light/attack_tk(mob/user) @@ -568,13 +620,13 @@ var/obj/item/light/L = drop_light_tube() L.attack_tk(user) -/obj/machinery/light/proc/break_light_tube(skip_sound_and_sparks = 0, overloaded = 0) +/obj/machinery/light/proc/break_light_tube(skip_sound_and_sparks = FALSE, overloaded = FALSE) if(status == LIGHT_EMPTY || status == LIGHT_BROKEN) return if(!skip_sound_and_sparks) if(status == LIGHT_OK || status == LIGHT_BURNED) - playsound(src.loc, 'sound/effects/glasshit.ogg', 75, 1) + playsound(loc, 'sound/effects/glasshit.ogg', 75, 1) if(on || overloaded) do_sparks(3, 1, src) status = LIGHT_BROKEN @@ -611,30 +663,39 @@ // explode the light /obj/machinery/light/proc/explode() - var/turf/T = get_turf(src.loc) - spawn(0) - break_light_tube() // break it first to give a warning - sleep(2) - explosion(T, 0, 0, 2, 2) - sleep(1) - qdel(src) + var/turf/T = get_turf(loc) + break_light_tube() // break it first to give a warning + sleep(2) + explosion(T, 0, 0, 2, 2) + qdel(src) -// the light item -// can be tube or bulb subtypes -// will fit into empty /obj/machinery/light of the corresponding type +/** + * # Light item + * + * Parent type of light fittings (Light bulbs, light tubes) + * + * Will fit into empty [/obj/machinery/light] of the corresponding type + */ /obj/item/light icon = 'icons/obj/lighting.dmi' force = 2 throwforce = 5 w_class = WEIGHT_CLASS_TINY - var/status = 0 // LIGHT_OK, LIGHT_BURNED or LIGHT_BROKEN + /// Light status (LIGHT_OK | LIGHT_BURNED | LIGHT_BROKEN) + var/status = LIGHT_OK var/base_state - var/switchcount = 0 // number of times switched + /// How many times has the light been switched on/off? + var/switchcount = 0 + /// Materials the light is made of materials = list(MAT_GLASS=100) - var/rigged = 0 // true if rigged to explode - var/brightness_range = 2 //how much light it gives off + /// Is the light rigged to explode? + var/rigged = FALSE + /// Light range + var/brightness_range = 2 + /// Light intensity var/brightness_power = 1 + /// Light colour var/brightness_color = null /obj/item/light/ComponentInitialize() @@ -656,6 +717,11 @@ qdel(src) return TRUE +/** + * # Light Tube + * + * For use in an empty [/obj/machinery/light] + */ /obj/item/light/tube name = "light tube" desc = "A replacement light tube." @@ -670,6 +736,11 @@ brightness_range = 15 brightness_power = 2 +/** + * # Light Bulb + * + * For use in an empty [/obj/machinery/light/small] + */ /obj/item/light/bulb name = "light bulb" desc = "A replacement light bulb." @@ -722,17 +793,21 @@ if(istype(I, /obj/item/reagent_containers/syringe)) var/obj/item/reagent_containers/syringe/S = I - to_chat(user, "You inject the solution into the [src].") + if(!length(S.reagents.reagent_list)) + return if(S.reagents.has_reagent("plasma", 5) || S.reagents.has_reagent("plasma_dust", 5)) - + to_chat(user, "You inject the solution into [src], rigging it to explode!") log_admin("LOG: [key_name(user)] injected a light with plasma, rigging it to explode.") message_admins("LOG: [key_name_admin(user)] injected a light with plasma, rigging it to explode.") - rigged = 1 + rigged = TRUE + S.reagents.clear_reagents() - S.reagents.clear_reagents() - else + else // If it has a reagent, but it's not plasma + to_chat(user, "You fail to rig [src] with the solution.") + + else // If it's not a syringe return ..() /obj/item/light/attack(mob/living/M, mob/living/user, def_zone) @@ -745,11 +820,11 @@ /obj/item/light/proc/shatter() if(status == LIGHT_OK || status == LIGHT_BURNED) - src.visible_message("[name] shatters.","You hear a small glass object shatter.") + visible_message("[src] shatters.", "You hear a small glass object shatter.") status = LIGHT_BROKEN force = 5 - sharp = 1 - playsound(src.loc, 'sound/effects/glasshit.ogg', 75, 1) + sharp = TRUE + playsound(loc, 'sound/effects/glasshit.ogg', 75, 1) update() /obj/item/light/suicide_act(mob/living/carbon/human/user) @@ -764,4 +839,4 @@ /obj/machinery/light/extinguish_light() on = FALSE visible_message("[src] flickers and falls dark.") - update(0) + update(FALSE)