|
@@ -0,0 +1,280 @@
|
|
|
+# Converts an Anarch map image to usable data.
|
|
|
+#
|
|
|
+# by drummyfish, edits by pixelbath
|
|
|
+# released under CC0 1.0.
|
|
|
+
|
|
|
+from PIL import Image
|
|
|
+import math
|
|
|
+
|
|
|
+class AnarchMap:
|
|
|
+ def __init__(self, image_path):
|
|
|
+ self.elementTypes = [
|
|
|
+ "NONE",
|
|
|
+ "BARREL",
|
|
|
+ "HEALTH",
|
|
|
+ "BULLETS",
|
|
|
+ "ROCKETS",
|
|
|
+ "PLASMA",
|
|
|
+ "TREE",
|
|
|
+ "FINISH",
|
|
|
+ "TELEPORTER",
|
|
|
+ "TERMINAL",
|
|
|
+ "COLUMN",
|
|
|
+ "RUIN",
|
|
|
+ "LAMP",
|
|
|
+ "CARD0",
|
|
|
+ "CARD1",
|
|
|
+ "CARD2",
|
|
|
+ "LOCK0",
|
|
|
+ "LOCK1",
|
|
|
+ "LOCK2",
|
|
|
+ "BLOCKER",
|
|
|
+ "",
|
|
|
+ "",
|
|
|
+ "",
|
|
|
+ "",
|
|
|
+ "",
|
|
|
+ "",
|
|
|
+ "",
|
|
|
+ "",
|
|
|
+ "",
|
|
|
+ "",
|
|
|
+ "",
|
|
|
+ "",
|
|
|
+ "MONSTER_SPIDER",
|
|
|
+ "MONSTER_DESTROYER",
|
|
|
+ "MONSTER_WARRIOR",
|
|
|
+ "MONSTER_PLASMABOT",
|
|
|
+ "MONSTER_ENDER",
|
|
|
+ "MONSTER_TURRET",
|
|
|
+ "MONSTER_EXPLODER"
|
|
|
+ ]
|
|
|
+
|
|
|
+ self.propertyTypes = [
|
|
|
+ "ELEVATOR",
|
|
|
+ "SQUEEZER",
|
|
|
+ "DOOR"
|
|
|
+ ]
|
|
|
+
|
|
|
+ with Image.open(image_path) as image:
|
|
|
+ self.pixel_data = image.load()
|
|
|
+
|
|
|
+ self.palette = []
|
|
|
+ self.paletteInverse = [0 for i in range(256)]
|
|
|
+
|
|
|
+ x = 5
|
|
|
+ y = 69
|
|
|
+ i = 0
|
|
|
+
|
|
|
+ # load the palette/sca
|
|
|
+ for i in range(256):
|
|
|
+ if i % 64 == 0:
|
|
|
+ x = 5
|
|
|
+ y += 1
|
|
|
+
|
|
|
+ self.palette.append(self.pixel_data[(x,y)])
|
|
|
+ self.paletteInverse[self.pixel_data[(x,y)]] = i
|
|
|
+ x += 1
|
|
|
+
|
|
|
+ self.floorDict = self.load_tile_dict(5,37)
|
|
|
+ self.ceilDict = self.load_tile_dict(5,5)
|
|
|
+ self.floorColor = self.get_pixel(41,122)
|
|
|
+ self.ceilColor = self.get_pixel(41,118)
|
|
|
+ self.backgroundTex = self.get_pixel(41,126)
|
|
|
+ self.doorTex = self.get_pixel(41,130)
|
|
|
+ self.playerStart = [0,0,0]
|
|
|
+ self.textures = []
|
|
|
+ self.elements = []
|
|
|
+ self.defines = []
|
|
|
+
|
|
|
+ self.levelMap = [[(0,False) for i in range(64)] for j in range(64)]
|
|
|
+
|
|
|
+ # load the map
|
|
|
+ for y in range(64):
|
|
|
+ for x in range(64):
|
|
|
+ n = self.get_pixel(70 + x, 5 + y)
|
|
|
+
|
|
|
+ if n < 64:
|
|
|
+ self.levelMap[63 - x][y] = (n,False)
|
|
|
+ else:
|
|
|
+ # tile with special property, create a define for it
|
|
|
+
|
|
|
+ prop = math.floor(n / 64) - 1
|
|
|
+ tile = n % 64
|
|
|
+
|
|
|
+ defNum = -1
|
|
|
+
|
|
|
+ for i in range(len(self.defines)):
|
|
|
+ if self.defines[i] == (tile, prop):
|
|
|
+ defNum = i
|
|
|
+ break
|
|
|
+
|
|
|
+ if defNum == -1: # not found:
|
|
|
+ defNum = len(self.defines)
|
|
|
+ self.defines.append((tile,prop))
|
|
|
+
|
|
|
+ self.levelMap[63 - x][y] = (defNum,True)
|
|
|
+
|
|
|
+ # load elements
|
|
|
+ playerFound = False
|
|
|
+
|
|
|
+ for y in range(64):
|
|
|
+ for x in range(64):
|
|
|
+ n = self.get_pixel(x + 70, y + 70)
|
|
|
+
|
|
|
+ if n < len(self.elementTypes):
|
|
|
+ self.elements.append((n,63 - x,y))
|
|
|
+ elif n >= 240:
|
|
|
+ if playerFound:
|
|
|
+ raise(Exception("Multiple player starting positions specified."))
|
|
|
+
|
|
|
+ playerStart = [63 - x,y,(n - 240) * 16]
|
|
|
+ playerFound = True
|
|
|
+
|
|
|
+ if not playerFound:
|
|
|
+ raise(Exception("Player starting position not specified."))
|
|
|
+
|
|
|
+ if len(self.elements) > 128:
|
|
|
+ raise(Exception("More than 128 level elements."))
|
|
|
+
|
|
|
+ for i in range(128 - len(self.elements)):
|
|
|
+ self.elements.append((0,0,0))
|
|
|
+
|
|
|
+ # load textures
|
|
|
+ x = 41
|
|
|
+ y = 114
|
|
|
+
|
|
|
+ for i in range(7):
|
|
|
+ self.textures.append(self.get_pixel(x, y))
|
|
|
+ x += 4
|
|
|
+
|
|
|
+
|
|
|
+ def get_pixel(self, x, y):
|
|
|
+ return self.paletteInverse[self.pixel_data[(x, y)]]
|
|
|
+
|
|
|
+ def load_tile_dict(self, x, y):
|
|
|
+ result = []
|
|
|
+
|
|
|
+ for i in range(64):
|
|
|
+ texture = self.get_pixel(x + i, y + 31)
|
|
|
+
|
|
|
+ if texture > 7:
|
|
|
+ raise(Exception("Texture index can't be higher than 7."))
|
|
|
+
|
|
|
+ height = 0
|
|
|
+
|
|
|
+ for j in range(31):
|
|
|
+ if self.get_pixel(x + i, y + 30 - j) == 7:
|
|
|
+ break
|
|
|
+
|
|
|
+ height += 1
|
|
|
+
|
|
|
+ result.append((texture,height))
|
|
|
+
|
|
|
+ return result
|
|
|
+
|
|
|
+ def defineName(self, n):
|
|
|
+ c = chr(ord("A") + n)
|
|
|
+ return c + c
|
|
|
+
|
|
|
+ def numAlign(self, n):
|
|
|
+ return str(n) + "," + (" " if n < 10 else "")
|
|
|
+
|
|
|
+ def mapXScale(self):
|
|
|
+ r = " // "
|
|
|
+
|
|
|
+ for i in range(64):
|
|
|
+ r += str(i).ljust(2) + " "
|
|
|
+
|
|
|
+ return r + "\n"
|
|
|
+
|
|
|
+ def printC(self):
|
|
|
+ result = ""
|
|
|
+ result += " { // level\n"
|
|
|
+ result += " { // mapArray\n"
|
|
|
+ result += " #define o 0\n"
|
|
|
+
|
|
|
+ for i in range(len(self.defines)):
|
|
|
+ print("%s : %s" % (self.defines[i][0], self.defines[i][1]))
|
|
|
+ result += " #define " + self.defineName(i) + " (" + str(self.defines[i][0]) + " | SFG_TILE_PROPERTY_" + self.propertyTypes[self.defines[i][1]] + ")\n"
|
|
|
+
|
|
|
+ result += self.mapXScale()
|
|
|
+
|
|
|
+ for y in range(64):
|
|
|
+ result += "/*" + str(y).ljust(2) + "*/ "
|
|
|
+
|
|
|
+ for x in range(64):
|
|
|
+ item = self.levelMap[x][y]
|
|
|
+
|
|
|
+ if not item[1]:
|
|
|
+ result += ("o " if item[0] == 0 else str(item[0]).ljust(2))
|
|
|
+ else:
|
|
|
+ result += self.defineName(item[0])
|
|
|
+
|
|
|
+ result += "," if y < 63 or x < 63 else " "
|
|
|
+
|
|
|
+ result += " /*" + str(y).ljust(2) + "*/ \n"
|
|
|
+
|
|
|
+ result += self.mapXScale()
|
|
|
+
|
|
|
+ for i in range(len(self.defines)):
|
|
|
+ result += " #undef " + self.defineName(i) + "\n"
|
|
|
+
|
|
|
+ result += " #undef o\n"
|
|
|
+ result += " },\n"
|
|
|
+ result += " { // tileDictionary\n "
|
|
|
+
|
|
|
+ for i in range(64):
|
|
|
+ result += "SFG_TD(" + str(self.floorDict[i][1]).rjust(2) + "," + str(self.ceilDict[i][1]).rjust(2) + "," + str(self.floorDict[i][0]) + "," + str(self.ceilDict[i][0]) + ")"
|
|
|
+
|
|
|
+ result += "," if i != 63 else " "
|
|
|
+
|
|
|
+ if (i + 1) % 4 == 0:
|
|
|
+ result += " // " + str(i - 3) + " \n "
|
|
|
+
|
|
|
+ result += "}, // tileDictionary\n"
|
|
|
+
|
|
|
+ s = ""
|
|
|
+ first = True
|
|
|
+
|
|
|
+ for t in self.textures:
|
|
|
+ if first:
|
|
|
+ first = False
|
|
|
+ else:
|
|
|
+ s += ","
|
|
|
+
|
|
|
+ s += str(t).ljust(2)
|
|
|
+
|
|
|
+ result += " {" + s + "}, // textureIndices\n"
|
|
|
+ result += " " + self.numAlign(self.doorTex) + " // doorTextureIndex\n"
|
|
|
+ result += " " + self.numAlign(self.floorColor) + " // floorColor\n"
|
|
|
+ result += " " + self.numAlign(self.ceilColor) + " // ceilingColor\n"
|
|
|
+ result += " {" + str(self.playerStart[0]).ljust(2) + ", " + str(self.playerStart[1]).ljust(2) + ", " + str(self.playerStart[2]).ljust(3) + "}, // player start: x, y, direction\n"
|
|
|
+ result += " " + self.numAlign(self.backgroundTex) + " // backgroundImage\n"
|
|
|
+ result += " { // elements\n"
|
|
|
+
|
|
|
+ even = True
|
|
|
+
|
|
|
+ i = 0
|
|
|
+
|
|
|
+ for e in self.elements:
|
|
|
+ if even:
|
|
|
+ result += " "
|
|
|
+
|
|
|
+ result += "{SFG_LEVEL_ELEMENT_" + self.elementTypes[e[0]] + ", {" + str(e[1]) + "," + str(e[2]) + "}}"
|
|
|
+
|
|
|
+ if i < 127:
|
|
|
+ result += ","
|
|
|
+
|
|
|
+ if not even:
|
|
|
+ result += "\n"
|
|
|
+
|
|
|
+ even = not even
|
|
|
+
|
|
|
+ i += 1
|
|
|
+
|
|
|
+ result += " }, // elements\n"
|
|
|
+ result += " } // level\n"
|
|
|
+
|
|
|
+ print(result)
|
|
|
+
|