bool C4Object::Exit(int32_t iX, int32_t iY, int32_t iR, C4Real iXDir, C4Real iYDir, C4Real iRDir, bool fCalls)
{
	// 1. Exit the current container.
	// 2. Update Contents of container object and set Contained to nullptr.
	// 3. Set offset position/motion if desired.
	// 4. Call Ejection for container and Departure for object.

	// Not contained
	C4Object *pContainer=Contained;
	if (!pContainer) return false;
	// Remove object from container
	pContainer->Contents.Remove(this);
	pContainer->UpdateMass();
	pContainer->SetOCF();
	// No container
	Contained=nullptr;
	// Position/motion
	fix_x=itofix(iX); fix_y=itofix(iY);
	fix_r=itofix(iR);
	BoundsCheck(fix_x, fix_y);
	xdir=iXDir; ydir=iYDir; rdir=iRDir;
	// Misc updates
	Mobile=true;
	InLiquid=false;
	CloseMenu(true);
	UpdateFace(true);
	SetOCF();
	// Object list callback (before script callbacks, because script callbacks may enter again)
	ObjectListChangeListener.OnObjectContainerChanged(this, pContainer, nullptr);
	// Engine calls
	if (fCalls) pContainer->Call(PSF_Ejection,&C4AulParSet(this));
	if (fCalls) Call(PSF_Departure,&C4AulParSet(pContainer));
	// Success (if the obj wasn't "re-entered" by script)
	return !Contained;
}
bool C4Object::Enter(C4Object *pTarget, bool fCalls, bool fCopyMotion, bool *pfRejectCollect)
{
	// 0. Query entrance and collection
	// 1. Exit if contained.
	// 2. Set new container.
	// 3. Update Contents and mass of the new container.
	// 4. Call collection for container
	// 5. Call entrance for object.

	// No valid target or target is self
	if (!pTarget || (pTarget==this)) return false;
	// check if entrance is allowed
	if (!! Call(PSF_RejectEntrance, &C4AulParSet(pTarget))) return false;
	// check if we end up in an endless container-recursion
	for (C4Object *pCnt=pTarget->Contained; pCnt; pCnt=pCnt->Contained)
		if (pCnt==this) return false;
	// Check RejectCollect, if desired
	if (pfRejectCollect)
	{
		if (!!pTarget->Call(PSF_RejectCollection,&C4AulParSet(Def, this)))
		{
			*pfRejectCollect = true;
			return false;
		}
		*pfRejectCollect = false;
	}
	// Exit if contained
	if (Contained) if (!Exit(GetX(),GetY())) return false;
	if (Contained || !Status || !pTarget->Status) return false;
	// Failsafe updates
	if (Menu)
	{
		CloseMenu(true);
		// CloseMenu might do bad stuff
		if (Contained || !Status || !pTarget->Status) return false;
	}
	SetOCF();
	// Set container
	Contained=pTarget;
	// Enter
	if (!Contained->Contents.Add(this, C4ObjectList::stContents))
	{
		Contained=nullptr;
		return false;
	}
	// Assume that the new container controls this object, if it cannot control itself (i.e.: Alive)
	// So it can be traced back who caused the damage, if a projectile hits its target
	if (!Alive)
		Controller = pTarget->Controller;
	// Misc updates
	// motion must be copied immediately, so the position will be correct when OCF is set, and
	// OCF_Available will be set for newly bought items, even if 50/50 is solid in the landscape
	// however, the motion must be preserved sometimes to keep flags like OCF_HitSpeed upon collection
	if (fCopyMotion)
	{
		// remove any solidmask before copying the motion...
		UpdateSolidMask(false);
		CopyMotion(Contained);
	}
	SetOCF();
	UpdateFace(true);
	// Update container
	Contained->UpdateMass();
	Contained->SetOCF();
	// Object list callback (before script callbacks, because script callbacks may exit again)
	ObjectListChangeListener.OnObjectContainerChanged(this, nullptr, Contained);
	// Collection call
	if (fCalls) pTarget->Call(PSF_Collection2,&C4AulParSet(this));
	if (!Contained || !Contained->Status || !pTarget->Status) return true;
	// Entrance call
	if (fCalls) Call(PSF_Entrance,&C4AulParSet(Contained));
	if (!Contained || !Contained->Status || !pTarget->Status) return true;
	// Success
	return true;
}
Exemple #3
0
int C4GameObjects::Load(C4Group &hGroup, bool fKeepInactive) {
  Clear(!fKeepInactive);

  // Load data component
  StdStrBuf Source;
  if (!hGroup.LoadEntryString(C4CFN_ScenarioObjects, Source)) return 0;

  // Compile
  StdStrBuf Name = hGroup.GetFullName() + DirSep C4CFN_ScenarioObjects;
  if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkParAdapt(*this, false),
                                                  Source, Name.getData()))
    return 0;

  // Process objects
  C4ObjectLink *cLnk = nullptr;
  C4Object *pObj = nullptr;
  bool fObjectNumberCollision = false;
  int32_t iMaxObjectNumber = 0;
  for (cLnk = Last; cLnk; cLnk = cLnk->Prev) {
    C4Object *pObj = cLnk->Obj;
    // check object number collision with inactive list
    if (fKeepInactive) {
      for (C4ObjectLink *clnk = InactiveObjects.First; clnk; clnk = clnk->Next)
        if (clnk->Obj->Number == pObj->Number) fObjectNumberCollision = true;
    }
    // keep track of numbers
    iMaxObjectNumber = Max<long>(iMaxObjectNumber, pObj->Number);
    // add to list of backobjects
    if (pObj->Category & C4D_Background)
      Game.BackObjects.Add(pObj, C4ObjectList::stMain, this);
    // add to list of foreobjects
    if (pObj->Category & C4D_Foreground)
      Game.ForeObjects.Add(pObj, C4ObjectList::stMain, this);
    // Unterminate end
  }

  // Denumerate pointers
  // if object numbers collideded, numbers will be adjusted afterwards
  // so fake inactive object list empty meanwhile
  C4ObjectLink *pInFirst = nullptr;
  if (fObjectNumberCollision) {
    pInFirst = InactiveObjects.First;
    InactiveObjects.First = NULL;
  }
  // denumerate pointers
  Denumerate();
  // update object enumeration index now, because calls like UpdateTransferZone
  // might create objects
  Game.ObjectEnumerationIndex =
      Max(Game.ObjectEnumerationIndex, iMaxObjectNumber);
  // end faking and adjust object numbers
  if (fObjectNumberCollision) {
    InactiveObjects.First = pInFirst;
    // simply renumber all inactive objects
    for (cLnk = InactiveObjects.First; cLnk; cLnk = cLnk->Next)
      if ((pObj = cLnk->Obj)->Status)
        pObj->Number = ++Game.ObjectEnumerationIndex;
  }

  // special checks:
  // -contained/contents-consistency
  // -StaticBack-objects zero speed
  for (cLnk = First; cLnk; cLnk = cLnk->Next)
    if ((pObj = cLnk->Obj)->Status) {
      // staticback must not have speed
      if (pObj->Category & C4D_StaticBack) {
        pObj->xdir = pObj->ydir = 0;
      }
      // contained must be in contents list
      if (pObj->Contained)
        if (!pObj->Contained->Contents.GetLink(pObj)) {
          DebugLogF(
              "Error in Objects.txt: Container of #%d is #%d, but not found in "
              "contents list!",
              pObj->Number, pObj->Contained->Number);
          pObj->Contained->Contents.Add(pObj, C4ObjectList::stContents);
        }
      // all contents must have contained set; otherwise, remove them!
      C4Object *pObj2;
      for (C4ObjectLink *cLnkCont = pObj->Contents.First; cLnkCont;
           cLnkCont = cLnkCont->Next) {
        // check double links
        if (pObj->Contents.GetLink(cLnkCont->Obj) != cLnkCont) {
          DebugLogF("Error in Objects.txt: Double containment of #%d by #%d!",
                    cLnkCont->Obj->Number, pObj->Number);
          // this remove-call will only remove the previous (dobuled) link, so
          // cLnkCont should be save
          pObj->Contents.Remove(cLnkCont->Obj);
          // contents checked already
          continue;
        }
        // check contents/contained-relation
        if ((pObj2 = cLnkCont->Obj)->Status)
          if (pObj2->Contained != pObj) {
            DebugLogF(
                "Error in Objects.txt: Object #%d not in container #%d as "
                "referenced!",
                pObj2->Number, pObj->Number);
            pObj2->Contained = pObj;
          }
      }
    }
  // sort out inactive objects
  C4ObjectLink *cLnkNext;
  for (cLnk = First; cLnk; cLnk = cLnkNext) {
    cLnkNext = cLnk->Next;
    if (cLnk->Obj->Status == C4OS_INACTIVE) {
      if (cLnk->Prev)
        cLnk->Prev->Next = cLnkNext;
      else
        First = cLnkNext;
      if (cLnkNext)
        cLnkNext->Prev = cLnk->Prev;
      else
        Last = cLnk->Prev;
      if (cLnk->Prev = InactiveObjects.Last)
        InactiveObjects.Last->Next = cLnk;
      else
        InactiveObjects.First = cLnk;
      InactiveObjects.Last = cLnk;
      cLnk->Next = NULL;
      Mass -= pObj->Mass;
    }
  }

  {
    C4DebugRecOff DBGRECOFF;  // - script callbacks that would kill
                              // DebugRec-sync for runtime start
    // update graphics
    UpdateGraphics(false);
    // Update faces
    UpdateFaces(false);
    // Update ocf
    SetOCF();
  }

  // make sure list is sorted by category - after sorting out inactives, because
  // inactives aren't sorted into the main list
  FixObjectOrder();

  // Sectors.Dump();

  // misc updates
  for (cLnk = First; cLnk; cLnk = cLnk->Next)
    if ((pObj = cLnk->Obj)->Status) {
      // add to plrview
      pObj->PlrFoWActualize();
      // update flipdir (for old objects.txt with no flipdir defined)
      // assigns Action.DrawDir as well
      pObj->UpdateFlipDir();
    }
  // Done
  return ObjectCount();
}