User:Tehtmi/Map Colorizer

From DoomRL Wiki

Jump to: navigation, search
# *********
# *WARNING* Spoilers contained in this file!
# *********

# This program colorizes doomrl maps for the doomrl wiki.
# Program written by tehtmi
# Contact: tehtmi gmail
#                @     .com
# You can also contact my on the DoomRL forums/wiki.
# I am tehtmi on both.

# Feel free to use/edit this script however you want for
# the purposes of contributing to the doomrl wiki.
# Please do not sell this program.
# This script is not intended to cause damage to your
# computer in any way, but understand that it is provided
# with no warranty or guarantee of safety.

# Usage Notes:
# Running this program requires your computer to have an
# implementation of python installed.  You can download
# python for free at:
# http://www.python.org/
# I believe this program should run on python 2.6 and 3.0.
# The program is designed to be run from the command line.
# In windows:
# 1) Install python (if necessary)
# 2) Put this script in a textfile with the .py extension.
# 2.5) It helps to put the script in the same directory as
#      the map you want to colorize
# 3) Open a command prompt (Start->Run...->"cmd")
# 4) Navigate to the location of the script ("cd")
# 5) Type "[filename].py map.txt colormap.txt"
#    Where [filename] is the name you gave to this
#    script, map.txt is the name of the map file
#    you want to colorize, and colormap.txt is the
#    name of file you want to output to.
# In *nix:
# 1-2.5) as above
# 3) Open a terminal (if necessary)
# 4) Navigate to the location of the script
# 5) Type "python [filename].py map.txt colormap.txt
# WARNING: the contents of the output file might be
#    overwritten if a file with that name already
#    exists!

# Map file format:
# Maps are plain text files
# Empty lines are ignored, as are lines that begin with //
# Use lines beginning with // to comment your map if desired.
# The file should contain a map grid enclosed between a line
# that contains only the word "map" and another line that
# contains only the words "end map".
# Each character in the map grid represents one DoomRL tile.
# By default, certain characters refer to certain tiles.  There
# is a list of default associations in the file below.
# Outside of the "map"/"end map" block, the map file can create
# and reassign associations between characters in the map grid
# and DoomRL tiles.  To do this, write a line of the form:
# [map grid character] : [DoomRL tile name]
# A list of recognized DoomRL tiles is found below.
# The program knows how to colorize and draw these tiles
# based on their names.
# NOTE: A map grid character DOES NOT have to be the same
# character used by DoomRL to draw the map-tile it represents.
# E.G. "P" could represent a pain elemental even though pain
# elementals look like "O" in the game.

import sys
import re

inputFileName = sys.argv[1]
outputFileName = sys.argv[2]

inputFile = open(inputFileName, 'r')

tileMapFlag = False

# Non ascii characters: generated using html escapes
middleDot = '·'  #Used for floor
brokenBar = '¦'  #Used for ammo
bullet = '•'     #Used for pool of blood
openBracket = '[' #Fixes wiki issue with [[ for two adjacent armors

# These are all the types of tile that are recognized by the program
# TODO: add different types of corpses
# TODO: do some enemies have green blood?
terms = {'stone wall'                 :('#', 'silver'),
         'blooded wall'               :('#', 'maroon'),
         'bloodstone'                 :('#', 'maroon'),
         'blooded wall (bloodstone)'  :('#', 'red'),
         'locked door'                :('+', 'olive'),
         'closed door'                :('+', 'olive'),
         'open door'                  :('/', 'olive'),
         'phobos rock'                :('.', 'maroon'),
         'floor'                      :(middleDot, 'silver'),
         'blood'                      :(middleDot, 'maroon'),
         'pool of blood'              :(bullet, 'maroon'),
         'corpse'                     :('%', 'maroon'),
         'stairs'                     :('>', 'silver'),
         'down stairs'                :('>', 'silver'),
         'special stairs'             :('>', 'red'),
         'water'                      :('=', 'navy'),
         'acid'                       :('=', 'green'),
         'lava'                       :('=', 'PLAID:yellow:maroon'),
         'lever'                      :('&', 'white'),
         'special lever'              :('&', 'fuchsia'),
         'teleporter'                 :('*', 'aqua'),
         'hellgate'                   :('0', 'fuchsia'),
         'barrel of fuel'             :('0', 'olive'),
         'barrel of acid'             :('0', 'green'),
         'barrel of napalm'           :('0', 'red'),
         'pistol'                     :('}', 'silver'),
         'shotgun'                    :('}', 'gray'),
         'combat shotgun'             :('}', 'blue'),
         'double shotgun'             :('}', 'white'),
         'chaingun'                   :('}', 'maroon'),
         'rocket launcher'            :('}', 'olive'),
         'plasma rifle'               :('}', 'teal'),
         'bfg 9000'                   :('}', 'fuchsia'),
         'exotic weapon'              :('}', 'fuchsia'),
         'unique weapon'              :('}', 'lime'),
         'combat knife'               :('\\', 'white'),
         'chainsaw'                   :('\\', 'fuchsia'),
         'exotic melee weapon'        :('\\', 'fuchsia'),
         'unique melee weapon'        :('\\', 'lime'),
         'artifact melee weapon'      :('\\', 'yellow'),
         '10mm ammo'                  :(brokenBar, 'silver'),
         'shotgun shell'              :(brokenBar, 'gray'),
         'rocket'                     :(brokenBar, 'olive'),
         'power cell'                 :(brokenBar, 'teal'),
         'green armor'                :(openBracket, 'green'),
         'blue armor'                 :(openBracket, 'navy'),
         'red armor'                  :(openBracket, 'maroon'),
         'exotic armor'               :(openBracket, 'fuchsia'),
         'unique armor'               :(openBracket, 'lime'),
         'artifact armor'             :(openBracket, 'yellow'),
         'steel boots'                :(';', 'white'),
         'protective boots'           :(';', 'green'),
         'plasteel boots'             :(';', 'navy'),
         'exotic boots'               :(';', 'fuchsia'),
         'unique boots'               :(';', 'lime'),
         'agility mod pack'           :('"', 'aqua'),
         'bulk mod pack'              :('"', 'blue'),
         'power mod pack'             :('"', 'fuchsia'),
         'technical mod pack'         :('"', 'yellow'),
         'exotic mod pack'            :('"', 'fuchsia'),
         'unique mod pack'            :('"', 'lime'),         
         'small med-pack'             :('+', 'red'),
         'large med-pack'             :('+', 'maroon'),
         'phase device'               :('+', 'navy'),
         'homing phase device'        :('+', 'blue'),
         'envirosuit pack'            :('+', 'green'),
         'exotic pack'                :('+', 'fuchsia'),
         'unique pack'                :('+', 'lime'),
         'thermonuclear bomb'         :('%', 'blue'),
         'thermonuclear bomb (active)':('0', 'maroon'),
         'unique staff'               :('?', 'lime'),
         'artifact staff'             :('?', 'yellow'),
         'small health globe'         :('^', 'red'),
         'large health globe'         :('^', 'maroon'),
         'berserk pack'               :('^', 'maroon'),
         'supercharge globe'          :('^', 'blue'),
         'invulnerabilty globe'       :('^', 'white'),
         'armour shard'               :('^', 'yellow'),
         'megasphere'                 :('^', 'fuchsia'),
         'computer map'               :('^', 'green'),
         'backpack'                   :('^', 'olive'),
         'player start'               :('@', 'silver'),
         'former human'               :('h', 'silver'),
         'former sergeant'            :('h', 'gray'),
         'former captain'             :('h', 'maroon'),
         'former commando'            :('h', 'blue'),
         'imp'                        :('i', 'olive'),
         'demon'                      :('c', 'red'),
         'lost soul'                  :('s', 'yellow'),
         'cacodemon'                  :('O', 'maroon'),
         'hell knight'                :('B', 'olive'),
         'pain elemental'             :('O', 'olive'),
         'mancubus'                   :('M', 'olive'),
         'arachnotron'                :('A', 'yellow'),
         'revenant'                   :('R', 'white'),
         'baron of hell'              :('B', 'maroon'),
         'arch-vile'                  :('V', 'yellow'),
         'nightmare imp'              :('i', 'blue'),
         'nightmare demon'            :('c', 'blue'),
         'nightmare cacodemon'        :('O', 'blue'),
         'arena master'               :('V', 'lime'),
         'shambler'                   :('B', 'white'),
         'bruiser'                    :('B', 'red'),
         'angel of death'             :('A', 'maroon'),
         'lava elemental'             :('E', 'yellow'),
         'cyberdemon'                 :('C', 'olive'),
         'john carmack'               :('@', 'blue'),
         'random armor or boots'      :('?', 'navy'),
         'random weapon'              :('?', 'olive'),
         '[space]'                    :(' ', None),
         '[newline]'                  :('\n', None),
         '[empty]'                    :('', None)}

# These are default associations between map characters and actual tiles
# Associations can be added or modified by the input file
assoc = {'@':'player start',
         '^':'supercharge globe',         
         '#':'stone wall',
         '+':'closed door',
         '.':'floor',
         '=':'lava',
         '&':'lever',
         '*':'teleporter',
         '0':'barrel',
         '>':'down stairs',
         'h':'former human',
         'i':'imp',
         's':'lost soul',
         'c':'demon',
         'B':'baron of hell',
         'O':'cacodemon',
         'R':'revenant',
         'A':'arachnotron',
         'M':'mancubus',
         'C':'cyberdemon',
         'V':'arch-vile',
         'E':'lava elemental'}

# Regular expressions used to parse the input file
mapProg = re.compile(r'[\s]*map[\s]*$')
endMapProg = re.compile(r'[\s]*end map[\s]*$')
prog = re.compile(r'[\s]*([\S])[\s]*:[\s]*(.*?)[\s]*$')

mapLines = []

autobindingPrefix = '_'
autobindingNumber = 0

# Read the input file.
for line in inputFile:
    if line == '\n':
        continue
    if len(line) >= 2 and line[0] == line[1] == '/':
        # comment
        continue
    elif not tileMapFlag:
        if mapProg.match(line) is not None:
            tileMapFlag = True
        else:
            result = prog.match(line)
            # print result.group(1), result.group(2)
            name = result.group(1)
            binding = result.group(2)
            if len(binding) >= 3 and binding[0] == '.':
                autobinding = autobindingPrefix + str(autobindingNumber)
                autobindingNumber += 1
                assoc[name] = autobinding
                terms[autobinding] = (binding[1], binding[3:])
            else:
                if not terms.has_key(binding):
                    print "Invalid tile name '" + binding + "'."
                    print "Check spelling and consult the list in the file."
                    sys.exit(1)
                assoc[name] = binding
    else:
        if endMapProg.match(line) is not None:
            tileMapFlag = False
        else:
            mapLines.append(line)
inputFile.close()

def handle(tiles, colorStack):
    if(len(tiles) == 0):
        return
    fColors = set([])
    bColors = set([])
    x = 0
    while fColors.isdisjoint(bColors):
        if x == len(tiles):
            # all colors are None
            for tile in tiles:
                outputFile.write(tile[0])
            return
        color1 = tiles[x][1]
        color2 = tiles[-(x + 1)][1]
        if color1 is not None:
            fColors.add(color1)
        if color2 is not None:
            bColors.add(color2)
        x += 1
    color = (fColors & bColors).pop()
    x = 0
    while not tiles[x][1] == color:
        x += 1  
    y = len(tiles) - 1
    while not tiles[y][1] == color:
        y -= 1
    handle(tiles[0:x], colorStack)
    if len(colorStack) >= 2 and colorStack[1] == color:
        inStack = colorStack[1:]
        outputFile.write('</span>')
        popMode = True
    else:
        inStack = [color] + colorStack
        outputFile.write('<span style="color:' + color + '">')
        popMode = False
    x1 = x
    while tiles[x1][1] in [color, None] and x1 < y:
        outputFile.write(tiles[x1][0])
        x1 += 1
    y1 = y
    while tiles[y1][1] in [color, None] and y1 >= x1:
        y1 -= 1
    y1 += 1
    handle(tiles[x1:y1], inStack)
    for y0 in range(y1, y + 1):
        outputFile.write(tiles[y0][0])
    if popMode:
        outputFile.write('<span style="color:' + colorStack[0] + '">')
    else:
        outputFile.write('</span>')
    handle(tiles[(y + 1):], colorStack)

outputFile = open(outputFileName, 'w')

tiles = []
for y in range(0, len(mapLines)):
    line = mapLines[y]
    for x in range(0, len(line) - 1): # -1 ignores ending '\n'
        c = line[x]
        tileChar, tileColor = terms[assoc[c].lower()]
        if tileColor.startswith('PLAID'):
            plaidColors = tileColor[6:].split(':')
            tileColor = plaidColors[(x + y) % len(plaidColors)]
        tiles.append((tileChar, tileColor))
    if y <= len(mapLines) - 2:
        tiles.append(terms['[newline]'])
        tiles.append(terms['[space]'])

outputFile.write(' <span style="font-size:larger;background-color:black">')
handle(tiles,[])
outputFile.write('</span>\n')
outputFile.close()
Personal tools