Stage Service: Support Player Spawns (#2311)

* Stage Service: Support Player Spawns

* Allow for spawn deletion, double-check spawn sizes

* Advance player_data on continue

---------

Co-authored-by: Luke Street <luke@street.dev>
This commit is contained in:
jdflyer
2026-09-03 10:01:42 -06:00
committed by GitHub
parent b748eaca1e
commit fb4142c7ef
2 changed files with 58 additions and 23 deletions
+55 -22
View File
@@ -1614,17 +1614,15 @@ DUSK_GAME_DATA dStage_roomControl_c::roomDzs_c dStage_roomControl_c::m_roomDzs;
u8 dStage_roomControl_c::mNoArcBank;
#endif
static void dStage_actorCreate(stage_actor_data_class* i_actorData, fopAcM_prm_class* i_actorPrm
IF_DUSK_ARG(size_t recordSize = sizeof(stage_actor_data_class))) {
#if TARGET_PC
static void dStage_actorCreate(stage_actor_data_class* i_actorData, fopAcM_prm_class* i_actorPrm,
size_t recordSize = sizeof(stage_actor_data_class)) {
if (!dusk::mods::svc::stage_apply_actor_edits(i_actorData, i_actorPrm, recordSize,
i_actorPrm->room_no))
{
JKRFree(i_actorPrm);
return;
}
#else
static void dStage_actorCreate(stage_actor_data_class* i_actorData, fopAcM_prm_class* i_actorPrm) {
#endif
dStage_objectNameInf* actorInf = dStage_searchName(i_actorData->name);
@@ -1687,6 +1685,8 @@ static int dStage_playerInit(dStage_dt_c* i_stage, void* i_data, int num, void*
fopAcM_prm_class* appen = fopAcM_CreateAppend();
JUT_ASSERT(1586, appen != NULL);
IF_DUSK(stage_actor_data_class newPoint{};) // data stored on the stack for any newly registered spawn points
int point = dComIfGp_getStartStagePoint();
u32 roomParam = dComIfGs_getRestartRoomParam();
if (point == -2 || point == -3) {
@@ -1707,6 +1707,13 @@ static int dStage_playerInit(dStage_dt_c* i_stage, void* i_data, int num, void*
int i;
for (i = 0; i < num; i++) {
#ifdef TARGET_PC
// If, for whatever reason, we want to patch the point ID, we need to do it here.
if (!dusk::mods::svc::stage_apply_actor_edits(player_data, nullptr, sizeof(*player_data), i_stage->getRoomNo())) {
player_data++;
continue; // If the spawn is deleted, check the next one
}
#endif
if ((u8)player_data->base.angle.z == unk) {
break;
}
@@ -1714,16 +1721,43 @@ static int dStage_playerInit(dStage_dt_c* i_stage, void* i_data, int num, void*
}
if (i == num) {
#if TARGET_PC
std::vector<s16> valid_points;
valid_points.reserve(num);
player_data = player->m_entries;
for (i = 0; i < num; i++) {
valid_points.push_back(player_data->base.angle.z);
player_data++;
// Search through all new actor edits and return the point data via userdata
struct newActors_userData {
stage_actor_data_class* out_point;
bool out_pointSet;
int16_t pointNo;
};
newActors_userData params = {
&newPoint, false, (int16_t)unk
};
dusk::mods::svc::stage_create_new_actors(i_stage->getRoomNo(),
[](void* user, const void* record, size_t size) {
const stage_actor_data_class* i_record = static_cast<const stage_actor_data_class*>(record);
auto* params = static_cast<newActors_userData*>(user);
// We don't cast i_record->base.angle.z to a u8 here so we can register any spawn point within the s16 range
if (i_record->base.angle.z == params->pointNo && size == sizeof(stage_actor_data_class) && strncmp(i_record->name,"Link",7) == 0) {
std::memcpy(params->out_point, i_record, size);
params->out_pointSet = true;
}
},
&params);
if (params.out_pointSet) {
player_data = &newPoint;
} else {
// If the requested spawn point isn't found, print all valid points within the log
std::vector<s16> valid_points;
valid_points.reserve(num);
player_data = player->m_entries;
for (i = 0; i < num; i++) {
valid_points.push_back(player_data->base.angle.z);
player_data++;
}
std::ranges::sort(valid_points);
DuskLog.fatal("Failed to find player start point for next stage! Requested point: {}, Valid points: [{}]",
point, fmt::join(valid_points, ", "));
}
std::ranges::sort(valid_points);
DuskLog.fatal("Failed to find player start point for next stage! Requested point: {}, Valid points: [{}]",
point, fmt::join(valid_points, ", "));
#else
OS_REPORT_ERROR("プレイヤーが発見できません。[No.%d]\n切り替えの情報や処理の確認をお願いします。\n", point);
#endif
@@ -2129,12 +2163,8 @@ static int dStage_doorInfoInit(dStage_dt_c* i_stage, void* i_data, int entryNum,
appen->base = actor_data->base;
appen->room_no = (int)i_stage->getRoomNo();
appen->scale = tgsc_data->scale;
#if TARGET_PC
dStage_actorCreate(actor_data, appen,
sizeof(stage_actor_data_class) + sizeof(fopAcM_prmScale_class));
#else
dStage_actorCreate(actor_data, appen);
#endif
dStage_actorCreate(actor_data, appen IF_DUSK_ARG(
sizeof(stage_actor_data_class) + sizeof(fopAcM_prmScale_class)));
}
tgsc_data++;
}
@@ -2567,6 +2597,11 @@ static void dusk_stage_svc_new_actor_create(dStage_dt_c* i_stage) {
stage_tgsc_data_class object{};
std::memcpy(&object, record, size);
if (strncmp(object.name,"Link",7) == 0) {
// We create new spawns for Link during dStage_playerInit. Ignore them here so we don't get duplicates.
return;
}
fopAcM_prm_class* appen = fopAcM_CreateAppend();
if (appen != nullptr) {
appen->base = object.base;
@@ -2778,9 +2813,7 @@ void dStage_dt_c_roomReLoader(void* i_data, dStage_dt_c* i_stage, int param_2) {
};
dStage_dt_c_decode(i_data, i_stage, l_funcTable, ARRAY_SIZEU(l_funcTable));
#if TARGET_PC
dusk_stage_svc_new_actor_create(i_stage);
#endif
IF_DUSK(dusk_stage_svc_new_actor_create(i_stage));
layerActorLoader(i_data, i_stage, param_2);
}
+3 -1
View File
@@ -130,7 +130,9 @@ bool stage_apply_actor_edits(void* actorData, void* actorPrm, size_t recordSize,
}
std::memcpy(actorData, winner->record.data(), winner->record.size());
std::memcpy(actorPrm, winner->record.data() + 8, winner->record.size() - 8);
if (actorPrm) {
std::memcpy(actorPrm, winner->record.data() + 8, winner->record.size() - 8);
}
return true;
}