Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 37 additions & 3 deletions src/game_interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3882,9 +3882,43 @@ bool Game_Interpreter::CommandBreakLoop(lcf::rpg::EventCommand const& /* com */)

// BreakLoop will jump to the end of the event if there is no loop.

bool has_bug = !Player::IsPatchManiac();
if (!has_bug) {
SkipToNextConditional({ Cmd::EndLoop }, list[index].indent - 1);
if (Player::IsPatchManiac()) {
// Maniac Patch: find the actual innermost enclosing loop by scanning backward.
// This correctly handles BreakLoop inside conditionals nested within a loop —
// e.g. BreakLoop at indent=2 inside an if (indent=1) inside an infinite loop
// (indent=0) must break the outer loop, not some unrelated inner loop that
// happens to appear later in the same loop body.
int break_indent = list[index].indent;
int target_indent = -1;

// endloop_depth[M] counts how many EndLoops at indent=M we have seen going
// backward; a matching Loop cancels one out rather than being the enclosing loop.
constexpr int kMaxIndent = 20;
int endloop_depth[kMaxIndent] = {};

for (int i = index - 1; i >= 0; --i) {
const auto& c = list[i];
if (c.indent >= break_indent) continue;
auto cc = static_cast<Cmd>(c.code);
if (cc == Cmd::EndLoop) {
if (c.indent < kMaxIndent) endloop_depth[c.indent]++;
} else if (cc == Cmd::Loop) {
int d = (c.indent < kMaxIndent) ? endloop_depth[c.indent] : 0;
if (d > 0) {
endloop_depth[c.indent]--;
} else {
target_indent = c.indent;
break;
}
}
}

if (target_indent < 0) {
index = static_cast<int>(list.size());
return true;
}

SkipToNextConditional({ Cmd::EndLoop }, target_indent);
++index;
return true;
}
Expand Down
18 changes: 18 additions & 0 deletions src/game_interpreter_shared.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,24 @@ inline bool Game_Interpreter_Shared::DecodeTargetEvaluationMode(lcf::rpg::EventC
return true;
}
break;
case 5: // Maniac Patch ScopedSingle: params[1] is positive self-var index → negative ID
if constexpr (validate_patches) {
if (!Player::IsPatchManiac()) {
return false;
}
}
id_0 = -com.parameters[1];
id_1 = id_0;
break;
case 6: // Maniac Patch ScopedRange
if constexpr (validate_patches) {
if (!Player::IsPatchManiac()) {
return false;
}
}
id_0 = -com.parameters[1];
id_1 = -com.parameters[2];
break;
default:
id_0 = 0;
id_1 = 0;
Expand Down
8 changes: 8 additions & 0 deletions src/game_message.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,13 @@ Game_Message::ParseParamResult Game_Message::ParseParam(
++iter;
bool stop_parsing = false;
bool got_valid_number = false;
bool is_negative = false;

// Maniac Patch: support negative variable IDs like \v[-1]
if (iter != end && *iter == '-') {
is_negative = true;
++iter;
}

while (iter != end && *iter != ']') {
if (stop_parsing) {
Expand Down Expand Up @@ -283,6 +290,7 @@ Game_Message::ParseParamResult Game_Message::ParseParam(
++iter;
}

if (is_negative) { value = -value; }
values.emplace_back(value);

// Actor 0 references the first party member
Expand Down
7 changes: 7 additions & 0 deletions src/game_variables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ void Game_Variables::WarnGet(int variable_id) const {

template <typename F>
Game_Variables::Var_t Game_Variables::SetOp(int variable_id, Var_t value, F&& op, const char* warn) {
// Maniac Patch self-variables: -1..-kSelfVarCount map to local storage
if (variable_id < 0 && variable_id >= -kSelfVarCount) {
auto& v = _self_vars[(-variable_id) - 1];
value = op(v, value);
v = Utils::Clamp(value, _min, _max);
return v;
}
if (EP_UNLIKELY(ShouldWarn(variable_id, variable_id))) {
Output::Debug(warn, variable_id, value);
--_warnings;
Expand Down
11 changes: 11 additions & 0 deletions src/game_variables.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ class Game_Variables {
void WriteArray(const int first_id_a, const int last_id_a, const int first_id_b, F&& op);

Variables_t _variables;
// Maniac Patch self-variables: negative IDs (-1..-N) map to self_vars[0..N-1]
static constexpr int kSelfVarCount = 64;
Var_t _self_vars[kSelfVarCount] = {};
Var_t _min = 0;
Var_t _max = 0;
size_t lower_limit = 0;
Expand Down Expand Up @@ -189,10 +192,18 @@ inline bool Game_Variables::IsValid(int variable_id) const {
}

inline bool Game_Variables::ShouldWarn(int first_id, int last_id) const {
// Self-variables (negative IDs in range) are valid, don't warn for them
if (first_id < 0 && first_id >= -kSelfVarCount && last_id < 0 && last_id >= -kSelfVarCount) {
return false;
}
return (first_id <= 0 || last_id > GetSizeWithLimit()) && _warnings > 0;
}

inline Game_Variables::Var_t Game_Variables::Get(int variable_id) const {
// Maniac Patch self-variables: -1..-kSelfVarCount map to local storage
if (variable_id < 0 && variable_id >= -kSelfVarCount) {
return _self_vars[(-variable_id) - 1];
}
if (EP_UNLIKELY(ShouldWarn(variable_id, variable_id))) {
WarnGet(variable_id);
}
Expand Down