Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Operations for ControlVariables / "ControlVariablesEx" #3346

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
58 changes: 48 additions & 10 deletions src/game_interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,12 @@
return CommandManiacCallCommand(com);
case Cmd::Maniac_GetGameInfo:
return CommandManiacGetGameInfo(com);
//case static_cast<Cmd>(2020): // EasyRpg_ControlVariablesEx (Not yet implemented)
// return CommandEasyRpgConditionalBranchEx(com);
//case static_cast<Cmd>(2021): // EasyRpg_ControlSwitchesEx (Not yet implemented)
// return CommandEasyRpgControlSwitchesEx(com);
case static_cast<Cmd>(2022): // EasyRpg_ControlVariablesEx

Check warning on line 793 in src/game_interpreter.cpp

View workflow job for this annotation

GitHub Actions / ubuntu:22.04

case value '2022' not in enumerated type 'Game_Interpreter::Cmd' {aka 'lcf::rpg::EventCommand::Code'} [-Wswitch]

Check warning on line 793 in src/game_interpreter.cpp

View workflow job for this annotation

GitHub Actions / debian:12

case value '2022' not in enumerated type 'Game_Interpreter::Cmd' {aka 'lcf::rpg::EventCommand::Code'} [-Wswitch]
return CommandEasyRpgControlVariablesEx(com);
case Cmd::EasyRpg_SetInterpreterFlag:
return CommandEasyRpgSetInterpreterFlag(com);
case Cmd::EasyRpg_ProcessJson:
Expand Down Expand Up @@ -1059,13 +1065,20 @@
return true;
}

template<bool EasyRpgEx>
bool Game_Interpreter::CommandControlVariables(lcf::rpg::EventCommand const& com) { // code 10220
int value = 0;
int operand = com.parameters[4];

if (EP_UNLIKELY(operand >= 9 && !Player::IsPatchManiac())) {
Output::Warning("ControlVariables: Unsupported operand {}", operand);
return true;
if constexpr (EasyRpgEx) {
if (EP_UNLIKELY(operand >= 200) && !ControlVariables::EasyRpgExCommand(com, value, *this)) {
return true;
}
} else {
if (EP_UNLIKELY(operand >= 9 && !Player::IsPatchManiac())) {
Output::Warning("ControlVariables: Unsupported operand {}", operand);
return true;
}
}

switch (operand) {
Expand Down Expand Up @@ -1235,18 +1248,36 @@
value = ManiacPatch::ParseExpression(MakeSpan(com.parameters).subspan(6, com.parameters[5]), *this);
break;
default:
if constexpr (EasyRpgEx) {
if (operand >= 200) {
break;
}
}
Output::Warning("ControlVariables: Unsupported operand {}", operand);
return true;
}

int start, end;
bool target_eval_result = DecodeTargetEvaluationMode<
/* validate_patches */ true,
/* support_range_indirect */ true,
/* support_expressions */ true,
/* support_bitmask */ false,
/* support_scopes */ false
>(com, start, end);
bool target_eval_result;

if constexpr (EasyRpgEx) {
target_eval_result = DecodeTargetEvaluationMode <
/* validate_patches */ false,
/* support_range_indirect */ true,
/* support_expressions */ true,
/* support_bitmask */ true,
/* support_scopes */ true, // (Not yet implemented)
/* support_named */ true // (Not yet implemented)
>(com, start, end);
} else {
target_eval_result = DecodeTargetEvaluationMode <
/* validate_patches */ true,
/* support_range_indirect */ true,
/* support_expressions */ true,
/* support_bitmask */ false,
/* support_scopes */ false
>(com, start, end);
}
if (!target_eval_result) {
Output::Warning("ControlVariables: Unsupported target evaluation mode {}", com.parameters[0]);
return true;
Expand Down Expand Up @@ -5325,6 +5356,13 @@
return true;
}

bool Game_Interpreter::CommandEasyRpgControlVariablesEx(lcf::rpg::EventCommand const& com) { // 2022
if (!Player::HasEasyRpgExtensions()) {
return true;
}
return CommandControlVariables<true>(com);
}

bool Game_Interpreter::CommandEasyRpgSetInterpreterFlag(lcf::rpg::EventCommand const& com) {
if (!Player::HasEasyRpgExtensions()) {
return true;
Expand Down
2 changes: 2 additions & 0 deletions src/game_interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ class Game_Interpreter : public Game_BaseInterpreterContext
bool CommandShowChoiceEnd(lcf::rpg::EventCommand const& com);
bool CommandInputNumber(lcf::rpg::EventCommand const& com);
bool CommandControlSwitches(lcf::rpg::EventCommand const& com);
template<bool EasyRpgEx = false>
bool CommandControlVariables(lcf::rpg::EventCommand const& com);
bool CommandTimerOperation(lcf::rpg::EventCommand const& com);
bool CommandChangeGold(lcf::rpg::EventCommand const& com);
Expand Down Expand Up @@ -297,6 +298,7 @@ class Game_Interpreter : public Game_BaseInterpreterContext
bool CommandManiacSetGameOption(lcf::rpg::EventCommand const& com);
bool CommandManiacControlStrings(lcf::rpg::EventCommand const& com);
bool CommandManiacCallCommand(lcf::rpg::EventCommand const& com);
bool CommandEasyRpgControlVariablesEx(lcf::rpg::EventCommand const& com);
bool CommandEasyRpgSetInterpreterFlag(lcf::rpg::EventCommand const& com);
bool CommandEasyRpgProcessJson(lcf::rpg::EventCommand const& com);
bool CommandEasyRpgCloneMapEvent(lcf::rpg::EventCommand const& com);
Expand Down
246 changes: 246 additions & 0 deletions src/game_interpreter_control_variables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@
#include "game_enemyparty.h"
#include "game_ineluki.h"
#include "game_interpreter.h"
#include "game_map.h"
#include "game_message.h"
#include "game_party.h"
#include "game_player.h"
#include "game_system.h"
#include "main_data.h"
#include "window_message.h"
#include "output.h"
#include "player.h"
#include "rand.h"
Expand Down Expand Up @@ -487,3 +490,246 @@ int ControlVariables::Divmul(int arg1, int arg2, int arg3) {
int ControlVariables::Between(int arg1, int arg2, int arg3) {
return (arg1 >= arg2 && arg2 <= arg3) ? 0 : 1;
}

//
// New EasyRpgEx operations
//

bool ControlVariables::EasyRpgExCommand(lcf::rpg::EventCommand const& com, int& value, const Game_BaseInterpreterContext& interpreter) {
using namespace Game_Interpreter_Shared;

int operand = com.parameters[4];

switch (operand) {
case 210: // eVarOperand_EasyRpg_DateTime
//
// Possible method for a string-based lookup (inspired by Jetrotal's initial suggestion):
/*
ControlVariables::DateTimeOp op;
if (!com.string.empty() && kDateTimeOpTags.etag(com.string.c_str(), op)) {
value = DateTime(op);
}
*/
value = DateTime(static_cast<DateTimeOp>(com.parameters[5]));
break;
case 211: // eVarOperand_EasyRpg_ActiveMapInfo
value = ActiveMapInfo(static_cast<ActiveMapInfoOp>(com.parameters[5]));
break;
case 212: // eVarOperand_EasyRpg_InspectMapTreeInfo
{
int map_id = 0, arg1 = 0;
if (com.parameters.size() >= 9) {
map_id = ValueOrVariableBitfield(com.parameters[8], 0, com.parameters[6], interpreter);
arg1 = ValueOrVariableBitfield(com.parameters[8], 1, com.parameters[7], interpreter);
} else if (com.parameters.size() == 8) {
arg1 = com.parameters[7];
}
if (map_id == 0) {
map_id = Game_Map::GetMapId();
}
value = InspectMapTreeInfo(static_cast<InspectMapTreeInfoOp>(com.parameters[5]), map_id, arg1);
break;
}
case 213: //eVarOperand_EasyRpg_MessageSystemState
value = MessageSystemState(static_cast<MessageSystemStateOp>(com.parameters[5]));
break;
case 214: //eVarOperand_EasyRpg_MessageWindowState
value = MessageWindowState(static_cast<MessageWindowStateOp>(com.parameters[5]));
break;
default:
Output::Warning("ControlVariables: Unsupported operand {}", com.parameters[5]);
return false;
}

return true;
}

int ControlVariables::DateTime(DateTimeOp op) {
std::time_t t = std::time(nullptr);
std::tm* tm = std::localtime(&t);

switch (op)
{
case DateTimeOp::Year:
return tm->tm_year + 1900;
case DateTimeOp::Month:
return tm->tm_mon + 1;
case DateTimeOp::Day:
return tm->tm_mday;
case DateTimeOp::Hour:
return tm->tm_hour;
case DateTimeOp::Minute:
return tm->tm_min;
case DateTimeOp::Second:
return tm->tm_sec;
case DateTimeOp::WeekDay:
return tm->tm_wday + 1;
case DateTimeOp::DayOfYear:
return tm->tm_yday + 1;
case DateTimeOp::IsDayLightSavings:
return tm->tm_isdst;
case DateTimeOp::TimeStamp:
return t;
}

Output::Warning("ControlVariables::GetTime: Unknown op {}", static_cast<int>(op));
return 0;
}

int ControlVariables::ActiveMapInfo(ActiveMapInfoOp op) {
switch (op)
{
case ActiveMapInfoOp::MapTileWidth:
return Game_Map::GetTilesX();
case ActiveMapInfoOp::MapTileHeight:
return Game_Map::GetTilesY();
case ActiveMapInfoOp::LoopHorizontal:
return Game_Map::LoopHorizontal();
case ActiveMapInfoOp::LoopVertical:
return Game_Map::LoopVertical();
}

Output::Warning("ControlVariables::ActiveMapInfo: Unknown op {}", static_cast<int>(op));
return 0;
}

int ControlVariables::InspectMapTreeInfo(InspectMapTreeInfoOp op, int map_id, int arg1) {
auto& map_info = Game_Map::GetMapInfo(map_id);
if (map_info.ID == 0) {
return 0;
}

switch (op)
{
case InspectMapTreeInfoOp::ParentMap:
return map_info.parent_map;
case InspectMapTreeInfoOp::OriginalEncounterSteps:
return map_info.encounter_steps;
case InspectMapTreeInfoOp::CountTroops:
return map_info.encounters.size();;
case InspectMapTreeInfoOp::CountArenas:
{
int cnt_arenas = 0;
for (unsigned int i = 0; i < lcf::Data::treemap.maps.size(); ++i) {
auto& map = lcf::Data::treemap.maps[i];
if (map.parent_map == map_info.ID && map.type == lcf::rpg::TreeMap::MapType_area) {
cnt_arenas++;
}
}
return cnt_arenas;
}
case InspectMapTreeInfoOp::Troop_Id:
{
// TODO: provide a way to conveniently copy values into a range of variables ("ControlVarArrayEx"?)
if (arg1 >= 1 && arg1 <= static_cast<int>(map_info.encounters.size())) {
return map_info.encounters[arg1 - 1].troop_id;
}
return 0;
}
case InspectMapTreeInfoOp::Arena_Top:
case InspectMapTreeInfoOp::Arena_Left:
case InspectMapTreeInfoOp::Arena_Bottom:
case InspectMapTreeInfoOp::Arena_Right:
case InspectMapTreeInfoOp::Arena_Width:
case InspectMapTreeInfoOp::Arena_Height:
{
// TODO: provide a way to conveniently copy values into a range of variables ("ControlVarArrayEx"?)
if (arg1 <= 0) {
return 0;
}

int cnt_arenas = 0;
for (unsigned int i = 0; i < lcf::Data::treemap.maps.size(); ++i) {
auto& map = lcf::Data::treemap.maps[i];
if (map.parent_map == map_info.ID && map.type == lcf::rpg::TreeMap::MapType_area) {
if (arg1 <= cnt_arenas) {
cnt_arenas++;
continue;
}
switch (op)
{
case InspectMapTreeInfoOp::Arena_Top:
return map.area_rect.t;
case InspectMapTreeInfoOp::Arena_Left:
return map.area_rect.l;
case InspectMapTreeInfoOp::Arena_Bottom:
return map.area_rect.b;
case InspectMapTreeInfoOp::Arena_Right:
return map.area_rect.r;
case InspectMapTreeInfoOp::Arena_Width:
return map.area_rect.r - map.area_rect.l;
case InspectMapTreeInfoOp::Arena_Height:
return map.area_rect.b - map.area_rect.t;
default:
break;
}
}
}
return 0;
}
}

Output::Warning("ControlVariables::InspectMapTreeInfo: Unknown op {}", static_cast<int>(op));
return 0;
}

int ControlVariables::MessageSystemState(MessageSystemStateOp op) {
switch (op)
{
case MessageSystemStateOp::IsMessageTransparent:
return Main_Data::game_system->IsMessageTransparent();
case MessageSystemStateOp::IsMessagePositionFixed:
return Main_Data::game_system->IsMessagePositionFixed();
case MessageSystemStateOp::IsContinueEvents:
return Main_Data::game_system->GetMessageContinueEvents();
case MessageSystemStateOp::MessagePosition:
return Main_Data::game_system->GetMessagePosition();
case MessageSystemStateOp::IsMessageFaceRightPosition:
return Main_Data::game_system->IsMessageFaceRightPosition();
}

Output::Warning("ControlVariables::MessageSystemState: Unknown op {}", static_cast<int>(op));
return 0;
}

int ControlVariables::MessageWindowState(MessageWindowStateOp op) {
if (op == MessageWindowStateOp::IsMessageActive) {
return Game_Message::IsMessageActive();
}

auto* window = Game_Message::GetWindow();
if (window) {
switch (op)
{
case MessageWindowStateOp::IsFaceActive:
return window->GetPendingMessage().IsFaceEnabled() && !Main_Data::game_system->GetMessageFaceName().empty();
case MessageWindowStateOp::CanContinue:
return !window->GetPause();
case MessageWindowStateOp::WindowTop:
return window->GetY();
case MessageWindowStateOp::WindowLeft:
return window->GetX();
case MessageWindowStateOp::WindowBottom:
return window->GetY() + window->GetHeight();
case MessageWindowStateOp::WindowRight:
return window->GetX() + window->GetWidth();
case MessageWindowStateOp::WindowWidth:
return window->GetWidth();
case MessageWindowStateOp::WindowHeight:
return window->GetHeight();
case MessageWindowStateOp::WindowType:
if (window->GetPendingMessage().HasChoices())
return 1;
if (window->GetPendingMessage().HasNumberInput())
return 2;
if (window->GetPendingMessage().ShowGoldWindow())
return 3;
return 0;
case MessageWindowStateOp::IsMessageActive:
break;
}
}

Output::Warning("ControlVariables::MessageWindowState: Unknown op {}", static_cast<int>(op));
return 0;
}
Loading
Loading