diff --git a/code/ZAS/Atom.dm b/code/ZAS/Atom.dm index ce69ad7162..4961355940 100644 --- a/code/ZAS/Atom.dm +++ b/code/ZAS/Atom.dm @@ -57,11 +57,11 @@ turf/c_airblock(turf/other) #ifdef ZASDBG ASSERT(isturf(other)) #endif - if(blocks_air || other.blocks_air) + if(((blocks_air & AIR_BLOCKED) || (other.blocks_air & AIR_BLOCKED))) return BLOCKED //Z-level handling code. Always block if there isn't an open space. - #ifdef ZLEVELS + #ifdef MULTIZAS if(other.z != src.z) if(other.z < src.z) if(!istype(src, /turf/simulated/open)) return BLOCKED @@ -69,6 +69,12 @@ turf/c_airblock(turf/other) if(!istype(other, /turf/simulated/open)) return BLOCKED #endif + if(((blocks_air & ZONE_BLOCKED) || (other.blocks_air & ZONE_BLOCKED))) + if(z == other.z) + return ZONE_BLOCKED + else + return AIR_BLOCKED + var/result = 0 for(var/atom/movable/M in contents) result |= M.c_airblock(other) diff --git a/code/ZAS/ConnectionManager.dm b/code/ZAS/ConnectionManager.dm index 1c101f4b45..28ef365830 100644 --- a/code/ZAS/ConnectionManager.dm +++ b/code/ZAS/ConnectionManager.dm @@ -37,7 +37,7 @@ Class Procs: /connection_manager/var/connection/E /connection_manager/var/connection/W -#ifdef ZLEVELS +#ifdef MULTIZAS /connection_manager/var/connection/U /connection_manager/var/connection/D #endif @@ -57,7 +57,7 @@ Class Procs: if(check(W)) return W else return null - #ifdef ZLEVELS + #ifdef MULTIZAS if(UP) if(check(U)) return U else return null @@ -73,7 +73,7 @@ Class Procs: if(EAST) E = c if(WEST) W = c - #ifdef ZLEVELS + #ifdef MULTIZAS if(UP) U = c if(DOWN) D = c #endif @@ -83,7 +83,7 @@ Class Procs: if(check(S)) S.update() if(check(E)) E.update() if(check(W)) W.update() - #ifdef ZLEVELS + #ifdef MULTIZAS if(check(U)) U.update() if(check(D)) D.update() #endif @@ -93,7 +93,7 @@ Class Procs: if(check(S)) S.erase() if(check(E)) E.erase() if(check(W)) W.erase() - #ifdef ZLEVELS + #ifdef MULTIZAS if(check(U)) U.erase() if(check(D)) D.erase() #endif diff --git a/code/ZAS/Controller.dm b/code/ZAS/Controller.dm index cfd6cff414..2469f7a36f 100644 --- a/code/ZAS/Controller.dm +++ b/code/ZAS/Controller.dm @@ -158,6 +158,9 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun //defer updating of self-zone-blocked turfs until after all other turfs have been updated. //this hopefully ensures that non-self-zone-blocked turfs adjacent to self-zone-blocked ones //have valid zones when the self-zone-blocked turfs update. + + //This ensures that doorways don't form their own single-turf zones, since doorways are self-zone-blocked and + //can merge with an adjacent zone, whereas zones that are formed on adjacent turfs cannot merge with the doorway. var/list/deferred = list() for(var/turf/T in updating) diff --git a/code/ZAS/Diagnostic.dm b/code/ZAS/Diagnostic.dm index ed94b7d582..10ec2e731b 100644 --- a/code/ZAS/Diagnostic.dm +++ b/code/ZAS/Diagnostic.dm @@ -39,6 +39,10 @@ client/proc/Test_ZAS_Connection(var/turf/simulated/T as turf) "South" = SOUTH,\ "East" = EAST,\ "West" = WEST,\ + #ifdef MULTIZAS + "Up" = UP,\ + "Down" = DOWN,\ + #endif "N/A" = null) var/direction = input("What direction do you wish to test?","Set direction") as null|anything in direction_list if(!direction) diff --git a/code/ZAS/Turf.dm b/code/ZAS/Turf.dm index 62cad5e469..bb5eb5050e 100644 --- a/code/ZAS/Turf.dm +++ b/code/ZAS/Turf.dm @@ -16,7 +16,7 @@ //dbg(blocked) return 1 - #ifdef ZLEVELS + #ifdef MULTIZAS for(var/d = 1, d < 64, d *= 2) #else for(var/d = 1, d < 16, d *= 2) @@ -52,34 +52,40 @@ */ /turf/simulated/proc/can_safely_remove_from_zone() - #ifdef ZLEVELS - return 0 //TODO generalize this to multiz. - #else - + + if(!zone) return 1 - + var/check_dirs = get_zone_neighbours(src) var/unconnected_dirs = check_dirs - - for(var/dir in list(NORTHWEST, NORTHEAST, SOUTHEAST, SOUTHWEST)) - + + #ifdef MULTIZAS + var/to_check = cornerdirsz + #else + var/to_check = cornerdirs + #endif + + for(var/dir in to_check) //for each pair of "adjacent" cardinals (e.g. NORTH and WEST, but not NORTH and SOUTH) if((dir & check_dirs) == dir) //check that they are connected by the corner turf var/connected_dirs = get_zone_neighbours(get_step(src, dir)) - if(connected_dirs && (dir & turn(connected_dirs, 180)) == dir) + if(connected_dirs && (dir & reverse_dir[connected_dirs]) == dir) unconnected_dirs &= ~dir //they are, so unflag the cardinals in question - + //it is safe to remove src from the zone if all cardinals are connected by corner turfs return !unconnected_dirs - - #endif //helper for can_safely_remove_from_zone() /turf/simulated/proc/get_zone_neighbours(turf/simulated/T) . = 0 if(istype(T) && T.zone) - for(var/dir in cardinal) + #ifdef MULTIZAS + var/to_check = cardinalz + #else + var/to_check = cardinal + #endif + for(var/dir in to_check) var/turf/simulated/other = get_step(T, dir) if(istype(other) && other.zone == T.zone && !(other.c_airblock(T) & AIR_BLOCKED) && get_dist(src, other) <= 1) . |= dir @@ -98,7 +104,7 @@ #endif if(zone) var/zone/z = zone - + if(can_safely_remove_from_zone()) //Helps normal airlocks avoid rebuilding zones all the time z.remove(src) else @@ -110,7 +116,7 @@ open_directions = 0 var/list/postponed - #ifdef ZLEVELS + #ifdef MULTIZAS for(var/d = 1, d < 64, d *= 2) #else for(var/d = 1, d < 16, d *= 2) @@ -161,7 +167,7 @@ //Might have assigned a zone, since this happens for each direction. if(!zone) - //We do not merge if + //We do not merge if // they are blocking us and we are not blocking them, or if // we are blocking them and not blocking ourselves - this prevents tiny zones from forming on doorways. if(((block & ZONE_BLOCKED) && !(r_block & ZONE_BLOCKED)) || ((r_block & ZONE_BLOCKED) && !(s_block & ZONE_BLOCKED))) diff --git a/code/ZAS/_docs.dm b/code/ZAS/_docs.dm index 1f652ffaab..4433478e32 100644 --- a/code/ZAS/_docs.dm +++ b/code/ZAS/_docs.dm @@ -28,8 +28,7 @@ Notes for people who used ZAS before: */ //#define ZASDBG -//#define ZLEVELS - +//#define MULTIZAS #define AIR_BLOCKED 1 #define ZONE_BLOCKED 2 #define BLOCKED 3 diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 57f2247255..5021453462 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -166,6 +166,7 @@ var/list/gamemode_cache = list() var/simultaneous_pm_warning_timeout = 100 var/use_recursive_explosions //Defines whether the server uses recursive or circular explosions. + var/multi_z_explosion_scalar = 0.5 //Multiplier for how much weaker explosions are on neighboring z levels. var/assistant_maint = 0 //Do assistants get maint access? var/gateway_delay = 18000 //How long the gateway takes before it activates. Default is half an hour. @@ -283,6 +284,9 @@ var/list/gamemode_cache = list() if ("use_recursive_explosions") use_recursive_explosions = 1 + if ("multi_z_explosion_scalar") + multi_z_explosion_scalar = text2num(value) + if ("log_ooc") config.log_ooc = 1 diff --git a/code/game/objects/explosion.dm b/code/game/objects/explosion.dm index b081530e10..dd18925da2 100644 --- a/code/game/objects/explosion.dm +++ b/code/game/objects/explosion.dm @@ -1,23 +1,26 @@ //TODO: Flash range does nothing currently -proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1, z_transfer = UP|DOWN) +proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1, z_transfer = UP|DOWN, shaped) + var/multi_z_scalar = config.multi_z_explosion_scalar src = null //so we don't abort once src is deleted spawn(0) - if(config.use_recursive_explosions) - var/power = devastation_range * 2 + heavy_impact_range + light_impact_range //The ranges add up, ie light 14 includes both heavy 7 and devestation 3. So this calculation means devestation counts for 4, heavy for 2 and light for 1 power, giving us a cap of 27 power. - explosion_rec(epicenter, power) - return - var/start = world.timeofday epicenter = get_turf(epicenter) if(!epicenter) return // Handles recursive propagation of explosions. - if(devastation_range > 2 || heavy_impact_range > 2) - if(HasAbove(epicenter.z) && z_transfer & UP) - explosion(GetAbove(epicenter), max(0, devastation_range - 2), max(0, heavy_impact_range - 2), max(0, light_impact_range - 2), max(0, flash_range - 2), 0, UP) - if(HasBelow(epicenter.z) && z_transfer & DOWN) - explosion(GetAbove(epicenter), max(0, devastation_range - 2), max(0, heavy_impact_range - 2), max(0, light_impact_range - 2), max(0, flash_range - 2), 0, DOWN) + if(z_transfer && multi_z_scalar) + var/adj_dev = max(0, (multi_z_scalar * devastation_range) - (shaped ? 2 : 0) ) + var/adj_heavy = max(0, (multi_z_scalar * heavy_impact_range) - (shaped ? 2 : 0) ) + var/adj_light = max(0, (multi_z_scalar * light_impact_range) - (shaped ? 2 : 0) ) + var/adj_flash = max(0, (multi_z_scalar * flash_range) - (shaped ? 2 : 0) ) + + + if(adj_dev > 0 || adj_heavy > 0) + if(HasAbove(epicenter.z) && z_transfer & UP) + explosion(GetAbove(epicenter), round(adj_dev), round(adj_heavy), round(adj_light), round(adj_flash), 0, UP, shaped) + if(HasBelow(epicenter.z) && z_transfer & DOWN) + explosion(GetBelow(epicenter), round(adj_dev), round(adj_heavy), round(adj_light), round(adj_flash), 0, DOWN, shaped) var/max_range = max(devastation_range, heavy_impact_range, light_impact_range, flash_range) @@ -30,31 +33,20 @@ proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impa far_dist += devastation_range * 20 var/frequency = get_rand_frequency() for(var/mob/M in player_list) - // Double check for client - if(M && M.client) + if(M.z == epicenter.z) var/turf/M_turf = get_turf(M) - if(M_turf && M_turf.z == epicenter.z) - var/dist = get_dist(M_turf, epicenter) - // If inside the blast radius + world.view - 2 - if(dist <= round(max_range + world.view - 2, 1)) - M.playsound_local(epicenter, get_sfx("explosion"), 100, 1, frequency, falloff = 5) // get_sfx() is so that everyone gets the same sound + var/dist = get_dist(M_turf, epicenter) + // If inside the blast radius + world.view - 2 + if(dist <= round(max_range + world.view - 2, 1)) + M.playsound_local(epicenter, get_sfx("explosion"), 100, 1, frequency, falloff = 5) // get_sfx() is so that everyone gets the same sound + else if(dist <= far_dist) + var/far_volume = Clamp(far_dist, 30, 50) // Volume is based on explosion size and dist + far_volume += (dist <= far_dist * 0.5 ? 50 : 0) // add 50 volume if the mob is pretty close to the explosion + M.playsound_local(epicenter, 'sound/effects/explosionfar.ogg', far_volume, 1, frequency, falloff = 5) - //You hear a far explosion if you're outside the blast radius. Small bombs shouldn't be heard all over the station. - - else if(dist <= far_dist) - var/far_volume = Clamp(far_dist, 30, 50) // Volume is based on explosion size and dist - far_volume += (dist <= far_dist * 0.5 ? 50 : 0) // add 50 volume if the mob is pretty close to the explosion - M.playsound_local(epicenter, 'sound/effects/explosionfar.ogg', far_volume, 1, frequency, falloff = 5) - - var/close = range(world.view+round(devastation_range,1), epicenter) - // to all distanced mobs play a different sound - for(var/mob/M in world) if(M.z == epicenter.z) if(!(M in close)) - // check if the mob can hear - if(M.ear_deaf <= 0 || !M.ear_deaf) if(!istype(M.loc,/turf/space)) - M << 'sound/effects/explosionfar.ogg' if(adminlog) - message_admins("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range]) in area [epicenter.loc.name] ([epicenter.x],[epicenter.y],[epicenter.z]) (JMP)") - log_game("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range]) in area [epicenter.loc.name] ") + message_admins("Explosion with [shaped ? "shaped" : "non-shaped"] size ([devastation_range], [heavy_impact_range], [light_impact_range]) in area [epicenter.loc.name] ([epicenter.x],[epicenter.y],[epicenter.z]) (JMP)") + log_game("Explosion with [shaped ? "shaped" : "non-shaped"] size ([devastation_range], [heavy_impact_range], [light_impact_range]) in area [epicenter.loc.name] ") var/approximate_intensity = (devastation_range * 3) + (heavy_impact_range * 2) + light_impact_range var/powernet_rebuild_was_deferred_already = defer_powernet_rebuild @@ -70,41 +62,42 @@ proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impa var/x0 = epicenter.x var/y0 = epicenter.y var/z0 = epicenter.z + if(config.use_recursive_explosions) + var/power = devastation_range * 2 + heavy_impact_range + light_impact_range //The ranges add up, ie light 14 includes both heavy 7 and devestation 3. So this calculation means devestation counts for 4, heavy for 2 and light for 1 power, giving us a cap of 27 power. + explosion_rec(epicenter, power, shaped) + else + for(var/turf/T in trange(max_range, epicenter)) + var/dist = sqrt((T.x - x0)**2 + (T.y - y0)**2) - for(var/turf/T in trange(max_range, epicenter)) - var/dist = sqrt((T.x - x0)**2 + (T.y - y0)**2) + if(dist < devastation_range) dist = 1 + else if(dist < heavy_impact_range) dist = 2 + else if(dist < light_impact_range) dist = 3 + else continue - if(dist < devastation_range) dist = 1 - else if(dist < heavy_impact_range) dist = 2 - else if(dist < light_impact_range) dist = 3 - else continue - - T.ex_act(dist) - if(T) + if(!T) + T = locate(x0,y0,z0) for(var/atom_movable in T.contents) //bypass type checking since only atom/movable can be contained by turfs anyway var/atom/movable/AM = atom_movable if(AM && AM.simulated) AM.ex_act(dist) + T.ex_act(dist) + var/took = (world.timeofday-start)/10 //You need to press the DebugGame verb to see these now....they were getting annoying and we've collected a fair bit of data. Just -test- changes to explosion code using this please so we can compare - if(Debug2) world.log << "## DEBUG: Explosion([x0],[y0],[z0])(d[devastation_range],h[heavy_impact_range],l[light_impact_range]): Took [took] seconds." + if(Debug2) world.log << "## DEBUG: Explosion([x0],[y0],[z0])(d[devastation_range],h[heavy_impact_range],l[light_impact_range]): Took [took] seconds." //Machines which report explosions. for(var/i,i<=doppler_arrays.len,i++) var/obj/machinery/doppler_array/Array = doppler_arrays[i] if(Array) Array.sense_explosion(x0,y0,z0,devastation_range,heavy_impact_range,light_impact_range,took) - sleep(8) if(!powernet_rebuild_was_deferred_already && defer_powernet_rebuild) makepowernets() defer_powernet_rebuild = 0 - return 1 - - proc/secondaryexplosion(turf/epicenter, range) for(var/turf/tile in range(range, epicenter)) tile.ex_act(2) diff --git a/code/game/turfs/simulated/wall_attacks.dm b/code/game/turfs/simulated/wall_attacks.dm index 47620d030a..098782ebdf 100644 --- a/code/game/turfs/simulated/wall_attacks.dm +++ b/code/game/turfs/simulated/wall_attacks.dm @@ -1,3 +1,6 @@ +#define ZONE_BLOCKED 2 +#define AIR_BLOCKED 1 + //Interactions /turf/simulated/wall/proc/toggle_open(var/mob/user) @@ -8,7 +11,9 @@ can_open = WALL_OPENING //flick("[material.icon_base]fwall_opening", src) density = 0 + blocks_air = ZONE_BLOCKED update_icon() + update_air() set_light(0) src.blocks_air = 0 set_opacity(0) @@ -18,7 +23,9 @@ can_open = WALL_OPENING //flick("[material.icon_base]fwall_closing", src) density = 1 + blocks_air = AIR_BLOCKED update_icon() + update_air() set_light(1) src.blocks_air = 1 set_opacity(1) @@ -28,6 +35,25 @@ can_open = WALL_CAN_OPEN update_icon() +#undef ZONE_BLOCKED +#undef AIR_BLOCKED + +/turf/simulated/wall/proc/update_air() + if(!air_master) + return + + for(var/turf/simulated/turf in loc) + update_thermal(turf) + air_master.mark_for_update(turf) + + +/turf/simulated/wall/proc/update_thermal(var/turf/simulated/source) + if(istype(source)) + if(density && opacity) + source.thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT + else + source.thermal_conductivity = initial(source.thermal_conductivity) + /turf/simulated/wall/proc/fail_smash(var/mob/user) user << "You smash against the wall!" take_damage(rand(25,75)) diff --git a/code/global.dm b/code/global.dm index 6970e68dff..ea27e3f823 100644 --- a/code/global.dm +++ b/code/global.dm @@ -89,7 +89,9 @@ var/list/blobstart = list() var/list/ninjastart = list() var/list/cardinal = list(NORTH, SOUTH, EAST, WEST) +var/list/cardinalz = list(NORTH, SOUTH, EAST, WEST, UP, DOWN) var/list/cornerdirs = list(NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST) +var/list/cornerdirsz = list(NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST, NORTH|UP, EAST|UP, WEST|UP, SOUTH|UP, NORTH|DOWN, EAST|DOWN, WEST|DOWN, SOUTH|DOWN) var/list/alldirs = list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST) var/list/reverse_dir = list( // reverse_dir[dir] = reverse of dir 2, 1, 3, 8, 10, 9, 11, 4, 6, 5, 7, 12, 14, 13, 15, 32, 34, 33, 35, 40, 42, diff --git a/config/example/config.txt b/config/example/config.txt index 8437829fd3..232d3c66ad 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -33,6 +33,9 @@ JOBS_HAVE_MINIMAL_ACCESS ## Unhash this to use recursive explosions, keep it hashed to use circle explosions. Recursive explosions react to walls, airlocks and blast doors, making them look a lot cooler than the boring old circular explosions. They require more CPU and are (as of january 2013) experimental #USE_RECURSIVE_EXPLOSIONS +Configure how fast explosion strength diminishes when travelling up/down z levels. All explosion distances are multiplied by this each time they go up/down z-levels. +#MULTI_Z_EXPLOSION_SCALAR 0.5 + ## log OOC channel LOG_OOC diff --git a/html/changelogs/Anewbe - MultiZAS.yml b/html/changelogs/Anewbe - MultiZAS.yml new file mode 100644 index 0000000000..c04097eb3d --- /dev/null +++ b/html/changelogs/Anewbe - MultiZAS.yml @@ -0,0 +1,36 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +################################# + +# Your name. +author: Anewbe + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - rscadd: "Ports but does not enable Bay's MultiZAS." \ No newline at end of file diff --git a/html/changelogs/Leshana - MultiZAS.yml b/html/changelogs/Leshana - MultiZAS.yml new file mode 100644 index 0000000000..891bf846c2 --- /dev/null +++ b/html/changelogs/Leshana - MultiZAS.yml @@ -0,0 +1,5 @@ +author: Leshana +delete-after: True +changes: + - tweak: "Optimized (but still not enabled) multi-z ZAS" + - rscadd: "Multi-Z explosion transfer coefficient is now configurable" \ No newline at end of file