mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-10 10:21:11 +00:00
Proc for generating map renders (#10302)
* Proc for generating map renders I mean what else is there to say, it doesn't make it into one large icon unfortunately but someone could do that. Or you could use the goon method to display. * This is useful as well * Modifications 1. Changed getflaticon to show more directional sprites 2. Changed getflaticon to properly order the rednering of underlays -> sprite -> overlays 3. Changed maprender to use a transparent icon instead of a full alpha white one 4. Changed maprender proc to use a better sorting method stolen from getflaticon 5. Changed maprender to eliminate spaces in the outputted folder paths 6. Changed maprender to properly name the maprenders so they are ordered correctly * Small fix * Update map link * Fix some more directional objects, fix multitile objects
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -36,6 +36,9 @@ players2.sqlite
|
||||
# New CMC Minimap images.
|
||||
/data/minimaps/*
|
||||
|
||||
# Map rendering
|
||||
/maprendering/renderoutput/*
|
||||
|
||||
# Backup maps created by maptools
|
||||
*.dmm.backup
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ var/list/directional = list(
|
||||
/obj/machinery/door/window,
|
||||
/obj/machinery/power/emitter,
|
||||
/obj/structure/disposalpipe,
|
||||
/obj/machinery/atmospherics/pipe,
|
||||
/obj/machinery/atmospherics,
|
||||
/obj/structure/window,
|
||||
/obj/structure/window/full,
|
||||
/obj/structure/bed/chair,
|
||||
@@ -38,13 +38,27 @@ var/list/directional = list(
|
||||
/obj/machinery/door/firedoor/border_only,
|
||||
/obj/item/projectile,
|
||||
/obj/effect/beam/emitter,
|
||||
/obj/machinery/conveyor
|
||||
/obj/machinery/conveyor,
|
||||
/obj/machinery/gateway,
|
||||
/obj/structure/shuttle/engine,
|
||||
/obj/machinery/power/terminal,
|
||||
/obj/machinery/power/generator,
|
||||
/obj/structure/particle_accelerator,
|
||||
/obj/structure/hanging_lantern,
|
||||
/turf/simulated/shuttle/wall
|
||||
)
|
||||
|
||||
var/list/exception = list(
|
||||
/obj/structure/window/full
|
||||
/obj/structure/window/full,
|
||||
/obj/machinery/atmospherics/unary/tank
|
||||
)
|
||||
|
||||
/client/verb/testflaticon(atom/A as mob|obj|turf)
|
||||
set name = "hey"
|
||||
var/dir = input("Hello here is the dir", "Dir") as num
|
||||
var/icon/I = getFlatIcon(A, dir)
|
||||
usr << browse(bicon(I), "window=hi")
|
||||
|
||||
proc/getFlatIcon(atom/A, dir, cache=1, exact=0) // 1 = use cache, 2 = override cache, 0 = ignore cache //exact = 1 means the atom won't be rotated if it's a lying mob/living/carbon
|
||||
|
||||
|
||||
@@ -65,11 +79,14 @@ proc/getFlatIcon(atom/A, dir, cache=1, exact=0) // 1 = use cache, 2 = override c
|
||||
else
|
||||
dir = 2//ugly fix for atoms showing invisible on pictures if they don't have a 4-directional icon_state sprite and their dir isn't south(2)
|
||||
|
||||
var/list/initialimage = list()
|
||||
// Add the atom's icon itself
|
||||
if(A.icon)
|
||||
// Make a copy without pixel_x/y settings
|
||||
if(A.pixel_x || A.pixel_y) //Lets assume any pixel shifted icon is directional
|
||||
dir = A.dir
|
||||
var/image/copy = image(icon=A.icon,icon_state=A.icon_state,layer=A.layer,dir=dir)
|
||||
layers[copy] = A.layer
|
||||
initialimage[copy] = A.layer
|
||||
|
||||
|
||||
// Loop through the underlays, then overlays, sorting them into the layers list
|
||||
@@ -83,8 +100,13 @@ proc/getFlatIcon(atom/A, dir, cache=1, exact=0) // 1 = use cache, 2 = override c
|
||||
var/compareOverlay // The overlay that the current overlay is being compared against
|
||||
var/compareIndex // The index in the layers list of 'compare'
|
||||
|
||||
var/list/underlaysort = list()
|
||||
var/list/overlaysort = list()
|
||||
var/list/sorting = underlaysort
|
||||
|
||||
while(TRUE)
|
||||
if(currentIndex<=process.len)
|
||||
//All this does is find the appropriate layer and image
|
||||
currentOverlay = process[currentIndex]
|
||||
currentLayer = currentOverlay:layer
|
||||
if(currentLayer<0) // Special case for FLY_LAYER
|
||||
@@ -94,15 +116,20 @@ proc/getFlatIcon(atom/A, dir, cache=1, exact=0) // 1 = use cache, 2 = override c
|
||||
else // Overlay
|
||||
currentLayer = A.layer+(1000+currentLayer)/1000
|
||||
|
||||
// Sort add into layers list
|
||||
for(compareIndex=1,compareIndex<=layers.len,compareIndex++)
|
||||
compareOverlay = layers[compareIndex]
|
||||
if(currentLayer < layers[compareOverlay]) // Associated value is the calculated layer
|
||||
layers.Insert(compareIndex,currentOverlay)
|
||||
layers[currentOverlay] = currentLayer
|
||||
//Next is a simple sort algorithm to place the overlay by layer
|
||||
if(!sorting.len)
|
||||
sorting[currentOverlay] = currentLayer
|
||||
currentIndex++
|
||||
continue
|
||||
|
||||
for(compareIndex=1,compareIndex<=sorting.len,compareIndex++)
|
||||
compareOverlay = sorting[compareIndex]
|
||||
if(currentLayer < sorting[compareOverlay]) // Associated value is the calculated layer
|
||||
sorting.Insert(compareIndex,currentOverlay)
|
||||
sorting[currentOverlay] = currentLayer
|
||||
break
|
||||
if(compareIndex>layers.len) // Reached end of list without inserting
|
||||
layers[currentOverlay]=currentLayer // Place at end
|
||||
if(compareIndex>sorting.len) // Reached end of list without inserting
|
||||
sorting[currentOverlay]=currentLayer // Place at end
|
||||
|
||||
currentIndex++
|
||||
|
||||
@@ -111,9 +138,16 @@ proc/getFlatIcon(atom/A, dir, cache=1, exact=0) // 1 = use cache, 2 = override c
|
||||
currentIndex = 1
|
||||
processSubset = 1
|
||||
process = A.overlays
|
||||
sorting = overlaysort
|
||||
else // All done
|
||||
break
|
||||
|
||||
//Get flat icon previously understood layers as interspersing
|
||||
//and could render overlays above the atom's icon before this following modification
|
||||
layers = underlaysort
|
||||
layers += initialimage
|
||||
layers += overlaysort
|
||||
|
||||
if(cache!=0) // If cache is NOT disabled
|
||||
// Create a hash value to represent this specific flattened icon
|
||||
for(var/I in layers)
|
||||
@@ -142,10 +176,13 @@ proc/getFlatIcon(atom/A, dir, cache=1, exact=0) // 1 = use cache, 2 = override c
|
||||
var/addY2
|
||||
|
||||
for(var/I in layers)
|
||||
var/layerdir = I:dir //We want overlays/underlays to use their correct directional icon state
|
||||
if(I == A || !layerdir)
|
||||
layerdir = dir
|
||||
|
||||
add = icon(I:icon || A.icon
|
||||
, I:icon_state || (I:icon && (A.icon_state in icon_states(I:icon)) && A.icon_state)
|
||||
, dir
|
||||
, layerdir
|
||||
, 1
|
||||
, 0)
|
||||
|
||||
|
||||
@@ -360,7 +360,8 @@ var/list/admin_verbs_mod = list(
|
||||
/client/proc/cmd_admin_areatest,
|
||||
/client/proc/readmin,
|
||||
/client/proc/nanomapgen_DumpImage,
|
||||
/client/proc/nanomapgen_DumpImageAll
|
||||
/client/proc/nanomapgen_DumpImageAll,
|
||||
/client/proc/maprender
|
||||
)
|
||||
|
||||
/client/proc/hide_most_verbs()//Allows you to keep some functionality while hiding some verbs
|
||||
|
||||
@@ -167,6 +167,7 @@ var/intercom_range_display_status = 0
|
||||
src.verbs += /client/proc/check_sim_unsim
|
||||
src.verbs += /client/proc/nanomapgen_DumpImage
|
||||
src.verbs += /client/proc/nanomapgen_DumpImageAll
|
||||
src.verbs += /client/proc/maprender
|
||||
//src.verbs += /client/proc/cmd_admin_rejuvenate
|
||||
|
||||
feedback_add_details("admin_verb","mDV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
@@ -9,19 +9,8 @@
|
||||
return
|
||||
if(alert("This will open the map render(s) in your browser. Are you sure?",,"Yes","No")=="No")
|
||||
return
|
||||
if(map)
|
||||
switch(map.nameShort)
|
||||
if("meta")
|
||||
src << link("[config.renders_url]/metaclub/")
|
||||
if("deff")
|
||||
src << link("[config.renders_url]/defficiency/")
|
||||
if("box")
|
||||
src << link("[config.renders_url]/tgstation/")
|
||||
if("taxi")
|
||||
src << link("[config.renders_url]/taxistation/")
|
||||
else
|
||||
to_chat(src, "<span class='warning'>No map render for [map.nameLong], bug Pomf about it!</span>")
|
||||
return
|
||||
var/mapname = replacetext(map.nameLong, " ", "")
|
||||
src << link("[config.renders_url]/images/maps/[mapname]")
|
||||
|
||||
/client/verb/wiki()
|
||||
set name = "wiki"
|
||||
|
||||
BIN
maprendering/maprender.png
Normal file
BIN
maprendering/maprender.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
121
maprendering/maprendering.dm
Normal file
121
maprendering/maprendering.dm
Normal file
@@ -0,0 +1,121 @@
|
||||
/client/proc/maprender()
|
||||
set category = "Mapping"
|
||||
set name = "Generate Map Render"
|
||||
|
||||
if(!holder)
|
||||
to_chat(src, "Only administrators may use this command.")
|
||||
return
|
||||
if(alert("Sure you want to do this? It should NEVER be done in an active round and cannot be cancelled", "generate maps", "Yes", "No") == "No")
|
||||
return
|
||||
|
||||
var/allz = alert("Do you wish to generate a specific zlevel or all zlevels?", "Generate what levels?", "All", "Specific", "Cancel")
|
||||
|
||||
var/zlevel = 1
|
||||
if(allz == "Cancel")
|
||||
return
|
||||
else if(allz == "Specific")
|
||||
zlevel = input("Input zlevel you wish to render") as num
|
||||
|
||||
message_admins("[ckey]/[src] started rendering maps")
|
||||
log_admin("[ckey]/[src] started rendering maps")
|
||||
|
||||
maprenders(zlevel, allz == "All" ? 1 : 0)
|
||||
|
||||
/client/proc/maprenders(var/currentz = 1, var/allz = 0)
|
||||
|
||||
to_chat(world, "Map Render: <B>GENERATE MAP FOR [allz? "ALL ZLEVELS" : "LEVEL [currentz]"]</B>")
|
||||
var/mapname = replacetext(map.nameLong, " ", "")
|
||||
|
||||
var/startz = currentz
|
||||
var/endz = currentz
|
||||
if(allz)
|
||||
startz = 1
|
||||
endz = world.maxz
|
||||
|
||||
var/const/icon_size = 64 //Depends on map render icon, in this case we're doing 2048x2048 pixels at 32x32 per tile
|
||||
|
||||
for(var/z = startz to endz)
|
||||
for(var/x = 0 to world.maxx step icon_size)
|
||||
for(var/y = 0 to world.maxy step icon_size)
|
||||
var/list/pixel_shift_objects = list()
|
||||
var/icon/map_icon = new/icon('maprendering/maprender.png') //2048 pixels, thats 32 tiles of 32 pixels
|
||||
for(var/a = 1 to icon_size)
|
||||
for(var/b = 1 to icon_size)
|
||||
//Finding turf and all turf contents
|
||||
var/turf/currentturf = locate(x+a,y+b,z)
|
||||
if(!currentturf || (currentturf.flags & NO_MINIMAP))
|
||||
continue
|
||||
var/list/allturfcontents = currentturf.contents.Copy()
|
||||
|
||||
//Remove the following line to allow lighting to be considered, if you do this it must be blended with BLEND_MULTIPLY instead of ICON_OVERLAY
|
||||
allturfcontents -= locate(/atom/movable/lighting_overlay) in allturfcontents
|
||||
|
||||
for(var/atom/movable/A in allturfcontents)
|
||||
if(A.locs.len > 1) //Fix for multitile objects I wish I didn't have to do this its probably slow
|
||||
if(A.locs[1] != A.loc)
|
||||
allturfcontents -= A
|
||||
|
||||
//Remove the following line if you want to add space to your renders, I think it is cheaper to merely use a pregenned image for this
|
||||
if(!istype(currentturf,/turf/space))
|
||||
allturfcontents += currentturf
|
||||
|
||||
//Due to processing order, a pixelshifted object will be overriden in certain directions,
|
||||
//we'll apply it at the end, they're almost always at the top layer anyway
|
||||
for(var/atom/A in allturfcontents)
|
||||
if(A.pixel_x || A.pixel_y)
|
||||
allturfcontents -= A
|
||||
pixel_shift_objects += A
|
||||
|
||||
if(!allturfcontents.len)
|
||||
continue
|
||||
|
||||
//Initializing our layer sorting variables
|
||||
var/list/sorting = list()
|
||||
var/atom/currentAtom = allturfcontents[1]
|
||||
var/currentLayer
|
||||
sorting[allturfcontents[1]] = currentAtom.layer
|
||||
allturfcontents -= currentAtom
|
||||
var/currentIndex = 1
|
||||
var/compareIndex = 1
|
||||
|
||||
if(allturfcontents.len)
|
||||
//Simple insertion sort, simple variant of the form in getflaticon
|
||||
while(currentIndex <= allturfcontents.len)
|
||||
currentAtom = allturfcontents[currentIndex]
|
||||
currentLayer = currentAtom.layer
|
||||
|
||||
for(compareIndex=1,compareIndex<=sorting.len,compareIndex++)
|
||||
if(currentLayer < sorting[sorting[compareIndex]])
|
||||
sorting.Insert(compareIndex,currentAtom)
|
||||
sorting[currentAtom] = currentLayer
|
||||
break
|
||||
if(compareIndex>sorting.len)
|
||||
sorting[currentAtom]=currentLayer
|
||||
|
||||
currentIndex++
|
||||
|
||||
//Preparing to blend get flat icon of
|
||||
for(var/atom/A in sorting)
|
||||
var/icon/icontoblend = getFlatIcon(A = A, dir = A.dir, cache = 0)
|
||||
map_icon.Blend(icontoblend, ICON_OVERLAY, ((a-1)*world.icon_size)+1, ((b-1)*world.icon_size)+1)
|
||||
sleep(-1)
|
||||
|
||||
for(var/atom/A in pixel_shift_objects)
|
||||
var/icon/icontoblend = getFlatIcon(A = A, dir = A.dir, cache = 0)
|
||||
//This part is tricky since we've skipped a and b, since these are map objects they have valid x,y. a and b should be the modulo'd value of x,y with icon_size
|
||||
map_icon.Blend(icontoblend, ICON_OVERLAY, (((A.x % icon_size)-1)*world.icon_size)+1+A.pixel_x, (((A.y % icon_size)-1)*world.icon_size)+1+A.pixel_y)
|
||||
|
||||
if(y >= world.maxy)
|
||||
map_icon.DrawBox(rgb(255,255,255,255), x1 = 1, y1 = 1, x2 = 32*icon_size, y2 = 32*(icon_size-world.maxy % icon_size))
|
||||
if(x >= world.maxx)
|
||||
map_icon.DrawBox(rgb(255,255,255,255), x1 = 32*(icon_size - world.maxx % icon_size), y1 = 1, x2 = 32*icon_size, y2 = 32*icon_size)
|
||||
|
||||
world.log << "Completed image z: [z], x: [x] to [x/icon_size], y: [round((world.maxy-y)/icon_size)]"
|
||||
var/resultpath = "maprendering/renderoutput/[mapname]/[z]/maprender[round((world.maxy-y)/icon_size)]-[x/icon_size].png"
|
||||
// BYOND BUG: map_icon now contains 4 directions? Create a new icon with only a single state.
|
||||
var/icon/result_icon = new/icon()
|
||||
|
||||
result_icon.Insert(map_icon, "", SOUTH, 1, 0)
|
||||
if(fexists(resultpath))
|
||||
fdel(resultpath)
|
||||
fcopy(result_icon, resultpath)
|
||||
BIN
maprendering/renderoutput/spacebase.png
Normal file
BIN
maprendering/renderoutput/spacebase.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 227 KiB |
@@ -1974,6 +1974,7 @@
|
||||
#include "goon\code\datums\browserOutput.dm"
|
||||
#include "interface\interface.dm"
|
||||
#include "interface\skin.dmf"
|
||||
#include "maprendering\maprendering.dm"
|
||||
#include "maps\_map.dm"
|
||||
#include "maps\tgstation.dm"
|
||||
#include "maps\defficiency\areas.dm"
|
||||
|
||||
Reference in New Issue
Block a user