Clone of PhatAC @ https://github.com/floaterxk/PhatAC

ClientEvents.cpp 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065
  1. #include "StdAfx.h"
  2. #include "Client.h"
  3. #include "ClientCommands.h"
  4. #include "ClientEvents.h"
  5. #include "World.h"
  6. // Database access
  7. #include "Database.h"
  8. #include "CharacterDatabase.h"
  9. // Network helpers
  10. #include "BinaryWriter.h"
  11. #include "ChatMsgs.h"
  12. #include "ObjectMsgs.h"
  13. // Objects
  14. #include "PhysicsObj.h"
  15. #include "Item.h"
  16. #include "Monster.h"
  17. #include "Player.h"
  18. #include "Door.h"
  19. #include "ChatMsgs.h"
  20. CClientEvents::CClientEvents(CClient *parent)
  21. {
  22. m_pClient = parent;
  23. m_pPlayer = NULL;
  24. m_fLogout = 0;
  25. }
  26. CClientEvents::~CClientEvents()
  27. {
  28. if (m_pPlayer)
  29. {
  30. g_pWorld->RemoveEntity(m_pPlayer);
  31. }
  32. }
  33. void CClientEvents::DetachPlayer()
  34. {
  35. m_pPlayer = NULL;
  36. }
  37. DWORD CClientEvents::GetPlayerID()
  38. {
  39. if (!m_pPlayer)
  40. return NULL;
  41. return m_pPlayer->m_dwGUID;
  42. }
  43. CBasePlayer* CClientEvents::GetPlayer()
  44. {
  45. return m_pPlayer;
  46. }
  47. void CClientEvents::ExitWorld()
  48. {
  49. if (m_pPlayer)
  50. {
  51. g_pWorld->RemoveEntity(m_pPlayer);
  52. DetachPlayer();
  53. }
  54. m_pClient->ExitWorld();
  55. m_fLogout = 0;
  56. }
  57. void CClientEvents::Think()
  58. {
  59. if (m_fLogout && ((m_fLogout + 5.00f) < g_pGlobals->Time()))
  60. {
  61. ExitWorld();
  62. }
  63. }
  64. void CClientEvents::BeginLogout()
  65. {
  66. if (m_pPlayer && !m_fLogout)
  67. {
  68. m_pPlayer->Animation_ClipMotions(0);
  69. m_pPlayer->Animation_PlayPrimary(286, 1.0f, 5.0f);
  70. m_fLogout = g_pGlobals->Time();
  71. }
  72. }
  73. void CClientEvents::LoginError(int iError)
  74. {
  75. DWORD ErrorPackage[2];
  76. ErrorPackage[0] = 0xF659;
  77. ErrorPackage[1] = iError;
  78. m_pClient->SendNetMessage(ErrorPackage, sizeof(ErrorPackage), PRIVATE_MSG);
  79. }
  80. void CClientEvents::LoginCharacter(DWORD dwGUID, const char *szAccount)
  81. {
  82. _CHARDESC desc;
  83. if (m_pPlayer || g_pWorld->FindPlayer(dwGUID))
  84. {
  85. // LOG(Temp, Normal, "Character already logged in!\n");
  86. LoginError(13);
  87. LOG(Client, Warning, "Login request, but character already logged in!\n");
  88. return;
  89. }
  90. if (!g_pDB->CharDB()->GetCharacterDesc(dwGUID, &desc))
  91. return;
  92. if (stricmp(szAccount, desc.szAccount))
  93. {
  94. LoginError(15);
  95. LOG(Client, Warning, "Bad account for login: \"%s\" \"%s\"\n", szAccount, desc.szAccount);
  96. return;
  97. }
  98. m_pPlayer = new CBasePlayer(m_pClient, dwGUID);
  99. g_pWorld->CreateEntity(m_pPlayer);
  100. m_pPlayer->LoginCharacter();
  101. return;
  102. }
  103. void CClientEvents::SendText(const char *szText, long lColor)
  104. {
  105. m_pClient->SendNetMessage(ServerText(szText, lColor), PRIVATE_MSG, FALSE, TRUE);
  106. }
  107. void CClientEvents::UpdateBurdenUI()
  108. {
  109. }
  110. void CClientEvents::Attack(DWORD dwTarget, DWORD dwHeight, float flPower)
  111. {
  112. m_pPlayer->Attack(dwTarget, dwHeight, flPower);
  113. }
  114. void CClientEvents::SendTellByGUID(const char* szText, DWORD dwGUID)
  115. {
  116. if (strlen(szText) > 300)
  117. return;
  118. //should really check for invalid characters and such ;]
  119. while (szText[0] == ' ') //Skip leading spaces.
  120. szText++;
  121. if (szText[0] == '\0') //Make sure the text isn't blank
  122. return;
  123. if (dwGUID == m_pPlayer->m_dwGUID)
  124. {
  125. m_pPlayer->SendNetMessage(ServerText("You really need some new friends..", 1), PRIVATE_MSG, FALSE);
  126. return;
  127. }
  128. CBasePlayer *pTarget;
  129. if (!(pTarget = g_pWorld->FindPlayer(dwGUID)))
  130. return;
  131. char szResponse[300];
  132. _snprintf(szResponse, 300, "You tell %s, \"%s\"", pTarget->GetName(), szText);
  133. m_pPlayer->SendNetMessage(ServerText(szResponse, 4), PRIVATE_MSG, FALSE);
  134. pTarget->SendNetMessage(DirectChat(szText, m_pPlayer->GetName(), m_pPlayer->m_dwGUID, dwGUID, 3), PRIVATE_MSG, TRUE);
  135. }
  136. void CClientEvents::SendTellByName(const char* szText, const char* szName)
  137. {
  138. if (strlen(szName) > 300)
  139. return;
  140. if (strlen(szText) > 300)
  141. return;
  142. //should really check for invalid characters and such ;]
  143. while (szText[0] == ' ') //Skip leading spaces.
  144. szText++;
  145. if (szText[0] == '\0') //Make sure the text isn't blank
  146. return;
  147. CBasePlayer *pTarget;
  148. if (!(pTarget = g_pWorld->FindPlayer(szName)))
  149. return;
  150. if (pTarget->m_dwGUID == m_pPlayer->m_dwGUID)
  151. {
  152. m_pPlayer->SendNetMessage(ServerText("You really need some new friends..", 1), PRIVATE_MSG, FALSE);
  153. return;
  154. }
  155. char szResponse[300];
  156. _snprintf(szResponse, 300, "You tell %s, \"%s\"", pTarget->GetName(), szText);
  157. m_pPlayer->SendNetMessage(ServerText(szResponse, 4), PRIVATE_MSG, FALSE, TRUE);
  158. pTarget->SendNetMessage(DirectChat(szText, m_pPlayer->GetName(), m_pPlayer->m_dwGUID, pTarget->m_dwGUID, 3), PRIVATE_MSG, TRUE);
  159. }
  160. void CClientEvents::ClientText(char* szText)
  161. {
  162. if (strlen(szText) > 1000)
  163. return;
  164. //should really check for invalid characters and such ;]
  165. while (szText[0] == ' ') //Skip leading spaces.
  166. szText++;
  167. if (szText[0] == '\0') //Make sure the text isn't blank
  168. return;
  169. if (szText[0] == '!' || szText[0] == '@' || szText[0] == '/')
  170. {
  171. CommandBase::Execute(++szText, m_pPlayer, m_pClient->GetAccessLevel());
  172. }
  173. else
  174. m_pPlayer->SpeakLocal(szText);
  175. }
  176. void CClientEvents::EmoteText(char* szText)
  177. {
  178. if (strlen(szText) > 300)
  179. return;
  180. //TODO: Check for invalid characters and such ;)
  181. while (szText[0] == ' ') //Skip leading spaces.
  182. szText++;
  183. if (szText[0] == '\0') //Make sure the text isn't blank
  184. return;
  185. m_pPlayer->EmoteLocal(szText);
  186. }
  187. void CClientEvents::ActionText(char* szText)
  188. {
  189. if (strlen(szText) > 300)
  190. return;
  191. //TODO: Check for invalid characters and such ;)
  192. while (szText[0] == ' ') //Skip leading spaces.
  193. szText++;
  194. if (szText[0] == '\0') //Make sure the text isn't blank
  195. return;
  196. m_pPlayer->ActionLocal(szText);
  197. }
  198. void CClientEvents::ChannelText(DWORD dwChannel, const char* szText)
  199. {
  200. if (strlen(szText) > 300)
  201. return;
  202. //TODO: Check for invalid characters and such ;)
  203. while (szText[0] == ' ')
  204. szText++;
  205. if (szText[0] == '\0')
  206. return;
  207. // 0x00000400 = Urgent Assistance "help channel"
  208. // 0x00000800 = Fellowship
  209. switch (dwChannel)
  210. {
  211. case 0x400:
  212. case 0x800:
  213. //For now we'll just spam them to everyone!
  214. // g_pWorld->BroadcastGlobal(ChannelChat(dwChannel, m_pPlayer->GetName(), szText), PRIVATE_MSG, m_pPlayer->m_dwGUID, TRUE);
  215. g_pWorld->BroadcastGlobal(ServerText(csprintf("%s says to your fellow testers, \"%s\"", m_pPlayer->GetName(), szText), 3), PRIVATE_MSG, m_pPlayer->m_dwGUID, TRUE);
  216. }
  217. //Give a special copy to this player?
  218. switch (dwChannel)
  219. {
  220. case 0x400:
  221. case 0x800:
  222. m_pClient->SendNetMessage(ServerText(csprintf("You say to your fellow testers, \"%s\"", szText), 3), PRIVATE_MSG, TRUE);
  223. break;
  224. default:
  225. SendText("The server does not support this channel.", 1);
  226. break;
  227. }
  228. if (dwChannel == 0x800)
  229. {
  230. LOG(Client, Normal, "[%s] %s says, \"%s\"\n", timestamp(), m_pPlayer->GetName(), szText);
  231. }
  232. }
  233. void CClientEvents::RequestHealthUpdate(DWORD dwGUID)
  234. {
  235. CPhysicsObj *pEntity = g_pWorld->FindWithinPVS(m_pPlayer, dwGUID);
  236. if (pEntity) {
  237. if (pEntity->IsMonster())
  238. {
  239. m_pClient->SendNetMessage(HealthUpdate((CBaseMonster *)pEntity), PRIVATE_MSG, TRUE, TRUE);
  240. }
  241. }
  242. }
  243. void CClientEvents::EquipItem(DWORD dwItemID, DWORD dwCoverage)
  244. {
  245. // Scenarios to consider:
  246. // 1. Item being equipped from the GROUND!
  247. // 2. Item being equipped from different equip slot.
  248. // 3. Item being equipped from the player's inventory.
  249. CPhysicsObj * pEntity;
  250. CBaseItem * pItem;
  251. //Does the player have an inventory?
  252. if (!(m_pPlayer->Container_HasContents()))
  253. return;
  254. //Find the item.
  255. if (!(pEntity = g_pWorld->FindWithinPVS(m_pPlayer, dwItemID)))
  256. return;
  257. if (!(pEntity->IsItem()))
  258. return;
  259. pItem = (CBaseItem *)pEntity;
  260. //Can this item be equipped?
  261. if (!(pItem->CanEquip()))
  262. return;
  263. if (!(m_pPlayer->Container_CanEquip(pItem, dwCoverage)))
  264. return;
  265. //Equip it
  266. DWORD dwCell = m_pPlayer->GetLandcell();
  267. //Take it out of whatever slot it may be in.
  268. if (pItem->HasOwner())
  269. m_pPlayer->Container_ReleaseItem(pItem, FALSE);
  270. if (!(pItem->m_ItemType & (TYPE_ARMOR | TYPE_CLOTHING)))
  271. {
  272. pItem->SetWorldWielder(dwCell, m_pPlayer);
  273. }
  274. pItem->SetWorldContainer(dwCell, NULL);
  275. pItem->SetWorldCoverage(dwCell, dwCoverage);
  276. //The container will auto-correct this slot into a valid range.
  277. m_pPlayer->Container_EquipItem(dwCell, pItem, dwCoverage);
  278. m_pPlayer->EmitSound(0x77, 1.0f);
  279. m_pClient->SendNetMessage(InventoryEquip(dwItemID, dwCoverage), PRIVATE_MSG, TRUE);
  280. UpdateBurdenUI();
  281. if (pItem->m_ItemType & (TYPE_ARMOR|TYPE_CLOTHING))
  282. {
  283. m_pPlayer->UpdateModel();
  284. }
  285. }
  286. void CClientEvents::StoreItem(DWORD dwItemID, DWORD dwContainer, char cSlot)
  287. {
  288. if (cSlot < 0)
  289. return;
  290. // Scenarios to consider:
  291. // 1. Item being stored is equipped.
  292. // 2. Item being stored is on the GROUND!
  293. // 3. Item being stored is already in the inventory.
  294. CPhysicsObj * pEntity;
  295. CPhysicsObj * pContainer;
  296. CBaseItem * pItem;
  297. //Find the container.
  298. if (!(m_pPlayer->Container_HasContents()))
  299. return;
  300. if (!(pContainer = m_pPlayer->Container_FindContainer(dwContainer)))
  301. return;
  302. //Find the item.
  303. if (!(pEntity = g_pWorld->FindWithinPVS(m_pPlayer, dwItemID)))
  304. return;
  305. if (!(pEntity->IsItem()))
  306. return;
  307. pItem = (CBaseItem *)pEntity;
  308. if (!(pItem->CanPickup()))
  309. return;
  310. if (!(pContainer->Container_CanStore(pItem)))
  311. return;
  312. if (pItem->GetSlotType()) //Only store/move ordinary items for now.
  313. return;
  314. DWORD dwCell = m_pPlayer->GetLandcell();
  315. //Take it out of whatever slot it's in.
  316. pItem->SetWorldContainer(dwCell, pContainer);
  317. if (pItem->IsWielded())
  318. pItem->SetWorldWielder(dwCell, NULL);
  319. m_pPlayer->Container_ReleaseItem(pItem, FALSE);
  320. //The container will auto-correct this slot into a valid range.
  321. cSlot = pContainer->Container_InsertInventoryItem(dwCell, pItem, cSlot);
  322. m_pPlayer->EmitSound(0x78, 1.0f);
  323. m_pClient->SendNetMessage(InventoryMove(dwItemID, dwContainer, cSlot, 0), PRIVATE_MSG, TRUE);
  324. UpdateBurdenUI();
  325. if (pItem->m_ItemType & (TYPE_ARMOR | TYPE_CLOTHING))
  326. m_pPlayer->UpdateModel();
  327. }
  328. void CClientEvents::DropItem(DWORD dwItemID)
  329. {
  330. // Scenarios to consider:
  331. // 1. Item being stored is equipped.
  332. // 3. Item being stored is in the inventory.
  333. CBaseItem * pItem;
  334. //Find the container.
  335. if (!(m_pPlayer->Container_HasContents()))
  336. return;
  337. //Find the item.
  338. if (!(pItem = m_pPlayer->Container_FindItem(dwItemID)))
  339. return;
  340. m_pPlayer->Animation_PlayPrimary(24, 1.0f, 2.0f);
  341. DWORD dwCell = m_pPlayer->GetLandcell();
  342. //Take it out of whatever slot it's in.
  343. m_pPlayer->Container_ReleaseItem(pItem, FALSE);
  344. pItem->SetWorldContainer(dwCell, NULL);
  345. //if ( pItem->IsWielded() )
  346. pItem->SetWorldWielder(dwCell, NULL);
  347. m_pPlayer->EmitSound(0x7B, 1.0f);
  348. m_pClient->SendNetMessage(InventoryDrop(dwItemID), PRIVATE_MSG, TRUE);
  349. g_pWorld->InsertEntity(pItem);
  350. pItem->Movement_Teleport(m_pPlayer->m_Origin, m_pPlayer->m_Angles);
  351. UpdateBurdenUI();
  352. if (pItem->m_ItemType & (TYPE_ARMOR | TYPE_CLOTHING))
  353. m_pPlayer->UpdateModel();
  354. }
  355. void CClientEvents::ChangeCombatStance(DWORD dwStance)
  356. {
  357. m_pPlayer->Animation_SetCombatMode((WORD)dwStance);
  358. ActionComplete();
  359. }
  360. void CClientEvents::ExitPortal()
  361. {
  362. m_pPlayer->ExitPortal();
  363. }
  364. void CClientEvents::Ping()
  365. {
  366. // Pong!
  367. DWORD Pong = 0x1EA;
  368. m_pClient->SendNetMessage(&Pong, sizeof(Pong), PRIVATE_MSG, TRUE);
  369. }
  370. void CClientEvents::UseItemEx(DWORD dwSourceID, DWORD dwDestID)
  371. {
  372. CPhysicsObj *pSource = m_pPlayer->FindChild(dwSourceID);
  373. CPhysicsObj *pDest = m_pPlayer->FindChild(dwDestID);
  374. if (!pSource)
  375. pSource = g_pWorld->FindWithinPVS(m_pPlayer, dwSourceID);
  376. if (!pDest)
  377. pDest = g_pWorld->FindWithinPVS(m_pPlayer, dwDestID);
  378. if (pSource && pDest)
  379. pDest->UseEx(m_pPlayer, pSource);
  380. else
  381. ActionComplete();
  382. }
  383. void CClientEvents::UseObject(DWORD dwEID)
  384. {
  385. CPhysicsObj *pTarget = m_pPlayer->FindChild(dwEID);
  386. if (!pTarget)
  387. pTarget = g_pWorld->FindWithinPVS(m_pPlayer, dwEID);
  388. if (pTarget)
  389. pTarget->Use(m_pPlayer);
  390. else
  391. ActionComplete();
  392. }
  393. void CClientEvents::ActionComplete()
  394. {
  395. DWORD ActionComplete[2];
  396. ActionComplete[0] = 0x1C7;
  397. ActionComplete[1] = 0;
  398. m_pClient->SendNetMessage(ActionComplete, sizeof(ActionComplete), PRIVATE_MSG, TRUE);
  399. }
  400. void CClientEvents::Identify(DWORD dwObjectID)
  401. {
  402. SendText("Identify will be incomplete -- it's not done!", 9);
  403. CPhysicsObj *pTarget = m_pPlayer->FindChild(dwObjectID);
  404. if (!pTarget)
  405. pTarget = g_pWorld->FindWithinPVS(m_pPlayer, dwObjectID);
  406. if (pTarget)
  407. {
  408. pTarget->Identify(m_pPlayer);
  409. m_pPlayer->SetLastAssessed(pTarget->m_dwGUID);
  410. }
  411. ActionComplete();
  412. }
  413. void CClientEvents::SpendAttributeXP(DWORD dwAttribute, DWORD dwXP)
  414. {
  415. if (dwAttribute < 1 || dwAttribute > 6)
  416. return;
  417. DWORD dwUnassignedXP = m_pPlayer->GetObjectStat(eUnassignedXP);
  418. if (dwUnassignedXP < dwXP)
  419. return;
  420. m_pPlayer->GiveAttributeXP((eAttribute)dwAttribute, dwXP);
  421. m_pPlayer->SetObjectStat(eUnassignedXP, dwUnassignedXP - dwXP);
  422. }
  423. void CClientEvents::SpendVitalXP(DWORD dwVital, DWORD dwXP)
  424. {
  425. if (dwVital != 1 && dwVital != 3 && dwVital != 5)
  426. return;
  427. DWORD dwUnassignedXP = m_pPlayer->GetObjectStat(eUnassignedXP);
  428. if (dwUnassignedXP < dwXP)
  429. return;
  430. m_pPlayer->GiveVitalXP((eVital)dwVital, dwXP);
  431. m_pPlayer->SetObjectStat(eUnassignedXP, dwUnassignedXP - dwXP);
  432. }
  433. void CClientEvents::SpendSkillXP(DWORD dwSkill, DWORD dwXP)
  434. {
  435. if (dwSkill < 1 || dwSkill > 0x27)
  436. return;
  437. DWORD dwUnassignedXP = m_pPlayer->GetObjectStat(eUnassignedXP);
  438. if (dwUnassignedXP < dwXP)
  439. return;
  440. m_pPlayer->GiveSkillXP((eSkill)dwSkill, dwXP);
  441. m_pPlayer->SetObjectStat(eUnassignedXP, dwUnassignedXP - dwXP);
  442. }
  443. void CClientEvents::LifestoneRecall()
  444. {
  445. //150 or 163?
  446. if (m_pPlayer->m_bLifestoneBound)
  447. {
  448. m_pPlayer->Animation_PlaySimpleAnimation(0x163, 1.0f, 18.0f, ANIM_LSRECALL);
  449. }
  450. else
  451. {
  452. m_pClient->SendNetMessage(ServerText("You are not bound to a Lifestone!", 7), PRIVATE_MSG);
  453. }
  454. }
  455. void CClientEvents::MarketplaceRecall()
  456. {
  457. //150 or 163?
  458. m_pPlayer->Animation_PlaySimpleAnimation(0x163, 1.0f, 18.0f, ANIM_MPRECALL);
  459. }
  460. // This is it!
  461. void CClientEvents::ProcessEvent(BinaryReader *in)
  462. {
  463. if (!m_pPlayer) return;
  464. DWORD dwSequence = in->ReadDWORD();
  465. DWORD dwEvent = in->ReadDWORD();
  466. if (in->GetLastError()) return;
  467. #ifdef _DEBUG
  468. LOG(Client, Verbose, "Processing event: 0x%X\n", dwEvent);
  469. #endif
  470. switch (dwEvent)
  471. {
  472. case 0x0008: //Attack
  473. {
  474. DWORD dwTarget = in->ReadDWORD();
  475. DWORD dwHeight = in->ReadDWORD();
  476. float flPower = in->ReadFloat();
  477. if (in->GetLastError()) break;
  478. Attack(dwTarget, dwHeight, flPower);
  479. break;
  480. }
  481. case 0x0015: //Client Text
  482. {
  483. char *szText = in->ReadString();
  484. if (in->GetLastError()) break;
  485. ClientText(szText);
  486. break;
  487. }
  488. case 0x0019: //Store Item
  489. {
  490. DWORD dwItemID = in->ReadDWORD();
  491. DWORD dwContainer = in->ReadDWORD();
  492. DWORD dwSlot = in->ReadDWORD();
  493. if (in->GetLastError()) break;
  494. StoreItem(dwItemID, dwContainer, (char)dwSlot);
  495. break;
  496. }
  497. case 0x001A: //Equip Item
  498. {
  499. DWORD dwItemID = in->ReadDWORD();
  500. DWORD dwCoverage = in->ReadDWORD();
  501. if (in->GetLastError()) break;
  502. EquipItem(dwItemID, dwCoverage);
  503. break;
  504. }
  505. case 0x001B: //Drop Item
  506. {
  507. DWORD dwItemID = in->ReadDWORD();
  508. if (in->GetLastError()) break;
  509. DropItem(dwItemID);
  510. break;
  511. }
  512. case 0x001F: // TOD 2017
  513. {
  514. DWORD dwUnk = in->ReadDWORD(); // value was "1"
  515. if (in->GetLastError()) break;
  516. break;
  517. }
  518. case 0x0032: //Send Tell by GUID
  519. {
  520. char* szText = in->ReadString();
  521. DWORD dwGUID = in->ReadDWORD();
  522. if (in->GetLastError()) break;
  523. SendTellByGUID(szText, dwGUID);
  524. break;
  525. }
  526. case 0x0035: //Use Item Ex
  527. {
  528. //SendText("Use extended not implemented yet.", 9);
  529. DWORD dwSourceID = in->ReadDWORD();
  530. DWORD dwDestID = in->ReadDWORD();
  531. if (in->GetLastError()) break;
  532. UseItemEx(dwSourceID, dwDestID);
  533. break;
  534. }
  535. case 0x0036: //Use Object
  536. {
  537. //SendText("Use not implemented yet.", 9);
  538. DWORD dwEID = in->ReadDWORD();
  539. if (in->GetLastError()) break;
  540. UseObject(dwEID);
  541. break;
  542. }
  543. case 0x0044: //Spend Vital XP
  544. {
  545. DWORD dwVital = in->ReadDWORD();
  546. DWORD dwXP = in->ReadDWORD();
  547. if (in->GetLastError()) break;
  548. SpendVitalXP(dwVital, dwXP);
  549. break;
  550. }
  551. case 0x0045: //Spend Attribute XP
  552. {
  553. DWORD dwAttribute = in->ReadDWORD();
  554. DWORD dwXP = in->ReadDWORD();
  555. if (in->GetLastError()) break;
  556. SpendAttributeXP(dwAttribute, dwXP);
  557. break;
  558. }
  559. case 0x0046: //Spend Skill XP
  560. {
  561. DWORD dwSkill = in->ReadDWORD();
  562. DWORD dwXP = in->ReadDWORD();
  563. if (in->GetLastError()) break;
  564. SpendSkillXP(dwSkill, dwXP);
  565. break;
  566. }
  567. case 0x0053: //Change Combat Mode
  568. {
  569. DWORD dwStance = in->ReadDWORD();
  570. if (in->GetLastError()) break;
  571. ChangeCombatStance(dwStance);
  572. break;
  573. }
  574. case 0x005D: //Send Tell by Name
  575. {
  576. char* szText = in->ReadString();
  577. char* szName = in->ReadString();
  578. if (in->GetLastError()) break;
  579. SendTellByName(szText, szName);
  580. break;
  581. }
  582. case 0x0063: //Lifestone Recall
  583. {
  584. LifestoneRecall();
  585. break;
  586. }
  587. case 0x00A1: // "Login Complete"
  588. {
  589. ExitPortal();
  590. break;
  591. }
  592. case 0x00C8: //Identify
  593. {
  594. DWORD dwObjectID = in->ReadDWORD();
  595. if (in->GetLastError()) break;
  596. Identify(dwObjectID);
  597. break;
  598. }
  599. case 0x00D6: // Advocate teleport (triggered by having an admin flag set, clicking the mini-map)
  600. {
  601. // Starts with string (was empty when I tested)
  602. in->ReadString();
  603. // Then position (target)
  604. loc_t loc = in->Read<loc_t>();
  605. heading_t angles = in->Read<heading_t>();
  606. m_pPlayer->Movement_Teleport(loc, angles);
  607. break;
  608. }
  609. case 0x0147: //Channel Text
  610. {
  611. DWORD dwChannel = in->ReadDWORD();
  612. char* szText = in->ReadString();
  613. if (in->GetLastError()) break;
  614. ChannelText(dwChannel, szText);
  615. }
  616. break;
  617. case 0x01BF: //Request health update
  618. {
  619. DWORD dwGUID = in->ReadDWORD();
  620. if (in->GetLastError()) break;
  621. RequestHealthUpdate(dwGUID);
  622. break;
  623. }
  624. case 0x01DF: //Indirect Text (@me)
  625. {
  626. char *szText = in->ReadString();
  627. if (in->GetLastError()) break;
  628. EmoteText(szText);
  629. break;
  630. }
  631. case 0x01E1: //Emote Text (*laugh* sends 'laughs')
  632. {
  633. char *szText = in->ReadString();
  634. if (in->GetLastError()) break;
  635. ActionText(szText);
  636. break;
  637. }
  638. case 0x01E9: //Ping
  639. {
  640. Ping();
  641. break;
  642. }
  643. case 0x028D: //Marketplace Recall
  644. {
  645. MarketplaceRecall();
  646. break;
  647. }
  648. case 0xF61B: //Jump Movement
  649. {
  650. float flPower = in->ReadFloat();
  651. float flDir1 = in->ReadFloat();
  652. float flDir2 = in->ReadFloat();
  653. float flHeight = in->ReadFloat();
  654. loc_t *location = (loc_t *)in->ReadArray(sizeof(loc_t));
  655. heading_t *angles = (heading_t *)in->ReadArray(sizeof(heading_t));
  656. if (in->GetLastError()) break;
  657. memcpy(&m_pPlayer->m_Origin, location, sizeof(loc_t));
  658. memcpy(&m_pPlayer->m_Angles, angles, sizeof(heading_t));
  659. m_pPlayer->Animation_Jump(flPower, flDir1, flDir2, flHeight);
  660. m_pPlayer->m_bAnimUpdate = TRUE;
  661. break;
  662. }
  663. case 0xF61C: //Update Vector Movement
  664. {
  665. float fSpeedMod = m_pPlayer->m_fSpeedMod;
  666. float flForwardMod = 0;
  667. float flStrafMod = 0;
  668. float flTurnMod = 0;
  669. WORD wForwardAnim = 0;
  670. WORD wStrafAnim = 0;
  671. WORD wTurnAnim = 0;
  672. WORD wStance = 0x3D;
  673. BOOL bRun = FALSE;
  674. DWORD dwFlags = in->ReadDWORD();
  675. if (dwFlags & 1)
  676. {
  677. DWORD dwRunUnk = in->ReadDWORD();
  678. #ifdef _DEBUG
  679. if (dwRunUnk != 2)
  680. {
  681. LOG(Temp, Normal, "RunUnk is %08X??\n", dwRunUnk);
  682. }
  683. else
  684. #endif
  685. bRun = TRUE;
  686. }
  687. if (!bRun)
  688. fSpeedMod = 1.0f;
  689. if (dwFlags & 2)
  690. {
  691. wStance = in->ReadWORD();
  692. in->ReadWORD(); //0x8000?? sequence maybe
  693. }
  694. if (dwFlags & 4)
  695. {
  696. DWORD dwForwardAnim = in->ReadDWORD();
  697. flForwardMod = fSpeedMod;
  698. switch (dwForwardAnim)
  699. {
  700. case 0x45000006: //backwards
  701. dwForwardAnim--;
  702. flForwardMod *= -backwards_factor;
  703. break;
  704. case 0x45000005: //forwards
  705. if (bRun)
  706. dwForwardAnim += 2;
  707. break;
  708. default:
  709. //0x45 = run/walk forward/back
  710. //0x44 = run/walk change
  711. //0x43 = emotes?
  712. //0x41 = change position? (kneel, sleep..)
  713. //0x40 = attack animations?
  714. //0x13 = drink something
  715. if ((dwForwardAnim >> 24) != 0x45)
  716. LOG(Temp, Normal, "Forward: %08X\n", dwForwardAnim);
  717. flForwardMod = 1.0000f;
  718. break;
  719. }
  720. wForwardAnim = (WORD)dwForwardAnim;
  721. }
  722. if (dwFlags & 8) {
  723. DWORD dwAutorunUnk = in->ReadDWORD();
  724. if (dwAutorunUnk != 2)
  725. {
  726. SendText("You cannot use right-click movement at this time!", 1);
  727. LOG(Temp, Normal, "Autorun is %08X?\n", dwAutorunUnk);
  728. }
  729. else
  730. {
  731. if (!bRun)
  732. wForwardAnim = 0x0005;
  733. else
  734. wForwardAnim = 0x0007;
  735. flForwardMod = fSpeedMod;
  736. }
  737. }
  738. if (dwFlags & 0x20) {
  739. DWORD dwStrafAnim = in->ReadDWORD();
  740. flStrafMod = fSpeedMod;
  741. switch (dwStrafAnim)
  742. {
  743. case 0x65000010: //left?
  744. dwStrafAnim--;
  745. flStrafMod *= -1.0f;
  746. case 0x6500000F: //right?
  747. flStrafMod *= (walk_anim_speed / sidestep_anim_speed) * sidestep_factor;
  748. if (flStrafMod > max_sidestep_anim_rate)
  749. flStrafMod = max_sidestep_anim_rate;
  750. else if (flStrafMod < -max_sidestep_anim_rate)
  751. flStrafMod = -max_sidestep_anim_rate;
  752. break;
  753. default:
  754. if ((dwStrafAnim >> 24) != 0x65)
  755. {
  756. // DebugMe();
  757. LOG(Animation, Warning, "Strafe anim == 0x%02X", dwStrafAnim >> 24);
  758. }
  759. flStrafMod = 1.0000f;
  760. break;
  761. }
  762. wStrafAnim = (WORD)dwStrafAnim;
  763. }
  764. if (dwFlags & 0x100) {
  765. DWORD dwTurnAnim = in->ReadDWORD();
  766. flTurnMod = 1.0f;
  767. switch (dwTurnAnim)
  768. {
  769. case 0x6500000E: //turn left
  770. dwTurnAnim--;
  771. flTurnMod *= -1.0f;
  772. case 0x6500000D: //turn right
  773. if (bRun)
  774. flTurnMod *= run_turn_factor;
  775. break;
  776. default:
  777. if ((dwTurnAnim >> 24) != 0x65)
  778. {
  779. LOG(Animation, Warning, "Turn anim == 0x%02X\n", dwTurnAnim >> 24);
  780. }
  781. break;
  782. }
  783. wTurnAnim = (WORD)dwTurnAnim;
  784. }
  785. DWORD dwEmotes = (dwFlags >> 11) & 7;
  786. animation_list lEmotes;
  787. for (unsigned int i = 0; i < dwEmotes; i++)
  788. {
  789. animation_t emote;
  790. emote.wIndex = in->ReadWORD(); //animation
  791. emote.wSequence = in->ReadWORD(); //counter
  792. emote.fSpeed = in->ReadFloat(); //play speed
  793. lEmotes.push_back(emote);
  794. }
  795. loc_t *location = (loc_t *)in->ReadArray(sizeof(loc_t));
  796. heading_t *angles = (heading_t *)in->ReadArray(sizeof(heading_t));
  797. if (in->GetLastError())
  798. {
  799. LOG(Animation, Verbose, "Bad animation message:\n");
  800. LOG_BYTES(Animation, Verbose, in->GetDataStart(), in->GetDataLen());
  801. break;
  802. }
  803. memcpy(&m_pPlayer->m_Origin, location, sizeof(loc_t));
  804. memcpy(&m_pPlayer->m_Angles, angles, sizeof(heading_t));
  805. m_pPlayer->m_fForwardSpeed = flForwardMod;
  806. m_pPlayer->m_fStrafSpeed = flStrafMod;
  807. m_pPlayer->m_fTurnSpeed = flTurnMod;
  808. m_pPlayer->m_wForwardAnim = wForwardAnim;
  809. m_pPlayer->m_wStrafAnim = wStrafAnim;
  810. m_pPlayer->m_wTurnAnim = wTurnAnim;
  811. m_pPlayer->m_wStance = wStance;
  812. DWORD dwAnimQueued = m_pPlayer->Animation_EmoteQueueCount();
  813. DWORD dwAnimNeeded = lEmotes.size();
  814. DWORD dwLastSequence = 0;
  815. for (unsigned int i = 0; i < dwAnimNeeded; i++)
  816. {
  817. if (dwAnimQueued >= MAX_EMOTE_QUEUE)
  818. break;
  819. dwAnimQueued++;
  820. for (animation_list::iterator it = lEmotes.begin(); it != lEmotes.end(); it++)
  821. {
  822. //find 0x800x
  823. if ((it->wSequence & 0xFF) == i)
  824. {
  825. m_pPlayer->Animation_PlayEmote(it->wIndex, it->fSpeed);
  826. break;
  827. }
  828. }
  829. }
  830. m_pPlayer->m_bAnimUpdate = TRUE;
  831. m_pPlayer->Movement_UpdatePos();
  832. break;
  833. }
  834. case 0xF753: //Update Exact Position
  835. {
  836. loc_t* location = reinterpret_cast<loc_t*> (in->ReadArray(sizeof(loc_t)));
  837. heading_t* angles = reinterpret_cast<heading_t*>(in->ReadArray(sizeof(heading_t)));
  838. WORD instance = in->ReadWORD();
  839. if (in->GetLastError()) break;
  840. if (instance != m_pPlayer->m_wInstance)
  841. {
  842. LOG(Temp, Normal, "Bad instance!!!!!!!!!!!!\n");
  843. break;
  844. }
  845. memcpy(&m_pPlayer->m_Origin, location, sizeof(loc_t));
  846. memcpy(&m_pPlayer->m_Angles, angles, sizeof(heading_t));
  847. m_pPlayer->Movement_UpdatePos();
  848. break;
  849. }
  850. default:
  851. {
  852. //Unknown Event
  853. // LOG(Temp, Normal, "Unhandled client event 0x%X:\n", dwEvent );
  854. // LOG_BYTES(Temp, Verbose, in->GetDataPtr(), in->GetDataEnd() - in->GetDataPtr() );
  855. }
  856. }
  857. }