EBLAN_ENGINE/tools/make_cutscene_demo.py
Artvasil09 a1d1baa869 feat: complete engine and editor subsystems (Stages 1-5 from TODO_ARTEM)
- Stage 1: Eliminate fish-eye distortion with precomputed tangential ray offset table and fractional DDA depth
- Stage 2: Full asset management in EblanStudio (stb_image/stb_image_write, 128x128 BGRA texture packaging, WAV sound manager)
- Stage 3: Multi-level room streaming, LevelTransition/CELL_PORTAL trigger system, 16-frame screen fade to black
- Stage 4: 60Hz cutscene engine in pure x86 ASM (v4 binary track, waypoint camera motion, shortest-arc pan, CP1251 Russian subtitles, letterbox overlay, audio)
- Stage 5: EblanStudio cutscene timeline (F6) and windowless Live ASM Viewport over shared-memory IPC directly rendered into DX11
- Tooling & Tests: comprehensive regression test suite (94 editor + 107 engine tests), IPC tests, and cutscene demo
2026-10-22 16:24:22 +03:00

46 lines
2.4 KiB
Python

#!/usr/bin/env python3
"""Build the separate cutscene example; never modifies the user's assets/ maps."""
from pathlib import Path
import math
import struct
import wave
ROOT = Path(__file__).resolve().parents[1] / "examples/cutscene_demo"
def write_map(name, *, spawn, angle, events=(), strings=(), transitions=(), autoplay=False):
grid = bytearray(1024)
for x in range(32): grid[x] = grid[31*32+x] = 1
for y in range(32): grid[y*32] = grid[y*32+31] = 1
for y in range(1, 18): grid[y*32+16] = 3
for x, y in ((6,6),(10,5),(12,12),(4,10)): grid[y*32+x] = 7
for t in transitions: grid[t[1]*32+t[0]] = 10
sprites = [(7*64+32,5*64+32,11,65536,0,0),(11*64+32,7*64+32,10,65536,0,0)]
header = struct.pack("<I7HII4H4s",0x4E4C4245,4,32,32,*spawn,angle,len(sprites),0x303848,0x282018,
len(transitions),len(events),len(strings),int(autoplay),bytes(4))
payload = header+grid+b"".join(struct.pack("<6i",*s) for s in sprites)
payload += b"".join(struct.pack("<BB32sHHHBB",*t) for t in transitions)
payload += b"".join(struct.pack("<HBBiii",*e) for e in events)
payload += b"".join(s.encode("cp1251").ljust(96,b"\0") for s in strings)
(ROOT / "assets" / name).write_bytes(payload)
def main():
(ROOT / "assets/sounds").mkdir(parents=True,exist_ok=True)
# END, WAIT, MOVE, PAN, TEXT, HIDE, SOUND, FADE, LEVEL
events = [(0,7,0,16,0,0),(0,7,0,0,60,0),(0,6,0,1,0,0),(0,2,0,544,224,180),
(20,4,0,0,140,0),(180,3,0,128,90,0),(180,2,0,544,544,180),
(240,4,0,2,170,0),(360,3,0,0,90,0),(360,2,0,800,544,180),
(450,5,0,0,0,0),(540,7,0,16,60,0),(600,8,0,0,0,0),(601,0,0,0,0,0)]
write_map("map.dat",spawn=(3,3),angle=0,events=events,
strings=("Кат-сцена: камера, текст и звук", "chime.wav", "Tab — пропустить. После пролёта — другая комната."),
transitions=((15,8,b"second_room.dat",3,3,0,1,0),),autoplay=True)
write_map("second_room.dat",spawn=(3,3),angle=0,
transitions=((2,3,b"map.dat",3,3,0,1,0),))
with wave.open(str(ROOT / "assets/sounds/chime.wav"),"wb") as sound:
sound.setnchannels(1);sound.setsampwidth(2);sound.setframerate(16000)
sound.writeframes(b"".join(struct.pack("<h",int(4000*math.sin(2*math.pi*660*i/16000)*(1-i/8000))) for i in range(8000)))
print(ROOT)
if __name__ == "__main__": main()