EBLAN_ENGINE/tests/studio_editor_checks.inl
Игорь Федосеев 7a56403d65 feat: add authored history and door interactions
Group level edits across inspector and canvas gestures, preserve legacy maps, and expose opt-in switch/key door bindings without changing wire layouts.

Refresh studio and pause UI with UE5-inspired surfaces and content drawer. Verify 162 editor and 307 ASM checks plus renderer parity.
2026-10-31 22:51:08 +03:00

106 lines
6.7 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

static void RunStudioEditorChecks() {
EditorApp app;
app.LoadLevel("../assets/map.dat");
auto original=app.CaptureSnapshot();
auto before=original;
app.RecordLevelEdit(before,71);
Check(app.undoStack.empty(),"selection-only gesture creates no undo entry");
app.playerStartX=7;
app.RecordLevelEdit(before,71);
before=app.CaptureSnapshot(); app.playerStartX=8; app.RecordLevelEdit(before,71);
Check(app.undoStack.size()==1,"continuous inspector gesture creates exactly one snapshot");
app.RecordLevelEdit(app.CaptureSnapshot(),0);
before=app.CaptureSnapshot(); app.floorColor=0; app.RecordLevelEdit(before,72);
Check(app.undoStack.size()==2,"next property edit gets an independent snapshot");
app.Undo(); Check(app.playerStartX==8 && app.floorColor==original.floorColor,"undo palette preserves preceding transform");
app.Undo(); Check(EditorApp::SameLevel(app.CaptureSnapshot(),original),"undo continuous transform restores initial state");
app.Redo(); app.Redo(); Check(app.playerStartX==8 && app.floorColor==0,"redo restores both authored edits");
app.Undo(); before=app.CaptureSnapshot(); app.ceilingColor=123; app.RecordLevelEdit(before,0);
Check(app.redoStack.empty(),"new edit after undo invalidates redo branch");
before=app.CaptureSnapshot();
app.sprites[0].x+=10; app.sprites[0].scale=131072; app.sprites[0].textureId=13;
SetSpriteEntityData(app.sprites[0],ENTITY_ARCHETYPE_GUARD,55,3,8);
app.transitions[0].targetSpawnX=9; app.transitions[0].targetAngle=128;
strcpy_s(app.transitions[0].targetMap,"other.dat");
app.cutscene.flags=CUTSCENE_AUTOPLAY;
app.RecordLevelEdit(before,0);
app.draggingLightRadiusIdx=2; app.strokeSnapshotTaken=true; app.Undo();
Check(EditorApp::SameLevel(app.CaptureSnapshot(),before),"snapshot restores sprite parameters portal names and cutscene flags together");
Check(app.draggingLightRadiusIdx==-1 && !app.strokeSnapshotTaken,"undo cancels light-radius drag and paint stroke");
for(int i=0;i<80;++i) {before=app.CaptureSnapshot(); app.floorColor=i+1; app.RecordLevelEdit(before,0);}
Check(app.undoStack.size()==64,"history remains bounded to sixty-four snapshots");
BinaryLevel level; std::string error;
ReadLevelFile("../assets/map.dat",level,error);
level.cells[10*32+10]=CELL_DOOR;
SetSwitchDoor(level.sprites[0],10,10,true);
Check(ValidateLevel(level,error) && HasSwitchDoor(level.sprites[0]),"switch binding uses existing v5 sprite bytes");
level.cells[10*32+10]=CELL_EMPTY;
Check(!ValidateLevel(level,error),"switch binding cannot target a non-door cell");
level.cells[10*32+10]=CELL_DOOR;
level.sprites.push_back(level.sprites[0]);
Check(!ValidateLevel(level,error),"duplicate door bindings are rejected");
level.sprites.pop_back(); level.sprites[0].flags|=0x20000000;
Check(!ValidateLevel(level,error),"out-of-bounds switch target is rejected");
// Drive actual ImGui inspector controls, not a duplicate model of UI edits.
ImGui::CreateContext();
auto& io=ImGui::GetIO(); io.IniFilename=nullptr; io.DisplaySize=ImVec2(1400,1000); io.DeltaTime=1.0f/60;
unsigned char* pixels; int w,h; io.Fonts->GetTexDataAsRGBA32(&pixels,&w,&h);
app.LoadLevel("../assets/map.dat"); app.selectedSpriteIdx=-1; app.selectedTransitionIdx=-1;
auto frame=[&](const char* target, const char* text) {
if(text) io.AddInputCharactersUTF8(text);
ImGui::NewFrame();
ImGui::SetNextWindowPos(ImVec2(0,0)); ImGui::SetNextWindowSize(ImVec2(600,950));
ImGui::Begin("InspectorTest",nullptr,ImGuiWindowFlags_NoSavedSettings);
if(target) {
auto& g=*ImGui::GetCurrentContext();
g.NavActivateId=g.NavActivateDownId=ImGui::GetID(target); g.NavActivateFlags=ImGuiActivateFlags_PreferInput;
}
app.RenderInspector();
ImGui::End(); ImGui::Render();
};
frame(nullptr,nullptr);
frame("Spawn X",nullptr);
frame(nullptr,"7");
io.AddKeyEvent(ImGuiKey_Enter,true); frame(nullptr,nullptr);
io.AddKeyEvent(ImGuiKey_Enter,false); frame(nullptr,nullptr);
Check(app.playerStartX==7 && app.undoStack.size()==1,"real inspector spawn input records original authored state");
app.Undo(); Check(app.playerStartX==2,"real inspector spawn input can be undone");
app.Redo(); Check(app.playerStartX==7,"real inspector spawn input can be redone");
app.selectedSpriteIdx=0;
frame(nullptr,nullptr);
auto oldX=app.sprites[0].x; auto count=app.undoStack.size();
frame("Позиция X",nullptr); frame(nullptr,"6");
io.AddKeyEvent(ImGuiKey_Enter,true); frame(nullptr,nullptr);
io.AddKeyEvent(ImGuiKey_Enter,false); frame(nullptr,nullptr);
Check(app.sprites[0].x==384 && app.undoStack.size()==count+1,"real sprite inspector transform participates in history");
app.Undo(); Check(app.sprites[0].x==oldX,"real sprite inspector undo restores raw world coordinates");
app.selectedSpriteIdx=-1; app.selectedTransitionIdx=0;
auto oldSpawn=app.transitions[0].targetSpawnX; count=app.undoStack.size();
frame(nullptr,nullptr); frame("Целевой Spawn X",nullptr); frame(nullptr,"11");
io.AddKeyEvent(ImGuiKey_Enter,true); frame(nullptr,nullptr);
io.AddKeyEvent(ImGuiKey_Enter,false); frame(nullptr,nullptr);
Check(app.transitions[0].targetSpawnX==11 && app.undoStack.size()==count+1,"real portal destination input records a transaction");
app.Undo(); Check(app.transitions[0].targetSpawnX==oldSpawn,"real portal destination undo restores original spawn");
app.selectedTransitionIdx=-1;
auto oldPalette=app.CaptureSnapshot(); count=app.undoStack.size();
frame(nullptr,nullptr); frame("Хоррор",nullptr); frame(nullptr,nullptr);
Check(app.ceilingColor==0x003B0710 && app.undoStack.size()==count+1,"real environment preset records both colors once");
app.Undo(); Check(app.ceilingColor==oldPalette.ceilingColor && app.floorColor==oldPalette.floorColor,
"real environment preset undo restores ceiling and floor");
app.viewportMode=2; app.showCutsceneTimeline=false;
auto uiBefore=app.CaptureSnapshot(); count=app.undoStack.size();
for (float width : {1000.0f,1360.0f,1920.0f}) {
io.DisplaySize=ImVec2(width,840);
app.contentDrawerOpen=true;
ImGui::NewFrame(); app.RenderUI(); ImGui::Render();
ImGui::NewFrame(); app.RenderUI(); ImGui::Render();
}
Check(ImGui::GetDrawData()->TotalVtxCount>0 && app.editDepth==0,
"full workspace and content drawer render at supported window widths");
Check(EditorApp::SameLevel(uiBefore,app.CaptureSnapshot()) && app.undoStack.size()==count,
"workspace layout and content browsing do not dirty authored history");
ImGui::DestroyContext();
}