Skip to content

Commit

Permalink
Refactor Interpreter Stack Frame Completion
Browse files Browse the repository at this point in the history
* Rename CommandEnd() to OnFinishStackFrame()

This name and it's nearby comment was very misleading. The
interpreter has actually always ignored CommandEnd commands.
This function was only called when the index ran past end of the stack
frame.

* Do OnFinishStackFrame() before execution loop.

Call this in one place and handle multiple stack pops. Also,
don't hang on empty events.

* Optimize parallel events

Reset the index and never pop the base stack frame. RPG_RT
does this and we avoid copying parallel event data every frame.
  • Loading branch information
mateofio committed Sep 3, 2019
1 parent 86a10d4 commit 723cd9f
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 22 deletions.
43 changes: 28 additions & 15 deletions src/game_interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@ void Game_Interpreter::Update(bool reset_loop_count) {
loop_count = 0;
}

if (!IsRunning()) {
return;
}

for (; loop_count < loop_limit; ++loop_count) {
// If something is calling a menu, we're allowed to execute only 1 command per interpreter. So we pass through if loop_count == 0, and stop at 1 or greater.
// RPG_RT compatible behavior.
Expand Down Expand Up @@ -326,7 +330,7 @@ void Game_Interpreter::Update(bool reset_loop_count) {

auto* frame = GetFrame();
if (frame == nullptr) {
return;
break;
}

if (continuation) {
Expand All @@ -353,11 +357,18 @@ void Game_Interpreter::Update(bool reset_loop_count) {
// Previous operations could have modified the stack.
// So we need to fetch the frame again.
frame = GetFrame();

if (!frame || frame->commands.empty()) {
if (frame == nullptr) {
break;
}

// Pop any completed stack frames
if (frame->current_command >= (int)frame->commands.size()) {
if (!OnFinishStackFrame()) {
break;
}
continue;
}

// Save the frame index before we call events.
int current_frame_idx = _state.stack.size() - 1;

Expand Down Expand Up @@ -682,26 +693,20 @@ bool Game_Interpreter::ExecuteCommand() {
}
}

bool Game_Interpreter::CommandEnd() { // code 10
bool Game_Interpreter::OnFinishStackFrame() {
auto* frame = GetFrame();
assert(frame);
auto& list = frame->commands;

// Is this the first event and not a called one?
const bool is_original_event = _state.stack.size() == 1;
const bool is_base_frame = _state.stack.size() == 1;

if (main_flag && is_original_event) {
if (main_flag && is_base_frame) {
Game_Message::SetFaceName("");
}

// FIXME: Hangs in some cases when Autostart events start
//if (main_flag) {
// Game_Message::FullClear();
//}

int event_id = frame->event_id;

if (is_original_event && event_id > 0) {
if (is_base_frame && event_id > 0) {
Game_Event* evnt = Game_Map::GetEvent(event_id);
if (!evnt) {
Output::Error("Call stack finished with invalid event id %d. This can be caused by a vehicle teleport?", event_id);
Expand All @@ -710,9 +715,17 @@ bool Game_Interpreter::CommandEnd() { // code 10
}
}

_state.stack.pop_back();
if (!main_flag && is_base_frame) {
// Parallel events will never clear the base stack frame. Instead we just
// reset the index back to 0 and wait for a frame.
// This not only optimizes away copying event code, it's also RPG_RT compatible.
frame->current_command = 0;
} else {
// If a called frame, or base frame of foreground interpreter, pop the stack.
_state.stack.pop_back();
}

return true;
return !is_base_frame;
}

std::vector<std::string> Game_Interpreter::GetChoices() {
Expand Down
6 changes: 5 additions & 1 deletion src/game_interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ class Game_Interpreter
static std::vector<Game_Actor*> GetActors(int mode, int id);
static int ValueOrVariable(int mode, int val);

/**
* When current frame finishes executing we pop the stack
*/
bool OnFinishStackFrame();

/**
* Triggers a game over when all party members are dead.
*/
Expand Down Expand Up @@ -240,7 +245,6 @@ class Game_Interpreter
bool CommandChangeBattleCommands(RPG::EventCommand const& com);
bool CommandExitGame(RPG::EventCommand const& com);
bool CommandToggleFullscreen(RPG::EventCommand const& com);
bool CommandEnd();

virtual bool DefaultContinuation(RPG::EventCommand const& com);
virtual bool ContinuationChoices(RPG::EventCommand const& com);
Expand Down
4 changes: 1 addition & 3 deletions src/game_interpreter_battle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ bool Game_Interpreter_Battle::ExecuteCommand() {
const auto& list = frame->commands;
auto& index = frame->current_command;

if (index >= list.size()) {
return CommandEnd();
}
assert(index < (int)list.size());

RPG::EventCommand const& com = list[index];

Expand Down
4 changes: 1 addition & 3 deletions src/game_interpreter_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,7 @@ bool Game_Interpreter_Map::ExecuteCommand() {
const auto& list = frame->commands;
auto& index = frame->current_command;

if (index >= list.size()) {
return CommandEnd();
}
assert(index < (int)list.size());

RPG::EventCommand const& com = list[index];

Expand Down

0 comments on commit 723cd9f

Please sign in to comment.