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

World.cpp 23KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015
  1. #include "StdAfx.h"
  2. #include "World.h"
  3. #include "Client.h"
  4. #include "PhysicsObj.h"
  5. #include "Monster.h"
  6. #include "Player.h"
  7. #include "ChatMsgs.h"
  8. //Database access
  9. #include "Database.h"
  10. #include "CharacterDatabase.h"
  11. #include "TurbineDungeon.h"
  12. #include "GameMode.h"
  13. CWorld::CWorld()
  14. {
  15. LOG(Temp, Normal, "Initializing World..\n");
  16. m_dwHintStaticGUID = 0x10000000;
  17. m_dwHintPlayerGUID = 0x50000000;
  18. m_dwHintItemGUID = 0x60000000;
  19. m_dwHintDynamicGUID = 0xC0000000;
  20. ZeroMemory(m_pBlocks, sizeof(m_pBlocks));
  21. LoadStateFile();
  22. LoadDungeonsFile();
  23. LoadMOTD();
  24. EnumerateDungeonsFromCellData();
  25. m_fLastSave = g_pGlobals->Time();
  26. m_pGameMode = NULL;
  27. }
  28. void CWorld::SaveWorld()
  29. {
  30. SaveDungeonsFile();
  31. SaveStateFile();
  32. }
  33. CWorld::~CWorld()
  34. {
  35. if (m_pGameMode)
  36. {
  37. delete m_pGameMode;
  38. m_pGameMode = NULL;
  39. }
  40. for (LandblockVector::iterator it = m_vBlocks.begin(); it != m_vBlocks.end(); it++)
  41. delete (*it);
  42. m_vBlocks.clear();
  43. SaveWorld();
  44. m_mDungeons.clear();
  45. for (DungeonDescMap::iterator it = m_mDungeonDescs.begin(); it != m_mDungeonDescs.end(); it++)
  46. {
  47. DungeonDesc_t* pdd = &it->second;
  48. SafeDeleteArray(pdd->szDungeonName);
  49. SafeDeleteArray(pdd->szDescription);
  50. SafeDeleteArray(pdd->szAuthor);
  51. }
  52. m_mDungeonDescs.clear();
  53. }
  54. loc_t CWorld::FindDungeonDrop()
  55. {
  56. if (m_mDungeons.empty())
  57. {
  58. loc_t di;
  59. memset(&di, 0, sizeof(di));
  60. return di;
  61. }
  62. LocationMap::iterator dit = m_mDungeons.begin();
  63. long index = RandomLong(0, (long)m_mDungeons.size() - 1);
  64. while (index > 0) {
  65. index--;
  66. dit++;
  67. }
  68. return dit->second;
  69. }
  70. loc_t CWorld::FindDungeonDrop(WORD wBlockID)
  71. {
  72. LocationMap::iterator i = m_mDungeons.upper_bound(((DWORD(wBlockID) << 16) + 0x100) - 1);
  73. if ((i == m_mDungeons.end()) || (BLOCK_WORD(i->second.landcell) != wBlockID))
  74. {
  75. loc_t di;
  76. memset(&di, 0, sizeof(di));
  77. return di;
  78. }
  79. return i->second;
  80. }
  81. void CWorld::LoadStateFile()
  82. {
  83. FILE *ws = g_pDB->DataFileOpen("worldstate");
  84. if (ws)
  85. {
  86. fread(&m_dwHintStaticGUID, sizeof(DWORD), 1, ws);
  87. fread(&m_dwHintItemGUID, sizeof(DWORD), 1, ws);
  88. fread(&m_dwHintPlayerGUID, sizeof(DWORD), 1, ws);
  89. fread(&m_dwHintDynamicGUID, sizeof(DWORD), 1, ws);
  90. fclose(ws);
  91. }
  92. }
  93. void CWorld::SaveStateFile()
  94. {
  95. FILE *ws = g_pDB->DataFileCreate("worldstate");
  96. if (ws)
  97. {
  98. fwrite(&m_dwHintStaticGUID, sizeof(DWORD), 1, ws);
  99. fwrite(&m_dwHintItemGUID, sizeof(DWORD), 1, ws);
  100. fwrite(&m_dwHintPlayerGUID, sizeof(DWORD), 1, ws);
  101. fwrite(&m_dwHintDynamicGUID, sizeof(DWORD), 1, ws);
  102. fclose(ws);
  103. }
  104. else
  105. MsgBox(MB_ICONHAND, "Error opening WorldState file! Close NOW to avoid corruption!");
  106. }
  107. const char* CWorld::GetMOTD()
  108. {
  109. return m_strMOTD.c_str();
  110. }
  111. void CWorld::LoadMOTD()
  112. {
  113. FILE *motd = g_pDB->DataFileOpen("motd.txt", "rt");
  114. if (motd)
  115. {
  116. long lFileSize = fsize(motd);
  117. char* pcFileData = new char[lFileSize + 1];
  118. long lEnd = (long)fread(pcFileData, sizeof(char), lFileSize, motd);
  119. pcFileData[lEnd] = 0;
  120. m_strMOTD = pcFileData;
  121. delete[] pcFileData;
  122. fclose(motd);
  123. }
  124. else
  125. m_strMOTD = "No MOTD set.";
  126. }
  127. void CWorld::LoadDungeonsFile()
  128. {
  129. FILE *wd = g_pDB->DataFileOpen("worlddesc");
  130. if (wd)
  131. {
  132. long lSize = fsize(wd);
  133. BYTE* pbData = new BYTE[lSize];
  134. long lRead = (long)fread(pbData, sizeof(BYTE), lSize, wd);
  135. BinaryReader input(pbData, lRead);
  136. DWORD dwDungeonCount = input.ReadDWORD();
  137. if (!input.GetLastError())
  138. {
  139. for (DWORD i = 0; i < dwDungeonCount; i++)
  140. {
  141. DungeonDesc_t dd;
  142. dd.wBlockID = input.ReadWORD();
  143. dd.szDungeonName = input.ReadString();
  144. dd.szAuthor = input.ReadString();
  145. dd.szDescription = input.ReadString();
  146. loc_t* origin = (loc_t *)input.ReadArray(sizeof(loc_t));
  147. heading_t* angles = (heading_t *)input.ReadArray(sizeof(heading_t));
  148. if (input.GetLastError()) break;
  149. //Avoid using a buffer thats going to be deleted.
  150. dd.szDungeonName = _strdup(dd.szDungeonName);
  151. dd.szAuthor = _strdup(dd.szAuthor);
  152. dd.szDescription = _strdup(dd.szDescription);
  153. memcpy(&dd.origin, origin, sizeof(loc_t));
  154. memcpy(&dd.angles, angles, sizeof(heading_t));
  155. m_mDungeonDescs[dd.wBlockID] = dd;
  156. }
  157. }
  158. delete[] pbData;
  159. fclose(wd);
  160. }
  161. }
  162. void CWorld::SaveDungeonsFile()
  163. {
  164. FILE *wd = g_pDB->DataFileCreate("worlddesc");
  165. if (wd)
  166. {
  167. BinaryWriter output;
  168. output.WriteDWORD((DWORD)m_mDungeonDescs.size());
  169. for (DungeonDescMap::iterator i = m_mDungeonDescs.begin(); i != m_mDungeonDescs.end(); i++)
  170. {
  171. output.WriteWORD(i->second.wBlockID);
  172. output.WriteString(i->second.szDungeonName);
  173. output.WriteString(i->second.szAuthor);
  174. output.WriteString(i->second.szDescription);
  175. output.AppendData(i->second.origin);
  176. output.AppendData(i->second.angles);
  177. }
  178. fwrite(output.GetData(), output.GetSize(), sizeof(BYTE), wd);
  179. fclose(wd);
  180. }
  181. else
  182. MsgBox(MB_ICONHAND, "Error opening WorldDesc file! Close NOW to avoid corruption!");
  183. }
  184. BOOL CWorld::DungeonExists(WORD wBlockID)
  185. {
  186. LocationMap::iterator i = m_mDungeons.upper_bound(((DWORD(wBlockID) << 16) + 0x100) - 1);
  187. if ((i == m_mDungeons.end()) || (BLOCK_WORD(i->second.landcell) != wBlockID))
  188. return FALSE;
  189. return TRUE;
  190. }
  191. LocationMap* CWorld::GetDungeons()
  192. {
  193. return &m_mDungeons;
  194. }
  195. DungeonDescMap* CWorld::GetDungeonDescs()
  196. {
  197. return &m_mDungeonDescs;
  198. }
  199. DungeonDesc_t* CWorld::GetDungeonDesc(const char* szDungeonName)
  200. {
  201. DungeonDescMap::iterator i = m_mDungeonDescs.begin();
  202. DungeonDescMap::iterator iend = m_mDungeonDescs.end();
  203. while (i != iend)
  204. {
  205. if (!stricmp(szDungeonName, i->second.szDungeonName))
  206. return &i->second;
  207. i++;
  208. }
  209. return NULL;
  210. }
  211. DungeonDesc_t* CWorld::GetDungeonDesc(WORD wBlockID)
  212. {
  213. DungeonDescMap::iterator it = m_mDungeonDescs.find(wBlockID);
  214. if (it != m_mDungeonDescs.end())
  215. return &it->second;
  216. else
  217. return NULL;
  218. }
  219. void CWorld::SetDungeonDesc(WORD wBlockID, const char* szDungeonName, const char* szAuthor, const char* szDescription, loc_t origin, heading_t angles)
  220. {
  221. DungeonDescMap::iterator it = m_mDungeonDescs.find(wBlockID);
  222. if (it != m_mDungeonDescs.end())
  223. {
  224. DungeonDesc_t* pdd = &it->second;
  225. SafeDeleteArray(pdd->szDungeonName);
  226. SafeDeleteArray(pdd->szDescription);
  227. SafeDeleteArray(pdd->szAuthor);
  228. m_mDungeonDescs.erase(it);
  229. }
  230. DungeonDesc_t dd;
  231. dd.wBlockID = wBlockID;
  232. dd.szDungeonName = _strdup(szDungeonName);
  233. dd.szAuthor = _strdup(szAuthor);
  234. dd.szDescription = _strdup(szDescription);
  235. dd.origin = origin;
  236. dd.angles = angles;
  237. m_mDungeonDescs[wBlockID] = dd;
  238. }
  239. void CWorld::InitializeHintGUIDs()
  240. {
  241. _CHARDESC dummy;
  242. CCharacterDatabase *CharDB = g_pDB->CharDB();
  243. while (CharDB->GetCharacterDesc(m_dwHintPlayerGUID, &dummy))
  244. m_dwHintPlayerGUID++;
  245. }
  246. //Hackish.
  247. DWORD CWorld::GenerateGUID(eGUIDClass type)
  248. {
  249. switch (type)
  250. {
  251. case ePresetGUID: return 0;
  252. case ePlayerGUID:
  253. {
  254. // 0x50000000 - 0x60000000
  255. if (m_dwHintPlayerGUID >= 0x60000000)
  256. {
  257. LOG(Temp, Normal, "Player GUID overflow!\n");
  258. return 0;
  259. }
  260. _CHARDESC dummy;
  261. CCharacterDatabase *CharDB = g_pDB->CharDB();
  262. while (CharDB->GetCharacterDesc(m_dwHintPlayerGUID, &dummy))
  263. m_dwHintPlayerGUID++;
  264. //Temporary, use and increment the hint counter.
  265. return (m_dwHintPlayerGUID++);
  266. }
  267. case eStaticGUID:
  268. {
  269. /* 0x10000000 - 0x50000000 */
  270. if (m_dwHintStaticGUID >= 0x50000000)
  271. {
  272. LOG(Temp, Normal, "Static GUID overflow!\n");
  273. return 0;
  274. }
  275. //Temporary, use and increment the hint counter.
  276. return (m_dwHintStaticGUID++);
  277. }
  278. case eDynamicGUID:
  279. {
  280. // 0xC0000000 - 0xF0000000
  281. if (m_dwHintDynamicGUID >= 0xF0000000)
  282. {
  283. LOG(Temp, Normal, "Dynamic GUID overflow!\n");
  284. return 0;
  285. }
  286. //Temporary, use and increment the hint counter.
  287. return (m_dwHintDynamicGUID++);
  288. }
  289. case eItemGUID:
  290. {
  291. // 0x60000000 - 0xC0000000
  292. if (m_dwHintItemGUID >= 0xC0000000)
  293. {
  294. LOG(Temp, Normal, "Item GUID overflow!\n");
  295. return 0;
  296. }
  297. //Temporary, use and increment the hint counter.
  298. return (m_dwHintItemGUID++);
  299. }
  300. }
  301. return 0;
  302. }
  303. void CWorld::ClearAllSpawns()
  304. {
  305. for (DWORD i = 0; i < (256 * 256); i++)
  306. {
  307. if (m_pBlocks[i])
  308. m_pBlocks[i]->ClearSpawns();
  309. }
  310. }
  311. CLandBlock* CWorld::GetLandblock(WORD wHeader)
  312. {
  313. return m_pBlocks[wHeader];
  314. }
  315. CLandBlock* CWorld::ActivateBlock(WORD wHeader)
  316. {
  317. CLandBlock **ppBlock = &m_pBlocks[wHeader];
  318. CLandBlock *pBlock;
  319. #if _DEBUG
  320. pBlock = *ppBlock;
  321. if (pBlock != NULL)
  322. {
  323. LOG(Temp, Normal, "Landblock already active!\n");
  324. return pBlock;
  325. }
  326. #endif
  327. pBlock = new CLandBlock(this, wHeader);
  328. m_vSpawns.push_back(pBlock);
  329. *ppBlock = pBlock;
  330. return pBlock;
  331. }
  332. void CWorld::CreateEntity(CPhysicsObj *pEntity)
  333. {
  334. pEntity->Precache();
  335. #if _DEBUG
  336. if (!pEntity->m_dwGUID)
  337. {
  338. LOG(Temp, Normal, "Null entid being placed in world.\n");
  339. return;
  340. }
  341. #endif
  342. WORD wHeader = BLOCK_WORD(pEntity->GetLandcell());
  343. if (pEntity->IsPlayer())
  344. {
  345. DWORD dwGUID = pEntity->m_dwGUID;
  346. m_mAllPlayers.insert(std::pair< DWORD, CBasePlayer* >(dwGUID, (CBasePlayer *)pEntity));
  347. BroadcastGlobal(ServerBroadcast("System", csprintf("%s has logged in.", pEntity->GetName()), 1), PRIVATE_MSG, dwGUID, FALSE, TRUE);
  348. }
  349. CLandBlock *pBlock = m_pBlocks[wHeader];
  350. if (pBlock)
  351. {
  352. if (pBlock->FindEntity(pEntity->m_dwGUID))
  353. {
  354. // DEBUGOUT("Not spawning duplicate entity!\n");
  355. // Already exists.
  356. delete pEntity;
  357. return;
  358. }
  359. }
  360. if (!pBlock)
  361. pBlock = ActivateBlock(wHeader);
  362. pBlock->Insert(pEntity, 0, TRUE);
  363. pEntity->MakeAware(pEntity);
  364. pEntity->Spawn();
  365. }
  366. void CWorld::InsertTeleportLocation(TeleTownList_s l)
  367. {
  368. m_vTeleTown.push_back(l);
  369. }
  370. std::string CWorld::GetTeleportList()
  371. {
  372. std::string result;
  373. for each (TeleTownList_s var in m_vTeleTown)
  374. {
  375. result.append(var.m_teleString).append(", ");
  376. }
  377. if (result.back() == 0x20) { //Get out behind of bad formatting
  378. result.pop_back();
  379. result.pop_back();
  380. }
  381. return result;
  382. }
  383. TeleTownList_s CWorld::GetTeleportLocation(std::string location)
  384. {
  385. TeleTownList_s val;
  386. std::transform(location.begin(), location.end(), location.begin(), ::tolower);
  387. for each (TeleTownList_s var in m_vTeleTown)
  388. {
  389. //Lets waste a bunch of time with this.. Hey, if its the first one on the list its O(1)
  390. std::string town = var.m_teleString;
  391. std::transform(town.begin(), town.end(), town.begin(), ::tolower);
  392. if (town.find(location) != std::string::npos) {
  393. val = var;
  394. break;
  395. }
  396. }
  397. return val;
  398. }
  399. void CWorld::InsertEntity(CPhysicsObj *pEntity, BOOL bSilent)
  400. {
  401. WORD wHeader = BLOCK_WORD(pEntity->GetLandcell());
  402. CLandBlock *pBlock = m_pBlocks[wHeader];
  403. if (!pBlock)
  404. pBlock = ActivateBlock(wHeader);
  405. if (bSilent)
  406. pBlock->Insert(pEntity, wHeader);
  407. else
  408. pBlock->Insert(pEntity);
  409. if (pEntity->IsPlayer())
  410. m_mAllPlayers.insert(std::pair< DWORD, CBasePlayer* >(pEntity->m_dwGUID, (CBasePlayer *)pEntity));
  411. }
  412. void CWorld::JuggleEntity(WORD wOld, CPhysicsObj* pEntity)
  413. {
  414. if (!pEntity->HasOwner())
  415. {
  416. WORD wHeader = BLOCK_WORD(pEntity->GetLandcell());
  417. CLandBlock *pBlock = GetLandblock(wHeader);
  418. if (!pBlock)
  419. pBlock = ActivateBlock(wHeader);
  420. pBlock->Insert(pEntity, wOld);
  421. }
  422. }
  423. PlayerMap* CWorld::GetPlayers()
  424. {
  425. return &m_mAllPlayers;
  426. }
  427. //==================================================
  428. //Global, player search by GUID.
  429. //==================================================
  430. CBasePlayer* CWorld::FindPlayer(DWORD dwGUID)
  431. {
  432. PlayerMap::iterator result = m_mAllPlayers.find(dwGUID);
  433. if (result == m_mAllPlayers.end())
  434. return NULL;
  435. return result->second;
  436. }
  437. //==================================================
  438. //Global, case insensitive player name search.
  439. //==================================================
  440. CBasePlayer* CWorld::FindPlayer(const char *szName)
  441. {
  442. if (*szName == '+')
  443. szName++;
  444. PlayerMap::iterator pit = m_mAllPlayers.begin();
  445. PlayerMap::iterator pend = m_mAllPlayers.end();
  446. while (pit != pend)
  447. {
  448. CBasePlayer *pPlayer = pit->second;
  449. if (pPlayer)
  450. {
  451. const char* szPN = pPlayer->GetName();
  452. if (*szPN == '+')
  453. szPN++;
  454. if (!stricmp(szName, szPN))
  455. return pPlayer;
  456. }
  457. pit++;
  458. }
  459. return NULL;
  460. }
  461. void CWorld::BroadcastPVS(DWORD dwCell, void *_data, DWORD _len, WORD _group, DWORD ignore_ent, BOOL _game_event)
  462. {
  463. if (!dwCell)
  464. return;
  465. // Don't ask.
  466. WORD block = BLOCK_WORD(dwCell);
  467. WORD cell = CELL_WORD(dwCell);
  468. DWORD basex = BASE_OFFSET(BLOCK_X(block), CELL_X(cell));
  469. DWORD basey = BASE_OFFSET(BLOCK_Y(block), CELL_Y(cell));
  470. DWORD minx = basex;
  471. DWORD maxx = basex;
  472. DWORD miny = basey;
  473. DWORD maxy = basey;
  474. //if ( cell < 0xFF ) //indoor structure
  475. {
  476. if (minx >= (dwMinimumCellX + PVC_RANGE)) minx -= PVC_RANGE; else minx = dwMinimumCellX;
  477. if (maxx <= (dwMaximumCellX - PVC_RANGE)) maxx += PVC_RANGE; else maxx = dwMaximumCellX;
  478. if (miny >= (dwMinimumCellY + PVC_RANGE)) miny -= PVC_RANGE; else miny = dwMinimumCellY;
  479. if (maxy <= (dwMaximumCellY - PVC_RANGE)) maxy += PVC_RANGE; else maxy = dwMaximumCellY;
  480. }
  481. minx = BLOCK_OFFSET(minx) << 8;
  482. miny = BLOCK_OFFSET(miny);
  483. maxx = BLOCK_OFFSET(maxx) << 8;
  484. maxy = BLOCK_OFFSET(maxy);
  485. for (DWORD xit = minx; xit <= maxx; xit += 0x100) {
  486. for (DWORD yit = miny; yit <= maxy; yit += 1)
  487. {
  488. CLandBlock* pBlock = m_pBlocks[xit | yit];
  489. if (pBlock)
  490. pBlock->Broadcast(_data, _len, _group, ignore_ent, _game_event);
  491. }
  492. }
  493. }
  494. void CWorld::BroadcastGlobal(BinaryWriter *food, WORD _group, DWORD ignore_ent, BOOL _game_event, BOOL del)
  495. {
  496. BroadcastGlobal(food->GetData(), food->GetSize(), _group, ignore_ent, _game_event);
  497. if (del)
  498. delete food;
  499. }
  500. void CWorld::BroadcastGlobal(void *_data, DWORD _len, WORD _group, DWORD ignore_ent, BOOL _game_event)
  501. {
  502. //This is a.. very.. very bad.
  503. #if TRUE
  504. //We could send with the player map.
  505. PlayerMap::iterator pit = m_mAllPlayers.begin();
  506. PlayerMap::iterator pend = m_mAllPlayers.end();
  507. while (pit != pend)
  508. {
  509. CBasePlayer *pPlayer;
  510. if (pPlayer = pit->second)
  511. {
  512. if (!ignore_ent || (pPlayer->m_dwGUID != ignore_ent))
  513. pPlayer->SendNetMessage(_data, _len, _group, _game_event);
  514. }
  515. pit++;
  516. }
  517. #else
  518. //We could also send by landblocks instead.
  519. LandblockVector::iterator lit = m_vBlocks.begin();
  520. LandblockVector::iterator lend = m_vBlocks.end();
  521. while (lit != lend)
  522. {
  523. CLandBlock *pBlock;
  524. if (pBlock = (*lit))
  525. pBlock->Broadcast(_data, _len, _group, ignore_ent, _game_event);
  526. lit++;
  527. }
  528. #endif
  529. }
  530. void CWorld::Test()
  531. {
  532. LOG(Temp, Normal, "<CWorld::Test()>\n");
  533. LOG(Temp, Normal, "Portal: v%lu, %lu files.\n", g_pPortal->GetVersion(), g_pPortal->GetFileCount());
  534. LOG(Temp, Normal, "Cell: v%lu, %u files.\n", g_pCell->GetVersion(), g_pCell->GetFileCount());
  535. LOG(Temp, Normal, "%u players:\n", m_mAllPlayers.size());
  536. for (PlayerMap::iterator pit = m_mAllPlayers.begin(); pit != m_mAllPlayers.end(); pit++)
  537. {
  538. CBasePlayer* pPlayer = pit->second;
  539. LOG(Temp, Normal, "%08X %s\n", pPlayer->m_dwGUID, pPlayer->GetName());
  540. }
  541. LOG(Temp, Normal, "%u active blocks:\n", m_vBlocks.size());
  542. for (LandblockVector::iterator it = m_vBlocks.begin(); it != m_vBlocks.end(); it++)
  543. {
  544. CLandBlock* pBlock = *it;
  545. LOG(Temp, Normal, "%08X %lu players %lu live %lu dormant\n", pBlock->GetHeader() << 16, pBlock->PlayerCount(), pBlock->LiveCount(), pBlock->DormantCount());
  546. }
  547. LOG(Temp, Normal, "</CWorld::Test()>\n");
  548. }
  549. void CWorld::RemoveEntity(CPhysicsObj *pEntity)
  550. {
  551. if (!pEntity)
  552. {
  553. return;
  554. }
  555. if (m_pGameMode)
  556. {
  557. m_pGameMode->OnRemoveEntity(pEntity);
  558. }
  559. DWORD dwGUID = pEntity->m_dwGUID;
  560. if (pEntity->IsPlayer())
  561. {
  562. m_mAllPlayers.erase(dwGUID);
  563. BroadcastGlobal(ServerBroadcast("System", csprintf("%s has logged out.", pEntity->GetName()), 1), PRIVATE_MSG, dwGUID, FALSE, TRUE);
  564. }
  565. CLandBlock* pBlock = pEntity->GetBlock();
  566. if (!pBlock)
  567. {
  568. pEntity->Save();
  569. DELETE_ENTITY(pEntity);
  570. }
  571. else
  572. {
  573. pBlock->Destroy(pEntity);
  574. }
  575. }
  576. void CWorld::Think()
  577. {
  578. LandblockVector::iterator lit = m_vBlocks.begin();
  579. LandblockVector::iterator lend = m_vBlocks.end();
  580. while (lit != lend)
  581. {
  582. CLandBlock *pBlock = *lit;
  583. if (pBlock->Think())
  584. lit++;
  585. else
  586. {
  587. //dead
  588. lit = m_vBlocks.erase(lit);
  589. lend = m_vBlocks.end();
  590. m_pBlocks[pBlock->GetHeader()] = NULL;
  591. delete pBlock;
  592. }
  593. }
  594. if (!m_vSpawns.empty())
  595. {
  596. std::copy(m_vSpawns.begin(), m_vSpawns.end(), std::back_inserter(m_vBlocks));
  597. m_vSpawns.clear();
  598. }
  599. if ((m_fLastSave + 300.0f) <= g_pGlobals->Time())
  600. {
  601. SaveWorld();
  602. m_fLastSave = g_pGlobals->Time();
  603. }
  604. if (m_pGameMode)
  605. {
  606. m_pGameMode->Think();
  607. }
  608. }
  609. CGameMode *CWorld::GetGameMode()
  610. {
  611. return m_pGameMode;
  612. }
  613. void CWorld::SetNewGameMode(CGameMode *pGameMode)
  614. {
  615. if (pGameMode)
  616. {
  617. g_pWorld->BroadcastGlobal(ServerText(csprintf("Setting game mode to %s", pGameMode->GetName())), PRIVATE_MSG);
  618. }
  619. else
  620. {
  621. if (!pGameMode && m_pGameMode)
  622. {
  623. g_pWorld->BroadcastGlobal(ServerText(csprintf("Turning off game mode %s", m_pGameMode->GetName())), PRIVATE_MSG);
  624. }
  625. }
  626. if (m_pGameMode)
  627. {
  628. delete m_pGameMode;
  629. }
  630. m_pGameMode = pGameMode;
  631. }
  632. void CWorld::EnumNearby(CPhysicsObj *pSource, float fRange, std::list<CPhysicsObj *> *pResults)
  633. {
  634. // Enumerate nearby world objects
  635. if (pSource != NULL && !pSource->HasOwner())
  636. {
  637. DWORD dwCell = pSource->GetLandcell();
  638. WORD block = BLOCK_WORD(dwCell);
  639. WORD cell = CELL_WORD(dwCell);
  640. DWORD basex = BASE_OFFSET(BLOCK_X(block), CELL_X(cell));
  641. DWORD basey = BASE_OFFSET(BLOCK_Y(block), CELL_Y(cell));
  642. DWORD minx = basex;
  643. DWORD maxx = basex;
  644. DWORD miny = basey;
  645. DWORD maxy = basey;
  646. //if ( cell < 0xFF ) // indoor structure
  647. {
  648. if (minx >= (dwMinimumCellX + PVC_RANGE)) minx -= PVC_RANGE; else minx = dwMinimumCellX;
  649. if (maxx <= (dwMaximumCellX - PVC_RANGE)) maxx += PVC_RANGE; else maxx = dwMaximumCellX;
  650. if (miny >= (dwMinimumCellY + PVC_RANGE)) miny -= PVC_RANGE; else miny = dwMinimumCellY;
  651. if (maxy <= (dwMaximumCellY - PVC_RANGE)) maxy += PVC_RANGE; else maxy = dwMaximumCellY;
  652. }
  653. minx = BLOCK_OFFSET(minx) << 8;
  654. miny = BLOCK_OFFSET(miny);
  655. maxx = BLOCK_OFFSET(maxx) << 8;
  656. maxy = BLOCK_OFFSET(maxy);
  657. for (DWORD xit = minx; xit <= maxx; xit += 0x100) {
  658. for (DWORD yit = miny; yit <= maxy; yit += 1)
  659. {
  660. CLandBlock* pBlock = m_pBlocks[xit | yit];
  661. if (pBlock)
  662. {
  663. pBlock->EnumNearby(pSource, fRange, pResults);
  664. }
  665. }
  666. }
  667. }
  668. }
  669. CPhysicsObj* CWorld::FindWithinPVS(CPhysicsObj *pSource, DWORD dwGUID)
  670. {
  671. if (!pSource)
  672. return NULL;
  673. //Find nearby world objects.
  674. if (!pSource->HasOwner())
  675. {
  676. DWORD dwCell = pSource->GetLandcell();
  677. WORD block = BLOCK_WORD(dwCell);
  678. WORD cell = CELL_WORD(dwCell);
  679. DWORD basex = BASE_OFFSET(BLOCK_X(block), CELL_X(cell));
  680. DWORD basey = BASE_OFFSET(BLOCK_Y(block), CELL_Y(cell));
  681. DWORD minx = basex;
  682. DWORD maxx = basex;
  683. DWORD miny = basey;
  684. DWORD maxy = basey;
  685. //if ( cell < 0xFF ) //indoor structure
  686. {
  687. if (minx >= (dwMinimumCellX + PVC_RANGE)) minx -= PVC_RANGE; else minx = dwMinimumCellX;
  688. if (maxx <= (dwMaximumCellX - PVC_RANGE)) maxx += PVC_RANGE; else maxx = dwMaximumCellX;
  689. if (miny >= (dwMinimumCellY + PVC_RANGE)) miny -= PVC_RANGE; else miny = dwMinimumCellY;
  690. if (maxy <= (dwMaximumCellY - PVC_RANGE)) maxy += PVC_RANGE; else maxy = dwMaximumCellY;
  691. }
  692. minx = BLOCK_OFFSET(minx) << 8;
  693. miny = BLOCK_OFFSET(miny);
  694. maxx = BLOCK_OFFSET(maxx) << 8;
  695. maxy = BLOCK_OFFSET(maxy);
  696. for (DWORD xit = minx; xit <= maxx; xit += 0x100) {
  697. for (DWORD yit = miny; yit <= maxy; yit += 1)
  698. {
  699. CLandBlock* pBlock = m_pBlocks[xit | yit];
  700. if (pBlock)
  701. {
  702. CPhysicsObj *pEntity = pBlock->FindEntity(dwGUID);
  703. if (pEntity)
  704. return pEntity;
  705. }
  706. }
  707. }
  708. }
  709. if (pSource->Container_HasContents())
  710. return (CPhysicsObj *)pSource->Container_FindItem(dwGUID);
  711. return NULL;
  712. }
  713. void CWorld::EnumerateDungeonsFromCellData()
  714. {
  715. // TODO Need to fix dungeon parsing for ToD first. Broke because of that.
  716. return;
  717. // This creates a list of dungeons
  718. FILEMAP* pFiles = g_pCell->GetFiles();
  719. FILEMAP::iterator i = pFiles->begin();
  720. FILEMAP::iterator iend = pFiles->end();
  721. while (i != iend)
  722. {
  723. DWORD dwID = i->first;
  724. if (((dwID & 0xFFFF) >= 0xFFFE) || ((dwID & 0xFFFF) < 0x100))
  725. {
  726. i++;
  727. continue;
  728. }
  729. TURBINEFILE* pGeometryFile = g_pCell->GetFile(dwID);
  730. BYTE *_data, *data;
  731. _data = data = pGeometryFile->GetData();
  732. DWORD dwGeoFlags = *((DWORD *)data);
  733. if (!(dwGeoFlags & 9))
  734. {
  735. data += sizeof(DWORD);
  736. DWORD dungeonID = *((DWORD*)data);
  737. data += sizeof(DWORD);
  738. UCHAR textureCount = *((UCHAR *)data);
  739. data += sizeof(UCHAR);
  740. UCHAR connectionCount = *((UCHAR *)data);
  741. data += sizeof(UCHAR);
  742. USHORT visibleBlocksCount = *((USHORT *)data);
  743. data += sizeof(USHORT);
  744. data += textureCount * sizeof(USHORT);
  745. if (textureCount & 1)
  746. data += sizeof(USHORT);
  747. WORD blockID = *((WORD *)data);
  748. data += sizeof(WORD);
  749. WORD partNum = *((WORD *)data);
  750. data += sizeof(WORD);
  751. loc_t di;
  752. di.landcell = dungeonID;
  753. float _xTrans = *((float *)data);
  754. data += sizeof(float);
  755. float _yTrans = *((float *)data);
  756. data += sizeof(float);
  757. float _zTrans = *((float *)data);
  758. data += sizeof(float);
  759. float rw = *((float *)data);
  760. data += sizeof(float);
  761. float rx = *((float *)data);
  762. data += sizeof(float);
  763. float ry = *((float *)data);
  764. data += sizeof(float);
  765. float rz = *((float *)data);
  766. data += sizeof(float);
  767. #if 0 //This code look for the dropsite models in dungeons.
  768. if (dwGeoFlags & 2)
  769. {
  770. data += sizeof(DWORD) * connectionCount * 2;
  771. data += sizeof(USHORT) * visibleBlocksCount;
  772. if (visibleBlocksCount & 1)
  773. data += sizeof(USHORT);
  774. DWORD numObjects = *((DWORD *)data);
  775. data += sizeof(DWORD);
  776. for (unsigned int h = 0; h < numObjects; h++)
  777. {
  778. DWORD dwModelID = *((DWORD *)data);
  779. data += sizeof(DWORD);
  780. if ((dwModelID >= 0x02000C39 && dwModelID <= 0x02000C48) || (dwModelID == 0x02000F4A)) {
  781. //OutputConsole( "%08X has drop site[%08X] @ %f %f %f\r\n", dwID, dwModelID,
  782. // ((float*)data)[0], ((float*)data)[1], ((float*)data)[2]);
  783. break;
  784. }
  785. data += sizeof(float) * 7;
  786. }
  787. }
  788. #endif
  789. //if ( BLOCK_WORD( dwID ) == 0xF924 )
  790. // OutputConsole( "%08X %04X %04X\r\n", dwGeoFlags, blockID, partNum );
  791. DUNGEON* pDungeon = g_pPortal->GetDungeon(0x0D000000 + blockID);
  792. if (pDungeon)
  793. {
  794. loc_t Point = pDungeon->FindLandingZone(partNum);
  795. if (Point.landcell)
  796. {
  797. Vector pt(Point.x, Point.y, Point.z);
  798. matrix mm;
  799. mm.defineByQuaternion(rw, rx, ry, rz);
  800. mm.applyToVector(pt);
  801. //Point.x = pt.x;
  802. //Point.y = pt.y;
  803. //Point.z = pt.z;
  804. di.x = (float)(_xTrans + pt.x);
  805. di.y = (float)(_yTrans + pt.y);
  806. di.z = (float)(_zTrans + pt.z + 0.025f);
  807. //if ( BLOCK_WORD( dwID ) == 0xF924 )
  808. // LOG(Temp, Normal, "%f %f %f\n", di.x, di.y, di.z );
  809. m_mDungeons[dungeonID] = di;
  810. //506e0108
  811. }
  812. else
  813. {
  814. delete pGeometryFile;
  815. i++;
  816. continue;
  817. }
  818. //50520100 is broke
  819. //54500000 is broke
  820. }
  821. else
  822. {
  823. LOG(Temp, Normal, "Dungeon block mismatch. Are portal/cell versions inconsistant?\n");
  824. }
  825. }
  826. delete pGeometryFile;
  827. if (dwID < (DWORD)(0 - 0x10100))
  828. i = pFiles->upper_bound(((dwID & 0xFFFF0000) + 0x10100) - 1);
  829. else
  830. break;
  831. }
  832. }