mirror of
https://github.com/luksamuk/engine-psx.git
synced 2025-04-28 13:28:02 +03:00
Add placements file
This commit is contained in:
parent
e28ede29a7
commit
7a857049b3
5 changed files with 122 additions and 33 deletions
BIN
assets/levels/R2/Z1.OMP
Normal file
BIN
assets/levels/R2/Z1.OMP
Normal file
Binary file not shown.
|
@ -9,9 +9,9 @@ from pprint import pp
|
||||||
def main():
|
def main():
|
||||||
map_src = realpath(sys.argv[1])
|
map_src = realpath(sys.argv[1])
|
||||||
obj_defs, obj_places = parse_map(map_src)
|
obj_defs, obj_places = parse_map(map_src)
|
||||||
# pp(obj_places)
|
|
||||||
for key, d in obj_defs.items():
|
for key, d in obj_defs.items():
|
||||||
d.write()
|
d.write()
|
||||||
|
obj_places.write()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -13,7 +13,7 @@ class DummyObjectId(Enum):
|
||||||
RING_3V = -2
|
RING_3V = -2
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get(name: str) -> int:
|
def get(name):
|
||||||
switch = {
|
switch = {
|
||||||
"ring_3h": DummyObjectId.RING_3H,
|
"ring_3h": DummyObjectId.RING_3H,
|
||||||
"ring_3v": DummyObjectId.RING_3V,
|
"ring_3v": DummyObjectId.RING_3V,
|
||||||
|
@ -23,6 +23,37 @@ class DummyObjectId(Enum):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectId(Enum):
|
||||||
|
RING = 0
|
||||||
|
MONITOR = 1
|
||||||
|
SPIKES = 2
|
||||||
|
CHECKPOINT = 3
|
||||||
|
SPRING_YELLOW = 4
|
||||||
|
SPRING_RED = 5
|
||||||
|
SPRING_YELLOW_DIAGONAL = 6
|
||||||
|
SPRING_RED_DIAGONAL = 7
|
||||||
|
GOAL_SIGN = 8
|
||||||
|
SWITCH = 9
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get(name):
|
||||||
|
switch = {
|
||||||
|
"ring": ObjectId.RING,
|
||||||
|
"monitor": ObjectId.MONITOR,
|
||||||
|
"spikes": ObjectId.SPIKES,
|
||||||
|
"checkpoint": ObjectId.CHECKPOINT,
|
||||||
|
"spring_yellow": ObjectId.SPRING_YELLOW,
|
||||||
|
"spring_red": ObjectId.SPRING_RED,
|
||||||
|
"spring_yellow_diagonal": ObjectId.SPRING_YELLOW_DIAGONAL,
|
||||||
|
"spring_red_diagonal": ObjectId.SPRING_RED_DIAGONAL,
|
||||||
|
"goal_sign": ObjectId.GOAL_SIGN,
|
||||||
|
"switch": ObjectId.SWITCH,
|
||||||
|
}
|
||||||
|
result = switch.get(name.lower())
|
||||||
|
assert result is not None, f"Unknown common object {name}"
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
# OBJECT TABLE DEFINITION (.OTN) LAYOUT
|
# OBJECT TABLE DEFINITION (.OTN) LAYOUT
|
||||||
# - is_level_specific (u8)
|
# - is_level_specific (u8)
|
||||||
# - num_classes (u16)
|
# - num_classes (u16)
|
||||||
|
@ -148,27 +179,45 @@ class ObjectMap:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
# OBJECT MAP PLACEMENT (.OMP) LAYOUT
|
# =======================================
|
||||||
# - is_level_specific (u8)
|
|
||||||
# - Type / ID (s8)
|
|
||||||
# - Flip Mask (u8)
|
class MonitorKind(Enum):
|
||||||
# - has_properties (u8)
|
NONE = 0
|
||||||
# - vx (s32)
|
RING = 1
|
||||||
# - vy (s32)
|
SPEEDSHOES = 2
|
||||||
# - Properties (exists depending on Type)
|
SHIELD = 3
|
||||||
# * Properties layout for monitor (id = 1):
|
INVINCIBILITY = 4
|
||||||
# - kind (u8)
|
LIFE = 5
|
||||||
|
SUPER = 6
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get(name):
|
||||||
|
switch = {
|
||||||
|
"NONE": MonitorKind.NONE,
|
||||||
|
"RING": MonitorKind.RING,
|
||||||
|
"SPEEDSHOES": MonitorKind.SPEEDSHOES,
|
||||||
|
"SHIELD": MonitorKind.SHIELD,
|
||||||
|
"INVINCIBILITY": MonitorKind.INVINCIBILITY,
|
||||||
|
"1UP": MonitorKind.LIFE,
|
||||||
|
"SUPER": MonitorKind.SUPER,
|
||||||
|
}
|
||||||
|
result = switch.get(name.upper())
|
||||||
|
assert result is not None, f"Unknown monitor kind {name}"
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class MonitorProperties:
|
class MonitorProperties:
|
||||||
kind: str = ""
|
kind: int = 0
|
||||||
|
|
||||||
|
def write_to(self, f):
|
||||||
|
f.write(c_ubyte(self.kind))
|
||||||
|
|
||||||
|
|
||||||
ObjectProperties = MonitorProperties | None
|
ObjectProperties = MonitorProperties | None
|
||||||
|
|
||||||
|
|
||||||
# Root for the .OMP datatype
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ObjectPlacement:
|
class ObjectPlacement:
|
||||||
is_level_specific: bool = False
|
is_level_specific: bool = False
|
||||||
|
@ -194,4 +243,37 @@ class ObjectPlacement:
|
||||||
f.write(c_int(self.x))
|
f.write(c_int(self.x))
|
||||||
f.write(c_int(self.y))
|
f.write(c_int(self.y))
|
||||||
f.write(c_ubyte(flipmask))
|
f.write(c_ubyte(flipmask))
|
||||||
# TODO: Properties
|
if self.properties is not None:
|
||||||
|
self.properties.write_to(f)
|
||||||
|
|
||||||
|
|
||||||
|
# OBJECT MAP PLACEMENT (.OMP) LAYOUT
|
||||||
|
# - num_objects (u16)
|
||||||
|
# - Array of object placements:
|
||||||
|
# - is_level_specific (u8)
|
||||||
|
# - Type / ID (s8)
|
||||||
|
# - Flip Mask (u8)
|
||||||
|
# - has_properties (u8)
|
||||||
|
# - vx (s32)
|
||||||
|
# - vy (s32)
|
||||||
|
# - Properties (exists depending on Type)
|
||||||
|
# * Properties layout for monitor (id = 1):
|
||||||
|
# - kind (u8)
|
||||||
|
|
||||||
|
|
||||||
|
# Root for the .OMP datatype
|
||||||
|
@dataclass
|
||||||
|
class ObjectLevelLayout:
|
||||||
|
out: str = ""
|
||||||
|
placements: [ObjectPlacement] = field(default_factory=list)
|
||||||
|
|
||||||
|
def write_to(self, f):
|
||||||
|
f.write(c_ushort(len(self.placements)))
|
||||||
|
for p in self.placements:
|
||||||
|
description = DummyObjectId(p.otype) if p.otype < 0 else ObjectId(p.otype)
|
||||||
|
print(f"Writing placement for object ID {p.otype} ({description})...")
|
||||||
|
p.write_to(f)
|
||||||
|
|
||||||
|
def write(self):
|
||||||
|
with open(self.out, "wb") as f:
|
||||||
|
self.write_to(f)
|
||||||
|
|
|
@ -36,6 +36,8 @@ def parse_tileset(firstgid: int, set_src: str) -> (ObjectMap, str):
|
||||||
|
|
||||||
ts = ts.find("tileset")
|
ts = ts.find("tileset")
|
||||||
|
|
||||||
|
o.is_level_specific = ts["name"] != "objects_common"
|
||||||
|
|
||||||
tiles = ts.find_all("tile")
|
tiles = ts.find_all("tile")
|
||||||
o.num_objs = int(ts["tilecount"])
|
o.num_objs = int(ts["tilecount"])
|
||||||
# "classes" becomes an entry on dict o.object_types.
|
# "classes" becomes an entry on dict o.object_types.
|
||||||
|
@ -87,7 +89,9 @@ def parse_tileset(firstgid: int, set_src: str) -> (ObjectMap, str):
|
||||||
# ]
|
# ]
|
||||||
# collision["points"] = points
|
# collision["points"] = points
|
||||||
|
|
||||||
o.obj_mapping[gid] = gid - firstgid
|
o.obj_mapping[gid] = (
|
||||||
|
(gid - firstgid) if o.is_level_specific else ObjectId.get(od.name).value
|
||||||
|
)
|
||||||
|
|
||||||
# Append TOML data
|
# Append TOML data
|
||||||
if extra:
|
if extra:
|
||||||
|
@ -116,7 +120,6 @@ def parse_tileset(firstgid: int, set_src: str) -> (ObjectMap, str):
|
||||||
|
|
||||||
o.firstgid = firstgid
|
o.firstgid = firstgid
|
||||||
o.out = splitext(set_src)[0] + ".OTD"
|
o.out = splitext(set_src)[0] + ".OTD"
|
||||||
o.is_level_specific = ts["name"] != "objects_common"
|
|
||||||
o.num_objs = len(o.object_types)
|
o.num_objs = len(o.object_types)
|
||||||
o.name = ts["name"]
|
o.name = ts["name"]
|
||||||
return (o, ts["name"])
|
return (o, ts["name"])
|
||||||
|
@ -136,11 +139,9 @@ def parse_object_group(
|
||||||
# Get first object's gid.
|
# Get first object's gid.
|
||||||
first_obj = sorted(objects, key=lambda x: int(x.get("gid")))[0]
|
first_obj = sorted(objects, key=lambda x: int(x.get("gid")))[0]
|
||||||
first_obj_gid = int(first_obj.get("gid"))
|
first_obj_gid = int(first_obj.get("gid"))
|
||||||
print(f"First object gid: {first_obj_gid}")
|
|
||||||
|
|
||||||
# Identify if this is from common objects tileset or from level-specific tileset.
|
# Identify if this is from common objects tileset or from level-specific tileset.
|
||||||
for key, ts in tilesets.items():
|
for key, ts in tilesets.items():
|
||||||
print(f"First gid: {ts.firstgid}")
|
|
||||||
result = ts.get_is_specific_if_from_this_map(first_obj_gid)
|
result = ts.get_is_specific_if_from_this_map(first_obj_gid)
|
||||||
if result is not None:
|
if result is not None:
|
||||||
current_ts = ts
|
current_ts = ts
|
||||||
|
@ -153,32 +154,38 @@ def parse_object_group(
|
||||||
for obj in objects:
|
for obj in objects:
|
||||||
p = ObjectPlacement()
|
p = ObjectPlacement()
|
||||||
p.is_level_specific = is_level_specific
|
p.is_level_specific = is_level_specific
|
||||||
p.otype = current_ts.get_otype_from_gid(int(obj.get("gid")))
|
gid = int(obj.get("gid"))
|
||||||
|
p.otype = current_ts.get_otype_from_gid(gid)
|
||||||
p.x = int(float(obj.get("x")))
|
p.x = int(float(obj.get("x")))
|
||||||
p.y = int(float(obj.get("y")))
|
p.y = int(float(obj.get("y")))
|
||||||
p.flipx = False # TODO
|
p.flipx = bool(gid & (1 << 31))
|
||||||
p.flipy = False # TODO
|
p.flipy = bool(gid & (1 << 30))
|
||||||
p.rotcw = int(float(obj.get("rotation", 0))) == 90
|
p.rotcw = int(float(obj.get("rotation", 0))) == 90
|
||||||
p.rotccw = int(float(obj.get("rotation", 0))) == -90
|
p.rotccw = int(float(obj.get("rotation", 0))) == -90
|
||||||
props = obj.find("properties")
|
props = obj.find("properties")
|
||||||
if props:
|
if props:
|
||||||
# TODO: Check type, build type-specific property, etc
|
if p.otype == ObjectId.MONITOR.value:
|
||||||
|
prop = props.find("property")
|
||||||
|
m = MonitorProperties()
|
||||||
|
m.kind = MonitorKind.get(prop.get("value")).value
|
||||||
|
p.properties = m
|
||||||
pass
|
pass
|
||||||
print(
|
# print(
|
||||||
f"Object type {current_ts.object_types[p.otype + current_ts.firstgid].name if p.otype >= 0 else 'DUMMY'}"
|
# f"Object type {current_ts.object_types[p.otype + current_ts.firstgid].name if p.otype >= 0 else 'DUMMY'}"
|
||||||
)
|
# )
|
||||||
pp(p)
|
# pp(p)
|
||||||
placements.append(p)
|
placements.append(p)
|
||||||
|
|
||||||
return placements
|
return placements
|
||||||
|
|
||||||
|
|
||||||
def parse_map(map_src: str) -> (typing.Dict[str, ObjectMap], [ObjectPlacement]):
|
def parse_map(map_src: str) -> (typing.Dict[str, ObjectMap], ObjectLevelLayout):
|
||||||
map = None
|
map = None
|
||||||
with open(map_src) as f:
|
with open(map_src) as f:
|
||||||
map = BeautifulSoup(f, "xml")
|
map = BeautifulSoup(f, "xml")
|
||||||
objmaps = {}
|
objmaps = {}
|
||||||
placements = []
|
layout = ObjectLevelLayout()
|
||||||
|
layout.out = realpath(splitext(map_src)[0] + ".OMP")
|
||||||
|
|
||||||
# Get all tilesets that are not 128x128.
|
# Get all tilesets that are not 128x128.
|
||||||
# Depends on tileset name.
|
# Depends on tileset name.
|
||||||
|
@ -194,6 +201,6 @@ def parse_map(map_src: str) -> (typing.Dict[str, ObjectMap], [ObjectPlacement]):
|
||||||
layergroup = map.find(name="group", attrs={"name": "OBJECTS"})
|
layergroup = map.find(name="group", attrs={"name": "OBJECTS"})
|
||||||
objgroups = layergroup.find_all("objectgroup")
|
objgroups = layergroup.find_all("objectgroup")
|
||||||
for objgroup in objgroups:
|
for objgroup in objgroups:
|
||||||
placements += parse_object_group(objmaps, objgroup)
|
layout.placements += parse_object_group(objmaps, objgroup)
|
||||||
|
|
||||||
return (objmaps, placements)
|
return (objmaps, layout)
|
||||||
|
|
|
@ -1701,8 +1701,8 @@ For brevity of detailing, the types will be described as C structs directly.
|
||||||
#define OBJ_IDX_CHECKPOINT 3
|
#define OBJ_IDX_CHECKPOINT 3
|
||||||
#define OBJ_IDX_SPRING_Y 4
|
#define OBJ_IDX_SPRING_Y 4
|
||||||
#define OBJ_IDX_SPRING_R 5
|
#define OBJ_IDX_SPRING_R 5
|
||||||
#define OBJ_IDX_SPRING_D_Y 6
|
#define OBJ_IDX_SPRING_Y_D 6
|
||||||
#define OBJ_IDX_SPRING_D_R 7
|
#define OBJ_IDX_SPRING_R_D 7
|
||||||
#define OBJ_IDX_GOAL_SIGN 8
|
#define OBJ_IDX_GOAL_SIGN 8
|
||||||
#define OBJ_IDX_SWITCH 9
|
#define OBJ_IDX_SWITCH 9
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue