PageRenderTime 1269ms CodeModel.GetById 22ms RepoModel.GetById 5ms app.codeStats 0ms

/src/game/object/C4GameObjects.cpp

https://bitbucket.org/randrian/openclonk2
C++ | 1008 lines | 737 code | 80 blank | 191 comment | 249 complexity | 560831bb82e903f953ea439bc942c9cb MD5 | raw file
Possible License(s): WTFPL, 0BSD, LGPL-2.1, CC-BY-3.0
  1. /*
  2. * OpenClonk, http://www.openclonk.org
  3. *
  4. * Copyright (c) 2001-2002, 2004-2008 Sven Eberhardt
  5. * Copyright (c) 2004 Matthes Bender
  6. * Copyright (c) 2004, 2006-2008 Peter Wortmann
  7. * Copyright (c) 2005-2008 G?nther Brammer
  8. * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
  9. *
  10. * Portions might be copyrighted by other authors who have contributed
  11. * to OpenClonk.
  12. *
  13. * Permission to use, copy, modify, and/or distribute this software for any
  14. * purpose with or without fee is hereby granted, provided that the above
  15. * copyright notice and this permission notice appear in all copies.
  16. * See isc_license.txt for full license and disclaimer.
  17. *
  18. * "Clonk" is a registered trademark of Matthes Bender.
  19. * See clonk_trademark_license.txt for full license.
  20. */
  21. // game object lists
  22. #include <C4Include.h>
  23. #include <C4GameObjects.h>
  24. #ifndef BIG_C4INCLUDE
  25. #include <C4Object.h>
  26. #include <C4ObjectCom.h>
  27. #include <C4Random.h>
  28. #include <C4SolidMask.h>
  29. #include <C4Network2Stats.h>
  30. #include <C4Game.h>
  31. #include <C4Log.h>
  32. #include <C4PlayerList.h>
  33. #include <C4Record.h>
  34. #endif
  35. C4GameObjects::C4GameObjects()
  36. {
  37. Default();
  38. }
  39. C4GameObjects::~C4GameObjects()
  40. {
  41. Sectors.Clear();
  42. }
  43. void C4GameObjects::Default()
  44. {
  45. ResortProc=NULL;
  46. Sectors.Clear();
  47. LastUsedMarker = 0;
  48. BackObjects.Default();
  49. ForeObjects.Default();
  50. }
  51. void C4GameObjects::Init(int32_t iWidth, int32_t iHeight)
  52. {
  53. // init sectors
  54. Sectors.Init(iWidth, iHeight);
  55. }
  56. bool C4GameObjects::Add(C4Object *nObj)
  57. {
  58. // add inactive objects to the inactive list only
  59. if (nObj->Status == C4OS_INACTIVE)
  60. return InactiveObjects.Add(nObj, C4ObjectList::stMain);
  61. // if this is a background object, add it to the list
  62. if (nObj->Category & C4D_Background)
  63. ::Objects.BackObjects.Add(nObj, C4ObjectList::stMain);
  64. // if this is a foreground object, add it to the list
  65. if (nObj->Category & C4D_Foreground)
  66. ::Objects.ForeObjects.Add(nObj, C4ObjectList::stMain);
  67. // manipulate main list
  68. if(!C4ObjectList::Add(nObj, C4ObjectList::stMain))
  69. return false;
  70. // add to sectors
  71. Sectors.Add(nObj, this);
  72. return true;
  73. }
  74. bool C4GameObjects::Remove(C4Object *pObj)
  75. {
  76. // if it's an inactive object, simply remove from the inactiv elist
  77. if (pObj->Status == C4OS_INACTIVE) return InactiveObjects.Remove(pObj);
  78. // remove from sectors
  79. Sectors.Remove(pObj);
  80. // remove from backlist
  81. ::Objects.BackObjects.Remove(pObj);
  82. // remove from forelist
  83. ::Objects.ForeObjects.Remove(pObj);
  84. // manipulate main list
  85. return C4ObjectList::Remove(pObj);
  86. }
  87. C4ObjectList &C4GameObjects::ObjectsAt(int ix, int iy)
  88. {
  89. return Sectors.SectorAt(ix, iy)->ObjectShapes;
  90. }
  91. void C4GameObjects::CrossCheck() // Every Tick1 by ExecObjects
  92. {
  93. C4Object *obj1,*obj2;
  94. DWORD ocf1,ocf2,focf,tocf;
  95. // AtObject-Check: Checks for first match of obj1 at obj2
  96. // Checks for this frame
  97. focf=tocf=OCF_None;
  98. // Medium level: Fight
  99. if (!::Game.iTick5)
  100. { focf|=OCF_FightReady; tocf|=OCF_FightReady; }
  101. // Very low level: Incineration
  102. if (!::Game.iTick35)
  103. { focf|=OCF_OnFire; tocf|=OCF_Inflammable; }
  104. if (focf && tocf)
  105. for (C4ObjectList::iterator iter=begin(); iter != end() && (obj1=*iter); ++iter)
  106. if (obj1->Status && !obj1->Contained)
  107. if (obj1->OCF & focf)
  108. {
  109. ocf1=obj1->OCF; ocf2=tocf;
  110. if (obj2=AtObject(obj1->GetX(),obj1->GetY(),ocf2,obj1))
  111. {
  112. // Incineration
  113. if ((ocf1 & OCF_OnFire) && (ocf2 & OCF_Inflammable))
  114. if (!Random(obj2->Def->ContactIncinerate))
  115. { obj2->Incinerate(obj1->GetFireCausePlr(), false, obj1); continue; }
  116. // Fight
  117. if ((ocf1 & OCF_FightReady) && (ocf2 & OCF_FightReady))
  118. if (::Players.Hostile(obj1->Owner,obj2->Owner))
  119. {
  120. // RejectFight callback
  121. C4AulParSet parset1(C4VObj(obj2) );
  122. C4AulParSet parset2(C4VObj(obj1) );
  123. if(obj1->Call(PSF_RejectFight, &parset1).getBool() ) continue;
  124. if(obj2->Call(PSF_RejectFight, &parset2).getBool() ) continue;
  125. ObjectActionFight(obj1,obj2);
  126. ObjectActionFight(obj2,obj1);
  127. continue;
  128. }
  129. }
  130. }
  131. // Reverse area check: Checks for all obj2 at obj1
  132. focf=tocf=OCF_None;
  133. // High level: Collection, Hit
  134. if (!::Game.iTick3)
  135. { focf|=OCF_Collection; tocf|=OCF_Carryable; }
  136. focf|=OCF_Alive; tocf|=OCF_HitSpeed2;
  137. if (focf && tocf)
  138. for (C4ObjectList::iterator iter=begin(); iter != end() && (obj1=*iter); ++iter)
  139. if (obj1->Status && !obj1->Contained && (obj1->OCF & focf))
  140. {
  141. unsigned int Marker = ++LastUsedMarker;
  142. C4LSector *pSct;
  143. for (C4ObjectList *pLst=obj1->Area.FirstObjects(&pSct); pLst; pLst=obj1->Area.NextObjects(pLst, &pSct))
  144. for (C4ObjectList::iterator iter2=pLst->begin(); iter2 != pLst->end() && (obj2=*iter2); ++iter2)
  145. if (obj2->Status && !obj2->Contained && (obj2!=obj1) && (obj2->OCF & tocf))
  146. if (Inside<int32_t>(obj2->GetX()-(obj1->GetX()+obj1->Shape.x),0,obj1->Shape.Wdt-1))
  147. if (Inside<int32_t>(obj2->GetY()-(obj1->GetY()+obj1->Shape.y),0,obj1->Shape.Hgt-1))
  148. if (obj1->pLayer == obj2->pLayer)
  149. {
  150. // handle collision only once
  151. if (obj2->Marker == Marker) continue;
  152. obj2->Marker = Marker;
  153. // Hit
  154. if ((obj2->OCF & OCF_HitSpeed2) && (obj1->OCF & OCF_Alive) && (obj2->Category & C4D_Object))
  155. if(!obj1->Call(PSF_QueryCatchBlow, &C4AulParSet(C4VObj(obj2))))
  156. {
  157. if(true /* "realistic" hit energy */)
  158. {
  159. FIXED dXDir = obj2->xdir - obj1->xdir, dYDir = obj2->ydir - obj1->ydir;
  160. int32_t iHitEnergy = fixtoi((dXDir * dXDir + dYDir * dYDir) * obj2->Mass / 5 );
  161. iHitEnergy = Max<int32_t>(iHitEnergy/3, !!iHitEnergy); // hit energy reduced to 1/3rd, but do not drop to zero because of this division
  162. obj1->DoEnergy(-iHitEnergy/5, false, C4FxCall_EngObjHit, obj2->Controller);
  163. int tmass=Max<int32_t>(obj1->Mass,50);
  164. if (!::Game.iTick3 || (obj1->Action.pActionDef && obj1->Action.pActionDef->GetPropertyInt(P_Procedure) != DFA_FLIGHT))
  165. obj1->Fling(obj2->xdir*50/tmass,-Abs(obj2->ydir/2)*50/tmass, false);
  166. obj1->Call(PSF_CatchBlow,&C4AulParSet(C4VInt(-iHitEnergy/5),
  167. C4VObj(obj2)));
  168. }
  169. else
  170. {
  171. obj1->DoEnergy(-obj2->Mass/5, false, C4FxCall_EngObjHit, obj2->Controller);
  172. int tmass=Max<int32_t>(obj1->Mass,50);
  173. obj1->Fling(obj2->xdir*50/tmass,
  174. -Abs(obj2->ydir/2)*50/tmass, false);
  175. obj1->Call(PSF_CatchBlow,&C4AulParSet(C4VInt(-obj2->Mass/5),
  176. C4VObj(obj2)));
  177. }
  178. // obj1 might have been tampered with
  179. if (!obj1->Status || obj1->Contained || !(obj1->OCF & focf))
  180. goto out1;
  181. continue;
  182. }
  183. // Collection
  184. if ((obj1->OCF & OCF_Collection) && (obj2->OCF & OCF_Carryable))
  185. if (Inside<int32_t>(obj2->GetX()-(obj1->GetX()+obj1->Def->Collection.x),0,obj1->Def->Collection.Wdt-1))
  186. if (Inside<int32_t>(obj2->GetY()-(obj1->GetY()+obj1->Def->Collection.y),0,obj1->Def->Collection.Hgt-1))
  187. {
  188. //if(!pLst->First) BREAKPOINT_HERE;
  189. obj1->Collect(obj2);
  190. //if(!pLst->First) BREAKPOINT_HERE;
  191. // obj1 might have been tampered with
  192. if (!obj1->Status || obj1->Contained || !(obj1->OCF & focf))
  193. goto out1;
  194. }
  195. }
  196. out1: ;
  197. }
  198. // Contained-Check: Checks for matching Contained
  199. // Checks for this frame
  200. focf=tocf=OCF_None;
  201. // Low level: Fight
  202. if (!::Game.iTick10)
  203. { focf|=OCF_FightReady; tocf|=OCF_FightReady; }
  204. if (focf && tocf)
  205. for (C4ObjectList::iterator iter = begin(); iter != end() && (obj1=*iter); ++iter)
  206. if (obj1->Status && obj1->Contained && (obj1->OCF & focf))
  207. {
  208. for (C4ObjectList::iterator iter2 = obj1->Contained->Contents.begin(); iter2 != end() && (obj2=*iter2); ++iter2)
  209. if (obj2->Status && obj2->Contained && (obj2!=obj1) && (obj2->OCF & tocf))
  210. if (obj1->pLayer == obj2->pLayer)
  211. {
  212. ocf1=obj1->OCF; ocf2=obj2->OCF;
  213. // Fight
  214. if ((ocf1 & OCF_FightReady) && (ocf2 & OCF_FightReady))
  215. if (::Players.Hostile(obj1->Owner,obj2->Owner))
  216. {
  217. ObjectActionFight(obj1,obj2);
  218. ObjectActionFight(obj2,obj1);
  219. // obj1 might have been tampered with
  220. if (!obj1->Status || obj1->Contained || !(obj1->OCF & focf))
  221. goto out2;
  222. continue;
  223. }
  224. }
  225. out2: ;
  226. }
  227. }
  228. C4Object* C4GameObjects::AtObject(int ctx, int cty, DWORD &ocf, C4Object *exclude)
  229. {
  230. DWORD cocf;
  231. C4Object *cObj; C4ObjectLink *clnk;
  232. for (clnk=ObjectsAt(ctx,cty).First; clnk && (cObj=clnk->Obj); clnk=clnk->Next)
  233. if (!exclude || (cObj!=exclude && exclude->pLayer == cObj->pLayer)) if (cObj->Status)
  234. {
  235. cocf=ocf | OCF_Exclusive;
  236. if (cObj->At(ctx,cty,cocf))
  237. {
  238. // Search match
  239. if (cocf & ocf) { ocf=cocf; return cObj; }
  240. // EXCLUSIVE block
  241. else return NULL;
  242. }
  243. }
  244. return NULL;
  245. }
  246. void C4GameObjects::Synchronize()
  247. {
  248. // synchronize unsorted objects
  249. ResortUnsorted();
  250. ExecuteResorts();
  251. // synchronize solidmasks
  252. RemoveSolidMasks();
  253. PutSolidMasks();
  254. }
  255. C4Object *C4GameObjects::FindInternal(C4ID id)
  256. {
  257. // search list of system objects (searches global list)
  258. return ObjectsInt().Find(id);
  259. }
  260. C4Object *C4GameObjects::ObjectPointer(int32_t iNumber)
  261. {
  262. // search own list
  263. C4PropList *pObj = PropLists.Get(iNumber);
  264. if (pObj) return pObj->GetObject();
  265. return 0;
  266. }
  267. int32_t C4GameObjects::ObjectNumber(C4PropList *pObj)
  268. {
  269. if(!pObj) return 0;
  270. C4PropList * const * p = PropLists.First();
  271. while (p)
  272. {
  273. if(*p == pObj) return (*p)->Number;
  274. p = PropLists.Next(p);
  275. }
  276. return 0;
  277. }
  278. C4Object *C4GameObjects::SafeObjectPointer(int32_t iNumber)
  279. {
  280. C4Object *pObj = ObjectPointer(iNumber);
  281. if (pObj) if (!pObj->Status) return NULL;
  282. return pObj;
  283. }
  284. const uint32_t C4EnumPointer1 = 1000000000;
  285. C4Object* C4GameObjects::Enumerated(C4Object *pObj)
  286. {
  287. uint32_t iPtrNum;
  288. // If object is enumerated, convert to enumerated pointer
  289. if (iPtrNum = ObjectNumber(pObj))
  290. return (C4Object*) (C4EnumPointer1 + iPtrNum);
  291. // Oops!
  292. return (C4Object*)-1;
  293. }
  294. C4Object* C4GameObjects::Denumerated(C4Object *pObj)
  295. {
  296. // convert to pointer
  297. return ObjectPointer((uint32_t)(intptr_t) pObj - C4EnumPointer1);
  298. }
  299. C4ObjectList &C4GameObjects::ObjectsInt()
  300. {
  301. // some time ago, only objects in the topleft corner used to be recognized
  302. // this is an unnecessary restriction though...
  303. //return ::Landscape.Sectors.First()->Objects;
  304. return *this;
  305. }
  306. void C4GameObjects::RemoveSolidMasks()
  307. {
  308. C4ObjectLink *cLnk;
  309. for (cLnk=First; cLnk; cLnk=cLnk->Next)
  310. if (cLnk->Obj->Status)
  311. if (cLnk->Obj->pSolidMaskData)
  312. cLnk->Obj->pSolidMaskData->Remove(false, false);
  313. }
  314. void C4GameObjects::PutSolidMasks()
  315. {
  316. C4ObjectLink *cLnk;
  317. for (cLnk=First; cLnk; cLnk=cLnk->Next)
  318. if (cLnk->Obj->Status)
  319. cLnk->Obj->UpdateSolidMask(false);
  320. }
  321. void C4GameObjects::DeleteObjects(bool fDeleteInactive)
  322. {
  323. C4ObjectList::DeleteObjects();
  324. BackObjects.Clear();
  325. ForeObjects.Clear();
  326. if (fDeleteInactive) InactiveObjects.DeleteObjects();
  327. }
  328. void C4GameObjects::Clear(bool fClearInactive)
  329. {
  330. DeleteObjects(fClearInactive);
  331. if(fClearInactive)
  332. InactiveObjects.Clear();
  333. ResortProc = NULL;
  334. LastUsedMarker = 0;
  335. }
  336. /* C4ObjResort */
  337. C4ObjResort::C4ObjResort()
  338. {
  339. Category=0;
  340. OrderFunc=NULL;
  341. Next=NULL;
  342. pSortObj = pObjBefore = NULL;
  343. fSortAfter = false;
  344. }
  345. C4ObjResort::~C4ObjResort()
  346. {
  347. }
  348. void C4ObjResort::Execute()
  349. {
  350. // no order func: resort given objects
  351. if (!OrderFunc)
  352. {
  353. // no objects given?
  354. if (!pSortObj || !pObjBefore) return;
  355. // object to be resorted died or changed category
  356. if (pSortObj->Status != C4OS_NORMAL || pSortObj->Unsorted) return;
  357. // exchange
  358. if (fSortAfter)
  359. ::Objects.OrderObjectAfter(pSortObj, pObjBefore);
  360. else
  361. ::Objects.OrderObjectBefore(pSortObj, pObjBefore);
  362. // done
  363. return;
  364. }
  365. else if (pSortObj)
  366. {
  367. // sort single object
  368. SortObject();
  369. return;
  370. }
  371. // get first link to start sorting
  372. C4ObjectLink *pLnk=::Objects.Last; if (!pLnk) return;
  373. // sort all categories given; one by one (sort by category is ensured by C4ObjectList::Add)
  374. for (int iCat=1; iCat<C4D_SortLimit; iCat<<=1)
  375. if (iCat & Category)
  376. {
  377. // get first link of this category
  378. while (!(pLnk->Obj->Status && (pLnk->Obj->Category & iCat) ))
  379. if (!(pLnk=pLnk->Prev))
  380. // no more objects to sort: done
  381. break;
  382. // first link found?
  383. if (pLnk)
  384. {
  385. // get last link of this category
  386. C4ObjectLink *pNextLnk=pLnk;
  387. while (!pLnk->Obj->Status || (pNextLnk->Obj->Category & iCat))
  388. if (!(pNextLnk=pNextLnk->Prev))
  389. // no more objects: end of list reached
  390. break;
  391. // get previous link, which is the last in the list of this category
  392. C4ObjectLink *pLastLnk;
  393. if (pNextLnk) pLastLnk=pNextLnk->Next; else pLastLnk=::Objects.First;
  394. // get next valid (there must be at least one: pLnk; so this loop should be safe)
  395. while (!pLastLnk->Obj->Status) pLastLnk=pLastLnk->Next;
  396. // now sort this portion of the list
  397. Sort(pLastLnk, pLnk);
  398. // start searching at end of this list next time
  399. // if the end has already been reached: stop here
  400. if (!(pLnk=pNextLnk)) return;
  401. }
  402. // continue with next category
  403. }
  404. }
  405. void C4ObjResort::SortObject()
  406. {
  407. // safety
  408. if (pSortObj->Status != C4OS_NORMAL || pSortObj->Unsorted) return;
  409. // pre-build parameters
  410. C4AulParSet Pars;
  411. Pars[1].Set(C4VObj(pSortObj));
  412. // first, check forward in list
  413. C4ObjectLink *pMoveLink=NULL;
  414. C4ObjectLink *pLnk = ::Objects.GetLink(pSortObj);
  415. C4ObjectLink *pLnkBck = pLnk;
  416. C4Object *pObj2; int iResult;
  417. if (!pLnk) return;
  418. while(pLnk = pLnk->Next)
  419. {
  420. // get object
  421. pObj2 = pLnk->Obj;
  422. if (!pObj2->Status) continue;
  423. // does the category still match?
  424. if (!(pObj2->Category & pSortObj->Category)) break;
  425. // perform the check
  426. Pars[0].Set(C4VObj(pObj2));
  427. iResult = OrderFunc->Exec(NULL, &Pars).getInt();
  428. if (iResult > 0) break;
  429. if (iResult < 0) pMoveLink=pLnk;
  430. }
  431. // check if movement has to be done
  432. if (pMoveLink)
  433. {
  434. // move link directly after pMoveLink
  435. // FIXME: Inform C4ObjectList that this is a reorder, not a remove+insert
  436. // move out of current position
  437. ::Objects.RemoveLink(pLnkBck);
  438. // put into new position
  439. ::Objects.InsertLink(pLnkBck, pMoveLink);
  440. }
  441. else
  442. {
  443. // no movement yet: check backwards in list
  444. Pars[0].Set(C4VObj(pSortObj));
  445. pLnk = pLnkBck;
  446. while (pLnk = pLnk->Prev)
  447. {
  448. // get object
  449. pObj2 = pLnk->Obj;
  450. if (!pObj2->Status) continue;
  451. // does the category still match?
  452. if (!(pObj2->Category & pSortObj->Category)) break;
  453. // perform the check
  454. Pars[1].Set(C4VObj(pObj2));
  455. iResult = OrderFunc->Exec(NULL, &Pars).getInt();
  456. if (iResult > 0) break;
  457. if (iResult < 0) pMoveLink=pLnk;
  458. }
  459. // no movement to be done? finish
  460. if (!pMoveLink) return;
  461. // move link directly before pMoveLink
  462. // move out of current position
  463. ::Objects.RemoveLink(pLnkBck);
  464. // put into new position
  465. ::Objects.InsertLinkBefore(pLnkBck, pMoveLink);
  466. }
  467. // object has been resorted: resort into area lists, too
  468. ::Objects.UpdatePosResort(pSortObj);
  469. // done
  470. }
  471. void C4ObjResort::Sort(C4ObjectLink *pFirst, C4ObjectLink *pLast)
  472. {
  473. #ifdef _DEBUG
  474. assert(::Objects.Sectors.CheckSort());
  475. #endif
  476. // do a simple insertion-like sort
  477. C4ObjectLink *pCurr; // current link to analyse
  478. C4ObjectLink *pCurr2; // second (previous) link to analyse
  479. C4ObjectLink *pNewFirst; // next link to be first
  480. C4ObjectLink *pFirstBck=pFirst; // backup of first link
  481. // pre-build parameters
  482. C4AulParSet Pars;
  483. // loop until there's nothing left to sort
  484. while (pFirst != pLast)
  485. {
  486. // start from the very end of the list
  487. pCurr=pNewFirst=pLast;
  488. // loop the checks up to the first list item to check
  489. while (pCurr != pFirst)
  490. {
  491. // get second check item
  492. pCurr2=pCurr->Prev;
  493. while (!pCurr2->Obj->Status) pCurr2=pCurr2->Prev;
  494. // perform the check
  495. Pars[0].Set(C4VObj(pCurr->Obj)); Pars[1].Set(C4VObj(pCurr2->Obj));
  496. if (OrderFunc->Exec(NULL, &Pars).getInt() < 0)
  497. {
  498. // so there's something to be reordered: swap the links
  499. // FIXME: Inform C4ObjectList about this reorder
  500. C4Object *pObj=pCurr->Obj; pCurr->Obj=pCurr2->Obj; pCurr2->Obj=pObj;
  501. // and readd to sector lists
  502. pCurr->Obj->Unsorted=pCurr2->Obj->Unsorted=true;
  503. // grow list section to scan next
  504. pNewFirst=pCurr;
  505. }
  506. // advance in list
  507. pCurr=pCurr2;
  508. }
  509. //reduce area to be checked
  510. pFirst=pNewFirst;
  511. }
  512. #ifdef _DEBUG
  513. assert(::Objects.Sectors.CheckSort());
  514. #endif
  515. // resort objects in sector lists
  516. for (pCurr=pFirstBck; pCurr!=pLast->Next; pCurr=pCurr->Next)
  517. {
  518. C4Object *pObj=pCurr->Obj;
  519. if (pObj->Status && pObj->Unsorted)
  520. {
  521. pObj->Unsorted=false;
  522. ::Objects.UpdatePosResort(pObj);
  523. }
  524. }
  525. #ifdef _DEBUG
  526. assert(::Objects.Sectors.CheckSort());
  527. #endif
  528. }
  529. #define C4CV_Section_Object "[Object]"
  530. int C4GameObjects::Load(C4Group &hGroup, bool fKeepInactive)
  531. {
  532. Clear(!fKeepInactive);
  533. // Load data component
  534. StdStrBuf Source;
  535. if (!hGroup.LoadEntryString(C4CFN_ScenarioObjects, Source))
  536. return 0;
  537. // Compile
  538. StdStrBuf Name = hGroup.GetFullName() + DirSep C4CFN_ScenarioObjects;
  539. if(!CompileFromBuf_LogWarn<StdCompilerINIRead>(
  540. mkParAdapt(*this, false),
  541. Source,
  542. Name.getData()))
  543. return 0;
  544. // Process objects
  545. C4ObjectLink *cLnk;
  546. C4Object *pObj;
  547. bool fObjectNumberCollision = false;
  548. int32_t iMaxObjectNumber = 0;
  549. for(cLnk = Last; cLnk; cLnk = cLnk->Prev)
  550. {
  551. C4Object *pObj = cLnk->Obj;
  552. // check object number collision with inactive list
  553. if (fKeepInactive)
  554. {
  555. for (C4ObjectLink *clnk = InactiveObjects.First; clnk; clnk=clnk->Next)
  556. if (clnk->Obj->Number == pObj->Number) fObjectNumberCollision = true;
  557. }
  558. // keep track of numbers
  559. iMaxObjectNumber = Max<long>(iMaxObjectNumber, pObj->Number);
  560. // add to list of backobjects
  561. if (pObj->Category & C4D_Background)
  562. ::Objects.BackObjects.Add(pObj, C4ObjectList::stMain, this);
  563. // add to list of foreobjects
  564. if (pObj->Category & C4D_Foreground)
  565. ::Objects.ForeObjects.Add(pObj, C4ObjectList::stMain, this);
  566. // Unterminate end
  567. }
  568. // Denumerate pointers
  569. // if object numbers collideded, numbers will be adjusted afterwards
  570. // so fake inactive object list empty meanwhile
  571. C4ObjectLink *pInFirst;
  572. if (fObjectNumberCollision) { pInFirst = InactiveObjects.First; InactiveObjects.First = NULL; }
  573. // denumerate pointers
  574. Denumerate();
  575. // update object enumeration index now, because calls like UpdateTransferZone might create objects
  576. Game.ObjectEnumerationIndex = Max(Game.ObjectEnumerationIndex, iMaxObjectNumber);
  577. // end faking and adjust object numbers
  578. if (fObjectNumberCollision)
  579. {
  580. InactiveObjects.First=pInFirst;
  581. // simply renumber all inactive objects
  582. for (cLnk=InactiveObjects.First; cLnk; cLnk=cLnk->Next)
  583. if ((pObj=cLnk->Obj)->Status)
  584. pObj->Number = ++Game.ObjectEnumerationIndex;
  585. }
  586. // special checks:
  587. // -contained/contents-consistency
  588. // -StaticBack-objects zero speed
  589. for (cLnk=First; cLnk; cLnk=cLnk->Next)
  590. if ((pObj=cLnk->Obj)->Status)
  591. {
  592. // staticback must not have speed
  593. if (pObj->Category & C4D_StaticBack)
  594. {
  595. pObj->xdir = pObj->ydir = 0;
  596. }
  597. // contained must be in contents list
  598. if (pObj->Contained)
  599. if (!pObj->Contained->Contents.GetLink(pObj))
  600. {
  601. DebugLogF("Error in Objects.txt: Container of #%d is #%d, but not found in contents list!", pObj->Number, pObj->Contained->Number);
  602. pObj->Contained->Contents.Add(pObj, C4ObjectList::stContents);
  603. }
  604. // all contents must have contained set; otherwise, remove them!
  605. C4Object *pObj2;
  606. for (C4ObjectLink *cLnkCont=pObj->Contents.First; cLnkCont; cLnkCont=cLnkCont->Next)
  607. {
  608. // check double links
  609. if (pObj->Contents.GetLink(cLnkCont->Obj) != cLnkCont)
  610. {
  611. DebugLogF("Error in Objects.txt: Double containment of #%d by #%d!", cLnkCont->Obj->Number, pObj->Number);
  612. // this remove-call will only remove the previous (dobuled) link, so cLnkCont should be save
  613. pObj->Contents.Remove(cLnkCont->Obj);
  614. // contents checked already
  615. continue;
  616. }
  617. // check contents/contained-relation
  618. if ((pObj2=cLnkCont->Obj)->Status)
  619. if (pObj2->Contained != pObj)
  620. {
  621. DebugLogF("Error in Objects.txt: Object #%d not in container #%d as referenced!", pObj2->Number, pObj->Number);
  622. pObj2->Contained = pObj;
  623. }
  624. }
  625. }
  626. // sort out inactive objects
  627. C4ObjectLink *cLnkNext;
  628. for (cLnk=First; cLnk; cLnk=cLnkNext)
  629. {
  630. cLnkNext = cLnk->Next;
  631. if (cLnk->Obj->Status == C4OS_INACTIVE)
  632. {
  633. if (cLnk->Prev) cLnk->Prev->Next=cLnkNext; else First=cLnkNext;
  634. if (cLnkNext) cLnkNext->Prev=cLnk->Prev; else Last=cLnk->Prev;
  635. if (cLnk->Prev = InactiveObjects.Last)
  636. InactiveObjects.Last->Next = cLnk;
  637. else
  638. InactiveObjects.First = cLnk;
  639. InactiveObjects.Last = cLnk; cLnk->Next = NULL;
  640. Mass-=pObj->Mass;
  641. }
  642. }
  643. {
  644. C4DebugRecOff DBGRECOFF; // - script callbacks that would kill DebugRec-sync for runtime start
  645. // update graphics
  646. UpdateGraphics(false);
  647. // Update faces
  648. UpdateFaces(false);
  649. // Update ocf
  650. SetOCF();
  651. }
  652. // make sure list is sorted by category - after sorting out inactives, because inactives aren't sorted into the main list
  653. FixObjectOrder();
  654. //Sectors.Dump();
  655. // misc updates
  656. for (cLnk=First; cLnk; cLnk=cLnk->Next)
  657. if ((pObj=cLnk->Obj)->Status)
  658. {
  659. // add to plrview
  660. pObj->PlrFoWActualize();
  661. // update flipdir (for old objects.txt with no flipdir defined)
  662. // assigns Action.DrawDir as well
  663. pObj->UpdateFlipDir();
  664. }
  665. // Done
  666. return ObjectCount();
  667. }
  668. bool C4GameObjects::Save(C4Group &hGroup, bool fSaveGame, bool fSaveInactive)
  669. {
  670. // Save to temp file
  671. char szFilename[_MAX_PATH+1]; SCopy( Config.AtTempPath(C4CFN_ScenarioObjects), szFilename );
  672. if (!Save(szFilename,fSaveGame,fSaveInactive)) return false;
  673. // Move temp file to group
  674. hGroup.Move(szFilename, NULL); // check?
  675. // Success
  676. return true;
  677. }
  678. bool C4GameObjects::Save(const char *szFilename, bool fSaveGame, bool fSaveInactive)
  679. {
  680. // Enumerate
  681. Enumerate();
  682. InactiveObjects.Enumerate();
  683. // Decompile objects to buffer
  684. StdStrBuf Buffer;
  685. bool fSuccess = DecompileToBuf_Log<StdCompilerINIWrite>(mkParAdapt(*this, false, !fSaveGame), &Buffer, szFilename);
  686. // Decompile inactives
  687. if(fSaveInactive)
  688. {
  689. StdStrBuf InactiveBuffer;
  690. fSuccess &= DecompileToBuf_Log<StdCompilerINIWrite>(mkParAdapt(InactiveObjects, false, !fSaveGame), &InactiveBuffer, szFilename);
  691. Buffer.Append("\r\n");
  692. Buffer.Append(InactiveBuffer);
  693. }
  694. // Denumerate
  695. InactiveObjects.Denumerate();
  696. Denumerate();
  697. // Error?
  698. if(!fSuccess)
  699. return false;
  700. // Write
  701. return Buffer.SaveToFile(szFilename);
  702. }
  703. void C4GameObjects::UpdateScriptPointers()
  704. {
  705. // call in sublists
  706. C4ObjectList::UpdateScriptPointers();
  707. InactiveObjects.UpdateScriptPointers();
  708. // adjust global effects
  709. if (Game.pGlobalEffects) Game.pGlobalEffects->ReAssignAllCallbackFunctions();
  710. }
  711. void C4GameObjects::UpdatePos(C4Object *pObj)
  712. {
  713. // Position might have changed. Update sector lists
  714. Sectors.Update(pObj, this);
  715. }
  716. void C4GameObjects::UpdatePosResort(C4Object *pObj)
  717. {
  718. // Object order for this object was changed. Readd object to sectors
  719. Sectors.Remove(pObj);
  720. Sectors.Add(pObj, this);
  721. }
  722. bool C4GameObjects::OrderObjectBefore(C4Object *pObj1, C4Object *pObj2)
  723. {
  724. // check that this won't screw the category sort
  725. if((pObj1->Category & C4D_SortLimit) < (pObj2->Category & C4D_SortLimit))
  726. return false;
  727. // reorder
  728. if(!C4ObjectList::OrderObjectBefore(pObj1, pObj2))
  729. return false;
  730. // update area lists
  731. UpdatePosResort(pObj1);
  732. // done, success
  733. return true;
  734. }
  735. bool C4GameObjects::OrderObjectAfter(C4Object *pObj1, C4Object *pObj2)
  736. {
  737. // check that this won't screw the category sort
  738. if((pObj1->Category & C4D_SortLimit) > (pObj2->Category & C4D_SortLimit))
  739. return false;
  740. // reorder
  741. if(!C4ObjectList::OrderObjectAfter(pObj1, pObj2))
  742. return false;
  743. // update area lists
  744. UpdatePosResort(pObj1);
  745. // done, success
  746. return true;
  747. }
  748. void C4GameObjects::FixObjectOrder()
  749. {
  750. // fixes the object order so it matches the global object order sorting constraints
  751. C4ObjectLink *pLnk0=First, *pLnkL=Last;
  752. while (pLnk0 != pLnkL)
  753. {
  754. C4ObjectLink *pLnk1stUnsorted=NULL, *pLnkLastUnsorted=NULL, *pLnkPrev=NULL, *pLnk;
  755. C4Object *pLastWarnObj = NULL;
  756. // forward fix
  757. uint32_t dwLastCategory = C4D_SortLimit;
  758. for (pLnk = pLnk0; pLnk!=pLnkL->Next; pLnk=pLnk->Next)
  759. {
  760. C4Object *pObj = pLnk->Obj;
  761. if (pObj->Unsorted || !pObj->Status) continue;
  762. DWORD dwCategory = pObj->Category & C4D_SortLimit;
  763. // must have exactly one SortOrder-bit set
  764. if (!dwCategory)
  765. {
  766. DebugLogF("Objects.txt: Object #%d is missing sorting category!", (int) pObj->Number);
  767. ++pObj->Category; dwCategory = 1;
  768. }
  769. else
  770. {
  771. DWORD dwCat2=dwCategory; int i=0;
  772. while (~dwCat2&1) { dwCat2 = dwCat2>>1; ++i; }
  773. if (dwCat2 != 1)
  774. {
  775. DebugLogF("Objects.txt: Object #%d has invalid sorting category %x!", (int) pObj->Number, (unsigned int) dwCategory);
  776. dwCategory = (1<<i);
  777. pObj->Category = (pObj->Category & ~C4D_SortLimit) | dwCategory;
  778. }
  779. }
  780. // fix order
  781. if (dwCategory > dwLastCategory)
  782. {
  783. // SORT ERROR! (note that pLnkPrev can't be 0)
  784. if (pLnkPrev->Obj != pLastWarnObj)
  785. {
  786. DebugLogF("Objects.txt: Wrong object order of #%d-#%d! (down)", (int) pObj->Number, (int) pLnkPrev->Obj->Number);
  787. pLastWarnObj = pLnkPrev->Obj;
  788. }
  789. pLnk->Obj = pLnkPrev->Obj;
  790. pLnkPrev->Obj = pObj;
  791. pLnkLastUnsorted = pLnkPrev;
  792. }
  793. else
  794. dwLastCategory = dwCategory;
  795. pLnkPrev = pLnk;
  796. }
  797. if (!pLnkLastUnsorted) break; // done
  798. pLnkL = pLnkLastUnsorted;
  799. // backwards fix
  800. dwLastCategory = 0;
  801. for (pLnk = pLnkL; pLnk!=pLnk0->Prev; pLnk=pLnk->Prev)
  802. {
  803. C4Object *pObj = pLnk->Obj;
  804. if (pObj->Unsorted || !pObj->Status) continue;
  805. DWORD dwCategory = pObj->Category & C4D_SortLimit;
  806. if (dwCategory < dwLastCategory)
  807. {
  808. // SORT ERROR! (note that pLnkPrev can't be 0)
  809. if (pLnkPrev->Obj != pLastWarnObj)
  810. {
  811. DebugLogF("Objects.txt: Wrong object order of #%d-#%d! (up)", (int) pObj->Number, (int) pLnkPrev->Obj->Number);
  812. pLastWarnObj = pLnkPrev->Obj;
  813. }
  814. pLnk->Obj = pLnkPrev->Obj;
  815. pLnkPrev->Obj = pObj;
  816. pLnk1stUnsorted = pLnkPrev;
  817. }
  818. else
  819. dwLastCategory = dwCategory;
  820. pLnkPrev = pLnk;
  821. }
  822. if (!pLnk1stUnsorted) break; // done
  823. pLnk0 = pLnk1stUnsorted;
  824. }
  825. // objects fixed!
  826. }
  827. void C4GameObjects::ResortUnsorted()
  828. {
  829. C4ObjectLink *clnk=First; C4Object *cObj;
  830. while (clnk && (cObj=clnk->Obj))
  831. {
  832. clnk=clnk->Next;
  833. if (cObj->Unsorted)
  834. {
  835. // readd to main object list
  836. Remove(cObj);
  837. cObj->Unsorted=false;
  838. if (!Add(cObj))
  839. {
  840. // readd failed: Better kill object to prevent leaking...
  841. Game.ClearPointers(cObj);
  842. delete cObj;
  843. }
  844. }
  845. }
  846. }
  847. void C4GameObjects::ExecuteResorts()
  848. {
  849. // custom object sort
  850. C4ObjResort *pRes = ResortProc;
  851. while (pRes)
  852. {
  853. C4ObjResort *pNextRes=pRes->Next;
  854. pRes->Execute();
  855. delete pRes;
  856. pRes=pNextRes;
  857. }
  858. ResortProc=NULL;
  859. }
  860. bool C4GameObjects::ValidateOwners()
  861. {
  862. // validate in sublists
  863. bool fSucc = true;
  864. if (!C4ObjectList::ValidateOwners()) fSucc = false;
  865. if (!InactiveObjects.ValidateOwners()) fSucc = false;
  866. return fSucc;
  867. }
  868. bool C4GameObjects::AssignInfo()
  869. {
  870. // assign in sublists
  871. bool fSucc = true;
  872. if (!C4ObjectList::AssignInfo()) fSucc = false;
  873. if (!InactiveObjects.AssignInfo()) fSucc = false;
  874. return fSucc;
  875. }
  876. void C4GameObjects::AssignPlrViewRange()
  877. {
  878. C4ObjectLink *cLnk;
  879. for (cLnk=Last; cLnk; cLnk=cLnk->Prev)
  880. if (cLnk->Obj->Status)
  881. cLnk->Obj->AssignPlrViewRange();
  882. }
  883. void C4GameObjects::SortByCategory()
  884. {
  885. C4ObjectLink *cLnk;
  886. bool fSorted;
  887. // Sort by category
  888. do
  889. {
  890. fSorted = true;
  891. for (cLnk=First; cLnk && cLnk->Next; cLnk=cLnk->Next)
  892. if ((cLnk->Obj->Category & C4D_SortLimit) < (cLnk->Next->Obj->Category & C4D_SortLimit))
  893. {
  894. RemoveLink(cLnk);
  895. InsertLink(cLnk,cLnk->Next);
  896. fSorted = false;
  897. break;
  898. }
  899. }
  900. while (!fSorted);
  901. }
  902. void C4GameObjects::SyncClearance()
  903. {
  904. C4ObjectLink *cLnk;
  905. for (cLnk=First; cLnk; cLnk=cLnk->Next)
  906. if (cLnk->Obj)
  907. cLnk->Obj->SyncClearance();
  908. }
  909. void C4GameObjects::UpdateTransferZones()
  910. {
  911. C4Object *cobj; C4ObjectLink *clnk;
  912. for (clnk=First; clnk && (cobj=clnk->Obj); clnk=clnk->Next)
  913. cobj->Call(PSF_UpdateTransferZone);
  914. }
  915. void C4GameObjects::ResetAudibility()
  916. {
  917. C4Object *cobj; C4ObjectLink *clnk;
  918. for (clnk=First; clnk && (cobj=clnk->Obj); clnk=clnk->Next)
  919. cobj->Audible=cobj->AudiblePan=0;
  920. }
  921. void C4GameObjects::SetOCF()
  922. {
  923. C4ObjectLink *cLnk;
  924. for (cLnk=First; cLnk; cLnk=cLnk->Next)
  925. if (cLnk->Obj->Status)
  926. cLnk->Obj->SetOCF();
  927. }
  928. C4GameObjects Objects;