fix(game): delete beaten monsters so ambushes start fights

This commit is contained in:
capyb2222 committed 2026-09-12 22:05:15 -07:00
1 parent 38880e4976
commit 201f1bb07f
3 files changed
+38 -2

No files matched your search

+17 -1
View File
@@ -184,6 +184,22 @@ void onSceneCastSkillCostMp(net::Session& session, const proto::SceneCastSkillCo
int64_t now() { return static_cast<int64_t>(util::nowSec()); }
// The monsters a won fight removes. A Stagnant Shadow stays, to be farmed again.
std::vector<uint32_t> defeated(const Player& player, const std::vector<uint32_t>& entityIds) {
const data::Tables& tables = data::Tables::get();
std::vector<uint32_t> out;
for (uint32_t entityId : entityIds) {
const SceneEntity* entity = player.sceneState().find(entityId);
if (entity == nullptr || entity->kind != EntityKind::Monster) continue;
uint32_t stage = entity->stageId != 0
? entity->stageId
: tables.stageForEvent(entity->eventId, player.worldLevel());
if (stage != 0 && tables.farmElement(stage) != nullptr) continue;
out.push_back(entityId);
}
return out;
}
// Calyx: the one fight a srtools build is allowed to take over, monsters, blessings and
// all. `wave` is how many runs were bought, one stage each.
BattleRequest cocoonRequest(uint32_t cocoonId, uint32_t wave, uint32_t worldLevel) {
@@ -453,7 +469,7 @@ void onPveBattleResult(net::Session& session, const proto::PVEBattleResultCsReq&
BattleContext& context = player->battle();
bool won = req.end_status == proto::BattleEndStatus::BATTLE_END_WIN && context.active &&
context.battleId == req.battle_id;
if (won) notify::monstersRemoved(session, *player, context.monsterEntityIds);
if (won) notify::monstersRemoved(session, *player, defeated(*player, context.monsterEntityIds));
// A farming run costs and pays only when it is won; losing or fleeing is free.
std::vector<data::ItemStack> drops;
if (won && (context.staminaCost != 0 || context.mappingInfoId != 0)) {
+3 -1
View File
@@ -85,7 +85,9 @@ void monstersRemoved(net::Session& session, Player& player,
for (uint32_t groupId : groups) {
auto& group = refresh.group_refresh_list.emplace_back();
group.group_id = groupId;
group.refresh_type = proto::SceneGroupRefreshType::SCENE_GROUP_REFRESH_TYPE_LOADED;
// Deletes go out as type 3. LOADED sends the client down a legacy path that
// throws, leaving the monster alive on its side but gone from ours.
group.refresh_type = proto::SceneGroupRefreshType::SCENE_GROUP_REFRESH_TYPE_AFIBFMAFNCC;
for (uint32_t entityId : entityIds) {
const SceneEntity* entity = player.sceneState().find(entityId);
if (entity == nullptr || entity->groupId != groupId) continue;
+18
View File
@@ -402,6 +402,24 @@ void runFlowTests() {
check(resultRsp.battle_id == result.battle_id, "battle id echoed");
check(resultRsp.end_status == proto::BattleEndStatus::BATTLE_END_WIN,
"end status echoed");
// LOADED here throws in the client and leaves the monster standing.
// Earlier refreshes (the lineup edit's) are still queued, so skip to
// the one that deletes.
bool deleted = false;
net::Packet refreshPacket;
while (!deleted && client.await(cmd::SceneGroupRefreshScNotify, refreshPacket, 500)) {
proto::SceneGroupRefreshScNotify refresh;
if (!parseBody(refreshPacket, refresh)) break;
for (const proto::GroupRefreshInfo& group : refresh.group_refresh_list) {
for (const proto::SceneEntityRefreshInfo& change : group.refresh_entity) {
if (change.delete_entity != monsters[0]) continue;
deleted = group.refresh_type ==
proto::SceneGroupRefreshType::SCENE_GROUP_REFRESH_TYPE_AFIBFMAFNCC;
}
}
}
check(deleted, "the beaten monster is deleted the way the client accepts");
}
}