Skip to content

Commit

Permalink
Runtime Mapper (#3972)
Browse files Browse the repository at this point in the history
# About the pull request

This PR adds a debug verb Mass-Screenshot that regardless of your zoom
level, teleports you around the map and forces your client to screenshot
images. These images can then be used in a new MapTileImageTool python
script (requires `pip install pillow`) to tile together into a single
image.

For example:
- [LV-624](https://imgur.com/HhxBIVu) ([actual
file](https://cdn.discordapp.com/attachments/322351602075697163/1132607486470389782/LV624.png)
is a 5600x7232 17.8MB PNG)
- [USS Almayer](https://imgur.com/oxCizwc) ([actual
file](https://cdn.discordapp.com/attachments/322351602075697163/1132607484985618492/Almayer.png)
is a 9600x6432 14.1MB PNG)

In order to have accurate map sizes, SSmapping now stores this data in
its z_list.

# Explain why it's good for the game

Allows us to create a full screen shot of an entire z-level even at
runtime as a client sees it. However, it is unlikely this will work
perfectly in multiplayer as players move around or events occur. Two
passes might be a solution to mitigate this or a larger zoom level.

# Testing Photographs and Procedure
1. Launch and start game
2. Teleport to some z-level you want to screenshot
3. Optionally change your zoom level and other ghost visibility settings
(self will automatically be altered) or medhud settings.
4. Use the debug tab verb Mass-Screenshot
5. Follow prompts and wait for process to complete
6. Install Pillow for python if you haven't already `pip install pillow`
7. Run `python tools\MapTileImageTool\MapTileImageTool.py`
8. Follow prompts (point to your BYOND/screenshots folder, pick a folder
for output, output file name, and 4 numbers given in game)


![image](https://github.com/cmss13-devs/cmss13/assets/76988376/05d89bc7-2a2d-421f-890d-a8e63523cc31)

# Changelog
:cl: Drathek
add: Added the debug verb Mass-Screenshot and a python script
MapTileImageTool to combine those images into a single full image map.
/:cl:
  • Loading branch information
Drulikar committed Jul 24, 2023
1 parent 4d91021 commit d018b21
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 5 deletions.
10 changes: 7 additions & 3 deletions code/controllers/subsystem/mapping.dm
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,14 @@ SUBSYSTEM_DEF(mapping)
++i

// load the maps
for (var/P in parsed_maps)
var/datum/parsed_map/pm = P
if (!pm.load(1, 1, start_z + parsed_maps[P], no_changeturf = TRUE))
for (var/datum/parsed_map/pm as anything in parsed_maps)
var/cur_z = start_z + parsed_maps[pm]
if (!pm.load(1, 1, cur_z, no_changeturf = TRUE))
errorList |= pm.original_path
if(istype(z_list[cur_z], /datum/space_level))
var/datum/space_level/cur_level = z_list[cur_z]
cur_level.x_bounds = pm.bounds[MAP_MAXX]
cur_level.y_bounds = pm.bounds[MAP_MAXY]
if(!silent)
INIT_ANNOUNCE("Loaded [name] in [(REALTIMEOFDAY - start_time)/10]s!")
return parsed_maps
Expand Down
1 change: 1 addition & 0 deletions code/modules/admin/admin_verbs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ var/list/admin_verbs_debug = list(
/client/proc/restart_controller,
/client/proc/debug_controller,
/client/proc/cmd_debug_toggle_should_check_for_win,
/client/proc/cmd_debug_mass_screenshot,
/client/proc/enable_debug_verbs,
/client/proc/toggledebuglogs,
/client/proc/togglenichelogs,
Expand Down
67 changes: 67 additions & 0 deletions code/modules/admin/verbs/debug.dm
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,74 @@
else
message_admins("[key_name(src)] disabled checking for round-end.")

/client/proc/cmd_debug_mass_screenshot()
set category = "Debug"
set name = "Mass Screenshot"
set background = TRUE
set waitfor = FALSE

if(!check_rights(R_MOD))
return

if(tgui_alert(usr, "Are you sure you want to mass screenshot this z-level? Ensure your visual settings are correct first (other ghost visibility, zoom level, etc.) and you have emptied your BYOND/screenshots folder.", "Mass Screenshot", list("Yes", "No")) != "Yes")
return

var/sleep_duration = tgui_input_number(usr, "Enter a delay in deciseconds between screenshots to allow the client to render changes.", "Screenshot delay", 2, 10, 1, 0, TRUE)
if(!sleep_duration)
return

if(!mob)
return

if(!isobserver(mob))
admin_ghost()

mob.alpha = 0
if(mob.hud_used)
mob.hud_used.show_hud(HUD_STYLE_NOHUD)
mob.animate_movement = NO_STEPS

message_admins(WRAP_STAFF_LOG(usr, "started a mass screenshot operation."))

var/half_chunk_size = view + 1
var/chunk_size = half_chunk_size * 2 - 1
var/cur_x = half_chunk_size
var/cur_y = half_chunk_size
var/cur_z = mob.z
var/width
var/height
if(istype(SSmapping.z_list[cur_z], /datum/space_level))
var/datum/space_level/cur_level = SSmapping.z_list[cur_z]
width = cur_level.x_bounds - half_chunk_size + 2
height = cur_level.y_bounds - half_chunk_size + 2
else
width = world.maxx - half_chunk_size + 2
height = world.maxy - half_chunk_size + 2
var/width_inside = width - 1
var/height_inside = height - 1

while(cur_y < height)
while(cur_x < width)
mob.on_mob_jump()
mob.forceMove(locate(cur_x, cur_y, cur_z))
sleep(sleep_duration)
winset(src, null, "command='.screenshot auto'")
if(cur_x == width_inside)
break
cur_x += chunk_size
cur_x = min(cur_x, width_inside)
if(cur_y == height_inside)
break
cur_x = half_chunk_size
cur_y += chunk_size
cur_y = min(cur_y, height_inside)

mob.alpha = initial(mob.alpha)
if(mob.hud_used)
mob.hud_used.show_hud(HUD_STYLE_STANDARD)
mob.animate_movement = SLIDE_STEPS // Initial is incorrect

to_chat(usr, "Provide these values when asked for the MapTileImageTool: [width] [height] [half_chunk_size] [world.icon_size]")

//TODO: merge the vievars version into this or something maybe mayhaps
/client/proc/cmd_debug_del_all()
Expand Down
6 changes: 4 additions & 2 deletions code/modules/mapping/space_management/space_level.dm
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
var/list/traits
var/z_value = 1 //actual z placement
var/linkage = SELFLOOPING
var/xi
var/yi //imaginary placements on the grid
var/x_bounds
var/y_bounds

/datum/space_level/New(new_z, new_name, list/new_traits = list())
z_value = new_z
name = new_name
traits = new_traits
//set_linkage(new_traits[ZTRAIT_LINKAGE])
x_bounds = world.maxx
y_bounds = world.maxy
85 changes: 85 additions & 0 deletions tools/MapTileImageTool/MapTileImageTool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#This script processes screenshots from the Mass-Screenshot Debug verb in SS13 into a full map image

# Loosely based on https://github.com/vortex1942/telescience/blob/master/src/tools/PhotoProcessor.py

# This script uses the Pillow library (PIL) install it with pip install pillow

#*****************************************************************
#******ALL .pngs in the rawimages folder will be processed********
#********Exported file may be overwritten in the output***********
#*****************************************************************

from PIL import Image
from os import listdir, path

# Selection of Input/Output directories
rawimgdir = str(input("Directory of RAW images: "))
if path.exists(rawimgdir) == False:
print("Directory could not be found!")
exit(1)

imgdir = str(input("Directory for output image (leave blank to use RAW image directory): "))
if imgdir == "":
imgdir = rawimgdir
elif path.exists(imgdir) == False:
print("Directory could not be found!")
exit(1)

exportfilename = str(input("Filename for Full Image (E.g LV624_Complete): "))
if exportfilename == "":
print("Filename is invalid!")
exit(1)

gamearguments = str(input("Values provided after using the Mass-Screenshot verb: "))
gamearguments = gamearguments.split()
if len(gamearguments) != 4:
print("Invalid arguments!")
exit(1)
pixelsize = int(gamearguments[3])
halfchunksize = int(gamearguments[2])
width = (int(gamearguments[0]) + halfchunksize - 2)
height = (int(gamearguments[1]) + halfchunksize - 2)
if width < 1 or height < 1 or pixelsize < 1 or halfchunksize < 1 or halfchunksize * 2 >= width or halfchunksize * 2 >= height:
print("Invalid arguments!")
exit(1)
width *= pixelsize
height *= pixelsize
halfchunksize *= pixelsize

# Function for saving the image (params: name=Filename, export=Image IMG variable)
def func_exportfullimage(name, export):
print("SAVING IMAGE")
file = (imgdir+"\\"+name+".png")
print(file)
file = open(file, "wb")
export.save(file)

# Where the magic happens, Creates a canvas and pastes RAWimages in a grid fashion
masterexport = Image.new("RGBA", (width,height), color=(0,0,0,255))
imagelist = [file for file in sorted(listdir(rawimgdir), key=lambda x: path.getmtime(rawimgdir+"\\"+x)) if file.endswith('.png')]
imagecount = len(imagelist)
chunksize = halfchunksize * 2 - pixelsize
x = fc = 0
y = height - chunksize

# For loop stitches RAw images together
for p in imagelist:
file = (rawimgdir+"\\"+p)
photo = Image.open(file).convert("RGBA")
#Verbose mode [Iteration] [Image Coords] [RAW Filename]
#print("iter: " f"{fc : >2}", "IMG XY: " f"{x : >4}", f"{y : >4}", "FILE: " f"{p : >13}")
masterexport.paste(photo, (x, y))
x += chunksize
fc += 1
if x >= width:
x = 0
y -= chunksize
y = max(y, 0)
progress = fc / imagecount * 100
print("%.1f" % progress, "%")
x = min(x, width - chunksize)

func_exportfullimage(exportfilename, masterexport)

# Hopefully you got this far
print("COMPLETED :)")

0 comments on commit d018b21

Please sign in to comment.