For some reason this keeps disappearing

This commit is contained in:
ZomgPonies
2014-06-21 05:17:42 -04:00
parent 768fb4a8bd
commit 6feabc0249
8 changed files with 1461 additions and 72 deletions
+3 -3
View File
@@ -5,8 +5,8 @@ Created on Sep 21, 2013
'''
import os
from .map import Map, Tile, MapRenderFlags
from .objtree import ObjectTree
from byond.map import Map, Tile, MapRenderFlags
from byond.objtree import ObjectTree
def GetFilesFromDME(dmefile='baystation12.dme', ext='.dm'):
filesInDME=[]
@@ -30,7 +30,7 @@ def GetFilesFromDME(dmefile='baystation12.dme', ext='.dm'):
if c == '"':
inString = not inString
if not inString:
filepath = os.path.join(rootdir, filename)
filepath = os.path.join(rootdir, filename.replace('\\',os.sep))
if filepath.endswith(ext):
filesInDME += [filepath]
filename = ''
+111 -61
View File
@@ -10,7 +10,48 @@ OBJ_LAYER = 3
MOB_LAYER = 4
FLY_LAYER = 5
import re
# @formatter:off
COLORS = {
'aqua': '#00FFFF',
'black': '#000000',
'blue': '#0000FF',
'brown': '#A52A2A',
'cyan': '#00FFFF',
'fuchsia': '#FF00FF',
'gold': '#FFD700',
'gray': '#808080',
'green': '#008000',
'grey': '#808080',
'lime': '#00FF00',
'magenta': '#FF00FF',
'maroon': '#800000',
'navy': '#000080',
'olive': '#808000',
'purple': '#800080',
'red': '#FF0000',
'silver': '#C0C0C0',
'teal': '#008080',
'white': '#FFFFFF',
'yellow': '#FFFF00'
}
# @formatter:on
_COLOR_LOOKUP = {}
def BYOND2RGBA(colorstring, alpha=255):
colorstring = colorstring.strip()
if colorstring[0] == '#':
colorstring = colorstring[1:]
r, g, b = colorstring[:2], colorstring[2:4], colorstring[4:]
r, g, b = [int(n, 16) for n in (r, g, b)]
return (r, g, b, alpha)
else:
return _COLOR_LOOKUP[colorstring]
for name, color in COLORS.items():
_COLOR_LOOKUP[name] = BYOND2RGBA(color)
import re, hashlib
from .utils import eval_expr
REGEX_TABS = re.compile('^(?P<tabs>\t*)')
class BYONDValue:
@@ -18,28 +59,28 @@ class BYONDValue:
Handles numbers and unhandled types like lists.
"""
def __init__(self, string, filename='', line=0, typepath='/', **kwargs):
#: The actual value.
# : The actual value.
self.value = string
#: Filename this was found in
# : Filename this was found in
self.filename = filename
#: Line of the originating file.
# : Line of the originating file.
self.line = line
#: Typepath of the value.
# : Typepath of the value.
self.type = typepath
#: Has this value been inherited?
# : Has this value been inherited?
self.inherited = kwargs.get('inherited', False)
#: Is this a declaration? (/var)
# : Is this a declaration? (/var)
self.declaration = kwargs.get('declaration', False)
#: Anything special? (global, const, etc.)
# : Anything special? (global, const, etc.)
self.special = kwargs.get('special', None)
#: If a list, what's the size?
# : If a list, what's the size?
self.size = kwargs.get('size', None)
def copy(self):
@@ -47,6 +88,8 @@ class BYONDValue:
return BYONDValue(self.value, self.filename, self.line, self.type, declaration=self.declaration, inherited=self.inherited, special=self.special)
def __str__(self):
if self.value is None:
return 'null'
return '{0}'.format(self.value)
def __repr__(self):
@@ -104,16 +147,16 @@ class BYONDString(BYONDValue):
class PropertyFlags:
'''Collection of flags that affect :func:`Atom.setProperty` behavior.'''
#: Property being set should be saved to the map
# : Property being set should be saved to the map
MAP_SPECIFIED = 1
#: Property being set should be handled as a string
# : Property being set should be handled as a string
STRING = 2
#: Property being set should be handled as a file reference
# : Property being set should be handled as a file reference
FILEREF = 4
#: Property being set should be handled as a value
# : Property being set should be handled as a value
VALUE = 8
class Atom:
@@ -128,61 +171,76 @@ class Atom:
The line in the aforementioned file.
'''
#: Prints all inherited properties, not just the ones that are mapSpecified.
# : Prints all inherited properties, not just the ones that are mapSpecified.
FLAG_INHERITED_PROPERTIES = 1
#: writeMap2 prints old_ids instead of the actual IID.
# : writeMap2 prints old_ids instead of the actual IID.
FLAG_USE_OLD_ID = 2
def __init__(self, path, filename='', line=0, **kwargs):
global TURF_LAYER, AREA_LAYER, OBJ_LAYER, MOB_LAYER
#: Absolute path of this atom
# : Absolute path of this atom
self.path = path
#: Vars of this atom, including inherited vars.
# : Vars of this atom, including inherited vars.
self.properties = {}
#: List of var names that were specified by the map, if atom was loaded from a :class:`byond.map.Map`.
# : List of var names that were specified by the map, if atom was loaded from a :class:`byond.map.Map`.
self.mapSpecified = []
#: Child atoms and procs.
# : Child atoms and procs.
self.children = {}
#: The parent of this atom.
# : The parent of this atom.
self.parent = None
#: The file this atom originated from.
# : The file this atom originated from.
self.filename = filename
#: Line from the originating file.
# : Line from the originating file.
self.line = line
#: Instance ID (maps only). Used internally, do NOT change.
# : Instance ID (maps only). Used internally, do NOT change.
self.id = None
#: Instance ID that was read from the map.
# : Instance ID that was read from the map.
self.old_id = None
#: Used internally.
self.ob_inherited=False
# : Used internally.
self.ob_inherited = False
#: Loaded from map, but missing in the code. (Maps only)
self.missing=kwargs.get('missing',False)
# : Loaded from map, but missing in the code. (Maps only)
self.missing = kwargs.get('missing', False)
#if not self.missing and path == '/area/engine/engineering':
# if not self.missing and path == '/area/engine/engineering':
# raise Exception('God damnit')
self._hash = None
def UpdateHash(self):
if self._hash is None:
self._hash = hashlib.md5(str(self)).hexdigest()
def InvalidateHash(self):
self._hash = None
def GetHash(self):
self.UpdateHash()
return self._hash
def copy(self):
'''
Make a copy of this atom, without dangling references.
:returns byond.basetypes.Atom
'''
new_node = Atom(self.path,self.filename,self.line,missing=self.missing)
new_node = Atom(self.path, self.filename, self.line, missing=self.missing)
new_node.properties = self.properties.copy()
new_node.mapSpecified = self.mapSpecified
new_node.id = self.id
new_node.old_id = self.old_id
new_node.UpdateHash()
# new_node.parent = self.parent
return new_node
@@ -250,10 +308,12 @@ class Atom:
self.properties[index] = BYONDFileRef(value)
else:
self.properties[index] = BYONDValue(value)
self.UpdateHash()
def InheritProperties(self):
if self.ob_inherited: return
#debugInheritance=self.path in ('/area','/obj','/mob','/atom/movable','/atom')
# debugInheritance=self.path in ('/area','/obj','/mob','/atom/movable','/atom')
if self.parent:
if not self.parent.ob_inherited:
self.parent.InheritProperties()
@@ -262,26 +322,26 @@ class Atom:
if key not in self.properties:
self.properties[key] = value
self.properties[key].inherited = True
#if debugInheritance:print(' {0}[{2}] -> {1}'.format(self.parent.path,self.path,key))
#assert 'name' in self.properties
self.ob_inherited=True
# if debugInheritance:print(' {0}[{2}] -> {1}'.format(self.parent.path,self.path,key))
# assert 'name' in self.properties
self.ob_inherited = True
for k in self.children.iterkeys():
self.children[k].InheritProperties()
def __ne__(self, atom):
return not self.__eq__(atom)
def __eq__(self, atom):
if atom == None:
return False
if self.mapSpecified != atom.mapSpecified:
return False
# if self.mapSpecified != atom.mapSpecified:
# return False
if self.path != atom.path:
return False
return self.properties == atom.properties
def handle_math(self,expr):
if isinstance(expr,str):
def handle_math(self, expr):
if isinstance(expr, str):
return eval_expr(expr)
return expr
@@ -291,12 +351,12 @@ class Atom:
myLayer = 0
otherLayer = 0
try:
myLayer = self.handle_math(self.getProperty('layer',myLayer))
myLayer = self.handle_math(self.getProperty('layer', myLayer))
except ValueError:
print('Failed to parse {0} as float.'.format(self.properties['layer'].value))
pass
try:
otherLayer = self.handle_math(other.getProperty('layer',otherLayer))
otherLayer = self.handle_math(other.getProperty('layer', otherLayer))
except ValueError:
print('Failed to parse {0} as float.'.format(other.properties['layer'].value))
pass
@@ -308,34 +368,24 @@ class Atom:
myLayer = 0
otherLayer = 0
try:
myLayer = self.handle_math(self.getProperty('layer',myLayer))
myLayer = self.handle_math(self.getProperty('layer', myLayer))
except ValueError:
print('Failed to parse {0} as float.'.format(self.properties['layer'].value))
pass
try:
otherLayer = self.handle_math(other.getProperty('layer',otherLayer))
otherLayer = self.handle_math(other.getProperty('layer', otherLayer))
except ValueError:
print('Failed to parse {0} as float.'.format(other.properties['layer'].value))
pass
return myLayer < otherLayer
def MapSerialize(self, flags=0):
atomContents = []
# print(repr(self.mapSpecified))
if (flags & Atom.FLAG_INHERITED_PROPERTIES):
for key, val in self.properties.items():
atomContents += ['{0} = {1}'.format(key, val)]
else:
for i in range(len(self.mapSpecified)):
key = self.mapSpecified[i]
if key in self.properties:
val = self.properties[key]
atomContents += ['{0} = {1}'.format(key, val)]
if len(atomContents) > 0:
return self.path + '{' + '; '.join(atomContents) + '}'
else:
return self.path
def __str__(self):
atomContents = []
for key, val in self.properties.items():
atomContents += ['{0}={1}'.format(key, val)]
return '{}{{{}}}'.format(self.path, ';'.join(atomContents))
def dumpPropInfo(self, name):
o = '{0}: '.format(name)
if name not in self.properties:
@@ -391,7 +441,7 @@ class Proc(Atom):
self.definition = False
self.origpath = ''
def figureOutName(self,path):
def figureOutName(self, path):
name = path.split('(')[0]
return name.split('/')[-1]
+791
View File
@@ -0,0 +1,791 @@
"""
Map Interface Module
Copyright 2013 Rob "N3X15" Nelson <nexis@7chan.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
import os, itertools, sys, numpy, logging, hashlib
from byond.map.format import GetMapFormat, Load as LoadMapFormats
from byond.DMI import DMI
from byond.directions import SOUTH, IMAGE_INDICES
from byond.basetypes import Atom, BYONDString, BYONDValue, BYONDFileRef, BYOND2RGBA
# from byond.objtree import ObjectTree
from PIL import Image, ImageChops
# Cache
_icons = {}
_dmis = {}
LoadMapFormats()
# From StackOverflow
def trim(im):
bg = Image.new(im.mode, im.size, im.getpixel((0, 0)))
diff = ImageChops.difference(im, bg)
diff = ImageChops.add(diff, diff, 2.0, -100)
bbox = diff.getbbox()
if bbox:
return im.crop(bbox)
# Bytes
def tint_image(image, tint_color):
return ImageChops.multiply(image, Image.new('RGBA', image.size, tint_color))
class TileIterator:
def __init__(self, _map):
self.map = _map
self.x = -1
self.y = 0
self.z = 0
self.max_z = len(self.map.zLevels)
def __iter__(self):
return self
def __next__(self):
return self.next()
def next(self):
self.x += 1
zLev = self.map.zLevels[self.z]
if self.x >= zLev.width:
self.y += 1
self.x = 0
if self.y >= zLev.height:
self.z += 1
self.y = 0
if self.z >= self.max_z:
raise StopIteration
t = self.map.GetTileAt(self.x, self.y, self.z)
#print('{} = {}'.format((self.x,self.y,self.z),str(t)))
return t
class Tile(object):
def __init__(self, _map):
self.origID = ''
self.ID = 0
self.instances = []
self.frame = None
self.unselected_frame = None
self.areaSelected = True
self.log = logging.getLogger(__name__ + '.Tile')
self.map = _map
self._hash = None
def UpdateHash(self):
if self._hash is None:
self._hash = hashlib.md5(str(self)).hexdigest()
def InvalidateHash(self):
self._hash = None
def GetHash(self):
self.UpdateHash()
return self._hash
def RemoveAtom(self, atom, hash=True):
'''
:param Atom atom:
Atom to remove. Raises ValueError if not found.
'''
self.instances.remove(atom)
self.InvalidateHash()
if hash: self.UpdateHash()
def AppendAtom(self, atom, hash=True):
'''
:param Atom atom:
Atom to add.
'''
self.instances.append(atom)
self.InvalidateHash()
if hash: self.UpdateHash()
def CountAtom(self, atom):
'''
:param Atom atom:
Atom to count.
:return int: Count of atoms
'''
return self.instances.count(atom)
def copy(self, origID=False):
tile = self.map.CreateTile()
tile.instances = self.instances
if origID:
tile.origID = self.origID
tile.UpdateHash()
return tile
def GetAtoms(self):
atoms = []
for atom in self.instances:
if atom is None: continue
atoms += [atom]
return atoms
def SortAtoms(self):
return sorted(self.GetAtoms(), reverse=True)
def GetAtom(self, idx):
return self.map.getInstance(self.instances[idx])
def GetInstances(self):
return self.instances
def __str__(self):
return self._serialize()
def __ne__(self, tile):
return not self.__eq__(tile)
def __eq__(self, other):
return other and ((other._hash and self._hash and self._hash == other._hash) or (len(self.instances) == len(other.instances) and self.instances == other.instances))
# else:
# return all(self.instances[i] == other.instances[i] for i in xrange(len(self.instances)))
def _serialize(self):
return ','.join([str(i) for i in self.GetInstances()])
def RenderToMapTile(self, passnum, basedir, renderflags, **kwargs):
img = Image.new('RGBA', (96, 96))
self.offset = (32, 32)
foundAPixelOffset = False
render_types = kwargs.get('render_types', ())
skip_alpha = kwargs.get('skip_alpha', False)
# for atom in sorted(self.GetAtoms(), reverse=True):
for atom in self.SortAtoms():
if len(render_types) > 0:
found = False
for path in render_types:
if atom.path.startswith(path):
found = True
if not found:
continue
aid = atom.id
# Ignore /areas. They look like ass.
if atom.path.startswith('/area'):
if not (renderflags & MapRenderFlags.RENDER_AREAS):
continue
# We're going to turn space black for smaller images.
if atom.path == '/turf/space':
if not (renderflags & MapRenderFlags.RENDER_STARS):
continue
if 'icon' not in atom.properties:
logging.critical('UNKNOWN ICON IN {0} (atom #{1})'.format(self.origID, aid))
logging.info(atom.MapSerialize())
logging.info(atom.MapSerialize(Atom.FLAG_INHERITED_PROPERTIES))
continue
dmi_file = atom.properties['icon'].value
if 'icon_state' not in atom.properties:
# Grab default icon_state ('') if we can't find the one defined.
atom.properties['icon_state'] = BYONDString("")
state = atom.properties['icon_state'].value
direction = SOUTH
if 'dir' in atom.properties:
try:
direction = int(atom.properties['dir'].value)
except ValueError:
logging.critical('FAILED TO READ dir = ' + repr(atom.properties['dir'].value))
continue
icon_key = '{0}|{1}|{2}'.format(dmi_file, state, direction)
frame = None
pixel_x = 0
pixel_y = 0
if icon_key in _icons:
frame, pixel_x, pixel_y = _icons[icon_key]
else:
dmi_path = os.path.join(basedir, dmi_file)
dmi = None
if dmi_path in _dmis:
dmi = _dmis[dmi_path]
else:
try:
dmi = DMI(dmi_path)
dmi.loadAll()
_dmis[dmi_path] = dmi
except Exception as e:
print(str(e))
for prop in ['icon', 'icon_state', 'dir']:
print('\t{0}'.format(atom.dumpPropInfo(prop)))
pass
if dmi.img is None:
logging.warning('Unable to open {0}!'.format(dmi_path))
continue
if dmi.img.mode not in ('RGBA', 'P'):
logging.warn('{} is mode {}!'.format(dmi_file, dmi.img.mode))
if direction not in IMAGE_INDICES:
logging.warn('Unrecognized direction {} on atom {} in tile {}!'.format(direction, atom.MapSerialize(), self.origID))
direction = SOUTH # DreamMaker property editor shows dir = 2. WTF?
frame = dmi.getFrame(state, direction, 0)
if frame == None:
# Get the error/default state.
frame = dmi.getFrame("", direction, 0)
if frame == None:
continue
if frame.mode != 'RGBA':
frame = frame.convert("RGBA")
pixel_x = 0
if 'pixel_x' in atom.properties:
pixel_x = int(atom.properties['pixel_x'].value)
pixel_y = 0
if 'pixel_y' in atom.properties:
pixel_y = int(atom.properties['pixel_y'].value)
_icons[icon_key] = (frame, pixel_x, pixel_y)
# Handle BYOND alpha and coloring
c_frame = frame
alpha = int(atom.getProperty('alpha', 255))
if skip_alpha:
alpha = 255
color = atom.getProperty('color', '#FFFFFF')
if alpha != 255 or color != '#FFFFFF':
c_frame = tint_image(frame, BYOND2RGBA(color, alpha))
img.paste(c_frame, (32 + pixel_x, 32 - pixel_y), c_frame) # Add to the top of the stack.
if pixel_x != 0 or pixel_y != 0:
if passnum == 0: return # Wait for next pass
foundAPixelOffset = True
if passnum == 1 and not foundAPixelOffset:
return None
if not self.areaSelected:
# Fade out unselected tiles.
bands = list(img.split())
# Excluding alpha band
for i in range(3):
bands[i] = bands[i].point(lambda x: x * 0.4)
img = Image.merge(img.mode, bands)
return img
vTile = numpy.vectorize(Tile)
class MapLayer:
def __init__(self, _map, height=255, width=255):
self.map = _map
self.min = (1, 1)
self.max = (height, width)
self.height = height
self.width = width
self.tiles = numpy.empty((height, width), object)
self.tiles[:, :] = vTile(self)
def Resize(self,height,width):
self.height=height
self.width=width
self.tiles.resize(height,width)
class MapRenderFlags:
RENDER_STARS = 1
RENDER_AREAS = 2
class Map:
def __init__(self, tree=None, **kwargs):
self.zLevels = []
self.DMIs = {}
self.tree = tree
self.generatedTexAtlas = False
self.selectedAreas = ()
self.whitelistTypes = None
self.forgiving_atom_lookups = kwargs.get('forgiving_atom_lookups', False)
self.log = logging.getLogger(__name__ + '.Map')
def CreateZLevel(self, height, width, z= -1):
zLevel = MapLayer(self, height, width)
if z >= 0:
self.zLevels[z] = zLevel
else:
self.zLevels.append(zLevel)
return zLevel
def Tiles(self):
return TileIterator(self)
def Load(self, filename, **kwargs):
_, ext = os.path.splitext(filename)
fmt=kwargs.get('format','dmm2' if ext=='dmm2' else 'dmm')
reader = GetMapFormat(self, fmt)
reader.Load(filename, **kwargs)
def Save(self, filename, **kwargs):
_, ext = os.path.splitext(filename)
fmt=kwargs.get('format','dmm2' if ext=='dmm2' else 'dmm')
reader = GetMapFormat(self, kwargs.get('format',fmt))
reader.Save(filename, **kwargs)
def writeMap2(self, filename, flags=0):
self.filename = filename
tileFlags = 0
atomFlags = 0
if flags & Map.WRITE_OLD_IDS:
tileFlags |= Tile.FLAG_USE_OLD_ID
atomFlags |= Atom.FLAG_USE_OLD_ID
padding = len(self.tileTypes[-1].ID2String())
with open(filename, 'w') as f:
f.write('// Atom Instances\n')
for atom in self.instances:
f.write('{0} = {1}\n'.format(atom.id, atom.MapSerialize(atomFlags)))
f.write('// Tiles\n')
for tile in self.tileTypes:
f.write('{0}\n'.format(tile.MapSerialize2(tileFlags, padding)))
f.write('// Layout\n')
for z in self.zLevels.keys():
f.write('\n(1,1,{0}) = {{"\n'.format(z))
zlevel = self.zLevels[z]
for y in xrange(zlevel.height):
for x in xrange(zlevel.width):
tile = zlevel.GetTileAt(x, y)
if flags & Map.WRITE_OLD_IDS:
f.write(tile.origID)
else:
f.write(tile.ID2String(padding))
f.write("\n")
f.write('"}\n')
def GetTileAt(self, x, y, z):
'''
:param int x:
:param int y:
:param int z:
:rtype Tile:
'''
if z < len(self.zLevels):
return self.zLevels[z].tiles[x, y]
def CopyTileAt(self, x, y, z):
'''
:param int x:
:param int y:
:param int z:
:rtype Tile:
'''
if z < len(self.zLevels):
return self.zLevels[z].tiles[x, y].copy()
def SetTileAt(self, x, y, z, tile):
'''
:param int x:
:param int y:
:param int z:
'''
if z < len(self.zLevels):
self.zLevels[z].tiles[x, y] = tile
def CreateTile(self):
'''
:rtype Tile:
'''
return Tile(self)
def generateTexAtlas(self, basedir, renderflags=0):
if self.generatedTexAtlas:
return
print('--- Generating texture atlas...')
self._icons = {}
self._dmis = {}
self.generatedTexAtlas = True
for tid in xrange(len(self.tileTypes)):
tile = self.tileTypes[tid]
img = Image.new('RGBA', (96, 96))
tile.offset = (32, 32)
tile.areaSelected = True
tile.render_deferred = False
for atom in sorted(tile.GetAtoms(), reverse=True):
aid = atom.id
# Ignore /areas. They look like ass.
if atom.path.startswith('/area'):
if not (renderflags & MapRenderFlags.RENDER_AREAS):
continue
# We're going to turn space black for smaller images.
if atom.path == '/turf/space':
if not (renderflags & MapRenderFlags.RENDER_STARS):
continue
if 'icon' not in atom.properties:
print('CRITICAL: UNKNOWN ICON IN {0} (atom #{1})'.format(tile.origID, aid))
print(atom.MapSerialize())
print(atom.MapSerialize(Atom.FLAG_INHERITED_PROPERTIES))
continue
dmi_file = atom.properties['icon'].value
if 'icon_state' not in atom.properties:
# Grab default icon_state ('') if we can't find the one defined.
atom.properties['icon_state'] = BYONDString("")
state = atom.properties['icon_state'].value
direction = SOUTH
if 'dir' in atom.properties:
try:
direction = int(atom.properties['dir'].value)
except ValueError:
print('FAILED TO READ dir = ' + repr(atom.properties['dir'].value))
continue
icon_key = '{0}:{1}[{2}]'.format(dmi_file, state, direction)
frame = None
pixel_x = 0
pixel_y = 0
if icon_key in self._icons:
frame, pixel_x, pixel_y = self._icons[icon_key]
else:
dmi_path = os.path.join(basedir, dmi_file)
dmi = None
if dmi_path in self._dmis:
dmi = self._dmis[dmi_path]
else:
try:
dmi = self.loadDMI(dmi_path)
self._dmis[dmi_path] = dmi
except Exception as e:
print(str(e))
for prop in ['icon', 'icon_state', 'dir']:
print('\t{0}'.format(atom.dumpPropInfo(prop)))
pass
if dmi.img is None:
self.log.warn('Unable to open {0}!'.format(dmi_path))
continue
if dmi.img.mode not in ('RGBA', 'P'):
self.log.warn('{} is mode {}!'.format(dmi_file, dmi.img.mode))
if direction not in IMAGE_INDICES:
self.log.warn('Unrecognized direction {} on atom {} in tile {}!'.format(direction, atom.MapSerialize(), tile.origID))
direction = SOUTH # DreamMaker property editor shows dir = 2. WTF?
frame = dmi.getFrame(state, direction, 0)
if frame == None:
# Get the error/default state.
frame = dmi.getFrame("", direction, 0)
if frame == None:
continue
if frame.mode != 'RGBA':
frame = frame.convert("RGBA")
pixel_x = 0
if 'pixel_x' in atom.properties:
pixel_x = int(atom.properties['pixel_x'].value)
pixel_y = 0
if 'pixel_y' in atom.properties:
pixel_y = int(atom.properties['pixel_y'].value)
self._icons[icon_key] = (frame, pixel_x, pixel_y)
img.paste(frame, (32 + pixel_x, 32 - pixel_y), frame) # Add to the top of the stack.
if pixel_x != 0 or pixel_y != 0:
tile.render_deferred = True
tile.frame = img
# Fade out unselected tiles.
bands = list(img.split())
# Excluding alpha band
for i in range(3):
bands[i] = bands[i].point(lambda x: x * 0.4)
tile.unselected_frame = Image.merge(img.mode, bands)
self.tileTypes[tid] = tile
def renderAtom(self, atom, basedir, skip_alpha=False):
if 'icon' not in atom.properties:
logging.critical('UNKNOWN ICON IN ATOM #{0} ({1})'.format(atom.id, atom.path))
logging.info(atom.MapSerialize())
logging.info(atom.MapSerialize(Atom.FLAG_INHERITED_PROPERTIES))
return None
# else:
# logging.info('Icon found for #{}.'.format(atom.id))
dmi_file = atom.properties['icon'].value
if dmi_file is None:
return None
# Grab default icon_state ('') if we can't find the one defined.
state = atom.getProperty('icon_state', '')
direction = SOUTH
if 'dir' in atom.properties:
try:
direction = int(atom.properties['dir'].value)
except ValueError:
logging.critical('FAILED TO READ dir = ' + repr(atom.properties['dir'].value))
return None
icon_key = '{0}|{1}|{2}'.format(dmi_file, state, direction)
frame = None
pixel_x = 0
pixel_y = 0
if icon_key in _icons:
frame, pixel_x, pixel_y = _icons[icon_key]
else:
dmi_path = os.path.join(basedir, dmi_file)
dmi = None
if dmi_path in _dmis:
dmi = _dmis[dmi_path]
else:
try:
dmi = DMI(dmi_path)
dmi.loadAll()
_dmis[dmi_path] = dmi
except Exception as e:
print(str(e))
for prop in ['icon', 'icon_state', 'dir']:
print('\t{0}'.format(atom.dumpPropInfo(prop)))
pass
if dmi.img is None:
logging.warning('Unable to open {0}!'.format(dmi_path))
return None
if dmi.img.mode not in ('RGBA', 'P'):
logging.warn('{} is mode {}!'.format(dmi_file, dmi.img.mode))
if direction not in IMAGE_INDICES:
logging.warn('Unrecognized direction {} on atom {}!'.format(direction, atom.MapSerialize()))
direction = SOUTH # DreamMaker property editor shows dir = 2. WTF?
frame = dmi.getFrame(state, direction, 0)
if frame == None:
# Get the error/default state.
frame = dmi.getFrame("", direction, 0)
if frame == None:
return None
if frame.mode != 'RGBA':
frame = frame.convert("RGBA")
pixel_x = 0
if 'pixel_x' in atom.properties:
pixel_x = int(atom.properties['pixel_x'].value)
pixel_y = 0
if 'pixel_y' in atom.properties:
pixel_y = int(atom.properties['pixel_y'].value)
_icons[icon_key] = (frame, pixel_x, pixel_y)
# Handle BYOND alpha and coloring
c_frame = frame
alpha = int(atom.getProperty('alpha', 255))
if skip_alpha:
alpha = 255
color = atom.getProperty('color', '#FFFFFF')
if alpha != 255 or color != '#FFFFFF':
c_frame = tint_image(frame, BYOND2RGBA(color, alpha))
return c_frame
def generateImage(self, filename_tpl, basedir='.', renderflags=0, z=None, **kwargs):
'''
Instead of generating on a tile-by-tile basis, this creates a large canvas and places
each atom on it after sorting layers. This resolves the pixel_(x,y) problem.
'''
if z is None:
for z in self.zLevels.keys():
self.generateImage(filename_tpl, basedir, renderflags, z, **kwargs)
return
self.selectedAreas = ()
skip_alpha = False
render_types = ()
if 'area' in kwargs:
self.selectedAreas = kwargs['area']
if 'render_types' in kwargs:
render_types = kwargs['render_types']
if 'skip_alpha' in kwargs:
skip_alpha = kwargs['skip_alpha']
print('Checking z-level {0}...'.format(z))
instancePositions = {}
for y in range(self.zLevels[z].height):
for x in range(self.zLevels[z].width):
t = self.zLevels[z].GetTileAt(x, y)
# print('*** {},{}'.format(x,y))
if t is None:
continue
if len(self.selectedAreas) > 0:
renderThis = True
for atom in t.GetAtoms():
if atom.path.startswith('/area'):
if atom.path not in self.selectedAreas:
renderThis = False
if not renderThis: continue
for atom in t.GetAtoms():
if atom is None: continue
iid = atom.id
if atom.path.startswith('/area'):
if atom.path not in self.selectedAreas:
continue
# Check for render restrictions
if len(render_types) > 0:
found = False
for path in render_types:
if atom.path.startswith(path):
found = True
if not found:
continue
# Ignore /areas. They look like ass.
if atom.path.startswith('/area'):
if not (renderflags & MapRenderFlags.RENDER_AREAS):
continue
# We're going to turn space black for smaller images.
if atom.path == '/turf/space':
if not (renderflags & MapRenderFlags.RENDER_STARS):
continue
if iid not in instancePositions:
instancePositions[iid] = []
# pixel offsets
'''
pixel_x = int(atom.getProperty('pixel_x', 0))
pixel_y = int(atom.getProperty('pixel_y', 0))
t_o_x = int(round(pixel_x / 32))
t_o_y = int(round(pixel_y / 32))
pos = (x + t_o_x, y + t_o_y)
'''
pos = (x, y)
instancePositions[iid].append(pos)
if len(instancePositions) == 0:
return
levelAtoms = []
for iid in instancePositions:
levelAtoms += [self.getInstance(iid)]
pic = Image.new('RGBA', ((self.zLevels[z].width + 2) * 32, (self.zLevels[z].height + 2) * 32), "black")
# Bounding box, used for cropping.
bbox = [99999, 99999, 0, 0]
# Replace {z} with current z-level.
filename = filename_tpl.replace('{z}', str(z))
pastes = 0
for atom in sorted(levelAtoms, reverse=True):
if atom.id not in instancePositions:
continue
icon = self.renderAtom(atom, basedir, skip_alpha)
if icon is None:
continue
for x, y in instancePositions[atom.id]:
new_bb = self.getBBoxForAtom(x, y, atom, icon)
# print('{0},{1} = {2}'.format(x, y, new_bb))
# Adjust cropping bounds
if new_bb[0] < bbox[0]:
bbox[0] = new_bb[0]
if new_bb[1] < bbox[1]:
bbox[1] = new_bb[1]
if new_bb[2] > bbox[2]:
bbox[2] = new_bb[2]
if new_bb[3] > bbox[3]:
bbox[3] = new_bb[3]
pic.paste(icon, new_bb, icon)
pastes += 1
if len(self.selectedAreas) == 0:
# Autocrop (only works if NOT rendering stars or areas)
pic = trim(pic)
else:
# if nSelAreas == 0:
# continue
pic = pic.crop(bbox)
if pic is not None:
# Saev
filedir = os.path.dirname(filename)
if not os.path.isdir(filedir):
os.makedirs(filedir)
print(' -> {} ({}x{}) - {} objects'.format(filename, pic.size[0], pic.size[1], pastes))
pic.save(filename, 'PNG')
def getBBoxForAtom(self, x, y, atom, icon):
icon_width, icon_height = icon.size
pixel_x = int(atom.getProperty('pixel_x', 0))
pixel_y = int(atom.getProperty('pixel_y', 0))
return self.tilePosToBBox(x, y, pixel_x, pixel_y, icon_height, icon_width)
def tilePosToBBox(self, tile_x, tile_y, pixel_x, pixel_y, icon_height, icon_width):
# Tile Pos
X = tile_x * 32
Y = tile_y * 32
# pixel offsets
X += pixel_x
Y -= pixel_y
# BYOND coordinates -> PIL coords.
# BYOND uses LOWER left.
# PIL uses UPPER left
X += 0
Y += 32 - icon_height
return (
X,
Y,
X + icon_width,
Y + icon_height
)
# So we can read a map without parsing the tree.
def GetAtom(self, path):
if self.tree is not None:
atom = self.tree.GetAtom(path)
if atom is None and self.forgiving_atom_lookups:
self.missing_atoms.add(path)
return Atom(path, self.filename, missing=True)
return atom
return Atom(path)
@@ -0,0 +1,13 @@
import glob, os
from .base import *
def Load():
print('Loading Map Format Modules...')
for f in glob.glob(os.path.dirname(__file__) + "/*.py"):
modName = 'byond.map.format.' + os.path.basename(f)[:-3]
print(' Loading module ' + modName)
mod = __import__(modName)
for attr in dir(mod):
if not attr.startswith('_'):
#print(' {} = {}'.format(attr,getattr(mod, attr)))
globals()[attr] = getattr(mod, attr)
+33
View File
@@ -0,0 +1,33 @@
# Decorator
class MapFormat(object):
all = {}
def __init__(self, ext, _id=None):
self.id = _id
self.extension = ext
def __call__(self, c):
if self.id is None:
fname_p = c.__name__
self.id = fname_p
print('Adding Map Format "{}" (.{})...'.format(self.id,self.extension))
if self.extension in MapFormat.all:
print('!!! *.{} FILES ARE ALREADY HANDLED BY {}!'.format(self.extension,self.id))
MapFormat.all[self.extension] = c
return c
def GetMapFormat(_map,ext):
f = MapFormat.all.get(ext.lstrip('.'),None)
if f is None:
print('Unable to find MapFormat for {}.'.format(ext))
return f(_map)
class BaseMapFormat:
def __init__(self, _map):
self.map = _map
self.missing_atoms = set()
def Load(self, filename, **kwargs):
return
def Save(self, filename, **kwargs):
return
+490
View File
@@ -0,0 +1,490 @@
from byond.basetypes import *
from byond.utils import getElapsed
# from byond.map import Tile, MapLayer
from byond.map.format.base import BaseMapFormat, MapFormat
import os, sys, logging, itertools, shutil, collections, math
from time import clock
ID_ENCODING_TABLE = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
IET_SIZE = len(ID_ENCODING_TABLE)
def chunker(iterable, chunksize):
"""
Return elements from the iterable in `chunksize`-ed lists. The last returned
chunk may be smaller (if length of collection is not divisible by `chunksize`).
>>> print list(chunker(xrange(10), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
"""
i = iter(iterable)
while True:
wrapped_chunk = [list(itertools.islice(i, int(chunksize)))]
if not wrapped_chunk[0]:
break
yield wrapped_chunk.pop()
def DMMSortAlg(v):
t = (v.upper(),)
l = [c.isupper() for c in v]
l.reverse()
return t + tuple(l)
@MapFormat('dmm')
class DMMFormat(BaseMapFormat):
def __init__(self, map):
BaseMapFormat.__init__(self, map)
self.tileTypes = []
self.instances = []
self.oldID2NewID = {}
self.log = logging.getLogger('DMMFormat')
self.filename = 'BUILT-IN?'
# Number of duplicates found when loading.
self.duplicates = 0
# Caches
self.tileChunk2ID = {}
self.atomCache = {}
self.atomBorders = {
'{':'}',
'"':'"',
'(':')'
}
nit = self.atomBorders.copy()
for start, stop in self.atomBorders.items():
if start != stop:
nit[stop] = None
self.atomBorders = nit
self.idlen = 0
self.dump_inherited = False
def Load(self, filename, **kwargs):
if not os.path.isfile(filename):
self.log.warn('File ' + filename + " does not exist.")
self.filename = filename
with open(filename, 'r') as f:
print('Reading tile types from {0}...'.format(self.filename))
self.consumeTiles(f)
print('Reading tile positions...')
self.consumeTileMap(f)
def consumeDataValue(self, value, lineNumber):
data = None
if value[0] in ('"', "'"):
quote = value[0]
if quote == '"':
data = BYONDString(value[1:-1], self.filename, lineNumber)
elif quote == "'":
data = BYONDFileRef(value[1:-1], self.filename, lineNumber)
elif value == 'null':
data = BYONDValue(None, self.filename, lineNumber)
else:
data = BYONDValue(value, self.filename, lineNumber)
return data
def consumeTileMap(self, f):
zLevel = None
y = 0
z = 0
inZLevel = False
width = 0
height = 0
while True:
line = f.readline()
if line == '':
return
# (1,1,1) = {"
if line.startswith('('):
coordChunk = line[1:line.index(')')].split(',')
# print(repr(coordChunk))
z = int(coordChunk[2])
inZLevel = True
y = 0
width = 0
height = 0
continue
if line.strip() == '"}':
inZLevel = False
if height == 0:
height = y
# self.map.zLevels[z] = zLevel
zLevel = None
# self.log.info('Added map layer {0} ({1}x{2})'.format(z, height, width))
print(' * Added map layer {0} ({1}x{2})'.format(z, height, width))
continue
if inZLevel:
if zLevel is None:
zLevel = self.map.CreateZLevel(height, width) # , z-1)
if width == 0:
width = len(line) / self.idlen
# print('Width detected as {}.'.format(width))
zLevel.Resize(width, width)
if width > 255:
logging.warn("Line is {} blocks long!".format(width))
x = 0
for chunk in chunker(line.strip(), self.idlen):
chunk = self.String2ID(''.join(chunk))
tid = self.oldID2NewID[chunk]
zLevel.tiles[x, y] = self.tileTypes[tid].copy(origID=True)
x += 1
y += 1
def consumeTiles(self, f):
index = 0
self.duplicates = 0
lineNumber = 0
self.tileChunk2ID = {}
while True:
line = f.readline()
lineNumber += 1
if line.startswith('"'):
t = self.consumeTile(line, lineNumber)
t.ID = index
t.UpdateHash()
self.tileTypes += [t]
self.idlen = max(self.idlen, len(self.ID2String(t.ID)))
self.oldID2NewID[t.origID] = t.ID
index += 1
# No longer needed, 2fast.
# if((index % 100) == 0):
# print(index)
else:
self.log.info('-- {} tiles loaded, {} duplicates discarded'.format(index, self.duplicates))
return
def consumeTileAtoms(self, line, lineNumber):
instances = []
atom_chunks = self.SplitAtoms(line)
for atom_chunk in atom_chunks:
if atom_chunk in self.atomCache:
instances += [self.atomCache[atom_chunk]]
else:
atom = self.consumeAtom(atom_chunk, lineNumber)
self.atomCache[atom_chunk] = atom
instances += [atom]
return instances
def SplitProperties(self, string):
o = []
buf = []
inString = False
for chunk in string.split(';'):
if not inString:
if '"' in chunk:
inString = False
pos = 0
while(True):
pos = chunk.find('"', pos)
if pos == -1:
break
pc = ''
if pos > 0:
pc = chunk[pos - 1]
if pc != '\\':
inString = not inString
pos += 1
if not inString:
o += [chunk]
else:
buf += [chunk]
else:
o += [chunk]
else:
if '"' in chunk:
o += [';'.join(buf + [chunk])]
inString = False
buf = []
else:
buf += [chunk]
return o
def SplitAtoms(self, string):
ignoreLevel = []
o = []
buf = ''
string = string.rstrip()
line_len = len(string)
for i in xrange(line_len):
c = string[i]
pc = ''
if i > 0:
pc = string[i - 1]
if c in self.atomBorders and pc != '\\':
end = self.atomBorders[c]
if end == c: # Just toggle.
if len(ignoreLevel) > 0:
if ignoreLevel[-1] == c:
ignoreLevel.pop()
else:
ignoreLevel.append(c)
else:
if end == None:
if len(ignoreLevel) > 0:
if ignoreLevel[-1] == c:
ignoreLevel.pop()
else:
ignoreLevel.append(end)
if c == ',' and len(ignoreLevel) == 0:
o += [buf]
buf = ''
else:
buf += c
if len(ignoreLevel) > 0:
print(repr(ignoreLevel))
sys.exit()
return o + [buf]
def consumeAtom(self, line, lineNumber=0):
if '{' not in line:
atom = line.strip()
if atom.endswith('/'):
self.log.warn('{file}:{line}: Malformed atom: {data} has ending slash. Stripping slashes from right side.'.format(file=self.filename, line=lineNumber, data=atom))
atom = atom.rstrip('/')
currentAtom = self.map.GetAtom(atom)
if currentAtom is not None:
return currentAtom
else:
self.log.critical('{file}:{line}: Failed to consumeAtom({data}): Unable to locate atom.'.format(file=self.filename, line=lineNumber, data=line))
return None
chunks = line.split('{')
if len(chunks) < 2:
self.log.error('{file}:{line}: Something went wrong in consumeAtom(). line={data}'.format(file=self.filename, line=lineNumber, data=line))
atom = chunks[0].strip()
if atom.endswith('/'):
self.log.warn('{file}:{line}: Malformed atom: {data} has ending slash. Stripping slashes from right side.'.format(file=self.filename, line=lineNumber, data=atom))
atom = atom.rstrip('/')
currentAtom = self.map.GetAtom(atom)
if currentAtom is not None:
currentAtom = currentAtom.copy()
else:
return None
if chunks[1].endswith('}'):
chunks[1] = chunks[1][:-1]
property_chunks = self.SplitProperties(chunks[1])
mapSupplied = []
for chunk in property_chunks:
if chunk.endswith('}'):
chunk = chunk[:-1]
pparts = chunk.split('=', 1)
key = pparts[0].strip()
value = pparts[1].strip()
if key == '':
self.log.warn('Ignoring property with blank name. (given {0})'.format(chunk))
continue
data = self.consumeDataValue(value, lineNumber)
if key not in currentAtom.mapSpecified:
mapSupplied += [key]
currentAtom.properties[key] = data
currentAtom.mapSpecified = mapSupplied
# Compare to base
if True: # TODO: not (self.readFlags & Map.READ_NO_BASE_COMP):
base_atom = self.map.GetAtom(currentAtom.path)
assert base_atom != None
# for key in base_atom.properties.keys():
# val = base_atom.properties[key]
# if key not in currentAtom.properties:
# currentAtom.properties[key] = val
for key in currentAtom.properties.iterkeys():
val = currentAtom.properties[key].value
if key in base_atom.properties and val == base_atom.properties[key].value:
if key in currentAtom.mapSpecified:
currentAtom.mapSpecified.remove(key)
# print('Removed {0} from atom: Equivalent to base atom!')
return currentAtom
def consumeTile(self, line, lineNumber):
t = self.map.CreateTile()
t.origID = self.consumeTileID(line)
tileChunk = line.strip()[line.index('(') + 1:-1]
if tileChunk in self.tileChunk2ID:
tid = self.tileChunk2ID[tileChunk]
print('{} duplicate of {}! Installing redirect...'.format(t.origID, tid))
self.oldID2NewID[t.origID] = tid
self.duplicates += 1
return self.tileTypes[tid]
self.tileChunk2ID[tileChunk]=t.origID
t.instances = self.consumeTileAtoms(tileChunk, lineNumber)
if t.origID == 0:
print('{} -> {}'.format(t.origID,tileChunk))
print('AKA {}'.format(str(t)))
return t
def consumeTileID(self, line):
e = line.index('"', 1)
return self.String2ID(line[1:e])
def SerializeAtom(self, atom):
atomContents = []
# print(repr(self.mapSpecified))
if self.dump_inherited:
for key, val in atom.properties.items():
atomContents += ['{0} = {1}'.format(key, val)]
else:
for i in range(len(atom.mapSpecified)):
key = atom.mapSpecified[i]
if key in atom.properties:
val = atom.properties[key]
atomContents += ['{0} = {1}'.format(key, val)]
if len(atomContents) > 0:
return atom.path + '{' + '; '.join(atomContents) + '}'
else:
return atom.path
def SerializeTile(self, tile):
# "aat" = (/obj/structure/grille,/obj/structure/window/reinforced{dir = 8},/obj/structure/window/reinforced{dir = 1},/obj/structure/window/reinforced,/obj/structure/cable{d1 = 2; d2 = 4; icon_state = "2-4"; tag = ""},/turf/simulated/floor/plating,/area/security/prison)
atoms = []
if tile.origID == 0:
print(str(tile))
print('len = {}'.format(len(tile.instances)))
for i in xrange(len(tile.instances)):
atom = tile.instances[i]
if atom.path != '':
atoms += [self.SerializeAtom(atom)]
return '({atoms})'.format(atoms=','.join(atoms))
def ID2String(self, _id, pad=0):
o = ''
while(_id >= len(ID_ENCODING_TABLE)):
i = _id % IET_SIZE
o = ID_ENCODING_TABLE[i] + o
_id -= i
_id /= IET_SIZE
o = ID_ENCODING_TABLE[_id] + o
if pad > len(o):
o = o.rjust(pad, ID_ENCODING_TABLE[0])
return o
def String2ID(self, _id):
o = 0
for i, c in enumerate(reversed(_id)):
o += ID_ENCODING_TABLE.index(c) * (IET_SIZE ** i)
return o
def AssignTID(self, tile):
'''
:param tile Tile:
Tile to assign ID to.
'''
if tile not in self.tileTypes:
self.tileTypes.append(tile)
tile.ID = self.tileTypes.index(tile)
return self.ID2String(tile)
def GetTID(self, tile):
# print('GetTID: origID: {}'.format(repr(tile.origID)))
if self.serialize_cleanly and tile.origID != '':
return tile.origID
else:
return tile.ID
def Save(self, filename, **kwargs):
self.filename = filename
self.tileTypes = []
examined = []
hashMap = {}
self.typeMap = {}
self.type2TID = {}
self.instances = []
self.serialize_cleanly = kwargs.get('clean', True)
self.dump_inherited = kwargs.get('inherited', False)
#print('"aaa" = {}'.format(self.map.GetTileAt(0,0,0)))
# Preprocess and assign IDs.
idlen = 0
it = self.map.Tiles()
lz = -1
last_str = None
start = None
maxid=0
for tile in it:
#print(str(tile))
if lz != it.z:
if start:
print(' -> Took {}'.format(getElapsed(start)))
print(' * Consolidating level {}...'.format(it.z+1))
start = clock()
lz = it.z
strt = tile.GetHash() # str(tile)
dbg = False
#if strt == 'e18826df83665059358c177f43f6ac72':
# dbg=True
if last_str == strt:
#if dbg: print('Continuing: Same as last examined.')
continue
if strt in examined:
#if dbg: print('Continuing: Hash already examined.')
last_str = strt
continue
# tile.ID = len(examined)
examined += [strt]
tid = self.GetTID(tile)
if tid in self.typeMap:
if hashMap.get(strt, None) == tid:
if dbg: print('Continuing: TID {} in typeMap, hashes match.'.format(tid))
continue
tile.origID = ''
tid = len(self.typeMap)
while tid in self.typeMap:
tid+=1
#print('{} assigned to new TID {}'.format(strt,tid))
maxid=max(maxid,tid)
#if tid == 0: print('<{},{},{}> {}: {}'.format(it.x, it.y, it.z, tid, str(tile)))
self.typeMap[tid] = (strt,self.SerializeTile(tile))
last_str = strt
hashMap[strt]=tid
############################################################################
## HACK ALERT BECAUSE SOMETHING'S HORRIBLY WRONG AND I DON'T KNOW WHAT IT IS
############################################################################
self.typeMap[0]=(self.typeMap[0][0],'(/turf/space,/area)')
### /hack
idlen=len(self.ID2String(maxid))
tmpfile = filename + '.tmp'
print('Opening {} for write...'.format(tmpfile))
print('idlen = {}'.format(idlen))
print('"aaa" = {}'.format(self.typeMap[0][1]))
start = clock()
with open(tmpfile, 'w') as f:
for tid in sorted(self.typeMap.keys()):
stid=self.ID2String(tid, idlen)
strt,serdata=self.typeMap[tid]
f.write('"{}" = {}\n'.format(stid, serdata))
self.type2TID[strt] = stid
print(' Wrote types in {}...'.format(getElapsed(start)))
lap = clock()
for z in xrange(len(self.map.zLevels)):
print(' Writing z={}...'.format(z))
f.write('\n(1,1,{0}) = {{"\n'.format(z+1))
zlevel = self.map.zLevels[z]
for y in xrange(zlevel.height):
for x in xrange(zlevel.width):
tile = self.map.GetTileAt(x, y, z)
thash = tile.GetHash()
f.write(self.type2TID[thash])
f.write("\n")
f.write('"}\n')
print(' Wrote tiles in {}...'.format(getElapsed(lap)))
if os.path.isfile(filename):
os.remove(filename)
os.rename(tmpfile, filename)
print('-> {} in {}'.format(filename, getElapsed(start)))
+16 -8
View File
@@ -22,7 +22,7 @@ def debug(filename, line, path, message):
class OTRCache:
# : Only used for obliterating outdated data.
VERSION = [28, 4, 2014]
VERSION = [18, 6, 2014]
def __init__(self, filename):
self.filename = filename
@@ -93,13 +93,13 @@ class ObjectTree:
'atom_defaults.dm'
)
def __init__(self, **options):
#: All atoms, in a list.
# : All atoms, in a list.
self.Atoms = {}
#: All atoms, in a tree-node structure.
# : All atoms, in a tree-node structure.
self.Tree = Atom('')
#: Skip loading from .OTR?
# : Skip loading from .OTR?
self.skip_otr = False
self.LoadedStdLib = False
@@ -193,7 +193,7 @@ class ObjectTree:
if c == '"':
inString = not inString
if not inString:
filepath = os.path.join(rootdir, filename)
filepath = os.path.join(rootdir, filename.replace('\\',os.sep))
if filepath.endswith(ext):
ToRead += [filepath]
filename = ''
@@ -583,9 +583,13 @@ class ObjectTree:
line = decl = self.lineBeforePreprocessing.strip()
else:
decl = line.strip()
if '[' in line:
if '[' in line and not 'list(' in line:
line_split, arr_decl = line.split('[', 1)
str_size = arr_decl[:arr_decl.index(']')]
try:
str_size = arr_decl[:arr_decl.index(']')]
except ValueError:
logging.warn('{}:{}: MALFORMED CODE: Unable to find ]'.format(filename, ln))
logging.info(line)
size = -1
if str_size != '':
try:
@@ -626,6 +630,8 @@ class ObjectTree:
'size':size
}
if typepath != '/':
if value == 'null':
return (name, BYONDValue(None, filename, ln, typepath, **kwargs))
return (name, BYONDValue(value, filename, ln, typepath, **kwargs))
elif value and value[0] == '"':
return (name, BYONDString(value[1:-1], filename, ln, **kwargs))
@@ -636,6 +642,8 @@ class ObjectTree:
return (name, BYONDValue(float(value), filename, ln, typepath, **kwargs))
except ValueError:
pass
elif value == 'null':
return (name, BYONDValue(None, filename, ln, typepath, **kwargs))
return (name, BYONDValue(value, filename, ln, typepath, **kwargs))
def MakeTree(self):
@@ -662,7 +670,7 @@ class ObjectTree:
if '(' in path_item:
cNode.children[path_item] = Proc('/'.join([''] + cpath), [])
else:
cNode.children[path_item] = Atom('/'.join([''] + cpath),atom.filename,atom.line)
cNode.children[path_item] = Atom('/'.join([''] + cpath), atom.filename, atom.line)
cNode.children[path_item].parent = cNode
parent_type = cNode.children[path_item].getProperty('parent_type')
if parent_type is not None:
+4
View File
@@ -1,4 +1,5 @@
import hashlib, ast, os
from time import clock
import operator as op
def md5sum(filename):
@@ -26,6 +27,9 @@ def eval_expr(expr):
"""
return eval_(ast.parse(expr).body[0].value) # Module(body=[Expr(value=...)])
def getElapsed(start):
return '%d:%02d:%02d.%03d' % reduce(lambda ll, b : divmod(ll[0], b) + ll[1:], [((clock() - start) * 1000,), 1000, 60, 60])
def eval_(node):
if isinstance(node, ast.Num): # <number>
return node.n