Skip to content

Commit

Permalink
Merge pull request #3218 from EasyRPG-NewFeatures/Jetrotal-SpawnMapEvent
Browse files Browse the repository at this point in the history
New Command 2056/2057 - Clone/Destroy Map Event
  • Loading branch information
fdelapena authored Oct 11, 2024
2 parents faa9502 + 85b82b7 commit 3b2cb25
Show file tree
Hide file tree
Showing 18 changed files with 463 additions and 52 deletions.
95 changes: 93 additions & 2 deletions src/async_op.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#ifndef EP_ASYNC_OP_H
#define EP_ASYNC_OP_H

#include "string_view.h"
#include <string>
#include <utility>
#include <cassert>

Expand All @@ -42,7 +44,9 @@ class AsyncOp {
eSave,
eLoad,
eYield,
eYieldRepeat
eYieldRepeat,
eCloneMapEvent,
eDestroyMapEvent
};

AsyncOp() = default;
Expand Down Expand Up @@ -80,6 +84,12 @@ class AsyncOp {
/** @return a Yield for one frame and repeat the command to e.g. fetch an important asset */
static AsyncOp MakeYieldRepeat();

/** @return a clone map event async operation */
static AsyncOp MakeCloneMapEvent(std::string name, int src_event_id, int target_event_id, int map_id, int x, int y);

/** @return a destroy map event async operation */
static AsyncOp MakeDestroyMapEvent(int target_event_id);

/** @return the type of async operation */
Type GetType() const;

Expand Down Expand Up @@ -128,13 +138,52 @@ class AsyncOp {
**/
int GetSaveResultVar() const;

/**
* @return the event id of the event being cloned
* @pre If GetType() is not eCloneMapEvent, the return value is undefined.
*/
int GetSourceEventId() const;

/**
* @return the event id of the event being created
* @pre If GetType() is not eCloneMapEvent or eDestroyMapEvent, the return value is undefined.
*/
int GetTargetEventId() const;

/**
* @return the map id where the event is cloned from
* @pre If GetType() is not eCloneMapEvent, the return value is undefined.
*/
int GetMapId() const;

/**
* @return the x coordinate where to spawn the event
* @pre If GetType() is not eCloneMapEvent, the return value is undefined.
*/
int GetX() const;

/**
* @return the y coordinate where to spawn the event
* @pre If GetType() is not eCloneMapEvent, the return value is undefined.
*/
int GetY() const;

/**
* @return the new name of the event
* @pre If GetType() is not eCloneMapEvent, the return value is undefined.
*/
StringView GetEventName() const;

private:
Type _type = eNone;
int _args[3] = {};
int _args[5] = {};
std::string _str_arg;

template <typename... Args>
explicit AsyncOp(Type type, Args&&... args);

template <typename... Args>
explicit AsyncOp(Type type, std::string str_arg, Args&&... args);
};

inline AsyncOp::Type AsyncOp::GetType() const {
Expand Down Expand Up @@ -180,12 +229,46 @@ inline int AsyncOp::GetSaveResultVar() const {
return _args[1];
}

inline int AsyncOp::GetSourceEventId() const {
assert(GetType() == eCloneMapEvent);
return _args[0];
}

inline int AsyncOp::GetTargetEventId() const {
assert(GetType() == eCloneMapEvent || GetType() == eDestroyMapEvent);
return _args[1];
}

inline int AsyncOp::GetMapId() const {
assert(GetType() == eCloneMapEvent);
return _args[2];
}

inline int AsyncOp::GetX() const {
assert(GetType() == eCloneMapEvent);
return _args[3];
}

inline int AsyncOp::GetY() const {
assert(GetType() == eCloneMapEvent);
return _args[4];
}

inline StringView AsyncOp::GetEventName() const {
assert(GetType() == eCloneMapEvent);
return _str_arg;
}

template <typename... Args>
inline AsyncOp::AsyncOp(Type type, Args&&... args)
: _type(type), _args{std::forward<Args>(args)...}
{}

template <typename... Args>
inline AsyncOp::AsyncOp(Type type, std::string str_arg, Args&&... args)
: _type(type), _args{std::forward<Args>(args)...}, _str_arg(std::move(str_arg))
{}

inline AsyncOp AsyncOp::MakeShowScreen(int transition_type) {
return AsyncOp(eShowScreen, transition_type);
}
Expand Down Expand Up @@ -230,4 +313,12 @@ inline AsyncOp AsyncOp::MakeYieldRepeat() {
return AsyncOp(eYieldRepeat);
}

inline AsyncOp AsyncOp::MakeCloneMapEvent(std::string name, int src_event_id, int target_event_id, int map_id, int x, int y) {
return AsyncOp(eCloneMapEvent, name, src_event_id, target_event_id, map_id, x, y);
}

inline AsyncOp AsyncOp::MakeDestroyMapEvent(int target_event_id) {
return AsyncOp(eDestroyMapEvent, 0, target_event_id);
}

#endif
12 changes: 8 additions & 4 deletions src/battle_animation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,14 @@ static int CalculateOffset(int pos, int target_height) {
/////////

BattleAnimationMap::BattleAnimationMap(const lcf::rpg::Animation& anim, Game_Character& target, bool global) :
BattleAnimation(anim), target(target), global(global)
BattleAnimation(anim), target(&target), global(global)
{
}

void BattleAnimationMap::SetTarget(Game_Character& target) {
this->target = &target;
}

void BattleAnimationMap::Draw(Bitmap& dst) {
if (IsOnlySound()) {
return;
Expand Down Expand Up @@ -263,8 +267,8 @@ void BattleAnimationMap::DrawSingle(Bitmap& dst) {
return;
}
const int character_height = 24;
int x_off = target.GetScreenX();
int y_off = target.GetScreenY(false);
int x_off = target->GetScreenX();
int y_off = target->GetScreenY(false);
if (Scene::instance->type == Scene::Map) {
x_off += static_cast<Scene_Map*>(Scene::instance.get())->spriteset->GetRenderOx();
y_off += static_cast<Scene_Map*>(Scene::instance.get())->spriteset->GetRenderOy();
Expand All @@ -276,7 +280,7 @@ void BattleAnimationMap::DrawSingle(Bitmap& dst) {
}

void BattleAnimationMap::FlashTargets(int r, int g, int b, int p) {
target.Flash(r, g, b, p, 0);
target->Flash(r, g, b, p, 0);
}

void BattleAnimationMap::ShakeTargets(int /* str */, int /* spd */, int /* time */) {
Expand Down
3 changes: 2 additions & 1 deletion src/battle_animation.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,15 @@ class BattleAnimation : public Sprite {
class BattleAnimationMap : public BattleAnimation {
public:
BattleAnimationMap(const lcf::rpg::Animation& anim, Game_Character& target, bool global);
void SetTarget(Game_Character& target);
void Draw(Bitmap& dst) override;
protected:
void FlashTargets(int r, int g, int b, int p) override;
void ShakeTargets(int str, int spd, int time) override;
void DrawSingle(Bitmap& dst);
void DrawGlobal(Bitmap& dst);

Game_Character& target;
Game_Character* target;
bool global = false;
};

Expand Down
4 changes: 3 additions & 1 deletion src/game_character.h
Original file line number Diff line number Diff line change
Expand Up @@ -1379,7 +1379,9 @@ inline Game_CharacterDataStorage<T>::Game_CharacterDataStorage(Game_CharacterDat
template <typename T>
inline Game_CharacterDataStorage<T>& Game_CharacterDataStorage<T>::operator=(Game_CharacterDataStorage&& o) noexcept
{
static_cast<Game_Character*>(this) = std::move(o);
auto* base = static_cast<Game_Character*>(this);
*base = std::move(o);

if (this != &o) {
_data = std::move(o._data);
Game_Character::_data = &this->_data;
Expand Down
5 changes: 5 additions & 0 deletions src/game_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ class Game_Event : public Game_EventBase {
*/
Game_Event(int map_id, const lcf::rpg::Event* event);

/** @param ev Event referenced */
void SetUnderlyingEvent(const lcf::rpg::Event* ev) {
event = ev;
}

/** Load from saved game */
void SetSaveData(lcf::rpg::SaveMapEvent save);

Expand Down
59 changes: 59 additions & 0 deletions src/game_interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,10 @@ bool Game_Interpreter::ExecuteCommand(lcf::rpg::EventCommand const& com) {
return CommandManiacCallCommand(com);
case Cmd::EasyRpg_SetInterpreterFlag:
return CommandEasyRpgSetInterpreterFlag(com);
case static_cast<Cmd>(2056): //EasyRPG_CloneMapEvent
return CommandEasyRpgCloneMapEvent(com);
case static_cast<Cmd>(2057): //EasyRPG_DestroyMapEvent
return CommandEasyRpgDestroyMapEvent(com);
default:
return true;
}
Expand Down Expand Up @@ -5011,6 +5015,61 @@ bool Game_Interpreter::CommandEasyRpgSetInterpreterFlag(lcf::rpg::EventCommand c
return true;
}

bool Game_Interpreter::CommandEasyRpgCloneMapEvent(lcf::rpg::EventCommand const& com) {
if (!Player::HasEasyRpgExtensions()) {
return true;
}

if (com.parameters.size() < 8) {
return true;
}

int src_map = ValueOrVariable(com.parameters[0], com.parameters[1]);
int src_event = ValueOrVariable(com.parameters[2], com.parameters[3]);
int target_x = ValueOrVariable(com.parameters[4], com.parameters[5]);
int target_y = ValueOrVariable(com.parameters[6], com.parameters[7]);

int target_event = 0;
if (com.parameters.size() >= 10) {
target_event = ValueOrVariable(com.parameters[8], com.parameters[9]);
}

std::string target_name = ToString(CommandStringOrVariable(com, 10, 11));

if (src_map == 0) {
src_map = Game_Map::GetMapId();
} else {
auto* request = Game_Map::RequestMap(src_map);
request->Start();

if (!request->IsReady()) {
// Download the map and try again
_async_op = AsyncOp::MakeYieldRepeat();
return true;
}
}

_async_op = AsyncOp::MakeCloneMapEvent(target_name, src_event, target_event, src_map, target_x, target_y);

return true;
}

bool Game_Interpreter::CommandEasyRpgDestroyMapEvent(lcf::rpg::EventCommand const& com) {
if (!Player::HasEasyRpgExtensions()) {
return true;
}

if (com.parameters.size() < 2) {
return true;
}

int target_event = ValueOrVariable(com.parameters[0], com.parameters[1]);

_async_op = AsyncOp::MakeDestroyMapEvent(target_event);

return true;
}

Game_Interpreter& Game_Interpreter::GetForegroundInterpreter() {
return Game_Battle::IsBattleRunning()
? Game_Battle::GetInterpreter()
Expand Down
14 changes: 14 additions & 0 deletions src/game_interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ class Game_Interpreter : public Game_BaseInterpreterContext
/** @return the event_id of the event at the base of the call stack */
int GetOriginalEventId() const;

/**
* Sets the ID of the event at the base of the call stack to 0.
* Used by DestroyMapEvent to prevent triggering a sanity check when the event is destroyed.
*/
void ClearOriginalEventId();

/** Return true if the interpreter is waiting for an async operation and needs to be resumed */
bool IsAsyncPending();

Expand Down Expand Up @@ -292,6 +298,8 @@ class Game_Interpreter : public Game_BaseInterpreterContext
bool CommandManiacControlStrings(lcf::rpg::EventCommand const& com);
bool CommandManiacCallCommand(lcf::rpg::EventCommand const& com);
bool CommandEasyRpgSetInterpreterFlag(lcf::rpg::EventCommand const& com);
bool CommandEasyRpgCloneMapEvent(lcf::rpg::EventCommand const& com);
bool CommandEasyRpgDestroyMapEvent(lcf::rpg::EventCommand const& com);

void SetSubcommandIndex(int indent, int idx);
uint8_t& ReserveSubcommandIndex(int indent);
Expand Down Expand Up @@ -370,6 +378,12 @@ inline int Game_Interpreter::GetOriginalEventId() const {
return !_state.stack.empty() ? _state.stack.front().event_id : 0;
}

inline void Game_Interpreter::ClearOriginalEventId() {
if (!_state.stack.empty()) {
_state.stack.front().event_id = 0;
}
}

inline int Game_Interpreter::GetLoopCount() const {
return loop_count;
}
Expand Down
Loading

0 comments on commit 3b2cb25

Please sign in to comment.