- fix death issues for bot corpses moving and being attackable
- guard hired bots from receiving movement orders - this should not happen for hired bots as they should follow the player
- added BG role re-roll after flag capture
- fix npc flag being removed in BG for hired bots.
- bot now uses their race specific language for SAY/YELL
- bot has a language menu in their gossip to pick from race specific language OR the Alliance Common or Horde Orchis languages.
({uint32 count; count x {uint32 activityId, uint32 reason}} - the layout
extractor mislabeled it struct{u32,u32,u32}); returns the current empty
set. Soft-blacklist model left for a sniff-confirmed follow-up.
- Listing expiry now notifies the leader (UPDATE_EXPIRATION + a final
UPDATE_STATUS unlisted) before the ticker drops the listing.
- Config: LFGList.ListingExpiryMinutes (120), LFGList.MaxSearchResults (100).
Completes handler coverage for the whole LFG_LIST_* client protocol.
application id (both directions agree on which application a RideTicket names):
- APPLY_TO_GROUP: register applicant (role mask + live spec/item level),
confirm with APPLY_TO_GROUP_RESULT, notify applicant (APPLICATION_STATUS
= Applied) and push the refreshed APPLICANT_LIST_UPDATE to the leader.
- CANCEL_APPLICATION / DECLINE_APPLICANT: applicant- and leader-initiated
removal with the appropriate status update and leader-list refresh.
- INVITE_APPLICANT -> INVITE_RESPONSE: leader invites; on accept the applicant
joins (or forms) the leader's party via GroupMgr, mirroring LFGMgr's group
creation; on decline the application is dropped.
LFGListMgr gains application storage + an application index with cleanup on
listing removal. Five opcodes registered (STATUS_LOGGEDIN).
HandleLFGListSearch filters published listings by category + activity
group (0 = wildcard, capped by LFGList.MaxSearchResults) directly against
the stored listing descriptors, so no GroupFinder DB2 store is required for
basic filtering. Builds SMSG_LFG_LIST_SEARCH_RESULTS rows (id, activity,
leader, live member count from GroupMgr, echoed listing info) and follows
with SMSG_LFG_LIST_SEARCH_STATUS(complete). CMSG_LFG_LIST_SEARCH registered.
On completion, award each player crests of the tier matching the keystone level.
Currency ids are EXTRACTED from CurrencyTypes.db2 @68275 (Midnight Season 1
Dawncrests: Veteran 3341, Champion 3343, Hero 3345, Myth 3347) - real values, not
guessed. Tier breakpoints (<=3 / <=6 / <=9 / 10+) and the per-run amount are season
tuning, so they are config-tunable (ChallengeMode.Crest.*). Granted via AddCurrency
with CurrencyGainSource::Loot, guarded on sCurrencyTypesStore.LookupEntry (safe no-op
if the currency is absent on this build).
Extraction also PROVED the RewardQuestID path is a dead end for modern M+: every
MapChallengeMode row from Legion onward has all-zero {First,}RewardQuestID arrays
(only legacy MoP/WoD challenge modes are populated) - so end-of-run rewards never
flowed through it. Details: c:\dumps\mplus_reward_extract_68275.md.
Builds clean (parallel 1, incremental).
Broadcast SMSG_CHALLENGE_MODE_COMPLETE to the party on completion
(ChallengeMode::Complete), carrying the finished run (MapSummary: map/level/affixes
+ present players as members).
The full deserializer tree was ground out via idat to the bottom: outer
sub_7FF729090EE0 -> inner sub_7FF729090C10 -> 112-byte run element sub_7FF7291CBD40
-> sub_7FF7291CC010; the deepest calls (sub_7FF7291CE090 / sub_7FF729184E40) are
std::vector grow/resize and read no wire, so every byte is now accounted for. The
packet is byte-aligned (the "bit7" helper reads store whole bytes -> uint8, no
bitstream). Full spec: c:\dumps\COMPLETE_PACKET_WIRE_68275.md.
The three trailing DungeonScoreData lists (pairs / runs / names) are sent empty:
the server does not persist the full per-run score tree. The framing is exact so
this is display-only, never a desync. Factored a shared operator<< for the
map-summary struct (also used by ALL_MAP_STATS). Even upstream TC leaves this SMSG
STATUS_UNHANDLED.
On a completed run the activated keystone is upgraded in place: a timed clear
raises it by the earned amount (+1/+2/+3) and rerolls it to a random dungeon
from the active season pool with fresh affixes; an over-time clear depletes it
by one (floor +2). This closes the progression loop without depending on the
seasonal keystone item entry (not resolvable offline), by editing the existing
item's ITEM_MODIFIER_CHALLENGE_* modifiers.
End-of-run loot, the Great Vault M+ track and the SMSG_MYTHIC_PLUS_ALL_MAP_STATS
UI reply are tracked follow-ups (see MYTHICPLUS_IMPLEMENTATION_STATUS_68275.md).
Builds clean.
Wire the first two Challenge Mode client requests to real replies, replacing
Handle_NULL. Packet layouts are confirmed from the client deserializers
(sub_7FF729091240 / sub_7FF729091470->290 @68275), not guessed:
- SMSG_MYTHIC_PLUS_SEASON_DATA = { bool IsMythicPlusActive } (single bit).
- SMSG_MYTHIC_PLUS_CURRENT_AFFIXES = { uint32 Count; N x {int32 KeystoneAffixID;
int32 SeasonID} } (plain 4-byte LE; field 2 = SeasonID per
C_MythicPlus.GetCurrentAffixes -> {id, seasonID}).
New ChallengeModePackets.{h,cpp} (registered in AllPackets.h), WorldSession
forward decls + handler decls, ChallengeModeHandler.cpp:
- HandleRequestMythicPlusSeasonData -> IsMythicPlusActive = season resolved.
- HandleRequestMythicPlusAffixes -> the configured weekly affixes + season.
Both driven by sChallengeModeMgr. worldserver.conf.dist gains ChallengeMode.*.
CMSG_REQUEST_MYTHIC_PLUS_{SEASON_DATA,AFFIXES} flipped to STATUS_LOGGEDIN.
New greenfield ChallengeMode module providing the static-data + scaling
foundation for Mythic Keystone dungeons:
- Per-level creature HP/damage multipliers via GlobalCurve
ChallengeModeHealth(21)/ChallengeModeDamage(22) -> CurvePoint (reproduces the
client GetPowerLevelDamageHealthMod curve, ~+10%/level). Fills the prior NYI gap.
- Dungeon pool, par times and +1/+2/+3 keystone-upgrade thresholds read from
MapChallengeMode.CriteriaCount[0..2].
- Active-season resolution + weekly affix schedule, config-driven
(ChallengeMode.SeasonId / AffixSchedule / AffixLevelBands) since the live
season/rotation is a runtime pointer, not a static DB2 value.
Initialized from World::SetInitialWorldSettings