diff --git a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm index 7d37863a3d..06d1bce437 100644 --- a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm +++ b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm @@ -19,7 +19,7 @@ var/hibernate = 0 //Do we even process? var/scrubbing = 1 //0 = siphoning, 1 = scrubbing - var/list/scrubbing_gas = list("carbon_dioxide") + var/list/scrubbing_gas = list("carbon_dioxide", "phoron") var/panic = 0 //is this scrubber panicked? diff --git a/code/__defines/lighting.dm b/code/__defines/lighting.dm index 56c95535fe..f311ea4aa2 100644 --- a/code/__defines/lighting.dm +++ b/code/__defines/lighting.dm @@ -4,3 +4,48 @@ for(type in view(range, dview_mob)) #define END_FOR_DVIEW dview_mob.loc = null + +#define LIGHTING_FALLOFF 1 // type of falloff to use for lighting; 1 for circular, 2 for square +#define LIGHTING_LAMBERTIAN 0 // use lambertian shading for light sources +#define LIGHTING_HEIGHT 1 // height off the ground of light sources on the pseudo-z-axis, you should probably leave this alone + +#define LIGHTING_LAYER 10 // drawing layer for lighting overlays +#define LIGHTING_ICON 'icons/effects/lighting_overlay.dmi' // icon used for lighting shading effects +#define LIGHTING_ICON_STATE_DARK "soft_dark" // Change between "soft_dark" and "dark" to swap soft darkvision + +#define LIGHTING_ROUND_VALUE (1 / 64) // Value used to round lumcounts, values smaller than 1/69 don't matter (if they do, thanks sinking points), greater values will make lighting less precise, but in turn increase performance, VERY SLIGHTLY. + +#define LIGHTING_SOFT_THRESHOLD 0.05 // If the max of the lighting lumcounts of each spectrum drops below this, disable luminosity on the lighting overlays. This also should be the transparancy of the "soft_dark" icon state. + +// If I were you I'd leave this alone. +#define LIGHTING_BASE_MATRIX \ + list \ + ( \ + LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \ + LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \ + LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \ + LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \ + 0, 0, 0, 1 \ + ) \ + +// Helpers so we can (more easily) control the colour matrices. +#define CL_MATRIX_RR 1 +#define CL_MATRIX_RG 2 +#define CL_MATRIX_RB 3 +#define CL_MATRIX_RA 4 +#define CL_MATRIX_GR 5 +#define CL_MATRIX_GG 6 +#define CL_MATRIX_GB 7 +#define CL_MATRIX_GA 8 +#define CL_MATRIX_BR 9 +#define CL_MATRIX_BG 10 +#define CL_MATRIX_BB 11 +#define CL_MATRIX_BA 12 +#define CL_MATRIX_AR 13 +#define CL_MATRIX_AG 14 +#define CL_MATRIX_AB 15 +#define CL_MATRIX_AA 16 +#define CL_MATRIX_CR 17 +#define CL_MATRIX_CG 18 +#define CL_MATRIX_CB 19 +#define CL_MATRIX_CA 20 diff --git a/code/__defines/machinery.dm b/code/__defines/machinery.dm index 6f8bec025b..cae68c3a23 100644 --- a/code/__defines/machinery.dm +++ b/code/__defines/machinery.dm @@ -3,6 +3,10 @@ var/global/defer_powernet_rebuild = 0 // True if net rebuild will be called #define CELLRATE 0.002 // Multiplier for watts per tick <> cell storage (e.g., 0.02 means if there is a load of 1000 watts, 20 units will be taken from a cell per second) // It's a conversion constant. power_used*CELLRATE = charge_provided, or charge_used/CELLRATE = power_provided +#define KILOWATTS *1000 +#define MEGAWATTS *1000000 +#define GIGAWATTS *1000000000 + // Doors! #define DOOR_CRUSH_DAMAGE 20 #define ALIEN_SELECT_AFK_BUFFER 1 // How many minutes that a person can be AFK before not being allowed to be an alien. diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index d244916ec2..6fa9d4ec0c 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -952,7 +952,7 @@ proc/DuplicateObject(obj/original, var/perfectcopy = 0 , var/sameloc = 0) // var/area/AR = X.loc -// if(AR.lighting_use_dynamic) +// if(AR.dynamic_lighting) // X.opacity = !X.opacity // X.sd_SetOpacity(!X.opacity) //TODO: rewrite this code so it's not messed by lighting ~Carn @@ -1299,3 +1299,16 @@ var/mob/dview/dview_mob = new tY = max(1, min(world.maxy, origin.y + (text2num(tY) - (world.view + 1)))) return locate(tX, tY, tZ) +// Displays something as commonly used (non-submultiples) SI units. +/proc/format_SI(var/number, var/symbol) + switch(round(abs(number))) + if(0 to 1000-1) + return "[number] [symbol]" + if(1e3 to 1e6-1) + return "[round(number / 1000, 0.1)] k[symbol]" // kilo + if(1e6 to 1e9-1) + return "[round(number / 1e6, 0.1)] M[symbol]" // mega + if(1e9 to 1e12-1) // Probably not needed but why not be complete? + return "[round(number / 1e9, 0.1)] G[symbol]" // giga + if(1e12 to 1e15-1) + return "[round(number / 1e12, 0.1)] T[symbol]" // tera diff --git a/code/controllers/Processes/lighting.dm b/code/controllers/Processes/lighting.dm new file mode 100644 index 0000000000..d46cab1d19 --- /dev/null +++ b/code/controllers/Processes/lighting.dm @@ -0,0 +1,98 @@ +/var/lighting_overlays_initialised = FALSE + +/var/list/lighting_update_lights = list() // List of lighting sources queued for update. +/var/list/lighting_update_corners = list() // List of lighting corners queued for update. +/var/list/lighting_update_overlays = list() // List of lighting overlays queued for update. + +/var/list/lighting_update_lights_old = list() // List of lighting sources currently being updated. +/var/list/lighting_update_corners_old = list() // List of lighting corners currently being updated. +/var/list/lighting_update_overlays_old = list() // List of lighting overlays currently being updated. + + +/datum/controller/process/lighting + // Queues of update counts, waiting to be rolled into stats lists + var/list/stats_queues = list( + "Source" = list(), "Corner" = list(), "Overlay" = list()) + // Stats lists + var/list/stats_lists = list( + "Source" = list(), "Corner" = list(), "Overlay" = list()) + var/update_stats_every = (1 SECONDS) + var/next_stats_update = 0 + var/stat_updates_to_keep = 5 + +/datum/controller/process/lighting/setup() + name = "lighting" + + schedule_interval = 0 // run as fast as you possibly can + sleep_interval = 10 // Yield every 10% of a tick + defer_usage = 80 // Defer at 80% of a tick + create_all_lighting_overlays() + lighting_overlays_initialised = TRUE + + // Pre-process lighting once before the round starts. Wait 30 seconds so the away mission has time to load. + spawn(300) + doWork(1) + +/datum/controller/process/lighting/doWork(roundstart) + + lighting_update_lights_old = lighting_update_lights //We use a different list so any additions to the update lists during a delay from scheck() don't cause things to be cut from the list without being updated. + lighting_update_lights = list() + for(var/datum/light_source/L in lighting_update_lights_old) + + if(L.check() || L.destroyed || L.force_update) + L.remove_lum() + if(!L.destroyed) + L.apply_lum() + + else if(L.vis_update) //We smartly update only tiles that became (in) visible to use. + L.smart_vis_update() + + L.vis_update = FALSE + L.force_update = FALSE + L.needs_update = FALSE + + SCHECK + + lighting_update_corners_old = lighting_update_corners //Same as above. + lighting_update_corners = list() + for(var/A in lighting_update_corners_old) + var/datum/lighting_corner/C = A + + C.update_overlays() + + C.needs_update = FALSE + + SCHECK + + lighting_update_overlays_old = lighting_update_overlays //Same as above. + lighting_update_overlays = list() + + for(var/A in lighting_update_overlays_old) + var/atom/movable/lighting_overlay/O = A + O.update_overlay() + O.needs_update = 0 + SCHECK + + stats_queues["Source"] += lighting_update_lights_old.len + stats_queues["Corner"] += lighting_update_corners_old.len + stats_queues["Overlay"] += lighting_update_overlays_old.len + + if(next_stats_update <= world.time) + next_stats_update = world.time + update_stats_every + for(var/stat_name in stats_queues) + var/stat_sum = 0 + var/list/stats_queue = stats_queues[stat_name] + for(var/count in stats_queue) + stat_sum += count + stats_queue.Cut() + + var/list/stats_list = stats_lists[stat_name] + stats_list.Insert(1, stat_sum) + if(stats_list.len > stat_updates_to_keep) + stats_list.Cut(stats_list.len) + +/datum/controller/process/lighting/statProcess() + ..() + stat(null, "[total_lighting_sources] sources, [total_lighting_corners] corners, [total_lighting_overlays] overlays") + for(var/stat_type in stats_lists) + stat(null, "[stat_type] updates: [jointext(stats_lists[stat_type], " | ")]") diff --git a/code/controllers/Processes/scheduler.dm b/code/controllers/Processes/scheduler.dm index fdbe55faed..276249bd55 100644 --- a/code/controllers/Processes/scheduler.dm +++ b/code/controllers/Processes/scheduler.dm @@ -25,6 +25,21 @@ catchException(e, last_object) SCHECK +// We've been restarted, probably due to having a massive list of tasks. +// Lets copy over the task list as safely as we can and try to chug thru it... +// Note: We won't be informed about tasks being destroyed, but this is the best we can do. +/datum/controller/process/scheduler/copyStateFrom(var/datum/controller/process/scheduler/target) + scheduled_tasks = list() + for(var/st in target.scheduled_tasks) + if(!deleted(st) && istype(st, /datum/scheduled_task)) + schedule(st) + scheduler = src + +// We are being killed. Least we can do is deregister all those events we registered +/datum/controller/process/scheduler/onKill() + for(var/st in scheduled_tasks) + destroyed_event.unregister(st, src) + /datum/controller/process/scheduler/statProcess() ..() stat(null, "[scheduled_tasks.len] task\s") @@ -130,4 +145,4 @@ /proc/repeat_scheduled_task(var/trigger_delay, var/datum/scheduled_task/st) st.trigger_time = world.time + trigger_delay - scheduler.schedule(st) \ No newline at end of file + scheduler.schedule(st) diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index fd504946e6..68361efb45 100755 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -100,7 +100,7 @@ var/list/ghostteleportlocs = list() icon_state = "space" requires_power = 1 always_unpowered = 1 - lighting_use_dynamic = 1 + dynamic_lighting = 1 power_light = 0 power_equip = 0 power_environ = 0 @@ -293,7 +293,7 @@ area/space/atmosalert() /area/shuttle/mining name = "\improper Mining Elevator" music = "music/escape.ogg" - lighting_use_dynamic = 0 + dynamic_lighting = 0 base_turf = /turf/simulated/mineral/floor/ignore_mapgen /area/shuttle/mining/station @@ -392,7 +392,7 @@ area/space/atmosalert() /area/shuttle/research name = "\improper Research Elevator" music = "music/escape.ogg" - lighting_use_dynamic = 0 + dynamic_lighting = 0 base_turf = /turf/simulated/mineral/floor/ignore_mapgen /area/shuttle/research/station @@ -418,7 +418,7 @@ area/space/atmosalert() name = "\improper CentCom" icon_state = "centcom" requires_power = 0 - lighting_use_dynamic = 0 + dynamic_lighting = 0 /area/centcom/control name = "\improper CentCom Control" @@ -491,7 +491,7 @@ area/space/atmosalert() name = "\improper Mercenary Base" icon_state = "syndie-ship" requires_power = 0 - lighting_use_dynamic = 0 + dynamic_lighting = 0 /area/syndicate_mothership/control name = "\improper Mercenary Control Room" @@ -543,7 +543,7 @@ area/space/atmosalert() name = "\improper Thunderdome" icon_state = "thunder" requires_power = 0 - lighting_use_dynamic = 0 + dynamic_lighting = 0 sound_env = ARENA /area/tdome/tdome1 @@ -624,7 +624,7 @@ area/space/atmosalert() name = "\improper Wizard's Den" icon_state = "yellow" requires_power = 0 - lighting_use_dynamic = 0 + dynamic_lighting = 0 /area/skipjack_station name = "\improper Skipjack" @@ -1485,7 +1485,7 @@ area/space/atmosalert() /area/holodeck name = "\improper Holodeck" icon_state = "Holodeck" - lighting_use_dynamic = 0 + dynamic_lighting = 0 sound_env = LARGE_ENCLOSED /area/holodeck/alphadeck @@ -1638,7 +1638,7 @@ area/space/atmosalert() /area/solar requires_power = 1 always_unpowered = 1 - lighting_use_dynamic = 0 + dynamic_lighting = 0 base_turf = /turf/space auxport @@ -2336,7 +2336,7 @@ area/space/atmosalert() /area/shuttle/constructionsite name = "\improper Construction Site Shuttle" icon_state = "yellow" - lighting_use_dynamic = 0 + dynamic_lighting = 0 base_turf = /turf/simulated/mineral/floor/ignore_mapgen /area/shuttle/constructionsite/station @@ -2493,25 +2493,25 @@ area/space/atmosalert() name = "\improper AI Sat Ext" icon_state = "storage" luminosity = 1 - lighting_use_dynamic = 0 + dynamic_lighting = 0 /area/AIsatextFS name = "\improper AI Sat Ext" icon_state = "storage" luminosity = 1 - lighting_use_dynamic = 0 + dynamic_lighting = 0 /area/AIsatextAS name = "\improper AI Sat Ext" icon_state = "storage" luminosity = 1 - lighting_use_dynamic = 0 + dynamic_lighting = 0 /area/AIsatextAP name = "\improper AI Sat Ext" icon_state = "storage" luminosity = 1 - lighting_use_dynamic = 0 + dynamic_lighting = 0 /area/NewAIMain name = "\improper AI Main New" @@ -2690,7 +2690,7 @@ area/space/atmosalert() name = "Beach" icon_state = "null" luminosity = 1 - lighting_use_dynamic = 0 + dynamic_lighting = 0 requires_power = 0 ambience = list() // Vorestation Removal - This is very broken. Sounds do not stop when you leave the area. @@ -2813,7 +2813,7 @@ var/list/the_station_areas = list ( name = "Keelin's private beach" icon_state = "null" luminosity = 1 - lighting_use_dynamic = 0 + dynamic_lighting = 0 requires_power = 0 var/sound/mysound = null /* diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 027a462070..505c3976a9 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -18,7 +18,7 @@ power_equip = 0 power_environ = 0 - if(lighting_use_dynamic) + if(dynamic_lighting) luminosity = 0 else luminosity = 1 diff --git a/code/game/area/asteroid_areas.dm b/code/game/area/asteroid_areas.dm index 68f9807ead..66a7e86913 100644 --- a/code/game/area/asteroid_areas.dm +++ b/code/game/area/asteroid_areas.dm @@ -126,7 +126,7 @@ /area/outpost/engineering/solarsoutside requires_power = 1 always_unpowered = 1 - lighting_use_dynamic = 0 + dynamic_lighting = 0 aft name = "\improper Engineering Outpost Solar Array" diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index b76620fa95..b511c9befd 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -40,7 +40,13 @@ reagents = null for(var/atom/movable/AM in contents) qdel(AM) + var/turf/un_opaque + if(opacity && isturf(loc)) + un_opaque = loc + loc = null + if(un_opaque) + un_opaque.recalc_atom_opacity() if (pulledby) if (pulledby.pulling == src) pulledby.pulling = null diff --git a/code/game/gamemodes/changeling/powers/panacea.dm b/code/game/gamemodes/changeling/powers/panacea.dm index 055cf3e28b..660434d185 100644 --- a/code/game/gamemodes/changeling/powers/panacea.dm +++ b/code/game/gamemodes/changeling/powers/panacea.dm @@ -28,6 +28,7 @@ C.reagents.clear_reagents() C.ingested.clear_reagents() + var/heal_amount = 5 if(src.mind.changeling.recursive_enhancement) heal_amount = heal_amount * 2 @@ -38,5 +39,17 @@ C.adjustToxLoss(-heal_amount) sleep(10) + for(var/obj/item/organ/external/E in C.organs) + var/obj/item/organ/external/G = E + if(G.germ_level) + var/germ_heal = heal_amount * 100 + G.germ_level = min(0, G.germ_level - germ_heal) + + for(var/obj/item/organ/internal/I in C.internal_organs) + var/obj/item/organ/internal/G = I + if(G.germ_level) + var/germ_heal = heal_amount * 100 + G.germ_level = min(0, G.germ_level - germ_heal) + feedback_add_details("changeling_powers","AP") return 1 \ No newline at end of file diff --git a/code/game/gamemodes/cult/hell_universe.dm b/code/game/gamemodes/cult/hell_universe.dm index 496af98ef8..75c97d5cee 100644 --- a/code/game/gamemodes/cult/hell_universe.dm +++ b/code/game/gamemodes/cult/hell_universe.dm @@ -62,7 +62,7 @@ In short: /datum/universal_state/hell/OverlayAndAmbientSet() spawn(0) - for(var/atom/movable/lighting_overlay/L in world) + for(var/datum/lighting_corner/L in world) L.update_lumcount(1, 0, 0) for(var/turf/space/T in turfs) diff --git a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm index 11c61cfed6..6f549d15f4 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm +++ b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm @@ -93,7 +93,7 @@ The access requirements on the Asteroid Shuttles' consoles have now been revoked /datum/universal_state/supermatter_cascade/OverlayAndAmbientSet() spawn(0) - for(var/atom/movable/lighting_overlay/L in world) + for(var/datum/lighting_corner/L in world) if(L.z in using_map.admin_levels) L.update_lumcount(1,1,1) else diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index bd056f7c69..ad8b89ec80 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -80,6 +80,10 @@ var/turf/T = locate(endx, endy, Z) return T +// Override for special behavior when getting hit by meteors, and only meteors. Return one if the meteor hasn't been 'stopped'. +/atom/proc/handle_meteor_impact(var/obj/effect/meteor/meteor) + return TRUE + /////////////////////// //The meteor effect ////////////////////// @@ -101,6 +105,11 @@ var/meteordrop = /obj/item/weapon/ore/iron var/dropamt = 2 + // How much damage it does to walls, using take_damage(). + // Normal walls will die to 150 or more, where as reinforced walls need 800 to penetrate. Durasteel walls need 1200 damage to go through. + // Multiply this and the hits var to get a rough idea of how penetrating a meteor is. + var/wall_power = 100 + /obj/effect/meteor/New() ..() z_original = z @@ -132,8 +141,11 @@ /obj/effect/meteor/Bump(atom/A) if(A) - ram_turf(get_turf(A)) - get_hit() + if(A.handle_meteor_impact(src)) // Used for special behaviour when getting hit specifically by a meteor, like a shield. + ram_turf(get_turf(A)) + get_hit() + else + die(0) /obj/effect/meteor/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) return istype(mover, /obj/effect/meteor) ? 1 : ..() @@ -146,7 +158,11 @@ //then, ram the turf if it still exists if(T) - T.ex_act(hitpwr) + if(istype(T, /turf/simulated/wall)) + var/turf/simulated/wall/W = T + W.take_damage(wall_power) // Stronger walls can halt asteroids. + else + T.ex_act(hitpwr) // Floors and other things lack fancy health. //process getting 'hit' by colliding with a dense object @@ -154,9 +170,12 @@ /obj/effect/meteor/proc/get_hit() hits-- if(hits <= 0) - make_debris() - meteor_effect() - qdel(src) + die(1) + +/obj/effect/meteor/proc/die(var/explode = 1) + make_debris() + meteor_effect(explode) + qdel(src) /obj/effect/meteor/ex_act() return @@ -172,21 +191,24 @@ var/obj/item/O = new meteordrop(get_turf(src)) O.throw_at(dest, 5, 10) -/obj/effect/meteor/proc/meteor_effect() +/obj/effect/meteor/proc/shake_players() + for(var/mob/M in player_list) + var/turf/T = get_turf(M) + if(!T || T.z != src.z) + continue + var/dist = get_dist(M.loc, src.loc) + shake_camera(M, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3) + +/obj/effect/meteor/proc/meteor_effect(var/explode) if(heavy) - for(var/mob/M in player_list) - var/turf/T = get_turf(M) - if(!T || T.z != src.z) - continue - var/dist = get_dist(M.loc, src.loc) - shake_camera(M, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3) + shake_players() /////////////////////// //Meteor types /////////////////////// -//Dust +// Dust breaks windows and hurts normal walls, generally more of an annoyance than a danger unless two happen to hit the same spot. /obj/effect/meteor/dust name = "space dust" icon_state = "dust" @@ -194,63 +216,74 @@ hits = 1 hitpwr = 3 meteordrop = /obj/item/weapon/ore/glass + wall_power = 50 -//Medium-sized +// Medium-sized meteors aren't very special and can be stopped easily by r-walls. /obj/effect/meteor/medium name = "meteor" dropamt = 3 + wall_power = 200 -/obj/effect/meteor/medium/meteor_effect() +/obj/effect/meteor/medium/meteor_effect(var/explode) ..() - explosion(src.loc, 0, 1, 2, 3, 0) + if(explode) + explosion(src.loc, 0, 1, 2, 3, 0) -//Large-sized +// Large-sized meteors generally pack the most punch, but are more concentrated towards the epicenter. /obj/effect/meteor/big name = "large meteor" icon_state = "large" - hits = 6 + hits = 8 heavy = 1 dropamt = 4 + wall_power = 400 -/obj/effect/meteor/big/meteor_effect() +/obj/effect/meteor/big/meteor_effect(var/explode) ..() - explosion(src.loc, 1, 2, 3, 4, 0) + if(explode) + explosion(src.loc, devastation_range = 2, heavy_impact_range = 4, light_impact_range = 6, flash_range = 12, adminlog = 0) -//Flaming meteor +// 'Flaming' meteors do less overall damage but are spread out more due to a larger but weaker explosion at the end. /obj/effect/meteor/flaming name = "flaming meteor" icon_state = "flaming" hits = 5 heavy = 1 meteordrop = /obj/item/weapon/ore/phoron + wall_power = 100 -/obj/effect/meteor/flaming/meteor_effect() +/obj/effect/meteor/flaming/meteor_effect(var/explode) ..() - explosion(src.loc, 1, 2, 3, 4, 0, 0, 5) + if(explode) + explosion(src.loc, devastation_range = 1, heavy_impact_range = 2, light_impact_range = 8, flash_range = 16, adminlog = 0) -//Radiation meteor +// Irradiated meteors do less physical damage but project a ten-tile ranged pulse of radiation upon exploding. /obj/effect/meteor/irradiated name = "glowing meteor" icon_state = "glowing" heavy = 1 meteordrop = /obj/item/weapon/ore/uranium + wall_power = 75 -/obj/effect/meteor/irradiated/meteor_effect() +/obj/effect/meteor/irradiated/meteor_effect(var/explode) ..() - explosion(src.loc, 0, 0, 4, 3, 0) + if(explode) + explosion(src.loc, devastation_range = 0, heavy_impact_range = 0, light_impact_range = 4, flash_range = 6, adminlog = 0) new /obj/effect/decal/cleanable/greenglow(get_turf(src)) - for(var/mob/living/L in view(5, src)) + for(var/mob/living/L in view(10, src)) L.apply_effect(40, IRRADIATE) +// This meteor fries toasters. /obj/effect/meteor/emp name = "conducting meteor" icon_state = "glowing_blue" desc = "Hide your floppies!" meteordrop = /obj/item/weapon/ore/osmium dropamt = 3 + wall_power = 80 -/obj/effect/meteor/emp/meteor_effect() +/obj/effect/meteor/emp/meteor_effect(var/explode) ..() // Best case scenario: Comparable to a low-yield EMP grenade. // Worst case scenario: Comparable to a standard yield EMP grenade. @@ -265,10 +298,12 @@ hitpwr = 1 heavy = 1 meteordrop = /obj/item/weapon/ore/phoron + wall_power = 150 -/obj/effect/meteor/tunguska/meteor_effect() +/obj/effect/meteor/tunguska/meteor_effect(var/explode) ..() - explosion(src.loc, 5, 10, 15, 20, 0) + if(explode) + explosion(src.loc, 5, 10, 15, 20, 0) /obj/effect/meteor/tunguska/Bump() ..() diff --git a/code/game/gamemodes/technomancer/spells/insert/purify.dm b/code/game/gamemodes/technomancer/spells/insert/purify.dm index b1e369f6e8..6ba36d44c6 100644 --- a/code/game/gamemodes/technomancer/spells/insert/purify.dm +++ b/code/game/gamemodes/technomancer/spells/insert/purify.dm @@ -32,5 +32,18 @@ H.adjustToxLoss(-heal_power / 5) H.adjustCloneLoss(-heal_power / 5) H.radiation = max(host.radiation - ( (heal_power * 2) / 5), 0) + + for(var/obj/item/organ/external/E in H.organs) + var/obj/item/organ/external/G = E + if(G.germ_level) + var/germ_heal = heal_power * 10 + G.germ_level = min(0, G.germ_level - germ_heal) + + for(var/obj/item/organ/internal/I in H.internal_organs) + var/obj/item/organ/internal/G = I + if(G.germ_level) + var/germ_heal = heal_power * 10 + G.germ_level = min(0, G.germ_level - germ_heal) + sleep(1 SECOND) on_expire() diff --git a/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm b/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm index 5bb032b9c7..c6e5a2804d 100644 --- a/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm +++ b/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm @@ -83,11 +83,8 @@ continue if(!true_sight) - var/atom/movable/lighting_overlay/light = locate(/atom/movable/lighting_overlay) in get_turf(L) - var/light_amount = 1 // Unsimulated tiles are pretend-lit, so we need to be pretend too if that somehow happens. - if(light) - light_amount = (light.lum_r + light.lum_g + light.lum_b) / 3 - + var/turf/T = get_turf(L) + var/light_amount = T.get_lumcount() if(light_amount <= 0.5) continue // Too dark to see. diff --git a/code/game/jobs/job/civilian.dm b/code/game/jobs/job/civilian.dm index 3a57b35f0a..1e4398546c 100644 --- a/code/game/jobs/job/civilian.dm +++ b/code/game/jobs/job/civilian.dm @@ -26,7 +26,12 @@ H.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(H), slot_shoes) H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/bartender(H), slot_w_uniform) H.equip_to_slot_or_del(new /obj/item/device/pda/bar(H), slot_belt) - return 1 + if(has_alt_title(H, alt_title,"Bartender")) + if(H.backbag == 1) + H.equip_to_slot_or_del(new /obj/item/weapon/permit/gun/bar(H), slot_l_hand) + else + H.equip_to_slot_or_del(new /obj/item/weapon/permit/gun/bar(H.back), slot_in_backpack) + return 1 diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index 9af5de87d4..d4f5dac431 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -93,7 +93,7 @@ req_access = list(access_rd, access_atmospherics, access_engine_equip) TLV["oxygen"] = list(-1.0, -1.0,-1.0,-1.0) // Partial pressure, kpa TLV["carbon dioxide"] = list(-1.0, -1.0, 5, 10) // Partial pressure, kpa - TLV["phoron"] = list(-1.0, -1.0, 0.2, 0.5) // Partial pressure, kpa + TLV["phoron"] = list(-1.0, -1.0, 0, 0.5) // Partial pressure, kpa TLV["other"] = list(-1.0, -1.0, 0.5, 1.0) // Partial pressure, kpa TLV["pressure"] = list(0,ONE_ATMOSPHERE*0.10,ONE_ATMOSPHERE*1.40,ONE_ATMOSPHERE*1.60) /* kpa */ TLV["temperature"] = list(20, 40, 140, 160) // K @@ -121,7 +121,7 @@ // breathable air according to human/Life() TLV["oxygen"] = list(16, 19, 135, 140) // Partial pressure, kpa TLV["carbon dioxide"] = list(-1.0, -1.0, 5, 10) // Partial pressure, kpa - TLV["phoron"] = list(-1.0, -1.0, 0.2, 0.5) // Partial pressure, kpa + TLV["phoron"] = list(-1.0, -1.0, 0, 0.5) // Partial pressure, kpa TLV["other"] = list(-1.0, -1.0, 0.5, 1.0) // Partial pressure, kpa TLV["pressure"] = list(ONE_ATMOSPHERE * 0.80, ONE_ATMOSPHERE * 0.90, ONE_ATMOSPHERE * 1.10, ONE_ATMOSPHERE * 1.20) /* kpa */ TLV["temperature"] = list(T0C - 26, T0C, T0C + 40, T0C + 66) // K @@ -758,6 +758,7 @@ user << "You [ locked ? "lock" : "unlock"] the Air Alarm interface." else user << "Access denied." + return return ..() /obj/machinery/alarm/power_change() diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index fb8b1899fc..6c0a8ebf9a 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -945,7 +945,7 @@ About the new airlock wires panel: healthcheck() /obj/effect/energy_field/airlock_crush(var/crush_damage) - Stress(crush_damage) + adjust_strength(crush_damage) /obj/structure/closet/airlock_crush(var/crush_damage) ..() diff --git a/code/game/objects/effects/chem/chemsmoke.dm b/code/game/objects/effects/chem/chemsmoke.dm index 3fa93e7e46..3f077116e9 100644 --- a/code/game/objects/effects/chem/chemsmoke.dm +++ b/code/game/objects/effects/chem/chemsmoke.dm @@ -162,9 +162,9 @@ smoke.pixel_x = -32 + rand(-8, 8) smoke.pixel_y = -32 + rand(-8, 8) walk_to(smoke, T) - smoke.opacity = 1 //switching opacity on after the smoke has spawned, and then + smoke.set_opacity(1) //switching opacity on after the smoke has spawned, and then sleep(150+rand(0,20)) // turning it off before it is deleted results in cleaner - smoke.opacity = 0 // lighting and view range updates + smoke.set_opacity(0) // lighting and view range updates fadeOut(smoke) qdel(src) diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index ed0056577b..47642603f7 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -66,7 +66,19 @@ var/global/list/obj/item/device/pda/PDAs = list() /obj/item/device/pda/examine(mob/user) if(..(user, 1)) - user << "The time [stationtime2text()] is displayed in the corner of the screen." + to_chat(user, "The time [stationtime2text()] is displayed in the corner of the screen.") + + +/obj/item/device/pda/AltClick() + if(issilicon(usr)) + return + + if ( can_use(usr) ) + if(id) + remove_id() + else + to_chat(usr, "This PDA does not have an ID in it.") + /obj/item/device/pda/medical default_cartridge = /obj/item/weapon/cartridge/medical @@ -247,7 +259,7 @@ var/global/list/obj/item/device/pda/PDAs = list() set name = "Send Message" set src in usr if(usr.stat == 2) - usr << "You can't send PDA messages because you are dead!" + to_chat(usr, "You can't send PDA messages because you are dead!") return var/list/plist = available_pdas() if (plist) @@ -262,28 +274,27 @@ var/global/list/obj/item/device/pda/PDAs = list() set name = "Toggle Sender/Receiver" set src in usr if(usr.stat == 2) - usr << "You can't do that because you are dead!" + to_chat(usr, "You can't send PDA messages because you are dead!") return toff = !toff - usr << "PDA sender/receiver toggled [(toff ? "Off" : "On")]!" + to_chat(usr, "PDA sender/receiver toggled [(toff ? "Off" : "On")]!") /obj/item/device/pda/ai/verb/cmd_toggle_pda_silent() set category = "AI IM" set name = "Toggle Ringer" set src in usr if(usr.stat == 2) - usr << "You can't do that because you are dead!" + to_chat(usr, "You can't send PDA messages because you are dead!") return message_silent=!message_silent - usr << "PDA ringer toggled [(message_silent ? "Off" : "On")]!" - + to_chat(usr, "PDA ringer toggled [(message_silent ? "Off" : "On")]!") /obj/item/device/pda/ai/verb/cmd_show_message_log() set category = "AI IM" set name = "Show Message Log" set src in usr if(usr.stat == 2) - usr << "You can't do that because you are dead!" + to_chat(usr, "You can't send PDA messages because you are dead!") return var/HTML = "AI PDA Message Log" for(var/index in tnote) @@ -809,7 +820,7 @@ var/global/list/obj/item/device/pda/PDAs = list() if (in_range(src, U) && loc == U) if (t) if(src.hidden_uplink && hidden_uplink.check_trigger(U, lowertext(t), lowertext(lock_code))) - U << "The PDA softly beeps." + to_chat(U, "The PDA softly beeps.") ui.close() else t = sanitize(t, 20) @@ -856,7 +867,7 @@ var/global/list/obj/item/device/pda/PDAs = list() U.show_message("Virus sent!", 1) P.honkamt = (rand(15,20)) else - U << "PDA not found." + to_chat(U, "PDA not found.") else ui.close() return 0 @@ -872,7 +883,7 @@ var/global/list/obj/item/device/pda/PDAs = list() P.ttone = "silence" P.newstone = "silence" else - U << "PDA not found." + to_chat(U, "PDA not found.") else ui.close() return 0 @@ -928,9 +939,10 @@ var/global/list/obj/item/device/pda/PDAs = list() message_admins("[key_name_admin(U)] just attempted to blow up [P] with the Detomatix cartridge and succeeded.", 1) detonate_act(P) else - U << "No charges left." + to_chat(U, "No charges left.") + else - U << "PDA not found." + to_chat(U, "PDA not found.") else U.unset_machine() ui.close() @@ -1048,7 +1060,7 @@ var/global/list/obj/item/device/pda/PDAs = list() if (ismob(loc)) var/mob/M = loc M.put_in_hands(id) - usr << "You remove the ID from the [name]." + to_chat(usr, "You remove the ID from the [name].") else id.loc = get_turf(src) id = null @@ -1080,11 +1092,11 @@ var/global/list/obj/item/device/pda/PDAs = list() if(reception.message_server && (reception.telecomms_reception & TELECOMMS_RECEPTION_SENDER)) // only send the message if it's stable if(reception.telecomms_reception & TELECOMMS_RECEPTION_RECEIVER == 0) // Does our recipient have a broadcaster on their level? - U << "ERROR: Cannot reach recipient." + to_chat(U, "ERROR: Cannot reach recipient.") return var/send_result = reception.message_server.send_pda_message("[P.owner]","[owner]","[t]") if (send_result) - U << "ERROR: Messaging server rejected your message. Reason: contains '[send_result]'." + to_chat(U, "ERROR: Messaging server rejected your message. Reason: contains '[send_result]'.") return tnote.Add(list(list("sent" = 1, "owner" = "[P.owner]", "job" = "[P.ownjob]", "message" = "[t]", "target" = "\ref[P]"))) @@ -1113,7 +1125,7 @@ var/global/list/obj/item/device/pda/PDAs = list() P.new_message_from_pda(src, t) nanomanager.update_user_uis(U, src) // Update the sending user's PDA UI so that they can see the new message else - U << "ERROR: Messaging server is not responding." + to_chat(U, "ERROR: Messaging server is not responding.") /obj/item/device/pda/proc/new_info(var/beep_silent, var/message_tone, var/reception_message) if (!beep_silent) @@ -1176,9 +1188,9 @@ var/global/list/obj/item/device/pda/PDAs = list() if(can_use(usr)) mode = 0 nanomanager.update_uis(src) - usr << "You press the reset button on \the [src]." + to_chat(usr, "You press the reset button on \the [src].") else - usr << "You cannot do this while restrained." + to_chat(usr, "You cannot do this while restrained.") /obj/item/device/pda/verb/verb_remove_id() set category = "Object" @@ -1192,9 +1204,9 @@ var/global/list/obj/item/device/pda/PDAs = list() if(id) remove_id() else - usr << "This PDA does not have an ID in it." + to_chat(usr, "This PDA does not have an ID in it.") else - usr << "You cannot do this while restrained." + to_chat(usr, "You cannot do this while restrained.") /obj/item/device/pda/verb/verb_remove_pen() @@ -1212,13 +1224,13 @@ var/global/list/obj/item/device/pda/PDAs = list() var/mob/M = loc if(M.get_active_hand() == null) M.put_in_hands(O) - usr << "You remove \the [O] from \the [src]." + to_chat(usr, "You remove \the [O] from \the [src].") return O.loc = get_turf(src) else - usr << "This PDA does not have a pen in it." + to_chat(usr, "This PDA does not have a pen in it.") else - usr << "You cannot do this while restrained." + to_chat(usr, "You cannot do this while restrained.") /obj/item/device/pda/verb/verb_remove_cartridge() set category = "Object" @@ -1241,9 +1253,9 @@ var/global/list/obj/item/device/pda/PDAs = list() if (cartridge.radio) cartridge.radio.hostpda = null cartridge = null - usr << "You remove \the [cartridge] from the [name]." + to_chat(usr, "You remove \the [cartridge] from the [name].") else - usr << "You cannot do this while restrained." + to_chat(usr, "You cannot do this while restrained.") /obj/item/device/pda/proc/id_check(mob/user as mob, choice as num)//To check for IDs; 1 for in-pda use, 2 for out of pda use. if(choice == 1) @@ -1273,7 +1285,7 @@ var/global/list/obj/item/device/pda/PDAs = list() cartridge = C user.drop_item() cartridge.loc = src - user << "You insert [cartridge] into [src]." + to_chat(usr, "You insert [cartridge] into [src].") nanomanager.update_uis(src) // update all UIs attached to src if(cartridge.radio) cartridge.radio.hostpda = src @@ -1281,19 +1293,19 @@ var/global/list/obj/item/device/pda/PDAs = list() else if(istype(C, /obj/item/weapon/card/id)) var/obj/item/weapon/card/id/idcard = C if(!idcard.registered_name) - user << "\The [src] rejects the ID." + to_chat(user, "\The [src] rejects the ID.") return if(!owner) owner = idcard.registered_name ownjob = idcard.assignment ownrank = idcard.rank name = "PDA-[owner] ([ownjob])" - user << "Card scanned." + to_chat(user, "Card scanned.") else //Basic safety check. If either both objects are held by user or PDA is on ground and card is in hand. if(((src in user.contents) && (C in user.contents)) || (istype(loc, /turf) && in_range(src, user) && (C in user.contents)) ) if(id_check(user, 2)) - user << "You put the ID into \the [src]'s slot." + to_chat(user, "You put the ID into \the [src]'s slot.") updateSelfDialog()//Update self dialog on success. return //Return in case of failed check or when successful. updateSelfDialog()//For the non-input related code. @@ -1301,16 +1313,16 @@ var/global/list/obj/item/device/pda/PDAs = list() user.drop_item() C.loc = src pai = C - user << "You slot \the [C] into [src]." + to_chat(user, "You slot \the [C] into \the [src].") nanomanager.update_uis(src) // update all UIs attached to src else if(istype(C, /obj/item/weapon/pen)) var/obj/item/weapon/pen/O = locate() in src if(O) - user << "There is already a pen in \the [src]." + to_chat(user, "There is already a pen in \the [src].") else user.drop_item() C.loc = src - user << "You slide \the [C] into \the [src]." + to_chat(user, "You slot \the [C] into \the [src].") return /obj/item/device/pda/attack(mob/living/C as mob, mob/living/user as mob) @@ -1346,18 +1358,18 @@ var/global/list/obj/item/device/pda/PDAs = list() if(2) if (!istype(C:dna, /datum/dna)) - user << "No fingerprints found on [C]" + to_chat(user, "No fingerprints found on [C]") else - user << text("\The [C]'s Fingerprints: [md5(C:dna.uni_identity)]") + to_chat(user, text("\The [C]'s Fingerprints: [md5(C:dna.uni_identity)]")) if ( !(C:blood_DNA) ) - user << "No blood found on [C]" + to_chat(user, "No blood found on [C]") if(C:blood_DNA) qdel(C:blood_DNA) else - user << "Blood found on [C]. Analysing..." + to_chat(user, "Blood found on [C]. Analysing...") spawn(15) for(var/blood in C:blood_DNA) - user << "Blood type: [C:blood_DNA[blood]]\nDNA: [blood]" + to_chat(user, "Blood type: [C:blood_DNA[blood]]\nDNA: [blood]") if(4) for (var/mob/O in viewers(C, null)) @@ -1379,13 +1391,13 @@ var/global/list/obj/item/device/pda/PDAs = list() if(!isnull(A.reagents)) if(A.reagents.reagent_list.len > 0) var/reagents_length = A.reagents.reagent_list.len - user << "[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found." + to_chat(user, "[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found.") for (var/re in A.reagents.reagent_list) - user << " [re]" + to_chat(user," [re]") else - user << "No active chemical agents found in [A]." + to_chat(user,"No active chemical agents found in [A].") else - user << "No significant chemical agents found in [A]." + to_chat(user,"No significantchemical agents found in [A].") if(5) analyze_gases(A, user) @@ -1437,8 +1449,7 @@ var/global/list/obj/item/device/pda/PDAs = list() // feature to the PDA, which would better convey the availability of the feature, but this will work for now. // Inform the user - user << "Paper scanned and OCRed to notekeeper." //concept of scanning paper copyright brainoblivion 2009 - + to_chat(user,"Paper scanned and OCRed to notekeeper.") //concept of scanning paper copyright brainoblivion 2009 /obj/item/device/pda/proc/explode() //This needs tuning. //Sure did. @@ -1471,7 +1482,7 @@ var/global/list/obj/item/device/pda/PDAs = list() var/list/namecounts = list() if (toff) - usr << "Turn on your receiver in order to send messages." + to_chat(usr, "Turn on your receiver in order to send messages.") return for (var/obj/item/device/pda/P in PDAs) diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm index a3c5d33d67..1190d7f812 100644 --- a/code/game/objects/items/weapons/handcuffs.dm +++ b/code/game/objects/items/weapons/handcuffs.dm @@ -42,7 +42,8 @@ /obj/item/weapon/handcuffs/proc/can_place(var/mob/target, var/mob/user) if(istype(user, /mob/living/silicon/robot)) - return 1 + if(user.Adjacent(target)) + return 1 else for(var/obj/item/weapon/grab/G in target.grabbed_by) if(G.loc == user && G.state >= GRAB_AGGRESSIVE) diff --git a/code/game/objects/items/weapons/material/bats.dm b/code/game/objects/items/weapons/material/bats.dm index 484ce5c903..73896640a2 100644 --- a/code/game/objects/items/weapons/material/bats.dm +++ b/code/game/objects/items/weapons/material/bats.dm @@ -9,6 +9,7 @@ default_material = "wood" force_divisor = 1.1 // 22 when wielded with weight 20 (steel) unwielded_force_divisor = 0.7 // 15 when unwielded based on above. + dulled_divisor = 0.75 // A "dull" bat is still gonna hurt slot_flags = SLOT_BACK //Predefined materials go here. diff --git a/code/game/objects/items/weapons/material/kitchen.dm b/code/game/objects/items/weapons/material/kitchen.dm index 8fdac8549e..b13b5e6ac2 100644 --- a/code/game/objects/items/weapons/material/kitchen.dm +++ b/code/game/objects/items/weapons/material/kitchen.dm @@ -94,7 +94,6 @@ icon_state = "tacknife" item_state = "knife" applies_material_colour = 0 - unbreakable = 1 /obj/item/weapon/material/kitchen/utensil/knife/attack(target as mob, mob/living/user as mob) if ((CLUMSY in user.mutations) && prob(50)) @@ -117,6 +116,7 @@ attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "whacked") default_material = "wood" force_divisor = 0.7 // 10 when wielded with weight 15 (wood) + dulled_divisor = 0.75 // Still a club thrown_force_divisor = 1 // as above /obj/item/weapon/material/kitchen/rollingpin/attack(mob/living/M as mob, mob/living/user as mob) diff --git a/code/game/objects/items/weapons/material/knives.dm b/code/game/objects/items/weapons/material/knives.dm index 8ebf5697c9..593b1d9237 100644 --- a/code/game/objects/items/weapons/material/knives.dm +++ b/code/game/objects/items/weapons/material/knives.dm @@ -33,7 +33,6 @@ name = "switchblade" desc = "A classic switchblade with gold engraving. Just holding it makes you feel like a gangster." icon_state = "switchblade" - unbreakable = 1 /obj/item/weapon/material/butterfly/boxcutter name = "box cutter" @@ -67,7 +66,6 @@ matter = list(DEFAULT_WALL_MATERIAL = 12000) origin_tech = "materials=1" attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - unbreakable = 1 /obj/item/weapon/material/knife/suicide_act(mob/user) viewers(user) << pick("\The [user] is slitting \his wrists with \the [src]! It looks like \he's trying to commit suicide.", \ diff --git a/code/game/objects/items/weapons/material/material_weapons.dm b/code/game/objects/items/weapons/material/material_weapons.dm index c724a4a774..31231964cb 100644 --- a/code/game/objects/items/weapons/material/material_weapons.dm +++ b/code/game/objects/items/weapons/material/material_weapons.dm @@ -16,9 +16,12 @@ ) var/applies_material_colour = 1 - var/unbreakable + var/unbreakable = 0 //Doesn't lose health + var/fragile = 0 //Shatters when it dies + var/dulled = 0 //Has gone dull var/force_divisor = 0.5 var/thrown_force_divisor = 0.5 + var/dulled_divisor = 0.5 //Just drops the damage by half var/default_material = DEFAULT_WALL_MATERIAL var/material/material var/drops_debris = 1 @@ -47,6 +50,8 @@ else force = material.get_blunt_damage() force = round(force*force_divisor) + if(dulled) + force = round(force*dulled_divisor) throwforce = round(material.get_blunt_damage()*thrown_force_divisor) //spawn(1) // world << "[src] has force [force] and throwforce [throwforce] when made from default material [material.name]" @@ -77,9 +82,18 @@ health-- check_health() +/obj/item/weapon/material/attackby(obj/item/weapon/W, mob/user as mob) + if(istype(W, /obj/item/weapon/whetstone)) + var/obj/item/weapon/whetstone/whet = W + repair(whet.repair_amount, whet.repair_time, user) + ..() + /obj/item/weapon/material/proc/check_health(var/consumed) if(health<=0) - shatter(consumed) + if(fragile) + shatter(consumed) + else if(!dulled) + dull() /obj/item/weapon/material/proc/shatter(var/consumed) var/turf/T = get_turf(src) @@ -90,6 +104,34 @@ playsound(src, "shatter", 70, 1) if(!consumed && drops_debris) material.place_shard(T) qdel(src) + +/obj/item/weapon/material/proc/dull() + var/turf/T = get_turf(src) + T.visible_message("\The [src] goes dull!") + playsound(src, "shatter", 70, 1) + dulled = 1 + if(is_sharp() || has_edge()) + sharp = 0 + edge = 0 + +/obj/item/weapon/material/proc/repair(var/repair_amount, var/repair_time, mob/living/user) + if(!fragile) + if(health < initial(health)) + user.visible_message("[user] begins repairing \the [src].", "You begin repairing \the [src].") + if(do_after(user, repair_time)) + user.visible_message("[user] has finished repairing \the [src]", "You finish repairing \the [src].") + health = min(health + repair_amount, initial(health)) + dulled = 0 + sharp = initial(sharp) + edge = initial(edge) + else + to_chat(user, "[src] doesn't need repairs.") + else + to_chat(user, "You can't repair \the [src].") + return + + + /* Commenting this out pending rebalancing of radiation based on small objects. /obj/item/weapon/material/process() diff --git a/code/game/objects/items/weapons/material/misc.dm b/code/game/objects/items/weapons/material/misc.dm index c68256922e..5edd1c77b5 100644 --- a/code/game/objects/items/weapons/material/misc.dm +++ b/code/game/objects/items/weapons/material/misc.dm @@ -15,6 +15,7 @@ gender = PLURAL w_class = ITEMSIZE_SMALL force_divisor = 0.63 + dulled_divisor = 0.75 //It's a heavy bit of metal attack_verb = list("punched", "beaten", "struck") applies_material_colour = 0 @@ -82,6 +83,7 @@ icon_state = "hoe" force_divisor = 0.25 // 5 with weight 20 (steel) thrown_force_divisor = 0.25 // as above + dulled_divisor = 0.75 //Still metal on a long pole w_class = ITEMSIZE_SMALL attack_verb = list("slashed", "sliced", "cut", "clawed") diff --git a/code/game/objects/items/weapons/material/twohanded.dm b/code/game/objects/items/weapons/material/twohanded.dm index 08489b7df3..5cb404a2b8 100644 --- a/code/game/objects/items/weapons/material/twohanded.dm +++ b/code/game/objects/items/weapons/material/twohanded.dm @@ -85,6 +85,7 @@ desc = "Truly, the weapon of a madman. Who would think to fight fire with an axe?" unwielded_force_divisor = 0.25 force_divisor = 0.7 // 10/42 with hardness 60 (steel) and 0.25 unwielded divisor + dulled_divisor = 0.75 //Still metal on a stick sharp = 1 edge = 1 w_class = ITEMSIZE_LARGE @@ -140,4 +141,5 @@ hitsound = 'sound/weapons/bladeslice.ogg' attack_verb = list("attacked", "poked", "jabbed", "torn", "gored") default_material = "glass" - applies_material_colour = 0 \ No newline at end of file + applies_material_colour = 0 + fragile = 1 //It's a haphazard thing of glass, wire, and steel \ No newline at end of file diff --git a/code/game/objects/items/weapons/material/whetstone.dm b/code/game/objects/items/weapons/material/whetstone.dm new file mode 100644 index 0000000000..e312680d1b --- /dev/null +++ b/code/game/objects/items/weapons/material/whetstone.dm @@ -0,0 +1,11 @@ +//This is in the material folder because it's used by them... +//Actual name may need to change +//All of the important code is in material_weapons.dm +/obj/item/weapon/whetstone + name = "whetstone" + desc = "A simple, fine grit stone, useful for sharpening dull edges and polishing out dents." + icon_state = "whetstone" + force = 3 + w_class = ITEMSIZE_SMALL + var/repair_amount = 5 + var/repair_time = 40 \ No newline at end of file diff --git a/code/game/objects/items/weapons/permits.dm b/code/game/objects/items/weapons/permits.dm index 8196a9b5ae..bb0ebfdd0c 100644 --- a/code/game/objects/items/weapons/permits.dm +++ b/code/game/objects/items/weapons/permits.dm @@ -12,6 +12,7 @@ if(isliving(user)) if(!owner) set_name(user.name) + to_chat(user, "[src] registers your name.") else to_chat(user, "[src] already has an owner!") diff --git a/code/game/objects/items/weapons/storage/wallets.dm b/code/game/objects/items/weapons/storage/wallets.dm index c63430f033..47de448abf 100644 --- a/code/game/objects/items/weapons/storage/wallets.dm +++ b/code/game/objects/items/weapons/storage/wallets.dm @@ -23,7 +23,9 @@ /obj/item/weapon/photo, /obj/item/weapon/reagent_containers/dropper, /obj/item/weapon/screwdriver, - /obj/item/weapon/stamp) + /obj/item/weapon/stamp, + /obj/item/weapon/permit + ) slot_flags = SLOT_ID var/obj/item/weapon/card/id/front_id = null diff --git a/code/game/objects/structures/curtains.dm b/code/game/objects/structures/curtains.dm index bd241bb63a..a2fdfd898d 100644 --- a/code/game/objects/structures/curtains.dm +++ b/code/game/objects/structures/curtains.dm @@ -27,7 +27,7 @@ ..() /obj/structure/curtain/proc/toggle() - opacity = !opacity + set_opacity(!opacity) if(opacity) icon_state = "closed" layer = SHOWER_CLOSED_LAYER diff --git a/code/game/objects/structures/simple_doors.dm b/code/game/objects/structures/simple_doors.dm index 10fa149e8b..58742c3b60 100644 --- a/code/game/objects/structures/simple_doors.dm +++ b/code/game/objects/structures/simple_doors.dm @@ -32,9 +32,9 @@ name = "[material.display_name] door" color = material.icon_colour if(material.opacity < 0.5) - opacity = 0 + set_opacity(0) else - opacity = 1 + set_opacity(1) if(material.products_need_process()) processing_objects |= src update_nearby_tiles(need_rebuild=1) @@ -99,7 +99,7 @@ flick("[material.door_icon_base]opening",src) sleep(10) density = 0 - opacity = 0 + set_opacity(0) state = 1 update_icon() isSwitchingStates = 0 @@ -111,7 +111,7 @@ flick("[material.door_icon_base]closing",src) sleep(10) density = 1 - opacity = 1 + set_opacity(1) state = 0 update_icon() isSwitchingStates = 0 diff --git a/code/game/objects/structures/window_spawner.dm b/code/game/objects/structures/window_spawner.dm index 449001492b..7cdf301f6c 100644 --- a/code/game/objects/structures/window_spawner.dm +++ b/code/game/objects/structures/window_spawner.dm @@ -22,6 +22,9 @@ /obj/effect/wingrille_spawn/attack_generic() activate() +/obj/effect/wingrille_spawn/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0) + return FALSE + /obj/effect/wingrille_spawn/initialize() ..() if(!win_path) diff --git a/code/game/turfs/simulated/wall_attacks.dm b/code/game/turfs/simulated/wall_attacks.dm index 71c17b5895..47620d030a 100644 --- a/code/game/turfs/simulated/wall_attacks.dm +++ b/code/game/turfs/simulated/wall_attacks.dm @@ -11,7 +11,7 @@ update_icon() set_light(0) src.blocks_air = 0 - src.opacity = 0 + set_opacity(0) for(var/turf/simulated/turf in loc) air_master.mark_for_update(turf) else @@ -21,7 +21,7 @@ update_icon() set_light(1) src.blocks_air = 1 - src.opacity = 1 + set_opacity(1) for(var/turf/simulated/turf in loc) air_master.mark_for_update(turf) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 1074af1b90..c13b4606d3 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -21,7 +21,6 @@ var/icon_old = null var/pathweight = 1 // How much does it cost to pathfind over this turf? var/blessed = 0 // Has the turf been blessed? - var/dynamic_lighting = 1 // Does the turf use dynamic lighting? var/list/decals diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm index 7379d2c80b..78946ff832 100644 --- a/code/game/turfs/turf_changing.dm +++ b/code/game/turfs/turf_changing.dm @@ -30,9 +30,10 @@ var/obj/fire/old_fire = fire var/old_opacity = opacity var/old_dynamic_lighting = dynamic_lighting - var/list/old_affecting_lights = affecting_lights + var/old_affecting_lights = affecting_lights var/old_lighting_overlay = lighting_overlay var/old_weather_overlay = weather_overlay + var/old_corners = corners //world << "Replacing [src.type] with [N]" @@ -94,15 +95,16 @@ W.post_change() . = W - lighting_overlay = old_lighting_overlay - affecting_lights = old_affecting_lights - if((old_opacity != opacity) || (dynamic_lighting != old_dynamic_lighting) || force_lighting_update) - reconsider_lights() - if(dynamic_lighting != old_dynamic_lighting) - if(dynamic_lighting) - lighting_build_overlays() - else - lighting_clear_overlays() - else - if(lighting_overlay) - lighting_overlay.update_overlay() \ No newline at end of file + recalc_atom_opacity() + + if(lighting_overlays_initialised) + lighting_overlay = old_lighting_overlay + affecting_lights = old_affecting_lights + corners = old_corners + if((old_opacity != opacity) || (dynamic_lighting != old_dynamic_lighting)) + reconsider_lights() + if(dynamic_lighting != old_dynamic_lighting) + if(dynamic_lighting) + lighting_build_overlay() + else + lighting_clear_overlay() diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm index 970e202bae..18b9c4e15e 100644 --- a/code/modules/clothing/head/hardhat.dm +++ b/code/modules/clothing/head/hardhat.dm @@ -8,6 +8,8 @@ flags_inv = 0 siemens_coefficient = 0.9 action_button_name = "Toggle Head-light" + w_class = ITEMSIZE_NORMAL + ear_protection = 1 /obj/item/clothing/head/hardhat/orange icon_state = "hardhat0_orange" diff --git a/code/modules/clothing/spacesuits/alien.dm b/code/modules/clothing/spacesuits/alien.dm index 996387aa1d..ff0f8ae077 100644 --- a/code/modules/clothing/spacesuits/alien.dm +++ b/code/modules/clothing/spacesuits/alien.dm @@ -2,7 +2,7 @@ /obj/item/clothing/head/helmet/space/skrell name = "Skrellian helmet" desc = "Smoothly contoured and polished to a shine. Still looks like a fishbowl." - armor = list(melee = 20, bullet = 20, laser = 50,energy = 50, bomb = 50, bio = 100, rad = 100) + armor = list(melee = 20, bullet = 20, laser = 20, energy = 50, bomb = 50, bio = 100, rad = 50) max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE species_restricted = list("Skrell","Human") @@ -15,7 +15,7 @@ /obj/item/clothing/suit/space/skrell name = "Skrellian voidsuit" desc = "Seems like a wetsuit with reinforced plating seamlessly attached to it. Very chic." - armor = list(melee = 20, bullet = 20, laser = 50,energy = 50, bomb = 50, bio = 100, rad = 100) + armor = list(melee = 20, bullet = 20, laser = 20, energy = 50, bomb = 50, bio = 100, rad = 50) allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd) heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE @@ -35,14 +35,14 @@ allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/melee/energy/sword,/obj/item/weapon/handcuffs,/obj/item/weapon/tank) phoronproof = 1 slowdown = 2 - armor = list(melee = 60, bullet = 50, laser = 40,energy = 15, bomb = 30, bio = 30, rad = 30) + armor = list(melee = 60, bullet = 50, laser = 40,energy = 15, bomb = 30, bio = 100, rad = 50) siemens_coefficient = 0.2 heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE species_restricted = list("Vox") /obj/item/clothing/head/helmet/space/vox - armor = list(melee = 60, bullet = 50, laser = 40, energy = 15, bomb = 30, bio = 30, rad = 30) + armor = list(melee = 60, bullet = 50, laser = 40, energy = 15, bomb = 30, bio = 100, rad = 50) siemens_coefficient = 0.2 item_flags = STOPPRESSUREDAMAGE | THICKMATERIAL | AIRTIGHT | PHORONGUARD flags_inv = 0 diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm index 820543f082..cec738479e 100644 --- a/code/modules/clothing/spacesuits/rig/modules/utility.dm +++ b/code/modules/clothing/spacesuits/rig/modules/utility.dm @@ -149,17 +149,19 @@ /obj/item/rig_module/chem_dispenser/ninja interface_desc = "Dispenses loaded chemicals directly into the wearer's bloodstream. This variant is made to be extremely light and flexible." - //just over a syringe worth of each. Want more? Go refill. Gives the ninja another reason to have to show their face. + //Want more? Go refill. Gives the ninja another reason to have to show their face. charges = list( - list("tricordrazine", "tricordrazine", 0, 20), - list("tramadol", "tramadol", 0, 20), - list("dexalin plus", "dexalinp", 0, 20), - list("antibiotics", "spaceacillin", 0, 20), - list("antitoxins", "anti_toxin", 0, 20), - list("nutrients", "glucose", 0, 80), - list("clotting agent", "myelamine", 0, 80), - list("hyronalin", "hyronalin", 0, 20), - list("radium", "radium", 0, 20) + list("tricordrazine", "tricordrazine", 0, 30), + list("tramadol", "tramadol", 0, 30), + list("dexalin plus", "dexalinp", 0, 30), + list("antibiotics", "spaceacillin", 0, 30), + list("antitoxins", "anti_toxin", 0, 60), + list("nutrients", "glucose", 0, 80), + list("bicaridine", "bicaridine", 0, 30), + list("clotting agent", "myelamine", 0, 30), + list("peridaxon", "peridaxon", 0, 30), + list("hyronalin", "hyronalin", 0, 30), + list("radium", "radium", 0, 30) ) /obj/item/rig_module/chem_dispenser/accepts_item(var/obj/item/input_item, var/mob/living/user) diff --git a/code/modules/clothing/spacesuits/rig/suits/alien.dm b/code/modules/clothing/spacesuits/rig/suits/alien.dm index 5d73e804b2..fe91639322 100644 --- a/code/modules/clothing/spacesuits/rig/suits/alien.dm +++ b/code/modules/clothing/spacesuits/rig/suits/alien.dm @@ -45,7 +45,7 @@ desc = "This metal box writhes and squirms as if it were alive..." suit_type = "alien" icon_state = "vox_rig" - armor = list(melee = 60, bullet = 50, laser = 40, energy = 15, bomb = 30, bio = 30, rad = 30) + armor = list(melee = 60, bullet = 50, laser = 40, energy = 15, bomb = 30, bio = 100, rad = 50) item_flags = THICKMATERIAL siemens_coefficient = 0.2 phoronproof = 1 @@ -88,7 +88,7 @@ /obj/item/weapon/rig/vox/carapace name = "dense alien control module" suit_type = "dense alien" - armor = list(melee = 60, bullet = 50, laser = 40, energy = 15, bomb = 30, bio = 30, rad = 30) + armor = list(melee = 60, bullet = 50, laser = 40, energy = 15, bomb = 30, bio = 100, rad = 50) emp_protection = 40 //change this to 30 if too high. phoronproof = 1 @@ -109,7 +109,7 @@ name = "sinister alien control module" suit_type = "sinister alien" icon_state = "voxstealth_rig" - armor = list(melee = 40, bullet = 30, laser = 30, energy = 15, bomb = 30, bio = 100, rad = 100) + armor = list(melee = 40, bullet = 30, laser = 30, energy = 15, bomb = 30, bio = 100, rad = 50) emp_protection = 40 //change this to 30 if too high. phoronproof = 1 diff --git a/code/modules/clothing/spacesuits/rig/suits/light.dm b/code/modules/clothing/spacesuits/rig/suits/light.dm index f81110013e..2fb3e040e2 100644 --- a/code/modules/clothing/spacesuits/rig/suits/light.dm +++ b/code/modules/clothing/spacesuits/rig/suits/light.dm @@ -93,7 +93,7 @@ /obj/item/rig_module/vision, /obj/item/rig_module/voice, /obj/item/rig_module/fabricator/energy_net, - /obj/item/rig_module/chem_dispenser, + /obj/item/rig_module/chem_dispenser/ninja, /obj/item/rig_module/grenade_launcher, /obj/item/rig_module/ai_container, /obj/item/rig_module/power_sink, diff --git a/code/modules/hydroponics/seed.dm b/code/modules/hydroponics/seed.dm index 1d92705dec..010888602f 100644 --- a/code/modules/hydroponics/seed.dm +++ b/code/modules/hydroponics/seed.dm @@ -296,11 +296,7 @@ // Handle light requirements. if(!light_supplied) - var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in current_turf - if(L) - light_supplied = max(0,min(10,L.lum_r + L.lum_g + L.lum_b)-5) - else - light_supplied = 5 + light_supplied = current_turf.get_lumcount() * 5 if(light_supplied) if(abs(light_supplied - get_trait(TRAIT_IDEAL_LIGHT)) > get_trait(TRAIT_LIGHT_TOLERANCE)) health_change += rand(1,3) * HYDRO_SPEED_MULTIPLIER diff --git a/code/modules/hydroponics/spreading/spreading.dm b/code/modules/hydroponics/spreading/spreading.dm index 48d2c542dd..1cff4314af 100644 --- a/code/modules/hydroponics/spreading/spreading.dm +++ b/code/modules/hydroponics/spreading/spreading.dm @@ -192,7 +192,7 @@ if(growth>2 && growth == max_growth) layer = 5 - opacity = 1 + set_opacity(1) if(!isnull(seed.chems["woodpulp"])) density = 1 else diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm index ddcde8a2b1..efdae46db7 100644 --- a/code/modules/hydroponics/trays/tray.dm +++ b/code/modules/hydroponics/trays/tray.dm @@ -625,12 +625,7 @@ if(closed_system && mechanical) light_string = "that the internal lights are set to [tray_light] lumens" else - var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T - var/light_available - if(L) - light_available = max(0,min(10,L.lum_r + L.lum_g + L.lum_b)-5) - else - light_available = 5 + var/light_available = T.get_lumcount() * 5 light_string = "a light level of [light_available] lumens" usr << "The tray's sensor suite is reporting [light_string] and a temperature of [environment.temperature]K at [environment.return_pressure()] kPa in the [environment_type] environment" diff --git a/code/modules/integrated_electronics/passive/power.dm b/code/modules/integrated_electronics/passive/power.dm index 9e08a96d24..2b8c200a27 100644 --- a/code/modules/integrated_electronics/passive/power.dm +++ b/code/modules/integrated_electronics/passive/power.dm @@ -21,11 +21,8 @@ var/max_power = 1 /obj/item/integrated_circuit/passive/power/solar_cell/make_energy() - var/atom/movable/lighting_overlay/light = locate(/atom/movable/lighting_overlay) in get_turf(src) - var/light_amount = 1 // Unsimulated tiles are pretend-lit, so we need to be pretend too if that somehow happens. - if(light) - light_amount = (light.lum_r + light.lum_g + light.lum_b) / 3 - + var/turf/T = get_turf(src) + var/light_amount = T ? T.get_lumcount() : 0 var/adjusted_power = max(max_power * light_amount, 0) adjusted_power = round(adjusted_power, 0.1) if(adjusted_power) diff --git a/code/modules/lighting/__lighting_docs.dm b/code/modules/lighting/__lighting_docs.dm index 5ac5897eb5..8ef93935e9 100644 --- a/code/modules/lighting/__lighting_docs.dm +++ b/code/modules/lighting/__lighting_docs.dm @@ -14,9 +14,9 @@ Changes from tg DAL: - light_range; range in tiles of the light, used for calculating falloff, - light_power; multiplier for the brightness of lights, - light_color; hex string representing the RGB colour of the light. - - SetLuminosity() is now set_light() and takes the three variables above. + - setLuminousity() is now set_light() and takes the three variables above. - Variables can be left as null to not update them. - - SetOpacity() is now set_opacity(). + - set_opacity() is now set_opacity(). - Areas have luminosity set to 1 permanently, no hard-lighting. - Objects inside other objects can have lights and they properly affect the turf. (flashlights) - area/master and area/list/related have been eviscerated since subareas aren't needed. @@ -51,7 +51,7 @@ turf: (lighting_turf.dm) - proc/lighting_clear_overlays(): - Delete (manual GC) all light overlays on this turf, used when changing turf to space - proc/lighting_build_overlays(): - - Create lighting overlays for this turf. Called by ChangeTurf in case the turf is being changed to use dynamic lighting. + - Create lighting overlays for this turf atom/movable/lighting_overlay: (lighting_overlay.dm) @@ -64,4 +64,4 @@ atom/movable/lighting_overlay: (lighting_overlay.dm) - Change the lumcount vars and queue the overlay for update - proc/update_overlay() - Called by the lighting process to update the color of the overlay -*/ +*/ \ No newline at end of file diff --git a/code/modules/lighting/_lighting_defs.dm b/code/modules/lighting/_lighting_defs.dm deleted file mode 100644 index 21fdafdaeb..0000000000 --- a/code/modules/lighting/_lighting_defs.dm +++ /dev/null @@ -1,9 +0,0 @@ -#define LIGHTING_INTERVAL 5 // frequency, in 1/10ths of a second, of the lighting process - -#define LIGHTING_FALLOFF 1 // type of falloff to use for lighting; 1 for circular, 2 for square -#define LIGHTING_LAMBERTIAN 1 // use lambertian shading for light sources -#define LIGHTING_HEIGHT 1 // height off the ground of light sources on the pseudo-z-axis, you should probably leave this alone -#define LIGHTING_ROUND_VALUE (1 / 128) //Value used to round lumcounts, values smaller than 1/255 don't matter (if they do, thanks sinking points), greater values will make lighting less precise, but in turn increase performance, VERY SLIGHTLY. - -#define LIGHTING_LAYER 10 // drawing layer for lighting overlays -#define LIGHTING_ICON 'icons/effects/lighting_overlay.dmi' // icon used for lighting shading effects diff --git a/code/modules/lighting/light_source.dm b/code/modules/lighting/light_source.dm deleted file mode 100644 index 3675cd945c..0000000000 --- a/code/modules/lighting/light_source.dm +++ /dev/null @@ -1,300 +0,0 @@ -//So much copypasta in this file, supposedly in the name of performance. If you change anything make sure to consider other places where the code may have been copied. - -/datum/light_source - var/atom/top_atom - var/atom/source_atom - - var/turf/source_turf - var/light_power - var/light_range - var/light_color // string, decomposed by parse_light_color() - - var/lum_r - var/lum_g - var/lum_b - - //hold onto the actual applied lum values so we can undo them when the lighting changes - var/tmp/applied_lum_r - var/tmp/applied_lum_g - var/tmp/applied_lum_b - - var/list/effect_str - var/list/effect_turf - - var/applied - - var/vis_update //Whetever we should smartly recalculate visibility. and then only update tiles that became (in) visible to us - var/needs_update - var/destroyed - var/force_update - -/datum/light_source/New(atom/owner, atom/top) - source_atom = owner - if(!source_atom.light_sources) source_atom.light_sources = list() - source_atom.light_sources += src - top_atom = top - if(top_atom != source_atom) - if(!top.light_sources) top.light_sources = list() - top_atom.light_sources += src - - source_turf = top_atom - light_power = source_atom.light_power - light_range = source_atom.light_range - light_color = source_atom.light_color - - parse_light_color() - - effect_str = list() - effect_turf = list() - - update() - - return ..() - -/datum/light_source/proc/destroy() - destroyed = 1 - force_update() - if(source_atom && source_atom.light_sources) source_atom.light_sources -= src - if(top_atom && top_atom.light_sources) top_atom.light_sources -= src - -/datum/light_source/proc/update(atom/new_top_atom) - if(new_top_atom && new_top_atom != top_atom) - if(top_atom != source_atom) top_atom.light_sources -= src - top_atom = new_top_atom - if(top_atom != source_atom) - if(!top_atom.light_sources) top_atom.light_sources = list() - top_atom.light_sources += src - - if(!needs_update) //Incase we're already updating either way. - lighting_update_lights += src - needs_update = 1 - -/datum/light_source/proc/force_update() - force_update = 1 - if(!needs_update) //Incase we're already updating either way. - needs_update = 1 - lighting_update_lights += src - -/datum/light_source/proc/vis_update() - if(!needs_update) - needs_update = 1 - lighting_update_lights += src - - vis_update = 1 - -/datum/light_source/proc/check() - if(!source_atom || !light_range || !light_power) - destroy() - return 1 - - if(!top_atom) - top_atom = source_atom - . = 1 - - if(istype(top_atom, /turf)) - if(source_turf != top_atom) - source_turf = top_atom - . = 1 - else if(top_atom.loc != source_turf) - source_turf = top_atom.loc - . = 1 - - if(source_atom.light_power != light_power) - light_power = source_atom.light_power - . = 1 - - if(source_atom.light_range != light_range) - light_range = source_atom.light_range - . = 1 - - if(light_range && light_power && !applied) - . = 1 - - if(source_atom.light_color != light_color) - light_color = source_atom.light_color - parse_light_color() - . = 1 - -/datum/light_source/proc/parse_light_color() - if(light_color) - lum_r = GetRedPart(light_color) / 255 - lum_g = GetGreenPart(light_color) / 255 - lum_b = GetBluePart(light_color) / 255 - else - lum_r = 1 - lum_g = 1 - lum_b = 1 - -#if LIGHTING_FALLOFF == 1 //circular - #define LUM_DISTANCE(swapvar, O, T) swapvar = (O.x - T.x)**2 + (O.y - T.y)**2 + LIGHTING_HEIGHT - #if LIGHTING_LAMBERTIAN == 1 - #define LUM_ATTENUATION(swapvar) swapvar = CLAMP01((1 - CLAMP01(sqrt(swapvar) / max(1,light_range))) * (1 / sqrt(swapvar + 1))) - #else - #define LUM_ATTENUATION(swapvar) swapvar = 1 - CLAMP01(sqrt(swapvar) / max(1,light_range)) - #endif -#elif LIGHTING_FALLOFF == 2 //square - #define LUM_DISTANCE(swapvar, O, T) swapvar = abs(O.x - T.x) + abs(O.y - T.y) + LIGHTING_HEIGHT - #if LIGHTING_LAMBERTIAN == 1 - #define LUM_ATTENUATION(swapvar) swapvar = CLAMP01((1 - CLAMP01(swapvar / max(1,light_range))) * (1 / sqrt(swapvar**2 + 1))) - #else - #define LUM_ATTENUATION(swapvar) swapvar = CLAMP01(swapvar / max(1,light_range)) - #endif -#endif - -#define LUM_FALLOFF(swapvar, O, T) \ - LUM_DISTANCE(swapvar, O, T); \ - LUM_ATTENUATION(swapvar); - -/datum/light_source/proc/apply_lum() - applied = 1 - - //Keep track of the last applied lum values so that the lighting can be reversed - applied_lum_r = lum_r - applied_lum_g = lum_g - applied_lum_b = lum_b - - if(istype(source_turf)) - FOR_DVIEW(var/turf/T, light_range, source_turf, INVISIBILITY_LIGHTING) - if(T.lighting_overlay) - var/strength - LUM_FALLOFF(strength, T, source_turf) - strength *= light_power - - if(!strength) //Don't add turfs that aren't affected to the affected turfs. - continue - - strength = round(strength, LIGHTING_ROUND_VALUE) //Screw sinking points. - - effect_str += strength - - T.lighting_overlay.update_lumcount( - applied_lum_r * strength, - applied_lum_g * strength, - applied_lum_b * strength - ) - - else - effect_str += 0 - - if(!T.affecting_lights) - T.affecting_lights = list() - - T.affecting_lights += src - effect_turf += T - END_FOR_DVIEW - -/datum/light_source/proc/remove_lum() - applied = 0 - var/i = 1 - for(var/turf/T in effect_turf) - if(T.affecting_lights) - T.affecting_lights -= src - - if(T.lighting_overlay) - var/str = effect_str[i] - T.lighting_overlay.update_lumcount( - -str * applied_lum_r, - -str * applied_lum_g, - -str * applied_lum_b - ) - - i++ - - effect_str.Cut() - effect_turf.Cut() - -//Smartly updates the lighting, only removes lum from and adds lum to turfs that actually got changed. -//This is for lights that need to reconsider due to nearby opacity changes. -//Stupid dumb copy pasta because BYOND and speed. -/datum/light_source/proc/smart_vis_update() - var/list/view[0] - FOR_DVIEW(var/turf/T, light_range, source_turf, INVISIBILITY_LIGHTING) - view += T //Filter out turfs. - END_FOR_DVIEW - //This is the part where we calculate new turfs (if any) - var/list/new_turfs = view - effect_turf //This will result with all the tiles that are added. - for(var/turf/T in new_turfs) - if(T.lighting_overlay) - LUM_FALLOFF(., T, source_turf) - . *= light_power - - if(!.) //Don't add turfs that aren't affected to the affected turfs. - continue - - . = round(., LIGHTING_ROUND_VALUE) - - effect_str += . - - T.lighting_overlay.update_lumcount( - applied_lum_r * ., - applied_lum_g * ., - applied_lum_b * . - ) - - else - effect_str += 0 - - if(!T.affecting_lights) - T.affecting_lights = list() - - T.affecting_lights += src - effect_turf += T - - var/list/old_turfs = effect_turf - view - for(var/turf/T in old_turfs) - //Insert not-so-huge copy paste from remove_lum(). - var/idx = effect_turf.Find(T) //Get the index, luckily Find() is cheap in small lists like this. (with small I mean under a couple thousand len) - if(T.affecting_lights) - T.affecting_lights -= src - - if(T.lighting_overlay) - var/str = effect_str[idx] - T.lighting_overlay.update_lumcount(-str * applied_lum_r, -str * applied_lum_g, -str * applied_lum_b) - - effect_turf.Cut(idx, idx + 1) - effect_str.Cut(idx, idx + 1) - -//Whoop yet not another copy pasta because speed ~~~~BYOND. -//Calculates and applies lighting for a single turf. This is intended for when a turf switches to -//using dynamic lighting when it was not doing so previously (when constructing a floor on space, for example). -//Assumes the turf is visible and such. -//For the love of god don't call this proc when it's not needed! Lighting artifacts WILL happen! -/datum/light_source/proc/calc_turf(var/turf/T) - var/idx = effect_turf.Find(T) - if(!idx) - return //WHY. - - if(T.lighting_overlay) - #if LIGHTING_FALLOFF == 1 // circular - . = (T.lighting_overlay.x - source_turf.x)**2 + (T.lighting_overlay.y - source_turf.y)**2 + LIGHTING_HEIGHT - #if LIGHTING_LAMBERTIAN == 1 - . = CLAMP01((1 - CLAMP01(sqrt(.) / light_range)) * (1 / (sqrt(. + 1)))) - #else - . = 1 - CLAMP01(sqrt(.) / light_range) - #endif - - #elif LIGHTING_FALLOFF == 2 // square - . = abs(T.lighting_overlay.x - source_turf.x) + abs(T.lighting_overlay.y - source_turf.y) + LIGHTING_HEIGHT - #if LIGHTING_LAMBERTIAN == 1 - . = CLAMP01((1 - CLAMP01(. / light_range)) * (1 / (sqrt(.)**2 + ))) - #else - . = 1 - CLAMP01(. / light_range) - #endif - #endif - . *= light_power - - . = round(., LIGHTING_ROUND_VALUE) - - effect_str[idx] = . - - //Since the applied_lum values are what are (later) removed by remove_lum. - //Anything we apply to the lighting overlays HAS to match what remove_lum uses. - T.lighting_overlay.update_lumcount( - applied_lum_r * ., - applied_lum_g * ., - applied_lum_b * . - ) - -#undef LUM_FALLOFF -#undef LUM_DISTANCE -#undef LUM_ATTENUATION diff --git a/code/modules/lighting/lighting_area.dm b/code/modules/lighting/lighting_area.dm new file mode 100644 index 0000000000..92923e6b0d --- /dev/null +++ b/code/modules/lighting/lighting_area.dm @@ -0,0 +1,9 @@ +/area + luminosity = TRUE + var/dynamic_lighting = TRUE + +/area/New() + . = ..() + + if(dynamic_lighting) + luminosity = FALSE \ No newline at end of file diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm index c900f17bf8..8700a65c6e 100644 --- a/code/modules/lighting/lighting_atom.dm +++ b/code/modules/lighting/lighting_atom.dm @@ -1,12 +1,14 @@ /atom var/light_power = 1 // intensity of the light var/light_range = 0 // range in tiles of the light - var/light_color // RGB string representing the colour of the light + var/light_color // Hexadecimal RGB string representing the colour of the light var/datum/light_source/light var/list/light_sources -/atom/proc/set_light(l_range, l_power, l_color) +// Nonsensical value for l_color default, so we can detect if it gets set to null. +#define NONSENSICAL_VALUE -99999 +/atom/proc/set_light(l_range, l_power, l_color = NONSENSICAL_VALUE) . = 0 //make it less costly if nothing's changed if(l_power != null && l_power != light_power) @@ -15,16 +17,17 @@ if(l_range != null && l_range != light_range) light_range = l_range . = 1 - if(l_color != null && l_color != light_color) + if(l_color != NONSENSICAL_VALUE && l_color != light_color) light_color = l_color . = 1 if(.) update_light() -/atom/proc/copy_light(atom/A) - set_light(A.light_range, A.light_power, A.light_color) +#undef NONSENSICAL_VALUE /atom/proc/update_light() + set waitfor = FALSE + if(!light_power || !light_range) if(light) light.destroy() @@ -42,9 +45,14 @@ /atom/New() . = ..() + if(light_power && light_range) update_light() + if(opacity && isturf(loc)) + var/turf/T = loc + T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guranteed to be on afterwards anyways. + /atom/Destroy() if(light) light.destroy() @@ -57,19 +65,38 @@ T.reconsider_lights() return ..() -/atom/Entered(atom/movable/obj, atom/prev_loc) +/atom/movable/Move() + var/turf/old_loc = loc . = ..() - if(obj && prev_loc != src) - for(var/datum/light_source/L in obj.light_sources) + if(loc != old_loc) + for(var/datum/light_source/L in light_sources) L.source_atom.update_light() + var/turf/new_loc = loc + if(istype(old_loc) && opacity) + old_loc.reconsider_lights() + + if(istype(new_loc) && opacity) + new_loc.reconsider_lights() + /atom/proc/set_opacity(new_opacity) - var/old_opacity = opacity + if(new_opacity == opacity) + return + opacity = new_opacity - var/turf/T = loc - if(old_opacity != new_opacity && istype(T)) + var/turf/T = isturf(src) ? src : loc + if(!isturf(T)) + return + + if(new_opacity == TRUE) + T.has_opaque_atom = TRUE T.reconsider_lights() + else + var/old_has_opaque_atom = T.has_opaque_atom + T.recalc_atom_opacity() + if(old_has_opaque_atom != T.has_opaque_atom) + T.reconsider_lights() /obj/item/equipped() . = ..() diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm new file mode 100644 index 0000000000..63ce123253 --- /dev/null +++ b/code/modules/lighting/lighting_corner.dm @@ -0,0 +1,133 @@ +/var/total_lighting_corners = 0 +/var/datum/lighting_corner/dummy/dummy_lighting_corner = new +// Because we can control each corner of every lighting overlay. +// And corners get shared between multiple turfs (unless you're on the corners of the map, then 1 corner doesn't). +// For the record: these should never ever ever be deleted, even if the turf doesn't have dynamic lighting. + +// This list is what the code that assigns corners listens to, the order in this list is the order in which corners are added to the /turf/corners list. +/var/list/LIGHTING_CORNER_DIAGONAL = list(NORTHEAST, SOUTHEAST, SOUTHWEST, NORTHWEST) + +/datum/lighting_corner + var/list/turf/masters = list() + var/list/datum/light_source/affecting = list() // Light sources affecting us. + var/active = FALSE // TRUE if one of our masters has dynamic lighting. + + var/x = 0 + var/y = 0 + var/z = 0 + + var/lum_r = 0 + var/lum_g = 0 + var/lum_b = 0 + + var/needs_update = FALSE + + var/cache_r = LIGHTING_SOFT_THRESHOLD + var/cache_g = LIGHTING_SOFT_THRESHOLD + var/cache_b = LIGHTING_SOFT_THRESHOLD + var/cache_mx = 0 + + var/update_gen = 0 + +/datum/lighting_corner/New(var/turf/new_turf, var/diagonal) + . = ..() + + total_lighting_corners++ + + masters[new_turf] = turn(diagonal, 180) + z = new_turf.z + + var/vertical = diagonal & ~(diagonal - 1) // The horizontal directions (4 and 8) are bigger than the vertical ones (1 and 2), so we can reliably say the lsb is the horizontal direction. + var/horizontal = diagonal & ~vertical // Now that we know the horizontal one we can get the vertical one. + + x = new_turf.x + (horizontal == EAST ? 0.5 : -0.5) + y = new_turf.y + (vertical == NORTH ? 0.5 : -0.5) + + // My initial plan was to make this loop through a list of all the dirs (horizontal, vertical, diagonal). + // Issue being that the only way I could think of doing it was very messy, slow and honestly overengineered. + // So we'll have this hardcode instead. + var/turf/T + var/i + + // Diagonal one is easy. + T = get_step(new_turf, diagonal) + if (T) // In case we're on the map's border. + if (!T.corners) + T.corners = list(null, null, null, null) + + masters[T] = diagonal + i = LIGHTING_CORNER_DIAGONAL.Find(turn(diagonal, 180)) + T.corners[i] = src + + // Now the horizontal one. + T = get_step(new_turf, horizontal) + if (T) // Ditto. + if (!T.corners) + T.corners = list(null, null, null, null) + + masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates. + i = LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180)) + T.corners[i] = src + + // And finally the vertical one. + T = get_step(new_turf, vertical) + if (T) + if (!T.corners) + T.corners = list(null, null, null, null) + + masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates. + i = LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180)) + T.corners[i] = src + + update_active() + +/datum/lighting_corner/proc/update_active() + active = FALSE + for (var/turf/T in masters) + if (T.lighting_overlay) + active = TRUE + +// God that was a mess, now to do the rest of the corner code! Hooray! +/datum/lighting_corner/proc/update_lumcount(var/delta_r, var/delta_g, var/delta_b) + lum_r += delta_r + lum_g += delta_g + lum_b += delta_b + + if (!needs_update) + needs_update = TRUE + lighting_update_corners += src + +/datum/lighting_corner/proc/update_overlays() + // Cache these values a head of time so 4 individual lighting overlays don't all calculate them individually. + var/lum_r = src.lum_r + var/lum_g = src.lum_g + var/lum_b = src.lum_b + var/mx = max(lum_r, lum_g, lum_b) // Scale it so 1 is the strongest lum, if it is above 1. + . = 1 // factor + if (mx > 1) + . = 1 / mx + + #if LIGHTING_SOFT_THRESHOLD != 0 + else if (mx < LIGHTING_SOFT_THRESHOLD) + . = 0 // 0 means soft lighting. + + cache_r = round(lum_r * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD + cache_g = round(lum_g * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD + cache_b = round(lum_b * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD + #else + cache_r = round(lum_r * ., LIGHTING_ROUND_VALUE) + cache_g = round(lum_g * ., LIGHTING_ROUND_VALUE) + cache_b = round(lum_b * ., LIGHTING_ROUND_VALUE) + #endif + cache_mx = round(mx, LIGHTING_ROUND_VALUE) + + for (var/TT in masters) + var/turf/T = TT + if (T.lighting_overlay) + if (!T.lighting_overlay.needs_update) + T.lighting_overlay.needs_update = TRUE + lighting_update_overlays += T.lighting_overlay + + +/datum/lighting_corner/dummy/New() + return diff --git a/code/modules/lighting/lighting_overlay.dm b/code/modules/lighting/lighting_overlay.dm index 2dd3889e4e..d46c5461df 100644 --- a/code/modules/lighting/lighting_overlay.dm +++ b/code/modules/lighting/lighting_overlay.dm @@ -1,102 +1,125 @@ +/var/total_lighting_overlays = 0 /atom/movable/lighting_overlay name = "" mouse_opacity = 0 simulated = 0 anchored = 1 - icon = LIGHTING_ICON - icon_state = "light1" layer = LIGHTING_LAYER invisibility = INVISIBILITY_LIGHTING - color = "#000000" + color = LIGHTING_BASE_MATRIX + icon_state = "light1" + auto_init = 0 // doesn't need special init + blend_mode = BLEND_MULTIPLY - var/lum_r - var/lum_g - var/lum_b + var/lum_r = 0 + var/lum_g = 0 + var/lum_b = 0 - var/needs_update + var/needs_update = FALSE -/atom/movable/lighting_overlay/New() +/atom/movable/lighting_overlay/New(var/atom/loc, var/no_update = FALSE) . = ..() verbs.Cut() + total_lighting_overlays++ - var/turf/T = loc //If this runtimes atleast we'll know what's creating overlays in things that aren't turfs. + var/turf/T = loc //If this runtimes atleast we'll know what's creating overlays outside of turfs. + T.lighting_overlay = src T.luminosity = 0 - -/atom/movable/lighting_overlay/proc/update_lumcount(delta_r, delta_g, delta_b) - if(!delta_r && !delta_g && !delta_b) //Nothing is being changed all together. + if(no_update) return - - var/should_update = 0 - - if(!needs_update) //If this isn't true, we're already updating anyways. - if(max(lum_r, lum_g, lum_b) < 1) //Any change that could happen WILL change appearance. - should_update = 1 - - else if(max(lum_r + delta_r, lum_g + delta_g, lum_b + delta_b) < 1) //The change would bring us under 1 max lum, again, guaranteed to change appearance. - should_update = 1 - - else //We need to make sure that the colour ratios won't change in this code block. - var/mx1 = max(lum_r, lum_g, lum_b) - var/mx2 = max(lum_r + delta_r, lum_g + delta_g, lum_b + delta_b) - - if(lum_r / mx1 != (lum_r + delta_r) / mx2 || lum_g / mx1 != (lum_g + delta_g) / mx2 || lum_b / mx1 != (lum_b + delta_b) / mx2) //Stuff would change. - should_update = 1 - - lum_r += delta_r - lum_g += delta_g - lum_b += delta_b - - if(!needs_update && should_update) - needs_update = 1 - lighting_update_overlays += src + update_overlay() /atom/movable/lighting_overlay/proc/update_overlay() + set waitfor = FALSE var/turf/T = loc - if(istype(T)) //Incase we're not on a turf, pool ourselves, something happened. - if(lum_r == lum_g && lum_r == lum_b) //greyscale - blend_mode = BLEND_OVERLAY - if(lum_r <= 0) - T.luminosity = 0 - color = "#000000" - alpha = 255 - else - T.luminosity = 1 - color = "#000000" - alpha = (1 - min(lum_r, 1)) * 255 + if(!istype(T)) + if(loc) + log_debug("A lighting overlay realised its loc was NOT a turf (actual loc: [loc][loc ? ", " + loc.type : "null"]) in update_overlay() and got qdel'ed!") else - alpha = 255 - var/mx = max(lum_r, lum_g, lum_b) - . = 1 // factor - if(mx > 1) - . = 1/mx - blend_mode = BLEND_MULTIPLY - color = rgb(lum_r * 255 * ., lum_g * 255 * ., lum_b * 255 * .) - if(color != "#000000") - T.luminosity = 1 - else //No light, set the turf's luminosity to 0 to remove it from view() - T.luminosity = 0 - else - warning("A lighting overlay realised its loc was NOT a turf (actual loc: [loc][loc ? ", " + loc.type : ""]) in update_overlay() and got pooled!") + log_debug("A lighting overlay realised it was in nullspace in update_overlay() and got pooled!") qdel(src) + return -/atom/movable/lighting_overlay/ResetVars() - loc = null + // To the future coder who sees this and thinks + // "Why didn't he just use a loop?" + // Well my man, it's because the loop performed like shit. + // And there's no way to improve it because + // without a loop you can make the list all at once which is the fastest you're gonna get. + // Oh it's also shorter line wise. + // Including with these comments. - lum_r = 0 - lum_g = 0 - lum_b = 0 + // See LIGHTING_CORNER_DIAGONAL in lighting_corner.dm for why these values are what they are. + // No I seriously cannot think of a more efficient method, fuck off Comic. + var/datum/lighting_corner/cr = T.corners[3] || dummy_lighting_corner + var/datum/lighting_corner/cg = T.corners[2] || dummy_lighting_corner + var/datum/lighting_corner/cb = T.corners[4] || dummy_lighting_corner + var/datum/lighting_corner/ca = T.corners[1] || dummy_lighting_corner - color = "#000000" + var/max = max(cr.cache_mx, cg.cache_mx, cb.cache_mx, ca.cache_mx) - needs_update = 0 + var/rr = cr.cache_r + var/rg = cr.cache_g + var/rb = cr.cache_b + + var/gr = cg.cache_r + var/gg = cg.cache_g + var/gb = cg.cache_b + + var/br = cb.cache_r + var/bg = cb.cache_g + var/bb = cb.cache_b + + var/ar = ca.cache_r + var/ag = ca.cache_g + var/ab = ca.cache_b + + #if LIGHTING_SOFT_THRESHOLD != 0 + var/set_luminosity = max > LIGHTING_SOFT_THRESHOLD + #else + // Because of floating points, it won't even be a flat 0. + // This number is mostly arbitrary. + var/set_luminosity = max > 1e-6 + #endif + + if((rr & gr & br & ar) && (rg + gg + bg + ag + rb + gb + bb + ab == 8)) + //anything that passes the first case is very likely to pass the second, and addition is a little faster in this case + icon_state = "transparent" + color = null + else if(!set_luminosity) + icon_state = LIGHTING_ICON_STATE_DARK + color = null + else + icon_state = null + color = list( + rr, rg, rb, 00, + gr, gg, gb, 00, + br, bg, bb, 00, + ar, ag, ab, 00, + 00, 00, 00, 01 + ) + + luminosity = set_luminosity + +// Variety of overrides so the overlays don't get affected by weird things. +/atom/movable/lighting_overlay/ex_act() + return + +/atom/movable/lighting_overlay/singularity_act() + return + +/atom/movable/lighting_overlay/singularity_pull() + return /atom/movable/lighting_overlay/Destroy() - lighting_update_overlays -= src + total_lighting_overlays-- + global.lighting_update_overlays -= src + global.lighting_update_overlays_old -= src var/turf/T = loc if(istype(T)) T.lighting_overlay = null - - ..() + T.luminosity = 1 + + return ..() diff --git a/code/modules/lighting/lighting_process.dm b/code/modules/lighting/lighting_process.dm deleted file mode 100644 index 055e366337..0000000000 --- a/code/modules/lighting/lighting_process.dm +++ /dev/null @@ -1,35 +0,0 @@ -/datum/controller/process/lighting/setup() - name = "lighting" - schedule_interval = LIGHTING_INTERVAL - - create_lighting_overlays() - -/datum/controller/process/lighting/doWork() - var/list/lighting_update_lights_old = lighting_update_lights //We use a different list so any additions to the update lists during a delay from scheck() don't cause things to be cut from the list without being updated. - lighting_update_lights = null //Nulling it first because of http://www.byond.com/forum/?post=1854520 - lighting_update_lights = list() - - for(var/datum/light_source/L in lighting_update_lights_old) - if(L.destroyed || L.check() || L.force_update) - L.remove_lum() - if(!L.destroyed) - L.apply_lum() - - else if(L.vis_update) //We smartly update only tiles that became (in) visible to use. - L.smart_vis_update() - - L.vis_update = 0 - L.force_update = 0 - L.needs_update = 0 - - SCHECK - - var/list/lighting_update_overlays_old = lighting_update_overlays //Same as above. - lighting_update_overlays = null //Same as above - lighting_update_overlays = list() - - for(var/atom/movable/lighting_overlay/O in lighting_update_overlays_old) - O.update_overlay() - O.needs_update = 0 - - SCHECK diff --git a/code/modules/lighting/lighting_setup.dm b/code/modules/lighting/lighting_setup.dm new file mode 100644 index 0000000000..981de65864 --- /dev/null +++ b/code/modules/lighting/lighting_setup.dm @@ -0,0 +1,16 @@ +/proc/create_all_lighting_overlays() + for(var/zlevel = 1 to world.maxz) + create_lighting_overlays_zlevel(zlevel) + +/proc/create_lighting_overlays_zlevel(var/zlevel) + ASSERT(zlevel) + + for(var/turf/T in block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel))) + if(!T.dynamic_lighting) + continue + + var/area/A = T.loc + if(!A.dynamic_lighting) + continue + + new /atom/movable/lighting_overlay(T, TRUE) diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm new file mode 100644 index 0000000000..d878d8c4b6 --- /dev/null +++ b/code/modules/lighting/lighting_source.dm @@ -0,0 +1,290 @@ +/var/total_lighting_sources = 0 +// This is where the fun begins. +// These are the main datums that emit light. + +/datum/light_source + var/atom/top_atom // The atom we're emitting light from(for example a mob if we're from a flashlight that's being held). + var/atom/source_atom // The atom that we belong to. + + var/turf/source_turf // The turf under the above. + var/light_power // Intensity of the emitter light. + var/light_range // The range of the emitted light. + var/light_color // The colour of the light, string, decomposed by parse_light_color() + + // Variables for keeping track of the colour. + var/lum_r + var/lum_g + var/lum_b + + // The lumcount values used to apply the light. + var/tmp/applied_lum_r + var/tmp/applied_lum_g + var/tmp/applied_lum_b + + var/list/datum/lighting_corner/effect_str // List used to store how much we're affecting corners. + var/list/turf/affecting_turfs + + var/applied = FALSE // Whether we have applied our light yet or not. + + var/vis_update // Whether we should smartly recalculate visibility. and then only update tiles that became(in)visible to us. + var/needs_update // Whether we are queued for an update. + var/destroyed // Whether we are destroyed and need to stop emitting light. + var/force_update + +/datum/light_source/New(var/atom/owner, var/atom/top) + total_lighting_sources++ + source_atom = owner // Set our new owner. + if(!source_atom.light_sources) + source_atom.light_sources = list() + + source_atom.light_sources += src // Add us to the lights of our owner. + top_atom = top + if(top_atom != source_atom) + if(!top.light_sources) + top.light_sources = list() + + top_atom.light_sources += src + + source_turf = top_atom + light_power = source_atom.light_power + light_range = source_atom.light_range + light_color = source_atom.light_color + + parse_light_color() + + effect_str = list() + affecting_turfs = list() + + update() + + + return ..() + +// Kill ourselves. +/datum/light_source/proc/destroy() + total_lighting_sources-- + destroyed = TRUE + force_update() + if(source_atom) + if(!source_atom.light_sources) + log_runtime(EXCEPTION("Atom [source_atom] was a light source, but lacked a light source list!\n"), source_atom) + else + source_atom.light_sources -= src + + if(top_atom) + top_atom.light_sources -= src + +// Call it dirty, I don't care. +// This is here so there's no performance loss on non-instant updates from the fact that the engine can also do instant updates. +// If you're wondering what's with the "BYOND" argument: BYOND won't let me have a() macro that has no arguments :|. +#define effect_update(BYOND) \ + if(!needs_update) \ + { \ + lighting_update_lights += src; \ + needs_update = TRUE; \ + } + +// This proc will cause the light source to update the top atom, and add itself to the update queue. +/datum/light_source/proc/update(var/atom/new_top_atom) + // This top atom is different. + if(new_top_atom && new_top_atom != top_atom) + if(top_atom != source_atom) // Remove ourselves from the light sources of that top atom. + top_atom.light_sources -= src + + top_atom = new_top_atom + + if(top_atom != source_atom) + if(!top_atom.light_sources) + top_atom.light_sources = list() + + top_atom.light_sources += src // Add ourselves to the light sources of our new top atom. + + effect_update(null) + +// Will force an update without checking if it's actually needed. +/datum/light_source/proc/force_update() + force_update = 1 + + effect_update(null) + +// Will cause the light source to recalculate turfs that were removed or added to visibility only. +/datum/light_source/proc/vis_update() + vis_update = 1 + + effect_update(null) + +// Will check if we actually need to update, and update any variables that may need to be updated. +/datum/light_source/proc/check() + if(!source_atom || !light_range || !light_power) + destroy() + return 1 + + if(!top_atom) + top_atom = source_atom + . = 1 + + if(isturf(top_atom)) + if(source_turf != top_atom) + source_turf = top_atom + . = 1 + else if(top_atom.loc != source_turf) + source_turf = top_atom.loc + . = 1 + + if(source_atom.light_power != light_power) + light_power = source_atom.light_power + . = 1 + + if(source_atom.light_range != light_range) + light_range = source_atom.light_range + . = 1 + + if(light_range && light_power && !applied) + . = 1 + + if(source_atom.light_color != light_color) + light_color = source_atom.light_color + parse_light_color() + . = 1 + +// Decompile the hexadecimal colour into lumcounts of each perspective. +/datum/light_source/proc/parse_light_color() + if(light_color) + lum_r = GetRedPart (light_color) / 255 + lum_g = GetGreenPart(light_color) / 255 + lum_b = GetBluePart (light_color) / 255 + else + lum_r = 1 + lum_g = 1 + lum_b = 1 + +// Macro that applies light to a new corner. +// It is a macro in the interest of speed, yet not having to copy paste it. +// If you're wondering what's with the backslashes, the backslashes cause BYOND to not automatically end the line. +// As such this all gets counted as a single line. +// The braces and semicolons are there to be able to do this on a single line. + +#define APPLY_CORNER(C) \ + . = LUM_FALLOFF(C, source_turf); \ + \ + . *= light_power; \ + \ + effect_str[C] = .; \ + \ + C.update_lumcount \ + ( \ + . * applied_lum_r, \ + . * applied_lum_g, \ + . * applied_lum_b \ + ); + +// I don't need to explain what this does, do I? +#define REMOVE_CORNER(C) \ + . = -effect_str[C]; \ + C.update_lumcount \ + ( \ + . * applied_lum_r, \ + . * applied_lum_g, \ + . * applied_lum_b \ + ); + +// This is the define used to calculate falloff. +#define LUM_FALLOFF(C, T)(1 - CLAMP01(sqrt((C.x - T.x) ** 2 +(C.y - T.y) ** 2 + LIGHTING_HEIGHT) / max(1, light_range))) + +/datum/light_source/proc/apply_lum() + var/static/update_gen = 1 + applied = 1 + + // Keep track of the last applied lum values so that the lighting can be reversed + applied_lum_r = lum_r + applied_lum_g = lum_g + applied_lum_b = lum_b + + FOR_DVIEW(var/turf/T, light_range, source_turf, INVISIBILITY_LIGHTING) + if(!T.lighting_corners_initialised) + T.generate_missing_corners() + + for(var/datum/lighting_corner/C in T.get_corners()) + if(C.update_gen == update_gen) + continue + + C.update_gen = update_gen + C.affecting += src + + if(!C.active) + effect_str[C] = 0 + continue + + APPLY_CORNER(C) + + if(!T.affecting_lights) + T.affecting_lights = list() + + T.affecting_lights += src + affecting_turfs += T + + update_gen++ + +/datum/light_source/proc/remove_lum() + applied = FALSE + + for(var/turf/T in affecting_turfs) + if(!T.affecting_lights) + T.affecting_lights = list() + else + T.affecting_lights -= src + + affecting_turfs.Cut() + + for(var/datum/lighting_corner/C in effect_str) + REMOVE_CORNER(C) + + C.affecting -= src + + effect_str.Cut() + +/datum/light_source/proc/recalc_corner(var/datum/lighting_corner/C) + if(effect_str.Find(C)) // Already have one. + REMOVE_CORNER(C) + + APPLY_CORNER(C) + +/datum/light_source/proc/smart_vis_update() + var/list/datum/lighting_corner/corners = list() + var/list/turf/turfs = list() + FOR_DVIEW(var/turf/T, light_range, source_turf, 0) + if(!T.lighting_corners_initialised) + T.generate_missing_corners() + corners |= T.get_corners() + turfs += T + + var/list/L = turfs - affecting_turfs // New turfs, add us to the affecting lights of them. + affecting_turfs += L + for(var/turf/T in L) + if(!T.affecting_lights) + T.affecting_lights = list(src) + else + T.affecting_lights += src + + L = affecting_turfs - turfs // Now-gone turfs, remove us from the affecting lights. + affecting_turfs -= L + for(var/turf/T in L) + T.affecting_lights -= src + + for(var/datum/lighting_corner/C in corners - effect_str) // New corners + C.affecting += src + if(!C.active) + effect_str[C] = 0 + continue + + APPLY_CORNER(C) + + for(var/datum/lighting_corner/C in effect_str - corners) // Old, now gone, corners. + REMOVE_CORNER(C) + C.affecting -= src + effect_str -= C + +#undef effect_update +#undef LUM_FALLOFF +#undef REMOVE_CORNER +#undef APPLY_CORNER diff --git a/code/modules/lighting/lighting_system.dm b/code/modules/lighting/lighting_system.dm deleted file mode 100644 index 0c84294f35..0000000000 --- a/code/modules/lighting/lighting_system.dm +++ /dev/null @@ -1,25 +0,0 @@ -/var/list/lighting_update_lights = list() -/var/list/lighting_update_overlays = list() - -/area/var/lighting_use_dynamic = 1 - -// duplicates lots of code, but this proc needs to be as fast as possible. -/proc/create_lighting_overlays(zlevel = 0) - var/area/A - if(zlevel == 0) // populate all zlevels - for(var/turf/T in world) - if(T.dynamic_lighting) - A = T.loc - if(A.lighting_use_dynamic) - var/atom/movable/lighting_overlay/O = PoolOrNew(/atom/movable/lighting_overlay, T) - T.lighting_overlay = O - - else - for(var/x = 1; x <= world.maxx; x++) - for(var/y = 1; y <= world.maxy; y++) - var/turf/T = locate(x, y, zlevel) - if(T.dynamic_lighting) - A = T.loc - if(A.lighting_use_dynamic) - var/atom/movable/lighting_overlay/O = PoolOrNew(/atom/movable/lighting_overlay, T) - T.lighting_overlay = O diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index f8f0a5ab10..cc9c678b97 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -1,32 +1,102 @@ /turf - var/list/affecting_lights - var/atom/movable/lighting_overlay/lighting_overlay + var/dynamic_lighting = TRUE // Does the turf use dynamic lighting? + luminosity = 1 + var/tmp/lighting_corners_initialised = FALSE + + var/tmp/list/datum/light_source/affecting_lights // List of light sources affecting this turf. + var/tmp/atom/movable/lighting_overlay/lighting_overlay // Our lighting overlay. + var/tmp/list/datum/lighting_corner/corners + var/tmp/has_opaque_atom = FALSE // Not to be confused with opacity, this will be TRUE if there's any opaque atom on the tile. + +/turf/New() + . = ..() + + if(opacity) + has_opaque_atom = TRUE + +// Causes any affecting light sources to be queued for a visibility update, for example a door got opened. /turf/proc/reconsider_lights() for(var/datum/light_source/L in affecting_lights) L.vis_update() -/turf/proc/lighting_clear_overlays() +/turf/proc/lighting_clear_overlay() if(lighting_overlay) qdel(lighting_overlay) -/turf/proc/lighting_build_overlays() + for(var/datum/lighting_corner/C in corners) + C.update_active() + +// Builds a lighting overlay for us, but only if our area is dynamic. +/turf/proc/lighting_build_overlay() + if(lighting_overlay) + return + + var/area/A = loc + if(A.dynamic_lighting) + if(!lighting_corners_initialised) + generate_missing_corners() + + new /atom/movable/lighting_overlay(src) + + for(var/datum/lighting_corner/C in corners) + if(!C.active) // We would activate the corner, calculate the lighting for it. + for(var/L in C.affecting) + var/datum/light_source/S = L + S.recalc_corner(C) + + C.active = TRUE + +// Used to get a scaled lumcount. +/turf/proc/get_lumcount(var/minlum = 0, var/maxlum = 1) if(!lighting_overlay) - var/area/A = loc - if(A.lighting_use_dynamic) - var/atom/movable/lighting_overlay/O = PoolOrNew(/atom/movable/lighting_overlay, src) - lighting_overlay = O + return 1 - //Make the light sources recalculate us so the lighting overlay updates immediately - for(var/datum/light_source/L in affecting_lights) - L.calc_turf(src) + var/totallums = 0 + for(var/datum/lighting_corner/L in corners) + totallums += max(L.lum_r, L.lum_g, L.lum_b) -/turf/Entered(atom/movable/obj) + totallums /= 4 // 4 corners, max channel selected, return the average + + totallums =(totallums - minlum) /(maxlum - minlum) + + return CLAMP01(totallums) + +// Can't think of a good name, this proc will recalculate the has_opaque_atom variable. +/turf/proc/recalc_atom_opacity() + has_opaque_atom = FALSE + for(var/atom/A in src.contents + src) // Loop through every movable atom on our tile PLUS ourselves (we matter too...) + if(A.opacity) + has_opaque_atom = TRUE + +// If an opaque movable atom moves around we need to potentially update visibility. +/turf/Entered(var/atom/movable/Obj, var/atom/OldLoc) . = ..() - if(obj && obj.opacity) + + if(Obj && Obj.opacity) + has_opaque_atom = TRUE // Make sure to do this before reconsider_lights(), incase we're on instant updates. Guaranteed to be on in this case. reconsider_lights() -/turf/Exited(atom/movable/obj) +/turf/Exited(var/atom/movable/Obj, var/atom/newloc) . = ..() - if(obj && obj.opacity) + + if(Obj && Obj.opacity) + recalc_atom_opacity() // Make sure to do this before reconsider_lights(), incase we're on instant updates. reconsider_lights() + +/turf/proc/get_corners() + if(has_opaque_atom) + return null // Since this proc gets used in a for loop, null won't be looped though. + + return corners + +/turf/proc/generate_missing_corners() + lighting_corners_initialised = TRUE + if(!corners) + corners = list(null, null, null, null) + + for(var/i = 1 to 4) + if(corners[i]) // Already have a corner on this direction. + continue + + corners[i] = new /datum/lighting_corner(src, LIGHTING_CORNER_DIAGONAL[i]) diff --git a/code/modules/lighting/~lighting_undefs.dm b/code/modules/lighting/~lighting_undefs.dm index 772f70557a..2f863b73d5 100644 --- a/code/modules/lighting/~lighting_undefs.dm +++ b/code/modules/lighting/~lighting_undefs.dm @@ -1,9 +1,7 @@ -#undef LIGHTING_INTERVAL - #undef LIGHTING_FALLOFF #undef LIGHTING_LAMBERTIAN #undef LIGHTING_HEIGHT -#undef LIGHTING_RESOLUTION -#undef LIGHTING_LAYER #undef LIGHTING_ICON + +#undef LIGHTING_BASE_MATRIX diff --git a/code/modules/materials/material_recipes.dm b/code/modules/materials/material_recipes.dm index 79e0ce03ae..11f6021c75 100644 --- a/code/modules/materials/material_recipes.dm +++ b/code/modules/materials/material_recipes.dm @@ -96,6 +96,7 @@ recipes += new/datum/stack_recipe("knife grip", /obj/item/weapon/material/butterflyhandle, 4, time = 20, one_per_turf = 0, on_floor = 1, supplied_material = "[name]") recipes += new/datum/stack_recipe("dark floor tile", /obj/item/stack/tile/floor_dark, 1, 4, 20) recipes += new/datum/stack_recipe("roller bed", /obj/item/roller, 5, time = 30, on_floor = 1) + recipes += new/datum/stack_recipe("whetstone", /obj/item/weapon/whetstone, 2, time = 10) /material/sandstone/generate_recipes() ..() diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm index f46d384f86..32ca92b311 100644 --- a/code/modules/mining/mine_turfs.dm +++ b/code/modules/mining/mine_turfs.dm @@ -82,6 +82,7 @@ var/list/mining_overlay_cache = list() /turf/simulated/mineral/proc/update_general() update_icon(1) if(ticker && ticker.current_state == GAME_STATE_PLAYING) + recalc_atom_opacity() reconsider_lights() if(air_master) air_master.mark_for_update(src) diff --git a/code/modules/mob/living/carbon/alien/diona/life.dm b/code/modules/mob/living/carbon/alien/diona/life.dm index e7fdc43dbc..fecf9b12c0 100644 --- a/code/modules/mob/living/carbon/alien/diona/life.dm +++ b/code/modules/mob/living/carbon/alien/diona/life.dm @@ -4,12 +4,7 @@ var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing if(isturf(loc)) //else, there's considered to be no light var/turf/T = loc - var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T - if(L) - light_amount = 2 * (min(5,L.lum_r + L.lum_g + L.lum_b) - 2.5) //hardcapped so it's not abused by having a ton of flashlights - else - light_amount = 5 - + light_amount = T.get_lumcount() * 5 nutrition += light_amount diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 3f863f04f5..de58677c81 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -706,7 +706,7 @@ if(istype(src.glasses, /obj/item/clothing/glasses/sunglasses)) if(istype(src.glasses, /obj/item/clothing/glasses/sunglasses/sechud/aviator)) var/obj/item/clothing/glasses/sunglasses/sechud/aviator/S = src.glasses - if(!S.active) + if(!S.on) number += 1 else if(istype(src.glasses, /obj/item/clothing/glasses/sunglasses/medhud/aviator)) number += 0 diff --git a/code/modules/mob/living/carbon/human/human_powers.dm b/code/modules/mob/living/carbon/human/human_powers.dm index 0567be8e8f..c21b070c46 100644 --- a/code/modules/mob/living/carbon/human/human_powers.dm +++ b/code/modules/mob/living/carbon/human/human_powers.dm @@ -235,8 +235,6 @@ nutrition -= 200 - // Theoretically the only internal organ a slime will have - // is the slime core. but we might as well be thorough. for(var/obj/item/organ/I in internal_organs) if(I.damage > 0) I.damage = 0 @@ -245,6 +243,7 @@ // Replace completely missing limbs. for(var/limb_type in src.species.has_limbs) var/obj/item/organ/external/E = src.organs_by_name[limb_type] + E.disfigured = 0 if(E && (E.is_stump() || (E.status & (ORGAN_DESTROYED|ORGAN_DEAD|ORGAN_MUTATED)))) E.removed() qdel(E) diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 26516fbb61..a44b1e8351 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -817,11 +817,7 @@ var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing if(isturf(loc)) //else, there's considered to be no light var/turf/T = loc - var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T - if(L) - light_amount = min(10,L.lum_r + L.lum_g + L.lum_b) - 2 //hardcapped so it's not abused by having a ton of flashlights - else - light_amount = 10 + light_amount = T.get_lumcount() * 10 nutrition += light_amount traumatic_shock -= light_amount @@ -840,11 +836,7 @@ var/light_amount = 0 if(isturf(loc)) var/turf/T = loc - var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T - if(L) - light_amount = L.lum_r + L.lum_g + L.lum_b //hardcapped so it's not abused by having a ton of flashlights - else - light_amount = 10 + light_amount = T.get_lumcount() * 10 if(light_amount > species.light_dam) //if there's enough light, start dying take_overall_damage(1,1) else //heal in the dark @@ -921,9 +913,9 @@ //Brain damage from Oxyloss if(should_have_organ("brain")) - var/brainOxPercent = 0.02 //Default2% of your current oxyloss is applied as brain damage, 50 oxyloss is 1 brain damage + var/brainOxPercent = 0.015 //Default 1.5% of your current oxyloss is applied as brain damage, 50 oxyloss is 1 brain damage if(CE_STABLE in chem_effects) - brainOxPercent = 0.01 //Halved in effect + brainOxPercent = 0.008 //Halved in effect if(oxyloss >= 20 && prob(5)) adjustBrainLoss(brainOxPercent * oxyloss) @@ -1364,8 +1356,7 @@ //0.1% chance of playing a scary sound to someone who's in complete darkness if(isturf(loc) && rand(1,1000) == 1) var/turf/T = loc - var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T - if(L && L.lum_r + L.lum_g + L.lum_b == 0) + if(T.get_lumcount() <= LIGHTING_SOFT_THRESHOLD) playsound_local(src,pick(scarySounds),50, 1, -1) /mob/living/carbon/human/handle_stomach() diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm index 2eb3c8d2a1..f3cd084ffa 100644 --- a/code/modules/mob/mob_grab.dm +++ b/code/modules/mob/mob_grab.dm @@ -75,14 +75,10 @@ if(affecting) if(affecting.buckled) return null - if(!affecting.Adjacent(affecting.grabbed_by)) - qdel(src) - return null if(state >= GRAB_AGGRESSIVE) animate(affecting, pixel_x = 0, pixel_y = 0, 4, 1) - . = affecting - qdel(src) - return + return affecting + return null @@ -299,9 +295,6 @@ return if(world.time < (last_action + 20)) return - if(!M.Adjacent(user)) - qdel(src) - return last_action = world.time reset_kill_state() //using special grab moves will interrupt choking them @@ -347,9 +340,6 @@ qdel(src) /obj/item/weapon/grab/proc/reset_kill_state() - if(!assailant) - qdel(src) - return if(state == GRAB_KILL) assailant.visible_message("[assailant] lost \his tight grip on [affecting]'s neck!") hud.icon_state = "kill" diff --git a/code/modules/mob/new_player/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories.dm index 7eee7bccd0..0c554f19cc 100644 --- a/code/modules/mob/new_player/sprite_accessories.dm +++ b/code/modules/mob/new_player/sprite_accessories.dm @@ -388,7 +388,7 @@ balding name = "Balding Hair" icon_state = "hair_e" - gender = MALE // turnoff! + gender = MALE familyman name = "The Family Man" @@ -529,9 +529,19 @@ icon_state = "hair_coffeehouse" gender = MALE - undercut + undercut1 name = "Undercut" - icon_state = "hair_undercut" + icon_state = "hair_undercut1" + gender = MALE + + undercut2 + name = "Undercut Swept Right" + icon_state = "hair_undercut2" + gender = MALE + + undercut3 + name = "Undercut Swept Left" + icon_state = "hair_undercut3" gender = MALE partfade @@ -551,7 +561,7 @@ /datum/sprite_accessory/facial_hair icon = 'icons/mob/Human_face.dmi' - gender = MALE // barf (unless you're a dorf, dorfs dig chix /w beards :P) + gender = MALE shaved name = "Shaved" diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index 4639369c7c..02450c2c5c 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -203,6 +203,7 @@ var/list/organ_cache = list() /obj/item/organ/proc/rejuvenate(var/ignore_prosthetic_prefs) damage = 0 status = 0 + germ_level = 0 if(!ignore_prosthetic_prefs && owner && owner.client && owner.client.prefs && owner.client.prefs.real_name == owner.real_name) var/status = owner.client.prefs.organ_data[organ_tag] if(status == "assisted") diff --git a/code/modules/projectiles/targeting/targeting_overlay.dm b/code/modules/projectiles/targeting/targeting_overlay.dm index 2dcf0032be..9e399c10de 100644 --- a/code/modules/projectiles/targeting/targeting_overlay.dm +++ b/code/modules/projectiles/targeting/targeting_overlay.dm @@ -163,6 +163,7 @@ obj/aiming_overlay/proc/update_aiming_deferred() owner.visible_message("\The [owner] turns \the [thing] on \the [target]!") else owner.visible_message("\The [owner] aims \the [thing] at \the [target]!") + log_and_message_admins("aimed \a [thing] at [key_name(target)].") if(owner.client) owner.client.add_gun_icons() diff --git a/code/modules/random_map/drop/droppod_doors.dm b/code/modules/random_map/drop/droppod_doors.dm index eb5b7c3e17..7009a5885b 100644 --- a/code/modules/random_map/drop/droppod_doors.dm +++ b/code/modules/random_map/drop/droppod_doors.dm @@ -70,11 +70,11 @@ // Create a decorative ramp bottom and flatten out our current ramp. density = 0 - opacity = 0 + set_opacity(0) icon_state = "ramptop" var/obj/structure/droppod_door/door_bottom = new(T) door_bottom.deployed = 1 door_bottom.density = 0 - door_bottom.opacity = 0 + door_bottom.set_opacity(0) door_bottom.dir = src.dir door_bottom.icon_state = "rampbottom" \ No newline at end of file diff --git a/code/modules/reagents/reagent_containers/food/drinks/bottle.dm b/code/modules/reagents/reagent_containers/food/drinks/bottle.dm index 0045769d74..3300eb91d6 100644 --- a/code/modules/reagents/reagent_containers/food/drinks/bottle.dm +++ b/code/modules/reagents/reagent_containers/food/drinks/bottle.dm @@ -134,7 +134,7 @@ if(rag) var/underlay_image = image(icon='icons/obj/drinks.dmi', icon_state=rag.on_fire? "[rag_underlay]_lit" : rag_underlay) underlays += underlay_image - copy_light(rag) + set_light(rag.light_range, rag.light_power, rag.light_color) else set_light(0) diff --git a/code/modules/shieldgen/emergency_shield.dm b/code/modules/shieldgen/emergency_shield.dm index 3690a1f2ba..f7bc1d1c80 100644 --- a/code/modules/shieldgen/emergency_shield.dm +++ b/code/modules/shieldgen/emergency_shield.dm @@ -54,8 +54,8 @@ playsound(src.loc, 'sound/effects/EMPulse.ogg', 75, 1) check_failure() - opacity = 1 - spawn(20) if(src) opacity = 0 + set_opacity(1) + spawn(20) if(!deleted(src)) set_opacity(0) ..() @@ -63,8 +63,8 @@ health -= Proj.get_structure_damage() ..() check_failure() - opacity = 1 - spawn(20) if(src) opacity = 0 + set_opacity(1) + spawn(20) if(!deleted(src)) set_opacity(0) /obj/machinery/shield/ex_act(severity) switch(severity) @@ -113,8 +113,8 @@ check_failure() //The shield becomes dense to absorb the blow.. purely asthetic. - opacity = 1 - spawn(20) if(src) opacity = 0 + set_opacity(1) + spawn(20) if(!deleted(src)) set_opacity(0) ..() return diff --git a/code/modules/shieldgen/energy_field.dm b/code/modules/shieldgen/energy_field.dm index 04b471182b..59f2f02ab6 100644 --- a/code/modules/shieldgen/energy_field.dm +++ b/code/modules/shieldgen/energy_field.dm @@ -1,61 +1,100 @@ //---------- actual energy field +// Each field object has a strength var (mensured in "Renwicks"). +// Melee weapons do 5% of their normal (force var) damage, so a harmbaton would do 0.75 Renwick. +// Projectiles do 5% of their structural damage, so a normal laser would do 2 Renwick damage. +// For meteors, one Renwick is about equal to one layer of r-wall. +// Meteors will be completely halted by the shield if the shield survives the impact. +// Explosions do 4 Renwick of damage per severity, at a max of 12. + /obj/effect/energy_field - name = "energy field" + name = "energy shield field" desc = "Impenetrable field of energy, capable of blocking anything as long as it's active." icon = 'icons/obj/machines/shielding.dmi' - icon_state = "shieldsparkles" + icon_state = "shield" + alpha = 100 anchored = 1 layer = 4.1 //just above mobs density = 0 - invisibility = 101 - var/strength = 0 + var/obj/machinery/shield_gen/my_gen = null + var/strength = 0 // in Renwicks var/ticks_recovering = 10 + var/max_strength = 10 -/obj/effect/energy_field/New() - ..() +/obj/effect/energy_field/New(var/newloc, var/new_gen) + ..(newloc) + my_gen = new_gen update_nearby_tiles() /obj/effect/energy_field/Destroy() update_nearby_tiles() + my_gen.field.Remove(src) + my_gen = null + var/turf/current_loc = get_turf(src) + spawn(1) // Updates neightbors after we're gone. + for(var/direction in cardinal) + var/turf/T = get_step(current_loc, direction) + if(T) + for(var/obj/effect/energy_field/F in T) + F.update_icon() ..() /obj/effect/energy_field/ex_act(var/severity) - Stress(0.5 + severity) + adjust_strength(-(4 - severity) * 4) /obj/effect/energy_field/bullet_act(var/obj/item/projectile/Proj) - Stress(Proj.get_structure_damage() / 10) + adjust_strength(-Proj.get_structure_damage() / 10) -/obj/effect/energy_field/proc/Stress(var/severity) - strength -= severity +/obj/effect/energy_field/attackby(obj/item/W, mob/user) + if(W.force) + adjust_strength(-W.force / 20) + user.do_attack_animation(src) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + ..() - //if we take too much damage, drop out - the generator will bring us back up if we have enough power - ticks_recovering = min(ticks_recovering + 2, 10) - if(strength < 1) - invisibility = 101 - density = 0 - ticks_recovering = 10 - strength = 0 - else if(strength >= 1) - invisibility = 0 - density = 1 +/obj/effect/energy_field/attack_hand(var/mob/living/user) + impact_effect(3) // Harmless, but still produces the 'impact' effect. + ..() -/obj/effect/energy_field/proc/Strengthen(var/severity) - strength += severity - if (strength < 0) - strength = 0 +/obj/effect/energy_field/Bumped(atom/A) + ..(A) + impact_effect(2) - //if we take too much damage, drop out - the generator will bring us back up if we have enough power +/obj/effect/energy_field/handle_meteor_impact(var/obj/effect/meteor/meteor) + var/penetrated = TRUE + adjust_strength(-max((meteor.wall_power * meteor.hits) / 800, 0)) // One renwick (strength var) equals one r-wall for the purposes of meteor-stopping. + sleep(1) + if(density) // Check if we're still up. + penetrated = FALSE + explosion(meteor.loc, 0, 0, 0, 0, 0, 0, 0) // For the sound effect. + + // Returning FALSE will kill the meteor. + return penetrated // If the shield's still around, the meteor was successfully stopped, otherwise keep going and plow into the station. + +/obj/effect/energy_field/proc/adjust_strength(amount, impact = 1) var/old_density = density - if(strength >= 1) - invisibility = 0 - density = 1 - else if(strength < 1) - invisibility = 101 - density = 0 - - if (density != old_density) + strength = between(0, strength + amount, max_strength) + + //maptext = "[round(strength, 0.1)]/[max_strength]" + + //if we take too much damage, drop out - the generator will bring us back up if we have enough power + if(amount < 0) // Taking damage. + if(impact) + impact_effect(round(abs(amount * 2))) + + ticks_recovering = min(ticks_recovering + 2, 10) + if(strength < 1) // We broke + density = 0 + ticks_recovering = 10 + strength = 0 + + else if(amount > 0) // Healing damage. + if(strength >= 1) + density = 1 + + if(density != old_density) + update_icon() update_nearby_tiles() /obj/effect/energy_field/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0) @@ -66,3 +105,44 @@ //return (!density || !height || air_group) return !density + +/obj/effect/energy_field/update_icon(var/update_neightbors = 0) + overlays.Cut() + var/list/adjacent_shields_dir = list() + for(var/direction in cardinal) + var/turf/T = get_step(src, direction) + if(T) // Incase we somehow stepped off the map. + for(var/obj/effect/energy_field/F in T) + if(update_neightbors) + F.update_icon(0) + adjacent_shields_dir |= direction + break + // Icon_state and Glow + if(density) + icon_state = "shield" + set_light(3, 3, "#66FFFF") + else + icon_state = "shield_broken" + set_light(3, 5, "#FF9900") + + // Edge overlays + for(var/found_dir in adjacent_shields_dir) + overlays += image(src.icon, src, icon_state = "shield_edge", dir = found_dir) + + +// Small visual effect, makes the shield tiles brighten up by becoming more opaque for a moment, and spreads to nearby shields. +/obj/effect/energy_field/proc/impact_effect(var/i, var/list/affected_shields = list()) + i = between(1, i, 10) + alpha = 200 + animate(src, alpha = initial(alpha), time = 1 SECOND) + affected_shields |= src + i-- + if(i) + spawn(2) + for(var/direction in cardinal) + var/turf/T = get_step(src, direction) + if(T) // Incase we somehow stepped off the map. + for(var/obj/effect/energy_field/F in T) + if(!(F in affected_shields)) + F.impact_effect(i, affected_shields) // Spread the effect to them. + diff --git a/code/modules/shieldgen/handheld_defuser.dm b/code/modules/shieldgen/handheld_defuser.dm new file mode 100644 index 0000000000..a2909fa7e6 --- /dev/null +++ b/code/modules/shieldgen/handheld_defuser.dm @@ -0,0 +1,49 @@ +/obj/item/weapon/shield_diffuser + name = "portable shield diffuser" + desc = "A small handheld device designed to disrupt energy barriers" + description_info = "This device disrupts shields on directly adjacent tiles (in a + shaped pattern), in a similar way the floor mounted variant does. It is, however, portable and run by an internal battery. Can be recharged with a regular recharger." + icon = 'icons/obj/machines/shielding.dmi' + icon_state = "hdiffuser_off" + var/obj/item/weapon/cell/device/cell + var/enabled = 0 + +/obj/item/weapon/shield_diffuser/update_icon() + if(enabled) + icon_state = "hdiffuser_on" + else + icon_state = "hdiffuser_off" + +/obj/item/weapon/shield_diffuser/New() + cell = new(src) + ..() + +/obj/item/weapon/shield_diffuser/Destroy() + qdel(cell) + cell = null + if(enabled) + processing_objects.Remove(src) + . = ..() + +/obj/item/weapon/shield_diffuser/process() + if(!enabled) + return + + for(var/direction in cardinal) + var/turf/simulated/shielded_tile = get_step(get_turf(src), direction) + for(var/obj/effect/energy_field/S in shielded_tile) + if(istype(S) && cell.checked_use(10 KILOWATTS * CELLRATE)) + qdel(S) + +/obj/item/weapon/shield_diffuser/attack_self() + enabled = !enabled + update_icon() + if(enabled) + processing_objects.Add(src) + else + processing_objects.Remove(src) + to_chat(usr, "You turn \the [src] [enabled ? "on" : "off"].") + +/obj/item/weapon/shield_diffuser/examine() + . = ..() + to_chat(usr, "The charge meter reads [cell ? cell.percent() : 0]%") + to_chat(usr, "It is [enabled ? "enabled" : "disabled"].") \ No newline at end of file diff --git a/code/modules/shieldgen/shield_capacitor.dm b/code/modules/shieldgen/shield_capacitor.dm index 45d7e311dd..53ddcec06e 100644 --- a/code/modules/shieldgen/shield_capacitor.dm +++ b/code/modules/shieldgen/shield_capacitor.dm @@ -19,14 +19,6 @@ var/charge_rate = 100000 //100 kW var/obj/machinery/shield_gen/owned_gen -/obj/machinery/shield_capacitor/New() - spawn(10) - for(var/obj/machinery/shield_gen/possible_gen in range(1, src)) - if(get_dir(src, possible_gen) == src.dir) - possible_gen.owned_capacitor = src - break - ..() - /obj/machinery/shield_capacitor/emag_act(var/remaining_charges, var/mob/user) if(prob(75)) src.locked = !src.locked @@ -54,13 +46,13 @@ if(anchored) spawn(0) for(var/obj/machinery/shield_gen/gen in range(1, src)) - if(get_dir(src, gen) == src.dir && !gen.owned_capacitor) + if(get_dir(src, gen) == src.dir) owned_gen = gen - owned_gen.owned_capacitor = src + owned_gen.capacitors |= src owned_gen.updateDialog() else - if(owned_gen && owned_gen.owned_capacitor == src) - owned_gen.owned_capacitor = null + if(owned_gen && src in owned_gen.capacitors) + owned_gen.capacitors -= src owned_gen = null else ..() @@ -82,16 +74,16 @@ else t += "This capacitor is: [active ? "Online" : "Offline" ] [active ? "\[Deactivate\]" : "\[Activate\]"]
" t += "Capacitor Status: [time_since_fail > 2 ? "OK." : "Discharging!"]
" - t += "Stored Energy: [round(stored_charge/1000, 0.1)] kJ ([100 * round(stored_charge/max_charge, 0.1)]%)
" + t += "Stored Energy: [format_SI(stored_charge, "J")] ([100 * round(stored_charge/max_charge, 0.01)]%)
" t += "Charge Rate: \ \[----\] \ \[---\] \ \[--\] \ - \[-\][charge_rate] W \ + \[-\][format_SI(charge_rate, "W")]\ \[+\] \ \[++\] \ \[+++\] \ - \[+++\]
" + \[++++\]
" t += "
" t += "Refresh " t += "Close
" diff --git a/code/modules/shieldgen/shield_diffuser.dm b/code/modules/shieldgen/shield_diffuser.dm new file mode 100644 index 0000000000..1d2edb43cb --- /dev/null +++ b/code/modules/shieldgen/shield_diffuser.dm @@ -0,0 +1,74 @@ +/obj/machinery/shield_diffuser + name = "shield diffuser" + desc = "A small underfloor device specifically designed to disrupt energy barriers." + description_info = "This device disrupts shields on directly adjacent tiles (in a + shaped pattern). They are commonly installed around exterior airlocks to prevent shields from blocking EVA access." + icon = 'icons/obj/machines/shielding.dmi' + icon_state = "fdiffuser_on" + use_power = 2 + idle_power_usage = 100 + active_power_usage = 2000 + anchored = 1 + density = 0 + level = 1 + var/alarm = 0 + var/enabled = 1 + +/obj/machinery/shield_diffuser/New() + ..() + var/turf/T = get_turf(src) + hide(!T.is_plating()) + +//If underfloor, hide the cable +/obj/machinery/shield_diffuser/hide(var/i) + if(istype(loc, /turf)) + invisibility = i ? 101 : 0 + update_icon() + +/obj/machinery/shield_diffuser/hides_under_flooring() + return 1 + +/obj/machinery/shield_diffuser/process() + if(alarm) + alarm-- + if(!alarm) + update_icon() + return + + if(!enabled) + return + for(var/direction in cardinal) + var/turf/simulated/shielded_tile = get_step(get_turf(src), direction) + for(var/obj/effect/energy_field/S in shielded_tile) + qdel(S) + +/obj/machinery/shield_diffuser/update_icon() + if(alarm) + icon_state = "fdiffuser_emergency" + return + if((stat & (NOPOWER | BROKEN)) || !enabled) + icon_state = "fdiffuser_off" + else + icon_state = "fdiffuser_on" + +/obj/machinery/shield_diffuser/attack_hand() + if(alarm) + to_chat(usr, "You press an override button on \the [src], re-enabling it.") + alarm = 0 + update_icon() + return + enabled = !enabled + use_power = enabled + 1 + update_icon() + to_chat(usr, "You turn \the [src] [enabled ? "on" : "off"].") + +/obj/machinery/shield_diffuser/proc/meteor_alarm(var/duration) + if(!duration) + return + alarm = round(max(alarm, duration)) + update_icon() + +/obj/machinery/shield_diffuser/examine(var/mob/user) + . = ..() + to_chat(user, "It is [enabled ? "enabled" : "disabled"].") + if(alarm) + to_chat(user, "A red LED labeled \"Proximity Alarm\" is blinking on the control panel.") \ No newline at end of file diff --git a/code/modules/shieldgen/shield_gen.dm b/code/modules/shieldgen/shield_gen.dm index 90e32d6492..1b50407be5 100644 --- a/code/modules/shieldgen/shield_gen.dm +++ b/code/modules/shieldgen/shield_gen.dm @@ -1,10 +1,3 @@ -//renwicks: fictional unit to describe shield strength -//a small meteor hit will deduct 1 renwick of strength from that shield tile -//light explosion range will do 1 renwick's damage -//medium explosion range will do 2 renwick's damage -//heavy explosion range will do 3 renwick's damage -//explosion damage is cumulative. if a tile is in range of light, medium and heavy damage, it will take a hit from all three - /obj/machinery/shield_gen name = "bubble shield generator" desc = "Machine that generates an impenetrable field of energy when activated." @@ -13,7 +6,7 @@ var/active = 0 var/field_radius = 3 var/max_field_radius = 100 - var/list/field + var/list/field = list() density = 1 var/locked = 0 var/average_field_strength = 0 @@ -23,20 +16,25 @@ var/min_dissipation = 0.01 //will dissipate by at least this rate in renwicks per field tile (otherwise field would never dissipate completely as dissipation is a percentage) var/powered = 0 var/check_powered = 1 - var/obj/machinery/shield_capacitor/owned_capacitor + var/list/capacitors = list() var/target_field_strength = 10 var/max_field_strength = 10 var/time_since_fail = 100 var/energy_conversion_rate = 0.0002 //how many renwicks per watt? + var/z_range = 0 // How far 'up and or down' to extend the shield to, in z-levels. Only works on MultiZ supported z-levels. use_power = 0 //doesn't use APC power /obj/machinery/shield_gen/New() - spawn(10) - for(var/obj/machinery/shield_capacitor/possible_cap in range(1, src)) - if(get_dir(possible_cap, src) == possible_cap.dir) - owned_capacitor = possible_cap - break - field = new/list() + spawn(1 SECOND) + if(anchored) + for(var/obj/machinery/shield_capacitor/cap in range(1, src)) + if(!cap.anchored) + continue + if(cap.owned_gen) + continue + if(get_dir(cap, src) == cap.dir) + capacitors |= cap + cap.owned_gen = src ..() /obj/machinery/shield_gen/Destroy() @@ -44,7 +42,7 @@ field.Remove(D) D.loc = null ..() - + /obj/machinery/shield_gen/emag_act(var/remaining_charges, var/mob/user) if(prob(75)) src.locked = !src.locked @@ -76,14 +74,14 @@ if(cap.owned_gen) continue if(get_dir(cap, src) == cap.dir && src.anchored) - owned_capacitor = cap - owned_capacitor.owned_gen = src + // owned_capacitor = cap + capacitors |= cap + cap.owned_gen = src updateDialog() - break + // break else - if(owned_capacitor && owned_capacitor.owned_gen == src) - owned_capacitor.owned_gen = null - owned_capacitor = null + for(var/obj/machinery/shield_capacitor/capacitor in capacitors) + capacitor.owned_gen = null else ..() @@ -105,7 +103,13 @@ if(locked) t += "Swipe your ID card to begin." else - t += "[owned_capacitor ? "Charge capacitor connected." : "Unable to locate charge capacitor!"]
" + t += "[capacitors.len ? "Charge capacitor(s) connected." : "Unable to locate charge capacitor!"]
" + var/i = 0 + for(var/obj/machinery/shield_capacitor/capacitor in capacitors) + i++ + t += "Capacitor #[i]: [capacitor.active ? "Online." : "Offline."] \ + Charge: [round(capacitor.stored_charge/1000, 0.1)] kJ ([100 * round(capacitor.stored_charge/capacitor.max_charge, 0.01)]%) \ + Status: [capacitor.time_since_fail > 2 ? "OK." : "Discharging!"]
" t += "This generator is: [active ? "Online" : "Offline" ] [active ? "\[Deactivate\]" : "\[Activate\]"]
" t += "Field Status: [time_since_fail > 2 ? "Stable" : "Unstable"]
" t += "Coverage Radius (restart required): \ @@ -116,12 +120,17 @@ + \ ++ \ +++
" + if(HasAbove(src.z) || HasBelow(src.z)) // Won't show up on maps lacking MultiZ support. + t += "Vertical Shielding (restart required): \ + - \ + [z_range] Vertical Range \ + +
" t += "Overall Field Strength: [round(average_field_strength, 0.01)] Renwick ([target_field_strength ? round(100 * average_field_strength / target_field_strength, 0.1) : "NA"]%)
" - t += "Upkeep Power: [round(field.len * max(average_field_strength * dissipation_rate, min_dissipation) / energy_conversion_rate)] W
" + t += "Upkeep Power: [format_SI(round(field.len * max(average_field_strength * dissipation_rate, min_dissipation) / energy_conversion_rate), "W")]
" t += "Charge Rate: -- \ [strengthen_rate] Renwick/s \ ++
" - t += "Shield Generation Power: [round(field.len * min(strengthen_rate, target_field_strength - average_field_strength) / energy_conversion_rate)] W
" + t += "Shield Generation Power: [format_SI(round(field.len * min(strengthen_rate, target_field_strength - average_field_strength) / energy_conversion_rate), "W")]
" t += "Maximum Field Strength: \ \[min\] \ -- \ @@ -148,13 +157,28 @@ var/renwick_upkeep_per_field = max(average_field_strength * dissipation_rate, min_dissipation) //figure out how much energy we need to draw from the capacitor - if(active && owned_capacitor && owned_capacitor.active) + if(active && capacitors.len) + // Get a list of active capacitors to drain from. + var/list/active_capacitors = list() + for(var/obj/machinery/shield_capacitor/capacitor in capacitors) // Some capacitors might be off. Exclude them. + if(capacitor.active && capacitor.stored_charge > 0) + active_capacitors |= capacitor + var/target_renwick_increase = min(target_field_strength - average_field_strength, strengthen_rate) + renwick_upkeep_per_field //per field tile var/required_energy = field.len * target_renwick_increase / energy_conversion_rate - var/assumed_charge = min(owned_capacitor.stored_charge, required_energy) + + // Gets the charge for all capacitors + var/sum_charge = 0 + for(var/obj/machinery/shield_capacitor/capacitor in active_capacitors) + sum_charge += capacitor.stored_charge + + var/assumed_charge = min(sum_charge, required_energy) total_renwick_increase = assumed_charge * energy_conversion_rate - owned_capacitor.stored_charge -= assumed_charge + + for(var/obj/machinery/shield_capacitor/capacitor in active_capacitors) + capacitor.stored_charge -= max(assumed_charge / active_capacitors.len, 0) // Drain from all active capacitors evenly. + else renwick_upkeep_per_field = max(renwick_upkeep_per_field, 0.5) @@ -162,12 +186,13 @@ average_field_strength = 0 //recalculate the average field strength for(var/obj/effect/energy_field/E in field) + E.max_strength = target_field_strength var/amount_to_strengthen = renwick_increase_per_field - renwick_upkeep_per_field if(E.ticks_recovering > 0 && amount_to_strengthen > 0) - E.Strengthen( min(amount_to_strengthen / 10, 0.1) ) + E.adjust_strength( min(amount_to_strengthen / 10, 0.1), 0 ) E.ticks_recovering -= 1 else - E.Strengthen(amount_to_strengthen) + E.adjust_strength(amount_to_strengthen, 0) average_field_strength += E.strength @@ -194,6 +219,8 @@ strengthen_rate = between(0, strengthen_rate + text2num(href_list["strengthen_rate"]), max_strengthen_rate) else if( href_list["target_field_strength"] ) target_field_strength = between(1, target_field_strength + text2num(href_list["target_field_strength"]), max_field_strength) + else if( href_list["z_range"] ) + z_range = between(0, z_range + text2num(href_list["z_range"]), 10) // Max is extending ten z-levels up and down. Probably too big of a number but it shouldn't matter. updateDialog() @@ -213,16 +240,19 @@ if(T in covered_turfs) covered_turfs.Remove(T) for(var/turf/O in covered_turfs) - var/obj/effect/energy_field/E = new(O) + var/obj/effect/energy_field/E = new(O, src) field.Add(E) covered_turfs = null for(var/mob/M in view(5,src)) M << "\icon[src] You hear heavy droning start up." + for(var/obj/effect/energy_field/E in field) // Update the icons here to ensure all the shields have been made already. + E.update_icon() else for(var/obj/effect/energy_field/D in field) field.Remove(D) - D.loc = null + //D.loc = null + qdel(D) for(var/mob/M in view(5,src)) M << "\icon[src] You hear heavy droning fade out." @@ -236,12 +266,38 @@ else icon_state = "generator0" -//TODO MAKE THIS MULTIZ COMPATIBLE //grab the border tiles in a circle around this machine /obj/machinery/shield_gen/proc/get_shielded_turfs() var/list/out = list() - var/turf/gen_turf = get_turf(src) + var/turf/T = get_turf(src) + if (!T) + return + + out += get_shielded_turfs_on_z_level(T) + + if(z_range) + var/i = z_range + while(HasAbove(T.z) && i) + T = GetAbove(T) + i-- + if(istype(T)) + out += get_shielded_turfs_on_z_level(T) + + T = get_turf(src) + i = z_range + + while(HasBelow(T.z) && i) + T = GetBelow(T) + i-- + if(istype(T)) + out += get_shielded_turfs_on_z_level(T) + + return out + +/obj/machinery/shield_gen/proc/get_shielded_turfs_on_z_level(var/turf/gen_turf) + var/list/out = list() + if (!gen_turf) return @@ -260,4 +316,4 @@ T = locate(gen_turf.x + field_radius, gen_turf.y + y_offset, gen_turf.z) if (T) out += T - return out + return out \ No newline at end of file diff --git a/code/modules/shieldgen/shield_gen_external.dm b/code/modules/shieldgen/shield_gen_external.dm index 182339d64e..8086d0b7e3 100644 --- a/code/modules/shieldgen/shield_gen_external.dm +++ b/code/modules/shieldgen/shield_gen_external.dm @@ -7,15 +7,13 @@ /obj/machinery/shield_gen/external/New() ..() -//NOT MULTIZ COMPATIBLE //Search for space turfs within range that are adjacent to a simulated turf. -/obj/machinery/shield_gen/external/get_shielded_turfs() +/obj/machinery/shield_gen/external/get_shielded_turfs_on_z_level(var/turf/gen_turf) var/list/out = list() - - var/turf/gen_turf = get_turf(src) + if (!gen_turf) return - + var/turf/T for (var/x_offset = -field_radius; x_offset <= field_radius; x_offset++) for (var/y_offset = -field_radius; y_offset <= field_radius; y_offset++) @@ -24,4 +22,4 @@ //check neighbors of T if (locate(/turf/simulated/) in orange(1, T)) out += T - return out + return out \ No newline at end of file diff --git a/code/modules/tables/interactions.dm b/code/modules/tables/interactions.dm index 954ab8156b..35dfdc33b6 100644 --- a/code/modules/tables/interactions.dm +++ b/code/modules/tables/interactions.dm @@ -12,7 +12,8 @@ return 1 if(locate(/obj/structure/table/bench) in get_turf(mover)) return 0 - if(locate(/obj/structure/table) in get_turf(mover)) + var/obj/structure/table/table = locate(/obj/structure/table) in get_turf(mover) + if(table && !table.flipped) return 1 return 0 diff --git a/code/modules/xenoarcheaology/effects/forcefield.dm b/code/modules/xenoarcheaology/effects/forcefield.dm index 6a48620b40..028eff3a75 100644 --- a/code/modules/xenoarcheaology/effects/forcefield.dm +++ b/code/modules/xenoarcheaology/effects/forcefield.dm @@ -30,9 +30,9 @@ ..() for(var/obj/effect/energy_field/E in created_field) if(E.strength < 1) - E.Strengthen(0.15) + E.adjust_strength(0.15, 0) else if(E.strength < 5) - E.Strengthen(0.25) + E.adjust_strength(0.25, 0) /datum/artifact_effect/forcefield/UpdateMove() if(created_field.len && holder) diff --git a/html/changelog.html b/html/changelog.html index 9241626f16..9686d06097 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -57,6 +57,22 @@

Anewbe updated: