Skip to content
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
6 changes: 4 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,8 @@ add_library(${PROJECT_NAME} OBJECT
src/version.h
src/weather.cpp
src/weather.h
src/window.cpp
src/window.h
src/window_about.cpp
src/window_about.h
src/window_actorinfo.cpp
Expand All @@ -423,7 +425,8 @@ add_library(${PROJECT_NAME} OBJECT
src/window_command.h
src/window_command_horizontal.cpp
src/window_command_horizontal.h
src/window.cpp
src/window_debug_picture.cpp
src/window_debug_picture.h
src/window_equip.cpp
src/window_equip.h
src/window_equipitem.cpp
Expand All @@ -436,7 +439,6 @@ add_library(${PROJECT_NAME} OBJECT
src/window_gamelist.h
src/window_gold.cpp
src/window_gold.h
src/window.h
src/window_help.cpp
src/window_help.h
src/window_import_progress.cpp
Expand Down
120 changes: 113 additions & 7 deletions src/game_interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4736,6 +4736,16 @@ bool Game_Interpreter::CommandManiacGetPictureInfo(lcf::rpg::EventCommand const&
int pic_id = ValueOrVariable(com.parameters[0], com.parameters[3]);
auto& pic = Main_Data::game_pictures->GetPicture(pic_id);

std::array<int, 4> args = {com.parameters[4], com.parameters[5], com.parameters[6], com.parameters[7]};

if (!pic.Exists()) {
for (auto arg: args) {
Main_Data::game_variables->Set(arg, 0);
Game_Map::SetNeedRefreshForVarChange(arg);
}
return true;
}

if (pic.IsRequestPending()) {
// Cannot do anything useful here without the dimensions
pic.MakeRequestImportant();
Expand Down Expand Up @@ -4785,12 +4795,14 @@ bool Game_Interpreter::CommandManiacGetPictureInfo(lcf::rpg::EventCommand const&
}
}

Main_Data::game_variables->Set(com.parameters[4], x);
Main_Data::game_variables->Set(com.parameters[5], y);
Main_Data::game_variables->Set(com.parameters[6], width);
Main_Data::game_variables->Set(com.parameters[7], height);
Main_Data::game_variables->Set(args[0], x);
Main_Data::game_variables->Set(args[1], y);
Main_Data::game_variables->Set(args[2], width);
Main_Data::game_variables->Set(args[3], height);

Game_Map::SetNeedRefresh(true);
for (auto arg: args) {
Game_Map::SetNeedRefreshForVarChange(arg);
}

return true;
}
Expand Down Expand Up @@ -5025,12 +5037,106 @@ bool Game_Interpreter::CommandManiacControlGlobalSave(lcf::rpg::EventCommand con
return true;
}

bool Game_Interpreter::CommandManiacChangePictureId(lcf::rpg::EventCommand const&) {
bool Game_Interpreter::CommandManiacChangePictureId(lcf::rpg::EventCommand const& com) {
/*
TPC Structure Reference:
@pic[target1].setId .move(target2, size) .ignoreError
@pic[target1].setId .swap(target2, size) .ignoreError
@pic[target1].setId .slide(distance, size) .ignoreError

Parameters:
[0] Operation: 0 = Move, 1 = Swap, 2 = Slide
[1] Packing:
Bits 0-3: Target 1 Mode (0: Const, 1: Var, 2: Indirect)
Bits 4-7: Size Mode (0: Const, 1: Var, 2: Indirect)
Bits 8-11: Object 2 / Distance Mode (0: Const, 1: Var, 2: Indirect)
[2] Target 1 Value
[3] Size Value
[4] Object 2 / Distance Value
[5] Ignore Error (1 = Ignore)
*/

if (!Player::IsPatchManiac()) {
return true;
}

Output::Warning("Maniac Patch: Command ChangePictureId not supported");
enum class Op {
Move,
Swap,
Slide
};

int op = com.parameters[0];
int from_id = ValueOrVariableBitfield(com, 1, 0, 2);
int size = ValueOrVariableBitfield(com, 1, 1, 3);
int arg = ValueOrVariableBitfield(com, 1, 2, 4); // Target 2 or Distance

bool ignore_error = com.parameters.size() > 5 && com.parameters[5] != 0;

if (size <= 0) {
return true;
}

auto& pictures = *Main_Data::game_pictures;

auto checkValidId = [&](int id, const char* msg) {
bool valid = id > 0;

if (!valid) {
auto outmsg = fmt::format("Maniac ChangePictureId {}: Invalid Picture ID {}", msg, id);
if (ignore_error) {
Output::DebugStr(outmsg);
} else {
Output::WarningStr(outmsg);
}
}

return valid;
};

if (op < 0 || op > 2) {
Output::Warning("Maniac ChangePictureId: Unknown operation {}", op);
return true;
}

auto operation = static_cast<Op>(op);

if (operation == Op::Slide) {
arg = from_id + arg;
}

auto func = (operation == Op::Swap)
? &Game_Pictures::SwapPicture
: &Game_Pictures::MovePicture;

for (int i = 0; i < size; ++i) {
int idx = i;

// Handle overlapping ranges
if (arg > from_id) {
idx = size - 1 - i;
}

int src_id = from_id + idx;
int dst_id = arg + idx;

if (!checkValidId(src_id, (
operation == Op::Move ? "Move (Source)" :
operation == Op::Swap ? "Swap (Source)" : "Slide (Source)"))) {
if (operation == Op::Swap) {
// Based on tests swapping with an invalid src is a no-op
continue;
}
}

if (!checkValidId(dst_id, (
operation == Op::Move ? "Move (Dest)" :
operation == Op::Swap ? "Swap (Dest)" : "Slide (Dest)"))) {
}

(pictures.*func)(src_id, dst_id);
}

return true;
}

Expand Down
85 changes: 83 additions & 2 deletions src/game_pictures.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,12 @@ int Game_Pictures::GetDefaultNumberOfPictures() {
return 0;
}

int Game_Pictures::GetPictureCount() const {
return static_cast<int>(pictures.size());
}

Game_Pictures::Picture& Game_Pictures::GetPicture(int id) {
assert(id > 0);
if (EP_UNLIKELY(id > static_cast<int>(pictures.size()))) {
pictures.reserve(id);
while (static_cast<int>(pictures.size()) < id) {
Expand All @@ -157,7 +162,7 @@ Game_Pictures::Picture& Game_Pictures::GetPicture(int id) {
}

Game_Pictures::Picture* Game_Pictures::GetPicturePtr(int id) {
return id <= static_cast<int>(pictures.size())
return id > 0 && id <= static_cast<int>(pictures.size())
? &pictures[id - 1] : nullptr;
}

Expand Down Expand Up @@ -347,7 +352,7 @@ void Game_Pictures::EraseAll() {

bool Game_Pictures::Picture::Exists() const {
// Incompatible with the Yume2kki edge-case that uses empty filenames
return !data.name.empty();
return !data.name.empty() || IsWindowAttached();
}

void Game_Pictures::Picture::CreateSprite() {
Expand Down Expand Up @@ -462,6 +467,9 @@ void Game_Pictures::Picture::ApplyOrigin(bool is_move) {
}
data.finish_x = x;
data.finish_y = y;

// Origin was applied, prevent applying again in later calls
origin = 0;
}

void Game_Pictures::Picture::OnMapScrolled(int dx16, int dy16) {
Expand Down Expand Up @@ -654,3 +662,76 @@ void Game_Pictures::Picture::SetNonEffectParams(const Params& params, bool set_p
int Game_Pictures::Picture::NumSpriteSheetFrames() const {
return data.spritesheet_cols * data.spritesheet_rows;
}

void Game_Pictures::MovePicture(int src_id, int dst_id) {
if (src_id == dst_id) {
return;
}

// Delete the destination, then swap
if (dst_id > 0) {
auto& dst_pic = GetPicture(dst_id);
dst_pic.Erase();
}

SwapPicture(src_id, dst_id);
}

void Game_Pictures::SwapPicture(int id1, int id2) {
if (id1 == id2 || (id1 <= 0 && id2 <= 0)) {
return;
}

auto max_id = std::max(id1, id2);
GetPicture(max_id); // Preallocate to ensure references are stable

Picture bad_pic{0}; // Sentinel when one of the pictures is invalid

auto* src_pic = &bad_pic;
auto* dst_pic = &bad_pic;

if (id1 > 0) {
src_pic = &GetPicture(id1);
} else {
bad_pic = Picture(id1);
}

if (id2 > 0) {
dst_pic = &GetPicture(id2);
} else {
bad_pic = Picture(id2);
}

// Handle Window Data (String Pictures)
if (src_pic->IsWindowAttached() || dst_pic->IsWindowAttached()) {
Main_Data::game_windows->SwapWindow(id1, id2);
}

std::swap(src_pic->data.ID, dst_pic->data.ID);
if (src_pic->sprite) {
if (id2 <= 0) {
src_pic->sprite.reset();
} else {
src_pic->sprite->SetPictureId(id2);
}
}
if (dst_pic->sprite) {
if (id1 <= 0) {
dst_pic->sprite.reset();
} else {
dst_pic->sprite->SetPictureId(id1);
}
}

// Cancel pending requests and restart them
if (src_pic->IsRequestPending()) {
RequestPictureSprite(*src_pic);
}

if (dst_pic->IsRequestPending()) {
RequestPictureSprite(*dst_pic);
}

// Must be last (invalidates references)
std::swap(*src_pic, *dst_pic);
}
26 changes: 26 additions & 0 deletions src/game_pictures.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class Game_Pictures {
void InitGraphics();

static int GetDefaultNumberOfPictures();
int GetPictureCount() const;

struct Params {
int position_x = 0;
Expand Down Expand Up @@ -130,9 +131,34 @@ class Game_Pictures {
bool IsWindowAttached() const;
};

/**
* @param id Picture ID
* @return Reference to a picture (allocates when necessary). Passing an invalid ID will abort!
*/
Picture& GetPicture(int id);

/**
* @param id Picture ID
* @return Pointer to an existing picture or nullptr if its an unused picture slot
*/
Picture* GetPicturePtr(int id);

/**
* Moves picture data to a different ID
*
* @param src_id Source ID to move from
* @param dst_id Destination ID to move to
*/
void MovePicture(int src_id, int dst_id);

/**
* Swaps picture data between two IDs
*
* @param id1 First ID to swap with
* @param id2 Second ID to swap with
*/
void SwapPicture(int id1, int id2);

private:
void RequestPictureSprite(Picture& pic);
void OnPictureSpriteReady(FileRequestResult*, int id);
Expand Down
45 changes: 45 additions & 0 deletions src/game_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,51 @@ Game_Windows::Window_User* Game_Windows::GetWindowPtr(int id) {
? &windows[id - 1] : nullptr;
}

void Game_Windows::MoveWindow(int src_id, int dst_id) {
if (src_id == dst_id) {
return;
}

// Delete the destination, then swap
if (dst_id > 0) {
auto& dst_win = GetWindow(dst_id);
dst_win.Erase();
}

SwapWindow(src_id, dst_id);
}

void Game_Windows::SwapWindow(int id1, int id2) {
if (id1 == id2 || (id1 <= 0 && id2 <= 0)) {
return;
}

auto max_id = std::max(id1, id2);
GetWindow(max_id); // Preallocate to ensure references are stable

Window_User bad_win{0};

auto* src_win = &bad_win;
auto* dst_win = &bad_win;

if (id1 > 0) {
src_win = &GetWindow(id1);
} else {
bad_win = Window_User(id1);
}

if (id2 > 0) {
dst_win = &GetWindow(id2);
} else {
bad_win = Window_User(id2);
}

std::swap(src_win->data.ID, dst_win->data.ID);

// Must be last (invalidates references)
std::swap(*src_win, *dst_win);
}

bool Game_Windows::Window_User::Create(const WindowParams& params) {
Erase();

Expand Down
Loading
Loading