from copy import deepcopy
import numpy as np
from PySide6.QtCore import QPoint, QPointF, QSize, QByteArray, QBuffer, QIODevice, QRect
from PySide6.QtGui import QImage, QPainter, QColor, QImageWriter
from BaseMod.tiles.tileUtils import PlacedMaterial, PlacedTileHead, PlacedTileBody
from RWS.Core import lingoIO
from RWS.Core import SPRITESIZE, CELLSIZE, ofsleft, ofstop
from RWS.Modify import LevelPart
from RWS.Utils import polar2point, point2polar
stack_pos = [1, 2, 11, 3, 4, 5, 6, 7, 9, 10, 12, 13, 19, 21, 20, 18]
[docs]
class InfoLevelPart(LevelPart):
[docs]
def level_resized(self, changerect: QRect):
from BaseMod.properties.PropertiesHistory import LevelResizedProperties
return LevelResizedProperties(self.level.history, changerect)
[docs]
def save_level(self):
self.level.data["EX2"]["extraTiles"] = self.extra_tiles.copy()
self.level.data["EX2"]["tileSeed"] = self.tile_seed
self.level.data["EX2"]["size"] = lingoIO.point(self.size)
self.level.data["WL"]["waterLevel"] = self.water_level
self.level.data["WL"]["waterInFront"] = 1 if self.water_in_front else 0
[docs]
def __init__(self, level):
super().__init__("info", level)
self.size: list[int] = lingoIO.fromarr(level.data["EX2"]["size"], "point")
self.extra_tiles: list[int] = level.data["EX2"]["extraTiles"].copy()
self.tile_seed: int = level.data["EX2"]["tileSeed"]
self.water_level: int = level.data["WL"]["waterLevel"]
self.water_in_front = level.data["WL"]["waterInFront"] == 1
@property
def width(self):
return self.size[0]
@property
def height(self):
return self.size[1]
[docs]
class GeoLevelPart(LevelPart):
[docs]
def __init__(self, level):
super().__init__("geo", level)
level_size = (len(level.data["GE"]), len(level.data["GE"][0]))
self.blocks = np.zeros((*level_size, 3), np.uint8)
self.stack = np.zeros((*level_size, 3), np.uint16)
# loading level
self.load_level()
[docs]
def load_level(self):
dat = self.level.data["GE"]
with np.nditer(self.blocks, flags=['multi_index'], op_flags=['writeonly']) as it:
for x in it:
x[...] = dat[it.multi_index[0]][it.multi_index[1]][it.multi_index[2]][0]
with np.nditer(self.stack, flags=['multi_index'], op_flags=['writeonly']) as it:
for x in it:
x[...] = self.stack2byte(dat[it.multi_index[0]][it.multi_index[1]][it.multi_index[2]][1])
#print("new")
#print(np.asarray(dat, np.int8))
[docs]
def save_level(self):
self.level.data["GE"] = [[[[0, []], [0, []], [0, []]] for _ in range(self.blocks.shape[1])] for _ in range(self.blocks.shape[0])]
with np.nditer(self.blocks, flags=['multi_index'], op_flags=['readonly']) as it:
for x in it:
self.level.data["GE"][it.multi_index[0]][it.multi_index[1]][it.multi_index[2]][0] = int(x[...])
with np.nditer(self.stack, flags=['multi_index'], op_flags=['readonly']) as it:
for x in it:
self.level.data["GE"][it.multi_index[0]][it.multi_index[1]][it.multi_index[2]][1] = self.byte2stack(x[...])
[docs]
def level_resized(self, changerect: QRect):
from BaseMod.geo.geoHistory import LevelResizedGeo
return LevelResizedGeo(self.level.history, changerect)
[docs]
@staticmethod
def stack2byte(stack) -> np.int16:
b = np.uint16()
for i in stack:
b |= 1 << stack_pos.index(i)
return b
[docs]
@staticmethod
def byte2stack(b) -> list:
stack = []
for i in range(16):
if b & 1 << i > 0:
stack.append(stack_pos[i])
return stack
[docs]
def getlevelgeo_all(self, x: int, y: int) -> [np.uint8, np.uint16]:
return [[self.blocks[x, y, 0], self.stack[x, y, 0]], [self.blocks[x, y, 1], self.stack[x, y, 1]], [self.blocks[x, y, 2], self.stack[x, y, 2]]]
[docs]
def getlevelgeo(self, x: int, y: int, l: int) -> [np.uint8, np.uint16]:
return [self.blocks[x, y, l], self.stack[x, y, l]]
[docs]
def setlevelgeo(self, x: int, y: int, l: int, v: [np.uint8, np.uint16]):
self.blocks[x, y, l], self.stack[x, y, l] = v
@property
def width(self):
return self.blocks.shape[0]
@property
def height(self):
return self.blocks.shape[1]
[docs]
class TileLevelPart(LevelPart):
[docs]
def __init__(self, level):
super().__init__("tiles", level)
# self.tiles = self.level.data["TE"]["tlMatrix"]
self.default_material = level["TE"]["defaultMaterial"]
self.tiles: list[list[list[PlacedTileHead | PlacedTileBody | PlacedMaterial | None]]] | None = None
self.load_tiles()
[docs]
def load_tiles(self):
# tilebodies: dict[(QPoint, int), list[PlacedTileBody]] = {}
self.tiles = [[[self.scantile(QPoint(x, y), 0),
self.scantile(QPoint(x, y), 1),
self.scantile(QPoint(x, y), 2)]
for y in range(self.level.level_height)] for x in range(self.level.level_width)]
# for k, v in tilebodies.items():
# pos = k[0]
# layer = k[1]
# tilehead = self.tiles[pos.x()][pos.y()][layer]
# if not isinstance(tilehead, PlacedTileHead):
# continue
# tilehead.tilebodies = v
# for i in v:
# i.tilehead = tilehead
[docs]
def scantile(self, pos: QPoint, layer: int):
tile = self.level.data["TE"]["tlMatrix"][pos.x()][pos.y()][layer]
match tile["tp"]:
case "material":
foundtile = self.level.manager.tiles.find_tile(tile["data"])
if foundtile is None:
return None # todo notfound material and tile exceptions
return PlacedMaterial(foundtile, pos, layer)
case "tileHead":
foundtile = self.level.manager.tiles.find_tile(tile["data"][1])
if foundtile is None:
return None
return PlacedTileHead(foundtile, pos, layer)
case "tileBody":
head = tile.get("data")
headlayer = head[1] - 1
tileheadpos = QPoint(*lingoIO.frompoint(head[0])) - QPoint(1, 1)
if self.level.data["TE"]["tlMatrix"][tileheadpos.x()][tileheadpos.y()][headlayer].get("tp") == "default":
return None # removing all improper tile bodies
# l = tb.get((tileheadpos, layer), None)
tileheadoffset = pos - tileheadpos
tilebody = PlacedTileBody(tileheadoffset, headlayer, pos, layer)
# if l is None:
# tb[(tileheadpos, layer)] = [tilebody]
# return tilebody
# l.append(tilebody)
return tilebody
[docs]
def save_level(self):
self.level.data["TE"]["tlMatrix"] = [[[self.convert_to_dict(x, y, 0), self.convert_to_dict(x, y, 1), self.convert_to_dict(x, y, 2)]
for y in range(self.level.level_height)] for x in range(self.level.level_width)]
self.level.data["TE"]["defaultMaterial"] = self.default_material
[docs]
def convert_to_dict(self, x, y, l):
tile = self.tiles[x][y][l]
if tile is None:
return {"tp": "default", "data": 0}
if isinstance(tile, PlacedMaterial):
return {"tp": "material", "data": tile.tile.name}
if isinstance(tile, PlacedTileBody):
return {"tp": "tileBody", "data": [lingoIO.point((tile.headpos + QPoint(1, 1)).toTuple()), tile.headlayer + 1]}
if isinstance(tile, PlacedTileHead):
return {"tp": "tileHead", "data": [lingoIO.point((tile.tile.cat + QPoint(1, 1)).toTuple()), tile.tile.name]}
raise NotImplementedError("someone probably fucked up")
[docs]
def level_resized(self, changerect: QRect):
from BaseMod.tiles.tileHistory import LevelResizedTiles
return LevelResizedTiles(self.level.history, changerect)
[docs]
def tile_data_xy(self, x: int, y: int, layer: int) -> None | PlacedTileBody | PlacedTileHead | PlacedMaterial:
"""
returns tile on specific layer
:param x: x position of tile
:param y: y position of tile
:param layer: layer of tile(0-2)
:return:
"""
return self.tiles[x][y][layer]
[docs]
def tile_data(self, pos: QPoint, layer: int) -> None | PlacedTileBody | PlacedTileHead | PlacedMaterial:
"""
returns tile on specific layer
:param pos: position of tile
:param layer: layer of tile(0-2)
:return:
"""
return self.tiles[pos.x()][pos.y()][layer]
def __call__(self, *args, **kwargs):
return self.tile_data(*args, **kwargs)
def __getitem__(self, item: QPoint):
return self.tiles[item.x()][item.y()]
[docs]
class PropLevelPart(LevelPart):
[docs]
def __init__(self, level):
super().__init__("props", level)
self.props: list[PropLevelPart.PlacedProp] = []
for i in self.level["PR"]["props"]:
newp = self.copyprop(i)
# find prop
found = self.manager.props.find_prop(newp[1])
if found is None:
found = self.manager.props.default
prop = PropLevelPart.PlacedProp(found, newp[0],
[QPointF(*lingoIO.fromarr(p, "point")) * (CELLSIZE / SPRITESIZE) for p in newp[3]],
newp[4])
# newp[3] = [QPointF(*lingoIO.fromarr(p, "point")) * (CELLSIZE / SPRITESIZE) for p in newp[3]]
# if newp[4].get("points") is not None:
# newp[4]["points"] = [QPointF(*lingoIO.fromarr(p, "point")) * (CELLSIZE / SPRITESIZE) for p in newp[4]["points"]]
self.props.append(prop)
[docs]
def level_resized(self, changerect: QRect):
from BaseMod.props.propHistory import LevelResizedProps
return LevelResizedProps(self.level.history, changerect)
[docs]
def save_level(self):
self.level["PR"]["props"] = []
for i in self.props:
self.level["PR"]["props"].append(i.tolist)
[docs]
class PlacedProp:
def __init__(self, prop, depth: int, quad: [QPointF, QPointF, QPointF, QPointF], settings: dict):
self.prop = prop
self.depth = depth
self.quad = quad
self.settings = settings
@property
def name(self):
return self.prop.name
@property
def tolist(self):
return [self.depth, self.name, lingoIO.point([i + 1 for i in self.prop.cat.toTuple()]),
[lingoIO.point((p * (SPRITESIZE / CELLSIZE)).toTuple()) for p in self.quad], self.settings]
[docs]
def copy(self):
return PropLevelPart.PlacedProp(self.prop, self.depth, self.quad.copy(), self.settings.copy())
def __len__(self):
return len(self.props)
def __iter__(self):
return self.props.__iter__()
def __getitem__(self, item):
return self.props[item]
def __setitem__(self, key, value):
self.props[key] = value
[docs]
def pop(self, index):
return self.props.pop(index)
[docs]
def insert(self, index, prop):
self.props.insert(index, prop)
[docs]
def copyprop(self, prop):
return [prop[0], prop[1], prop[2], prop[3].copy(), prop[4].copy()]
[docs]
def index(self, item):
return self.props.index(item)
[docs]
class EffectLevelPart(LevelPart):
[docs]
def __init__(self, level):
super().__init__("effects", level)
self.effects = []
self.load_level()
[docs]
def level_resized(self, changerect: QRect):
from BaseMod.effects.effectHistory import LevelResizedEffects
return LevelResizedEffects(self.level.history, changerect)
[docs]
def load_level(self):
dat = self.level.data["FE"]["effects"]
for i in dat:
self.effects.append(deepcopy({k: v for k, v in i.items() if k != "mtrx"}))
self.effects[-1]["mtrx"] = np.zeros(self.level.level_size.toTuple(), np.float16)
with np.nditer(self.effects[-1]["mtrx"], flags=['multi_index'], op_flags=['writeonly']) as it:
for x in it:
x[...] = i["mtrx"][it.multi_index[0]][it.multi_index[1]]
[docs]
def save_level(self):
self.level.data["FE"]["effects"] = []
for i in self.effects:
newi = deepcopy({k: v for k, v in i.items() if k != "mtrx"})
newi["mtrx"] = [[round(float(i["mtrx"][x, y]), 4) for y in range(self.level.level_height)] for x in range(self.level.level_width)]
self.level.data["FE"]["effects"].append(newi)
[docs]
def effect_data_xy(self, index: int, x: int, y:int) -> float:
"""
returns specific value of effect
:param index: effect index
:param x: x pos of value
:param y: y pos of value
"""
return self.effects[index]["mtrx"][x, y]
def __len__(self):
return len(self.effects)
def __getitem__(self, item):
if isinstance(item, int):
return self.effects[item]
return self.effects[item[0]]["mtrx"][item[1], item[2]]
def __setitem__(self, key, value):
self.effects[key[0]]["mtrx"][key[1], key[2]] = value
def __iter__(self):
return self.effects.__iter__()
[docs]
def append(self, item):
self.effects.append(item)
[docs]
def insert(self, index, item):
self.effects.insert(index, item)
[docs]
def pop(self, index=-1):
return self.effects.pop(index)
[docs]
def index(self, item):
return self.effects.index(item)
[docs]
class CameraLevelPart(LevelPart):
[docs]
def __init__(self, level):
super().__init__("camera", level)
self.cameras: list[CameraLevelPart.Camera] = []
for i, v in enumerate(level.data["CM"]["cameras"]):
quad = [QPointF(*k) for k in level.data["CM"]["quads"][i]]
quad = [polar2point(QPointF(i.x() - 90, i.y())) for i in quad]
self.cameras.append(CameraLevelPart.Camera(QPointF(*lingoIO.frompoint(v)), quad))
[docs]
def level_resized(self, changerect: QRect):
from BaseMod.camera.cameraHistory import LevelResizedCameras
return LevelResizedCameras(self.level.history, changerect)
[docs]
def save_level(self):
self.level.data["CM"]["cameras"] = []
self.level.data["CM"]["quads"] = []
for i in self.cameras:
newquads = [point2polar(q) for q in i.quads]
quad = [[(round(k.x(), 4) + 90) % 360, round(k.y(), 4)] for k in newquads]
self.level.data["CM"]["quads"].append(quad)
self.level.data["CM"]["cameras"].append(lingoIO.point(i.pos.toTuple()))
[docs]
class Camera:
def __init__(self, pos: QPointF, quads: list[QPointF]):
self.pos = QPointF(round(pos.x(), 4), round(pos.y(), 4))
self.quads = quads
def __iter__(self):
return self.cameras.__iter__()
def __len__(self):
return self.cameras.__len__()
[docs]
def pop(self, index):
return self.cameras.pop(index)
[docs]
def insert(self, index, prop):
self.cameras.insert(index, prop)
[docs]
def index(self, item):
return self.cameras.index(item)
def __getitem__(self, item):
return self.cameras.__getitem__(item)
[docs]
class LightLevelPart(LevelPart):
[docs]
def __init__(self, level):
super().__init__("light", level)
self.angle = level.data["LE"]["lightAngle"]
self.flatness = level.data["LE"]["flatness"]
newcolortable = [QColor(0, 0, 0, 0).rgba(), QColor(0, 0, 0, 255).rgba()]
imagesize = QSize((self.level.level_width + ofsleft) * CELLSIZE, (self.level.level_height + ofstop) * CELLSIZE)
if level.lightdata is None:
newimage = QImage(imagesize, QImage.Format.Format_Mono)
newimage.setColorTable(newcolortable)
newimage.fill(0)
self.image = newimage
return
self.image = QImage.fromData(level.lightdata)
self.image.convertTo(QImage.Format.Format_Mono)
self.image.setColorTable(newcolortable)
if self.image.size() != imagesize:
newimage = QImage(imagesize, QImage.Format.Format_Mono)
newimage.setColorTable(newcolortable)
newimage.fill(0)
painter = QPainter(newimage)
painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_Source)
painter.drawImage(QPoint(), self.image)
self.image = newimage
[docs]
def level_resized(self, changerect: QRect):
from BaseMod.light.lightHistory import LevelResizedLight
return LevelResizedLight(self.level.history, changerect)
[docs]
def save_level(self):
self.level.data["LE"]["lightAngle"] = round(self.angle, 4)
self.level.data["LE"]["flatness"] = int(round(self.flatness))
ba = QByteArray()
buff = QBuffer(ba)
buff.open(QIODevice.OpenModeFlag.WriteOnly)
writer = QImageWriter(buff, b"PNG")
writer.write(self.image)
self.level.lightdata = ba.data()