Esempio n. 1
0
void C4Object::MovePosition(C4Real dx, C4Real dy)
{
	// move object position; repositions SolidMask
	if (pSolidMaskData) pSolidMaskData->Remove(true);
	fix_x+=dx;
	fix_y+=dy;
	UpdatePos();
	UpdateSolidMask(true);
}
Esempio n. 2
0
void C4Object::DoMovement()
{
	int32_t iContact=0;
	bool fAnyContact=false; int iContacts = 0;
	BYTE fTurned=0,fRedirectYR=0,fNoAttach=0;
	// Restrictions
	if (Def->NoHorizontalMove) xdir=0;
	// Dig free target area
	C4PropList* pActionDef = GetAction();
	if (pActionDef)
		if (pActionDef->GetPropertyInt(P_DigFree))
		{
			int ctcox, ctcoy;
			// Shape size square
			if (pActionDef->GetPropertyInt(P_DigFree)==1)
			{
				ctcox=fixtoi(fix_x+xdir); ctcoy=fixtoi(fix_y+ydir);
				::Landscape.DigFreeRect(ctcox+Shape.GetX(),ctcoy+Shape.GetY(),Shape.Wdt,Shape.Hgt,this);
			}
			// Free size round (variable size)
			else
			{
				ctcox=fixtoi(fix_x+xdir); ctcoy=fixtoi(fix_y+ydir);
				int32_t rad = pActionDef->GetPropertyInt(P_DigFree);
				if (Con<FullCon) rad = rad*6*Con/5/FullCon;
				::Landscape.DigFree(ctcox,ctcoy-1,rad,this);
			}
		}

	// store previous movement and ocf
	C4Real oldxdir(xdir), oldydir(ydir);
	uint32_t old_ocf = OCF;

	bool fMoved = false;
	C4Real new_x = fix_x + xdir;
	C4Real new_y = fix_y + ydir;
	SideBounds(new_x);

	if (!Action.t_attach) // Unattached movement  = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
	{
		// Horizontal movement - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		// Move to target
		while (fixtoi(new_x) != fixtoi(fix_x))
		{
			// Next step
			int step = Sign(new_x - fix_x);
			uint32_t border_hack_contacts = 0;
			iContact=ContactCheck(GetX() + step, GetY(), &border_hack_contacts);
			if (iContact || border_hack_contacts)
			{
				fAnyContact=true; iContacts |= t_contact | border_hack_contacts;
			}
			if (iContact)
			{
				// Abort horizontal movement
				new_x = fix_x;
				// Vertical redirection (always)
				RedirectForce(xdir,ydir,-1);
				ApplyFriction(ydir,ContactVtxFriction(this));
			}
			else // Free horizontal movement
			{
				DoMotion(step, 0);
				fMoved = true;
			}
		}
		// Vertical movement - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		// Movement target
		new_y = fix_y + ydir;
		// Movement bounds (vertical)
		VerticalBounds(new_y);
		// Move to target
		while (fixtoi(new_y) != fixtoi(fix_y))
		{
			// Next step
			int step = Sign(new_y - fix_y);
			if ((iContact=ContactCheck(GetX(), GetY() + step, nullptr, ydir > 0)))
			{
				fAnyContact=true; iContacts |= t_contact;
				new_y = fix_y;
				// Vertical contact horizontal friction
				ApplyFriction(xdir,ContactVtxFriction(this));
				// Redirection slide or rotate
				if (!ContactVtxCNAT(this,CNAT_Left))
					RedirectForce(ydir,xdir,-1);
				else if (!ContactVtxCNAT(this,CNAT_Right))
					RedirectForce(ydir,xdir,+1);
				else
				{
					// living things are always capable of keeping their rotation
					if (OCF & OCF_Rotate) if (iContact==1) if (!Alive)
							{
								RedirectForce(ydir,rdir,-ContactVtxWeight(this));
								fRedirectYR=1;
							}
					ydir=0;
				}
			}
			else // Free vertical movement
			{
				DoMotion(0,step);
				fMoved = true;
			}
		}
	}
	if (Action.t_attach) // Attached movement = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
	{
		VerticalBounds(new_y);
		// Move to target
		do
		{
			// Set next step target
			int step_x = 0, step_y = 0;
			if (fixtoi(new_x) != GetX())
				step_x = Sign(fixtoi(new_x) - GetX());
			else if (fixtoi(new_y) != GetY())
				step_y = Sign(fixtoi(new_y) - GetY());
			int32_t ctx = GetX() + step_x;
			int32_t cty = GetY() + step_y;
			// Attachment check
			if (!Shape.Attach(ctx,cty,Action.t_attach))
				fNoAttach=1;
			else
			{
				// Attachment change to ctx/cty overrides target
				if (ctx != GetX() + step_x)
				{
					xdir = Fix0; new_x = itofix(ctx);
				}
				if (cty != GetY() + step_y)
				{
					ydir = Fix0; new_y = itofix(cty);
				}
			}
			// Contact check & evaluation
			uint32_t border_hack_contacts = 0;
			iContact=ContactCheck(ctx,cty,&border_hack_contacts);
			if (iContact || border_hack_contacts)
			{
				fAnyContact=true; iContacts |= border_hack_contacts | t_contact;
			}
			if (iContact)
			{
				// Abort movement
				if (ctx != GetX())
				{
					ctx = GetX(); new_x = fix_x;
				}
				if (cty != GetY())
				{
					cty = GetY(); new_y = fix_y;
				}
			}
			DoMotion(ctx - GetX(), cty - GetY());
			fMoved = true;
		}
		while (fixtoi(new_x) != GetX() || fixtoi(new_y) != GetY());
	}

	if(fix_x != new_x || fix_y != new_y)
	{
		fMoved = true;
		if (pSolidMaskData) pSolidMaskData->Remove(true);
		fix_x = new_x;
		fix_y = new_y;
	}
	// Rotation  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	if (OCF & OCF_Rotate && !!rdir)
	{
		C4Real target_r = fix_r + rdir * 5;
		// Rotation limit
		if (Def->Rotateable>1)
		{
			if (target_r > itofix(Def->Rotateable))
				{ target_r = itofix(Def->Rotateable); rdir=0; }
			if (target_r < itofix(-Def->Rotateable))
				{ target_r = itofix(-Def->Rotateable); rdir=0; }
		}
		int32_t ctx=GetX(); int32_t cty=GetY();
		// Move to target
		while (fixtoi(fix_r) != fixtoi(target_r))
		{
			// Save step undos
			C4Real lcobjr = fix_r; C4Shape lshape=Shape;
			// Try next step
			fix_r += Sign(target_r - fix_r);
			UpdateShape();
			// attached rotation: rotate around attachment pos
			if (Action.t_attach && !fNoAttach)
			{
				// more accurately, attachment should be evaluated by a rotation around the attachment vertex
				// however, as long as this code is only used for some surfaces adjustment for large vehicles,
				// it's enough to assume rotation around the center
				ctx=GetX(); cty=GetY();
				// evaluate attachment, but do not bother about attachment loss
				// that will then be done in next execution cycle
				Shape.Attach(ctx,cty,Action.t_attach);
			}
			// check for contact
			if ((iContact=ContactCheck(ctx,cty))) // Contact
			{
				fAnyContact=true; iContacts |= t_contact;
				// Undo step and abort movement
				Shape=lshape;
				target_r = fix_r = lcobjr;
				// last UpdateShape-call might have changed sector lists!
				UpdatePos();
				// Redirect to GetY()
				if (iContact==1) if (!fRedirectYR)
						RedirectForce(rdir,ydir,-1);
				// Stop rotation
				rdir=0;
			}
			else
			{
				fTurned=1;
				if (ctx != GetX() || cty != GetY())
				{
					fix_x = itofix(ctx); fix_y = itofix(cty);
				}
			}
		}
		// Circle bounds
		if (target_r < -FixHalfCircle) { target_r += FixFullCircle; }
		if (target_r > +FixHalfCircle) { target_r -= FixFullCircle; }
		fix_r = target_r;
	}
	// Reput solid mask if moved by motion
	if (fMoved || fTurned) UpdateSolidMask(true);
	// Misc checks ===========================================================================================
	// InLiquid check
	// this equals C4Object::UpdateLiquid, but the "fNoAttach=false;"-line
	if (IsInLiquidCheck()) // In Liquid
	{
		if (!InLiquid) // Enter liquid
		{
			if (OCF & OCF_HitSpeed2) if (Mass>3)
					Splash(GetX(),GetY()+1,std::min(Shape.Wdt*Shape.Hgt/10,20),this);
			fNoAttach=false;
			InLiquid=1;
		}
	}
	else // Out of liquid
	{
		if (InLiquid) // Leave liquid
			InLiquid=0;
	}
	// Contact Action
	if (fAnyContact)
	{
		t_contact = iContacts;
		ContactAction();
	}
	// Attachment Loss Action
	if (fNoAttach)
		NoAttachAction();
	// Movement Script Execution
	if (fAnyContact)
	{
		C4AulParSet pars(C4VInt(fixtoi(oldxdir, 100)), C4VInt(fixtoi(oldydir, 100)));
		if (old_ocf & OCF_HitSpeed1) Call(PSF_Hit, &pars);
		if (old_ocf & OCF_HitSpeed2) Call(PSF_Hit2, &pars);
		if (old_ocf & OCF_HitSpeed3) Call(PSF_Hit3, &pars);
	}
	// Rotation gfx
	if (fTurned)
		UpdateFace(true);
	else
		// pos changed?
		if (fMoved) UpdatePos();
}
Esempio n. 3
0
void C4Object::ForcePosition(C4Real tx, C4Real ty)
{
	fix_x=tx; fix_y=ty;
	UpdatePos();
	UpdateSolidMask(false);
}
Esempio n. 4
0
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;
}