mirror of
https://github.com/bombsquad-community/plugin-manager.git
synced 2025-10-08 14:54:36 +00:00
1391 lines
36 KiB
Python
Executable file
1391 lines
36 KiB
Python
Executable file
# Copyright 2025 - Solely by BrotherBoard
|
|
# Intended for personal use only
|
|
# Bug? Feedback? Telegram >> @BroBordd
|
|
|
|
"""
|
|
Replay v2.5 - Simple replay player
|
|
|
|
Experimental. Feedback is appreciated.
|
|
Adds a button to pause menu and watch menu.
|
|
|
|
Features:
|
|
- Common features (pause/play/seek/speed/replay)
|
|
- Press on progress bar to seek anywhere
|
|
- Advanced free camera target control
|
|
- Ability to zoom in/out to target
|
|
- Uses pybrp to display how long a replay is
|
|
- Good UI with detailed toast pop ups
|
|
- Ability to show/hide UI
|
|
- Uses threading everywhere
|
|
"""
|
|
|
|
from babase import Plugin
|
|
from bauiv1 import (
|
|
get_virtual_screen_size as res,
|
|
get_special_widget as gsw,
|
|
clipboard_set_text as COPY,
|
|
get_replays_dir as rdir,
|
|
containerwidget as ocw,
|
|
screenmessage as push,
|
|
spinnerwidget as spin,
|
|
buttonwidget as obw,
|
|
fade_screen as fade,
|
|
scrollwidget as sw,
|
|
SpecialChar as sc,
|
|
imagewidget as iw,
|
|
textwidget as otw,
|
|
gettexture as gt,
|
|
apptimer as teck,
|
|
AppTimer as tock,
|
|
getsound as gs,
|
|
UIScale as UIS,
|
|
charstr as cs,
|
|
Call,
|
|
app
|
|
)
|
|
from bascenev1 import (
|
|
set_replay_speed_exponent as SET,
|
|
get_replay_speed_exponent as GET,
|
|
new_replay_session as PLAY,
|
|
resume_replay as RESUME,
|
|
pause_replay as PAUSE,
|
|
seek_replay as SEEK,
|
|
is_in_replay as ON
|
|
)
|
|
from _babase import (
|
|
set_camera_position as SCP,
|
|
get_camera_position as GCP,
|
|
set_camera_manual as SCM,
|
|
set_camera_target as SCT,
|
|
get_camera_target as GCT
|
|
)
|
|
from os.path import join, dirname, getsize, basename
|
|
from time import time, strftime, gmtime
|
|
from random import uniform as uf
|
|
from threading import Thread
|
|
from os import listdir as ls
|
|
from struct import unpack
|
|
|
|
|
|
class Replay:
|
|
VER = '2.5'
|
|
COL1 = (0.18, 0.18, 0.18)
|
|
COL2 = (1, 1, 1)
|
|
COL3 = (0, 1, 0)
|
|
COL4 = (0, 1, 1)
|
|
BUSY = False
|
|
|
|
@classmethod
|
|
def BUS(c, b=None):
|
|
if b is None:
|
|
return c.BUSY
|
|
c.BUSY = b
|
|
|
|
def __init__(s, source=None):
|
|
s.sl = s.rn = s.buf = None
|
|
s.ohno = False
|
|
s._h = _H()
|
|
s.p = s.cw(
|
|
src=source.get_screen_space_center(),
|
|
p=GOS(),
|
|
size=(400, 500),
|
|
oac=lambda: (ocw(s.p, transition='out_scale' if source and source.exists()
|
|
else 'out_left'), s.snd('laser'), s.trs.stop())
|
|
)
|
|
s.trs = s.snd('powerup01')
|
|
s.tw(
|
|
p=s.p,
|
|
h_align='center',
|
|
text='Replay',
|
|
pos=(175, 460),
|
|
scale=2
|
|
)
|
|
sy = 360
|
|
p1 = sw(
|
|
parent=s.p,
|
|
size=(sy, sy),
|
|
position=(25, 80)
|
|
)
|
|
s.rd = rdir()
|
|
a = [_ for _ in ls(s.rd) if _.endswith('.brp')]
|
|
v = 30*len(a)
|
|
p2 = ocw(
|
|
parent=p1,
|
|
background=False,
|
|
size=(sy, v)
|
|
)
|
|
s.kids = []
|
|
for i, _ in enumerate(a):
|
|
t = s.tw(
|
|
p=p2,
|
|
click_activate=True,
|
|
selectable=True,
|
|
pos=(0, v-30*i-30),
|
|
text=_,
|
|
maxwidth=sy,
|
|
size=(sy, 30),
|
|
color=s.COL2,
|
|
oac=Call(s.hl, i, _)
|
|
)
|
|
s.kids.append(t)
|
|
s.psrc = None
|
|
for _ in range(3):
|
|
b = s.bw(
|
|
p=s.p,
|
|
pos=(25+120*_, 30),
|
|
size=(120, 40),
|
|
label=['Show', 'Copy', 'Run'][_],
|
|
oac=Call(s.con, [s.show, s.copy, s.play][_]),
|
|
icon=gt(['folder', 'file', 'nextLevelIcon'][_])
|
|
)
|
|
if _ == 2:
|
|
s.psrc = b
|
|
|
|
def snd(s, t):
|
|
h = gs(t)
|
|
h.play()
|
|
teck(uf(0.14, 0.17), h.stop)
|
|
return h
|
|
|
|
def get(s):
|
|
return join(s.rd, s.rn)
|
|
|
|
def copy(s):
|
|
s.snd('dingSmallHigh')
|
|
COPY(s.get())
|
|
push('Copied replay path to clipboard!', color=s.COL3)
|
|
|
|
def show(s):
|
|
gs('ding').play()
|
|
push(s.get(), color=s.COL3)
|
|
|
|
def con(s, f):
|
|
if s.sl is None:
|
|
BTW('Select a replay!')
|
|
return
|
|
if ON():
|
|
BTW('A replay is already running!')
|
|
return
|
|
return f()
|
|
|
|
def hl(s, i, n):
|
|
if s.sl == i:
|
|
s.psrc = s.kids[i]
|
|
s.play()
|
|
return
|
|
s.sl = i
|
|
s.rn = n
|
|
[otw(_, color=s.COL2) for _ in s.kids]
|
|
otw(s.kids[i], color=s.COL3)
|
|
|
|
def play(s):
|
|
if s.BUS():
|
|
return
|
|
s.BUS(True)
|
|
gs('deek').play()
|
|
s.load()
|
|
|
|
def load(s):
|
|
src = s.psrc.get_screen_space_center()
|
|
if s.psrc.get_widget_type() == 'text':
|
|
src = (src[0]-170, src[1])
|
|
s.parc = c = s.cw(
|
|
src=src,
|
|
size=(300, 200),
|
|
p=GOS()
|
|
)
|
|
s.tw(
|
|
p=c,
|
|
text='Player',
|
|
pos=(125, 150),
|
|
h_align='center',
|
|
scale=1.4
|
|
)
|
|
spin(
|
|
parent=c,
|
|
size=60,
|
|
position=(75, 100)
|
|
)
|
|
s.st = s.tw(
|
|
p=c,
|
|
text='Reading...',
|
|
pos=(115, 87)
|
|
)
|
|
s.tpar = s.tw(
|
|
p=c,
|
|
pos=(125, 30),
|
|
maxwidth=240,
|
|
text=f'{s.rn} with total of {getsize(s.get())} bytes\nstreaming bytes to pybrp_stream',
|
|
h_align='center'
|
|
)
|
|
s.tpar2 = s.tw(
|
|
p=c,
|
|
maxwidth=240,
|
|
pos=(30, 20),
|
|
v_align='bottom'
|
|
)
|
|
s.par = [0, 1]
|
|
teck(0.5, Thread(target=s.calc).start)
|
|
teck(0.5, s.fpar)
|
|
s.spy(s.calc2)
|
|
|
|
def fpar(s):
|
|
a, b = s.par
|
|
teck(0.1, s.fpar) if (a != b) and (not s.ohno) else 0
|
|
if not a:
|
|
return
|
|
p = a/b*100
|
|
t = '\u2588'*int(p)+'\u2591'*int(100-p)
|
|
if not s.ohno:
|
|
try:
|
|
otw(s.tpar, text=t)
|
|
otw(s.tpar2, text=f'{a} of {b} bytes read')
|
|
except:
|
|
return
|
|
|
|
def calc(s):
|
|
try:
|
|
s.buf = GMS(s._h, s.get(), s.par)
|
|
except:
|
|
s.buf = 0
|
|
|
|
def calc2(s, t):
|
|
otw(s.st, text='Starting...' if t else 'Wait what?')
|
|
otw(s.tpar2, text=f'result was {t} milleseconds') if t else t
|
|
if not t:
|
|
s.ohno = True
|
|
otw(s.tpar, text='pybrp returned zero duration, error?\nclosing this window in 5 seconds')
|
|
otw(s.tpar2, text='')
|
|
teck(1 if t else 5, Call(s._play, t))
|
|
|
|
def spy(s, f, i=60):
|
|
if not i:
|
|
s.buf = None
|
|
f(None)
|
|
return
|
|
if s.buf is not None:
|
|
b = s.buf
|
|
s.buf = None
|
|
f(b)
|
|
return
|
|
teck(0.5, Call(s.spy, f, i-1))
|
|
|
|
def _play(s, t):
|
|
if t == 0:
|
|
BTW("Couldn't load replay!")
|
|
ocw(s.parc, transition='out_scale')
|
|
s.BUS(False)
|
|
return
|
|
SET(0)
|
|
fade(1)
|
|
Player(path=s.get(), duration=t)
|
|
s.BUS(False)
|
|
bw = lambda s, p=None, oac=None, pos=None, **k: obw(
|
|
parent=p,
|
|
color=s.COL1,
|
|
textcolor=s.COL2,
|
|
on_activate_call=oac,
|
|
position=pos,
|
|
button_type='square',
|
|
enable_sound=False,
|
|
**k
|
|
)
|
|
cw = lambda s, p=None, pos=None, src=None, oac=None, **k: ocw(
|
|
color=s.COL1,
|
|
parent=p,
|
|
position=pos,
|
|
scale_origin_stack_offset=src,
|
|
transition='in_scale',
|
|
on_outside_click_call=oac,
|
|
**k
|
|
)
|
|
tw = lambda s, color=None, oac=None, p=None, pos=None, **k: otw(
|
|
parent=p,
|
|
position=pos,
|
|
color=color or s.COL2,
|
|
on_activate_call=oac,
|
|
**k
|
|
)
|
|
|
|
|
|
class Player:
|
|
TICK = 0.01
|
|
COL0 = (0.5, 0, 0)
|
|
COL1 = (1, 0, 0)
|
|
COL2 = (0.5, 0.5, 0)
|
|
COL3 = (1, 1, 0)
|
|
COL4 = (0, 0.5, 0)
|
|
COL5 = (0, 1, 0)
|
|
COL6 = (0, 0.5, 0.5)
|
|
COL7 = (0, 1, 1)
|
|
COL8 = (0.6, 0.6, 0.6)
|
|
COL9 = (8, 0, 0)
|
|
COL10 = (0.5, 0.25, 0)
|
|
COL11 = (1, 0.5, 0)
|
|
COL12 = (0.5, 0.25, 0.5)
|
|
COL13 = (1, 0.5, 1)
|
|
COL14 = (0.5, 0.5, 0.5)
|
|
COL15 = (1, 1, 1)
|
|
COL16 = (0.1, 0.2, 0.4)
|
|
COL17 = (1, 1.7, 2)
|
|
|
|
def __init__(s, path, duration):
|
|
s.path = path
|
|
s.du = duration
|
|
s.ds = s.du / 1000
|
|
s.ps = s.nah = s.camon = s.snma = s.gay = False
|
|
s.caml = None
|
|
s.rn = s.st = s.pr = 0
|
|
s.camz = 1
|
|
[setattr(s, _, []) for _ in ['kids', 'camkids', 'hdkids', 'snkids', 'snuikids']]
|
|
PLAY(path)
|
|
x, y = res()
|
|
s.sy = 80
|
|
s.p = ocw(
|
|
size=(x, s.sy),
|
|
stack_offset=(0, -y/2+s.sy/2),
|
|
background=False
|
|
)
|
|
s.bg = iw(
|
|
parent=s.p,
|
|
texture=gt('black'),
|
|
size=(x+3, s.sy+5),
|
|
position=(0, -2),
|
|
opacity=0.4
|
|
)
|
|
s.mkui()
|
|
s.mkhd()
|
|
# finally
|
|
s.sp = 1
|
|
s.foc()
|
|
s.play()
|
|
|
|
def mkhd(s):
|
|
f = s.hdkids.append
|
|
s.tex = ['\u25bc', '\u25b2']
|
|
s.kekb = s.bw(
|
|
p=s.p,
|
|
pos=(20, 15),
|
|
size=(50, 50),
|
|
oac=s.kek,
|
|
color=s.COL10
|
|
)
|
|
f(s.kekb)
|
|
s.kekt = otw(
|
|
parent=s.p,
|
|
text=s.tex[s.nah],
|
|
position=(44, 30),
|
|
scale=2,
|
|
shadow=0.4,
|
|
color=s.COL11
|
|
)
|
|
f(s.kekt)
|
|
f(iw(
|
|
parent=s.p,
|
|
position=(18, 13),
|
|
size=(54, 54),
|
|
color=s.COL10,
|
|
texture=gt('white'),
|
|
opacity=0.4
|
|
))
|
|
|
|
def killhd(s):
|
|
[_.delete() for _ in s.hdkids]
|
|
s.hdkids.clear()
|
|
|
|
def mkui(s):
|
|
s.up = True
|
|
f = s.kids.append
|
|
x, y = res()
|
|
sy = s.sy
|
|
p = s.p
|
|
# exit
|
|
f(s.bw(
|
|
p=p,
|
|
pos=(x-65, 15),
|
|
size=(50, 50),
|
|
color=s.COL0,
|
|
oac=s.bye
|
|
))
|
|
c = s.COL1
|
|
f(iw(
|
|
parent=p,
|
|
texture=gt('crossOut'),
|
|
color=(c[0]*10, c[1]*10, c[2]*10),
|
|
position=(x-60, 20),
|
|
size=(40, 40)
|
|
))
|
|
# speed
|
|
for _ in range(2):
|
|
a = [
|
|
'FAST_FORWARD_BUTTON',
|
|
'REWIND_BUTTON'
|
|
][_]
|
|
pos = (x-130-260*_, 15)
|
|
f(s.bw(
|
|
p=p,
|
|
pos=pos,
|
|
size=(50, 50),
|
|
color=s.COL2,
|
|
oac=Call(s.boost, [1, -1][_]),
|
|
repeat=True
|
|
))
|
|
f(otw(
|
|
parent=p,
|
|
text=cs(getattr(sc, a)),
|
|
color=s.COL3,
|
|
position=(pos[0]-2, pos[1]+13),
|
|
h_align='center',
|
|
v_align='center',
|
|
scale=1.8,
|
|
shadow=0.3
|
|
))
|
|
# seek
|
|
for _ in range(2):
|
|
a = [
|
|
'RIGHT_ARROW',
|
|
'LEFT_ARROW'
|
|
][_]
|
|
pos = (x-195-130*_, 15)
|
|
f(s.bw(
|
|
p=p,
|
|
pos=pos,
|
|
size=(50, 50),
|
|
color=s.COL4,
|
|
oac=Call(s.seek, [1, -1][_]),
|
|
repeat=True
|
|
))
|
|
f(otw(
|
|
parent=p,
|
|
text=cs(getattr(sc, a)),
|
|
color=s.COL5,
|
|
position=(pos[0]-1, pos[1]+12),
|
|
h_align='center',
|
|
v_align='center',
|
|
scale=1.7,
|
|
shadow=0.2
|
|
))
|
|
# pause
|
|
pos = (x-260, 15)
|
|
f(s.bw(
|
|
p=p,
|
|
pos=pos,
|
|
size=(50, 50),
|
|
color=s.COL6,
|
|
oac=s.toggle
|
|
))
|
|
s.tt = otw(
|
|
parent=p,
|
|
color=s.COL7,
|
|
position=(pos[0]+12, pos[1]+11),
|
|
scale=1.5,
|
|
shadow=0.3
|
|
)
|
|
f(s.tt)
|
|
s.toggle(dry=True)
|
|
# replay
|
|
pos = (x-455, 15)
|
|
f(s.bw(
|
|
p=p,
|
|
pos=pos,
|
|
size=(50, 50),
|
|
color=s.COL12,
|
|
oac=s.rloop
|
|
))
|
|
c = s.COL13
|
|
sk = 1.5
|
|
f(iw(
|
|
parent=p,
|
|
texture=gt('replayIcon'),
|
|
color=(c[0]*sk, c[1]*sk, c[2]*sk),
|
|
position=(pos[0]+2, pos[1]+1),
|
|
size=(47, 47),
|
|
))
|
|
# progress
|
|
pos = (285, sy/2-2)
|
|
s.px = x-790
|
|
f(iw(
|
|
parent=p,
|
|
texture=gt('white'),
|
|
size=(s.px, 5),
|
|
position=pos,
|
|
opacity=0.4,
|
|
color=s.COL8
|
|
))
|
|
s.nbp = (pos[0]-24, pos[1]-22)
|
|
s.nb = iw(
|
|
parent=p,
|
|
texture=gt('nub'),
|
|
size=(50, 50),
|
|
position=s.nbp,
|
|
opacity=0.4,
|
|
color=s.COL9
|
|
)
|
|
f(s.nb)
|
|
# timestamp
|
|
s.ct = otw(
|
|
parent=p,
|
|
position=(155, 40),
|
|
color=s.COL7,
|
|
text=FOR(s.rn-s.st)
|
|
)
|
|
f(s.ct)
|
|
f(otw(
|
|
parent=p,
|
|
position=(155, 11),
|
|
text=FOR(s.ds),
|
|
color=s.COL6
|
|
))
|
|
# sensor
|
|
sx, sy = (285, 15)
|
|
n = 100
|
|
tp = s.px/n
|
|
for _ in range(n):
|
|
f(obw(
|
|
label='',
|
|
parent=p,
|
|
position=(sx+tp*_, sy),
|
|
size=(tp, 50),
|
|
texture=gt('empty'),
|
|
enable_sound=False,
|
|
on_activate_call=Call(s.jump, _/n),
|
|
selectable=False
|
|
))
|
|
# camera
|
|
f(s.bw(
|
|
p=s.p,
|
|
pos=(85, 15),
|
|
size=(50, 50),
|
|
color=s.COL14,
|
|
oac=s.cam
|
|
))
|
|
c = s.COL15
|
|
sk = 1.5
|
|
f(iw(
|
|
parent=s.p,
|
|
texture=gt('achievementOutline'),
|
|
position=(88, 18),
|
|
color=(c[0]*sk, c[1]*sk, c[2]*sk),
|
|
size=(45, 45)
|
|
))
|
|
# info
|
|
ix, iy = (443, 98)
|
|
s.ok = iw(
|
|
texture=gt('white'),
|
|
position=(x-456, 100),
|
|
parent=p,
|
|
size=(ix, iy),
|
|
opacity=0
|
|
)
|
|
f(s.ok)
|
|
s.ok2 = otw(
|
|
position=(x-ix+182.5, iy+64),
|
|
h_align='center',
|
|
scale=1.2,
|
|
parent=p,
|
|
maxwidth=ix-20
|
|
)
|
|
f(s.ok2)
|
|
s.ok3 = otw(
|
|
position=(x-ix+182.5, iy+10),
|
|
h_align='center',
|
|
parent=p,
|
|
maxwidth=ix-20
|
|
)
|
|
f(s.ok3)
|
|
|
|
def mkcamui(s):
|
|
f = s.camkids.append
|
|
x, y = (19, 100)
|
|
s.cambg = iw(
|
|
parent=s.p,
|
|
size=(235, 260),
|
|
position=(x, y),
|
|
texture=gt('white'),
|
|
color=s.COL14,
|
|
opacity=0.05
|
|
)
|
|
f(s.cambg)
|
|
# tip
|
|
s.cambg2 = iw(
|
|
parent=s.p,
|
|
size=(235, 100),
|
|
opacity=0.05,
|
|
position=(x, y+267),
|
|
color=s.COL14,
|
|
texture=gt('white')
|
|
)
|
|
f(s.cambg2)
|
|
s.fcam()
|
|
f(otw(
|
|
parent=s.p,
|
|
h_align='center',
|
|
v_align='center',
|
|
text='To maintain animated\nsmooth camera, keep\nzoom at auto.',
|
|
maxwidth=205,
|
|
max_height=190,
|
|
position=(x+92, y+300),
|
|
color=s.COL15
|
|
))
|
|
# reset
|
|
f(s.bw(
|
|
p=s.p,
|
|
label='Reset',
|
|
color=s.COL14,
|
|
textcolor=s.COL15,
|
|
size=(205, 30),
|
|
pos=(x+15, y+7),
|
|
oac=s.camr
|
|
))
|
|
# seperator
|
|
f(iw(
|
|
parent=s.p,
|
|
size=(219, 2),
|
|
position=(x+8, y+44),
|
|
texture=gt('white'),
|
|
color=s.COL15,
|
|
opacity=0.6
|
|
))
|
|
# look
|
|
f(s.bw(
|
|
p=s.p,
|
|
label='Look',
|
|
pos=(x+15, y+53),
|
|
color=s.COL14,
|
|
textcolor=s.COL15,
|
|
size=(205, 30),
|
|
oac=s.look
|
|
))
|
|
s.ltw = otw(
|
|
parent=s.p,
|
|
text=str(RND(s.caml) if s.caml else 'players'),
|
|
v_align='center',
|
|
h_align='center',
|
|
position=(x+92, y+90),
|
|
color=s.COL14,
|
|
maxwidth=205,
|
|
max_height=40
|
|
)
|
|
f(s.ltw)
|
|
f(otw(
|
|
parent=s.p,
|
|
text='Currently looking at:',
|
|
v_align='center',
|
|
h_align='center',
|
|
position=(x+92, y+120),
|
|
color=s.COL15,
|
|
maxwidth=205,
|
|
max_height=40
|
|
))
|
|
# seperator
|
|
f(iw(
|
|
parent=s.p,
|
|
size=(219, 2),
|
|
position=(x+8, y+154),
|
|
texture=gt('white'),
|
|
color=s.COL15,
|
|
opacity=0.6
|
|
))
|
|
# zoom
|
|
[f(s.bw(
|
|
p=s.p,
|
|
label=['-', '+'][_],
|
|
pos=(x+13+113*_, y+163),
|
|
color=s.COL14,
|
|
textcolor=s.COL15,
|
|
size=(98, 30),
|
|
repeat=True,
|
|
oac=Call(s.zoom, [1, -1][_])
|
|
)) for _ in [0, 1]]
|
|
s.ztw = otw(
|
|
parent=s.p,
|
|
text=f'x{round(0.5**(s.camz-1), 2)}' if s.camz != 1 else 'x1.0' if s.gay else 'auto',
|
|
v_align='center',
|
|
h_align='center',
|
|
position=(x+92, y+200),
|
|
color=s.COL14,
|
|
maxwidth=205,
|
|
max_height=40
|
|
)
|
|
f(s.ztw)
|
|
f(otw(
|
|
parent=s.p,
|
|
text='Current zoom:',
|
|
v_align='center',
|
|
h_align='center',
|
|
position=(x+92, y+227),
|
|
color=s.COL15,
|
|
maxwidth=205,
|
|
max_height=40
|
|
))
|
|
|
|
def zoom(s, i):
|
|
n = round(s.camz+i*0.05, 2)
|
|
if s.camz == 1 and not s.gay:
|
|
SCM(True)
|
|
s.camp = GCP()
|
|
s.caml = GCT()
|
|
otw(s.ltw, text=str(RND(s.caml)))
|
|
if n == 1 and not s.gay:
|
|
SCM(False)
|
|
s.caml = None
|
|
otw(s.ltw, text='players')
|
|
s.camz = n
|
|
otw(s.ztw, text=f'x{round(0.5**(n-1), 2)}' if n != 1 else 'x1.0' if s.gay else 'auto')
|
|
s.zom()
|
|
|
|
def look(s):
|
|
s.killui()
|
|
s.killhd()
|
|
s.fkek(0.4, -0.1)
|
|
s.camlo = s.caml
|
|
s.campo = GCP()
|
|
s.gayo = s.gay
|
|
s.mksns()
|
|
s.mksnui()
|
|
s.mksnb()
|
|
s.mksni()
|
|
|
|
def _look(s, x, y):
|
|
o = s.caml or GCT()
|
|
sk = 0.7*s.camz
|
|
s.caml = n = (o[0]+x*sk, o[1]+y*sk, o[2])
|
|
0 if s.snma else otw(s.snt, text=str(RND(n)))
|
|
s.camp = GCP()
|
|
if s.camz != 1:
|
|
s.gay = True
|
|
s.camz = 1
|
|
|
|
def foc(s):
|
|
s.tfoc = tock(0.01, s.focus, repeat=True)
|
|
|
|
def focus(s):
|
|
SCT(*s.caml) if s.caml else 0
|
|
|
|
def zom(s):
|
|
if s.camz == 1 and not s.gay:
|
|
return
|
|
z = s.camz
|
|
tx, ty, tz = GCT()
|
|
px, py, pz = s.camp
|
|
npx = tx+(px-tx)*z
|
|
npy = ty+(py-ty)*z
|
|
npz = tz+(pz-tz)*z
|
|
SCP(npx, npy, npz)
|
|
|
|
def fockill(s):
|
|
s.tfoc = None
|
|
|
|
def snsave(s):
|
|
s.snbye()
|
|
|
|
def snbye(s):
|
|
s.killsn()
|
|
s.mkui()
|
|
s.mkhd()
|
|
s.fkek(0, 0.1)
|
|
s.pro()
|
|
|
|
def mksns(s):
|
|
x, y = res()
|
|
sz = 50
|
|
a = int(x/sz)
|
|
b = int(y/sz)
|
|
ha = a/2
|
|
hb = b/2
|
|
[s.snkids.append(obw(
|
|
parent=s.p,
|
|
size=(sz, sz),
|
|
position=(i*sz, j*sz),
|
|
texture=gt('empty'),
|
|
enable_sound=False,
|
|
on_activate_call=Call(s._look, i-ha, j-hb),
|
|
label='',
|
|
repeat=True
|
|
))
|
|
for i in range(a)
|
|
for j in range(b)]
|
|
|
|
def mksnui(s):
|
|
f = s.snuikids.append
|
|
f(iw(
|
|
parent=s.p,
|
|
position=(0, 3),
|
|
color=s.COL14,
|
|
opacity=0.4,
|
|
texture=gt('white'),
|
|
size=(232, 190)
|
|
))
|
|
# buttons
|
|
f(s.bw(
|
|
p=s.p,
|
|
pos=(14, 50),
|
|
size=(204, 30),
|
|
label='Target Players',
|
|
color=s.COL14,
|
|
textcolor=s.COL15,
|
|
oac=s.sntar
|
|
))
|
|
f(s.bw(
|
|
p=s.p,
|
|
pos=(10, 90),
|
|
color=s.COL14,
|
|
label='Cancel',
|
|
size=(99, 30),
|
|
textcolor=s.COL15,
|
|
oac=s.sncancel
|
|
))
|
|
f(s.bw(
|
|
p=s.p,
|
|
pos=(123, 90),
|
|
color=s.COL14,
|
|
label='Save',
|
|
size=(99, 30),
|
|
textcolor=s.COL15,
|
|
oac=s.snsave
|
|
))
|
|
# info
|
|
f(otw(
|
|
parent=s.p,
|
|
position=(90, 160),
|
|
color=s.COL15,
|
|
text='Currently looking at:',
|
|
h_align='center',
|
|
maxwidth=220
|
|
))
|
|
s.snt = otw(
|
|
parent=s.p,
|
|
position=(90, 130),
|
|
color=s.COL14,
|
|
h_align='center',
|
|
text=str(RND(s.caml) if s.caml else 'players')
|
|
)
|
|
f(s.snt)
|
|
# tip
|
|
f(iw(
|
|
parent=s.p,
|
|
position=(0, 200),
|
|
color=s.COL14,
|
|
opacity=0.4,
|
|
texture=gt('white'),
|
|
size=(232, 110)
|
|
))
|
|
f(otw(
|
|
parent=s.p,
|
|
position=(90, 240),
|
|
text='Longpress anywhere\nto look around. Tap on \nsomething to look at it.\nPause for calmer control!',
|
|
h_align='center',
|
|
v_align='center',
|
|
maxwidth=225,
|
|
max_height=105
|
|
))
|
|
# crosshair
|
|
x, y = res()
|
|
h = 20
|
|
[f(iw(
|
|
parent=s.p,
|
|
position=(x/2, y/2-h/2+h*0.1) if _ else (x/2-h/2, y/2+h*0.1),
|
|
size=(3, h*1.15) if _ else (h*1.15, 3),
|
|
color=s.COL1,
|
|
texture=gt('white')
|
|
)) for _ in [0, 1]]
|
|
# top
|
|
k = 60
|
|
for j in range(2):
|
|
f(iw(
|
|
parent=s.p,
|
|
texture=gt('white'),
|
|
color=s.COL1,
|
|
position=(x/2+[-k, k-h][j], y/2+k),
|
|
size=(h*1.1, 3)
|
|
))
|
|
# right
|
|
for j in range(2):
|
|
f(iw(
|
|
parent=s.p,
|
|
texture=gt('white'),
|
|
color=s.COL1,
|
|
position=(x/2+k, y/2+[k-h, -k+h*0.3][j]),
|
|
size=(3, h*+1.1)
|
|
))
|
|
# bottom
|
|
for j in range(2):
|
|
f(iw(
|
|
parent=s.p,
|
|
texture=gt('white'),
|
|
color=s.COL1,
|
|
position=(x/2+[-k, k-h][j], y/2-h/2-k+h*0.8),
|
|
size=(h*1.1, 3)
|
|
))
|
|
# left
|
|
for j in range(2):
|
|
f(iw(
|
|
parent=s.p,
|
|
texture=gt('white'),
|
|
color=s.COL1,
|
|
position=(x/2-k, y/2+[k-h, -k+h*0.3][j]),
|
|
size=(3, h*1.1)
|
|
))
|
|
|
|
def killsnui(s):
|
|
[_.delete() for _ in s.snuikids]
|
|
|
|
def snhide(s):
|
|
if getattr(s, 'snbusy', 0):
|
|
return
|
|
s.snma = not s.snma
|
|
s.snbusy = True
|
|
if s.snma:
|
|
s.snanim(204, 14, -1)
|
|
obw(s.snbtn, label=cs(sc.UP_ARROW))
|
|
s.killsnui()
|
|
else:
|
|
obw(s.snbtn, texture=gt('white'))
|
|
s.snanim(36, 7, 1)
|
|
iw(s.sni, opacity=0)
|
|
|
|
def mksni(s):
|
|
s.sni = iw(
|
|
parent=s.p,
|
|
position=(7, 8),
|
|
color=s.COL14,
|
|
opacity=0,
|
|
size=(36, 33),
|
|
texture=gt('white')
|
|
)
|
|
s.snkids.append(s.sni)
|
|
|
|
def mksnb(s):
|
|
s.snbtn = s.bw(
|
|
p=s.p,
|
|
pos=(14, 10),
|
|
color=s.COL14,
|
|
label='Cinema Mode',
|
|
size=(204, 30),
|
|
textcolor=s.COL15,
|
|
oac=s.snhide
|
|
)
|
|
s.snkids.append(s.snbtn)
|
|
|
|
def snanim(s, a, b, i):
|
|
a += (163/35)*i
|
|
b += 0.2*i
|
|
obw(s.snbtn, size=(a, 30), position=(b, 10))
|
|
if not (14 >= b >= 7):
|
|
s.snbusy = False
|
|
if s.snma:
|
|
obw(s.snbtn, texture=gt('empty'))
|
|
iw(s.sni, opacity=0.4)
|
|
else:
|
|
s.mksnui()
|
|
s.snbtn.delete()
|
|
s.mksnb()
|
|
obw(s.snbtn, label='Cinema Mode')
|
|
return
|
|
teck(0.004, Call(s.snanim, a, b, i))
|
|
|
|
def killsn(s):
|
|
s.killsnui()
|
|
[_.delete() for _ in s.snkids]
|
|
|
|
def all(s):
|
|
return s.trash()+s.hdkids+s.snkids
|
|
|
|
def sntar(s):
|
|
s.caml = None
|
|
otw(s.snt, text='players')
|
|
if s.camz != 1 or s.gay:
|
|
s.camz = 1
|
|
s.gay = False
|
|
SCM(False)
|
|
|
|
def sncancel(s):
|
|
s.caml = s.camlo
|
|
s.camp = s.campo
|
|
s.gay = s.gayo
|
|
if s.camz != 1 or s.gay:
|
|
SCM(True)
|
|
SCP(*s.camp)
|
|
s.snbye()
|
|
|
|
def cam(s):
|
|
if s.camon:
|
|
s.camon = False
|
|
[_.delete() for _ in s.camkids]
|
|
s.camkids.clear()
|
|
s.fcam(0.4, -0.1)
|
|
else:
|
|
s.camon = True
|
|
s.mkcamui()
|
|
|
|
def camr(s):
|
|
SCM(False)
|
|
s.caml = None
|
|
s.gay = False
|
|
s.camz = 1
|
|
otw(s.ltw, text='players')
|
|
otw(s.ztw, text='auto')
|
|
|
|
def fcam(s, i=0, a=0.1):
|
|
if i > 0.4 or i < 0:
|
|
if a < 0:
|
|
s.cambg.delete()
|
|
return
|
|
if not s.cambg.exists():
|
|
return
|
|
iw(s.cambg, opacity=i)
|
|
iw(s.cambg2, opacity=i)
|
|
teck(0.02, Call(s.fcam, i+a, a))
|
|
|
|
def rloop(s):
|
|
s.loop()
|
|
s.fixps()
|
|
s.hm('Replay', f'Version {Replay.VER} BETA', s.COL12, s.COL13)
|
|
|
|
def killui(s):
|
|
s.up = s.camon = False
|
|
[_.delete() for _ in s.trash()]
|
|
s.kids.clear()
|
|
s.camkids.clear()
|
|
|
|
def trash(s):
|
|
return s.kids+s.camkids
|
|
|
|
def kek(s):
|
|
if getattr(s, 'kekbusy', 0):
|
|
return
|
|
s.kekbusy = True
|
|
if getattr(s, 'tbye', 0) and getattr(s, 'frbro', 0):
|
|
s.frbro = s.tbye = False
|
|
s.okt = None
|
|
s.nah = b = not s.nah
|
|
otw(s.kekt, text=s.tex[b])
|
|
if b:
|
|
teck(0.2, lambda: obw(s.kekb, texture=gt('empty')))
|
|
s.fkek(0.4, -0.05)
|
|
s.killui()
|
|
else:
|
|
obw(s.kekb, texture=gt('white'))
|
|
s.fkek(0, 0.05)
|
|
s.mkui()
|
|
s.pro()
|
|
teck(0.21, Call(setattr, s, 'kekbusy', 0))
|
|
|
|
def fkek(s, i=0, a=0.1):
|
|
if i > 0.4 or i < 0:
|
|
return
|
|
if not s.bg.exists():
|
|
return
|
|
iw(s.bg, opacity=i)
|
|
teck(0.02, Call(s.fkek, i+a, a))
|
|
|
|
def hm(s, t1, t2, c1, c2):
|
|
if getattr(s, 'tbye', 0) and getattr(s, 'frbro', 0):
|
|
s.frbro = s.tbye = False
|
|
s.okt = None
|
|
iw(s.ok, color=c1)
|
|
otw(s.ok2, text=t1, color=c2)
|
|
otw(s.ok3, text=t2, color=c2)
|
|
s.fok()
|
|
s.okt = tock(1.5, s.unhm)
|
|
|
|
def unhm(s):
|
|
s.fok(0.7, -0.1)
|
|
[otw(_, text='') for _ in [s.ok2, s.ok3] if _.exists()]
|
|
|
|
def fok(s, i=0, a=0.1):
|
|
if i > 0.7 or i < 0:
|
|
return
|
|
if not s.ok.exists():
|
|
return
|
|
iw(s.ok, opacity=i)
|
|
teck(0.02, Call(s.fok, i+a, a))
|
|
|
|
def toggle(s, dry=False, shut=False):
|
|
if not dry:
|
|
s.ps = not s.ps
|
|
t = cs(getattr(sc, ['PAUSE', 'PLAY'][s.ps]+'_BUTTON'))
|
|
otw(s.tt, text=t)
|
|
if not dry:
|
|
if not shut:
|
|
s.hm(['Resume', 'Pause'][s.ps], basename(s.path) +
|
|
f' of {getsize(s.path)} bytes', s.COL6, s.COL7)
|
|
if s.ps:
|
|
s.stop()
|
|
PAUSE()
|
|
else:
|
|
s.play()
|
|
RESUME()
|
|
|
|
def fixps(s):
|
|
if not s.ps:
|
|
return
|
|
s.toggle(shut=True)
|
|
teck(0.02, Call(s.toggle, shut=True))
|
|
|
|
def clock(s):
|
|
t = time()
|
|
r = t - s.rt
|
|
s.rt = t
|
|
s.rn += r * s.sp
|
|
|
|
def boost(s, i):
|
|
n = GET()+i
|
|
SET(n)
|
|
s.sp = 2**n
|
|
h = 'Snail Mode' if s.sp == 0.0625 else 'Slow Motion' if s.sp < 1 else 'Quake Pro' if s.sp == 16 else 'Fast Motion' if s.sp > 1 else 'Normal Speed'
|
|
s.hm(h, f'Current exponent: x{s.sp}', s.COL2, s.COL3)
|
|
|
|
def play(s):
|
|
s.rt = time()
|
|
s.clock()
|
|
s.ptplay()
|
|
s.clt = tock(s.TICK, s.clock, repeat=True)
|
|
|
|
def stop(s):
|
|
s.clt = None
|
|
s.ptkill()
|
|
|
|
def ptkill(s):
|
|
s.pt = None
|
|
|
|
def ptplay(s):
|
|
s.pt = tock(s.TICK, s.pro, repeat=True)
|
|
|
|
def seek(s, i):
|
|
h = ['Forward by', 'Rewind by'][i == -1]
|
|
i = i * s.sp
|
|
i = (s.ds/20)*i
|
|
t = (s.rn-s.st)+i
|
|
if (t >= s.ds) or (t <= 0):
|
|
s.loop()
|
|
else:
|
|
s.st = s.rn-t
|
|
s.replay()
|
|
SEEK(t)
|
|
s.rt = time()
|
|
s.fixps()
|
|
i = abs(round(i, 2))
|
|
s.hm('Seek', h+f" {i} second{['s', ''][i == 1]}", s.COL4, s.COL5)
|
|
|
|
def jump(s, p):
|
|
t = s.ds * p
|
|
s.st = s.rn-t
|
|
s.replay()
|
|
SEEK(t)
|
|
s.rt = time()
|
|
s.fixps()
|
|
|
|
def bye(s):
|
|
if getattr(s, 'frbro', 0):
|
|
s._bye()
|
|
return
|
|
s.hm('Exit', 'Press again to confirm', s.COL0, s.COL1)
|
|
s.frbro = True
|
|
s.tbye = tock(1.5, Call(setattr, s, 'frbro', False))
|
|
|
|
def _bye(s):
|
|
fade(0, time=0.75, endcall=Call(fade, 1, time=0.75))
|
|
gs('deek').play()
|
|
BYE()
|
|
s.stop()
|
|
s.fockill()
|
|
s.tbye = None
|
|
SCM(False)
|
|
|
|
def pro(s):
|
|
t = s.rn-s.st
|
|
if s.rn-s.st >= s.ds:
|
|
s.loop()
|
|
x, y = s.nbp
|
|
p = (t/s.ds)*s.px
|
|
try:
|
|
iw(s.nb, position=(x+p, y))
|
|
otw(s.ct, text=FOR(t))
|
|
except ReferenceError:
|
|
pass
|
|
|
|
def replay(s):
|
|
SEEK(-10**10)
|
|
|
|
def loop(s):
|
|
s.st = s.rn = 0
|
|
s.replay()
|
|
bw = lambda s, label='', p=None, oac=None, pos=None, texture='white', **k: obw(
|
|
parent=p,
|
|
on_activate_call=oac,
|
|
position=pos,
|
|
label=label,
|
|
texture=gt(texture),
|
|
enable_sound=False,
|
|
**k
|
|
)
|
|
|
|
|
|
# Tools
|
|
def BYE(): return app.classic.return_to_main_menu_session_gracefully(reset_ui=False)
|
|
|
|
|
|
def BTW(t): return (gs('block').play() or 1) and push(t, color=(1, 1, 0))
|
|
def GOS(): return gsw('overlay_stack')
|
|
|
|
|
|
def FOR(t): return strftime('%H:%M:%S', gmtime(t))
|
|
def SCL(a, b, c=None): return ((s := app.ui_v1.uiscale),
|
|
a if s is UIS.SMALL else b if s is UIS.MEDIUM else (c or b))[1]
|
|
|
|
|
|
def RND(t): return type(t)([round(_, 1) for _ in t])
|
|
|
|
# pybrp
|
|
|
|
|
|
def Z(_): return [0]*_
|
|
|
|
|
|
def G_FREQS(): return [
|
|
101342, 9667, 3497, 1072, 0, 3793, *Z(2), 2815, 5235, *Z(3), 3570, *Z(3),
|
|
1383, *Z(3), 2970, *Z(2), 2857, *Z(8), 1199, *Z(30),
|
|
1494, 1974, *Z(12), 1351, *Z(122), 1475, *Z(65)
|
|
]
|
|
|
|
|
|
def CMD_NAMES(): return {0: 'BaseTimeStep', 1: 'StepSceneGraph', 2: 'AddSceneGraph', 3: 'RemoveSceneGraph', 4: 'AddNode', 5: 'NodeOnCreate', 6: 'SetForegroundScene', 7: 'RemoveNode', 8: 'AddMaterial', 9: 'RemoveMaterial', 10: 'AddMaterialComponent', 11: 'AddTexture', 12: 'RemoveTexture', 13: 'AddMesh', 14: 'RemoveMesh', 15: 'AddSound', 16: 'RemoveSound', 17: 'AddCollisionMesh', 18: 'RemoveCollisionMesh', 19: 'ConnectNodeAttribute', 20: 'NodeMessage', 21: 'SetNodeAttrFloat', 22: 'SetNodeAttrInt32', 23: 'SetNodeAttrBool', 24: 'SetNodeAttrFloats', 25: 'SetNodeAttrInt32s', 26: 'SetNodeAttrString', 27: 'SetNodeAttrNode', 28: 'SetNodeAttrNodeNull',
|
|
29: 'SetNodeAttrNodes', 30: 'SetNodeAttrPlayer', 31: 'SetNodeAttrPlayerNull', 32: 'SetNodeAttrMaterials', 33: 'SetNodeAttrTexture', 34: 'SetNodeAttrTextureNull', 35: 'SetNodeAttrTextures', 36: 'SetNodeAttrSound', 37: 'SetNodeAttrSoundNull', 38: 'SetNodeAttrSounds', 39: 'SetNodeAttrMesh', 40: 'SetNodeAttrMeshNull', 41: 'SetNodeAttrMeshes', 42: 'SetNodeAttrCollisionMesh', 43: 'SetNodeAttrCollisionMeshNull', 44: 'SetNodeAttrCollisionMeshes', 45: 'PlaySoundAtPosition', 46: 'PlaySound', 47: 'EmitBGDynamics', 48: 'EndOfFile', 49: 'DynamicsCorrection', 50: 'ScreenMessageBottom', 51: 'ScreenMessageTop', 52: 'AddData', 53: 'RemoveData', 54: 'CameraShake'}
|
|
|
|
|
|
class _H:
|
|
class _N:
|
|
def __init__(self):
|
|
self.l, self.r, self.p, self.f = -1, -1, 0, 0
|
|
|
|
def __init__(self):
|
|
gf, self.nodes = G_FREQS(), [self._N()for _ in range(511)]
|
|
for i in range(256):
|
|
self.nodes[i].f = gf[i]
|
|
nc = 256
|
|
while nc < 511:
|
|
s1, s2 = -1, -1
|
|
i = 0
|
|
while self.nodes[i].p != 0:
|
|
i += 1
|
|
s1 = i
|
|
i += 1
|
|
while self.nodes[i].p != 0:
|
|
i += 1
|
|
s2 = i
|
|
i += 1
|
|
while i < nc:
|
|
if self.nodes[i].p == 0:
|
|
if self.nodes[s1].f > self.nodes[s2].f:
|
|
if self.nodes[i].f < self.nodes[s1].f:
|
|
s1 = i
|
|
elif self.nodes[i].f < self.nodes[s2].f:
|
|
s2 = i
|
|
i += 1
|
|
self.nodes[nc].f = self.nodes[s1].f+self.nodes[s2].f
|
|
self.nodes[s1].p = self.nodes[s2].p = nc-255
|
|
self.nodes[nc].r, self.nodes[nc].l = s1, s2
|
|
nc += 1
|
|
|
|
def decompress(self, src):
|
|
if not src:
|
|
return b''
|
|
rem, comp = src[0] & 15, src[0] >> 7
|
|
if not comp:
|
|
return src
|
|
out, ptr, l = bytearray(), src[1:], len(src)
|
|
bl = ((l-1)*8)-rem
|
|
bit = 0
|
|
while bit < bl:
|
|
m_bit = (ptr[bit >> 3] >> (bit & 7)) & 1
|
|
bit += 1
|
|
if m_bit:
|
|
n = 510
|
|
while n >= 256:
|
|
if bit >= bl:
|
|
raise ValueError("Incomplete Huffman code")
|
|
p_bit = (ptr[bit >> 3] >> (bit & 7)) & 1
|
|
bit += 1
|
|
n = self.nodes[n].l if p_bit == 0 else self.nodes[n].r
|
|
out.append(n)
|
|
else:
|
|
if bit+8 > bl:
|
|
break
|
|
bi, b_in_b = bit >> 3, bit & 7
|
|
val = ptr[bi]if b_in_b == 0 else (ptr[bi] >> b_in_b) | (ptr[bi+1] << (8-b_in_b))
|
|
out.append(val & 255)
|
|
bit += 8
|
|
return bytes(out)
|
|
|
|
|
|
def GMS(_h, brp_path, par):
|
|
total_ms = 0
|
|
with open(brp_path, 'rb') as f:
|
|
f.seek(0, 2)
|
|
par[1] = f.tell()
|
|
f.seek(6)
|
|
while True:
|
|
if par:
|
|
par[0] = f.tell()
|
|
b_data = f.read(1)
|
|
if not b_data:
|
|
break
|
|
b1, comp_len = b_data[0], 0
|
|
if b1 < 254:
|
|
comp_len = b1
|
|
elif b1 == 254:
|
|
comp_len = int.from_bytes(f.read(2), 'little')
|
|
else: # 255
|
|
comp_len = int.from_bytes(f.read(4), 'little')
|
|
if comp_len == 0:
|
|
continue
|
|
raw_msg = _h.decompress(f.read(comp_len))
|
|
if not raw_msg or raw_msg[0] != 1:
|
|
continue
|
|
sub_off = 1
|
|
while sub_off < len(raw_msg):
|
|
try:
|
|
sub_size = int.from_bytes(raw_msg[sub_off:sub_off+2], 'little')
|
|
except IndexError:
|
|
break
|
|
except ValueError:
|
|
break
|
|
sub_data = raw_msg[sub_off+2:sub_off+2+sub_size]
|
|
if sub_data and sub_data[0] == 0:
|
|
total_ms += sub_data[1]
|
|
sub_off += 2 + sub_size
|
|
if par:
|
|
par[0] = par[1]
|
|
return total_ms
|
|
|
|
# brobord collide grass
|
|
# ba_meta require api 9
|
|
# ba_meta export babase.Plugin
|
|
|
|
|
|
class byBordd(Plugin):
|
|
def __init__(s):
|
|
from bauiv1lib.ingamemenu import InGameMenuWindow as m
|
|
a = '_refresh_in_game'
|
|
o = getattr(m, a)
|
|
setattr(m, a, lambda v, *a, **k: (s.mk(v), o(v, *a, **k))[1])
|
|
from bauiv1lib.watch import WatchWindow as n
|
|
b = '__init__'
|
|
p = getattr(n, b)
|
|
setattr(n, b, lambda v, *a, **k: (p(v, *a, **k), s.mk(v, 1))[0])
|
|
|
|
def mk(s, v, i=0):
|
|
if i:
|
|
x = v._width/2+SCL(v._scroll_width*-0.5+93, 0)+100
|
|
y = v.yoffs-SCL(63, 10)-25
|
|
s.b = Replay.bw(
|
|
Replay,
|
|
p=v._root_widget,
|
|
label='Replay',
|
|
pos=(x, y) if i else (-70, 0),
|
|
icon=gt('replayIcon'),
|
|
iconscale=1.6 if i else 0.8,
|
|
size=(140, 50) if i else (90, 35),
|
|
oac=lambda: Replay(source=s.b)
|
|
)
|