Add AdventureBot social interactions and player-led groups
This commit is contained in:
1 parent
39a515dbd1
commit
e333f2f9fc
29 files changed
+2003
-29
No files matched your search
@@ -1,3 +1,51 @@
|
||||
# Aktuell: Interaktionen und spielergeführte Gruppen umgesetzt
|
||||
|
||||
Stand 28.10.2026. Nutzer beauftragte Umsetzung nach gemeinsamer Planung.
|
||||
Branch feature/clientless-player-core; vorher sauber. Nutzer hat den lokalen
|
||||
Commit dieses Stands beauftragt; kein Push. Neuer AdventureInteractionMgr/Policy,
|
||||
generische GUID-Queues für Textemotes und tatsächliche Kampfhilfe, native
|
||||
Einladungsantworten, intent-/persönlichkeitsabhängige Annahme, MMAP-Folgen und
|
||||
TravelGraph-Handoff, grundlegende Gruppen-Kampfunterstützung. Spieler/Pet-
|
||||
Selbstverteidigung im bestehenden CombatMgr, keine eigenständigen PvP-Angriffe.
|
||||
Details und Grenzen in doc/clientless/interactions.md.
|
||||
|
||||
Austritt nur außerhalb Kampf/Tod/Instanz/Transport/Flug. Intent-/Locationwechsel
|
||||
oder manuelle Blockfrist erlaubt Entscheidung; group_loyalty beeinflusst Bleiben
|
||||
(Arthoran0.35, Standard0.25). Native AFK oder beobachtete Inaktivität des
|
||||
Anführers volle600s; Führungswechsel beginnt neues Zeitfenster. Chat/Emotes,
|
||||
Zauber/Itemanfragen, Bewegung/Blick/Ziel, Kampf/Casts zählen als Aktivität.
|
||||
Native Gruppenmitgliedschaft bleibt über Neustart; Beobachtungsfristen starten
|
||||
neu. Rollenprofile, autonome Bot-Gruppen, freie Chatgespräche und eigenständige
|
||||
PvP-Erstangriffe bleiben ausdrücklich spätere Arbeit.
|
||||
|
||||
Debug-Build-j10 erfolgreich installiert. 57 Testskripte bestanden: vollständiger
|
||||
56er-Lauf plus neuer Social-Queue-Concurrencytest; der anschließend erweiterte
|
||||
Combat-Test für echten Spielerfokus zusätzlich erneut erfolgreich. Native
|
||||
Testserver PID59086, frische Ready-Meldung und Port8085 bestätigt. Arthoran26
|
||||
online, Persönlichkeit8Werte, neue Diagnose clientless interaction26 erfolgreich:
|
||||
sociability0.65/loyalty0.35, keine Gruppe. Echte Kreaturen-Selbstverteidigung
|
||||
nach Neustart weiterhin beobachtet, kein Beleg für Live-PvP/Gruppenannahme.
|
||||
|
||||
Arthorans unveränderte initiale Profilvorgabe startete bei Login die normale
|
||||
Goldhain-Reise. clientless adventure stop26 wurde angefordert, letzte Diagnose
|
||||
noch Switching/waiting for safe player/transport state. Keine Teleportierung,
|
||||
kein temporärer Profiloverride; normale Herkunftsdateien bleiben erhalten.
|
||||
Für Live-Abnahme Position und Reise zuerst erneut prüfen. Gruppeneinladungen
|
||||
werden in Flug-/Transport-/Kampfzuständen bis zur sicheren Lage zurückgestellt.
|
||||
|
||||
Beim sauberen Shutdown der ALTEN, vor Installation laufenden Binärdatei trat
|
||||
Object.h:448 GetMap ASSERTION FAILED m_currMap auf. AlterProzess49765 endete;
|
||||
keine fremden Prozesse beendet. Das ist ein beobachteter Shutdownfehler des
|
||||
Ausgangsstands, Ursache hier nicht untersucht/behoben. Neuer Start erfolgreich.
|
||||
|
||||
Offene Gameplay-Abnahmen: echte Spielereinladungen, zwei gleichzeitige Bots,
|
||||
PvP/Pet-Verteidigung, native Gruppenportale/kartenübergreifendes Folgen,
|
||||
zehn echte Minuten AFK. Klassenrotationen außer Krieger und Supportrollen
|
||||
weiterhin begrenzt. Belege: checkpoints/2026-10-28-interactions/.
|
||||
Logs /tmp/vcore-interactions-{build-final,install}.log und zugehörige Tests.
|
||||
|
||||
---
|
||||
|
||||
# Aktuell: Selbstversorgung und gemeinsame Nebenberufe abgeschlossen
|
||||
|
||||
Stand 27.10.2026. Branch feature/clientless-player-core, vorheriger Commit
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
[1/10] cd /home/wow_admin/wow_cata_server/source/TrinityCore && /usr/bin/cmake -DBUILDDIR="/home/wow_admin/wow_cata_server/build/debug" -P /home/wow_admin/wow_cata_server/source/TrinityCore/cmake/genrev.cmake /home/wow_admin/wow_cata_server/build/debug
|
||||
[2/10] Building CXX object src/server/game/CMakeFiles/game.dir/World/AdventureCombatMgr.cpp.o
|
||||
[3/10] Building CXX object src/server/game/CMakeFiles/game.dir/World/ClientlessLuaEngine.cpp.o
|
||||
[4/10] Building CXX object src/server/game/CMakeFiles/game.dir/World/AdventureInteractionMgr.cpp.o
|
||||
[5/10] Building CXX object src/server/game/CMakeFiles/game.dir/World/AdventureSupplyMgr.cpp.o
|
||||
[6/10] Building CXX object src/server/scripts/CMakeFiles/scripts.dir/Custom/viano_bot_commands.cpp.o
|
||||
/home/wow_admin/wow_cata_server/source/TrinityCore/src/server/scripts/Custom/viano_bot_commands.cpp: In function ‘bool VianoBotCommands::HandleClientlessLocationAdd(ChatHandler*, const char*)’:
|
||||
/home/wow_admin/wow_cata_server/source/TrinityCore/src/server/scripts/Custom/viano_bot_commands.cpp:2341:20: warning: unused variable ‘loaded’ [-Wunused-variable]
|
||||
2341 | bool const loaded = sClientlessWorldPointMgr->Reload();
|
||||
| ^~~~~~
|
||||
/home/wow_admin/wow_cata_server/source/TrinityCore/src/server/scripts/Custom/viano_bot_commands.cpp: In function ‘bool VianoBotCommands::HandleClientlessLocationMove(ChatHandler*, const char*)’:
|
||||
/home/wow_admin/wow_cata_server/source/TrinityCore/src/server/scripts/Custom/viano_bot_commands.cpp:2378:20: warning: unused variable ‘loaded’ [-Wunused-variable]
|
||||
2378 | bool const loaded = sClientlessWorldPointMgr->Reload();
|
||||
| ^~~~~~
|
||||
/home/wow_admin/wow_cata_server/source/TrinityCore/src/server/scripts/Custom/viano_bot_commands.cpp: In function ‘bool VianoBotCommands::HandleClientlessLocationRemove(ChatHandler*, const char*)’:
|
||||
/home/wow_admin/wow_cata_server/source/TrinityCore/src/server/scripts/Custom/viano_bot_commands.cpp:2402:20: warning: unused variable ‘loaded’ [-Wunused-variable]
|
||||
2402 | bool const loaded = sClientlessWorldPointMgr->Reload();
|
||||
| ^~~~~~
|
||||
[7/10] Building CXX object src/server/game/CMakeFiles/game.dir/World/AdventureBehaviorMgr.cpp.o
|
||||
[8/10] Linking CXX static library src/server/game/libgame.a
|
||||
[9/10] Linking CXX static library src/server/scripts/libscripts.a
|
||||
[10/10] Linking CXX executable src/server/worldserver/worldserver
|
||||
@@ -0,0 +1 @@
|
||||
Production combat focus, damage ordering, risk filters, corpse radius/delay, alternate paths and malus-free persisted fallback passed.
|
||||
@@ -0,0 +1,5 @@
|
||||
/tmp/tmp2j7tsqhj/test.cpp: In member function ‘void AdventureInteractionMgr::Leave(Player*, Record&, const char*)’:
|
||||
/tmp/tmp2j7tsqhj/test.cpp:140:69: warning: unused parameter ‘reason’ [-Wunused-parameter]
|
||||
140 | void AdventureInteractionMgr::Leave(Player* p,Record& r,char const* reason)
|
||||
| ~~~~~~~~~~~~^~~~~~
|
||||
production interaction manager: invitations, factions, reservations, follow, AFK and deferred departure passed
|
||||
@@ -0,0 +1 @@
|
||||
Production social/helper queues: concurrent GUID-only delivery, bounded queues, human/self/zero-damage gates, pet attribution and lethal-cleanup ordering passed
|
||||
@@ -0,0 +1,114 @@
|
||||
Validating spell scripts...
|
||||
Spell `85123` Effect `Index: EFFECT_0 Target: 7` of script `spell_siege_cannon`
|
||||
did not match dbc effect data - handler bound to hook `OnObjectAreaTargetSelect`
|
||||
of SpellScript won't be executed
|
||||
>> Validated 3470 scripts in 12 ms
|
||||
Loading SmartAI scripts...
|
||||
>> Loaded SmartAIMgr Helpers in 16 ms
|
||||
>> Loaded 13506 SmartAI scripts in 167 ms
|
||||
Loading Calendar data...
|
||||
>> Loaded 0 calendar events in 1 ms
|
||||
>> Loaded 0 calendar invites in 0 ms
|
||||
Loading Petitions...
|
||||
>> Loaded 0 petitions.
|
||||
Loading Signatures...
|
||||
>> Loaded 0 Petition signs!
|
||||
Loading Summon Properties parameter data...
|
||||
>> Loaded 78 summon properties parameters in 1 ms
|
||||
Loading Item loot...
|
||||
>> Loaded 0 stored item loots
|
||||
>> Loaded 0 stored item money
|
||||
Initialize query data...
|
||||
>> Initialized query cache data in 1019 ms
|
||||
Initialize commands...
|
||||
Initialize game time and timers
|
||||
Mail timer set to: 1260, mail return is called every 1440 minutes
|
||||
Starting Map System
|
||||
Starting Game Event system...
|
||||
Initialize AuctionHouseBot...
|
||||
Initializing Opcodes...
|
||||
Starting Arena Season...
|
||||
Starting Battleground System
|
||||
>> Loaded 15 battlegrounds in 0 ms
|
||||
Starting Outdoor PvP System
|
||||
>> Loaded 5 outdoor PvP definitions in 1 ms
|
||||
Starting Battlefield System
|
||||
>> Loaded 2 battlefields in 0 ms
|
||||
Loading Warden Checks...
|
||||
Loading Warden Action Overrides...
|
||||
Deleting expired bans...
|
||||
Initializing quest reset times...
|
||||
Calculate random battleground reset time...
|
||||
Calculate guild limitation(s) reset time...
|
||||
Calculate next currency reset time...
|
||||
World initialized in 0 minutes 13 seconds
|
||||
TC>TC>TrinityCore rev. 39a515dbd13e+ 2026-10-27 23:56:43 +0100 (feature/clientle
|
||||
ss-player-core branch) (Unix, Debug, Static) (worldserver-daemon) ready...
|
||||
clientless login 26
|
||||
TC>Registered clientless session for character GUID 26 (account 6), state=Loadin
|
||||
g.
|
||||
Created clientless player session for character GUID 26 (account 6).
|
||||
ClientlessPlayerMgr started character GUID 26 (account 6).
|
||||
Clientless login started for 'Arthoran' (GUID 26, account 6).
|
||||
TC> Loading saved respawn time of 1790175804 for spawnid (0,323981) - spawn does
|
||||
not exist, ignoring
|
||||
Loading saved respawn time of 1790090644 for spawnid (0,396680) - spawn does not
|
||||
exist, ignoring
|
||||
Loading saved respawn time of 1790019808 for spawnid (0,396681) - spawn does not
|
||||
exist, ignoring
|
||||
Loading saved respawn time of 1790019853 for spawnid (0,396682) - spawn does not
|
||||
exist, ignoring
|
||||
Initialized transport GameObject grids: map=0 instance=0 grids=4 candidates=15
|
||||
Clientless player character GUID 26 (account 6) reached state=Online.
|
||||
ClientlessLua initialized, script root '/home/wow_admin/wow_cata_server/source/T
|
||||
rinityCore/src/server/scripts/Custom/clientless'.
|
||||
[AdventureBot][Profile] loaded guid=26 name='Arthoran' source='/home/wow_admin/w
|
||||
ow_cata_server/source/TrinityCore/src/server/scripts/Custom/clientless/adventure
|
||||
/profiles/arthoran.lua' home='elwynn' canGroundMount=1 mounts=1 mountMinDistance
|
||||
=45.0 mountSpeedRate=1.60 personality=8 regionAffinity=4 activityAffinity=7
|
||||
PROFILE_PROFESSIONS guid=26 primary=2
|
||||
ClientlessLua[26]: Arthoran ready; no automatic test journey queued
|
||||
ClientlessLua[26]: executed adventure override script '/home/wow_admin/wow_cata_
|
||||
server/source/TrinityCore/src/server/scripts/Custom/clientless/adventure/overrid
|
||||
es/arthoran.lua'.
|
||||
ADVENTURE_CATALOG locations=2973 actions=84 lines=76
|
||||
INTENT_TRAVEL guid=26 intent='social' location='goldshire_tavern' area=0
|
||||
[AdventureBot] loaded 3966 clientless world points.
|
||||
[AdventureBot] loaded travel graph: 993 node(s), 1765 directed edge(s).
|
||||
clientless interaction 26
|
||||
TC>Interactions guid=26 grouped=0 leader=0 partner=0 intent='social' schedule=''
|
||||
leaderIdleSeconds=0 afkSeconds=0 sociability=0.65 loyalty=0.35
|
||||
TC> SUPPLY_RESTOCK guid=26 day=20261028 daily=0 afterCombat=1
|
||||
COMBAT_START guid=26 target='GUID Full: 0xf130a1fe000006ce Type: Creature Entry:
|
||||
41470 Low: 1742' training=0 defending=1 until=0
|
||||
COMBAT_CAST guid=26 target='GUID Full: 0x000000000000001a Type: Player Low: 26'
|
||||
spell=6673
|
||||
COMBAT_CAST guid=26 target='GUID Full: 0xf130a1fe000006ce Type: Creature Entry:
|
||||
41470 Low: 1742' spell=78
|
||||
COMBAT_CAST guid=26 target='GUID Full: 0xf130a1fe000006ce Type: Creature Entry:
|
||||
41470 Low: 1742' spell=12294
|
||||
COMBAT_CAST guid=26 target='GUID Full: 0xf130a1fe000006ce Type: Creature Entry:
|
||||
41470 Low: 1742' spell=772
|
||||
COMBAT_END guid=26 target='GUID Full: 0xf130a1fe000006ce Type: Creature Entry: 4
|
||||
1470 Low: 1742' training=0
|
||||
INTENT_TRAVEL guid=26 intent='social' location='goldshire_tavern' area=0
|
||||
SUPPLY_RESTOCK guid=26 day=20261028 daily=0 afterCombat=1
|
||||
COMBAT_START guid=26 target='GUID Full: 0xf130a1ff000006d0 Type: Creature Entry:
|
||||
41471 Low: 1744' training=0 defending=1 until=0
|
||||
COMBAT_CAST guid=26 target='GUID Full: 0xf130a1ff000006d0 Type: Creature Entry:
|
||||
41471 Low: 1744' spell=12294
|
||||
COMBAT_END guid=26 target='GUID Full: 0xf130a1ff000006d0 Type: Creature Entry: 4
|
||||
1471 Low: 1744' training=0
|
||||
SUPPLY_RESTOCK guid=26 day=20261028 daily=0 afterCombat=1
|
||||
INTENT_TRAVEL guid=26 intent='social' location='goldshire_tavern' area=0
|
||||
clientless adventure stop 26
|
||||
TC>Behavior stop requested at a safe point.
|
||||
TC> clientless adventure status 26
|
||||
TC>clientless interaction 26
|
||||
TC>Adventure GUID 26: enabled=1 schedule=0 phase=3 intent='social' location='gol
|
||||
dshire_tavern' micro='' deadline=0 detail='waiting for safe player/transport sta
|
||||
te'
|
||||
TC> Interactions guid=26 grouped=0 leader=0 partner=0 intent='social' schedule='
|
||||
'
|
||||
leaderIdleSeconds=0 afkSeconds=0 sociability=0.65 loyalty=0.35
|
||||
TC>
|
||||
@@ -0,0 +1,968 @@
|
||||
TrinityCore rev. 39a515dbd13e+ 2026-10-27 23:56:43 +0100 (feature/clientless-pla
|
||||
yer-core branch) (Unix, Debug, Static) (worldserver-daemon)
|
||||
<Ctrl-C> to stop.
|
||||
|
||||
______ __
|
||||
/\__ _\ __ __/\ \__
|
||||
\/_/\ \/ _ __ /\_\ ___ /\_\ \, _\ __ __
|
||||
\ \ \/\`'__\/\ \ /' _ `\/\ \ \ \/ /\ \/\ \
|
||||
\ \ \ \ \/ \ \ \/\ \/\ \ \ \ \ \_\ \ \_\ \
|
||||
\ \_\ \_\ \ \_\ \_\ \_\ \_\ \__\\/`____ \
|
||||
\/_/\/_/ \/_/\/_/\/_/\/_/\/__/ `/___/> \
|
||||
C O R E /\___/
|
||||
http://TrinityCore.org \/__/
|
||||
|
||||
Using configuration file /home/wow_admin/wow_cata_server/server/debug/etc/worlds
|
||||
erver.conf.
|
||||
Using SSL version: OpenSSL 3.5.5 27 Jan 2026 (library: OpenSSL 3.5.5 27 Jan 2026
|
||||
)
|
||||
Using Boost version: 1.90.0
|
||||
Automatic database updates are disabled for all databases!
|
||||
Realm running as realm ID 2 region 2 battlegroup 1
|
||||
Using World DB: TDB 434.22011
|
||||
Using enUS DBC Locale
|
||||
Will clear `logs` table of entries older than 1209600 seconds every 10 minutes.
|
||||
Client cache version set to: 22011
|
||||
Using DataDir /home/wow_admin/wow_cata_server/data/
|
||||
WORLD: MMap data directory is: /home/wow_admin/wow_cata_server/data/mmaps
|
||||
VMap support included. LineOfSight: 1, getHeight: 1, indoorCheck: 1
|
||||
VMap data directory is: /home/wow_admin/wow_cata_server/data/vmaps
|
||||
Missing name DungeonFinder.LowPopulation.Enable in config file /home/wow_admin/w
|
||||
ow_cata_server/server/debug/etc/worldserver.conf, add "DungeonFinder.LowPopulati
|
||||
on.Enable = 0" to this file
|
||||
Missing name DungeonFinder.LowPopulation.FallbackDelay in config file /home/wow_
|
||||
admin/wow_cata_server/server/debug/etc/worldserver.conf, add "DungeonFinder.LowP
|
||||
opulation.FallbackDelay = 60" to this file
|
||||
Missing name DungeonFinder.LowPopulation.MinPlayers in config file /home/wow_adm
|
||||
in/wow_cata_server/server/debug/etc/worldserver.conf, add "DungeonFinder.LowPopu
|
||||
lation.MinPlayers = 1" to this file
|
||||
Missing name DungeonFinder.LowPopulation.SoloGroupOutsideTimeout in config file
|
||||
/home/wow_admin/wow_cata_server/server/debug/etc/worldserver.conf, add "DungeonF
|
||||
inder.LowPopulation.SoloGroupOutsideTimeout = 300" to this file
|
||||
Loading Trinity strings...
|
||||
>> Loaded 991 trinity strings in 7 ms
|
||||
Initialize data stores...
|
||||
>> Initialized 167 DBC data stores in 857 ms
|
||||
>> Initialized 5 DB2 data stores in 200 ms
|
||||
>> Loading Cinematic Camera files
|
||||
>> Loaded 0 cinematic waypoint sets in 0 ms
|
||||
Loading IP Location Database...
|
||||
Initializing PlayerDump tables...
|
||||
>> Initialized tables for PlayerDump in 14 ms.
|
||||
Loading SpellInfo store...
|
||||
>> Loaded SpellInfo store in 110 ms
|
||||
Loading SpellInfo corrections...
|
||||
>> Loaded SpellInfo corrections in 22 ms
|
||||
Loading SkillLineAbilityMultiMap Data...
|
||||
>> Loaded 8384 SkillLineAbility MultiMap Data in 3 ms
|
||||
Loading SpellInfo custom attributes...
|
||||
>> Loaded 195 spell custom attributes from DB in 2 ms
|
||||
>> Loaded SpellInfo custom attributes in 76 ms
|
||||
Loading SpellInfo diminishing infos...
|
||||
>> Loaded SpellInfo diminishing infos in 5 ms
|
||||
Loading SpellInfo immunity infos...
|
||||
>> Loaded SpellInfo immunity infos in 16 ms
|
||||
Loading GameObject models...
|
||||
>> Loaded 3255 GameObject models in 3 ms
|
||||
Loading Script Names...
|
||||
>> Loaded 4057 ScriptNames in 1376 ms
|
||||
Loading Instance Template...
|
||||
>> Loaded 91 instance templates in 0 ms
|
||||
Loading instances...
|
||||
>> Loaded instances in 6 ms
|
||||
Loading character cache store...
|
||||
Loaded character infos for 13 characters in 0 ms
|
||||
Loading Broadcast texts...
|
||||
>> Loaded 73039 broadcast texts in 360 ms
|
||||
>> Loaded 73039 broadcast text locales in 1637 ms
|
||||
Loading Localization strings...
|
||||
>> Loaded 29790 creature locale strings in 405 ms
|
||||
>> Loaded 19670 gameobject_template_locale strings in 226 ms
|
||||
>> Loaded 14986 Quest locale strings in 999 ms
|
||||
>> Loaded 1946 PageText locale strings in 109 ms
|
||||
>> Loaded 453 points_of_interest locale strings in 4 ms
|
||||
>> Loaded 0 quest_greeting locales. DB table `quest_greeting_locale` is empty.
|
||||
>> Localization strings loaded in 1758 ms
|
||||
Loading Account Roles and Permissions...
|
||||
>> Loaded 666 permission definitions, 650 linked permissions and 4 default permi
|
||||
ssions in 343 ms
|
||||
Loading Page Texts...
|
||||
>> Loaded 1962 page texts in 13 ms
|
||||
Loading Game Object Templates...
|
||||
>> Loaded 29501 game object templates in 156 ms
|
||||
Loading Game Object template addons...
|
||||
>> Loaded 29495 game object template addons in 41 ms
|
||||
Loading Transport templates...
|
||||
>> Loaded 43 transport templates in 9 ms
|
||||
Loading Transport animations and rotations...
|
||||
Loading Transport spawns...
|
||||
>> Spawned 0 continent transports in 0 ms
|
||||
Loading Spell Rank Data...
|
||||
>> Loaded 174 spell rank records in 1 ms
|
||||
Loading Spell Required Data...
|
||||
>> Loaded 22 spell required records in 1 ms
|
||||
Loading Spell Group types...
|
||||
>> Loaded 550 spell group definitions in 2 ms
|
||||
Loading Spell Learn Skills...
|
||||
>> Loaded 137 Spell Learn Skills from DBC in 5 ms
|
||||
Loading SpellInfo SpellSpecific and AuraState...
|
||||
>> Loaded SpellInfo SpellSpecific and AuraState in 10 ms
|
||||
Loading Spell Learn Spells...
|
||||
The spell 17002 listed in `spell_learn_spell` attempts learning talent spell 248
|
||||
67, skipped.
|
||||
The spell 24866 listed in `spell_learn_spell` attempts learning talent spell 248
|
||||
64, skipped.
|
||||
The spell 33872 listed in `spell_learn_spell` attempts learning talent spell 471
|
||||
79, skipped.
|
||||
The spell 33873 listed in `spell_learn_spell` attempts learning talent spell 471
|
||||
80, skipped.
|
||||
>> Loaded 24 spell learn spells, 512 found in Spell.dbc and 31 from TalentTab.db
|
||||
c in 7 ms
|
||||
Loading Spell Proc conditions and data...
|
||||
>> Loaded 1556 spell proc conditions and data in 5 ms
|
||||
Generating spell proc data from SpellMap...
|
||||
Spell Id 36488 has SPELL_ATTR3_CAN_PROC_FROM_PROCS attribute and no restriction
|
||||
on what spells can cause it to proc and no cooldown. This spell can cause infini
|
||||
te proc loops. Proc data for this spell was not generated, data in `spell_proc`
|
||||
table is required for it to function!
|
||||
Spell Id 39440 has SPELL_ATTR3_CAN_PROC_FROM_PROCS attribute and no restriction
|
||||
on what spells can cause it to proc and no cooldown. This spell can cause infini
|
||||
te proc loops. Proc data for this spell was not generated, data in `spell_proc`
|
||||
table is required for it to function!
|
||||
Spell Id 47261 has SPELL_ATTR3_CAN_PROC_FROM_PROCS attribute and no restriction
|
||||
on what spells can cause it to proc and no cooldown. This spell can cause infini
|
||||
te proc loops. Proc data for this spell was not generated, data in `spell_proc`
|
||||
table is required for it to function!
|
||||
Spell Id 47262 has SPELL_ATTR3_CAN_PROC_FROM_PROCS attribute and no restriction
|
||||
on what spells can cause it to proc and no cooldown. This spell can cause infini
|
||||
te proc loops. Proc data for this spell was not generated, data in `spell_proc`
|
||||
table is required for it to function!
|
||||
Spell Id 67726 has SPELL_ATTR3_CAN_PROC_FROM_PROCS attribute and no restriction
|
||||
on what spells can cause it to proc and no cooldown. This spell can cause infini
|
||||
te proc loops. Proc data for this spell was not generated, data in `spell_proc`
|
||||
table is required for it to function!
|
||||
Spell Id 67736 has SPELL_ATTR3_CAN_PROC_FROM_PROCS attribute and no restriction
|
||||
on what spells can cause it to proc and no cooldown. This spell can cause infini
|
||||
te proc loops. Proc data for this spell was not generated, data in `spell_proc`
|
||||
table is required for it to function!
|
||||
Spell Id 67740 has SPELL_ATTR3_CAN_PROC_FROM_PROCS attribute and no restriction
|
||||
on what spells can cause it to proc and no cooldown. This spell can cause infini
|
||||
te proc loops. Proc data for this spell was not generated, data in `spell_proc`
|
||||
table is required for it to function!
|
||||
Spell Id 67744 has SPELL_ATTR3_CAN_PROC_FROM_PROCS attribute and no restriction
|
||||
on what spells can cause it to proc and no cooldown. This spell can cause infini
|
||||
te proc loops. Proc data for this spell was not generated, data in `spell_proc`
|
||||
table is required for it to function!
|
||||
Spell Id 85556 has SPELL_ATTR3_CAN_PROC_FROM_PROCS attribute and no restriction
|
||||
on what spells can cause it to proc and no cooldown. This spell can cause infini
|
||||
te proc loops. Proc data for this spell was not generated, data in `spell_proc`
|
||||
table is required for it to function!
|
||||
>> Generated spell proc data for 1620 spells in 8 ms
|
||||
Loading Aggro Spells Definitions...
|
||||
>> Loaded 44 SpellThreatEntries in 2 ms
|
||||
Loading Spell Group Stack Rules...
|
||||
>> Loaded 41 spell group stack rules in 2 ms
|
||||
>> Parsing SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT stack rules...
|
||||
>> Parsed 23 SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT stack rules in 0 ms
|
||||
Loading NPC Texts...
|
||||
>> Loaded 8406 npc texts in 92 ms
|
||||
Loading Enchant Spells Proc datas...
|
||||
>> Loaded 45 enchant proc data definitions in 1 ms
|
||||
Loading Item Random Enchantments Table...
|
||||
>> Loaded 29217 Item Enchantment definitions in 25 ms
|
||||
Loading Disables
|
||||
>> Loaded 5211 disables in 10 ms
|
||||
Loading Items...
|
||||
>> Loaded 54296 item templates in 39 ms
|
||||
Loading Item set names...
|
||||
>> Loaded 622 item addon templates in 2 ms
|
||||
>> Loaded 21 item script names in 0 ms
|
||||
Loading Creature Model Based Info Data...
|
||||
>> Loaded 31775 creature model based info in 33 ms
|
||||
Loading Creature templates...
|
||||
>> Loaded 64891 creature template models in 77 ms
|
||||
>> Loaded 46633 creature definitions in 580 ms
|
||||
Loading Equipment templates...
|
||||
>> Loaded 16167 equipment templates in 23 ms
|
||||
Loading Creature template addons...
|
||||
Creature (Entry: 47203) has SPELL_AURA_CONTROL_VEHICLE aura 87978 defined in `au
|
||||
ras` field in `creature_template_addon`.
|
||||
>> Loaded 46613 creature template addons in 73 ms
|
||||
Loading Reputation Reward Rates...
|
||||
>> Loaded 16 reputation_reward_rate in 0 ms
|
||||
Loading Creature Reward OnKill Data...
|
||||
>> Loaded 2343 creature award reputation definitions in 7 ms
|
||||
Loading Reputation Spillover Data...
|
||||
>> Loaded 28 reputation_spillover_template in 2 ms
|
||||
Loading Points Of Interest Data...
|
||||
>> Loaded 603 Points of Interest definitions in 2 ms
|
||||
Loading Database Game Tables...
|
||||
>> Loaded 0 npc total health game table entries in 5 ms
|
||||
>> Loaded 0 npc damage by class game table entries in 4 ms
|
||||
Loading Creature Base Stats...
|
||||
>> Loaded 400 creature base stats in 8 ms
|
||||
Loading Spawn Group Templates...
|
||||
>> Loaded 98 spawn group templates in 1 ms
|
||||
Loading Creature Data...
|
||||
Table `creature` has creature (GUID: 396679 Entry: 32491) with disallowed `unit_
|
||||
flags` 32768, removing incorrect flag.
|
||||
>> Loaded 221565 creatures in 1182 ms
|
||||
Loading Temporary Summon Data...
|
||||
>> Loaded 869 temp summons in 4 ms
|
||||
Loading pet levelup spells...
|
||||
>> Loaded 263 pet levelup and default spells for 50 families in 8 ms
|
||||
Loading pet default spells additional to levelup spells...
|
||||
>> Loaded addition spells for 0 pet spell data entries in 2 ms
|
||||
Loading summonable creature templates...
|
||||
>> Loaded 427 summonable creature templates in 10 ms
|
||||
Loading Creature Addon Data...
|
||||
>> Loaded 52002 creature addons in 93 ms
|
||||
Loading Creature Movement Overrides...
|
||||
>> Loaded 0 creature movement overrides. DB table `creature_movement_override` i
|
||||
s empty!
|
||||
Loading Creature Movement Info...
|
||||
>> Loaded 270 creature movement info override entries in 1 ms
|
||||
Loading Gameobject Data...
|
||||
>> Loaded 78938 gameobjects in 392 ms
|
||||
Loading Spawn Group Data...
|
||||
>> Loaded 94612 spawn group members in 58 ms
|
||||
Loading instance spawn groups...
|
||||
>> Loaded 27 instance spawn groups in 2 ms
|
||||
Loading GameObject Addon Data...
|
||||
>> Loaded 24493 gameobject addons in 38 ms
|
||||
Loading GameObject Quest Items...
|
||||
>> Loaded 1352 gameobject quest items in 3 ms
|
||||
Loading Creature Quest Items...
|
||||
>> Loaded 5361 creature quest items in 8 ms
|
||||
Loading Creature Sparring Data...
|
||||
>> Loaded 0 creature sparring templates in 1 ms
|
||||
Loading Creature Linked Respawn...
|
||||
>> Loaded 5239 linked respawns in 7 ms
|
||||
Loading Weather Data...
|
||||
>> Loaded 36 weather definitions in 0 ms
|
||||
Loading Quests...
|
||||
Quest 29507 has `RequiredCurrencyId1` = 393 but `RequiredCurrencyCount1` = 0, qu
|
||||
est can't be done.
|
||||
Quest 29283 has `RequiredCurrencyId1` = 416 but `RequiredCurrencyCount1` = 0, qu
|
||||
est can't be done.
|
||||
Quest 29281 has `RequiredCurrencyId1` = 416 but `RequiredCurrencyCount1` = 0, qu
|
||||
est can't be done.
|
||||
Quest 29279 has `RequiredCurrencyId1` = 416 but `RequiredCurrencyCount1` = 0, qu
|
||||
est can't be done.
|
||||
Quest 29214 has `RequiredCurrencyId1` = 416 but `RequiredCurrencyCount1` = 0, qu
|
||||
est can't be done.
|
||||
Quest 29199 has `RequiredCurrencyId1` = 416 but `RequiredCurrencyCount1` = 0, qu
|
||||
est can't be done.
|
||||
Quest 29181 has `RequiredCurrencyId1` = 416 but `RequiredCurrencyCount1` = 0, qu
|
||||
est can't be done.
|
||||
Quest 13478 has `RequiredCurrencyId1` = 392 but `RequiredCurrencyCount1` = 0, qu
|
||||
est can't be done.
|
||||
Quest 13476 has `RequiredCurrencyId1` = 392 but `RequiredCurrencyCount1` = 0, qu
|
||||
est can't be done.
|
||||
Quest 8371 has `RequiredCurrencyId1` = 392 but `RequiredCurrencyCount1` = 0, que
|
||||
st can't be done.
|
||||
Quest 8368 has `RequiredCurrencyId1` = 392 but `RequiredCurrencyCount1` = 0, que
|
||||
st can't be done.
|
||||
Quest 8367 has `RequiredCurrencyId1` = 392 but `RequiredCurrencyCount1` = 0, que
|
||||
st can't be done.
|
||||
>> Loaded 14991 quests definitions in 347 ms
|
||||
Checking Quest Disables
|
||||
>> Checked 5124 quest disables in 0 ms
|
||||
Loading Quest POI
|
||||
>> Loaded 13941 quest POI definitions in 189 ms
|
||||
Loading Quest Money Rewards
|
||||
>> Loaded 100 quest money reward entries in 1 ms
|
||||
Loading Quests Starters and Enders...
|
||||
Loading GO Start Quest Data...
|
||||
>> Loaded 618 quest relations from gameobject_queststarter in 2 ms
|
||||
Loading GO End Quest Data...
|
||||
>> Loaded 517 quest relations from gameobject_questender in 2 ms
|
||||
Loading Creature Start Quest Data...
|
||||
>> Loaded 8725 quest relations from creature_queststarter in 10 ms
|
||||
Loading Creature End Quest Data...
|
||||
>> Loaded 9136 quest relations from creature_questender in 12 ms
|
||||
Loading Quests Greetings...
|
||||
>> Loaded 0 quest greetings. DB table `quest_greeting` is empty.
|
||||
Loading Objects Pooling Data...
|
||||
>> Loaded 1721 objects pools in 3 ms
|
||||
Loading Creatures Pooling Data...
|
||||
>> Loaded 835 creatures in pools in 2 ms
|
||||
Loading Gameobject Pooling Data...
|
||||
>> Loaded 7404 gameobject in pools in 10 ms
|
||||
Loading Mother Pooling Data...
|
||||
>> Loaded 1218 pools in mother pools in 3 ms
|
||||
Starting objects pooling system...
|
||||
Loading Quest Pooling Data...
|
||||
>> Loaded 56 daily, 5 weekly and 0 monthly quest pools in 2 ms
|
||||
Loading Game Event Data...
|
||||
>> Loaded 156 holiday dates in 1 ms
|
||||
>> Loaded 78 game events in 1 ms.
|
||||
Loading Game Event Saves Data...
|
||||
>> Loaded 0 game event saves in game events. DB table `game_event_save` is empty
|
||||
.
|
||||
Loading Game Event Prerequisite Data...
|
||||
>> Loaded 0 game event prerequisites in game events. DB table `game_event_prereq
|
||||
uisite` is empty.
|
||||
Loading Game Event Creature Data...
|
||||
>> Loaded 2790 creatures in game events in 2 ms.
|
||||
Loading Game Event GO Data...
|
||||
>> Loaded 21863 gameobjects in game events in 10 ms.
|
||||
Loading Game Event Model/Equipment Change Data...
|
||||
>> Loaded 22 model/equipment changes in game events in 2 ms.
|
||||
Loading Game Event Quest Data...
|
||||
>> Loaded 327 quests additions in game events in 1 ms.
|
||||
Loading Game Event GO Quest Data...
|
||||
>> Loaded 189 quests additions in game events in 1 ms.
|
||||
Loading Game Event Quest Condition Data...
|
||||
>> Loaded 0 quest event conditions in game events. DB table `game_event_quest_co
|
||||
ndition` is empty.
|
||||
Loading Game Event Condition Data...
|
||||
>> Loaded 0 conditions in game events. DB table `game_event_condition` is empty.
|
||||
Loading Game Event Condition Save Data...
|
||||
>> Loaded 0 condition saves in game events. DB table `game_event_condition_save`
|
||||
is empty.
|
||||
Loading Game Event NPCflag Data...
|
||||
>> Loaded 0 npcflags in game events. DB table `game_event_npcflag` is empty.
|
||||
Loading Game Event Seasonal Quest Relations...
|
||||
>> Loaded 690 quests additions in game events in 1 ms.
|
||||
Loading Game Event Vendor Additions Data...
|
||||
>> Loaded 2 vendor additions in game events in 1 ms.
|
||||
Loading Game Event Battleground Holiday Data...
|
||||
>> Loaded 8 battleground holidays in game events in 1 ms.
|
||||
Loading Game Event Pool Data...
|
||||
>> Loaded 2 pools for game events in 0 ms.
|
||||
Loading UNIT_NPC_FLAG_SPELLCLICK Data...
|
||||
>> Loaded 702 spellclick definitions in 3 ms
|
||||
Loading Vehicle Template Accessories...
|
||||
>> Loaded 431 Vehicle Template Accessories in 2 ms
|
||||
Loading Vehicle Accessories...
|
||||
>> Loaded 0 Vehicle Accessories in 0 ms
|
||||
Loading Vehicle Seat Addon Data...
|
||||
>> Loaded 15 Vehicle Seat Addon entries in 1 ms
|
||||
Loading SpellArea Data...
|
||||
>> Loaded 872 spell area requirements in 4 ms
|
||||
Loading AreaTrigger definitions...
|
||||
>> Loaded 316 area trigger teleport definitions in 2 ms
|
||||
Loading Access Requirements...
|
||||
>> Loaded 152 access requirement definitions in 1 ms
|
||||
Loading Quest Area Triggers...
|
||||
>> Loaded 49 quest trigger points in 1 ms
|
||||
Loading Tavern Area Triggers...
|
||||
>> Loaded 175 tavern triggers in 1 ms
|
||||
Loading AreaTrigger script names...
|
||||
>> Loaded 229 areatrigger scripts in 0 ms
|
||||
Loading LFG entrance positions...
|
||||
>> Loaded 68 lfg dungeon templates in 2 ms
|
||||
Loading Dungeon boss data...
|
||||
>> Loaded 617 instance encounters in 2 ms
|
||||
Loading LFG rewards...
|
||||
>> Loaded 20 lfg dungeon rewards in 1 ms
|
||||
Loading Graveyard-zone links...
|
||||
>> Loaded 855 graveyard-zone links in 2 ms
|
||||
Loading Graveyard Orientations...
|
||||
>> Loaded 1016 graveyard orientations in 2 ms
|
||||
Loading spell pet auras...
|
||||
>> Loaded 34 spell pet auras in 1 ms
|
||||
Loading Spell target coordinates...
|
||||
>> Loaded 1438 spell teleport coordinates in 4 ms
|
||||
Loading linked spells...
|
||||
>> Loaded 413 linked spells in 1 ms
|
||||
Loading Player Create Data...
|
||||
>> Loaded 91 player create definitions in 1 ms
|
||||
Loading Player Create Items Data...
|
||||
>> Loaded 1 custom player create items in 1 ms
|
||||
Loading Player Create Skill Data...
|
||||
>> Loaded player create skills in 1 ms
|
||||
Loading Player Create Custom Spell Data...
|
||||
>> Loaded 4507 custom player create spells in 1 ms
|
||||
Loading Player Create Cast Spell Data...
|
||||
>> Loaded 63 player create cast spells in 1 ms
|
||||
Loading Player Create Action Data...
|
||||
>> Loaded 352 player create actions in 1 ms
|
||||
Loading Player Create Level Stats Data...
|
||||
>> Loaded 850 level stats definitions in 5 ms
|
||||
Loading Player Create XP Data...
|
||||
>> Loaded 84 xp for level definitions in 1 ms
|
||||
Loading Exploration BaseXP Data...
|
||||
>> Loaded 80 BaseXP definitions in 1 ms
|
||||
Loading Pet Name Parts...
|
||||
>> Loaded 313 pet name parts in 1 ms
|
||||
Loading the max pet number...
|
||||
>> Loaded the max pet number: 28 in 0 ms
|
||||
Loading pet level stats...
|
||||
>> Loaded 2720 level pet stats definitions in 5 ms
|
||||
Loading Player level dependent mail rewards...
|
||||
>> Loaded 24 level dependent mail rewards in 1 ms
|
||||
Loading creature loot templates...
|
||||
>> Loaded 528193 creature loot templates in 634 ms
|
||||
Loading fishing loot templates...
|
||||
>> Loaded 266 fishing loot templates in 2 ms
|
||||
Loading gameobject loot templates...
|
||||
>> Loaded 19834 gameobject loot templates in 32 ms
|
||||
Loading item loot templates...
|
||||
>> Loaded 5031 item loot templates in 16 ms
|
||||
Loading mail loot templates...
|
||||
>> Loaded 90 mail loot templates in 0 ms
|
||||
Loading milling loot templates...
|
||||
>> Loaded 51 milling loot templates in 4 ms
|
||||
Loading pickpocketing loot templates...
|
||||
>> Loaded 10981 pickpocketing loot templates in 20 ms
|
||||
Loading skinning loot templates...
|
||||
>> Loaded 3390 skinning loot templates in 10 ms
|
||||
Loading disenchanting loot templates...
|
||||
>> Loaded 180 disenchanting loot templates in 1 ms
|
||||
Loading prospecting loot templates...
|
||||
>> Loaded 43 prospecting loot templates in 4 ms
|
||||
Loading spell loot templates...
|
||||
>> Loaded 292 spell loot templates in 3 ms
|
||||
Loading reference loot templates...
|
||||
>> Loaded refence loot templates in 48 ms
|
||||
Loading Skill Discovery Table...
|
||||
>> Loaded 347 skill discovery definitions in 3 ms.
|
||||
Loading Skill Extra Item Table...
|
||||
>> Loaded 225 spell specialization definitions in 1 ms
|
||||
Loading Skill Perfection Data Table...
|
||||
>> Loaded 72 spell perfection definitions in 0 ms.
|
||||
Loading Skill Fishing base level requirements...
|
||||
>> Loaded 95 areas for fishing base skill level in 0 ms
|
||||
Loading Archaeology store...
|
||||
>> Loaded 180 archaeology sites in 1 ms
|
||||
>> Loaded 180 archaeology site polygons in 157 ms
|
||||
>> Loaded 9 archaeology branches in 0 ms
|
||||
>> Loaded 139 archaeology projects in 1 ms
|
||||
>> Calculated 9 archaeology branch chances in 0 ms
|
||||
Loading Achievements...
|
||||
>> Loaded 47 achievement references in 0 ms.
|
||||
Loading Achievement Criteria Lists...
|
||||
>> Loaded 9803 achievement criteria and 952 guild achievement crieteria in 5 ms.
|
||||
Loading Achievement Criteria Data...
|
||||
>> Loaded 1291 additional achievement criteria data in 2 ms
|
||||
Loading Achievement Rewards...
|
||||
>> Loaded 120 achievement rewards in 1 ms.
|
||||
Loading Achievement Reward Locales...
|
||||
>> Loaded 0 achievement reward locale strings. DB table `achievement_reward_loc
|
||||
ale` is empty.
|
||||
Loading Completed Achievements...
|
||||
>> Loaded 77 realm first completed achievements in 1 ms.
|
||||
Loading Item Auctions...
|
||||
>> Loaded 0 auction items. DB table `auctionhouse` or `item_instance` is empty!
|
||||
Loading Auctions...
|
||||
>> Loaded 0 auctions. DB table `auctionhouse` is empty.
|
||||
Loading Guild XP for level...
|
||||
>> Loaded 24 xp for guild level definitions in 1 ms
|
||||
Loading Guild rewards...
|
||||
>> Loaded 42 guild reward definitions in 0 ms
|
||||
Initializing Guild Profession Data Store...
|
||||
Loading Guild Challenges...
|
||||
>> Loaded 4 guild challenge definitions in 1 ms
|
||||
Loading Guilds...
|
||||
Loading guilds definitions...
|
||||
>> Loaded 1 guild definitions in 2 ms
|
||||
Loading guild ranks...
|
||||
>> Loaded 5 guild ranks in 0 ms
|
||||
Loading guild members...
|
||||
>> Loaded 4 guild members in 5 ms
|
||||
Loading bank tab rights...
|
||||
>> Loaded 30 bank tab rights in 1 ms
|
||||
Loading guild event logs...
|
||||
>> Loaded 8 guild event logs in 2 ms
|
||||
Loading guild bank event logs...
|
||||
>> Loaded 22 guild bank event logs in 0 ms
|
||||
Loading Guild News...
|
||||
>> Loaded 0 guild event logs. DB table `guild_newslog` is empty.
|
||||
Loading guild bank tabs...
|
||||
>> Loaded 6 guild bank tabs in 0 ms
|
||||
>> Loaded 22 guild bank tab items in 1 ms
|
||||
>> Validated data of loaded guilds in 0 ms
|
||||
Loading guild finder guild-related settings...
|
||||
>> Loaded 0 guild finder guild-related settings. Table `guild_finder_guild_setti
|
||||
ngs` is empty.
|
||||
Loading guild finder membership requests...
|
||||
>> Loaded 0 guild finder membership requests. Table `guild_finder_applicant` is
|
||||
empty.
|
||||
Loading ArenaTeams...
|
||||
>> Loaded 0 arena teams. DB table `arena_team` is empty!
|
||||
Loading Groups...
|
||||
>> Loaded 0 group definitions. DB table `groups` is empty!
|
||||
Loading ReservedNames...
|
||||
>> Loaded 0 reserved player names. DB table `reserved_name` is empty!
|
||||
Loading GameObjects for quests...
|
||||
>> Loaded 2750 GameObjects for quests in 5 ms
|
||||
Loading BattleMasters...
|
||||
>> Loaded 171 battlemaster entries in 3 ms
|
||||
Loading GameTeleports...
|
||||
>> Loaded 1531 GameTeleports in 5 ms
|
||||
Loading Trainers...
|
||||
>> Loaded 88 Trainers in 10 ms
|
||||
Loading Gossip menu...
|
||||
>> Loaded 8225 gossip_menu IDs in 10 ms
|
||||
Loading Gossip menu options...
|
||||
>> Loaded 5837 gossip_menu_option entries in 18 ms
|
||||
Loading Creature Template Gossip...
|
||||
creature_template_gossip has gossip definitions for menu id 55002 but this menu
|
||||
doesn't exist
|
||||
creature_template_gossip has gossip definitions for menu id 55002 but this menu
|
||||
doesn't exist
|
||||
creature_template_gossip has gossip definitions for menu id 6268 but this menu d
|
||||
oesn't exist
|
||||
creature_template_gossip has gossip definitions for menu id 9856 but this menu d
|
||||
oesn't exist
|
||||
creature_template_gossip has gossip definitions for menu id 10844 but this menu
|
||||
doesn't exist
|
||||
creature_template_gossip has gossip definitions for menu id 10852 but this menu
|
||||
doesn't exist
|
||||
creature_template_gossip has gossip definitions for menu id 2522 but this menu d
|
||||
oesn't exist
|
||||
creature_template_gossip has gossip definitions for menu id 12720 but this menu
|
||||
doesn't exist
|
||||
creature_template_gossip has gossip definitions for menu id 12718 but this menu
|
||||
doesn't exist
|
||||
creature_template_gossip has gossip definitions for menu id 13160 but this menu
|
||||
doesn't exist
|
||||
>> Loaded 5435 creature template gossip menus in 8 ms
|
||||
Loading Creature trainers...
|
||||
>> Loaded 1187 default trainers in 4 ms
|
||||
Loading Vendors...
|
||||
Table `(game_event_)npc_vendor` for Vendor (Entry: 44972) have in item list non-
|
||||
existed item (60822, type 1), ignore
|
||||
>> Loaded 48278 Vendors in 71 ms
|
||||
Loading Waypoints...
|
||||
>> Loaded 110626 waypoints in 177 ms
|
||||
Loading Waypoint Addons...
|
||||
>> Loaded 639 waypoint addon data in 2 ms
|
||||
Loading SmartAI Waypoints...
|
||||
>> Loaded 586 SmartAI waypoint paths (total 7064 waypoints) in 10 ms
|
||||
Loading Creature Formations...
|
||||
>> Loaded 2517 creatures in formations in 5 ms
|
||||
Loading World State templates...
|
||||
Table `world_state` contains a world state 3490 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3490 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3491 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3491 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3600 with invalid MapID (1134), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3600 with invalid MapID (1504), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3600 with invalid MapID (1505), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3600 with invalid MapID (1552), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3600 with invalid MapID (1672), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3600 with invalid MapID (1825), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3600 with invalid MapID (1911), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3600 with invalid MapID (2167), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3600 with invalid MapID (2373), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3600 with invalid MapID (2509), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3600 with invalid MapID (2511), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3600 with invalid MapID (2547), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3601 with invalid MapID (1134), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3601 with invalid MapID (1504), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3601 with invalid MapID (1505), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3601 with invalid MapID (1552), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3601 with invalid MapID (1672), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3601 with invalid MapID (1825), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3601 with invalid MapID (1911), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3601 with invalid MapID (2167), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3601 with invalid MapID (2373), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3601 with invalid MapID (2509), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3601 with invalid MapID (2511), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3601 with invalid MapID (2547), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3610 with invalid MapID (1134), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3610 with invalid MapID (1504), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3610 with invalid MapID (1505), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3610 with invalid MapID (1552), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3610 with invalid MapID (1672), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3610 with invalid MapID (1825), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3610 with invalid MapID (1911), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3610 with invalid MapID (2167), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3610 with invalid MapID (2373), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3610 with invalid MapID (2509), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3610 with invalid MapID (2511), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3610 with invalid MapID (2547), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3680 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3680 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3681 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3681 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3698 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3698 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3699 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3699 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3700 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3700 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3701 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3701 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3702 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3702 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3703 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3703 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3704 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3704 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3705 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3705 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3706 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3706 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3710 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3710 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3711 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3711 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3712 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3712 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3713 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3713 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3714 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3714 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3749 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3749 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3750 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3750 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3751 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3751 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3752 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3752 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3753 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3753 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3754 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3754 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3755 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3755 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3756 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3756 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3757 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3757 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3758 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3758 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3759 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3759 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3760 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3760 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3761 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3761 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3762 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3762 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3763 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3763 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3764 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3764 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3765 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3765 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3766 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3766 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3767 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3767 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3768 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3768 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3769 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3769 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3770 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3770 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3771 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3771 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3772 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3772 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3773 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3773 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3781 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3781 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3801 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3801 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3802 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3802 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3803 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3803 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 3815 with invalid MapID (1544), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 3816 with invalid MapID (1544), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 4022 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 4022 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 4023 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 4023 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 4024 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 4024 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 4025 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 4025 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
Table `world_state` contains a world state 4354 with invalid MapID (2118), map i
|
||||
gnored
|
||||
Table `world_state` contains a world state 4354 with invalid AreaID (10176), are
|
||||
a ignored
|
||||
>> Loaded 526 world state templates 4 ms
|
||||
>> Loaded 41 saved world state values 24 ms
|
||||
Loading Persistend World Variables...
|
||||
>> Loaded 9 world variables in 4 ms
|
||||
Loading Terrain World Map definitions...
|
||||
>> Loaded 4 terrain world maps in 0 ms.
|
||||
Loading Terrain Swap Default definitions...
|
||||
>> Loaded 4 terrain swap defaults in 1 ms.
|
||||
Loading Phase Area definitions...
|
||||
>> Loaded 25 phase areas in 1 ms.
|
||||
Loading Conditions...
|
||||
>> Loaded 15912 conditions in 45 ms
|
||||
Loading faction change achievement pairs...
|
||||
>> Loaded 124 faction change achievement pairs in 2 ms
|
||||
Loading faction change spell pairs...
|
||||
>> Loaded 113 faction change spell pairs in 1 ms
|
||||
Loading faction change quest pairs...
|
||||
>> Loaded 250 faction change quest pairs in 1 ms
|
||||
Loading faction change item pairs...
|
||||
>> Loaded 752 faction change item pairs in 2 ms
|
||||
Loading faction change reputation pairs...
|
||||
>> Loaded 17 faction change reputation pairs in 1 ms
|
||||
Loading faction change title pairs...
|
||||
>> Loaded 22 faction change title pairs in 1 ms
|
||||
Loading GM tickets...
|
||||
>> Loaded 2 GM tickets in 0 ms
|
||||
Loading GM surveys...
|
||||
>> Loaded GM Survey count from database in 1 ms
|
||||
Loading client addons...
|
||||
>> Loaded 35 known addons in 0 ms
|
||||
Returning old mails...
|
||||
>> No expired mails found.
|
||||
Loading Autobroadcasts...
|
||||
>> Loaded 0 autobroadcasts definitions. DB table `autobroadcast` is empty for th
|
||||
is realm!
|
||||
Loading spell_scripts...
|
||||
>> Loaded 143 script definitions in 1 ms
|
||||
Loading event_scripts...
|
||||
>> Loaded 461 script definitions in 2 ms
|
||||
Loading waypoint_scripts...
|
||||
>> Loaded 422 script definitions in 2 ms
|
||||
Loading spell script names...
|
||||
Scriptname: `spell_vcore_dungeon_horn` spell (Id: 120000) does not exist.
|
||||
Scriptname: `spell_vcore_dungeon_horn` spell (Id: 120001) does not exist.
|
||||
Scriptname: `spell_vcore_dungeon_horn` spell (Id: 120002) does not exist.
|
||||
Scriptname: `spell_vcore_dungeon_horn` spell (Id: 120003) does not exist.
|
||||
>> Loaded 3282 spell script names in 7 ms
|
||||
Loading Creature Texts...
|
||||
>> Loaded 16797 creature texts for 3997 creatures in 53 ms
|
||||
Loading Creature Text Locales...
|
||||
Loading creature StaticFlags overrides...
|
||||
>> Loaded 133 creature static flag overrides in 1 ms
|
||||
Loading Taxi node level definitions...
|
||||
>> Loaded 274 taxi node level definitions in 1 ms
|
||||
Initializing Scripts...
|
||||
Loading Script Waypoints for 54 creature(s)...
|
||||
>> Loaded 1377 Script Waypoint nodes in 4 ms
|
||||
>> Loaded spline chain data for 85 chains, consisting of 272 splines with 1414 w
|
||||
aypoints in 4 ms
|
||||
Loading C++ scripts
|
||||
Script named 'world_map_set_faction_worldstates_609' does not have a script name
|
||||
assigned in database.
|
||||
Script named 'item_gor_dreks_ointment' does not have a script name assigned in d
|
||||
atabase.
|
||||
>> Loaded 4106 C++ scripts in 146 ms
|
||||
Validating spell scripts...
|
||||
Spell `85123` Effect `Index: EFFECT_0 Target: 7` of script `spell_siege_cannon`
|
||||
did not match dbc effect data - handler bound to hook `OnObjectAreaTargetSelect`
|
||||
of SpellScript won't be executed
|
||||
>> Validated 3470 scripts in 12 ms
|
||||
Loading SmartAI scripts...
|
||||
>> Loaded SmartAIMgr Helpers in 16 ms
|
||||
>> Loaded 13506 SmartAI scripts in 167 ms
|
||||
Loading Calendar data...
|
||||
>> Loaded 0 calendar events in 1 ms
|
||||
>> Loaded 0 calendar invites in 0 ms
|
||||
Loading Petitions...
|
||||
>> Loaded 0 petitions.
|
||||
Loading Signatures...
|
||||
>> Loaded 0 Petition signs!
|
||||
Loading Summon Properties parameter data...
|
||||
>> Loaded 78 summon properties parameters in 1 ms
|
||||
Loading Item loot...
|
||||
>> Loaded 0 stored item loots
|
||||
>> Loaded 0 stored item money
|
||||
Initialize query data...
|
||||
>> Initialized query cache data in 1019 ms
|
||||
Initialize commands...
|
||||
Initialize game time and timers
|
||||
Mail timer set to: 1260, mail return is called every 1440 minutes
|
||||
Starting Map System
|
||||
Starting Game Event system...
|
||||
Initialize AuctionHouseBot...
|
||||
Initializing Opcodes...
|
||||
Starting Arena Season...
|
||||
Starting Battleground System
|
||||
>> Loaded 15 battlegrounds in 0 ms
|
||||
Starting Outdoor PvP System
|
||||
>> Loaded 5 outdoor PvP definitions in 1 ms
|
||||
Starting Battlefield System
|
||||
>> Loaded 2 battlefields in 0 ms
|
||||
Loading Warden Checks...
|
||||
Loading Warden Action Overrides...
|
||||
Deleting expired bans...
|
||||
Initializing quest reset times...
|
||||
Calculate random battleground reset time...
|
||||
Calculate guild limitation(s) reset time...
|
||||
Calculate next currency reset time...
|
||||
World initialized in 0 minutes 13 seconds
|
||||
TC>TC>TrinityCore rev. 39a515dbd13e+ 2026-10-27 23:56:43 +0100 (feature/clientle
|
||||
ss-player-core branch) (Unix, Debug, Static) (worldserver-daemon) ready...
|
||||
clientless login 26
|
||||
TC>Registered clientless session for character GUID 26 (account 6), state=Loadin
|
||||
g.
|
||||
Created clientless player session for character GUID 26 (account 6).
|
||||
ClientlessPlayerMgr started character GUID 26 (account 6).
|
||||
Clientless login started for 'Arthoran' (GUID 26, account 6).
|
||||
TC> Loading saved respawn time of 1790175804 for spawnid (0,323981) - spawn does
|
||||
not exist, ignoring
|
||||
Loading saved respawn time of 1790090644 for spawnid (0,396680) - spawn does not
|
||||
exist, ignoring
|
||||
Loading saved respawn time of 1790019808 for spawnid (0,396681) - spawn does not
|
||||
exist, ignoring
|
||||
Loading saved respawn time of 1790019853 for spawnid (0,396682) - spawn does not
|
||||
exist, ignoring
|
||||
Initialized transport GameObject grids: map=0 instance=0 grids=4 candidates=15
|
||||
Clientless player character GUID 26 (account 6) reached state=Online.
|
||||
ClientlessLua initialized, script root '/home/wow_admin/wow_cata_server/source/T
|
||||
rinityCore/src/server/scripts/Custom/clientless'.
|
||||
[AdventureBot][Profile] loaded guid=26 name='Arthoran' source='/home/wow_admin/w
|
||||
ow_cata_server/source/TrinityCore/src/server/scripts/Custom/clientless/adventure
|
||||
/profiles/arthoran.lua' home='elwynn' canGroundMount=1 mounts=1 mountMinDistance
|
||||
=45.0 mountSpeedRate=1.60 personality=8 regionAffinity=4 activityAffinity=7
|
||||
PROFILE_PROFESSIONS guid=26 primary=2
|
||||
ClientlessLua[26]: Arthoran ready; no automatic test journey queued
|
||||
ClientlessLua[26]: executed adventure override script '/home/wow_admin/wow_cata_
|
||||
server/source/TrinityCore/src/server/scripts/Custom/clientless/adventure/overrid
|
||||
es/arthoran.lua'.
|
||||
ADVENTURE_CATALOG locations=2973 actions=84 lines=76
|
||||
INTENT_TRAVEL guid=26 intent='social' location='goldshire_tavern' area=0
|
||||
[AdventureBot] loaded 3966 clientless world points.
|
||||
[AdventureBot] loaded travel graph: 993 node(s), 1765 directed edge(s).
|
||||
clientless interaction 26
|
||||
TC>Interactions guid=26 grouped=0 leader=0 partner=0 intent='social' schedule=''
|
||||
leaderIdleSeconds=0 afkSeconds=0 sociability=0.65 loyalty=0.35
|
||||
TC> SUPPLY_RESTOCK guid=26 day=20261028 daily=0 afterCombat=1
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
test_adventure_area_node.py: PASS
|
||||
test_adventure_behavior_policy.py: PASS
|
||||
test_adventure_combat_exercise.py: PASS
|
||||
test_adventure_combat_preparation.py: PASS
|
||||
test_adventure_combat_reactions.py: PASS
|
||||
test_adventure_economy.py: PASS
|
||||
test_adventure_economy_catalog.py: PASS
|
||||
test_adventure_exploration.py: PASS
|
||||
test_adventure_exploration_catalog.py: PASS
|
||||
test_adventure_graph_handover.py: PASS
|
||||
test_adventure_instance.py: PASS
|
||||
test_adventure_instance_autoresume.py: PASS
|
||||
test_adventure_instance_begin.py: PASS
|
||||
test_adventure_instance_catalog.py: PASS
|
||||
test_adventure_instance_persistence.py: PASS
|
||||
test_adventure_intent_state.py: PASS
|
||||
test_adventure_interactions.py: PASS
|
||||
test_adventure_leisure.py: PASS
|
||||
test_adventure_leisure_catalog.py: PASS
|
||||
test_adventure_location_import.py: PASS
|
||||
test_adventure_micro_reset.py: PASS
|
||||
test_adventure_profession_catalog.py: PASS
|
||||
test_adventure_profession_profile.py: PASS
|
||||
test_adventure_profession_sightline.py: PASS
|
||||
test_adventure_profession_targets.py: PASS
|
||||
test_adventure_repair_catalog.py: PASS
|
||||
test_adventure_rest.py: PASS
|
||||
test_adventure_rest_fire.py: PASS
|
||||
test_adventure_rest_space.py: PASS
|
||||
test_adventure_rest_targets.py: PASS
|
||||
test_adventure_social.py: PASS
|
||||
test_adventure_social_fire.py: PASS
|
||||
test_adventure_supplies.py: PASS
|
||||
test_adventure_supply_catalog.py: PASS
|
||||
test_adventure_supply_items.py: PASS
|
||||
test_adventure_training_catalog.py: PASS
|
||||
test_adventure_walk.py: PASS
|
||||
test_clientless_companion.py: PASS
|
||||
test_clientless_damage_notifications.py: PASS
|
||||
test_clientless_near_teleport_observer.py: PASS
|
||||
test_default_phase.py: PASS
|
||||
test_flight_area_query.py: PASS
|
||||
test_flight_handoff.py: PASS
|
||||
test_flight_policy.py: PASS
|
||||
test_follow_spline_velocity.py: PASS
|
||||
test_free_flight.py: PASS
|
||||
test_free_flight_planner.py: PASS
|
||||
test_ground_exit_continuation.py: PASS
|
||||
test_local_spline_segments.py: PASS
|
||||
test_lua_reload.py: PASS
|
||||
test_player_pure_max_skill.py: PASS
|
||||
test_portal_transfer.py: PASS
|
||||
test_portal_use.py: PASS
|
||||
test_taxi_import_disabled.py: PASS
|
||||
test_transport.py: PASS
|
||||
test_transport_grid_loading.py: PASS
|
||||
56/56 passed; failures: []
|
||||
@@ -0,0 +1,8 @@
|
||||
Debug build -j10 and installation succeeded. Test worldserver59086 freshly ready, port8085 listening.
|
||||
56/56 full suite passed; additional new social notification concurrency test passed (57 scripts total).
|
||||
Final expanded combat test with player focus also passed.
|
||||
Native login26 and interaction diagnostic succeeded; sociability0.65 loyalty0.35.
|
||||
Real creature defense after restart observed. No live player invitation/PvP/pet/portal/ten-minute AFK claim.
|
||||
Normal initial Goldshire trip started on login; behavior stop requested at safe point, last observed switching/waiting for safe state.
|
||||
OLD executable shutdown observed Object.h:448 GetMap ASSERTION FAILED m_currMap; process49765 exited before installation. Cause not investigated; new startup succeeded.
|
||||
Local commit requested by user; no push. No DB migrations. No temporary profile overrides.
|
||||
@@ -0,0 +1,103 @@
|
||||
# AdventureBot interactions and player-led groups
|
||||
|
||||
Implemented 2026-10-28 on feature/clientless-player-core. No autonomous bot-to-bot
|
||||
party formation, role assignment, healer/tank AI or proactive PvP aggression.
|
||||
PlayerAI.cpp supplies spell/attack foundations and charmed-player follow logic;
|
||||
it does not implement invitation decisions. AdventureBots keep independent
|
||||
controllers and use the native party invitation response handler.
|
||||
|
||||
## Encounters
|
||||
|
||||
Directed native text emotes greet/hello/wave/bow/salute/smile/thank and
|
||||
rude/threaten/roar generate GUID-only notices in ClientlessPlayerMgr. Map workers
|
||||
never call AdventureBot policy directly. Queues are bounded and delivered on the
|
||||
world thread. Participants must be alive, mutually visible, in the same map,
|
||||
instance and phase, within 25 yards and in line of sight.
|
||||
|
||||
Replies wait 1–3 seconds, briefly interrupt the current activity, face the other
|
||||
player and use native animation emotes. Friendly encounters wave/bow; hostility
|
||||
or hostile emotes produce a negative gesture. There is no automatic attack just
|
||||
because another player is hostile. Original intent deadlines are retained.
|
||||
The global reply cooldown is 15 seconds, the same partner has 120 seconds.
|
||||
Direct chat/free conversation parsing is not implemented.
|
||||
|
||||
Bots in social/leisure/rest can occasionally initiate a five-second encounter
|
||||
with another available AdventureBot within 12 yards. The lower character GUID
|
||||
initiates and reserves both participants. The peer must also have a compatible
|
||||
intent. Sociability weights initiation. Pair cooldown is five minutes; native
|
||||
animation replies never feed back into incoming text-emote events. Occasional
|
||||
localized 'Safe travels' accompanies greetings. This is a short greeting, not a
|
||||
multi-line conversation or synchronized shared activity.
|
||||
|
||||
Real positive damage by another player or their pet against the bot's active
|
||||
combat target can queue a thank-you. Damage attribution is captured after damage
|
||||
rejection/duel clamping and before lethal cleanup; no threat-only inference.
|
||||
The thank-you is deferred until combat and the existing self-care finish, and
|
||||
requires the helper still to be nearby and friendly. Dummy training is excluded.
|
||||
|
||||
## Invitations and group behavior
|
||||
|
||||
An actual native invitation is answered after 2–4 seconds in a safe state.
|
||||
Acceptance probability clamps sociability plus an intent adjustment to [0,1]:
|
||||
rest −0.50, profession/economy −0.25, adventure/instance +0.20, other +0.10.
|
||||
Inviter must be alive and friendly. Bots do not accept invitations from other
|
||||
clientless players yet; autonomous bot grouping is explicitly deferred.
|
||||
Invitations are declined inside dungeons. Normal faction/group restrictions run
|
||||
before the invitation reaches the bot; none are bypassed.
|
||||
|
||||
Membership suspends autonomous intent execution, clears its queued actions and
|
||||
retains its deadline. Native membership survives server restart. Bots follow the
|
||||
current leader with checked local MMAP paths, aiming about three yards behind
|
||||
and stopping within five yards. Longer/cross-map routes use existing
|
||||
travel_to_node actions toward a nearby TravelGraph node at the leader's location;
|
||||
no new teleport shortcut is introduced. Coverage of the graph and native portal
|
||||
rules determines whether these routes work. An active graph trip is allowed to
|
||||
finish before local follow resumes. Transport/flight states are not interrupted.
|
||||
Unreachable destinations are retried conservatively; an active leader does not
|
||||
cause a timeout merely because the route is missing.
|
||||
|
||||
Nearby (40 yards) group members' actual combat targets or attackers can be
|
||||
supported with the existing damage executor. Self-defense retains priority and
|
||||
the first actual damager focus. Assist requires native attack permission, valid
|
||||
visibility/phase, a verified path, at least 30% HP, and excludes world bosses and
|
||||
creatures more than two levels above the bot. Other classes currently retain
|
||||
basic autoattacks; class-specific support roles remain future work.
|
||||
|
||||
## Leaving
|
||||
|
||||
Never voluntarily leave while dead, fighting, in a dungeon/raid, on a transport,
|
||||
or in flight. Pending conditions are reevaluated when safe:
|
||||
|
||||
- A changed scheduled intent/location permits leaving. Personality
|
||||
`group_loyalty` is the chance of staying for that new block (default 0.25,
|
||||
Arthoran 0.35). The choice is made once per observed change, not each tick.
|
||||
A new date generates/loads the native daily plan even while grouped.
|
||||
For manual intents, expiry of the original deadline also changes this token.
|
||||
- The leader being explicitly AFK for ten minutes, or ten minutes of observed
|
||||
inactivity, permits leaving. Position/orientation, selection, combat,
|
||||
casting, native chat, text emotes, spell and item requests count as activity.
|
||||
Sampling cannot detect every conceivable stationary action. Native AFK takes
|
||||
precedence while set. A new leader starts a fresh observation window; returning
|
||||
from AFK ends the explicit AFK window. Observation clocks restart after a
|
||||
server restart, so a restart does not immediately evict a bot.
|
||||
|
||||
Before voluntarily leaving, a localized farewell is sent to the native party
|
||||
chat. Native RemoveMember performs removal. Removal/disbanding by other players
|
||||
also resumes autonomous scheduling at the current time, retaining deadlines.
|
||||
|
||||
## Diagnostics and verification
|
||||
|
||||
`clientless interaction 26` reports membership, leader, partner, intent,
|
||||
schedule observation, idle/AFK seconds, sociability and loyalty. No state changes.
|
||||
|
||||
`tests/clientless/test_adventure_interactions.py` compiles and executes production
|
||||
manager code with deterministic native-world adapters: invitation acceptance and
|
||||
rejection, group following/path rejection, leader changes, 599/600-second
|
||||
boundaries, activity, deferred exits, directed group emotes, hostile replies,
|
||||
pair reservation, cooldowns, graph handoff and deferred thanks.
|
||||
`test_adventure_combat_reactions.py` also covers player target permission and
|
||||
player damage notices while preserving corpse recovery regression checks.
|
||||
|
||||
These tests do not replace live client acceptance. Multi-client player invites,
|
||||
real PvP/pet defense, cross-map/instance group following, ten real minutes of AFK,
|
||||
and two simultaneously online AdventureBots require separate gameplay checks.
|
||||
@@ -890,6 +890,9 @@ bool Unit::HasBreakableByDamageCrowdControlAura(Unit* excludeCasterChannel) cons
|
||||
victim->ToCreature()->LowerPlayerDamageReq(health < damage ? health : damage);
|
||||
}
|
||||
|
||||
if (damage)
|
||||
sClientlessPlayerMgr->SharedTargetDamage(victim, attacker, std::min(health, damage));
|
||||
|
||||
if (health <= damage)
|
||||
{
|
||||
if (victim->GetTypeId() == TYPEID_PLAYER && victim != attacker)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "WorldSession.h"
|
||||
#include "ClientlessPlayerMgr.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include "AccountMgr.h"
|
||||
@@ -104,6 +105,7 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
|
||||
}
|
||||
|
||||
Player* sender = GetPlayer();
|
||||
sClientlessPlayerMgr->MarkActivity(sender);
|
||||
|
||||
//TC_LOG_DEBUG("misc", "CHAT: packet received. type %u, lang %u", type, lang);
|
||||
|
||||
@@ -569,6 +571,7 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
|
||||
void WorldSession::HandleAddonMessagechatOpcode(WorldPacket& recvData)
|
||||
{
|
||||
Player* sender = GetPlayer();
|
||||
sClientlessPlayerMgr->MarkActivity(sender);
|
||||
ChatMsg type;
|
||||
|
||||
switch (recvData.GetOpcode())
|
||||
@@ -808,6 +811,7 @@ void WorldSession::HandleSendTextEmoteOpcode(WorldPackets::Chat::SendTextEmote&
|
||||
return;
|
||||
}
|
||||
|
||||
sClientlessPlayerMgr->MarkActivity(GetPlayer());
|
||||
sScriptMgr->OnPlayerTextEmote(GetPlayer(), packet.SoundIndex, packet.EmoteID, packet.Target);
|
||||
|
||||
EmotesTextEntry const* em = sEmotesTextStore.LookupEntry(packet.EmoteID);
|
||||
@@ -848,6 +852,9 @@ void WorldSession::HandleSendTextEmoteOpcode(WorldPackets::Chat::SendTextEmote&
|
||||
|
||||
GetPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE, packet.EmoteID, 0, 0, unit);
|
||||
|
||||
if (unit && unit->ToPlayer())
|
||||
sClientlessPlayerMgr->TextEmote(_player, unit->ToPlayer(), packet.EmoteID);
|
||||
|
||||
//Send scripted event call
|
||||
if (unit && unit->GetTypeId() == TYPEID_UNIT && ((Creature*)unit)->AI())
|
||||
((Creature*)unit)->AI()->ReceiveEmote(GetPlayer(), packet.EmoteID);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "WorldSession.h"
|
||||
#include "ClientlessPlayerMgr.h"
|
||||
#include "Archaeology.h"
|
||||
#include "Common.h"
|
||||
#include "Config.h"
|
||||
@@ -93,6 +94,7 @@ void WorldSession::HandleClientCastFlags(WorldPacket& recvPacket, uint8 castFlag
|
||||
|
||||
void WorldSession::HandleUseItemOpcode(WorldPackets::Spells::UseItem& packet)
|
||||
{
|
||||
sClientlessPlayerMgr->MarkActivity(GetPlayer());
|
||||
Player* user = _player;
|
||||
|
||||
// ignore for remote control state
|
||||
@@ -265,6 +267,7 @@ void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket)
|
||||
|
||||
void WorldSession::HandleCastSpellOpcode(WorldPackets::Spells::CastSpell& cast)
|
||||
{
|
||||
sClientlessPlayerMgr->MarkActivity(GetPlayer());
|
||||
// ignore for remote control state (for player case)
|
||||
Unit* mover = GetGameClient()->GetActivelyMovedUnit();
|
||||
if (!mover || (mover != _player && mover->IsPlayer()))
|
||||
|
||||
@@ -1763,3 +1763,27 @@ std::string AdventureBehaviorMgr::CombatIntent(Player* player) const
|
||||
(it->second.State!=Phase::Travel && it->second.State!=Phase::OnSite)) return {};
|
||||
return it->second.Intent;
|
||||
}
|
||||
|
||||
std::string AdventureBehaviorMgr::InteractionIntent(Player* player) const
|
||||
{
|
||||
auto it=_records.find(player->GetGUID().GetCounter());
|
||||
return it==_records.end()?std::string():it->second.Intent;
|
||||
}
|
||||
std::string AdventureBehaviorMgr::InteractionSchedule(Player* player)
|
||||
{
|
||||
auto it=_records.find(player->GetGUID().GetCounter());
|
||||
if(it==_records.end())return {};
|
||||
auto& r=it->second;
|
||||
auto local=Local(GameTime::GetGameTime());
|
||||
if(r.Schedule && r.Date!=DateOf(local)){Generate(player,r,DateOf(local));r.Hour=-1;}
|
||||
if(!r.Schedule)return (r.Deadline && GameTime::GetGameTime()>=r.Deadline?"expired:":"")+r.Intent+":"+r.Location;
|
||||
auto const& slot=r.Plan[Local(GameTime::GetGameTime()).tm_hour];
|
||||
return slot.Intent+":"+slot.Location;
|
||||
}
|
||||
|
||||
void AdventureBehaviorMgr::InterruptForInteraction(Player* player)
|
||||
{
|
||||
InterruptForCombat(player);
|
||||
auto it=_records.find(player->GetGUID().GetCounter());
|
||||
if(it!=_records.end())it->second.Detail="social/group interaction; original deadline retained";
|
||||
}
|
||||
@@ -30,7 +30,10 @@ public:
|
||||
void Update(Player* player, uint32 diff);
|
||||
bool Command(ChatHandler* handler, char const* args);
|
||||
void InterruptForCombat(Player* player);
|
||||
void InterruptForInteraction(Player* player);
|
||||
std::string CombatIntent(Player* player) const;
|
||||
std::string InteractionIntent(Player* player) const;
|
||||
std::string InteractionSchedule(Player* player);
|
||||
private:
|
||||
enum class Phase {Idle, Travel, OnSite, Switching, Handover};
|
||||
struct RecoveryScene
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "AdventureCombatMgr.h"
|
||||
#include "AdventureInteractionMgr.h"
|
||||
#include "AdventureCombatPolicy.h"
|
||||
#include "AdventureBehaviorMgr.h"
|
||||
#include "ClientlessLuaEngine.h"
|
||||
@@ -30,7 +31,7 @@ class AdventureCombatMgr::Fighter : public PlayerAI
|
||||
public:
|
||||
explicit Fighter(Player* player):PlayerAI(player) { }
|
||||
void UpdateAI(uint32) override { }
|
||||
void Tick(Creature* target,uint32 diff)
|
||||
void Tick(Unit* target,uint32 diff)
|
||||
{
|
||||
_rend=_rend>diff?_rend-diff:0;_filler=_filler>diff?_filler-diff:0;
|
||||
me->SetFacingToObject(target);
|
||||
@@ -59,13 +60,16 @@ private:
|
||||
uint32 _rend=0,_filler=0;
|
||||
};
|
||||
AdventureCombatMgr* AdventureCombatMgr::instance(){static AdventureCombatMgr mgr;return &mgr;}
|
||||
bool AdventureCombatMgr::Valid(Player* p,Creature* c,bool training) const
|
||||
bool AdventureCombatMgr::Valid(Player* p,Unit* c,bool training) const
|
||||
{
|
||||
return c && c->IsAlive() && c->IsInWorld() && p->IsInPhase(c) && p->CanSeeOrDetect(c) &&
|
||||
p->IsValidAttackTarget(c) && !c->GetControllingPlayer() && !c->IsEvadingAttacks() &&
|
||||
(training?(c->GetScriptName()=="npc_training_dummy" && c->getLevel()<=p->getLevel()+2 && !c->isWorldBoss()):c->GetScriptName()!="npc_training_dummy");
|
||||
if(!c || !c->IsAlive() || !c->IsInWorld() || !p->IsInPhase(c) ||
|
||||
!p->CanSeeOrDetect(c) || !p->IsValidAttackTarget(c))return false;
|
||||
if(auto creature=c->ToCreature())
|
||||
return !creature->IsEvadingAttacks() && (!training || !creature->GetControllingPlayer()) &&
|
||||
(training?(creature->GetScriptName()=="npc_training_dummy" && creature->getLevel()<=p->getLevel()+2 && !creature->isWorldBoss()):creature->GetScriptName()!="npc_training_dummy");
|
||||
return !training && c->ToPlayer(); // Native PvP/sanctuary/duel checks above.
|
||||
}
|
||||
bool AdventureCombatMgr::Reachable(Player* p,Creature* c) const
|
||||
bool AdventureCombatMgr::Reachable(Player* p,Unit* c) const
|
||||
{
|
||||
if(!p->IsWithinLOSInMap(c))return false;
|
||||
if(p->IsWithinMeleeRange(c))return true;
|
||||
@@ -75,7 +79,7 @@ bool AdventureCombatMgr::Reachable(Player* p,Creature* c) const
|
||||
}
|
||||
bool AdventureCombatMgr::Safe(Player* p,Creature* c) const
|
||||
{
|
||||
if(!Valid(p,c,false) || p->GetExactDist(c)>40 || (c->GetVictim() && c->GetVictim()!=p))return false;
|
||||
if(!Valid(p,c,false) || c->GetControllingPlayer() || p->GetExactDist(c)>40 || (c->GetVictim() && c->GetVictim()!=p))return false;
|
||||
std::vector<Creature*> others;p->GetCreatureListWithEntryInGrid(others,0,35);
|
||||
unsigned hostiles=0;
|
||||
for(auto other:others)if(other->IsAlive() && p->IsInPhase(other) && p->IsHostileTo(other) &&
|
||||
@@ -95,7 +99,7 @@ Creature* AdventureCombatMgr::Dummy(Player* p) const
|
||||
return best;
|
||||
}
|
||||
bool AdventureCombatMgr::TrainingAvailable(Player* p) const{return Dummy(p)!=nullptr;}
|
||||
bool AdventureCombatMgr::Start(Player* p,Record& r,Creature* c,bool training,bool defending,time_t until)
|
||||
bool AdventureCombatMgr::Start(Player* p,Record& r,Unit* c,bool training,bool defending,time_t until)
|
||||
{
|
||||
if(!Valid(p,c,training) || !p->IsAlive() || p->IsBeingTeleported() || p->GetTransport() || p->IsInFlight() || p->IsCharmed())return false;
|
||||
sAdventureBehaviorMgr->InterruptForCombat(p);
|
||||
@@ -122,8 +126,8 @@ bool AdventureCombatMgr::Attack(Player* p,uint32 spawn)
|
||||
}
|
||||
void AdventureCombatMgr::DamageTaken(Player* p,Unit* attacker,uint32 damage)
|
||||
{
|
||||
auto c=attacker?attacker->ToCreature():nullptr;
|
||||
if(!damage || !p->IsAlive() || !c || c->GetControllingPlayer())return;
|
||||
auto c=attacker;
|
||||
if(!damage || !p->IsAlive() || !c)return;
|
||||
auto& r=_records[p->GetGUID().GetCounter()];
|
||||
if(std::find(r.Damagers.begin(),r.Damagers.end(),c->GetGUID())==r.Damagers.end())r.Damagers.push_back(c->GetGUID());
|
||||
}
|
||||
@@ -131,6 +135,8 @@ void AdventureCombatMgr::Finish(Player* p,Record& r)
|
||||
{
|
||||
TC_LOG_INFO("server.adventurebot","COMBAT_END guid=%u target='%s' training=%u",p->GetGUID().GetCounter(),r.Target.ToString().c_str(),r.Training);
|
||||
bool training=r.Training;
|
||||
if(!training && r.Helper)sAdventureInteractionMgr->CombatHelp(p,r.Helper);
|
||||
r.Helper.Clear();
|
||||
p->AttackStop();p->CastStop();p->StopMoving();p->GetMotionMaster()->Clear();p->SetSelection(ObjectGuid::Empty);
|
||||
sClientlessTravelMgr->CancelTravel(p->GetGUID().GetCounter());
|
||||
if(training && std::all_of(p->getAttackers().begin(),p->getAttackers().end(),[](Unit* u)
|
||||
@@ -253,15 +259,15 @@ bool AdventureCombatMgr::Update(Player* p,uint32 diff)
|
||||
// target. Keep all subsequent damagers ordered for after that focus dies.
|
||||
if(!r.Defending)while(!r.Damagers.empty())
|
||||
{
|
||||
auto id=r.Damagers.front();r.Damagers.pop_front();auto c=ObjectAccessor::GetCreature(*p,id);
|
||||
auto id=r.Damagers.front();r.Damagers.pop_front();auto c=ObjectAccessor::GetUnit(*p,id);
|
||||
if(!Valid(p,c,false))continue;
|
||||
if(r.AI)Finish(p,r);
|
||||
Start(p,r,c,false,true,0);break;
|
||||
}
|
||||
if(!r.AI && !p->IsInCombat() && !r.Cooldown)
|
||||
if(!r.AI && !p->GetGroup() && !p->IsInCombat() && !r.Cooldown)
|
||||
if(auto c=Proactive(p))Start(p,r,c,false,false,0);
|
||||
if(!r.AI)return false;
|
||||
auto c=ObjectAccessor::GetCreature(*p,r.Target);
|
||||
auto c=ObjectAccessor::GetUnit(*p,r.Target);
|
||||
if(!Valid(p,c,r.Training) || (r.Until && GameTime::GetGameTime()>=r.Until) || p->GetExactDist(c)>80)
|
||||
{Finish(p,r);return true;}
|
||||
if(!Reachable(p,c))
|
||||
@@ -291,3 +297,18 @@ bool AdventureCombatMgr::Busy(Player* player) const
|
||||
auto it=_records.find(player->GetGUID().GetCounter());
|
||||
return it!=_records.end() && (it->second.AI || it->second.Dead || !it->second.Damagers.empty());
|
||||
}
|
||||
|
||||
bool AdventureCombatMgr::Assist(Player* p,Unit* target)
|
||||
{
|
||||
if(!p->GetGroup() || Busy(p) || !Valid(p,target,false) || p->GetExactDist(target)>40 ||
|
||||
!Reachable(p,target) || p->GetHealthPct()<30)return false;
|
||||
if(auto c=target->ToCreature())
|
||||
if(c->isWorldBoss() || c->getLevel()>p->getLevel()+2)return false;
|
||||
return Start(p,_records[p->GetGUID().GetCounter()],target,false,false,0);
|
||||
}
|
||||
|
||||
void AdventureCombatMgr::SharedTargetDamage(Player* p,ObjectGuid helper,ObjectGuid target)
|
||||
{
|
||||
auto it=_records.find(p->GetGUID().GetCounter());
|
||||
if(it!=_records.end() && it->second.Target==target && !it->second.Training)it->second.Helper=helper;
|
||||
}
|
||||
@@ -22,23 +22,25 @@ public:
|
||||
bool Train(Player* player,uint32 durationMs,time_t deadline=0);
|
||||
bool Attack(Player* player,uint32 creatureSpawnId);
|
||||
void Stop(Player* player);
|
||||
bool Assist(Player* player,Unit* target);
|
||||
void SharedTargetDamage(Player* player,ObjectGuid helper,ObjectGuid target);
|
||||
bool Busy(Player* player) const;
|
||||
private:
|
||||
class Fighter;
|
||||
struct Record
|
||||
{
|
||||
std::shared_ptr<Fighter> AI;
|
||||
ObjectGuid Target;
|
||||
ObjectGuid Target,Helper;
|
||||
std::deque<ObjectGuid> Damagers;
|
||||
bool Training=false,Defending=false,Suspended=false,Dead=false,CemeteryPending=false;
|
||||
time_t Until=0,NextCorpseSearch=0;
|
||||
std::vector<Position> FailedCorpsePoints;
|
||||
uint32 Timer=0,UnreachableMs=0,Cooldown=0;
|
||||
};
|
||||
bool Start(Player* player,Record& record,Creature* target,bool training,bool defending,time_t until);
|
||||
bool Valid(Player* player,Creature* target,bool training) const;
|
||||
bool Start(Player* player,Record& record,Unit* target,bool training,bool defending,time_t until);
|
||||
bool Valid(Player* player,Unit* target,bool training) const;
|
||||
bool Safe(Player* player,Creature* target) const;
|
||||
bool Reachable(Player* player,Creature* target) const;
|
||||
bool Reachable(Player* player,Unit* target) const;
|
||||
Creature* Dummy(Player* player) const;
|
||||
Creature* Proactive(Player* player) const;
|
||||
bool Death(Player* player,Record& record);
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
#include "AdventureInteractionMgr.h"
|
||||
#include "AdventureInteractionPolicy.h"
|
||||
#include "AdventureBehaviorMgr.h"
|
||||
#include "AdventureCombatMgr.h"
|
||||
#include "ClientlessLuaEngine.h"
|
||||
#include "ClientlessPlayerMgr.h"
|
||||
#include "ClientlessTravelMgr.h"
|
||||
#include "ClientlessActionExecutorMgr.h"
|
||||
#include "ClientlessTravelGraphMgr.h"
|
||||
#include "Chat.h"
|
||||
#include "Creature.h"
|
||||
#include "GameTime.h"
|
||||
#include "Group.h"
|
||||
#include "Map.h"
|
||||
#include "MotionMaster.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "Opcodes.h"
|
||||
#include "PartyPackets.h"
|
||||
#include "Player.h"
|
||||
#include "Random.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "Log.h"
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
float Sociability(Player* p)
|
||||
{
|
||||
auto profile=sClientlessLuaEngine->GetAdventureProfile(p->GetGUID().GetCounter());
|
||||
if(profile){auto it=profile->Personality.find("sociability");if(it!=profile->Personality.end())return it->second;}
|
||||
return 0.5f;
|
||||
}
|
||||
float Loyalty(Player* p)
|
||||
{
|
||||
auto profile=sClientlessLuaEngine->GetAdventureProfile(p->GetGUID().GetCounter());
|
||||
if(profile){auto it=profile->Personality.find("group_loyalty");if(it!=profile->Personality.end())return std::max(0.0f,std::min(1.0f,it->second));}
|
||||
return 0.25f;
|
||||
}
|
||||
bool German(Player* p)
|
||||
{
|
||||
auto profile=sClientlessLuaEngine->GetAdventureProfile(p->GetGUID().GetCounter());
|
||||
return profile && profile->Locale=="deDE";
|
||||
}
|
||||
bool HostileEmote(uint32 emote)
|
||||
{return emote==TEXT_EMOTE_RUDE || emote==TEXT_EMOTE_THREATEN || emote==TEXT_EMOTE_ROAR;}
|
||||
bool FriendlyEmote(uint32 emote)
|
||||
{
|
||||
switch(emote)
|
||||
{
|
||||
case TEXT_EMOTE_GREET:case TEXT_EMOTE_HELLO:case TEXT_EMOTE_WAVE:case TEXT_EMOTE_BOW:
|
||||
case TEXT_EMOTE_SALUTE:case TEXT_EMOTE_SMILE:case TEXT_EMOTE_THANK:return true;
|
||||
default:return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
AdventureInteractionMgr* AdventureInteractionMgr::instance(){static AdventureInteractionMgr mgr;return &mgr;}
|
||||
bool AdventureInteractionMgr::Visible(Player* p,Player* other,float distance) const
|
||||
{
|
||||
return other && other!=p && other->IsAlive() && other->IsInWorld() && p->IsInMap(other) &&
|
||||
p->IsInPhase(other) && p->CanSeeOrDetect(other) && other->CanSeeOrDetect(p) && p->GetExactDist(other)<=distance && p->IsWithinLOSInMap(other);
|
||||
}
|
||||
void AdventureInteractionMgr::PartySay(Player* p,char const* german,char const* english)
|
||||
{
|
||||
auto group=p->GetGroup();if(!group)return;
|
||||
WorldPacket packet;
|
||||
ChatHandler::BuildChatPacket(packet,CHAT_MSG_PARTY,p->GetTeam()==ALLIANCE?LANG_COMMON:LANG_ORCISH,p,nullptr,German(p)?german:english);
|
||||
group->BroadcastPacket(&packet,false);
|
||||
}
|
||||
void AdventureInteractionMgr::Leave(Player* p,Record& r,char const* reason)
|
||||
{
|
||||
PartySay(p,"Ich muss weiter. Gute Reise!","I must be going. Safe travels!");
|
||||
TC_LOG_INFO("server.adventurebot","INTERACTION_GROUP_LEAVE guid=%u reason='%s'",p->GetGUID().GetCounter(),reason);
|
||||
p->GetGroup()->RemoveMember(p->GetGUID(),GROUP_REMOVEMETHOD_LEAVE);
|
||||
sAdventureBehaviorMgr->InterruptForInteraction(p);
|
||||
r.Grouped=false;r.Leader.Clear();r.Invite.Clear();r.Partner.Clear();r.SceneUntil=0;
|
||||
}
|
||||
void AdventureInteractionMgr::Emote(Player* p,Player* source,uint32 emote)
|
||||
{
|
||||
if(!Visible(p,source,25) || p->IsInCombat() || !p->IsAlive() || p->GetMap()->IsDungeon() ||
|
||||
p->IsBeingTeleported() || p->GetTransport() || p->IsInFlight() || p->IsCharmed())return;
|
||||
auto now=GameTime::GetGameTime();auto& r=_records[p->GetGUID().GetCounter()];
|
||||
if(r.Partner || now<r.NextSocial || (!FriendlyEmote(emote) && !HostileEmote(emote)))return;
|
||||
auto it=r.Encounters.find(source->GetGUID());if(it!=r.Encounters.end() && now<it->second)return;
|
||||
r.Partner=source->GetGUID();r.Reply=emote;r.ReplyAt=now+urand(1,3);r.SceneUntil=r.ReplyAt+2;
|
||||
r.NextSocial=now+15;r.Encounters[source->GetGUID()]=now+120;
|
||||
}
|
||||
void AdventureInteractionMgr::Reply(Player* p,Player* other,uint32 emote)
|
||||
{
|
||||
p->SetStandState(UNIT_STAND_STATE_STAND);p->SetFacingToObject(other);
|
||||
// Hostility alone never grants permission to attack. Native attack checks
|
||||
// remain in combat; these are visual reactions without combat side effects.
|
||||
bool friendly=p->IsFriendlyTo(other) && !HostileEmote(emote);
|
||||
p->HandleEmoteCommand(friendly?(emote==TEXT_EMOTE_BOW?EMOTE_ONESHOT_BOW:EMOTE_ONESHOT_WAVE):EMOTE_ONESHOT_NO);
|
||||
if(emote==TEXT_EMOTE_GREET && friendly && urand(0,2)==0)
|
||||
p->Say(German(p)?"Gute Reise!":"Safe travels!",p->GetTeam()==ALLIANCE?LANG_COMMON:LANG_ORCISH,other);
|
||||
TC_LOG_INFO("server.adventurebot","INTERACTION_EMOTE guid=%u other=%u friendly=%u",p->GetGUID().GetCounter(),other->GetGUID().GetCounter(),friendly);
|
||||
}
|
||||
void AdventureInteractionMgr::CombatHelp(Player* p,ObjectGuid helper)
|
||||
{ _records[p->GetGUID().GetCounter()].Helper=helper; }
|
||||
void AdventureInteractionMgr::Offline(Player* p){_records.erase(p->GetGUID().GetCounter());}
|
||||
bool AdventureInteractionMgr::Update(Player* p,uint32 diff)
|
||||
{
|
||||
auto& r=_records[p->GetGUID().GetCounter()];auto now=GameTime::GetGameTime();
|
||||
if(r.Timer>diff){r.Timer-=diff;return r.Grouped || r.SceneUntil>now;}
|
||||
r.Timer=1000;
|
||||
bool unsafe=!p->IsAlive() || p->IsInCombat() || sAdventureCombatMgr->Busy(p) ||
|
||||
p->IsBeingTeleported() || p->GetTransport() || p->IsInFlight() || p->IsCharmed();
|
||||
if(unsafe){r.Partner.Clear();r.SceneUntil=0;return p->GetGroup()!=nullptr;}
|
||||
for(auto it=r.Encounters.begin();it!=r.Encounters.end();)if(now>=it->second)it=r.Encounters.erase(it);else ++it;
|
||||
if(r.Helper)
|
||||
{
|
||||
auto helper=ObjectAccessor::FindPlayer(r.Helper);r.Helper.Clear();
|
||||
if(Visible(p,helper,25) && p->IsFriendlyTo(helper))
|
||||
p->Say(German(p)?"Danke für die Hilfe!":"Thank you for the help!",p->GetTeam()==ALLIANCE?LANG_COMMON:LANG_ORCISH,helper);
|
||||
}
|
||||
if(auto invite=p->GetGroupInvite())
|
||||
{
|
||||
auto leader=invite->GetLeaderGUID();
|
||||
if(r.Invite!=leader){r.Invite=leader;r.InviteAt=now+urand(2,4);}
|
||||
if(now>=r.InviteAt)
|
||||
{
|
||||
auto inviter=ObjectAccessor::FindPlayer(leader);
|
||||
bool accept=inviter && inviter->IsAlive() && p->IsFriendlyTo(inviter) && !p->GetMap()->IsDungeon() &&
|
||||
!inviter->GetSession()->IsClientless() && frand(0,1)<AdventureInteractionPolicy::Willingness(sAdventureBehaviorMgr->InteractionIntent(p),Sociability(p));
|
||||
WorldPackets::Party::PartyInviteResponse response{WorldPacket(CMSG_PARTY_INVITE_RESPONSE)};response.Accept=accept;
|
||||
p->GetSession()->HandlePartyInviteResponseOpcode(response);r.Invite.Clear();
|
||||
TC_LOG_INFO("server.adventurebot","INTERACTION_INVITE guid=%u leader=%u accepted=%u",p->GetGUID().GetCounter(),leader.GetCounter(),accept);
|
||||
}
|
||||
}
|
||||
else r.Invite.Clear();
|
||||
if(r.Partner)
|
||||
{
|
||||
auto other=ObjectAccessor::FindPlayer(r.Partner);
|
||||
if(!Visible(p,other,25) || now>=r.SceneUntil){r.Partner.Clear();r.SceneUntil=0;}
|
||||
else if(now>=r.ReplyAt)
|
||||
{
|
||||
sAdventureBehaviorMgr->InterruptForInteraction(p);Reply(p,other,r.Reply);r.ReplyAt=r.SceneUntil+1;
|
||||
}
|
||||
if(r.Partner)return true;
|
||||
}
|
||||
if(auto group=p->GetGroup())
|
||||
{
|
||||
auto leader=ObjectAccessor::FindPlayer(group->GetLeaderGUID());
|
||||
if(!r.Grouped)
|
||||
{
|
||||
r.Grouped=true;r.Schedule=sAdventureBehaviorMgr->InteractionSchedule(p);
|
||||
r.Partner.Clear();r.SceneUntil=0;
|
||||
sAdventureBehaviorMgr->InterruptForInteraction(p);
|
||||
PartySay(p,"Ich begleite euch eine Weile.","I'll accompany you for a while.");
|
||||
}
|
||||
if(r.Leader!=group->GetLeaderGUID())
|
||||
{
|
||||
r.Leader=group->GetLeaderGUID();r.LastActivity=now;r.AfkSince=0;
|
||||
if(leader){r.LeaderPosition.Relocate(leader);r.LeaderMap=leader->GetMapId();r.Selection=leader->GetTarget();}
|
||||
}
|
||||
if(leader)
|
||||
{
|
||||
r.LastActivity=std::max(r.LastActivity,sClientlessPlayerMgr->LastActivity(leader->GetGUID()));
|
||||
if(r.LeaderMap!=leader->GetMapId() || r.LeaderPosition.GetExactDist(leader)>0.1f ||
|
||||
r.LeaderPosition.GetOrientation()!=leader->GetOrientation() || r.Selection!=leader->GetTarget() ||
|
||||
leader->IsInCombat() || leader->HasUnitState(UNIT_STATE_CASTING))r.LastActivity=now;
|
||||
r.LeaderPosition.Relocate(leader);r.LeaderMap=leader->GetMapId();r.Selection=leader->GetTarget();
|
||||
if(leader->isAFK()){if(!r.AfkSince)r.AfkSince=now;}else r.AfkSince=0;
|
||||
}
|
||||
if(AdventureInteractionPolicy::CanLeave(p->IsAlive(),p->IsInCombat(),p->GetMap()->IsDungeon(),p->GetTransport() || p->IsInFlight()))
|
||||
{
|
||||
if(r.Schedule!=sAdventureBehaviorMgr->InteractionSchedule(p))
|
||||
{
|
||||
r.Schedule=sAdventureBehaviorMgr->InteractionSchedule(p);
|
||||
if(frand(0,1)>=Loyalty(p)){Leave(p,r,"intent-change");return false;}
|
||||
}
|
||||
if(AdventureInteractionPolicy::LeaderExpired(now,r.AfkSince?r.AfkSince:r.LastActivity)){Leave(p,r,"leader-afk");return false;}
|
||||
}
|
||||
// Existing warrior executor supplies damage assistance. Roles/healing
|
||||
// deliberately remain a later profile-driven extension.
|
||||
for(auto member=group->GetFirstMember();member;member=member->next())
|
||||
{
|
||||
auto ally=member->GetSource();if(!ally || !ally->IsAlive() || !p->IsInMap(ally) || p->GetExactDist(ally)>40)continue;
|
||||
auto target=ally->IsInCombat()?ally->GetVictim():nullptr;
|
||||
if(!target)for(auto attacker:ally->getAttackers())if(attacker->GetVictim()==ally){target=attacker;break;}
|
||||
if(target && sAdventureCombatMgr->Assist(p,target))return true;
|
||||
}
|
||||
if(!leader || leader==p || !leader->IsAlive())return true;
|
||||
auto execution=sClientlessActionExecutorMgr->GetStatus(p->GetGUID().GetCounter());
|
||||
if(execution && (execution->State==ClientlessActionExecutionState::Moving || execution->State==ClientlessActionExecutionState::SelectingTarget))return true;
|
||||
if(!p->IsInMap(leader) || p->GetExactDist(leader)>=100)
|
||||
{
|
||||
if(now>=r.NextRoute)
|
||||
{
|
||||
r.NextRoute=now+15;
|
||||
std::string node;float distance=0;
|
||||
sClientlessTravelGraphMgr->EnsureLoaded();
|
||||
if(sClientlessTravelGraphMgr->FindNearestNodeToPosition(leader->GetMapId(),leader->GetPositionX(),leader->GetPositionY(),leader->GetPositionZ(),node,distance) && distance<1000)
|
||||
{
|
||||
ClientlessAction action;action.Name="travel_to_node";action.Parameters["node"]=node;
|
||||
sClientlessPlayerMgr->QueueAction(p->GetGUID().GetCounter(),std::move(action));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if(!p->IsInPhase(leader) || !p->CanSeeOrDetect(leader))return true;
|
||||
if(p->GetExactDist(leader)>5 && p->GetExactDist(leader)<100)
|
||||
{
|
||||
auto nav=sClientlessTravelMgr->GetStatus(p->GetGUID().GetCounter());
|
||||
if(!nav || nav->State!=ClientlessTravelState::Moving || nav->Target.GetExactDist(leader)>5)
|
||||
{
|
||||
Position end;leader->GetNearPoint(p,end.m_positionX,end.m_positionY,end.m_positionZ,3,leader->GetOrientation()+3.14159265f);
|
||||
ClientlessPathDiagnostics path;
|
||||
if(sClientlessTravelMgr->AnalyzePath(p,end,path) && path.Complete && !path.Suspicious && path.PathLength<150)
|
||||
sClientlessTravelMgr->StartTravel(p,end);
|
||||
}
|
||||
}
|
||||
else if(p->GetExactDist(leader)<=5)sClientlessTravelMgr->CancelTravel(p->GetGUID().GetCounter());
|
||||
return true;
|
||||
}
|
||||
if(r.Grouped){r.Grouped=false;r.Leader.Clear();sAdventureBehaviorMgr->InterruptForInteraction(p);}
|
||||
auto intent=sAdventureBehaviorMgr->InteractionIntent(p);
|
||||
if(now>=r.NextEncounter && now>=r.NextSocial && !p->GetMap()->IsDungeon() && (intent=="social" || intent=="leisure" || intent=="rest"))
|
||||
{
|
||||
r.NextEncounter=now+urand(30,60);
|
||||
std::vector<Player*> people;p->GetPlayerListInGrid(people,12);
|
||||
for(auto other:people)
|
||||
{
|
||||
if(!Visible(p,other,12) || !sClientlessLuaEngine->GetAdventureProfile(other->GetGUID().GetCounter()) ||
|
||||
p->GetGUID().GetCounter()>=other->GetGUID().GetCounter() || other->IsInCombat() || other->GetGroup())continue;
|
||||
auto& peer=_records[other->GetGUID().GetCounter()];
|
||||
auto peerIntent=sAdventureBehaviorMgr->InteractionIntent(other);
|
||||
if((peerIntent!="social" && peerIntent!="leisure" && peerIntent!="rest") || other->HasUnitState(UNIT_STATE_CASTING))continue;
|
||||
if(peer.Partner || peer.NextSocial>now || r.Encounters.count(other->GetGUID()) || frand(0,1)>Sociability(p))continue;
|
||||
// One initiator reserves both participants; animation replies do
|
||||
// not generate incoming text-emote events, preventing loops.
|
||||
r.Partner=other->GetGUID();r.Reply=p->IsFriendlyTo(other)?TEXT_EMOTE_GREET:TEXT_EMOTE_THREATEN;
|
||||
r.ReplyAt=now+1;r.SceneUntil=now+5;r.Encounters[other->GetGUID()]=now+300;
|
||||
peer.Partner=p->GetGUID();peer.Reply=r.Reply;peer.ReplyAt=now+3;peer.SceneUntil=now+5;
|
||||
peer.NextSocial=now+60;peer.Encounters[p->GetGUID()]=now+300;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AdventureInteractionMgr::Report(ChatHandler* handler,Player* p) const
|
||||
{
|
||||
auto it=_records.find(p->GetGUID().GetCounter());
|
||||
if(it==_records.end()){handler->SendSysMessage("No interaction state yet.");return;}
|
||||
auto const& r=it->second;auto now=GameTime::GetGameTime();
|
||||
handler->PSendSysMessage("Interactions guid=%u grouped=%u leader=%u partner=%u intent='%s' schedule='%s'",
|
||||
p->GetGUID().GetCounter(),p->GetGroup()?1:0,r.Leader.GetCounter(),r.Partner.GetCounter(),
|
||||
sAdventureBehaviorMgr->InteractionIntent(p).c_str(),r.Schedule.c_str());
|
||||
handler->PSendSysMessage(" leaderIdleSeconds=%llu afkSeconds=%llu sociability=%.2f loyalty=%.2f",
|
||||
static_cast<unsigned long long>(r.LastActivity && now>=r.LastActivity?now-r.LastActivity:0),
|
||||
static_cast<unsigned long long>(r.AfkSince && now>=r.AfkSince?now-r.AfkSince:0),Sociability(p),Loyalty(p));
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef VCORE_ADVENTURE_INTERACTION_MGR_H
|
||||
#define VCORE_ADVENTURE_INTERACTION_MGR_H
|
||||
#include "Common.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Position.h"
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
class Player;
|
||||
class ChatHandler;
|
||||
class TC_GAME_API AdventureInteractionMgr
|
||||
{
|
||||
public:
|
||||
static AdventureInteractionMgr* instance();
|
||||
bool Update(Player* player,uint32 diff);
|
||||
void Emote(Player* player,Player* source,uint32 emote);
|
||||
void Offline(Player* player);
|
||||
void CombatHelp(Player* player,ObjectGuid helper);
|
||||
void Report(ChatHandler* handler,Player* player) const;
|
||||
private:
|
||||
struct Record
|
||||
{
|
||||
uint32 Timer=0;
|
||||
ObjectGuid Leader,Invite,Partner,Selection,Helper;
|
||||
time_t NextRoute=0,NextEncounter=0;
|
||||
Position LeaderPosition;
|
||||
uint32 LeaderMap=0;
|
||||
time_t InviteAt=0,LastActivity=0,AfkSince=0,ReplyAt=0,SceneUntil=0,NextSocial=0;
|
||||
uint32 Reply=0;
|
||||
bool Grouped=false;
|
||||
std::string Schedule;
|
||||
std::unordered_map<ObjectGuid,time_t> Encounters;
|
||||
};
|
||||
bool Visible(Player* player,Player* other,float distance) const;
|
||||
void Reply(Player* player,Player* other,uint32 emote);
|
||||
void Leave(Player* player,Record& record,char const* reason);
|
||||
void PartySay(Player* player,char const* german,char const* english);
|
||||
std::unordered_map<uint32,Record> _records;
|
||||
};
|
||||
#define sAdventureInteractionMgr AdventureInteractionMgr::instance()
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef VCORE_ADVENTURE_INTERACTION_POLICY_H
|
||||
#define VCORE_ADVENTURE_INTERACTION_POLICY_H
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
namespace AdventureInteractionPolicy
|
||||
{
|
||||
constexpr std::int64_t LeaderTimeout=600;
|
||||
inline float Willingness(std::string const& intent,float sociability)
|
||||
{
|
||||
float bias=intent=="rest"?-0.5f:intent=="profession"||intent=="economy"?-0.25f:
|
||||
intent=="adventure"||intent=="instance"?0.2f:0.1f;
|
||||
return std::max(0.0f,std::min(1.0f,sociability+bias));
|
||||
}
|
||||
inline bool CanLeave(bool alive,bool combat,bool dungeon,bool transport)
|
||||
{return alive && !combat && !dungeon && !transport;}
|
||||
inline bool LeaderExpired(std::int64_t now,std::int64_t lastActivity)
|
||||
{return lastActivity>0 && now>=lastActivity && now-lastActivity>=LeaderTimeout;}
|
||||
}
|
||||
#endif
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "ClientlessTravelMgr.h"
|
||||
#include "AdventureBehaviorMgr.h"
|
||||
#include "AdventureCombatMgr.h"
|
||||
#include "AdventureInteractionMgr.h"
|
||||
#include "AdventureSupplyMgr.h"
|
||||
#include "AdventureSocialActions.h"
|
||||
#include "Config.h"
|
||||
@@ -223,12 +224,17 @@ public:
|
||||
void Update(Player* player, uint32 diff) override
|
||||
{
|
||||
if(sAdventureSupplyMgr->Update(player,diff,sAdventureCombatMgr->Busy(player)))return;
|
||||
if(!sAdventureCombatMgr->Update(player,diff))sAdventureBehaviorMgr->Update(player,diff);
|
||||
if(!sAdventureCombatMgr->Update(player,diff) && !sAdventureInteractionMgr->Update(player,diff))
|
||||
sAdventureBehaviorMgr->Update(player,diff);
|
||||
}
|
||||
void OnSharedTargetDamage(Player* player,ObjectGuid helper,ObjectGuid target) override
|
||||
{ sAdventureCombatMgr->SharedTargetDamage(player,helper,target); }
|
||||
void OnTextEmote(Player* player, Player* source, uint32 emote) override
|
||||
{ sAdventureInteractionMgr->Emote(player,source,emote); }
|
||||
void OnDamageTaken(Player* player, Unit* attacker, uint32 damage) override
|
||||
{ sAdventureCombatMgr->DamageTaken(player, attacker, damage); }
|
||||
void OnPlayerLogout(Player* player) override
|
||||
{ sAdventureSupplyMgr->Offline(player); sAdventureCombatMgr->Offline(player); sAdventureBehaviorMgr->Offline(player); }
|
||||
{ sAdventureInteractionMgr->Offline(player); sAdventureSupplyMgr->Offline(player); sAdventureCombatMgr->Offline(player); sAdventureBehaviorMgr->Offline(player); }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ClientlessPlayerMgr.h"
|
||||
#include "GameTime.h"
|
||||
#include "ClientlessTravelMgr.h"
|
||||
#include "ClientlessTransportMgr.h"
|
||||
#include "ClientlessPerceptionMgr.h"
|
||||
@@ -154,7 +155,9 @@ void ClientlessPlayerMgr::Update(uint32 diff)
|
||||
// Damage can arrive from different map workers. Hand GUID-only notices to
|
||||
// the world-thread controller, preserving order within each character.
|
||||
decltype(_damageNotices) damageNotices;
|
||||
{ std::lock_guard<std::mutex> lock(_damageMutex); damageNotices.swap(_damageNotices); }
|
||||
decltype(_emoteNotices) emoteNotices;
|
||||
decltype(_helpNotices) helpNotices;
|
||||
{ std::lock_guard<std::mutex> lock(_damageMutex); damageNotices.swap(_damageNotices); emoteNotices.swap(_emoteNotices); helpNotices.swap(_helpNotices); }
|
||||
|
||||
// CLIENTLESS_PLAYER_CORE_C8_2_TRAVEL_ROBUSTNESS
|
||||
sClientlessTravelMgr->Update(diff);
|
||||
@@ -199,6 +202,12 @@ void ClientlessPlayerMgr::Update(uint32 diff)
|
||||
auto notices=damageNotices.find(characterGuid);
|
||||
if(notices!=damageNotices.end())for(auto const& notice:notices->second)
|
||||
record.Controller->OnDamageTaken(player,ObjectAccessor::GetUnit(*player,notice.Attacker),notice.Damage);
|
||||
auto emotes=emoteNotices.find(characterGuid);
|
||||
if(emotes!=emoteNotices.end())for(auto const& notice:emotes->second)
|
||||
record.Controller->OnTextEmote(player,ObjectAccessor::FindPlayer(notice.Source),notice.Emote);
|
||||
auto helpers=helpNotices.find(characterGuid);
|
||||
if(helpers!=helpNotices.end())for(auto const& notice:helpers->second)
|
||||
record.Controller->OnSharedTargetDamage(player,notice.Helper,notice.Target);
|
||||
record.Controller->Update(player, diff);
|
||||
}
|
||||
}
|
||||
@@ -219,3 +228,43 @@ void ClientlessPlayerMgr::DamageTaken(Player* player, Unit* attacker, uint32 dam
|
||||
std::lock_guard<std::mutex> lock(_damageMutex);
|
||||
_damageNotices[player->GetGUID().GetCounter()].push_back({attacker->GetGUID(),damage});
|
||||
}
|
||||
|
||||
void ClientlessPlayerMgr::TextEmote(Player* source, Player* target, uint32 emote)
|
||||
{
|
||||
if(!source || !target || source==target || !target->GetSession()->IsClientless())return;
|
||||
std::lock_guard<std::mutex> lock(_damageMutex);
|
||||
auto& queue=_emoteNotices[target->GetGUID().GetCounter()];
|
||||
if(queue.size()<8)queue.push_back({source->GetGUID(),emote});
|
||||
}
|
||||
|
||||
void ClientlessPlayerMgr::MarkActivity(Player* player)
|
||||
{
|
||||
if(!player)return;
|
||||
std::lock_guard<std::mutex> lock(_damageMutex);
|
||||
auto now=GameTime::GetGameTime();
|
||||
if(_activity.size()>4096)
|
||||
{
|
||||
for(auto it=_activity.begin();it!=_activity.end();)
|
||||
if(now-it->second>600)it=_activity.erase(it);else ++it;
|
||||
}
|
||||
_activity[player->GetGUID()]=now;
|
||||
}
|
||||
time_t ClientlessPlayerMgr::LastActivity(ObjectGuid player)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_damageMutex);
|
||||
auto it=_activity.find(player);return it==_activity.end()?0:it->second;
|
||||
}
|
||||
|
||||
void ClientlessPlayerMgr::SharedTargetDamage(Unit* victim,Unit* attacker,uint32 damage)
|
||||
{
|
||||
auto helper=attacker?attacker->GetControllingPlayer():nullptr;
|
||||
if(!damage || !helper || !victim || helper==victim)return;
|
||||
std::lock_guard<std::mutex> lock(_damageMutex);
|
||||
for(auto unit:victim->getAttackers())
|
||||
if(auto player=unit->ToPlayer())
|
||||
if(player!=helper && player->GetSession()->IsClientless())
|
||||
{
|
||||
auto& queue=_helpNotices[player->GetGUID().GetCounter()];
|
||||
if(queue.size()<8)queue.push_back({helper->GetGUID(),victim->GetGUID()});
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,8 @@ public:
|
||||
virtual void OnPlayerLogout(Player*) { }
|
||||
// Notification after health was reduced, never the mutable pre-damage event.
|
||||
virtual void OnDamageTaken(Player*, Unit*, uint32) { }
|
||||
virtual void OnTextEmote(Player*, Player*, uint32) { }
|
||||
virtual void OnSharedTargetDamage(Player*, ObjectGuid, ObjectGuid) { }
|
||||
};
|
||||
|
||||
class TC_GAME_API ClientlessPlayerMgr
|
||||
@@ -52,6 +54,10 @@ public:
|
||||
|
||||
bool IsManaged(uint32 characterGuid) const;
|
||||
void DamageTaken(Player* player, Unit* attacker, uint32 damage);
|
||||
void TextEmote(Player* source, Player* target, uint32 emote);
|
||||
void SharedTargetDamage(Unit* victim,Unit* attacker,uint32 damage);
|
||||
void MarkActivity(Player* player);
|
||||
time_t LastActivity(ObjectGuid player);
|
||||
uint32 GetAccountId(uint32 characterGuid) const;
|
||||
uint32 GetManagedCount() const { return uint32(_records.size()); }
|
||||
|
||||
@@ -79,6 +85,11 @@ private:
|
||||
};
|
||||
|
||||
struct DamageNotice { ObjectGuid Attacker; uint32 Damage; };
|
||||
struct HelpNotice { ObjectGuid Helper,Target; };
|
||||
std::unordered_map<uint32,std::deque<HelpNotice>> _helpNotices;
|
||||
struct EmoteNotice { ObjectGuid Source; uint32 Emote; };
|
||||
std::unordered_map<uint32,std::deque<EmoteNotice>> _emoteNotices;
|
||||
std::unordered_map<ObjectGuid,time_t> _activity;
|
||||
std::mutex _damageMutex;
|
||||
std::unordered_map<uint32,std::deque<DamageNotice>> _damageNotices;
|
||||
std::unordered_map<uint32, ManagedPlayer> _records;
|
||||
|
||||
@@ -34,6 +34,7 @@ return {
|
||||
},
|
||||
|
||||
personality = {
|
||||
group_loyalty = 0.35, -- Chance to stay when the scheduled intent changes.
|
||||
sociability = 0.65,
|
||||
adventurousness = 0.70,
|
||||
wanderlust = 0.30,
|
||||
|
||||
@@ -18,6 +18,7 @@ return {
|
||||
},
|
||||
|
||||
personality = {
|
||||
group_loyalty = 0.25, -- Chance to stay when the scheduled intent changes.
|
||||
sociability = 0.50,
|
||||
adventurousness = 0.50,
|
||||
wanderlust = 0.50,
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "ClientlessWorldPointMgr.h"
|
||||
#include "AdventureBehaviorMgr.h"
|
||||
#include "AdventureCombatMgr.h"
|
||||
#include "AdventureInteractionMgr.h"
|
||||
#include "AdventureSupplyMgr.h"
|
||||
#include "ClientlessTravelGraphMgr.h"
|
||||
#include "ClientlessRouteRecorderMgr.h"
|
||||
@@ -60,6 +61,18 @@
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace
|
||||
{
|
||||
bool InteractionStatus(ChatHandler* handler,char const* args)
|
||||
{
|
||||
std::istringstream input(args?args:"");uint32 guid=0;input>>guid;
|
||||
auto session=sWorld->FindClientlessSession(guid);auto player=session?session->GetPlayer():nullptr;
|
||||
if(!player || !sClientlessPlayerMgr->IsManaged(guid))
|
||||
{handler->SendSysMessage("Usage: clientless interaction <online-managed-guid>");return false;}
|
||||
sAdventureInteractionMgr->Report(handler,player);return true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ClientlessCombatPreparation
|
||||
{
|
||||
struct Gear {uint8 Slot;uint32 Entry;std::string Enchantments;uint8 RandomType;uint32 RandomId;};
|
||||
@@ -2504,6 +2517,7 @@ static bool HandleClientlessTravelReload(ChatHandler* handler, char const* /*arg
|
||||
static std::vector<ChatCommand> clientlessCommands =
|
||||
{
|
||||
{ "combat", rbac::RBAC_PERM_COMMAND_GM, true, &ClientlessCombatPreparation::Command, "" },
|
||||
{ "interaction", rbac::RBAC_PERM_COMMAND_GM, true, &InteractionStatus, "" },
|
||||
{ "luareload", rbac::RBAC_PERM_COMMAND_GM, true, &HandleClientlessLuaReload, "" },
|
||||
{ "transport", rbac::RBAC_PERM_COMMAND_GM, true, &HandleClientlessTransport, "" },
|
||||
{ "login", rbac::RBAC_PERM_COMMAND_GM, true, &HandleClientlessLogin, "" },
|
||||
|
||||
@@ -33,9 +33,14 @@ struct Position{
|
||||
float GetExactDist(Position const* b)const{return GetExactDist(*b);}
|
||||
};
|
||||
struct Map{float GetHeight(int,float,float,float,bool,float){return 0;}}map;
|
||||
struct Creature;
|
||||
struct Unit:Position{virtual Creature* ToCreature(){return nullptr;}};
|
||||
struct Creature:Unit{ObjectGuid guid;bool valid=true,alive=true,controlled=false,evading=false,boss=false,phase=true,visible=true,inWorld=true;unsigned level=60;std::string script="mob";Creature(unsigned n):guid{n}{}ObjectGuid GetGUID(){return guid;}Creature* ToCreature()override{return this;}bool GetControllingPlayer(){return controlled;}bool IsAlive(){return alive;}bool IsInWorld(){return inWorld;}bool IsEvadingAttacks(){return evading;}bool isWorldBoss(){return boss;}unsigned getLevel(){return level;}std::string GetScriptName(){return script;}};
|
||||
struct Creature;struct Player;
|
||||
struct Unit:Position{
|
||||
ObjectGuid guid;bool valid=true,alive=true,phase=true,visible=true,inWorld=true;
|
||||
virtual Creature* ToCreature(){return nullptr;}virtual Player* ToPlayer(){return nullptr;}
|
||||
virtual bool IsAlive(){return alive;}bool IsInWorld(){return inWorld;}virtual ObjectGuid GetGUID(){return guid;}
|
||||
void GetNearPoint(Player*,float&a,float&b,float&c,float,float){a=x;b=y;c=z;}float GetAngle(Player*){return 0;}
|
||||
};
|
||||
struct Creature:Unit{bool controlled=false,evading=false,boss=false;unsigned level=60;std::string script="mob";Creature(unsigned n){guid={n};}ObjectGuid GetGUID(){return guid;}Creature* ToCreature()override{return this;}bool GetControllingPlayer(){return controlled;}bool IsAlive(){return alive;}bool IsInWorld(){return inWorld;}bool IsEvadingAttacks(){return evading;}bool isWorldBoss(){return boss;}unsigned getLevel(){return level;}std::string GetScriptName(){return script;}};
|
||||
struct Player;
|
||||
struct Corpse:Position{
|
||||
time_t released=100;int saves=0;ObjectGuid GetGUID(){return {700};}time_t GetGhostTime(){return released;}int GetType(){return 0;}
|
||||
@@ -45,10 +50,10 @@ struct WorldPacket{int opcode;WorldPacket(int o,int):opcode(o){}template<class T
|
||||
struct Session{Player* owner=nullptr;int releases=0,reclaims=0;void HandleRepopRequestOpcode(WorldPacket&);void HandleReclaimCorpseOpcode(WorldPacket&);};
|
||||
struct Player:Unit{
|
||||
bool alive=true,ghost=false,teleport=false,combat=false,melee=true,los=true;unsigned delay=30;int saves=0,revives=0,bones=0,graveyards=0;bool sickness=false;int durability=75;
|
||||
Session session;Player(){session.owner=this;}ObjectGuid GetGUID(){return {26};}bool IsAlive(){return alive;}bool IsInWorld(){return true;}bool IsInCombat(){return combat;}bool IsBeingTeleported(){return teleport;}bool GetTransport(){return false;}bool IsInFlight(){return false;}bool IsCharmed(){return false;}
|
||||
Session session;Player(){session.owner=this;guid={26};}ObjectGuid GetGUID(){return guid;}Player* ToPlayer()override{return this;}bool GetGroup(){return false;}bool IsAlive(){return alive;}bool IsInWorld(){return true;}bool IsInCombat(){return combat;}bool IsBeingTeleported(){return teleport;}bool GetTransport(){return false;}bool IsInFlight(){return false;}bool IsCharmed(){return false;}
|
||||
unsigned GetMapId(){return 0;}unsigned GetInstanceId(){return 0;}bool InBattleground(){return false;}bool InArena(){return false;}bool HasFlag(int,int){return ghost;}Session*GetSession(){return &session;}Corpse*GetCorpse(){return &corpse;}
|
||||
uint32 GetCorpseReclaimDelay(bool){return delay;}Map*GetMap(){return ↦}int GetPhaseShift(){return 0;}
|
||||
bool IsInPhase(Creature*c){return c->phase;}bool CanSeeOrDetect(Creature*c){return c->visible;}bool IsValidAttackTarget(Creature*c){return c->valid;}unsigned getLevel(){return 60;}bool IsWithinMeleeRange(Creature*){return melee;}float GetCombatReach(){return 1;}float GetAngle(Player*){return 0;}
|
||||
bool IsInPhase(Unit*c){return c->phase;}bool CanSeeOrDetect(Unit*c){return c->visible;}bool IsValidAttackTarget(Unit*c){return c->valid;}unsigned getLevel(){return 60;}bool IsWithinMeleeRange(Unit*){return melee;}float GetCombatReach(){return 1;}float GetAngle(Player*){return 0;}
|
||||
void SaveToDB(){++saves;}void RepopAtGraveyard(){++graveyards;teleport=true;Relocate(-100,0,0);}void ResurrectPlayer(float hp,bool sick){assert(hp==0.5f);sickness=sick;alive=true;ghost=false;++revives;}void SpawnCorpseBones(){++bones;}
|
||||
void StopMoving(){}
|
||||
};
|
||||
@@ -59,7 +64,7 @@ void Session::HandleReclaimCorpseOpcode(WorldPacket&){++reclaims;assert(corpse.I
|
||||
void nearPoint(Creature*,Player*,float&,float&,float&,float,float){}
|
||||
struct FakeCreature:Creature{using Creature::Creature;void GetNearPoint(Player*,float&x,float&y,float&z,float,float){x=this->x;y=this->y;z=this->z;}float GetAngle(Player*){return 0;}};
|
||||
namespace GameTime{time_t now=100;time_t GetGameTime(){return now;}}
|
||||
namespace ObjectAccessor{std::unordered_map<unsigned,Creature*> objects;Creature* GetCreature(Player&,ObjectGuid g){auto i=objects.find(g.id);return i==objects.end()?nullptr:i->second;}}
|
||||
namespace ObjectAccessor{std::unordered_map<unsigned,Unit*> objects;Unit* GetUnit(Player&,ObjectGuid g){auto i=objects.find(g.id);return i==objects.end()?nullptr:i->second;}}
|
||||
struct ClientlessPathDiagnostics{bool Complete=true,Suspicious=false;float PathLength=100;};
|
||||
enum class ClientlessTravelState{Moving,Arrived,Stuck,PathRejected};
|
||||
struct TravelStatus{ClientlessTravelState State=ClientlessTravelState::Arrived;Position Target;};
|
||||
@@ -78,11 +83,11 @@ code=code.replace('bool GetControllingPlayer(){return controlled;}bool IsAlive()
|
||||
# Forward declaration needed in Creature's adapter.
|
||||
code=code.replace('struct Creature;','struct Creature;struct Player;',1)
|
||||
code+=header+r'''
|
||||
class AdventureCombatMgr::Fighter{public:int ticks=0;void Tick(Creature*,uint32){++ticks;}};
|
||||
class AdventureCombatMgr::Fighter{public:int ticks=0;void Tick(Unit*,uint32){++ticks;}};
|
||||
|
||||
bool AdventureCombatMgr::Reachable(Player*p,Creature*)const{return p->los;}
|
||||
bool AdventureCombatMgr::Reachable(Player*p,Unit*)const{return p->los;}
|
||||
Creature* AdventureCombatMgr::Proactive(Player*)const{return nullptr;}
|
||||
bool AdventureCombatMgr::Start(Player*,Record&r,Creature*c,bool train,bool defense,time_t until){r.AI=std::make_shared<Fighter>();r.Target=c->guid;r.Training=train;r.Defending=defense;r.Until=until;return true;}
|
||||
bool AdventureCombatMgr::Start(Player*,Record&r,Unit*c,bool train,bool defense,time_t until){r.AI=std::make_shared<Fighter>();r.Target=c->GetGUID();r.Training=train;r.Defending=defense;r.Until=until;return true;}
|
||||
void AdventureCombatMgr::Finish(Player*,Record&r){r.AI.reset();r.Target.Clear();r.Defending=false;r.Training=false;r.Cooldown=10000;}
|
||||
'''
|
||||
for name in ('bool AdventureCombatMgr::Valid(', 'void AdventureCombatMgr::DamageTaken(', 'bool AdventureCombatMgr::Death(', 'bool AdventureCombatMgr::Update('):code+='\n'+function(name)
|
||||
@@ -101,6 +106,9 @@ int main(){
|
||||
assert(SegmentDistance(5,3,0,0,0,0,10,0,0)==3);
|
||||
AdventureCombatMgr mgr;Player p;Creature a(1),b(2),c(3);ObjectAccessor::objects={{1,&a},{2,&b},{3,&c}};
|
||||
|
||||
Player enemy;enemy.guid={99};ObjectAccessor::objects[99]=&enemy;
|
||||
assert(mgr.Valid(&p,&enemy,false));assert(!mgr.Valid(&p,&enemy,true));
|
||||
enemy.valid=false;assert(!mgr.Valid(&p,&enemy,false));enemy.valid=true;
|
||||
assert(mgr.Valid(&p,&a,false));assert(!mgr.Valid(&p,&a,true));
|
||||
Creature dummy(4);dummy.script="npc_training_dummy";assert(mgr.Valid(&p,&dummy,true));assert(!mgr.Valid(&p,&dummy,false));
|
||||
dummy.level=63;assert(!mgr.Valid(&p,&dummy,true));dummy.level=60;
|
||||
@@ -110,6 +118,7 @@ int main(){
|
||||
dummy.controlled=true;assert(!mgr.Valid(&p,&dummy,true));dummy.controlled=false;
|
||||
dummy.valid=false;assert(!mgr.Valid(&p,&dummy,true));
|
||||
auto&r=mgr._records[26];
|
||||
mgr.DamageTaken(&p,&enemy,1);assert(r.Damagers.front().id==99);r.Damagers.clear();
|
||||
mgr.DamageTaken(&p,&a,0);assert(r.Damagers.empty());
|
||||
mgr.DamageTaken(&p,&a,5);mgr.DamageTaken(&p,&b,5);mgr.Update(&p,500);
|
||||
assert(r.Target.id==1 && r.Defending); // first actual damager
|
||||
@@ -117,6 +126,10 @@ int main(){
|
||||
a.alive=false;mgr.Update(&p,500);assert(!r.AI);mgr.Update(&p,500);assert(r.Target.id==2);
|
||||
mgr.Finish(&p,r);r.Damagers.clear();mgr.Start(&p,r,&c,true,false,500);
|
||||
mgr.DamageTaken(&p,&b,1);mgr.Update(&p,500);assert(r.Target.id==2 && r.Defending && !r.Training);
|
||||
// Actual player attackers use the same first-damager focus and native PvP permission.
|
||||
mgr.Finish(&p,r);r.Damagers.clear();mgr.DamageTaken(&p,&enemy,1);mgr.Update(&p,500);
|
||||
assert(r.Target.id==99 && r.Defending);mgr.DamageTaken(&p,&c,1);mgr.Update(&p,500);assert(r.Target.id==99);
|
||||
enemy.valid=false;mgr.Update(&p,500);assert(!r.AI);r.Damagers.clear();
|
||||
// Release through native handler and save ghost plus corpse timestamp.
|
||||
mgr.Finish(&p,r);p.alive=false;p.ghost=false;r.Dead=false;mgr.Death(&p,r);
|
||||
assert(p.ghost && p.session.releases==1 && p.saves==1 && corpse.saves==1);
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
"""Run production interaction manager against deterministic native-world adapters."""
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import tempfile
|
||||
root=Path(__file__).resolve().parents[2]
|
||||
header=(root/'src/server/game/World/AdventureInteractionMgr.h').read_text()
|
||||
header='\n'.join(line for line in header.splitlines() if not line.startswith('#include')).replace('private:','public:')
|
||||
source=(root/'src/server/game/World/AdventureInteractionMgr.cpp').read_text()
|
||||
source='\n'.join(line for line in source.splitlines() if not line.startswith('#include'))
|
||||
code=r'''
|
||||
#include "AdventureInteractionPolicy.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
using uint32=unsigned;
|
||||
#define TC_GAME_API
|
||||
#define TC_LOG_INFO(...) ((void)0)
|
||||
struct ObjectGuid { unsigned id=0;unsigned GetCounter()const{return id;}void Clear(){id=0;}explicit operator bool()const{return id!=0;}bool operator==(ObjectGuid b)const{return id==b.id;}bool operator!=(ObjectGuid b)const{return id!=b.id;}};
|
||||
namespace std { template<>struct hash<ObjectGuid>{size_t operator()(ObjectGuid g)const{return g.id;}}; }
|
||||
struct Position {
|
||||
float m_positionX=0,m_positionY=0,m_positionZ=0,o=0;
|
||||
void Relocate(Position const*p){*this=*p;}
|
||||
float GetPositionX()const{return m_positionX;}float GetPositionY()const{return m_positionY;}float GetPositionZ()const{return m_positionZ;}float GetOrientation()const{return o;}
|
||||
float GetExactDist(Position const*p)const{return std::hypot(m_positionX-p->m_positionX,m_positionY-p->m_positionY);}
|
||||
};
|
||||
enum {TEXT_EMOTE_RUDE=1,TEXT_EMOTE_THREATEN,TEXT_EMOTE_ROAR,TEXT_EMOTE_GREET,TEXT_EMOTE_HELLO,TEXT_EMOTE_WAVE,TEXT_EMOTE_BOW,TEXT_EMOTE_SALUTE,TEXT_EMOTE_SMILE,TEXT_EMOTE_THANK,EMOTE_ONESHOT_BOW,EMOTE_ONESHOT_WAVE,EMOTE_ONESHOT_NO,UNIT_STAND_STATE_STAND,ALLIANCE,LANG_COMMON,LANG_ORCISH,CHAT_MSG_PARTY,GROUP_REMOVEMETHOD_LEAVE,CMSG_PARTY_INVITE_RESPONSE,UNIT_STATE_CASTING};
|
||||
struct WorldPacket { explicit WorldPacket(int=0){} };
|
||||
struct Player;struct Group;
|
||||
namespace WorldPackets {namespace Party {struct PartyInviteResponse{explicit PartyInviteResponse(WorldPacket&&){}bool Accept=false;};}}
|
||||
struct Session {Player*owner=nullptr;bool clientless=false;bool IsClientless(){return clientless;}void HandlePartyInviteResponseOpcode(WorldPackets::Party::PartyInviteResponse&);};
|
||||
struct Map {bool dungeon=false;bool IsDungeon(){return dungeon;}};
|
||||
struct Unit:Position {Player*victim=nullptr;std::vector<Unit*> attackers;virtual ~Unit()=default;virtual struct Creature*ToCreature(){return nullptr;}Unit*GetVictim(){return reinterpret_cast<Unit*>(victim);}auto&getAttackers(){return attackers;}};
|
||||
struct Creature:Unit{};
|
||||
struct Member {Player*player=nullptr;Member*nextMember=nullptr;Player*GetSource(){return player;}Member*next(){return nextMember;}};
|
||||
struct Player:Unit {
|
||||
ObjectGuid guid,selection;bool alive=true,inWorld=true,combat=false,teleport=false,transport=false,flight=false,charmed=false,casting=false,afk=false,visible=true,phase=true,los=true;
|
||||
unsigned mapid=0,team=ALLIANCE;Session session;Group*group=nullptr,*invite=nullptr;Map map;int gestures=0,says=0,posture=0;uint32 lastEmote=0;
|
||||
explicit Player(unsigned id=26){guid.id=id;session.owner=this;}
|
||||
ObjectGuid GetGUID(){return guid;}bool IsAlive(){return alive;}bool IsInWorld(){return inWorld;}bool IsInCombat(){return combat;}bool IsBeingTeleported(){return teleport;}bool GetTransport(){return transport;}bool IsInFlight(){return flight;}bool IsCharmed(){return charmed;}bool HasUnitState(int){return casting;}
|
||||
bool IsInMap(Player*p){return mapid==p->mapid;}bool IsInPhase(Player*p){return phase && p->phase;}bool CanSeeOrDetect(Player*p){return p->visible;}bool IsWithinLOSInMap(Player*p){return los && p->los;}bool IsFriendlyTo(Player*p){return team==p->team;}
|
||||
Map*GetMap(){return ↦}unsigned GetMapId(){return mapid;}Session*GetSession(){return &session;}Group*GetGroup(){return group;}Group*GetGroupInvite(){return invite;}unsigned GetTeam(){return team;}ObjectGuid GetTarget(){return selection;}bool isAFK(){return afk;}
|
||||
void SetStandState(int s){posture=s;}void SetFacingToObject(Player*){}void HandleEmoteCommand(uint32 e){++gestures;lastEmote=e;}void Say(char const*,int,Player*){++says;}
|
||||
void GetNearPoint(Player*,float&x,float&y,float&z,float,float){x=m_positionX;y=m_positionY;z=m_positionZ;}
|
||||
std::vector<Player*> people;void GetPlayerListInGrid(std::vector<Player*>&out,float){out=people;}
|
||||
};
|
||||
struct Group {ObjectGuid leader;Member first;int removals=0,messages=0;ObjectGuid GetLeaderGUID(){return leader;}Member*GetFirstMember(){return first.player?&first:nullptr;}void BroadcastPacket(WorldPacket*,bool){++messages;}void RemoveMember(ObjectGuid,int){++removals;assert(first.player);first.player->group=nullptr;}};
|
||||
int accepts=0,declines=0;
|
||||
void Session::HandlePartyInviteResponseOpcode(WorldPackets::Party::PartyInviteResponse&p){assert(owner->invite);if(p.Accept){++accepts;owner->group=owner->invite;}else ++declines;owner->invite=nullptr;}
|
||||
struct ChatHandler{void SendSysMessage(char const*){}template<class...Args>void PSendSysMessage(char const*,Args...){}static void BuildChatPacket(WorldPacket&,int,int,Player*,Player*,char const*){}};
|
||||
namespace GameTime {time_t now=100;time_t GetGameTime(){return now;}}
|
||||
unsigned urand(unsigned a,unsigned){return a;}float roll=0;float frand(float,float){return roll;}
|
||||
namespace ObjectAccessor {std::unordered_map<ObjectGuid,Player*> players;Player*FindPlayer(ObjectGuid id){auto it=players.find(id);return it==players.end()?nullptr:it->second;}}
|
||||
struct Profile {std::string Locale="deDE";std::unordered_map<std::string,float> Personality;};
|
||||
struct Lua {std::unordered_map<unsigned,Profile> profiles;Profile const*GetAdventureProfile(unsigned id){auto it=profiles.find(id);return it==profiles.end()?nullptr:&it->second;}}lua;auto sClientlessLuaEngine=&lua;
|
||||
struct Behavior {int interrupts=0;std::string intent="social",schedule="social:inn";std::string InteractionIntent(Player*){return intent;}std::string InteractionSchedule(Player*){return schedule;}void InterruptForInteraction(Player*){++interrupts;}}behavior;auto sAdventureBehaviorMgr=&behavior;
|
||||
struct Combat {bool busy=false;int assists=0;bool Busy(Player*){return busy;}bool Assist(Player*,Unit*){++assists;return true;}}combat;auto sAdventureCombatMgr=&combat;
|
||||
struct ClientlessAction{std::string Name;std::unordered_map<std::string,std::string> Parameters;};
|
||||
struct Core {time_t activity=0;int queued=0;time_t LastActivity(ObjectGuid){return activity;}bool QueueAction(unsigned,ClientlessAction){++queued;return true;}}core;auto sClientlessPlayerMgr=&core;
|
||||
enum class ClientlessTravelState{Moving,Arrived};struct Nav {ClientlessTravelState State=ClientlessTravelState::Arrived;Position Target;};
|
||||
struct ClientlessPathDiagnostics{bool Complete=true,Suspicious=false;float PathLength=10;};
|
||||
struct Travel {Nav nav;bool active=false,valid=true;int starts=0,cancels=0;Nav*GetStatus(unsigned){return active?&nav:nullptr;}bool AnalyzePath(Player*,Position const&,ClientlessPathDiagnostics&){return valid;}void StartTravel(Player*,Position const&p){++starts;nav.Target=p;nav.State=ClientlessTravelState::Moving;active=true;}void CancelTravel(unsigned){++cancels;active=false;}}travel;auto sClientlessTravelMgr=&travel;
|
||||
enum class ClientlessActionExecutionState{Moving,SelectingTarget,Completed};struct Execution {ClientlessActionExecutionState State=ClientlessActionExecutionState::Completed;};
|
||||
struct Executor {Execution status;Execution*GetStatus(unsigned){return &status;}}executor;auto sClientlessActionExecutorMgr=&executor;
|
||||
struct Graph {void EnsureLoaded(){}bool FindNearestNodeToPosition(unsigned,float,float,float,std::string&node,float&distance){node="entrance";distance=10;return true;}}graph;auto sClientlessTravelGraphMgr=&graph;
|
||||
'''+header+'\n'+source+r'''
|
||||
int main(){
|
||||
using namespace AdventureInteractionPolicy;
|
||||
assert(Willingness("adventure",.65f)>Willingness("profession",.65f));assert(Willingness("rest",0)==0);
|
||||
assert(!LeaderExpired(699,100));assert(LeaderExpired(700,100));assert(!LeaderExpired(99,100));
|
||||
assert(CanLeave(true,false,false,false));assert(!CanLeave(true,true,false,false));assert(!CanLeave(true,false,true,false));
|
||||
AdventureInteractionMgr mgr;Player bot(26),leader(21),other(27);bot.session.clientless=true;other.session.clientless=true;
|
||||
ObjectAccessor::players={{bot.guid,&bot},{leader.guid,&leader},{other.guid,&other}};
|
||||
lua.profiles[26].Personality["sociability"]=.65f;lua.profiles[26].Personality["group_loyalty"]=0;lua.profiles[27]={};
|
||||
Group group;group.leader=leader.guid;group.first.player=⊥
|
||||
auto tick=[&](time_t now){GameTime::now=now;return mgr.Update(&bot,1000);};
|
||||
// Native invitation response creates actual membership, then suspends intent.
|
||||
bot.invite=&group;tick(100);assert(!bot.group);tick(102);assert(bot.group && accepts==1);
|
||||
assert(mgr._records[26].Grouped && behavior.interrupts==1);
|
||||
// Follow uses verified local MMAP paths; invalid paths never move the player.
|
||||
leader.m_positionX=20;tick(103);assert(travel.starts==1);travel.active=false;travel.valid=false;leader.m_positionX=30;tick(104);assert(travel.starts==1);travel.valid=true;
|
||||
// Changing leader resets both AFK and inactivity timers.
|
||||
mgr._records[26].AfkSince=1;group.leader=other.guid;tick(105);assert(mgr._records[26].AfkSince==0 && mgr._records[26].LastActivity==105);
|
||||
group.leader=leader.guid;tick(106);
|
||||
// Chat/spells count as activity even while the leader stands still.
|
||||
core.activity=650;tick(650);tick(1249);assert(bot.group);tick(1250);assert(!bot.group && group.removals==1);
|
||||
// Hour intent change is deferred inside instances and during combat.
|
||||
core.activity=0;bot.group=&group;behavior.schedule="adventure:road";tick(1300);behavior.schedule="economy:city";
|
||||
bot.map.dungeon=true;tick(1301);assert(bot.group);bot.map.dungeon=false;bot.combat=true;tick(1302);assert(bot.group);bot.combat=false;tick(1303);assert(!bot.group);
|
||||
// Native AFK needs a full ten minutes even if manually set; no early exit.
|
||||
bot.group=&group;tick(1400);leader.afk=true;tick(1401);tick(2000);assert(bot.group);tick(2001);assert(!bot.group);leader.afk=false;
|
||||
// Grouped bots also answer directed emotes; responses do not create events.
|
||||
leader.m_positionX=0;bot.group=&group;tick(2100);mgr.Emote(&bot,&leader,TEXT_EMOTE_WAVE);tick(2101);assert(bot.gestures==1);tick(2102);assert(bot.gestures==1);tick(2103);assert(bot.group);
|
||||
// Friendly greeting versus hostile contact; no combat call is made.
|
||||
bot.group=nullptr;tick(2120);mgr.Emote(&bot,&other,TEXT_EMOTE_WAVE);tick(2121);assert(bot.lastEmote==EMOTE_ONESHOT_WAVE);tick(2123);
|
||||
other.team=999;mgr._records[26].Encounters.clear();mgr._records[26].NextSocial=0;mgr.Emote(&bot,&other,TEXT_EMOTE_THREATEN);tick(2124);assert(bot.lastEmote==EMOTE_ONESHOT_NO && combat.assists==0);tick(2126);
|
||||
// Invisible/distant targets and repeated emotes are ignored.
|
||||
auto gestures=bot.gestures;other.visible=false;mgr.Emote(&bot,&other,TEXT_EMOTE_WAVE);tick(2130);assert(bot.gestures==gestures);other.visible=true;
|
||||
// Bot encounters reserve both participants; cooldown prevents new loops.
|
||||
other.team=ALLIANCE;bot.people={&other};mgr._records.clear();tick(2200);assert(mgr._records[26].Partner==other.guid && mgr._records[27].Partner==bot.guid);
|
||||
tick(2201);GameTime::now=2203;mgr.Update(&other,1000);assert(other.gestures==1);tick(2205);GameTime::now=2205;mgr.Update(&other,1000);tick(2265);assert(!mgr._records[26].Partner);
|
||||
// Bot-origin invitations are declined; autonomous bot grouping is deferred.
|
||||
mgr._records.clear();group.leader=other.guid;bot.invite=&group;tick(2300);tick(2302);assert(!bot.group && declines==1);
|
||||
// Missing leader map uses the existing graph executor, without teleporting.
|
||||
group.leader=leader.guid;bot.group=&group;leader.mapid=1;tick(2400);assert(core.queued==1);executor.status.State=ClientlessActionExecutionState::Moving;tick(2416);assert(core.queued==1);
|
||||
// Pending help is acknowledged only outside combat, after caller's self-care.
|
||||
mgr.CombatHelp(&bot,leader.guid);leader.mapid=0;executor.status.State=ClientlessActionExecutionState::Completed;bot.combat=true;auto says=bot.says;tick(2417);assert(bot.says==says);bot.combat=false;tick(2418);assert(bot.says==says+1);
|
||||
mgr.Offline(&bot);assert(!mgr._records.count(26));
|
||||
}
|
||||
'''
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
p=Path(d);(p/'test.cpp').write_text(code)
|
||||
subprocess.run(['g++','-std=c++17','-Wall','-Wextra','-I',str(root/'src/server/game/World'),str(p/'test.cpp'),'-o',str(p/'test')],check=True)
|
||||
subprocess.run([str(p/'test')],check=True)
|
||||
print('production interaction manager: invitations, factions, reservations, follow, AFK and deferred departure passed')
|
||||
@@ -0,0 +1,56 @@
|
||||
"""Exercise production map-worker social/helper queues concurrently."""
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import tempfile
|
||||
root=Path(__file__).resolve().parents[2]
|
||||
source=(root/'src/server/game/World/ClientlessPlayerMgr.cpp').read_text()
|
||||
def function(signature):
|
||||
a=source.index(signature);b=source.index('{',a);e=b+1;depth=1
|
||||
while depth:depth+=(source[e]=='{')-(source[e]=='}');e+=1
|
||||
return source[a:e]
|
||||
code=r'''
|
||||
#include <cassert>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
using uint32=unsigned;
|
||||
struct ObjectGuid{unsigned id;unsigned GetCounter()const{return id;}bool operator==(ObjectGuid b)const{return id==b.id;}};
|
||||
struct Player;
|
||||
struct Unit {ObjectGuid guid;Player*owner=nullptr;std::vector<Unit*> attackers;ObjectGuid GetGUID(){return guid;}virtual Player*ToPlayer(){return nullptr;}Player*GetControllingPlayer(){return owner;}auto&getAttackers(){return attackers;}};
|
||||
struct Session {bool clientless=true;bool IsClientless(){return clientless;}};
|
||||
struct Player:Unit {Session session;Player(){owner=this;}Player*ToPlayer()override{return this;}Session*GetSession(){return &session;}};
|
||||
struct ClientlessPlayerMgr {
|
||||
struct EmoteNotice{ObjectGuid Source;uint32 Emote;};struct HelpNotice{ObjectGuid Helper,Target;};
|
||||
std::mutex _damageMutex;std::unordered_map<uint32,std::deque<EmoteNotice>> _emoteNotices;std::unordered_map<uint32,std::deque<HelpNotice>> _helpNotices;
|
||||
void TextEmote(Player*,Player*,uint32);void SharedTargetDamage(Unit*,Unit*,uint32);
|
||||
};
|
||||
'''+function('void ClientlessPlayerMgr::TextEmote(')+'\n'+function('void ClientlessPlayerMgr::SharedTargetDamage(')+r'''
|
||||
int main(){
|
||||
ClientlessPlayerMgr mgr;std::vector<Player> bots(16);std::vector<std::thread> threads;
|
||||
Player helper;helper.guid={77};helper.session.clientless=false;
|
||||
for(unsigned i=0;i<bots.size();++i){bots[i].guid={i+1};threads.emplace_back([&,i]{
|
||||
Unit target;target.guid={900+i};target.attackers={&bots[i],&helper};
|
||||
for(unsigned n=1;n<=1000;++n){mgr.TextEmote(&helper,&bots[i],n);mgr.SharedTargetDamage(&target,&helper,n);}
|
||||
});}
|
||||
for(auto&t:threads)t.join();
|
||||
for(unsigned i=0;i<bots.size();++i){
|
||||
auto const&emotes=mgr._emoteNotices[i+1];assert(emotes.size()==8);
|
||||
auto const&helps=mgr._helpNotices[i+1];assert(helps.size()==8);
|
||||
for(unsigned n=0;n<8;++n){assert(emotes[n].Source.id==77 && emotes[n].Emote==n+1);assert(helps[n].Helper.id==77 && helps[n].Target.id==900+i);}
|
||||
}
|
||||
mgr._emoteNotices.clear();mgr.TextEmote(nullptr,&bots[0],1);mgr.TextEmote(&helper,&helper,1);mgr.TextEmote(&bots[0],&helper,1);assert(mgr._emoteNotices.empty());
|
||||
mgr._helpNotices.clear();Unit target;target.guid={1000};target.attackers={&bots[0]};
|
||||
mgr.SharedTargetDamage(&target,&helper,0);mgr.SharedTargetDamage(&target,nullptr,10);assert(mgr._helpNotices.empty());
|
||||
Unit pet;pet.guid={800};pet.owner=&helper;mgr.SharedTargetDamage(&target,&pet,10);assert(mgr._helpNotices[1].front().Helper.id==77);
|
||||
}
|
||||
'''
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
p=Path(d);(p/'test.cpp').write_text(code)
|
||||
subprocess.run(['g++','-std=c++17','-pthread',str(p/'test.cpp'),'-o',str(p/'test')],check=True)
|
||||
subprocess.run([str(p/'test')],check=True)
|
||||
unit=(root/'src/server/game/Entities/Unit/Unit.cpp').read_text();start=unit.index('uint32 Unit::DealDamage(')
|
||||
hook=unit.index('sClientlessPlayerMgr->SharedTargetDamage(',start)
|
||||
assert unit.index('GetCommandStatus(CHEAT_GOD)',start)<hook<unit.index('Unit::Kill(attacker, victim, durabilityLoss);',hook)
|
||||
print('Production social/helper queues: concurrent GUID-only delivery, bounded queues, human/self/zero-damage gates, pet attribution and lethal-cleanup ordering passed')
|
||||
Reference in new issue
Block a user