Merge branch 'rando-mod' of https://github.com/TwilitRealm/dusk into rando-mod

This commit is contained in:
gymnast86
2026-08-15 05:24:56 -07:00
122 changed files with 5782 additions and 552 deletions
+1 -1
+6
View File
@@ -1436,6 +1436,7 @@ set(DUSK_FILES
src/dusk/gamepad_color.cpp
src/dusk/globals.cpp
src/dusk/gyro.cpp
src/dusk/hq_minimap.cpp
src/dusk/imgui/ImGuiActorSpawner.cpp
src/dusk/imgui/ImGuiBloomWindow.cpp
src/dusk/imgui/ImGuiBloomWindow.hpp
@@ -1472,6 +1473,9 @@ set(DUSK_FILES
src/dusk/mods/loader/native_module.hpp
src/dusk/mods/loader/prepatch.cpp
src/dusk/mods/loader/prepatch.hpp
src/dusk/mods/item.hpp
src/dusk/mods/item_checks.cpp
src/dusk/mods/item_gives.cpp
src/dusk/mods/log_buffer.cpp
src/dusk/mods/log_buffer.hpp
src/dusk/mods/manifest.cpp
@@ -1483,6 +1487,8 @@ set(DUSK_FILES
src/dusk/mods/svc/gfx.cpp
src/dusk/mods/svc/hook.cpp
src/dusk/mods/svc/host.cpp
src/dusk/mods/svc/item.cpp
src/dusk/mods/svc/item.hpp
src/dusk/mods/svc/log.cpp
src/dusk/mods/svc/overlay.cpp
src/dusk/mods/svc/registry.cpp
+1 -1
View File
@@ -123,7 +123,7 @@ public:
bool checkFlag(u16 flag) { return field_0x68e & flag; }
void setAction(u8 action) { mAction = action; }
private:
// private:
/* 0x56C */ request_of_phase_process_class mPhase1;
/* 0x574 */ request_of_phase_process_class mPhase2;
/* 0x57C */ J3DModel* mModel1;
+3
View File
@@ -96,6 +96,9 @@ public:
/* 0xDE0 */ cXyz field_0xde0;
/* 0xDEC */ u8 field_0xdec[0xdf9 - 0xdec];
/* 0xDF9 */ u8 field_0xdf9;
#if TARGET_PC
bool mItemCheckOverridden;
#endif
};
STATIC_ASSERT(sizeof(daE_HP_c) == 0xdfc);
+1 -1
View File
@@ -296,7 +296,7 @@ public:
static DUSK_GAME_DATA char DUSK_CONST* DUSK_CONST mCutNameList[18];
static DUSK_GAME_DATA cutFunc DUSK_CONST mCutList[18];
private:
// private:
/* 0x0E40 */ NPC_FAIRY_HIO_CLASS* mHIO;
/* 0x0E44 */ dCcD_Cyl mCyl;
/* 0x0F80 */ u8 mType;
+1 -1
View File
@@ -287,7 +287,7 @@ public:
static DUSK_GAME_DATA char DUSK_CONST* DUSK_CONST mCutNameList[10];
static DUSK_GAME_DATA cutFunc DUSK_CONST mCutList[10];
private:
// private:
/* 0x0E40 */ mDoExt_McaMorfSO* mFishModelMorf;
/* 0x0E44 */ mDoExt_McaMorfSO* mLeafModelMorf;
/* 0x0E48 */ NPC_YKM_HIO_CLASS* mpHIO;
+1 -1
View File
@@ -96,7 +96,7 @@ public:
static DUSK_GAME_DATA char DUSK_CONST* DUSK_CONST mEvtCutNameList[2];
static DUSK_GAME_DATA EventFn DUSK_CONST mEvtCutList[2];
private:
// private:
/* 0xB48 */ Z2Creature mCreatureSound;
/* 0xBD8 */ daNpcF_MatAnm_c* mpMatAnm;
/* 0xBDC */ daNpcF_Lookat_c mLookat;
+1 -1
View File
@@ -114,7 +114,7 @@ public:
void himoCalc();
void adjustShapeAngle() {}
private:
// private:
/* 0x0B48 */ Z2Creature mCreatureSound;
/* 0x0BD8 */ daNpcF_MatAnm_c* mpMatAnm;
/* 0x0BDC */ mDoExt_invisibleModel mInvisibleModel;
+1 -1
View File
@@ -60,7 +60,7 @@ public:
bool isFirst() { return fopAcM_GetParamBit(this, 0x1b, 1); }
void setAction(u8 action) { mAction = action; }
private:
// private:
/* 0x568 */ request_of_phase_process_class mPhaseReq;
/* 0x570 */ J3DModel* mpModel;
/* 0x574 */ mDoExt_btkAnm* mpBtkAnm[2];
+5
View File
@@ -88,6 +88,11 @@ private:
/* 0x99C */ dPa_followEcallBack mEffect2;
/* 0x9B0 */ Z2SoundObjSimple mSound;
/* 0x9D0 */ u8 mIsHookCarry;
#if TARGET_PC
u8 mOriginalItemNo;
bool mItemOverridden;
bool mOverrideHover;
#endif
};
STATIC_ASSERT(sizeof(daObjLife_c) == 0x9d4);
+1 -1
View File
@@ -51,7 +51,7 @@ public:
static DUSK_GAME_DATA daObjMasterSword_Attr_c const mAttr;
static DUSK_GAME_DATA actionFunc ActionTable[];
private:
// private:
/* 0x568 */ J3DModel* mpModel;
/* 0x56C */ request_of_phase_process_class mPhase;
/* 0x574 */ mDoExt_btkAnm mBtk;
+3
View File
@@ -194,6 +194,9 @@ private:
/* 0x9FC */ u8 field_0x9fc;
/* 0x9FD */ u8 field_0x9fd;
/* 0xA00 */ Mtx mDrawMtx;
#if TARGET_PC
u8 mOriginalItemNo;
#endif
};
STATIC_ASSERT(sizeof(daTbox_c) == 0xA30);
+5 -1
View File
@@ -67,7 +67,7 @@ public:
u8 getModelType() { return fopAcM_GetParamBit(this, 8, 4); }
u8 getItemNo() { return fopAcM_GetParamBit(this, 0, 8); }
private:
// private:
/* 0x718 */ u8 mReturnRupee;
/* 0x71C */ request_of_phase_process_class mPhase;
/* 0x724 */ J3DModel* mpModel;
@@ -83,6 +83,10 @@ private:
/* 0xAC0 */ u8 field_0xAC0[0xAC4 - 0xAC0];
/* 0xAC4 */ int mStaffIdx;
/* 0xAC8 */ dBgW* mBoxBgW;
#if TARGET_PC
bool mParamsInit;
u8 mOriginalItemNo;
#endif
};
STATIC_ASSERT(sizeof(daTbox2_c) == 0xACC);
+4
View File
@@ -145,6 +145,10 @@ public:
/* 0x9BC */ u8 field_0x9bc[4];
/* 0x9C0 */ u8 field_0x9c0;
/* 0x9C1 */ u8 field_0x9c1;
#if TARGET_PC
u8 mOriginalItemNo;
bool mItemOverridden;
#endif
}; // Size: 0x9C4
#endif /* D_A_D_A_ITEM_STATIC_H */
+7
View File
@@ -50,6 +50,9 @@ public:
csXyz* getRotateP();
cXyz* getPosP();
const char* getShopArcname();
#if TARGET_PC
const ResourceData& getResourceData() const;
#endif
u16 getHeapSize();
void CreateInit();
void set_mtx();
@@ -110,6 +113,10 @@ private:
/* 0x960 */ s16 mAngleX;
/* 0x962 */ s16 mAngleY;
/* 0x964 */ u8 mShopItemID;
#if TARGET_PC
ResourceData mOverrideData;
bool mItemOverridden;
#endif
};
int CheckShopItemCreateHeap(fopAc_ac_c* i_this);
+1 -1
View File
@@ -37,7 +37,7 @@ public:
CPaneMgrAlpha* getNoDatBase() { return mNoDatBase; }
void draw() { _draw(); }
private:
// private:
/* 0x04 */ JKRArchive* mArchive;
/* 0x08 */ dDlst_FileInfo_c mFileInfo;
/* 0x1C */ u8 field_0x1c[4];
+3 -1
View File
@@ -11,7 +11,9 @@ public:
static DUSK_GAME_DATA u8* mData;
};
void execItemGet(u8 item_id);
class fopAc_ac_c;
void execItemGet(
u8 item_id IF_DUSK_ARG(u32 item_give_tag = 0) IF_DUSK_ARG(fopAc_ac_c* giver = NULL));
void item_func_HEART();
void item_func_GREEN_RUPEE();
+5
View File
@@ -318,6 +318,11 @@ public:
/* 0x566 */ s8 field_0x566;
/* 0x567 */ s8 field_0x567;
#if TARGET_PC
u32 mItemGiveTag;
u8 mItemGiveOriginalNo;
#endif
#if !__MWERKS__
s8 actor_last_base_field;
#endif
+20 -13
View File
@@ -79,6 +79,10 @@ struct fopAcM_prm_class {
/* 0x1C */ fpc_ProcID parent_id;
/* 0x20 */ s8 argument;
/* 0x21 */ s8 room_no;
#if TARGET_PC
u32 mItemGiveTag;
u8 mItemGiveOriginalNo;
#endif
};
struct fopAcM_search4ev_prm {
@@ -516,8 +520,9 @@ s32 fopAcM_SearchByName(s16 i_procName, fopAc_ac_c** i_outActor);
fopAcM_prm_class* fopAcM_CreateAppend();
fopAcM_prm_class* createAppend(u16 i_setId, u32 i_parameters, const cXyz* i_pos, int i_roomNo,
const csXyz* i_angle, const cXyz* i_scale, s8 i_argument,
fpc_ProcID i_parentId);
const csXyz* i_angle, const cXyz* i_scale, s8 i_argument,
fpc_ProcID i_parentId IF_DUSK_ARG(u32 i_itemGiveTag = 0)
IF_DUSK_ARG(u8 i_itemOriginalNo = 0xFF));
void fopAcM_Log(fopAc_ac_c const* i_actor, char const* i_message);
@@ -526,19 +531,20 @@ s32 fopAcM_delete(fopAc_ac_c* i_actor);
s32 fopAcM_delete(fpc_ProcID i_actorID);
fpc_ProcID fopAcM_create(s16 i_procName, u16 i_setId, u32 i_parameters, const cXyz* i_pos,
int i_roomNo, const csXyz* i_angle, const cXyz* i_scale, s8 i_argument,
createFunc i_createFunc);
int i_roomNo, const csXyz* i_angle, const cXyz* i_scale, s8 i_argument,
createFunc i_createFunc IF_DUSK_ARG(u32 i_itemGiveTag = 0));
fpc_ProcID fopAcM_create(s16 i_procName, u32 i_parameters, const cXyz* i_pos, int i_roomNo,
const csXyz* i_angle, const cXyz* i_scale, s8 i_argument);
const csXyz* i_angle, const cXyz* i_scale, s8 i_argument IF_DUSK_ARG(u32 i_itemGiveTag = 0));
inline fpc_ProcID fopAcM_Create(s16 i_procName, createFunc i_createFunc, void* params) {
return fpcM_Create(i_procName, i_createFunc,params);
}
fopAc_ac_c* fopAcM_fastCreate(s16 i_procName, u32 i_parameters, const cXyz* i_pos, int i_roomNo,
const csXyz* i_angle, const cXyz* i_scale, s8 i_argument,
createFunc i_createFunc, void* i_createFuncData);
const csXyz* i_angle, const cXyz* i_scale, s8 i_argument, createFunc i_createFunc,
void* i_createFuncData IF_DUSK_ARG(u32 i_itemGiveTag = 0)
IF_DUSK_ARG(u8 i_itemOriginalNo = 0xFF));
fopAc_ac_c* fopAcM_fastCreate(const char* i_actorname, u32 i_parameters, const cXyz* i_pos,
int i_roomNo, const csXyz* i_angle, const cXyz* i_scale,
@@ -623,11 +629,11 @@ fopAc_ac_c* fopAcM_getItemEventPartner(const fopAc_ac_c*);
fopAc_ac_c* fopAcM_getEventPartner(const fopAc_ac_c*);
fpc_ProcID fopAcM_createItemForPresentDemo(cXyz const* i_pos, int i_itemNo, u8 param_2,
int i_itemBitNo, int i_roomNo, csXyz const* i_angle,
cXyz const* i_scale);
int i_itemBitNo, int i_roomNo, csXyz const* i_angle,
cXyz const* i_scale IF_DUSK_ARG(u32 i_itemGiveTag = 0));
fpc_ProcID fopAcM_createItemForTrBoxDemo(cXyz const* i_pos, int i_itemNo, int i_itemBitNo,
int i_roomNo, csXyz const* i_angle, cXyz const* i_scale);
int i_roomNo, csXyz const* i_angle, cXyz const* i_scale IF_DUSK_ARG(u32 i_itemGiveTag = 0));
u8 fopAcM_getItemNoFromTableNo(u8 i_tableNo);
@@ -641,11 +647,12 @@ fpc_ProcID fopAcM_createItemFromTable(cXyz const* i_pos, int i_tableNo, int i_it
bool i_createDirect);
fpc_ProcID fopAcM_createDemoItem(const cXyz* i_pos, int i_itemNo, int i_itemBitNo,
const csXyz* i_angle, int i_roomNo, const cXyz* scale, u8 param_7);
const csXyz* i_angle, int i_roomNo, const cXyz* scale,
u8 param_7 IF_DUSK_ARG(u32 i_itemGiveTag = 0));
fpc_ProcID fopAcM_createItemForBoss(const cXyz* i_pos, int i_itemNo, int i_roomNo,
const csXyz* i_angle, const cXyz* i_scale, f32 i_speedF,
f32 i_speedY, int param_8);
const csXyz* i_angle, const cXyz* i_scale, f32 i_speedF, f32 i_speedY,
int param_8 IF_DUSK_ARG(const char* i_itemCheckName = NULL));
fpc_ProcID fopAcM_createItemForMidBoss(const cXyz* i_pos, int i_itemNo, int i_roomNo,
const csXyz* i_angle, const cXyz* i_scale, int param_6,
+13
View File
@@ -256,4 +256,17 @@ using std::isnan;
#define DUSK_CONST IF_DUSK(const)
#define DUSK_CONSTEXPR IF_DUSK(constexpr)
#if TARGET_PC && defined(DUSK_BUILDING_GAME)
#include "dusk/mods/item.hpp"
#define DUSK_ITEM_CHECK(name, item_no, giver) \
(item_no) = ::dusk::mods::item_check(name, (item_no), giver)
#define DUSK_ITEM_CHECK_EXPR(name, item_no, giver) \
(::dusk::mods::item_check(name, (item_no), giver))
#define DUSK_GIVE_TAG(name) IF_DUSK_ARG(::dusk::mods::item_give_tag(name))
#else
#define DUSK_ITEM_CHECK(name, item_no, giver)
#define DUSK_ITEM_CHECK_EXPR(name, item_no, giver) (item_no)
#define DUSK_GIVE_TAG(name)
#endif
#endif
+7 -12
View File
@@ -38,7 +38,7 @@ void JFWDisplay::ctor_subroutine(bool enableAlpha) {
mTickRate = 0;
mCombinationRatio = 0.0f;
field_0x30 = 0;
field_0x2c = OSGetTick();
field_0x2c = DUSK_IF_ELSE(static_cast<OSTick>(OSGetNativeTime()), OSGetTick());
field_0x34 = 0;
field_0x48 = 0;
field_0x4a = 0;
@@ -258,7 +258,7 @@ void JFWDisplay::beginRender() {
waitForTick(mTickRate, mFrameRate);
JUTVideo::getManager()->waitRetraceIfNeed();
OSTick tick = OSGetTick();
OSTick tick = DUSK_IF_ELSE(static_cast<OSTick>(OSGetNativeTime()), OSGetTick());
field_0x30 = tick - field_0x2c;
field_0x2c = tick;
field_0x34 = field_0x2c - JUTVideo::getVideoLastTick();
@@ -371,7 +371,7 @@ constexpr auto FRAME_PERIOD = std::chrono::duration_cast<std::chrono::nanosecond
constexpr auto RETRACE_PERIOD = FRAME_PERIOD / 2;
static void waitPrecise(Limiter& limiter, Limiter::duration_t targetNs) {
const auto sleepTime = limiter.Sleep(targetNs);
const auto sleepTime = limiter.Sleep(targetNs);
dusk::frameUsagePct =
100.0f * (1.0f - static_cast<float>(sleepTime) / static_cast<float>(targetNs));
}
@@ -381,15 +381,12 @@ static void waitForTick(u32 p1, u16 p2) {
#if TARGET_PC
static Limiter limiter;
if (dusk::frame_interp::is_enabled() && !dusk::getTransientSettings().skipFrameRateLimit) {
dusk::frameUsagePct = 0.f;
return;
if (dusk::frame_interp::is_enabled() || dusk::getTransientSettings().turboMode) {
limiter.Reset();
dusk::frameUsagePct = 0.f;
return;
}
if (dusk::getTransientSettings().skipFrameRateLimit) {
p1 = OS_TIMER_CLOCK / 120;
}
if (fopOvlpM_IsPeek() && dusk::getTransientSettings().stateShareLoadActive) {
return;
}
@@ -399,7 +396,6 @@ static void waitForTick(u32 p1, u16 p2) {
if (p1 != 0) {
#if TARGET_PC
static Limiter limiter;
waitPrecise(limiter, static_cast<Uint64>(OSTicksToMicroseconds(p1)) * 1000ULL);
#else
static OSTime nextTick = OSGetTime();
@@ -413,7 +409,6 @@ static void waitForTick(u32 p1, u16 p2) {
} else {
u32 uVar1 = (p2 == 0) ? 1 : p2;
#if TARGET_PC
static Limiter limiter;
waitPrecise(limiter, static_cast<Uint64>((RETRACE_PERIOD * uVar1).count()));
#else
static u32 nextCount = VIGetRetraceCount();
+2 -2
View File
@@ -42,7 +42,7 @@ JUTVideo::JUTVideo(GXRenderModeObj const* param_0) {
mRetraceCount = VIGetRetraceCount();
field_0x10 = 1;
field_0x18 = 0;
sVideoLastTick = OSGetTick();
sVideoLastTick = DUSK_IF_ELSE(static_cast<OSTick>(OSGetNativeTime()), OSGetTick());
sVideoInterval = 670000;
mPreRetraceCallback = VISetPreRetraceCallback(preRetraceProc);
mPostRetraceCallback = VISetPostRetraceCallback(postRetraceProc);
@@ -68,7 +68,7 @@ void JUTVideo::preRetraceProc(u32 retrace_count) {
(*sManager->mPreCallback)(retrace_count);
}
OSTick tick = OSGetTick();
OSTick tick = DUSK_IF_ELSE(static_cast<OSTick>(OSGetNativeTime()), OSGetTick());
sVideoInterval = tick - sVideoLastTick;
sVideoLastTick = tick;
+2
View File
@@ -1,5 +1,6 @@
cmake_minimum_required(VERSION 3.25)
project(randomizer CXX)
set(CMAKE_CXX_STANDARD 23)
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
set(DUSK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../.." CACHE PATH "Path to dusk source root")
@@ -86,6 +87,7 @@ set(RANDOMIZER_SOURCES
src/paths.cpp
src/session.cpp
src/hooks.cpp
src/item.cpp
src/ui/config_store.cpp
src/ui/rando_seed_generation.cpp
src/ui/rando_config.cpp
@@ -1222,7 +1222,7 @@
Forward:
Connection: Kakariko Renados Sanctuary -> Kakariko Renados Sanctuary Basement
Stage: 75
Room: 5
Room: 7
Spawn: "00"
Spawn Type: "FF"
Parameters: "FFFF"
@@ -356,4 +356,31 @@ namespace randomizer::logic::entrance
return {parentAreaName, connectedAreaName};
}
bool Entrance::SetGameInfo(const YAML::Node& node) {
bool ret = true;
std::string stageIdString = node["Stage"].as<std::string>();
if (stageIdString == "null") {
stageIdString = "-1";
ret = false;
}
int stageId = std::stoi(stageIdString);
if (stageId == -1) {
stageId = 0xFF;
}
SetStageId(static_cast<uint8_t>(stageId));
std::string roomNoString = node["Room"].as<std::string>();
if (roomNoString == "null") {
roomNoString = "-1";
ret = false;
}
SetRoomNo(static_cast<int8_t>(std::stoi(roomNoString)));
SetPointNo(static_cast<int16_t>(std::stoi("0x0"+node["Spawn"].as<std::string>(),nullptr,16)));
int layer = std::stoi("0x0"+node["State"].as<std::string>(),nullptr,16);
if (layer == 0xFF) {
layer = -1;
}
SetLayerNo(static_cast<int8_t>(layer));
return ret;
}
} // namespace randomizer::logic::entrance
@@ -5,6 +5,8 @@
#include <unordered_set>
#include <map>
#include <string>
#include <cstdint>
#include "../utility/yaml.hpp"
// Forward Declarations
namespace randomizer::logic::area
@@ -148,6 +150,18 @@ namespace randomizer::logic::entrance
*/
Entrance* AssumeReachable();
void SetStageId(uint8_t stageId) { _stageId = stageId; }
void SetRoomNo(int8_t roomNo) { _roomNo = roomNo; }
void SetLayerNo(int8_t layerNo) { _layerNo = layerNo; }
void SetPointNo(int16_t pointNo) { _pointNo = pointNo; }
uint8_t GetStageId() const { return _stageId; }
int8_t GetRoomNo() const { return _roomNo; }
int8_t GetLayerNo() const { return _layerNo; }
int16_t GetPointNo() const { return _pointNo; }
bool SetGameInfo(const YAML::Node& node);
void SetCoupledDoor(int16_t entrance) { _coupledEntrance = entrance; }
int16_t getCoupledDoor() { return _coupledEntrance; }
private:
int _id = -1;
area::Area* _parentArea = nullptr;
@@ -159,6 +173,11 @@ namespace randomizer::logic::entrance
std::string _alias = "";
world::World* _world = nullptr;
uint8_t _stageId = 0xFF;
int8_t _roomNo = -1;
int8_t _layerNo = -1;
int16_t _pointNo = -1;
/**
* @brief The local requirement for this entrance assuming we have access to its parent area.
*/
@@ -202,6 +221,9 @@ namespace randomizer::logic::entrance
// to this one. So if the entrance is North Faron Woods -> Forest Temple Entrance,
// then _assumed is the target entrance Root -> Forest Temple Entrance
Entrance* _assumed = nullptr;
// If the entrance is a coupled door, this is the other door's point to override
int16_t _coupledEntrance = -1;
};
using EntrancePool = std::vector<Entrance*>;
@@ -65,7 +65,7 @@ namespace randomizer::logic::entrance_shuffle
auto forwardEntrance = world->GetEntrance(forwardEntry["Connection"].as<std::string>());
forwardEntrance->SetType(type);
// TODO: Set actual entrance data
forwardEntrance->SetGameInfo(forwardEntry);
forwardEntrance->SetID(world->GetNewEntranceID());
forwardEntrance->SetPrimary(true);
forwardEntrance->SetAlias(
@@ -78,7 +78,7 @@ namespace randomizer::logic::entrance_shuffle
auto returnEntrance = world->GetEntrance(returnEntry["Connection"].as<std::string>());
returnEntrance->SetType(type);
// TODO: Set actual entrance data
returnEntrance->SetGameInfo(returnEntry);
returnEntrance->SetID(world->GetNewEntranceID());
returnEntrance->SetAlias(
returnEntry["Alias"] ? returnEntry["Alias"].as<std::string>() : "");
@@ -114,7 +114,7 @@ namespace randomizer::logic::entrance_shuffle
[&](const auto& door) { return door->IsPrimary() == mainDoor->IsPrimary(); });
auto coupledDoor = *coupledDoorItr;
// TODO: Add the coupled door's info to the main door
mainDoor->SetCoupledDoor(coupledDoor->GetPointNo());
// Completely remove the coupled door from the world graph
doors.erase(coupledDoorItr);
@@ -471,7 +471,7 @@ namespace randomizer::logic::entrance_shuffle
// plandomizer stuff going on. Currently, the only non-assumed entrance we're shuffling
// is the randomized spawn, but if we ever shuffle warp portals, they'll go here too.
int retries = 20;
int retries = 10000;
while (retries > 0)
{
std::unordered_map<Entrance*, Entrance*> rollbacks = {};
@@ -4,6 +4,7 @@
#include "../item.hpp"
#include <iterator>
#include <cstdint>
BitVector::BitVector(const std::list<int>& bits)
{
@@ -10,6 +10,7 @@
#include <set>
#include <memory>
#include <stdexcept>
#include <cstdint>
namespace randomizer::seedgen::settings
{
File diff suppressed because it is too large Load Diff
+1
View File
@@ -4,4 +4,5 @@
namespace randomizer::hooks {
ModResult initialize();
ModResult uninstall();
}
File diff suppressed because it is too large Load Diff
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <dolphin/types.h>
namespace randomizer::item {
void exec_item_get(u8 item_no);
int check_item_get(u8 item_no, int param);
void apply_item_data_tables();
void restore_item_data_tables();
} // namespace randomizer::item
+7 -1
View File
@@ -1,9 +1,10 @@
#include "mods/service.hpp"
#include "mods/svc/log.h"
#include "hooks.hpp"
#include "item.hpp"
#include "session.hpp"
#include "ui/ui.hpp"
#include "hooks.hpp"
DEFINE_MOD();
IMPORT_SERVICE(HostService, svc_host);
@@ -14,6 +15,7 @@ IMPORT_SERVICE(ResourceService, svc_res);
IMPORT_SERVICE(ConfigService, svc_config);
IMPORT_SERVICE(SaveService, svc_save);
IMPORT_SERVICE(StageService, svc_stage);
IMPORT_SERVICE(ItemService, svc_item);
extern "C" {
@@ -28,6 +30,7 @@ MOD_EXPORT ModResult mod_initialize(ModError* error) {
svc_config,
svc_save,
svc_stage,
svc_item,
});
if (result != MOD_OK) {
return mods::set_error(error, result, "failed to initialize session");
@@ -49,10 +52,13 @@ MOD_EXPORT ModResult mod_initialize(ModError* error) {
MOD_EXPORT ModResult mod_update(ModError*) {
randomizer::ui::update();
randomizer::session::update();
return MOD_OK;
}
MOD_EXPORT ModResult mod_shutdown(ModError*) {
randomizer::session::deactivateSeed();
randomizer::hooks::uninstall();
svc_log->info(mod_ctx, "randomizer unloaded");
return MOD_OK;
}
+73 -46
View File
@@ -13,6 +13,7 @@
#include "../generator/randomizer.hpp"
#include "../generator/utility/text.hpp"
#include "../generator/utility/string.hpp"
#include "../generator/logic/entrance_shuffle.hpp"
#include <fstream>
#include <mods/svc/log.hpp>
@@ -136,11 +137,11 @@ std::optional<std::string> RandomizerContext::WriteToFile() {
}
for (const auto& [key, override] : mEntranceOverrides) {
out["mEntranceOverrides"][key] = std::bit_cast<int>(override);
out["mEntranceOverrides"][std::bit_cast<uint64_t>(key)] = std::bit_cast<uint64_t>(override);
}
for (const auto& [key, override] : mReturnToPlaceOverrides) {
out["mReturnToPlaceOverrides"][key] = std::bit_cast<int>(override);
out["mReturnToPlaceOverrides"][key] = std::bit_cast<uint64_t>(override);
}
seedData << YAML::Dump(out);
@@ -255,7 +256,7 @@ std::optional<std::string> RandomizerContext::LoadFromHash(const std::string& ha
// Items we call by location name
for (const auto& locationNode : in["mItemLocations"]) {
const auto& locationName = locationNode.first.as<std::string>();
const auto& locationName = nameLookupOverride(locationNode.first.as<std::string>());
retrieveItemData(this->mItemLocations[locationName], locationNode.second);
}
@@ -320,15 +321,15 @@ std::optional<std::string> RandomizerContext::LoadFromHash(const std::string& ha
// Entrance Overrides
for (const auto& entranceNode : in["mEntranceOverrides"]) {
auto key = entranceNode.first.as<int>();
auto override = std::bit_cast<EntranceOverride>(entranceNode.second.as<int>());
const auto key = std::bit_cast<EntranceOverride>(entranceNode.first.as<uint64_t>());;
const auto override = std::bit_cast<EntranceOverride>(entranceNode.second.as<uint64_t>());
this->mEntranceOverrides[key] = override;
}
// Return to Place Overrides
for (const auto& entranceNode : in["mReturnToPlaceOverrides"]) {
auto key = entranceNode.first.as<int>();
auto override = std::bit_cast<EntranceOverride>(entranceNode.second.as<int>());
const auto override = std::bit_cast<EntranceOverride>(entranceNode.second.as<uint64_t>());
this->mReturnToPlaceOverrides[key] = override;
}
@@ -857,21 +858,26 @@ std::vector<u8> HexToBytes(std::string hex) {
}
int randomizer_getItemAtLocation(const std::string& locationName) {
return randomizer_GetContext().mItemLocations[locationName].itemId;
return randomizer_GetContext().mItemLocations[nameLookupOverride(locationName)].itemId;
}
void randomizer_checkAndOverrideEntranceData(const char*& stageName, s8& roomNo, s16& pointNo, s8& mapLayer) {
RandomizerContext::EntranceOverride override = {
static_cast<u8>(getStageID(stageName)), roomNo, static_cast<s8>(pointNo), mapLayer
};
.stageId = static_cast<u8>(getStageID(stageName)), .roomNo = roomNo, .mapLayer = mapLayer, .pointNo = static_cast<s16>(pointNo)};
int key = std::bit_cast<int>(override);
if (randomizer_GetContext().mEntranceOverrides.contains(key)) {
auto& newOverride = randomizer_GetContext().mEntranceOverrides[key];
// Debugging: Dump mEntranceOverrides
// for (const auto& [key,val] : randomizer_GetContext().mEntranceOverrides) {
// mods::log::debug("({},{},{},{}) -> ({},{},{},{})",key.stageId,key.roomNo,key.mapLayer,key.pointNo,val.stageId,val.roomNo,val.mapLayer,val.pointNo);
// }
// mods::log::debug("Original Stage:{}, {}, {}, {}",stageName,roomNo,pointNo,mapLayer);
if (randomizer_GetContext().mEntranceOverrides.contains(override)) {
auto& newOverride = randomizer_GetContext().mEntranceOverrides[override];
stageName = allStages[newOverride.stageId];
pointNo = newOverride.pointNo;
roomNo = newOverride.roomNo;
mapLayer = newOverride.mapLayer;
// mods::log::debug("New Stage:{}, {}, {}, {}",stageName,roomNo,pointNo,mapLayer);
}
}
@@ -893,7 +899,7 @@ void randomizer_setTempFlag(RandomizerContext::itemLocationData data) {
}
void randomizer_setTempFlagForLocation(const std::string& locationName) {
randomizer_setTempFlag(randomizer_GetContext().mItemLocations[locationName]);
randomizer_setTempFlag(randomizer_GetContext().mItemLocations[nameLookupOverride(locationName)]);
}
void randomizer_setTempFlagForFLWOverride(u32 key) {
@@ -1357,7 +1363,7 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
// Items that we lookup just by calling their location name
if (location->HasCategories("Name Lookup")) {
for (const auto& locationNameNode : metaData["Name Lookup"]) {
const auto& locationName = locationNameNode.as<std::string>();
const auto& locationName = nameLookupOverride(locationNameNode.as<std::string>());
const int itemId = location->GetCurrentItem()->GetID();
randoData.mItemLocations[locationName].itemId = itemId;
getNodeFlags(randoData.mItemLocations[locationName], metaData);
@@ -1723,39 +1729,18 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
}
}
// Entrance Overrides
if (world->Setting("Mirror Chamber Access") == "Closed") {
// Set exiting the Arbiter's Grounds Boss Room to spawn at the Arbiter's Grounds entrance
// if mirror chamber access is closed
RandomizerContext::EntranceOverride original = {
StageIDs::Mirror_Chamber,
4,
0,
-1
};
RandomizerContext::EntranceOverride override = {
StageIDs::Bulblin_Camp,
3,
3,
-1
};
randoData.mEntranceOverrides[std::bit_cast<int>(original)] = override;
}
// Vanilla Return to Place Overrides. Will need to change when boss/miniboss ER is implemented
static const std::list<std::pair<std::list<int>, RandomizerContext::EntranceOverride>> defaultPlaceOverrides{
{{Forest_Temple, Ook, Diababa}, {Forest_Temple, 22, 0, -1}},
{{Goron_Mines, Dangoro, Fyrus}, {Goron_Mines, 1, 0, -1}},
{{Lakebed_Temple, Deku_Toad, Morpheel}, {Lakebed_Temple, 0, 0, -1}},
{{Arbiters_Grounds, Death_Sword, Stallord}, {Arbiters_Grounds, 0, 0, -1}},
{{Snowpeak_Ruins, Darkhammer, Blizzeta}, {Snowpeak_Ruins, 0, 0, -1}},
{{Temple_of_Time, Darknut, Armogohma}, {Temple_of_Time, 0, 0, -1}},
{{City_in_the_Sky, Aeralfos, Argorok}, {City_in_the_Sky, 0, 3, -1}},
static const std::vector<std::pair<std::vector<int>, RandomizerContext::EntranceOverride>> defaultPlaceOverrides{
{{Forest_Temple, Ook, Diababa}, {.stageId = Forest_Temple, .roomNo = 22, .mapLayer = -1, .pointNo = 0}},
{{Goron_Mines, Dangoro, Fyrus}, {.stageId = Goron_Mines, .roomNo = 1, .mapLayer = -1, .pointNo = 0}},
{{Lakebed_Temple, Deku_Toad, Morpheel}, {.stageId = Lakebed_Temple, .roomNo = 0, .mapLayer = -1, .pointNo = 0}},
{{Arbiters_Grounds, Death_Sword, Stallord}, {.stageId = Arbiters_Grounds, .roomNo = 0, .mapLayer = -1, .pointNo = 0}},
{{Snowpeak_Ruins, Darkhammer, Blizzeta}, {.stageId = Snowpeak_Ruins, .roomNo = 0, .mapLayer = -1, .pointNo = 0}},
{{Temple_of_Time, Darknut, Armogohma}, {.stageId = Temple_of_Time, .roomNo = 0, .mapLayer = -1, .pointNo = 0}},
{{City_in_the_Sky, Aeralfos, Argorok}, {.stageId = City_in_the_Sky, .roomNo = 0, .mapLayer = -1, .pointNo = 3}},
{{Palace_of_Twilight, Phantom_Zant_1,
Phantom_Zant_2, Zant_Main_Room, Zant_Fight}, {Palace_of_Twilight, 0, 0, -1}},
{{Hyrule_Castle, Ganondorf_Castle, Ganondorf_Field}, {Hyrule_Castle, 11, 0, -1}},
Phantom_Zant_2, Zant_Main_Room, Zant_Fight}, {.stageId = Palace_of_Twilight, .roomNo = 0, .mapLayer = -1, .pointNo = 0}},
{{Hyrule_Castle, Ganondorf_Castle, Ganondorf_Field}, {.stageId = Hyrule_Castle, .roomNo = 11, .mapLayer = -1, .pointNo = 0}},
};
// Return to Place Overrides
@@ -1765,6 +1750,48 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
}
}
// Apply entrance randomization
auto shuffleData = LOAD_EMBED_YAML(RANDO_DATA_PATH "entrance_shuffle_data.yaml");
if (world->AnyEntranceRandomizerEnabled()) {
for (const auto& entrance : world->GetShuffledEntrances()) {
randomizer::logic::entrance::Entrance* replacesEntrance = entrance->GetReplaces();
RandomizerContext::EntranceOverride forward = {.stageId = entrance->GetStageId(), .roomNo = entrance->GetRoomNo(), .mapLayer = entrance->GetLayerNo(), .pointNo = entrance->GetPointNo()};
RandomizerContext::EntranceOverride replaces = {.stageId = replacesEntrance->GetStageId(), .roomNo = replacesEntrance->GetRoomNo(), .mapLayer = replacesEntrance->GetLayerNo(), .pointNo = replacesEntrance->GetPointNo()};
if (forward.stageId == 0xFF || replaces.stageId == 0xFF || forward.roomNo == -1 || replaces.roomNo == -1) {
continue;
}
randoData.mEntranceOverrides[forward] = replaces;
// Set an override for the second door, if the entrance is coupled
if (entrance->getCoupledDoor() != -1) {
RandomizerContext::EntranceOverride coupled = {.stageId = entrance->GetStageId(), .roomNo = entrance->GetRoomNo(), .mapLayer = entrance->GetLayerNo(), .pointNo = entrance->getCoupledDoor()};
randoData.mEntranceOverrides[coupled] = replaces;
}
}
}
if (world->Setting("Mirror Chamber Access") == "Closed") {
// Set exiting the Arbiter's Grounds Boss Room to spawn at the Arbiter's Grounds entrance
// if mirror chamber access is closed
RandomizerContext::EntranceOverride original = {
.stageId = StageIDs::Mirror_Chamber,
.roomNo = 4,
.mapLayer = -1,
.pointNo = 0,
};
RandomizerContext::EntranceOverride override = {
.stageId = StageIDs::Bulblin_Camp,
.roomNo = 3,
.mapLayer = -1,
.pointNo = 3,
};
// Check if we are already overriding the bulblin camp entrance, and correctly override the entrance
const auto& it = randoData.mEntranceOverrides.find(override);
randoData.mEntranceOverrides[original] = (it != randoData.mEntranceOverrides.end()) ? it->second : override;
}
return std::move(randoData);
}
@@ -1807,4 +1834,4 @@ bool GenerateAndWriteSeed(std::string& generationStatusMsg) {
generationStatusMsg = fmt::format("Seed generated! Hash: {}", randoData.mHash);
return true;
}
}
+24 -12
View File
@@ -20,7 +20,7 @@
class RandomizerContext {
public:
static constexpr size_t ACTR_CRC_SIZE = 32;
static constexpr size_t TGSC_CRC_SIZE = 35; // 3 extra bytes for scale x, y, z
static constexpr size_t TGSC_CRC_SIZE = 35; // 3 extra bytes for scale x, y, z
static constexpr size_t OBJ_DELETE_SIZE = 1;
static constexpr u8 ROOM_STAGE = 0xFF;
@@ -37,7 +37,7 @@ public:
std::unordered_map<u8, std::list<u8>> mStartRegionFlags{};
std::list<u8> mStartingInventory{};
struct itemLocationData{
struct itemLocationData {
int itemId{0xFF};
int stage{0xFF};
u16 flag{0xFFFF};
@@ -50,7 +50,7 @@ public:
std::unordered_map<u16, u8> mSkyCharacterOverrides{};
std::unordered_map<u16, u8> mGoldenWolfOverrides{};
std::unordered_map<u16, u8> mShopOverrides{};
std::unordered_map<u16, u16> mTwilitInsectOverrides{}; // Just used in tracker for now
std::unordered_map<u16, u16> mTwilitInsectOverrides{}; // Just used in tracker for now
std::unordered_map<u32, itemLocationData> mFlowItemMessageOverrides{};
std::unordered_map<std::string, itemLocationData> mItemLocations{};
@@ -71,15 +71,26 @@ public:
// Map of language -> map of key -> string
std::unordered_map<int, std::unordered_map<u32, std::string>> mTextOverrides{};
// Padded to 8 bytes so bit_cast can convert it to a uint64_t
struct EntranceOverride {
u8 stageId = 0xFF;
s8 roomNo = -1;
s8 pointNo = -1;
s8 mapLayer = -1;
u8 _pad0;
s16 pointNo = -1;
u16 _pad1;
bool operator==(const EntranceOverride& rhs) const = default;
};
// keyed by stageId << 24 | pointNo << 16 | roomNo << 8 | mapLayer
std::unordered_map<int, EntranceOverride> mEntranceOverrides{};
// Packs an EntranceOverride into a uint64_t
struct EntranceOverrideHash {
size_t operator()(const EntranceOverride& x) const noexcept {
return std::hash<uint64_t>{}(std::bit_cast<uint64_t>(x));
}
};
std::unordered_map<EntranceOverride, EntranceOverride, EntranceOverrideHash>
mEntranceOverrides{};
// Overrides for returning to spawn. Keyed by stageId
std::unordered_map<int, EntranceOverride> mReturnToPlaceOverrides{};
@@ -153,7 +164,7 @@ public:
static constexpr u8 EVENT_ITEM_QUEUE_SIZE = 10;
RandomizerState() {mInitialized = false;}
RandomizerState() { mInitialized = false; }
int _create();
int _delete();
@@ -161,19 +172,19 @@ public:
int draw();
void addItemToEventQueue(u8 item);
void initGiveItemToPlayer();
//void handleBonkDamage();
// void handleBonkDamage();
void handleTimeOfDayChange();
void handleTimeSpeed();
void offLoad();
void handlePoeItem(u8 bitSw);
u8 getGiveItemToPlayerStatus() const { return mEventItemStatus;}
u8 getGiveItemToPlayerStatus() const { return mEventItemStatus; }
u8 getTimeChange() const { return mTimeChange; }
bool getRoomReloadingState() const { return mRoomReloadingState; }
bool getHasPendingToDChange() const { return mHasPendingToDChange; }
void setGiveItemToPlayerStatus(u8 status) { mEventItemStatus = status;}
void setGiveItemToPlayerStatus(u8 status) { mEventItemStatus = status; }
void setHasPendingToDChange(bool hasPending) { mHasPendingToDChange = hasPending; }
void setTimeChange(u8 newTimeChange) { mTimeChange = newTimeChange; }
void setRoomReloadingState(bool newState) { mRoomReloadingState = newState; }
@@ -217,7 +228,8 @@ int randomizer_getItemAtLocation(const std::string& locationName);
/*
* @brief Overrides the given entrance paramaters if an override exists for them
*/
void randomizer_checkAndOverrideEntranceData(const char*& i_Name, s8& i_RoomNo, s16& i_Point, s8& i_Layer);
void randomizer_checkAndOverrideEntranceData(
const char*& i_Name, s8& i_RoomNo, s16& i_Point, s8& i_Layer);
/*
* @brief Puts the associated flag into the randomizer state's temporary flag
* variable. This allows the tracker/Archipelago to know a location has been checked
@@ -285,4 +297,4 @@ u32 getStageObjCRC32(u8* data, size_t size);
*/
bool GenerateAndWriteSeed(std::string& generationStatusMsg);
#endif //DUSK_RANDOMIZER_CONTEXT_HPP
#endif // DUSK_RANDOMIZER_CONTEXT_HPP
+273 -10
View File
@@ -7,6 +7,8 @@
#include "item_ids.h"
#include "tools.h"
#include "stages.h"
#include "item.hpp"
#include "verify_item_functions.h"
#include "d/d_com_inf_game.h"
#include "d/d_item.h"
@@ -14,11 +16,149 @@
namespace randomizer::session {
ServiceManager svc_mng;
std::string g_pending_seed_hash{};
ModResult initialize(const ServiceManager& services) {
svc_mng = services;
SaveObserverHandle s_save_observer{};
ItemCheckHandle s_check_resolver{};
ItemGiveHandle s_check_observer{};
std::vector<StageActorHandle> s_stage_edits{};
return MOD_OK;
constexpr const char* kSeedHashBlobName = "seed_hash";
struct DerivedKey {
int stage_id;
u16 key;
};
std::optional<DerivedKey> parse_derived(const char* name, std::string_view prefix) {
if (std::strncmp(name, prefix.data(), prefix.size()) != 0) {
return std::nullopt;
}
const char* stage_begin = name + prefix.size();
const char* stage_end = std::strchr(stage_begin, ':');
if (stage_end == nullptr) {
return std::nullopt;
}
const std::string stage{stage_begin, stage_end};
const int stage_id = getStageID(stage.c_str());
if (stage_id < 0) {
return std::nullopt;
}
const int n = std::atoi(stage_end + 1);
return DerivedKey{stage_id, static_cast<u16>((stage_id << 8) | (n & 0xFF))};
}
template <typename Map>
bool lookup_override(const Map& map, u16 key, uint8_t* out_item, bool progressive) {
const auto it = map.find(key);
if (it == map.end()) {
return false;
}
*out_item = progressive ? static_cast<uint8_t>(verifyProgressiveItem(it->second)) : it->second;
return true;
}
bool resolve_check(ModContext*, const ItemCheckInfo* info, uint8_t* out_item, void*) {
auto& ctx = randomizer_GetContext();
if (auto it = ctx.mItemLocations.find(info->name); it != ctx.mItemLocations.end()) {
*out_item = static_cast<uint8_t>(it->second.itemId);
return true;
}
if (auto key = parse_derived(info->name, "chest:")) {
return lookup_override(ctx.mTreasureChestOverrides, key->key, out_item, false);
}
if (auto key = parse_derived(info->name, "freestanding:")) {
if (key->stage_id == Ook) {
if (auto it = ctx.mItemLocations.find("Forest Temple Gale Boomerang");
it != ctx.mItemLocations.end()) {
*out_item = static_cast<uint8_t>(verifyProgressiveItem(it->second.itemId));
return true;
}
return false;
}
return lookup_override(ctx.mFreestandingItemOverrides, key->key, out_item, true);
}
if (auto key = parse_derived(info->name, "poe:")) {
return lookup_override(ctx.mPoeOverrides, key->key, out_item, false);
}
if (auto key = parse_derived(info->name, "shop:")) {
return lookup_override(ctx.mShopOverrides, key->key, out_item, true);
}
if (auto key = parse_derived(info->name, "sky:")) {
return lookup_override(ctx.mSkyCharacterOverrides, key->key, out_item, true);
}
if (std::strncmp(info->name, "bug:", 4) == 0) {
const u8 insect = static_cast<u8>(std::atoi(info->name + 4));
if (auto it = ctx.mBugRewardOverrides.find(insect); it != ctx.mBugRewardOverrides.end()) {
*out_item = static_cast<uint8_t>(verifyProgressiveItem(it->second));
return true;
}
return false;
}
return false;
}
void observe_give(ModContext*, const ItemGiveInfo* info, void*) {
if (info->check_name == nullptr) {
return;
}
auto& ctx = randomizer_GetContext();
if (ctx.mItemLocations.contains(info->check_name)) {
randomizer_setTempFlagForLocation(info->check_name);
}
if (std::strcmp(info->check_name, "dungeon_reward:D_MN10") == 0) {
dComIfGs_onItem(0x9E, -1);
} else if (auto key = parse_derived(info->check_name, "freestanding:");
key && key->stage_id == Ook)
{
dComIfGs_onItem(0x9D, -1);
randomizer_setTempFlagForLocation("Forest Temple Gale Boomerang");
}
}
bool activateSeed(const char* hash) {
auto& ctx = randomizer_GetContext();
ctx = RandomizerContext();
if (auto err = ctx.LoadFromHash(hash); err.has_value() || ctx.mHash.empty()) {
mods::log::error("failed to load seed {}", hash);
return false;
}
item::apply_item_data_tables();
svc_mng.item->set_check_resolver(mod_ctx, nullptr, resolve_check, nullptr, &s_check_resolver);
svc_mng.item->observe_gives(mod_ctx, observe_give, nullptr, &s_check_observer);
registerStageEdits();
mods::log::info("activated seed {}", ctx.mHash);
return true;
}
void deactivateSeed() {
if (s_check_resolver != 0) {
svc_mng.item->clear_check_resolver(mod_ctx, s_check_resolver);
s_check_resolver = 0;
}
if (s_check_observer != 0) {
svc_mng.item->unobserve_gives(mod_ctx, s_check_observer);
s_check_observer = 0;
}
for (auto handle : s_stage_edits) {
svc_mng.stage->remove_actor_edit(mod_ctx, handle);
}
s_stage_edits.clear();
item::restore_item_data_tables();
randomizer_GetContext() = RandomizerContext{};
g_randomizerState = RandomizerState{};
}
void setupRandomizerFile() {
@@ -46,22 +186,22 @@ void setupRandomizerFile() {
{
dComIfGs_onDarkClearLV(0);
dComIfGs_setLightDropNum(0, 0x10);
execItemGet(dItemNo_Randomizer_DROP_CONTAINER_e);
execItemGet(dItemNo_Randomizer_WEAR_KOKIRI_e);
item::exec_item_get(dItemNo_Randomizer_DROP_CONTAINER_e);
item::exec_item_get(dItemNo_Randomizer_WEAR_KOKIRI_e);
}
if (dComIfGs_isEventBit(CLEARED_ELDIN_TWILIGHT))
{
dComIfGs_onDarkClearLV(1);
dComIfGs_setLightDropNum(1, 0x10);
execItemGet(dItemNo_Randomizer_DROP_CONTAINER02_e);
item::exec_item_get(dItemNo_Randomizer_DROP_CONTAINER02_e);
}
if (dComIfGs_isEventBit(CLEARED_LANAYRU_TWILIGHT))
{
dComIfGs_onDarkClearLV(2);
dComIfGs_setLightDropNum(2, 0x10);
execItemGet(dItemNo_Randomizer_DROP_CONTAINER03_e);
item::exec_item_get(dItemNo_Randomizer_DROP_CONTAINER03_e);
}
if (randoData.mSettings[RandomizerContext::SKIP_MINOR_CUTSCENES] == RandomizerContext::ON)
@@ -90,7 +230,7 @@ void setupRandomizerFile() {
// Set starting inventory
for (const auto& itemId: randoData.mStartingInventory) {
execItemGet(itemId);
item::exec_item_get(itemId);
}
g_randomizerState = RandomizerState();
@@ -126,6 +266,10 @@ void registerStageEdits() {
res = svc_mng.stage->patch_actor(
mod_ctx, stage, room, layer, crc, bytes.data(), bytes.size(), &handle);
}
if (res == MOD_OK) {
s_stage_edits.push_back(handle);
}
}
}
@@ -139,9 +283,128 @@ void registerStageEdits() {
const s8 layer = static_cast<s8>(key & 0xFF);
for (const auto& bytes : additions) {
StageActorHandle handle{};
svc_mng.stage->add_actor(mod_ctx, stage, room, layer, bytes.data(), bytes.size(), &handle);
ModResult rt;
rt = svc_mng.stage->add_actor(mod_ctx, stage, room, layer, bytes.data(), bytes.size(), &handle);
if (rt == MOD_OK) {
s_stage_edits.push_back(handle);
}
}
}
}
}
// Set link to spawn outside of his house if we are playing with entrance rando and aren't already spawning in a dungeon
void registerStartingLocation() {
auto& ctx = randomizer_GetContext();
if (ctx.mEntranceOverrides.empty()) {
return;
}
// Check that we aren't playing only with the setting "Mirror Chamber Access") == "Closed"
bool isExclusivelyMirrorChamber = false;
const auto& mirrorChamberIt = ctx.mEntranceOverrides.find(RandomizerContext::EntranceOverride{
.stageId = StageIDs::Mirror_Chamber,
.roomNo = 4,
.mapLayer = -1,
.pointNo = 0
});
if (ctx.mEntranceOverrides.size() == 1 && mirrorChamberIt != ctx.mEntranceOverrides.end()) {
if (mirrorChamberIt->second == RandomizerContext::EntranceOverride{
.stageId = StageIDs::Bulblin_Camp,
.roomNo = 3,
.mapLayer = -1,
.pointNo = 3,
}) {
isExclusivelyMirrorChamber = true;
}
}
if (isExclusivelyMirrorChamber) {
return;
}
constexpr std::array<std::string_view,9> kDungeonNames = {
"D_MN01","D_MN04","D_MN05","D_MN06","D_MN07","D_MN08","D_MN09","D_MN10","D_MN11"
};
dSv_player_return_place_c& returnPlace = dComIfGs_getSaveData()->mPlayer.getPlayerReturnPlace();
bool isDungeon = false;
for (const auto& name : kDungeonNames) {
if (returnPlace.getName() == name) {
isDungeon = true;
break;
}
}
if (isDungeon) {
// Force-set the entrance without the override
g_dComIfG_gameInfo.play.mNextStage.getStartStage()->set(returnPlace.getName(), returnPlace.getRoomNo(), returnPlace.mPlayerStatus, -1);
return;
}
// Set the spawn to outside of link's house, which will get overrided later if we are replacing it
returnPlace.set("F_SP103",1,1);
// g_dComIfG_gameInfo.play.mNextStage.getStartStage()->set("F_SP103", 1, 1, -1);
dComIfGp_setNextStage("F_SP103", 1, 1, -1);
}
void onNewSave(ModContext*, uint32_t, void*) {
const std::string hash = g_pending_seed_hash;
if (hash.empty())
return;
if (!activateSeed(hash.c_str()))
return;
svc_mng.save->set_blob(svc_mng.mod_ctx, kSeedHashBlobName, hash.data(), hash.size());
setupRandomizerFile();
}
void onSaveLoaded(ModContext*, uint32_t, void*) {
size_t size = 0;
if (svc_mng.save->get_blob(mod_ctx, kSeedHashBlobName, nullptr, &size) != MOD_OK || size == 0) {
mods::log::error("seed_hash not found!");
deactivateSeed();
return;
}
std::string hash(size, '\0');
if (svc_mng.save->get_blob(mod_ctx, kSeedHashBlobName, hash.data(), &size) != MOD_OK) {
mods::log::error("failed to get seed_hash!");
deactivateSeed();
return;
}
if (randomizer_GetContext().mHash != hash) {
deactivateSeed();
activateSeed(hash.c_str());
}
loadAncientDocumentNum();
}
ModResult initialize(const ServiceManager& services) {
svc_mng = services;
ModResult rt = svc_mng.save->observe_saves(
svc_mng.mod_ctx,
onNewSave,
onSaveLoaded,
nullptr,
nullptr,
&s_save_observer);
if (rt != MOD_OK) {
return rt;
}
return MOD_OK;
}
void update() {
if (!g_randomizerState.mInitialized) {
g_randomizerState._create();
}
g_randomizerState.execute();
}
}
+9 -1
View File
@@ -8,6 +8,9 @@
#include "mods/svc/resource.h"
#include "mods/svc/save.h"
#include "mods/svc/stage.h"
#include "mods/svc/item.h"
#include <string>
namespace randomizer::session {
struct ServiceManager {
@@ -20,12 +23,17 @@ struct ServiceManager {
const ConfigService* config;
const SaveService* save;
const StageService* stage;
const ItemService* item;
};
extern ServiceManager svc_mng;
extern std::string g_pending_seed_hash;
ModResult initialize(const ServiceManager& services);
void update();
void deactivateSeed();
void setupRandomizerFile();
void registerStageEdits();
}
void registerStartingLocation();
}
+50 -1
View File
@@ -501,6 +501,55 @@ randomizer::logic::item_pool::ItemPool getSaveItemPool(randomizer::logic::world:
return pool;
}
// overriding location names from generator to match item service api format
std::string nameLookupOverride(const std::string& locationName) {
// [generator name], [service name]
static std::unordered_map<std::string, std::string> nameLookup = {
{"Arbiters Grounds Dungeon Reward", "dungeon_reward:D_MN10"},
{"Ashei Sketch", "ashei_sketch"},
{"Auru Gift To Fyer", "auru_memo"},
{"Cave of Ordeals Great Fairy Reward", "fairy_reward:D_SB01"},
{"Charlo Donation Blessing", "prayer_reward"},
{"Coro Bottle", "coro_bottle"},
{"Gift From Ralis", "coral_earring"},
{"Goron Mines Gor Amato Key Shard", "key_shard_1:D_MN04"},
{"Goron Mines Gor Ebizo Key Shard", "key_shard_2:D_MN04"},
{"Goron Mines Gor Liggs Key Shard", "key_shard_3:D_MN04"},
{"Herding Goats Reward", "goats_reward"},
{"Hyrule Castle King Bulblin Key", "bulblin_key:D_MN09"},
{"Ilia Charm", "ilia_charm"},
{"Iza Helping Hand", "iza_reward_1"},
{"Iza Raging Rapids Minigame", "iza_reward_2"},
{"Jovani 20 Poe Soul Reward", "jovani_reward_1"},
{"Jovani 60 Poe Soul Reward", "jovani_reward_2"},
{"Ordon Cat Rescue", "sera_reward"},
{"Ordon Shield", "ordon_shield"},
{"Ordon Sword", "ordon_sword"},
{"Plumm Fruit Balloon Minigame", "plumm_minigame_reward"},
{"Renados Letter", "renado_letter"},
{"Rutelas Blessing", "zora_armor"},
{"Skybook From Impaz", "skybook"},
{"Snowboard Racing Prize", "snowboard_race_reward"},
{"Snowpeak Ruins Ball and Chain", "ball_and_chain:D_MN11"},
{"Snowpeak Ruins Mansion Map", "dungeon_map:D_MN11"},
{"STAR Prize 1", "star_reward_1"},
{"STAR Prize 2", "star_reward_2"},
{"Talo Sharpshooting", "archery_reward:F_SP109"},
{"Telma Invoice", "telma_invoice"},
{"Uli Cradle Delivery", "uli_cradle_reward"},
{"Wooden Statue", "wood_statue"},
{"Zoras Domain Underwater Goron", "goron_reward:F_SP113"},
};
for (auto& [oldName, newName] : nameLookup) {
if (locationName == oldName) {
return newName;
}
}
return locationName;
}
bool isLocationObtained(randomizer::logic::location::Location* location) {
auto& locationMeta = location->GetMetadata();
if (auto& chestNode = locationMeta["Chest"]) {
@@ -604,7 +653,7 @@ int getLocationItem(randomizer::logic::location::Location* location) {
return context.mFlowItemMessageOverrides[key].itemId;
}
if (auto& nameNode = locationMeta["Name Lookup"]) {
auto name = nameNode[0].as<std::string>();
auto name = nameLookupOverride(nameNode[0].as<std::string>());
return context.mItemLocations[name].itemId;
}
return -1;
+2
View File
@@ -29,6 +29,8 @@ int numFusedShadows();
int numMirrorShards();
int getTempleKeysFound(int saveId);
std::string nameLookupOverride(const std::string& locationName);
/*
* Reads the current player inventory and returns an ItemPool that can be used for logic searches
*
+74 -19
View File
@@ -273,18 +273,58 @@ ModResult buildSeedManagementTab(ModContext* ctx, UiWindowHandle, UiElementHandl
SaveRandomizerConfig();
});
add_button(leftPane,
"Delete Seeds",
"Delete any seed not currently being used.",
[](ModContext*, void*) {
// TODO
});
{
std::filesystem::path seed_dir = paths::GetRandomizerSeedsPath();
if (!std::filesystem::exists(seed_dir))
std::filesystem::create_directory(seed_dir);
std::string help_rml = "Delete any seed not currently being used.";
std::vector<std::string> seedHashes;
for (const auto& entry : std::filesystem::directory_iterator(seed_dir)) {
if (entry.is_directory()) {
seedHashes.push_back(entry.path().filename().string());
}
}
std::vector<const char*> availableSeeds;
for (const auto& hash : seedHashes) {
availableSeeds.push_back(hash.c_str());
}
add_select(leftPane,
"Delete Seeds",
help_rml.c_str(),
availableSeeds.data(),
availableSeeds.size(),
[](ModContext*, void*, UiControlValue* out_value) {
out_value->int_value = 0;
},
[](ModContext*, void*, const UiControlValue* value) {
int idx = 0;
for (const auto& entry : std::filesystem::directory_iterator(paths::GetRandomizerSeedsPath())) {
if (entry.is_directory()) {
if (idx == value->int_value) {
std::string hash = entry.path().filename().string();
if (randomizer_GetContext().mHash == hash) {
randomizer_GetContext() = RandomizerContext{};
}
std::filesystem::remove_all(entry);
break;
}
idx++;
}
}
});
}
add_section(leftPane, "Permalink");
{
std::string help_rml = "Copy your current settings permalink to share with others.";
help_rml += fmt::format("<br/>Current Permalink: {}", GetRandomizerConfig().GetPermalink());
std::string help_rml = "Copy your current settings permalink to share with others.<br/>";
help_rml += fmt::format(
"<br/>Current Permalink:<br/><span style=\"word-break: break-all;\">{}</span>",
GetRandomizerConfig().GetPermalink());
add_button(leftPane,
"Copy Permalink",
help_rml.c_str(),
@@ -380,6 +420,17 @@ ModResult buildSeedOptionsTab(ModContext* ctx, UiWindowHandle, UiElementHandle l
add_select_setting(leftPane, "Lower Hyrule Castle Chandelier");
add_select_setting(leftPane, "Skip Bridge Donation");
add_section(leftPane, "Entrance Randomizer Settings");
add_select_setting(leftPane, "Randomize Starting Spawn");
add_select_setting(leftPane, "Randomize Dungeon Entrances");
add_select_setting(leftPane, "Randomize Boss Entrances");
add_select_setting(leftPane, "Randomize Grotto Entrances");
add_select_setting(leftPane, "Randomize Cave Entrances");
add_select_setting(leftPane, "Randomize Interior Entrances");
add_select_setting(leftPane, "Randomize Overworld Entrances");
add_select_setting(leftPane, "Decouple Double Door Entrances");
add_select_setting(leftPane, "Decouple Entrances");
add_section(leftPane, "Additional Settings");
add_select_setting(leftPane, "Starting Time of Day");
add_select_setting(leftPane, "Logic Transform Anywhere");
@@ -513,7 +564,7 @@ ModResult buildPlayTab(ModContext* ctx, UiWindowHandle, UiElementHandle leftPane
for (const auto& entry : std::filesystem::directory_iterator(paths::GetRandomizerSeedsPath())) {
if (entry.is_directory()) {
std::string hash = entry.path().filename().string();
if (randomizer_GetContext().mHash == hash) {
if (session::g_pending_seed_hash == hash) {
break;
}
idx++;
@@ -527,10 +578,7 @@ ModResult buildPlayTab(ModContext* ctx, UiWindowHandle, UiElementHandle leftPane
for (const auto& entry : std::filesystem::directory_iterator(paths::GetRandomizerSeedsPath())) {
if (entry.is_directory()) {
if (idx == value->int_value) {
std::string hash = entry.path().filename().string();
randomizer_GetContext() = RandomizerContext();
randomizer_GetContext().LoadFromHash(hash);
session::registerStageEdits();
session::g_pending_seed_hash = entry.path().filename().string();
break;
}
idx++;
@@ -538,15 +586,22 @@ ModResult buildPlayTab(ModContext* ctx, UiWindowHandle, UiElementHandle leftPane
}
});
add_button(leftPane,
"Start Randomizer",
"",
[](ModContext*, void* userdata) {
{
UiControlDesc desc = UI_CONTROL_DESC_INIT;
desc.kind = UI_CONTROL_BUTTON;
desc.label = "Start Randomizer";
desc.help_rml = "";
desc.on_pressed = [](ModContext*, void* userdata) {
// set flag to move to name screen after window close
g_file_select_window_ctx.is_proceed = true;
session::svc_mng.ui->window_close(session::svc_mng.mod_ctx, *static_cast<UiWindowHandle*>(userdata));
},
&g_file_select_window_ctx.window_handle);
};
desc.user_data = &g_file_select_window_ctx.window_handle;
desc.is_disabled = [](ModContext*, void*) {
return session::g_pending_seed_hash.empty();
};
session::svc_mng.ui->pane_add_control(session::svc_mng.mod_ctx, leftPane, &desc, nullptr);
}
return MOD_OK;
}
+1 -1
View File
@@ -16,7 +16,7 @@
* of letting them corrupt memory.
*/
#define GAME_SERVICE_ID "dev.twilitrealm.dusklight.game"
#define GAME_SERVICE_MAJOR 1u
#define GAME_SERVICE_MAJOR 2u
#define GAME_SERVICE_MINOR 0u
typedef struct GameService {
+94
View File
@@ -0,0 +1,94 @@
#pragma once
#include <mods/api.h>
#ifdef __cplusplus
#include <mods/service.hpp>
#endif
#define ITEM_SERVICE_ID "dev.twilitrealm.dusklight.item"
#define ITEM_SERVICE_MAJOR 2u
#define ITEM_SERVICE_MINOR 0u
/* 0 is never a valid handle. */
typedef uint64_t ItemCheckHandle;
typedef uint64_t ItemGiveHandle;
/*
* Item check resolution and inventory grants.
*
* Check names are case-sensitive. Resolvers must be free of side effects because the game may
* resolve a check more than once, including once for display and again when granting the item.
* Registrations and pending grants are removed when the calling mod is detached. Callbacks run
* on the game thread.
*/
/* Host-owned callback data, valid only for the duration of the callback. */
typedef struct ItemCheckInfo {
const char* name;
const void* giver_actor; /* fopAc_ac_c*, or NULL when unavailable */
uint8_t vanilla_item;
uint8_t current_item;
} ItemCheckInfo;
/* Return true and write out_item to replace current_item, or false to leave it unchanged. */
typedef bool (*ItemCheckResolveFn)(
ModContext* ctx, const ItemCheckInfo* info, uint8_t* out_item, void* user_data);
typedef enum ItemGiveOrigin {
ITEM_GIVE_ORIGIN_GAME = 0,
ITEM_GIVE_ORIGIN_QUEUE = 1,
ITEM_GIVE_ORIGIN_QUEUE_SILENT = 2,
} ItemGiveOrigin;
/* Host-owned callback data, valid only for the duration of the callback. */
typedef struct ItemGiveInfo {
const char* check_name; /* NULL when the grant is not attributed to a check */
const void* giver_actor; /* fopAc_ac_c*, or NULL when unavailable */
uint8_t item;
uint8_t origin; /* ItemGiveOrigin */
} ItemGiveInfo;
typedef void (*ItemGiveObserveFn)(ModContext* ctx, const ItemGiveInfo* info, void* user_data);
enum {
/* Apply the inventory change without a get-item demo. */
ITEM_GIVE_SILENT = 1u << 0,
/* Resolve item_no as the named check's vanilla item when the queue entry is dispatched. */
ITEM_GIVE_RESOLVE = 1u << 1,
};
typedef struct ItemService {
ServiceHeader header;
/* Set or replace the calling mod's fixed override for name. */
ModResult (*set_check_override)(ModContext* ctx, const char* name, uint8_t item_no);
ModResult (*clear_check_override)(ModContext* ctx, const char* name);
/* Register for name, or for every check when name is NULL. out_handle may be NULL. */
ModResult (*set_check_resolver)(ModContext* ctx, const char* name, ItemCheckResolveFn fn,
void* user_data, ItemCheckHandle* out_handle);
ModResult (*clear_check_resolver)(ModContext* ctx, ItemCheckHandle handle);
/* Resolve without granting an item or notifying give observers. */
ModResult (*resolve_check)(
ModContext* ctx, const char* name, uint8_t vanilla_item, uint8_t* out_item);
/*
* Add a grant to the global FIFO. check_name may be NULL unless ITEM_GIVE_RESOLVE is set.
* Returns MOD_UNAVAILABLE when the queue is full. Entries wait until gameplay is in a safe
* state and are cleared when the active save slot changes.
*/
ModResult (*give_item)(
ModContext* ctx, const char* check_name, uint8_t item_no, uint32_t flags);
/* Observe inventory grants. out_handle may be NULL. */
ModResult (*observe_gives)(
ModContext* ctx, ItemGiveObserveFn fn, void* user_data, ItemGiveHandle* out_handle);
ModResult (*unobserve_gives)(ModContext* ctx, ItemGiveHandle handle);
} ItemService;
MOD_DECLARE_SERVICE(ItemService, svc_item, ITEM_SERVICE_ID, ITEM_SERVICE_MAJOR, ITEM_SERVICE_MINOR);
+7 -1
View File
@@ -2260,6 +2260,12 @@ int daAlink_c::procCoGetItemInit() {
s16 var_r22 = 0;
BOOL var_r31 = FALSE;
BOOL var_r30 = FALSE;
#if TARGET_PC
// Queued grants have no event partner, so create the demo item from the event's GtItm.
if (dusk::mods::item_give_queue_dispatching()) {
mDemo.setParam0(0x100);
}
#endif
if (mProcID == PROC_GET_ITEM || mProcID == PROC_INSECT_CATCH ||
(mProcID == PROC_PREACTION_UNEQUIP && !checkNoUpperAnime()))
{
@@ -2299,7 +2305,7 @@ int daAlink_c::procCoGetItemInit() {
}
fpc_ProcID item_partner_id = fopAcM_createItemForPresentDemo(&current.pos, item_no, 0, -1,
fopAcM_GetRoomNo(this), NULL, NULL);
fopAcM_GetRoomNo(this), NULL, NULL IF_DUSK_ARG(dusk::mods::item_give_queue_take_tag()));
if (item_partner_id != fpcM_ERROR_PROCESS_ID_e) {
dComIfGp_event_setItemPartnerId(item_partner_id);
}
+4
View File
@@ -4084,6 +4084,10 @@ void daB_DS_c::executeBattle2Dead() {
camera->mCamera.SetTrimSize(0);
dComIfGp_event_reset();
dComIfGs_onStageBossEnemy(0x13);
#if TARGET_PC
// This reward has no original grant at this point in the cutscene.
dusk::mods::item_check_enqueue("dungeon_reward:D_MN10", dItemNo_NONE_e);
#endif
/* dSv_event_flag_c::F_0265 - Arbiter's Grounds - Arbiter's Grounds clear */
dComIfGs_onEventBit(0x2010);
fopAcM_delete(this);
+1 -1
View File
@@ -170,7 +170,7 @@ void daDitem_c::actionEvent() {
if (chkDead()) {
if (!chkArgFlag(0x1)) {
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
}
if (m_itemNo == dItemNo_KAKERA_HEART_e) {
+12 -5
View File
@@ -726,11 +726,18 @@ void daE_HP_c::executeDead() {
fopAcM_onSwitch(this, bitSw);
}
dComIfGs_addPohSpiritNum();
#if TARGET_PC
mItemCheckOverridden =
dusk::mods::item_check_poe(bitSw, dItemNo_POU_SPIRIT_e, this) != dItemNo_POU_SPIRIT_e;
if (mItemCheckOverridden) {
dusk::mods::item_check_enqueue_poe(bitSw, dItemNo_POU_SPIRIT_e);
} else
#endif
dComIfGs_addPohSpiritNum();
field_0x784 = -1;
if (dComIfGs_getPohSpiritNum() == 20) {
if (dComIfGs_getPohSpiritNum() == 20 IF_DUSK(&&!mItemCheckOverridden)) {
dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[0x1c9]);
}
@@ -752,13 +759,13 @@ void daE_HP_c::executeDead() {
field_0x788 = 1;
}
}
} else if (field_0x788 != 0) {
} else if (field_0x788 != 0 IF_DUSK(|| mItemCheckOverridden)) {
fopAcM_createDisappear(this, &current.pos, 8, 3, 0xff);
fopAcM_delete(this);
} else {
if (field_0x784 == -1) {
field_0x784 = fopAcM_createItemForPresentDemo(&current.pos, dItemNo_POU_SPIRIT_e, 0, -1,
-1, 0, 0);
field_0x784 = fopAcM_createItemForPresentDemo(&current.pos, dItemNo_POU_SPIRIT_e, 0,
-1, -1, 0, 0 IF_DUSK_ARG(dusk::mods::item_give_tag_poe(bitSw)));
}
if (fopAcM_IsExecuting(field_0x784) != FALSE) {
+16 -1
View File
@@ -1132,12 +1132,20 @@ static void e_po_dead(e_po_class* i_this) {
camera_player->mCamera.Start();
camera_player->mCamera.SetTrimSize(0);
dComIfGp_event_reset();
dComIfGs_addPohSpiritNum();
#if TARGET_PC
if (dusk::mods::item_check_poe(i_this->BitSW, dItemNo_POU_SPIRIT_e, a_this) ==
dItemNo_POU_SPIRIT_e)
{
#endif
dComIfGs_addPohSpiritNum();
#if !PLATFORM_SHIELD
if (dComIfGs_getPohSpiritNum() == 0x14) {
/* dSv_event_flag_c::F_0457 - Castle Town - Revived cat */
dComIfGs_onEventBit(dSv_event_flag_c::saveBitLabels[457]);
}
#endif
#if TARGET_PC
}
#endif
daPy_getPlayerActorClass()->cancelOriginalDemo();
} else if (mArg0Check(i_this, 0) != 0) {
@@ -1265,8 +1273,15 @@ static void e_po_dead(e_po_class* i_this) {
}
} else {
if (i_this->field_0x75C == -1) {
#if TARGET_PC
const u8 itemNo =
dusk::mods::item_check_poe(i_this->BitSW, dItemNo_POU_SPIRIT_e, a_this);
i_this->field_0x75C = fopAcM_createItemForPresentDemo(&a_this->current.pos, itemNo,
0, -1, -1, NULL, NULL, dusk::mods::item_give_tag_poe(i_this->BitSW));
#else
i_this->field_0x75C = fopAcM_createItemForPresentDemo(&a_this->current.pos, 0xE0, 0,
-1, -1, NULL, NULL);
#endif
}
if (fopAcM_IsExecuting(i_this->field_0x75C)) {
i_this->field_0x762 =
+3 -1
View File
@@ -1250,7 +1250,9 @@ static void demo_camera(e_rdb_class* i_this) {
}
if (iVar1 != 0) {
daPy_getPlayerActorClass()->changeDemoMode(11, 32, 0, 0);
daPy_getPlayerActorClass()->changeDemoMode(11,
DUSK_ITEM_CHECK_EXPR("bulblin_key:D_MN09", dItemNo_SMALL_KEY_e, &i_this->enemy), 0,
0);
i_this->mDemoMode = 12;
i_this->field_0x10aa = 0;
i_this->field_0xfe5 = 1;
+3 -1
View File
@@ -931,7 +931,9 @@ static void get_demo(e_th_ball_class* i_this) {
case 0:
break;
case 1:
demo_id = fopAcM_createItemForTrBoxDemo(&i_this->current.pos, dItemNo_IRONBALL_e, -1, fopAcM_GetRoomNo(i_this), NULL, NULL);
demo_id = fopAcM_createItemForTrBoxDemo(&i_this->current.pos,
DUSK_ITEM_CHECK_EXPR("ball_and_chain:D_MN11", dItemNo_IRONBALL_e, i_this), -1,
fopAcM_GetRoomNo(i_this), NULL, NULL DUSK_GIVE_TAG("ball_and_chain:D_MN11"));
JUT_ASSERT(1670, demo_id != fpcM_ERROR_PROCESS_ID_e);
i_this->mDemoMode = 2;
break;
+3 -1
View File
@@ -1744,7 +1744,9 @@ int daNpc_Aru_c::cutSpeakTo(int i_staffID) {
switch (eventId) {
case 1:
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("goats_reward", itemNo, this);
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, itemNo,
0, -1, -1, NULL, NULL DUSK_GIVE_TAG("goats_reward"));
}
if (fopAcM_IsExecuting(mItemPartnerId)) {
+3 -2
View File
@@ -1001,8 +1001,9 @@ BOOL daNpcAshB_c::EvCut_Appear(int i_staffID) {
case '0008':
local_30[0] = 0;
if (mFlow.getEventId(local_30) == 1) {
mItemPartnerId =
fopAcM_createItemForPresentDemo(&current.pos, local_30[0], 0, -1, -1, 0, 0);
DUSK_ITEM_CHECK("ashei_sketch", local_30[0], this);
mItemPartnerId = fopAcM_createItemForPresentDemo(
&current.pos, local_30[0], 0, -1, -1, 0, 0 DUSK_GIVE_TAG("ashei_sketch"));
dComIfGp_event_setItemPartnerId(mItemPartnerId);
mItemPartnerId = -1;
}
+12 -1
View File
@@ -1804,8 +1804,19 @@ int daNpcChin_c::_Evt_GameSucceed_CutMain(const int& param_0) {
itemId1 = 0;
}
#if TARGET_PC
const char* itemCheckName = nullptr;
if (itemId1 == dItemNo_ARROW_LV2_e) {
itemCheckName = "star_reward_1";
} else if (itemId1 == dItemNo_ARROW_LV3_e) {
itemCheckName = "star_reward_2";
}
if (itemCheckName != nullptr) {
itemId1 = dusk::mods::item_check(itemCheckName, itemId1, this);
}
#endif
fpc_ProcID itemId2 = fopAcM_createItemForPresentDemo(&current.pos, itemId1, 0, -1, -1,
0, 0);
0, 0 IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
if (itemId2 != -1) {
dComIfGp_event_setItemPartnerId(itemId2);
}
+3 -1
View File
@@ -1333,7 +1333,9 @@ void daNpc_Fairy_c::PresentDemoCall() {
item_no = 0;
}
fpc_ProcID id = fopAcM_createItemForPresentDemo(&current.pos, item_no, 0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("fairy_reward:D_SB01", item_no, this);
fpc_ProcID id = fopAcM_createItemForPresentDemo(&current.pos, item_no, 0, -1, -1, NULL,
NULL DUSK_GIVE_TAG("fairy_reward:D_SB01"));
if (id != fpcM_ERROR_PROCESS_ID_e) {
dComIfGp_event_setItemPartnerId(id);
}
+9 -2
View File
@@ -4018,8 +4018,15 @@ BOOL daNpc_grA_c::talk(void*) {
}
if (r26 && talkProc(NULL, TRUE, NULL)) {
if (mFlow.getEventId(&sp8) == 1) {
field_0x1480 =
fopAcM_createItemForPresentDemo(&current.pos, sp8, 0, -1, -1, NULL, NULL);
#if TARGET_PC
const char* itemCheckName = nullptr;
if (sp8 == dItemNo_BOMB_IN_BAG_e) {
itemCheckName = "goron_reward:F_SP113";
sp8 = dusk::mods::item_check(itemCheckName, sp8, this);
}
#endif
field_0x1480 = fopAcM_createItemForPresentDemo(&current.pos, sp8, 0, -1, -1, NULL,
NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
if (field_0x1480 != fpcM_ERROR_PROCESS_ID_e) {
s16 r25 = dComIfGp_getEventManager().getEventIdx(this, "DEFAULT_GETITEM", 0xff);
dComIfGp_getEvent()->reset(this);
+3 -1
View File
@@ -1681,7 +1681,9 @@ int daNpc_grO_c::talk(void* param_1) {
if (facePlayerFlag && talkProc(NULL, TRUE, NULL)) {
if (mType == TYPE_MINES) {
if (mFlow.getEventId(&itemId) == 1) {
mItemID = fopAcM_createItemForPresentDemo(&current.pos, itemId, 0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("key_shard_2:D_MN04", itemId, this);
mItemID = fopAcM_createItemForPresentDemo(&current.pos, itemId, 0, -1, -1,
NULL, NULL DUSK_GIVE_TAG("key_shard_2:D_MN04"));
if (mItemID != fpcM_ERROR_PROCESS_ID_e) {
s16 eventIdx = dComIfGp_getEventManager().getEventIdx(this, "DEFAULT_GETITEM", 0xFF);
dComIfGp_getEvent()->reset(this);
+3 -1
View File
@@ -1338,7 +1338,9 @@ int daNpc_grR_c::talk(void* param_1) {
if (bVar1 && talkProc(NULL, TRUE, NULL)) {
if (mType == TYPE_0) {
if (mFlow.getEventId(&i_itemNo) == 1) {
mItemID = fopAcM_createItemForPresentDemo(&current.pos, i_itemNo, 0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("key_shard_3:D_MN04", i_itemNo, this);
mItemID = fopAcM_createItemForPresentDemo(&current.pos, i_itemNo, 0, -1, -1,
NULL, NULL DUSK_GIVE_TAG("key_shard_3:D_MN04"));
if (mItemID != fpcM_ERROR_PROCESS_ID_e) {
s16 i_eventID = dComIfGp_getEventManager().getEventIdx(this, "DEFAULT_GETITEM", 0xFF);
+3 -2
View File
@@ -1190,8 +1190,9 @@ int daNpc_grS_c::talk(void* param_0) {
if (unkFlag1 && talkProc(NULL, 1, NULL)) {
if (mType == 0) {
if (mFlow.getEventId(&unkInt2) == 1) {
mPresentItemId =
fopAcM_createItemForPresentDemo(&current.pos, unkInt2, 0, -1, -1, 0, 0);
DUSK_ITEM_CHECK("key_shard_1:D_MN04", unkInt2, this);
mPresentItemId = fopAcM_createItemForPresentDemo(&current.pos, unkInt2, 0, -1,
-1, 0, 0 DUSK_GIVE_TAG("key_shard_1:D_MN04"));
if (mPresentItemId != fpcM_ERROR_PROCESS_ID_e) {
s16 eventIdx =
+6 -4
View File
@@ -971,8 +971,9 @@ BOOL daNpcImpal_c::EvCut_ImpalAppear1(int i_cut_index) {
if (talkProc(NULL, 1, NULL)) {
int evt_id = 0;
if (mFlow.getEventId(&evt_id) == 1) {
mItemPartnerId =
fopAcM_createItemForPresentDemo(&current.pos, evt_id, 0, -1, -1, 0, 0);
DUSK_ITEM_CHECK("ilia_charm", evt_id, this);
mItemPartnerId = fopAcM_createItemForPresentDemo(
&current.pos, evt_id, 0, -1, -1, 0, 0 DUSK_GIVE_TAG("ilia_charm"));
if (mItemPartnerId != 0xffffffff) {
s16 evt_idx =
dComIfGp_getEventManager().getEventIdx(this, "DEFAULT_GETITEM", 0xff);
@@ -1060,8 +1061,9 @@ BOOL daNpcImpal_c::EvCut_CopyRod(int i_cut_index) {
case '0003':
int evt_id = 0;
if (mFlow.getEventId(&evt_id) == 1) {
mItemPartnerId =
fopAcM_createItemForPresentDemo(&current.pos, evt_id, 0, -1, -1, 0, 0);
DUSK_ITEM_CHECK("skybook", evt_id, this);
mItemPartnerId = fopAcM_createItemForPresentDemo(
&current.pos, evt_id, 0, -1, -1, 0, 0 DUSK_GIVE_TAG("skybook"));
dComIfGp_event_setItemPartnerId(mItemPartnerId);
mItemPartnerId = -1;
}
+16 -1
View File
@@ -11,6 +11,10 @@
#include "d/d_msg_object.h"
#include <cstring>
#if TARGET_PC
static u8 s_givenInsectId = dItemNo_NONE_e;
#endif
enum Ins_RES_File_ID {
/* BCK */
/* 0x06 */ BCK_INS_F_HAPPY = 0x6,
@@ -1259,6 +1263,7 @@ int daNpcIns_c::waitPresent(void* param_1) {
daPy_py_c* player = daPy_getPlayerActorClass();
player->changeOriginalDemo();
player->changeDemoMode(0x25, 2, type, 0);
IF_DUSK(s_givenInsectId = type;)
} else {
mInsectMsgNo = 0x719;
}
@@ -1487,7 +1492,17 @@ int daNpcIns_c::talk(void* param_1) {
OS_REPORT("会話終了時 イベントID=%d アイテムNo=%d\n", eventID, itemNo);
if (eventID == 1) {
mItemID = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL, NULL);
#if TARGET_PC
u32 itemGiveTag = 0;
if (s_givenInsectId != dItemNo_NONE_e) {
itemNo = dusk::mods::item_check_bug(
s_givenInsectId, itemNo & 0xFF, this);
itemGiveTag = dusk::mods::item_give_tag_bug(s_givenInsectId);
s_givenInsectId = dItemNo_NONE_e;
}
#endif
mItemID = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1,
-1, NULL, NULL IF_DUSK_ARG(itemGiveTag));
if (mItemID != fpcM_ERROR_PROCESS_ID_e) {
daPy_getPlayerActorClass()->cancelOriginalDemo();
+10 -1
View File
@@ -1181,7 +1181,16 @@ int daNpc_Kkri_c::talk(void*) {
switch (eventId) {
case 1:
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, item_no, 0, -1, -1, NULL, NULL);
#if TARGET_PC
const char* itemCheckName = nullptr;
if (item_no == dItemNo_OIL_BOTTLE3_e) {
itemCheckName = "coro_bottle";
item_no = dusk::mods::item_check(itemCheckName, item_no, this);
}
#endif
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, item_no,
0, -1, -1, NULL,
NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
}
if (fopAcM_IsExecuting(mItemPartnerId)) {
+2 -1
View File
@@ -1236,8 +1236,9 @@ int daNpc_Len_c::talk(void* param_0) {
switch (evt_id) {
case 1:
if (mItemPartnerId == -1) {
DUSK_ITEM_CHECK("renado_letter", local_18, this);
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, local_18,
0, -1, -1, 0, 0);
0, -1, -1, 0, 0 DUSK_GIVE_TAG("renado_letter"));
}
if (fopAcM_IsExecuting(mItemPartnerId)) {
mEvtNo = 1;
+4 -1
View File
@@ -2393,7 +2393,10 @@ int daNpc_Maro_c::cutArrowTutorial(int arg0) {
switch (evt_ret) {
case 1: {
if (mItemPartnerId == -1) {
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, evt_id, 0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("archery_reward:F_SP109", evt_id, this);
mItemPartnerId =
fopAcM_createItemForPresentDemo(&current.pos, evt_id, 0, -1, -1,
NULL, NULL DUSK_GIVE_TAG("archery_reward:F_SP109"));
}
if (fopAcM_IsExecuting(mItemPartnerId)) {
+9 -1
View File
@@ -1191,7 +1191,15 @@ int daNpc_myna2_c::ECut_gameGoalSuccess(int i_staffId) {
case 20: {
int itemNo = 0;
if (mFlow.getEventId(&itemNo) == 1) {
mItemPid = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL, NULL);
#if TARGET_PC
const char* itemCheckName = nullptr;
if (itemNo == dItemNo_KAKERA_HEART_e) {
itemCheckName = "plumm_minigame_reward";
itemNo = dusk::mods::item_check(itemCheckName, itemNo, this);
}
#endif
mItemPid = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL,
NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
}
break;
}
+12 -1
View File
@@ -958,8 +958,19 @@ int daNpc_Pouya_c::cutHaveFavorToAsk(int param_0) {
switch (evt_id) {
case 1:
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
#if TARGET_PC
const char* itemCheckName = nullptr;
if (local_64 == dItemNo_DROP_BOTTLE_e) {
itemCheckName = "jovani_reward_1";
} else if (local_64 == dItemNo_SILVER_RUPEE_e) {
itemCheckName = "jovani_reward_2";
}
if (itemCheckName != nullptr) {
local_64 = dusk::mods::item_check(itemCheckName, local_64, this);
}
#endif
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, local_64, 0,
-1, -1, 0, 0);
-1, -1, 0, 0 IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
}
if (fopAcM_IsExecuting(mItemPartnerId)) {
field_0xfce = 1;
+2 -1
View File
@@ -720,7 +720,8 @@ fpc_ProcID daNpcPray_c::createHeart() {
mDoMtx_stack_c::ZXYrotS(rot);
mDoMtx_stack_c::multVec(&offset, &offset);
pos += offset;
return fopAcM_createItemForBoss(&pos, dItemNo_KAKERA_HEART_e, fopAcM_GetRoomNo(this), &rot, &size, 0.0f, 0.0f, 0);
return fopAcM_createItemForBoss(&pos, dItemNo_KAKERA_HEART_e, fopAcM_GetRoomNo(this), &rot,
&size, 0.0f, 0.0f, 0 IF_DUSK_ARG("prayer_reward"));
}
BOOL daNpcPray_c::_Evt_GetHeart(int i_staffID) {
+6 -2
View File
@@ -1310,7 +1310,9 @@ bool daNpcRafrel_c::talk(void* param_0) {
OS_REPORT("会話終了時 イベントID=%d アイテムNo=%d\n", eventId, itemNo);
if (eventId == 1) {
field_0xe00 = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("auru_memo", itemNo, this);
field_0xe00 = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1,
NULL, NULL DUSK_GIVE_TAG("auru_memo"));
if (field_0xe00 != fpcM_ERROR_PROCESS_ID_e) {
s16 eventIdx = dComIfGp_getEventManager().getEventIdx(this, "DEFAULT_GETITEM", 0xFF);
dComIfGp_getEvent()->reset(this);
@@ -1562,7 +1564,9 @@ int daNpcRafrel_c::EvCut_Appear(int i_staffId) {
int itemNo = 0;
u16 eventId = mFlow.getEventId(&itemNo);
if (eventId == 1) {
field_0xe00 = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("auru_memo", itemNo, this);
field_0xe00 = fopAcM_createItemForPresentDemo(
&current.pos, itemNo, 0, -1, -1, NULL, NULL DUSK_GIVE_TAG("auru_memo"));
dComIfGp_event_setItemPartnerId(field_0xe00);
field_0xe00 = fpcM_ERROR_PROCESS_ID_e;
}
+2 -1
View File
@@ -823,8 +823,9 @@ BOOL daNpcThe_c::talk(void* param_0) {
}
int item_no = 0;
if (mFlow.getEventId(&item_no) == 1) {
DUSK_ITEM_CHECK("telma_invoice", item_no, this);
mItemID = fopAcM_createItemForPresentDemo(&current.pos, item_no, 0, -1, -1,
NULL, NULL);
NULL, NULL DUSK_GIVE_TAG("telma_invoice"));
if (mItemID != -1) {
s16 event_id = dComIfGp_getEventManager().getEventIdx(
this, "DEFAULT_GETITEM", 0xff);
+3 -2
View File
@@ -1165,8 +1165,9 @@ int daNpc_Uri_c::cutEndCarryTutorial(int param_1) {
(s32)mFlow.getEventId(&local_48) == 1)
{
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
mItemPartnerId =
fopAcM_createItemForPresentDemo(&current.pos, local_48, 0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("uli_cradle_reward", local_48, this);
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, local_48, 0, -1, -1,
NULL, NULL DUSK_GIVE_TAG("uli_cradle_reward"));
}
if (fopAcM_IsExecuting(mItemPartnerId)) {
/* T_0007 - Ordon Village - During Uli's pick-up tutorial */
+6 -4
View File
@@ -2213,8 +2213,9 @@ int daNpc_ykW_c::cutEndSnowboardRace(int param_0) {
switch (eventId) {
case 1:
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, itemId, 0,
-1, -1, 0, 0);
DUSK_ITEM_CHECK("snowboard_race_reward", itemId, this);
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, itemId, 0, -1,
-1, 0, 0 DUSK_GIVE_TAG("snowboard_race_reward"));
}
if (fopAcM_IsExecuting(mItemPartnerId)) {
@@ -2911,8 +2912,9 @@ int daNpc_ykW_c::talk(void* param_0) {
switch (eventId) {
case 1:
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
mItemPartnerId =
fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, 0, 0);
DUSK_ITEM_CHECK("dungeon_map:D_MN11", itemNo, this);
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, itemNo,
0, -1, -1, 0, 0 DUSK_GIVE_TAG("dungeon_map:D_MN11"));
}
if (fopAcM_IsExecuting(mItemPartnerId)) {
+13 -2
View File
@@ -2609,8 +2609,19 @@ BOOL daNpc_zrA_c::ECut_thanksBlast(int i_staffID) {
case 31: {
int item_id = 0;
if (mFlow.getEventId(&item_id) == 1) {
mItemID = fopAcM_createItemForPresentDemo(&current.pos, item_id,
0, -1, -1, NULL, NULL);
#if TARGET_PC
const char* itemCheckName = nullptr;
if (item_id == dItemNo_BOMB_IN_BAG_e) {
itemCheckName = "iza_reward_1";
} else if (item_id == dItemNo_BOMB_BAG_LV2_e) {
itemCheckName = "iza_reward_2";
}
if (itemCheckName != nullptr) {
item_id = dusk::mods::item_check(itemCheckName, item_id, this);
}
#endif
mItemID = fopAcM_createItemForPresentDemo(&current.pos, item_id, 0, -1, -1, NULL,
NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
}
field_0x9eb = true;
break;
+3 -2
View File
@@ -1681,8 +1681,9 @@ BOOL daNpc_zrC_c::ECut_earringGet(int i_staffID) {
case 40: {
int item_no = 0;
if (mFlow.getEventId(&item_no) == 1) {
mItemID = fopAcM_createItemForPresentDemo(&current.pos, item_no,
0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("coral_earring", item_no, this);
mItemID = fopAcM_createItemForPresentDemo(
&current.pos, item_no, 0, -1, -1, NULL, NULL DUSK_GIVE_TAG("coral_earring"));
}
break;
}
+3 -2
View File
@@ -1729,8 +1729,9 @@ BOOL daNpc_zrZ_c::ECut_clothesGet(int i_staffID) {
}
item_no = 0;
if (mFlow.getEventId(&item_no) == 1) {
mItemID = fopAcM_createItemForPresentDemo(&current.pos, item_no,
0, -1, -1, NULL, NULL);
DUSK_ITEM_CHECK("zora_armor", item_no, this);
mItemID = fopAcM_createItemForPresentDemo(
&current.pos, item_no, 0, -1, -1, NULL, NULL DUSK_GIVE_TAG("zora_armor"));
}
break;
+65 -14
View File
@@ -206,7 +206,7 @@ void daItem_c::CreateInit() {
initBaseMtx();
animPlay(1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f);
if (m_itemNo == dItemNo_BOOMERANG_e) {
if (m_itemNo == dItemNo_BOOMERANG_e IF_DUSK(&&!mItemOverridden)) {
itemGetNextExecute();
} else if ((m_itemNo == dItemNo_ORANGE_RUPEE_e || m_itemNo == dItemNo_SILVER_RUPEE_e) &&
mSparkleEmtr.getEmitter() == NULL)
@@ -264,6 +264,17 @@ int daItem_c::_daItem_create() {
shape_angle.z = 0;
shape_angle.x = 0;
#if TARGET_PC
const u32 params = fopAcM_GetParam(this);
mOriginalItemNo = params & 0xFF;
const u8 resolvedItem = dusk::mods::item_check_freestanding(
daItem_prm::getItemBitNo(this), mOriginalItemNo, this);
mItemOverridden = resolvedItem != mOriginalItemNo;
mItemGiveTag = dusk::mods::item_give_tag_freestanding(daItem_prm::getItemBitNo(this));
if (mItemOverridden) {
fopAcM_SetParam(this, (params & 0xFFFFFF00) | resolvedItem);
}
#endif
field_0x95d = true;
}
@@ -507,9 +518,18 @@ void daItem_c::procInitGetDemoEvent() {
fopAcM_orderItemEvent(this, 0, 0);
eventInfo.onCondition(dEvtCnd_CANGETITEM_e);
m_item_id = fopAcM_createItemForTrBoxDemo(&current.pos, m_itemNo, -1, fopAcM_GetRoomNo(this),
NULL, NULL);
#if TARGET_PC
const u8 displayItemNo = m_itemNo;
if (mItemOverridden) {
m_itemNo = dusk::mods::item_check_tagged(mItemGiveTag, mOriginalItemNo, this);
}
#endif
m_item_id = fopAcM_createItemForTrBoxDemo(
&current.pos, m_itemNo, -1, fopAcM_GetRoomNo(this), NULL, NULL IF_DUSK_ARG(mItemGiveTag));
JUT_ASSERT(0, m_item_id != fpcM_ERROR_PROCESS_ID_e);
#if TARGET_PC
m_itemNo = displayItemNo;
#endif
setStatus(STATUS_WAIT_GET_DEMO_EVENT_e);
}
@@ -521,7 +541,7 @@ void daItem_c::procWaitGetDemoEvent() {
dComIfGp_event_setItemPartnerId(m_item_id);
}
} else {
if (m_itemNo == dItemNo_BOOMERANG_e) {
if (m_itemNo == dItemNo_BOOMERANG_e IF_DUSK(&&!mItemOverridden)) {
fopAcM_orderItemEvent(this, 0, 0);
eventInfo.onCondition(dEvtCnd_CANGETITEM_e);
return;
@@ -537,7 +557,9 @@ void daItem_c::procWaitGetDemoEvent() {
procInitSimpleGetDemo();
itemGet();
if (!haveItem) {
if (!haveItem IF_DUSK(&&(!mItemOverridden || (m_itemNo >= dItemNo_GREEN_RUPEE_e &&
m_itemNo <= dItemNo_SILVER_RUPEE_e))))
{
dComIfGs_offItemFirstBit(m_itemNo);
}
} else {
@@ -864,6 +886,12 @@ void daItem_c::itemGetNextExecute() {
procInitGetDemoEvent();
break;
default:
#if TARGET_PC
if (mItemOverridden) {
procInitGetDemoEvent();
break;
}
#endif
// "[daItem_c] Get process not defined[%d]\n"
OS_REPORT_ERROR("[daItem_c]ゲット処理が定義されていません[%d]\n", m_itemNo);
}
@@ -877,38 +905,48 @@ void daItem_c::itemGetNextExecute() {
}
void daItem_c::itemGet() {
#if TARGET_PC
const u8 displayItemNo = m_itemNo;
if (mItemOverridden) {
m_itemNo = dusk::mods::item_check_tagged(mItemGiveTag, mOriginalItemNo, this);
}
#endif
switch (m_itemNo) {
#if TARGET_PC
case dItemNo_UTAWA_HEART_e:
case dItemNo_KAKERA_HEART_e:
#endif
case dItemNo_HEART_e:
mDoAud_seStart(Z2SE_HEART_PIECE_GET, NULL, 0, 0);
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
break;
case dItemNo_GREEN_RUPEE_e:
mDoAud_seStart(Z2SE_GREEN_LUPY_GET, NULL, 0, 0);
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
break;
case dItemNo_BLUE_RUPEE_e:
mDoAud_seStart(Z2SE_BLUE_LUPY_GET, NULL, 0, 0);
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
break;
case dItemNo_YELLOW_RUPEE_e:
mDoAud_seStart(Z2SE_BLUE_LUPY_GET, NULL, 0, 0);
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
break;
case dItemNo_RED_RUPEE_e:
mDoAud_seStart(Z2SE_RED_LUPY_GET, NULL, 0, 0);
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
break;
case dItemNo_PURPLE_RUPEE_e:
mDoAud_seStart(Z2SE_RED_LUPY_GET, NULL, 0, 0);
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
break;
case dItemNo_ORANGE_RUPEE_e:
mDoAud_seStart(Z2SE_RED_LUPY_GET, NULL, 0, 0);
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
break;
case dItemNo_SILVER_RUPEE_e:
mDoAud_seStart(Z2SE_RED_LUPY_GET, NULL, 0, 0);
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
break;
case dItemNo_BOOMERANG_e:
break;
@@ -918,12 +956,25 @@ void daItem_c::itemGet() {
case dItemNo_ARROW_1_e:
case dItemNo_PACHINKO_SHOT_e:
mDoAud_seStart(Z2SE_CONSUMP_ITEM_GET, NULL, 0, 0);
execItemGet(m_itemNo);
execItemGet(m_itemNo IF_DUSK_ARG(mItemGiveTag) IF_DUSK_ARG(this));
#if TARGET_PC
break;
#endif
default:
#if TARGET_PC
if (mItemOverridden) {
mDoAud_seStart(Z2SE_CONSUMP_ITEM_GET, NULL, 0, 0);
execItemGet(m_itemNo, mItemGiveTag, this);
break;
}
#endif
// "[daItem_c] Get process not defined[%d]\n"
OS_REPORT_ERROR("[daItem_c]ゲット処理が定義されていません[%d]\n", m_itemNo);
break;
}
#if TARGET_PC
m_itemNo = displayItemNo;
#endif
}
BOOL daItem_c::checkCountTimer() {
+39 -2
View File
@@ -97,6 +97,12 @@ int daObjLife_c::Create() {
field_0x94c = 0.7f;
mRotateSpeed = 7000;
#if TARGET_PC
if (mOverrideHover) {
fopAcM_SetGravity(this, 0.0f);
mRotateSpeed = 550;
}
#endif
setEffect();
mSound.init(&current.pos, 1);
return 1;
@@ -140,6 +146,27 @@ int daObjLife_c::create() {
home.angle.x = home.angle.z = 0;
current.angle.x = current.angle.z = 0;
shape_angle.x = shape_angle.z = 0;
#if TARGET_PC
const u32 params = fopAcM_GetParam(this);
const u8 parameterItemNo = params & 0xFF;
if (mItemGiveOriginalNo == dItemNo_NONE_e) {
mOriginalItemNo = parameterItemNo;
const u8 resolvedItem =
dusk::mods::item_check_freestanding(getSaveBitNo(), mOriginalItemNo, this);
mItemGiveTag = dusk::mods::item_give_tag_freestanding(getSaveBitNo());
mItemOverridden = resolvedItem != mOriginalItemNo;
if (mItemOverridden) {
fopAcM_SetParam(this, (params & 0xFFFFFF00) | resolvedItem);
}
} else {
mOriginalItemNo = mItemGiveOriginalNo;
mItemOverridden = parameterItemNo != mItemGiveOriginalNo;
}
mOverrideHover =
mItemOverridden &&
(mOriginalItemNo == dItemNo_UTAWA_HEART_e ||
(mOriginalItemNo >= dItemNo_M_BEETLE_e && mOriginalItemNo <= dItemNo_F_MAYFLY_e));
#endif
mIsPrmsInit = true;
}
@@ -153,7 +180,7 @@ int daObjLife_c::create() {
return cPhs_ERROR_e;
}
if (m_itemNo == dItemNo_UTAWA_HEART_e && dComIfGs_isStageLife()) {
if (m_itemNo == dItemNo_UTAWA_HEART_e && dComIfGs_isStageLife() IF_DUSK(&&!mItemOverridden)) {
return cPhs_ERROR_e;
}
@@ -296,8 +323,18 @@ int daObjLife_c::initActionOrderGetDemo() {
fopAcM_orderItemEvent(this, 0, 0);
eventInfo.onCondition(dEvtCnd_CANGETITEM_e);
mItemId = fopAcM_createItemForTrBoxDemo(&current.pos, m_itemNo, -1, fopAcM_GetRoomNo(this), NULL, NULL);
#if TARGET_PC
const u8 displayItemNo = m_itemNo;
if (mItemOverridden) {
m_itemNo = dusk::mods::item_check_tagged(mItemGiveTag, mOriginalItemNo, this);
}
#endif
mItemId = fopAcM_createItemForTrBoxDemo(
&current.pos, m_itemNo, -1, fopAcM_GetRoomNo(this), NULL, NULL IF_DUSK_ARG(mItemGiveTag));
JUT_ASSERT(699, mItemId != fpcM_ERROR_PROCESS_ID_e);
#if TARGET_PC
m_itemNo = displayItemNo;
#endif
setStatus(STATUS_ORDER_GET_DEMO_e);
return 1;
+4 -2
View File
@@ -109,6 +109,7 @@ int daItemShield_c::__CreateHeap() {
int daItemShield_c::create() {
fopAcM_ct(this, daItemShield_c);
m_itemNo = dItemNo_WOOD_SHIELD_e;
DUSK_ITEM_CHECK("ordon_shield", m_itemNo, this);
if (fopAcM_isSwitch(this, getSwBit2())) {
OS_REPORT("木の盾:もう取ったので出ません\n");
return cPhs_ERROR_e;
@@ -240,8 +241,9 @@ int daItemShield_c::initActionOrderGetDemo() {
daItemBase_c::hide();
fopAcM_orderItemEvent(this, 0, 0);
eventInfo.onCondition(dEvtCnd_CANGETITEM_e);
mItemId =
fopAcM_createItemForTrBoxDemo(&current.pos, m_itemNo, -1, fopAcM_GetRoomNo(this), 0, 0);
mItemId = fopAcM_createItemForTrBoxDemo(&current.pos,
DUSK_ITEM_CHECK_EXPR("ordon_shield", dItemNo_WOOD_SHIELD_e, this), -1,
fopAcM_GetRoomNo(this), 0, 0 DUSK_GIVE_TAG("ordon_shield"));
JUT_ASSERT(682, mItemId != fpcM_ERROR_PROCESS_ID_e)
setStatus(STATUS_ORDERGETDEMO);
return 1;
+4 -2
View File
@@ -37,6 +37,7 @@ int daObjSword_c::Create() {
cPhs_Step daObjSword_c::create() {
fopAcM_ct(this, daObjSword_c);
m_itemNo = 0x28;
DUSK_ITEM_CHECK("ordon_sword", m_itemNo, this);
if (fopAcM_isItem(this, getItemBit())) {
return cPhs_ERROR_e;
}
@@ -71,8 +72,9 @@ int daObjSword_c::initActionOrderGetDemo() {
hide();
fopAcM_orderItemEvent(this, 0, 0);
eventInfo.onCondition(8);
mProcID = fopAcM_createItemForTrBoxDemo(&current.pos, m_itemNo, -1, fopAcM_GetRoomNo(this),
NULL, NULL);
mProcID =
fopAcM_createItemForTrBoxDemo(&current.pos, DUSK_ITEM_CHECK_EXPR("ordon_sword", 0x28, this),
-1, fopAcM_GetRoomNo(this), NULL, NULL DUSK_GIVE_TAG("ordon_sword"));
setStatus(1);
return 1;
}
+3 -2
View File
@@ -255,8 +255,9 @@ int daObjWStatue_c::initActionOrderGetDemo() {
s16 eventIdx = dComIfGp_getEventManager().getEventIdx(this, "DEFAULT_GETITEM", 0xff);
dComIfGp_getEvent()->reset(this);
fopAcM_orderChangeEventId(this, eventIdx, 1, 0xffff);
mItemId = fopAcM_createItemForTrBoxDemo(&current.pos, m_itemNo, 0xffffffff,
fopAcM_GetRoomNo(this), 0, 0);
mItemId = fopAcM_createItemForTrBoxDemo(&current.pos,
DUSK_ITEM_CHECK_EXPR("wood_statue", m_itemNo, this), 0xffffffff, fopAcM_GetRoomNo(this),
0, 0 DUSK_GIVE_TAG("wood_statue"));
JUT_ASSERT(544, mItemId != fpcM_ERROR_PROCESS_ID_e);
setStatus(STATUS_ORDER_GET_DEMO);
return 1;
+1
View File
@@ -32,6 +32,7 @@ int daObjZCloth_c::Create() {
int daObjZCloth_c::create() {
fopAcM_ct(this, daObjZCloth_c);
m_itemNo = 0x31;
DUSK_ITEM_CHECK("zora_armor", m_itemNo, this);
int phase = dComIfG_resLoad(&mPhase, dItem_data::getFieldArc(m_itemNo));
if (phase == cPhs_COMPLEATE_e) {
if (!fopAcM_entrySolidHeap(this, (heapCallbackFunc)CheckFieldItemCreateHeap, 0x2fb0)) {
+59 -16
View File
@@ -11,6 +11,20 @@
#include "m_Do/m_Do_lib.h"
#include <cstring>
#if TARGET_PC
#include "d/d_item_data.h"
const ResourceData& daShopItem_c::getResourceData() const {
if (mItemOverridden) {
return mOverrideData;
}
return mData[mShopItemID];
}
#define SHOP_RESOURCE_DATA getResourceData()
#else
#define SHOP_RESOURCE_DATA mData[mShopItemID]
#endif
const char* daShopItem_c::getShopArcname() {
switch (m_itemNo) {
case dItemNo_NONE_e:
@@ -88,7 +102,29 @@ const char* daShopItem_c::getShopArcname() {
return NULL;
}
return mData[mShopItemID].get_arcName();
#if TARGET_PC
if (m_itemNo != dItemNo_NONE_e && mItemGiveOriginalNo == dItemNo_NONE_e) {
mItemGiveOriginalNo = m_itemNo;
const u8 resolvedItem = dusk::mods::item_check_shop(mItemGiveOriginalNo, this);
mItemOverridden = resolvedItem != mItemGiveOriginalNo;
if (mItemOverridden) {
mOverrideData = mData[mShopItemID];
mOverrideData.mArcName = dItem_data::getArcName(resolvedItem);
mOverrideData.mBmdName = dItem_data::getBmdName(resolvedItem);
mOverrideData.mBtkName = dItem_data::getBtkName(resolvedItem);
mOverrideData.mBckName = dItem_data::getBckName(resolvedItem);
mOverrideData.mBrkName = dItem_data::getBrkName(resolvedItem);
mOverrideData.mBtpName = dItem_data::getBtpName(resolvedItem);
mOverrideData.mTevFrm = dItem_data::getTevFrm(resolvedItem);
mOverrideData.mBtpFrm = -1;
mOverrideData.mFlag = static_cast<u32>(-1);
mOverrideData.mOffsetY = mShopItemID == SHOP_ITEMNO_ARMOR ? 60.0f : 15.0f;
mOverrideData.mScale = 1.0f;
}
}
#endif
return SHOP_RESOURCE_DATA.get_arcName();
}
DUSK_GAME_DATA const f32 daShopItem_c::m_cullfar_max = 5000.0f;
@@ -104,6 +140,11 @@ u16 daShopItem_c::getHeapSize() {
OS_REPORT("ShopItemID [%u]\n", a_ShopItemID);
ASSERT(a_ShopItemID < SHOP_ITEMNO_MAX);
#if TARGET_PC
if (mItemOverridden) {
return 0x8000;
}
#endif
return HeapSizeTbl[a_ShopItemID];
}
@@ -119,11 +160,11 @@ void daShopItem_c::CreateInit() {
if (strcmp("R_SP109", dComIfGp_getStartStageName()) == 0 && dComIfGp_getStartStageRoomNo() == 1)
{
scale.set(mData[mShopItemID].get_scale() * 0.8f, mData[mShopItemID].get_scale() * 0.8f,
mData[mShopItemID].get_scale() * 0.8f);
scale.set(SHOP_RESOURCE_DATA.get_scale() * 0.8f, SHOP_RESOURCE_DATA.get_scale() * 0.8f,
SHOP_RESOURCE_DATA.get_scale() * 0.8f);
} else {
scale.set(mData[mShopItemID].get_scale(), mData[mShopItemID].get_scale(),
mData[mShopItemID].get_scale());
scale.set(SHOP_RESOURCE_DATA.get_scale(), SHOP_RESOURCE_DATA.get_scale(),
SHOP_RESOURCE_DATA.get_scale());
}
home.pos = current.pos;
@@ -137,8 +178,8 @@ void daShopItem_c::set_mtx() {
if (daShopItem_prm::getGroup(this) == 15) {
mDoMtx_stack_c::transS(current.pos.x, current.pos.y, current.pos.z);
} else {
mDoMtx_stack_c::transS(current.pos.x, current.pos.y + mData[mShopItemID].get_offsetY(),
current.pos.z);
mDoMtx_stack_c::transS(
current.pos.x, current.pos.y + SHOP_RESOURCE_DATA.get_offsetY(), current.pos.z);
}
MTXCopy(mDoMtx_stack_c::get(), mMtx);
@@ -147,8 +188,8 @@ void daShopItem_c::set_mtx() {
if (daShopItem_prm::getGroup(this) == 15) {
mDoMtx_stack_c::ZXYrotM(-11300, 32700, 7300);
} else {
mDoMtx_stack_c::ZXYrotM(mAngleX + mData[mShopItemID].get_angleX(),
mData[mShopItemID].get_angleY(), mData[mShopItemID].get_angleZ());
mDoMtx_stack_c::ZXYrotM(mAngleX + SHOP_RESOURCE_DATA.get_angleX(),
SHOP_RESOURCE_DATA.get_angleY(), SHOP_RESOURCE_DATA.get_angleZ());
}
mDoMtx_stack_c::ZXYrotM(current.angle.x, current.angle.y, current.angle.z);
@@ -156,7 +197,7 @@ void daShopItem_c::set_mtx() {
if (daShopItem_prm::getGroup(this) == 15) {
mDoMtx_stack_c::XrotM(0);
} else {
mDoMtx_stack_c::XrotM(mData[mShopItemID].get_angleOffsetX());
mDoMtx_stack_c::XrotM(SHOP_RESOURCE_DATA.get_angleOffsetX());
}
mpModel->setBaseTRMtx(mDoMtx_stack_c::get());
@@ -190,29 +231,31 @@ void daShopItem_c::setShadow() {
}
BOOL daShopItem_c::chkFlag(int i_flag) {
return mData[mShopItemID].get_flag() & i_flag;
return SHOP_RESOURCE_DATA.get_flag() & i_flag;
}
s8 daShopItem_c::getTevFrm() {
return mData[mShopItemID].get_tevfrm();
return SHOP_RESOURCE_DATA.get_tevfrm();
}
s8 daShopItem_c::getBtpFrm() {
return mData[mShopItemID].get_btpfrm();
return SHOP_RESOURCE_DATA.get_btpfrm();
}
u8 daShopItem_c::getShadowSize() {
return mData[mShopItemID].get_shadowSize();
return SHOP_RESOURCE_DATA.get_shadowSize();
}
u8 daShopItem_c::getCollisionH() {
return mData[mShopItemID].get_collisionH();
return SHOP_RESOURCE_DATA.get_collisionH();
}
u8 daShopItem_c::getCollisionR() {
return mData[mShopItemID].get_collisionR();
return SHOP_RESOURCE_DATA.get_collisionR();
}
#undef SHOP_RESOURCE_DATA
int daShopItem_c::_create() {
fopAcM_ct(this, daShopItem_c);
+5 -3
View File
@@ -362,9 +362,11 @@ int daTagStatue_c::demoProc() {
item = dItemNo_AIR_LETTER_e;
}
mItemId =
fopAcM_createItemForTrBoxDemo(&current.pos, item, -1,
fopAcM_GetRoomNo(this), 0, 0);
#if TARGET_PC
item = dusk::mods::item_check_sky_character(item & 0xFF, this);
#endif
mItemId = fopAcM_createItemForTrBoxDemo(&current.pos, item, -1,
fopAcM_GetRoomNo(this), 0, 0 IF_DUSK_ARG(dusk::mods::item_give_tag_sky_character()));
JUT_ASSERT(580, mItemId != fpcM_ERROR_PROCESS_ID_e);
+13 -2
View File
@@ -1390,6 +1390,10 @@ u8 daTbox_c::getBombItemNoMain(u8 i_itemNo) {
int daTbox_c::setGetDemoItem() {
u8 item_no = getItemNo();
#if TARGET_PC
const u32 giveTag = dusk::mods::item_give_tag_chest(getTboxNo());
item_no = dusk::mods::item_check_tagged(giveTag, mOriginalItemNo, this);
#endif
if (item_no == dItemNo_BOMB_5_e || item_no == dItemNo_BOMB_10_e || item_no == dItemNo_BOMB_20_e || item_no == dItemNo_BOMB_30_e ||
item_no == dItemNo_WATER_BOMB_5_e || item_no == dItemNo_WATER_BOMB_10_e || item_no == dItemNo_WATER_BOMB_20_e || item_no == dItemNo_WATER_BOMB_30_e ||
item_no == dItemNo_BOMB_INSECT_5_e || item_no == dItemNo_BOMB_INSECT_10_e || item_no == dItemNo_BOMB_INSECT_20_e || item_no == dItemNo_BOMB_INSECT_30_e)
@@ -1399,9 +1403,11 @@ int daTbox_c::setGetDemoItem() {
fpc_ProcID item_id;
if (field_0x718) {
item_id = fopAcM_createItemForPresentDemo(&current.pos, item_no, 1, -1, -1, NULL, NULL);
item_id = fopAcM_createItemForPresentDemo(
&current.pos, item_no, 1, -1, -1, NULL, NULL IF_DUSK_ARG(giveTag));
} else {
item_id = fopAcM_createItemForTrBoxDemo(&current.pos, item_no, -1, -1, NULL, NULL);
item_id = fopAcM_createItemForTrBoxDemo(
&current.pos, item_no, -1, -1, NULL, NULL IF_DUSK_ARG(giveTag));
}
if (item_id != fpcM_ERROR_PROCESS_ID_e) {
@@ -1785,6 +1791,11 @@ void daTbox_c::mode_exec() {
cPhs_Step daTbox_c::create1st() {
if (!mParamsInit) {
field_0x980 = home.angle.x;
#if TARGET_PC
mOriginalItemNo = (home.angle.z >> 8) & 0xFF;
const u8 resolvedItem = dusk::mods::item_check_chest(getTboxNo(), mOriginalItemNo, this);
home.angle.z = static_cast<s16>((home.angle.z & ~0xFF00) | (resolvedItem << 8));
#endif
field_0x982 = home.angle.z;
home.angle.z = 0;
home.angle.x = 0;
+18 -2
View File
@@ -138,6 +138,17 @@ int daTbox2_c::create1st() {
fopAcM_ct(this, daTbox2_c);
mModelType = getModelType();
#if TARGET_PC
if (!mParamsInit) {
mOriginalItemNo = getItemNo();
int tboxNo = fopAcM_GetParamBit(this, 16, 8);
const u8 resolvedItem = dusk::mods::item_check_chest(tboxNo, mOriginalItemNo, this);
u32 params = (fopAcM_GetParam(this) & 0xFFFFFF00) | resolvedItem;
fopAcM_SetParam(this, params);
mParamsInit = true;
}
#endif
int phase_state = dComIfG_resLoad(&mPhase, l_arcName);
if (phase_state == cPhs_COMPLEATE_e) {
u32 heap_size;
@@ -370,12 +381,17 @@ void daTbox2_c::actionOpenWait() {
int daTbox2_c::setGetDemoItem() {
u8 item_no = getItemNo();
#if TARGET_PC
int tboxNo = fopAcM_GetParamBit(this, 16, 8);
const u32 giveTag = dusk::mods::item_give_tag_chest(tboxNo);
item_no = dusk::mods::item_check_tagged(giveTag, mOriginalItemNo, this);
#endif
u32 partner_id;
if (mReturnRupee) {
partner_id = fopAcM_createItemForPresentDemo(&current.pos, item_no, 1, -1, -1, NULL, NULL);
partner_id = fopAcM_createItemForPresentDemo(&current.pos, item_no, 1, -1, -1, NULL, NULL IF_DUSK_ARG(giveTag));
} else {
partner_id = fopAcM_createItemForTrBoxDemo(&current.pos, item_no, -1, -1, NULL, NULL);
partner_id = fopAcM_createItemForTrBoxDemo(&current.pos, item_no, -1, -1, NULL, NULL IF_DUSK_ARG(giveTag));
}
if (partner_id != -1) {
+7
View File
@@ -45,6 +45,12 @@ int CheckShopItemCreateHeap(fopAc_ac_c* i_this) {
daShopItem_c* a_this1 = static_cast<daShopItem_c*>(i_this);
daShopItem_c* a_this2 = static_cast<daShopItem_c*>(i_this);
#if TARGET_PC
const ResourceData& data = a_this2->getResourceData();
return a_this1->CreateItemHeap(data.get_arcName(), data.get_bmdName(), data.get_btk1Name(),
data.get_bpk1Name(), data.get_bck1Name(), data.get_bxa1Name(), data.get_brk1Name(),
data.get_btp1Name());
#else
u8 a_ShopItemID = a_this2->getShopItemID();
return a_this1->CreateItemHeap(daShopItem_c::mData[a_ShopItemID].get_arcName(),
daShopItem_c::mData[a_ShopItemID].get_bmdName(),
@@ -54,4 +60,5 @@ int CheckShopItemCreateHeap(fopAc_ac_c* i_this) {
daShopItem_c::mData[a_ShopItemID].get_bxa1Name(),
daShopItem_c::mData[a_ShopItemID].get_brk1Name(),
daShopItem_c::mData[a_ShopItemID].get_btp1Name());
#endif
}
+8
View File
@@ -335,6 +335,14 @@ bool dEvent_manager_c::setObjectArchive(DUSK_CONST char* arcname) {
if (arcname != NULL) {
rt = dComIfG_getObjectRes(arcname, DataFileName);
#if TARGET_PC
if (rt != nullptr && strcmp(arcname, "Prayer") == 0) {
// pointer to Prayer event `011get_item` prm0 in it's event_list.dat
u8* itemNo = static_cast<u8*>(rt) + 0x927;
*itemNo =
dusk::mods::item_check("prayer_reward", dItemNo_KAKERA_HEART_e, NULL);
}
#endif
int base_status = mEventList[BASE_ACTOR].init((char*)rt, -1);
#if DEBUG
+6 -2
View File
@@ -269,13 +269,17 @@ static void (*item_func_ptr[256])() = {
item_func_noentry,
};
inline void getItemFunc(u8 i_itemNo) {
#if !TARGET_PC
inline
#endif
void getItemFunc(u8 i_itemNo) {
dComIfGs_onItemFirstBit(i_itemNo);
item_func_ptr[i_itemNo]();
}
void execItemGet(u8 i_itemNo) {
void execItemGet(u8 i_itemNo IF_DUSK_ARG(u32 i_itemGiveTag) IF_DUSK_ARG(fopAc_ac_c* i_giver)) {
getItemFunc(i_itemNo);
IF_DUSK(dusk::mods::item_granted(i_itemNo, i_itemGiveTag, i_giver);)
}
static int (*item_getcheck_func_ptr[256])() = {
+1 -1
View File
@@ -2346,7 +2346,7 @@ void dScnKy_env_light_c::setLight() {
u8 next_pal_end_id;
#if TARGET_PC
const f32 deltaTime = dusk::game_clock::consume_interval(this);
timeScale = deltaTime / dusk::game_clock::period_for_original_frames(1.0f);
timeScale = deltaTime / dusk::game_clock::kSimPeriod;
#endif
setLight_palno_get(&g_env_light.PrevCol, &g_env_light.UseCol, &g_env_light.wether_pat0,
&g_env_light.wether_pat1, &prev_pal_start_id, &prev_pal_end_id,
+1 -1
View File
@@ -118,7 +118,7 @@ static int dKyeff_Create(kankyo_class* i_this) {
if (strcmp(dComIfGp_getStartStageName(), "Name") == 0) {
camera_process_class* camera = dComIfGp_getCamera(0);
OSTime time = OSGetTime();
OSTime time = DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime());
OSTicksToCalendarTime(time, &calendar);
g_env_light.global_wind_influence.vec.x = 1.0f;
+3 -128
View File
@@ -16,23 +16,14 @@
#ifdef TARGET_PC
#include "dusk/settings.h"
#include "dusk/hq_minimap.hpp"
#include "m_Do/m_Do_graphic.h"
#include <dolphin/gx/GXAurora.h>
#include <aurora/math.hpp>
#include <algorithm>
#include <array>
#include <cmath>
#include <functional>
#include <limits>
#include <numbers>
#include <span>
constexpr u16 kMapIconResolutionMultiplier = 4;
constexpr u16 kMapImageSide = 16 * kMapIconResolutionMultiplier;
constexpr u32 kMapImageTotalPixels = kMapImageSide * kMapImageSide;
typedef std::function<u8(size_t, size_t)> PaintI8Fn;
u16 scaled_map_axis(u16 value, f32 scale) {
const auto scaledValue =
@@ -59,27 +50,6 @@ aurora::Vec2<u16> map_render_size_for(u16 width, u16 height) {
scaled_map_axis(height, irScaleY * hudScale),
};
}
void paint_i8(std::span<u8> dst, size_t width, PaintI8Fn paint) {
const auto blocksAcross = width >> 3;
for (size_t i = 0; i < dst.size(); i++) {
// 8x4 block swizzling for I8
const auto blockIdx = i >> 5;
const auto localIdx = i & 31;
const auto blockY = blockIdx / blocksAcross;
const auto blockX = blockIdx % blocksAcross;
const auto localY = localIdx >> 3;
const auto localX = localIdx & 7;
const auto x = (blockX << 3) + localX;
const auto y = (blockY << 2) + localY;
dst[i] = paint(x, y);
}
}
#endif
void dMpath_n::dTexObjAggregate_c::create() {
@@ -100,106 +70,11 @@ void dMpath_n::dTexObjAggregate_c::create() {
JUT_ASSERT(72, image != NULL);
JUT_ASSERT(73, image->minFilter == GX_NEAR);
JUT_ASSERT(74, image->magFilter == GX_NEAR);
IF_DUSK(dusk::hq_minimap::register_pointer(data[lp1], reinterpret_cast<u8*>(image) + image->imageOffset));
mDoLib_setResTimgObj(image, mp_texObj[lp1], 0, NULL);
}
#if TARGET_PC
static bool hqTexsDrawn = false;
static u8 hqCircleData[kMapImageTotalPixels];
static u8 hqCircleAltData[kMapImageTotalPixels];
static u8 hqNijumaruData[kMapImageTotalPixels];
static u8 hqEnterData[kMapImageTotalPixels];
static u8 hqTryForceData[kMapImageTotalPixels];
if (!hqTexsDrawn) {
constexpr auto center = kMapImageSide / 2.0f;
constexpr auto radiusSq = center * center;
// 6: map_icon_circle16x16_4i.bti - simple circle
paint_i8(std::span{hqCircleData}, kMapImageSide, [=](auto x, auto y) {
const auto dx = (x + 0.5f) - center;
const auto dy = (y + 0.5f) - center;
return (dx * dx + dy * dy < radiusSq) ? 0x11 : 0;
});
// 4: im_map_icon_circle_4i.bti - outlined circle
paint_i8(std::span{hqCircleAltData}, kMapImageSide, [=](auto x, auto y) {
constexpr auto innerRadius = kMapImageSide * 3.0f / 8.0f;
constexpr auto innerRadiusSq = innerRadius * innerRadius;
const auto dx = (x + 0.5f) - center;
const auto dy = (y + 0.5f) - center;
const auto dSq = dx * dx + dy * dy;
return dSq < radiusSq ? (dSq < innerRadiusSq ? 0x22 : 0x11) : 0;
});
// 3: im_map_icon_nijumaru_4i.bti - concentric rings
paint_i8(std::span{hqNijumaruData}, kMapImageSide, [=](auto x, auto y) {
constexpr u8 nijumaruRings[] = {0x11, 0x22, 0x11, 0x11, 0x22, 0x22};
const auto dx = (x + 0.5f) - center;
const auto dy = (y + 0.5f) - center;
const auto dSq = dx * dx + dy * dy;
if (dSq < radiusSq) {
const auto ringIndex =
static_cast<size_t>(std::trunc(std::sqrt(dSq) / kMapImageSide * 12));
return nijumaruRings[ringIndex];
}
return u8{0};
});
// 2: im_map_icon_enter_4i.bti - outlined octagram
paint_i8(std::span{hqEnterData}, kMapImageSide, [=](auto x, auto y) {
constexpr auto outlineWidth = kMapImageSide / 6.0f;
const auto adx = std::abs((x + 0.5f) - center);
const auto ady = std::abs((y + 0.5f) - center);
const auto dist =
std::min(adx + ady, std::max(adx, ady) * std::numbers::sqrt2_v<float>) -
kMapImageSide / 2.0f;
return dist > 0.0f ? 0 : (dist > -outlineWidth ? 0x22 : 0x33);
});
// 5: im_map_icon_try_force_4i.bti - outlined circle with triangle
paint_i8(std::span{hqTryForceData}, kMapImageSide, [=](auto x, auto y) {
constexpr auto innerRadiusNorm = 5.0f / 12.0f;
constexpr auto innerRadius = kMapImageSide * innerRadiusNorm;
constexpr auto innerRadiusSq = innerRadius * innerRadius;
constexpr auto triRadius = kMapImageSide * innerRadiusNorm / 2.0f;
const auto dx = (x + 0.5f) - center;
const auto dy = (y + 0.5f) - center;
const auto dSq = dx * dx + dy * dy;
const auto triSideDist = (std::numbers::sqrt3_v<float> * std::abs(dx) - dy) * 0.5f;
const auto insideTri = std::max(dy, triSideDist) < triRadius;
return insideTri ? 0x22 : (dSq < radiusSq ? (dSq < innerRadiusSq ? 0x33 : 0x22) : 0);
});
hqTexsDrawn = true;
}
constexpr auto replacements = std::to_array<std::pair<size_t, const u8*> >({
{2, hqEnterData},
{3, hqNijumaruData},
{4, hqCircleAltData},
{5, hqTryForceData},
{6, hqCircleData},
});
for (const auto& [idx, data] : replacements) {
JKR_DELETE(mp_texObj[idx]);
const auto texobj = JKR_NEW TGXTexObj();
GXInitTexObj(
texobj, data, kMapImageSide, kMapImageSide, GX_TF_I8, GX_CLAMP, GX_CLAMP, GX_FALSE);
GXInitTexObjLOD(texobj, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1);
mp_texObj[idx] = texobj;
}
#endif
IF_DUSK(dusk::hq_minimap::initialize_if_needed());
}
void dMpath_n::dTexObjAggregate_c::remove() {
+1 -1
View File
@@ -1798,7 +1798,7 @@ void dMenu_Ring_c::drawSelectItem() {
if (field_0x674[i] != 0) {
#if TARGET_PC
mSelectItemSlideElapsed[i] += dusk::game_clock::consume_interval(this);
const f32 u = std::min(mSelectItemSlideElapsed[i] / dusk::game_clock::period_for_original_frames(10.0f), 1.0f);
const f32 u = std::min(mSelectItemSlideElapsed[i] / (dusk::game_clock::kSimPeriod * 10.0f), 1.0f);
if (u >= 1.0f) {
setSelectItemForce(i);
} else {
+7 -5
View File
@@ -123,7 +123,13 @@ public:
#endif
}
void setCaptureFlag() { mFlag = 1; }
void setCaptureFlag() {
mFlag = 1;
#ifdef TARGET_PC
dusk::frame_interp::request_presentation_sync();
#endif
}
bool checkDraw() { return mFlag; }
u8 getAlpha() { return mAlpha; }
u8 getTopFlag() { return mTopFlag; }
@@ -1092,10 +1098,6 @@ void dMw_c::dMw_ring_create(u8 i_origin) {
}
mpCapture->setCaptureFlag();
#ifdef TARGET_PC
dusk::frame_interp::request_presentation_sync();
#endif
}
bool dMw_c::dMw_ring_delete() {
+2 -2
View File
@@ -1828,7 +1828,7 @@ int dSv_info_c::memory_to_card(char* card_ptr, int dataNum) {
savedata->getPlayer().getPlayerInfo().setTotalTime(play_time);
}
savedata->getPlayer().getPlayerStatusB().setDateIpl(OSGetTime());
savedata->getPlayer().getPlayerStatusB().setDateIpl(DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime()));
memcpy(card_ptr, savedata, sizeof(dSv_save_c));
card_ptr += 0x958;
@@ -2035,7 +2035,7 @@ void flagFile_c::listenPropertyEvent(const JORPropertyEvent* i_event) {
}
case 102: {
OSCalendarTime time;
OSTicksToCalendarTime(OSGetTime(), &time);
OSTicksToCalendarTime(DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime()), &time);
const char* start_stage_name = dComIfGp_getStartStageName();
char filename[64];
+16 -4
View File
@@ -905,8 +905,16 @@ int dShopSystem_c::seq_start(fopAc_ac_c* actor, dMsgFlow_c* i_flow) {
int itemNo;
if (mFlow.getEventId(&itemNo) == 1) {
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
mItemPartnerId = fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1,
-1, NULL, NULL);
#if TARGET_PC
const char* itemCheckName = nullptr;
if (itemNo == dItemNo_HALF_MILK_BOTTLE_e) {
itemCheckName = "sera_reward";
itemNo = dusk::mods::item_check(itemCheckName, itemNo, actor);
}
#endif
mItemPartnerId =
fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL,
NULL IF_DUSK_ARG(dusk::mods::item_give_tag(itemCheckName)));
}
if (fpcEx_IsExist(mItemPartnerId)) {
@@ -1195,8 +1203,12 @@ int dShopSystem_c::seq_decide_yes(fopAc_ac_c* actor, dMsgFlow_c* i_flow) {
if (mFlow.getEventId(&itemNo) == 1) {
if (i_flow->doFlow(actor, NULL, 0)) {
if (mItemPartnerId == fpcM_ERROR_PROCESS_ID_e) {
mItemPartnerId =
fopAcM_createItemForPresentDemo(&current.pos, itemNo, 0, -1, -1, NULL, NULL);
#if TARGET_PC
const u32 itemGiveTag = dusk::mods::item_give_tag_shop(itemNo & 0xFF);
itemNo = dusk::mods::item_check_shop(itemNo & 0xFF, actor);
#endif
mItemPartnerId = fopAcM_createItemForPresentDemo(
&current.pos, itemNo, 0, -1, -1, NULL, NULL IF_DUSK_ARG(itemGiveTag));
}
if (fpcEx_IsExist(mItemPartnerId)) {
+9
View File
@@ -41,6 +41,15 @@ bool isActionBound(ActionBinds action, u32 port) {
return getActionBindButton(action, port) != PAD_NATIVE_BUTTON_INVALID;
}
bool isActionBoundAnyPort(ActionBinds action) {
for (u32 port = 0; port < PAD_CHANMAX; ++port) {
if (isActionBound(action, port)) {
return true;
}
}
return false;
}
void updateActionBindings() {
for (u32 port = 0; port < PAD_CHANMAX; ++port) {
// Move the current press to the previous frame
+1
View File
@@ -31,6 +31,7 @@ using ActionBindsMap = std::unordered_map<ActionBinds, ActionBindData>;
ActionBindsMap& getActionBinds();
bool isActionBound(ActionBinds action, u32 port);
bool isActionBoundAnyPort(ActionBinds action);
void updateActionBindings();
+4
View File
@@ -147,6 +147,10 @@ void begin_frame(FrameInterpMode mode, bool is_sim_frame, float step) {
g_enabled = mode != FrameInterpMode::Off;
g_is_sim_frame = is_sim_frame;
g_step = std::clamp(step, 0.0f, 1.0f);
if (!g_enabled) {
g_interpolating = false;
clear_replacements();
}
}
bool is_enabled() {
+86 -55
View File
@@ -1,104 +1,135 @@
#include "dusk/game_clock.h"
#include <algorithm>
#include <aurora/time.hpp>
#include <chrono>
#include <cmath>
#include <unordered_map>
#include <dusk/frame_interpolation.h>
#include <unordered_map>
namespace dusk::game_clock {
using clock = std::chrono::steady_clock;
using native_clock = aurora::time::native_clock;
using game_clock = aurora::time::game_clock;
FrameTiming g_frameTiming;
namespace {
bool s_initialized = false;
clock::time_point s_previous_sample{};
clock::time_point s_current_snapshot_time{};
bool s_fixedStepActive = false;
bool s_simTickActive = false;
native_clock::time_point s_previousNativeSample{};
game_clock::time_point s_latestGameSample{};
game_clock::time_point s_currentSnapshotTime{};
game_clock::time_point s_pendingSimTime{};
std::unordered_map<uintptr_t, clock::time_point> s_interval_last_sample;
std::unordered_map<uintptr_t, game_clock::time_point> s_intervalLastSample;
constexpr clock::duration kSimPeriodDuration =
std::chrono::duration_cast<clock::duration>(std::chrono::duration<float>(sim_pace()));
constexpr clock::duration kAbnormalGapResetThreshold = std::chrono::milliseconds(250);
constexpr int kMaxSimTicksPerFrame = 2;
constexpr game_clock::duration kSimPeriodDuration =
std::chrono::duration_cast<game_clock::duration>(std::chrono::duration<float>(kSimPeriod));
constexpr native_clock::duration kAbnormalGapResetThreshold = std::chrono::milliseconds(250);
constexpr int kMaxSimTicksPerFrame = static_cast<int>(aurora::time::kMaximumTimeScale) * 4;
} // namespace
void ensure_initialized() {
void initialize() {
if (s_initialized) {
return;
}
s_previous_sample = clock::now();
s_current_snapshot_time = s_previous_sample;
s_previousNativeSample = native_clock::now();
s_latestGameSample = game_clock::now();
s_currentSnapshotTime = s_latestGameSample;
s_pendingSimTime = s_latestGameSample;
s_initialized = true;
}
void reset_frame_timer() {
s_previous_sample = clock::now();
s_current_snapshot_time = s_previous_sample - kSimPeriodDuration;
void reset() {
s_previousNativeSample = native_clock::now();
s_latestGameSample = game_clock::now();
s_currentSnapshotTime = s_latestGameSample - kSimPeriodDuration;
s_pendingSimTime = s_currentSnapshotTime;
s_simTickActive = false;
}
MainLoopPacer advance_main_loop() {
ensure_initialized();
const FrameTiming& advance() {
const auto nativeNow = native_clock::now();
const auto gameNow = game_clock::now();
const auto nativeFrameGap = nativeNow - s_previousNativeSample;
s_previousNativeSample = nativeNow;
s_latestGameSample = gameNow;
const clock::time_point now = clock::now();
const clock::duration frame_gap = now - s_previous_sample;
const float presentation_dt = std::chrono::duration<float>(frame_gap).count();
s_previous_sample = now;
auto& out = g_frameTiming;
out = {.dt = std::chrono::duration<float>().count()};
MainLoopPacer out{};
out.presentation_dt_seconds = presentation_dt;
const float timeScale = aurora::time::scale();
const bool interpolating =
getSettings().game.enableFrameInterpolation.getValue() != FrameInterpMode::Off;
const bool separatePresentation = interpolating || timeScale != 1.0f;
out.interpolating = interpolating;
out.separatePresentation = separatePresentation;
s_fixedStepActive = separatePresentation;
const bool should_interpolate = dusk::getSettings().game.enableFrameInterpolation.getValue() !=
dusk::FrameInterpMode::Off &&
!dusk::getTransientSettings().skipFrameRateLimit;
out.is_interpolating = should_interpolate;
out.sim_pace = sim_pace();
if (!should_interpolate) {
s_current_snapshot_time = now;
out.sim_ticks_to_run = 1;
if (!separatePresentation) {
s_currentSnapshotTime = gameNow;
out.numSimTicks = 1;
return out;
}
if (frame_gap > kAbnormalGapResetThreshold) {
s_current_snapshot_time = now - kSimPeriodDuration;
out.sim_ticks_to_run = 0;
const auto simulationTarget = interpolating ? gameNow - kSimPeriodDuration : gameNow;
if (timeScale == 0.f || nativeFrameGap > kAbnormalGapResetThreshold) {
s_currentSnapshotTime = simulationTarget;
out.numSimTicks = 0;
return out;
}
int sim_ticks_to_run = 0;
clock::time_point projected_snapshot_time = s_current_snapshot_time;
const clock::time_point render_time = now - kSimPeriodDuration;
while (sim_ticks_to_run < kMaxSimTicksPerFrame && projected_snapshot_time < render_time) {
projected_snapshot_time += kSimPeriodDuration;
sim_ticks_to_run++;
int numSimTicks = 0;
auto projectedSnapshotTime = s_currentSnapshotTime;
while (numSimTicks < kMaxSimTicksPerFrame) {
const bool tickDue = interpolating ?
projectedSnapshotTime < simulationTarget :
projectedSnapshotTime + kSimPeriodDuration <= simulationTarget;
if (!tickDue) {
break;
}
projectedSnapshotTime += kSimPeriodDuration;
numSimTicks++;
}
out.sim_ticks_to_run = sim_ticks_to_run;
out.numSimTicks = numSimTicks;
return out;
}
void begin_sim_tick() {
s_pendingSimTime =
s_fixedStepActive ? s_currentSnapshotTime + kSimPeriodDuration : s_latestGameSample;
s_simTickActive = true;
}
void commit_sim_tick() {
ensure_initialized();
s_current_snapshot_time += kSimPeriodDuration;
if (s_simTickActive) {
s_currentSnapshotTime = s_pendingSimTime;
s_simTickActive = false;
} else {
s_currentSnapshotTime += kSimPeriodDuration;
}
}
float sample_interpolation_step() {
ensure_initialized();
const float step =
std::chrono::duration<float>(clock::now() - s_current_snapshot_time).count() / sim_pace();
std::chrono::duration<float>(game_clock::now() - s_currentSnapshotTime).count() /
kSimPeriod;
return std::clamp(step, 0.0f, 1.0f);
}
float consume_interval(const void* consumer) {
ensure_initialized();
const uintptr_t key = reinterpret_cast<uintptr_t>(consumer);
const clock::time_point now = clock::now();
float dt = ui_initial_dt();
const auto it = s_interval_last_sample.find(key);
if (it != s_interval_last_sample.end()) {
const auto key = reinterpret_cast<uintptr_t>(consumer);
const auto now = s_simTickActive ? s_pendingSimTime : game_clock::now();
const float timeScale = aurora::time::scale();
float dt = kUiInitialDt * timeScale;
if (const auto it = s_intervalLastSample.find(key); it != s_intervalLastSample.end()) {
dt = std::chrono::duration<float>(now - it->second).count();
dt = std::min(dt, ui_maximum_dt());
const float maximumDt = std::max(kUiMaximumDt * timeScale, kSimPeriod);
dt = std::min(dt, maximumDt);
}
s_interval_last_sample[key] = now;
s_intervalLastSample[key] = now;
return dt;
}
+18 -13
View File
@@ -2,22 +2,27 @@
namespace dusk::game_clock {
void ensure_initialized();
void reset_frame_timer();
// Amount of time that a simulation tick advances
constexpr float kSimPeriod = 1.0f / 30.0f;
constexpr float kUiMaximumDt = 0.05f;
constexpr float kUiInitialDt = 1.0f / 60.0f;
constexpr float sim_pace() { return 1.0f / 30.0f; }
constexpr float period_for_original_frames(float frame_count) { return frame_count * sim_pace(); }
constexpr float ui_maximum_dt() { return 0.05f; }
constexpr float ui_initial_dt() { return 1.0f / 60.0f; }
struct MainLoopPacer {
float presentation_dt_seconds;
bool is_interpolating;
int sim_ticks_to_run;
float sim_pace;
struct FrameTiming {
// Amount of time elapsed in seconds since the last advance
float dt;
// Whether interpolation is active
bool interpolating;
// Run simulation and presentation separately (for interpolation or time scaling)
bool separatePresentation;
// Number of simulation ticks to run
int numSimTicks;
};
extern FrameTiming g_frameTiming;
MainLoopPacer advance_main_loop();
void initialize();
void reset();
const FrameTiming& advance();
void begin_sim_tick();
void commit_sim_tick();
float sample_interpolation_step();
+210
View File
@@ -0,0 +1,210 @@
#include "aurora/texture.hpp"
#include "dusk/texture_replacements.hpp"
#include "fmt/format.h"
#include <array>
#include <cassert>
#include <span>
#include <numbers>
namespace {
constexpr u16 kMapIconResolutionMultiplier = 4;
constexpr u16 kMapImageSide = 16 * kMapIconResolutionMultiplier;
constexpr u32 kMapImageTotalPixels = kMapImageSide * kMapImageSide;
// give higher priority to user and mod replacements
constexpr auto kInternalTextureReplacementPriority = dusk::texture_replacements::kUserTextureReplacementPriority - 1;
typedef std::function<u8(size_t, size_t)> PaintI8Fn;
enum class ArcIndex : int {
Circle16 = 82, // map_icon_circle16x16_4i.bti - simple circle
Circle = 76, // im_map_icon_circle_4i.bti - outlined circle
Nijumaru = 78, // im_map_icon_nijumaru_4i.bti - concentric rings
Enter = 77, // im_map_icon_enter_4i.bti - outlined octagram
TryForce = 81, // im_map_icon_try_force_4i.bti - outlined circle with triangle
};
struct Replacement {
ArcIndex index;
PaintI8Fn painter;
};
struct Icon {
u8* origData = nullptr;
std::unique_ptr<u8[]> newData;
std::string label;
std::optional<aurora::texture::ReplacementRegistration> reg;
};
bool s_initialized = false;
bool s_active = false;
std::unordered_map<int, Icon> s_icons;
void paint_i8(std::span<u8> dst, size_t width, PaintI8Fn paint) {
assert(width % 8 == 0 && dst.size() % 32 == 0);
const auto blocksAcross = width >> 3;
for (size_t i = 0; i < dst.size(); i++) {
// 8x4 block swizzling for I8
const auto blockIdx = i >> 5;
const auto localIdx = i & 31;
const auto blockY = blockIdx / blocksAcross;
const auto blockX = blockIdx % blocksAcross;
const auto localY = localIdx >> 3;
const auto localX = localIdx & 7;
const auto x = (blockX << 3) + localX;
const auto y = (blockY << 2) + localY;
dst[i] = paint(x, y);
}
}
void draw_all_replacements() {
constexpr auto center = kMapImageSide / 2.0f;
constexpr auto radiusSq = center * center;
// clang-format off
const auto replacements = std::to_array<Replacement>({
{
ArcIndex::Circle16,
[=](auto x, auto y) {
const auto dx = (x + 0.5f) - center;
const auto dy = (y + 0.5f) - center;
return (dx * dx + dy * dy < radiusSq) ? 0x11 : 0;
}
},
{
ArcIndex::Circle,
[=](auto x, auto y) {
constexpr auto innerRadius = kMapImageSide * 3.0f / 8.0f;
constexpr auto innerRadiusSq = innerRadius * innerRadius;
const auto dx = (x + 0.5f) - center;
const auto dy = (y + 0.5f) - center;
const auto dSq = dx * dx + dy * dy;
return dSq < radiusSq ? (dSq < innerRadiusSq ? 0x22 : 0x11) : 0;
}
},
{
ArcIndex::Nijumaru,
[=](auto x, auto y) {
constexpr u8 nijumaruRings[] = {0x11, 0x22, 0x11, 0x11, 0x22, 0x22};
const auto dx = (x + 0.5f) - center;
const auto dy = (y + 0.5f) - center;
const auto dSq = dx * dx + dy * dy;
if (dSq < radiusSq) {
auto ringIndex = static_cast<size_t>(std::trunc(std::sqrt(dSq) / kMapImageSide * 12));
ringIndex = std::min(ringIndex, sizeof(nijumaruRings) - 1);
return nijumaruRings[ringIndex];
}
return u8{0};
}
},
{
ArcIndex::Enter,
[=](auto x, auto y) {
constexpr auto outlineWidth = kMapImageSide / 6.0f;
const auto adx = std::abs((x + 0.5f) - center);
const auto ady = std::abs((y + 0.5f) - center);
const auto dist =
std::min(adx + ady, std::max(adx, ady) * std::numbers::sqrt2_v<float>) -
kMapImageSide / 2.0f;
return dist > 0.0f ? 0 : (dist > -outlineWidth ? 0x22 : 0x33);
}
},
{
ArcIndex::TryForce,
[=](auto x, auto y) {
constexpr auto innerRadiusNorm = 5.0f / 12.0f;
constexpr auto innerRadius = kMapImageSide * innerRadiusNorm;
constexpr auto innerRadiusSq = innerRadius * innerRadius;
constexpr auto triRadius = kMapImageSide * innerRadiusNorm / 2.0f;
const auto dx = (x + 0.5f) - center;
const auto dy = (y + 0.5f) - center;
const auto dSq = dx * dx + dy * dy;
const auto triSideDist = (std::numbers::sqrt3_v<float> * std::abs(dx) - dy) * 0.5f;
const auto insideTri = std::max(dy, triSideDist) < triRadius;
return insideTri ? 0x22 : (dSq < radiusSq ? (dSq < innerRadiusSq ? 0x33 : 0x22) : 0);
}
}
});
// clang-format on
for (const auto r : replacements) {
auto pixels = std::make_unique_for_overwrite<u8[]>(kMapImageTotalPixels);
paint_i8(std::span{pixels.get(), kMapImageTotalPixels}, kMapImageSide, r.painter);
auto& icon = s_icons[static_cast<int>(r.index)];
icon.newData = std::move(pixels);
icon.label = fmt::format("hq minimap icon {}", static_cast<int>(r.index));
}
}
} // namespace
namespace dusk::hq_minimap {
void register_pointer(int idx, u8* ptr) {
if (s_initialized) {
return;
}
s_icons[idx].origData = ptr;
}
void set_active(bool active) {
s_active = active;
}
void update() {
if (!s_initialized) {
return;
}
for (auto& [idx, icon] : s_icons) {
const bool shouldBeRegistered = s_active && icon.origData && icon.newData;
if (shouldBeRegistered && !icon.reg) {
aurora::texture::ReplacementKey key{aurora::texture::TexturePointerKey{icon.origData}};
aurora::texture::RawTextureReplacement repl{
.bytes = std::span{icon.newData.get(), kMapImageTotalPixels},
.width = kMapImageSide,
.height = kMapImageSide,
.mipCount = 1,
.gxFormat = GX_TF_I8,
.label = icon.label,
};
icon.reg = aurora::texture::register_replacement(
key, repl, {.priority = kInternalTextureReplacementPriority});
} else if (!shouldBeRegistered && icon.reg) {
aurora::texture::unregister_replacement(*icon.reg);
icon.reg.reset();
}
}
}
void initialize_if_needed() {
if (s_initialized) {
return;
}
draw_all_replacements();
s_initialized = true;
update();
}
} // namespace dusk::hq_minimap
+21
View File
@@ -0,0 +1,21 @@
#pragma once
namespace dusk::hq_minimap {
/// Adds a mapping of an image resource index (into `Always.arc`) to the pointer to its image data.
/// If `initialize_if_needed` has been called, this is a no-op. Pointers are expected to be stable
/// and valid for the program's entire lifetime.
void register_pointer(int idx, u8* ptr);
/// Sets whether HQ minimap texture replacements should be active or not. Does not manage
/// replacement registrations itself; see `update`.
void set_active(bool active);
/// Registers or unregisters texture replacements depending on active state.
void update();
/// Called once after registering image pointers, in which their HQ replacements are procedurally
/// drawn and `update` is called. Further calls are no-ops.
void initialize_if_needed();
}

Some files were not shown because too many files have changed in this diff Show More