bunny_shifter/bunny_shifter.py

266 lines
7.3 KiB
Python

# WARNING: running this file will directly modify .lua and .b3d files
# without creating backups. Running it again will _accumulate_ shift
# in b3d files (if there's any change in lua part).
# where the origin should be put between top and bottom of the box
RATIO = 2/3
WRITE_LUA = True
import os.path
import re
import copy
# return new corners
def fix_box_coords(p1, p2):
assert(p1[1] < p2[1])
p1 = copy.copy(p1)
p2 = copy.copy(p2)
height = p2[1] - p1[1]
ratio = RATIO
#shift = p1 - ()
shift = -height * ratio - p1[1]
shift = round_to_base(shift, prec=2, base=0.05)
p1[1] += shift
p2[1] += shift
# assert(p2[1] - p1[1] == height) # lol, floats
p1 = list(map(make_float_pretty, p1))
p2 = list(map(make_float_pretty, p2))
# print("diff:", height - (p2[1] - p1[1]))
return p1, p2
def vector_sub(a,b):
return [
a[0]-b[0],
a[1]-b[1],
a[2]-b[2],
]
def round_to_base(x, prec=2, base=0.05):
return round(base * round(x/base), prec)
def make_float_pretty(n):
n = float(f'{n:.4f}')
return n
def replace_func(p1, p2):
def replace(match):
if match.group("var") == b"p1":
r = f"p1 = {{x = {p1[0]}, y = {p1[1]}, z = {p1[2]}}}"
print(r)
return match.group("spaces") + r.encode('utf-8')
elif match.group("var") == b"p2":
r = f"p2 = {{x = {p2[0]}, y = {p2[1]}, z = {p2[2]}}}"
print(r)
return match.group("spaces") + r.encode('utf-8')
else:
raise Exception("this is wrong")
return replace
POINT_PAT = rb"""
(?P<spaces>\s+)(?P<var>p[12])\s*=\s*\{
\s* x \s* = \s* (?P<x>[+-]?(\d+(\.\d*)?|\.\d+))\s*,
\s* y \s* = \s* (?P<y>[+-]?(\d+(\.\d*)?|\.\d+))\s*,
\s* z \s* = \s* (?P<z>[+-]?(\d+(\.\d*)?|\.\d+))\s*
\}
"""
MESH_PAT = rb"""
\s+mesh\s*=\s*
(?P<quote>['"])(?P<name>[^'"]+)(?P=quote)
"""
def find_and_change_collisionbox(file):
print(f"******* {file} ********")
with open(file, 'rb+') as f:
p1 = [0,0,0]
p2 = [0,0,0]
text = f.read()
# first pass
iter = re.finditer(POINT_PAT, text, re.VERBOSE)
match = next(iter)
print(match.groupdict())
assert(match.group("var") == b"p1")
p1[0] = float(match.group("x"))
p1[1] = float(match.group("y"))
p1[2] = float(match.group("z"))
match = next(iter)
print(match.groupdict())
assert(match.group("var") == b"p2")
p2[0] = float(match.group("x"))
p2[1] = float(match.group("y"))
p2[2] = float(match.group("z"))
try:
# make sure there are no more matches
match = next(iter)
assert(False) # could be selectionbox!
except StopIteration:
pass
# do shifting calc
print(p1, p2)
new_p1, new_p2 = fix_box_coords(p1, p2)
print(new_p1, new_p2)
shift = vector_sub(new_p1, p1)
print(shift)
# second pass
fixed_text = re.sub(POINT_PAT, replace_func(new_p1, new_p2), text, flags=re.VERBOSE)
# print(fixed_text)
if WRITE_LUA:
f.seek(0)
f.write(fixed_text)
f.truncate()
mesh_name = re.search(MESH_PAT, text, flags=re.VERBOSE)
if mesh_name:
mesh_name = mesh_name.group("name").decode('utf-8')
else:
mesh_name = os.path.basename(file)
m = re.match("(?P<name>[a-z]+)_mobkit.lua", mesh_name)
mesh_name = "petz_" + m.group("name") + ".b3d"
#raise mesh_name
assert(mesh_name)
print(mesh_name)
shift_b3d(os.path.join("petz", "models", mesh_name), shift)
##############################################################
import struct
def shift_b3d(filename, shift):
with open(filename, "rb+") as f:
data = f.read()
if not data.startswith(b"BB3D"):
raise Exception("Not a b3d file?")
offset = data.find(b"NODE")
print("%x" % offset)
offset += 8 # skip NODE and 4 bytes after (id? len?)
name_offset = offset
offset = data.find(b'\0', name_offset)
print("node name:", data[name_offset:offset])
offset += 1 # terminating \0
tx, = struct.unpack_from("f", data, offset)
ty, = struct.unpack_from("f", data, offset+4)
tz, = struct.unpack_from("f", data, offset+8)
print(tx, ty, tz)
out = bytearray(data)
struct.pack_into("f", out, offset, tx + shift[0])
struct.pack_into("f", out, offset+4, ty + shift[1])
struct.pack_into("f", out, offset+8, tz + shift[2])
new_y = struct.unpack_from("f", out, offset+4)
print(f"{new_y}")
f.seek(0)
f.write(out)
f.truncate()
##############################################################
def check_if_petz_dir():
def ensure_file(*names):
fname = os.path.join(*names)
assert(os.path.isfile(fname))
def ensure_dir(*names):
fname = os.path.join(*names)
assert(os.path.isdir(fname))
ensure_dir("petz")
ensure_dir("petz", "petz")
ensure_dir("petz", "models")
ensure_file("petz", "petz.conf")
print("* Looks like petz dir")
if __name__ == "__main__":
check_if_petz_dir()
# TODO: do this for passed args only
# filepath = os.path.join("petz", "petz", "grizzly_mobkit.lua")
# filepath = os.path.join("petz", "petz", "bunny_mobkit.lua")
# filepath = os.path.join("petz", "petz", "foxy_mobkit.lua")
# import sys
# filepath = sys.arv[1]
# files = os.listdir(os.path.join("petz","petz"))
# for f in files:
# print(f'"{f}",')
broken = [
# "ant_mobkit.lua", # does not work
"bat_mobkit.lua",
"beaver_mobkit.lua",
"bee_mobkit.lua",
"bunny_mobkit.lua",
"butterfly_mobkit.lua",
"calf_mobkit.lua",
"camel_mobkit.lua",
# "chicken_mobkit.lua",
# "chimp_mobkit.lua",
"clownfish_mobkit.lua",
"dolphin_mobkit.lua",
"ducky_mobkit.lua",
"elephant_mobkit.lua",
"flamingo_mobkit.lua",
"foxy_mobkit.lua",
"frog_mobkit.lua",
"gecko_mobkit.lua",
"goat_mobkit.lua",
"grizzly_mobkit.lua",
"hamster_mobkit.lua",
"kitty_mobkit.lua",
"lamb_mobkit.lua",
"leopard_mobkit.lua",
"lion_mobkit.lua",
"moth_mobkit.lua",
"mr_pumpkin_mobkit.lua",
"panda_mobkit.lua",
"parrot_mobkit.lua",
"penguin_mobkit.lua",
"pigeon_mobkit.lua",
"piggy_mobkit.lua",
"polar_bear_mobkit.lua",
"pony_mobkit.lua",
"puppy_mobkit.lua",
"rat_mobkit.lua",
"santa_killer_mobkit.lua",
"silkworm_mobkit.lua",
"squirrel_mobkit.lua",
"tarantula_mobkit.lua",
"toucan_mobkit.lua",
"tropicalfish_mobkit.lua",
"turtle_mobkit.lua",
"wolf_mobkit.lua",
]
failed = []
if True:
for name in broken:
filepath = os.path.join("petz", "petz", name)
try:
find_and_change_collisionbox(filepath)
except Exception as e:
failed.append(name)
else:
name = "puppy_mobkit.lua"
filepath = os.path.join("petz", "petz", name)
find_and_change_collisionbox(filepath)
print(failed)