openclonk/src/object/C4Movement.cpp

860 lines
19 KiB
C++
Raw Normal View History

2009-05-08 13:28:41 +00:00
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 1998-2000, Matthes Bender
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
2016-04-03 18:18:29 +00:00
* Copyright (c) 2009-2016, The OpenClonk Team and contributors
2009-05-08 13:28:41 +00:00
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
2009-05-08 13:28:41 +00:00
*
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
* See accompanying file "TRADEMARK" for details.
2009-05-08 13:28:41 +00:00
*
* To redistribute this file separately, substitute the full license texts
* for the above references.
2009-05-08 13:28:41 +00:00
*/
/* Object motion, collision, friction */
#include "C4Include.h"
2009-05-08 13:28:41 +00:00
#include "game/C4Physics.h"
#include "landscape/C4Landscape.h"
#include "landscape/C4SolidMask.h"
#include "object/C4Def.h"
#include "object/C4Object.h"
#include "script/C4Effect.h"
2009-05-08 13:28:41 +00:00
/* Some physical constants */
const C4Real FRedirect = C4REAL100(50);
const C4Real FFriction = C4REAL100(30);
const C4Real FixFullCircle = itofix(360);
const C4Real FixHalfCircle = FixFullCircle / 2;
const C4Real FloatFriction = C4REAL100(2);
const C4Real RotateAccel = C4REAL100(20);
const C4Real HitSpeed1 = C4REAL100(150); // Hit Event
const C4Real HitSpeed2 = itofix(2); // Cross Check Hit
const C4Real HitSpeed3 = itofix(6); // Scale disable, kneel
const C4Real HitSpeed4 = itofix(8); // Flat
const C4Real DefaultGravAccel = C4REAL100(20);
2009-05-08 13:28:41 +00:00
/* Some helper functions */
void RedirectForce(C4Real &from, C4Real &to, int32_t tdir)
2010-03-28 18:58:01 +00:00
{
C4Real fred;
fred = std::min(Abs(from), FRedirect);
from -= fred * Sign(from);
to += fred * tdir;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void ApplyFriction(C4Real &tval, int32_t percent)
2010-03-28 18:58:01 +00:00
{
C4Real ffric = FFriction * percent / 100;
if (tval > +ffric)
{
tval -= ffric;
}
else if (tval < -ffric)
{
tval += ffric;
}
else
{
tval = 0;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Compares all Shape.VtxContactCNAT[] CNAT flags to search flag.
// Returns true if CNAT match has been found.
bool ContactVtxCNAT(C4Object *cobj, BYTE cnat_dir)
2010-03-28 18:58:01 +00:00
{
for (int32_t cnt = 0; cnt < cobj->Shape.VtxNum; cnt++)
{
if (cobj->Shape.VtxContactCNAT[cnt] & cnat_dir)
{
return true;
}
}
return false;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Finds first vertex with contact flag set.
// Returns -1/0/+1 for relation on vertex to object center.
int32_t ContactVtxWeight(C4Object *cobj)
2010-03-28 18:58:01 +00:00
{
for (int32_t cnt = 0; cnt < cobj->Shape.VtxNum; cnt++)
{
if (cobj->Shape.VtxContactCNAT[cnt])
2010-03-28 18:58:01 +00:00
{
if (cobj->Shape.VtxX[cnt] < 0)
{
return -1;
}
if (cobj->Shape.VtxX[cnt] > 0)
{
return +1;
}
2010-03-28 18:58:01 +00:00
}
}
return 0;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// ContactVtxFriction: Returns 0-100 friction value of first
// contacted vertex;
int32_t ContactVtxFriction(C4Object *cobj)
2010-03-28 18:58:01 +00:00
{
for (int32_t cnt = 0; cnt < cobj->Shape.VtxNum; cnt++)
{
if (cobj->Shape.VtxContactCNAT[cnt])
{
return cobj->Shape.VtxFriction[cnt];
}
}
return 0;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
const char *CNATName(int32_t cnat)
2010-03-28 18:58:01 +00:00
{
switch (cnat)
2010-03-28 18:58:01 +00:00
{
case CNAT_None: return "None";
case CNAT_Left: return "Left";
case CNAT_Right: return "Right";
case CNAT_Top: return "Top";
case CNAT_Bottom: return "Bottom";
case CNAT_Center: return "Center";
}
2010-03-28 18:58:01 +00:00
return "Undefined";
}
2009-05-08 13:28:41 +00:00
bool C4Object::Contact(int32_t iCNAT)
2010-03-28 18:58:01 +00:00
{
if (GetPropertyInt(P_ContactCalls))
2010-03-28 18:58:01 +00:00
{
2009-05-11 13:09:53 +00:00
return !! Call(FormatString(PSF_Contact, CNATName(iCNAT)).getData());
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
return false;
}
2009-05-08 13:28:41 +00:00
void C4Object::DoMotion(int32_t mx, int32_t my)
2010-03-28 18:58:01 +00:00
{
RemoveSolidMask(true);
fix_x += mx;
fix_y += my;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Object::StopAndContact(C4Real & ctco, C4Real limit, C4Real & speed, int32_t cnat)
2010-03-28 18:58:01 +00:00
{
ctco = limit;
speed = 0;
Contact(cnat);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
int32_t C4Object::ContactCheck(int32_t iAtX, int32_t iAtY, uint32_t *border_hack_contacts, bool collide_halfvehic)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Check shape contact at given position
Shape.ContactCheck(iAtX, iAtY, border_hack_contacts, collide_halfvehic);
2009-05-08 13:28:41 +00:00
// Store shape contact values in object t_contact
t_contact = Shape.ContactCNAT;
2009-05-08 13:28:41 +00:00
// Contact script call for the first contacted cnat
if (Shape.ContactCNAT)
{
for (int32_t ccnat = 0; ccnat < 4; ccnat++) // Left, right, top bottom
{
if (Shape.ContactCNAT & (1 << ccnat))
{
if (Contact(1 << ccnat))
{
2009-05-08 13:28:41 +00:00
break; // Will stop on first positive return contact call!
}
}
}
}
2009-05-08 13:28:41 +00:00
// Return shape contact count
return Shape.ContactCount;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Stop the object and do contact calls if it collides with the border
void C4Object::SideBounds(C4Real &ctcox)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// layer bounds
if (Layer && (Layer->GetPropertyInt(P_BorderBound) & C4D_Border_Layer))
{
C4PropList* pActionDef = GetAction();
if (!pActionDef || pActionDef->GetPropertyP(P_Procedure) != DFA_ATTACH)
{
C4Real lbound = itofix(Layer->GetX() + Layer->Shape.GetX() - Shape.GetX());
C4Real rbound = itofix(Layer->GetX() + Layer->Shape.GetX() + Layer->Shape.Wdt - (Shape.GetX() + Shape.Wdt));
if (ctcox < lbound)
{
StopAndContact(ctcox, lbound, xdir, CNAT_Left);
}
if (ctcox > rbound)
{
StopAndContact(ctcox, rbound, xdir, CNAT_Right);
}
}
}
2009-05-08 13:28:41 +00:00
// landscape bounds
C4Real lbound = itofix(0 - Shape.GetX());
C4Real rbound = itofix(::Landscape.GetWidth() - (Shape.GetX() + Shape.Wdt));
if (ctcox < lbound && (GetPropertyInt(P_BorderBound) & C4D_Border_Sides))
{
StopAndContact(ctcox, lbound, xdir, CNAT_Left);
}
if (ctcox > rbound && (GetPropertyInt(P_BorderBound) & C4D_Border_Sides))
{
StopAndContact(ctcox, rbound, xdir, CNAT_Right);
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Object::VerticalBounds(C4Real &ctcoy)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// layer bounds
if (Layer && (Layer->GetPropertyInt(P_BorderBound) & C4D_Border_Layer))
{
C4PropList* pActionDef = GetAction();
if (!pActionDef || pActionDef->GetPropertyP(P_Procedure) != DFA_ATTACH)
{
C4Real tbound = itofix(Layer->GetY() + Layer->Shape.GetY() - Shape.GetY());
C4Real bbound = itofix(Layer->GetY() + Layer->Shape.GetY() + Layer->Shape.Hgt - (Shape.GetY() + Shape.Hgt));
if (ctcoy < tbound)
{
StopAndContact(ctcoy, tbound, ydir, CNAT_Top);
}
if (ctcoy > bbound)
{
StopAndContact(ctcoy, bbound, ydir, CNAT_Bottom);
}
}
}
2009-05-08 13:28:41 +00:00
// landscape bounds
C4Real tbound = itofix(0 - Shape.GetY());
C4Real bbound = itofix(::Landscape.GetHeight() - (Shape.GetY() + Shape.Hgt));
if (ctcoy < tbound && (GetPropertyInt(P_BorderBound) & C4D_Border_Top))
{
StopAndContact(ctcoy, tbound, ydir, CNAT_Top);
}
if (ctcoy > bbound && (GetPropertyInt(P_BorderBound) & C4D_Border_Bottom))
{
StopAndContact(ctcoy, bbound, ydir, CNAT_Bottom);
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Object::DoMovement()
2010-03-28 18:58:01 +00:00
{
int32_t iContact = 0;
bool fAnyContact = false;
int iContacts = 0;
BYTE fTurned = 0;
BYTE fRedirectYR = 0;
BYTE fNoAttach = 0;
2009-05-08 13:28:41 +00:00
// Restrictions
if (Def->NoHorizontalMove)
{
xdir = 0;
}
2009-05-08 13:28:41 +00:00
// Dig free target area
C4PropList* pActionDef = GetAction();
if (pActionDef)
{
if (pActionDef->GetPropertyInt(P_DigFree))
2010-03-28 18:58:01 +00:00
{
int ctcox, ctcoy;
2009-05-08 13:28:41 +00:00
// Shape size square
if (pActionDef->GetPropertyInt(P_DigFree) == 1)
2010-03-28 18:58:01 +00:00
{
ctcox = fixtoi(fix_x + xdir);
ctcoy = fixtoi(fix_y + ydir);
::Landscape.DigFreeRect(ctcox + Shape.GetX(), ctcoy + Shape.GetY(), Shape.Wdt, Shape.Hgt, this);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Free size round (variable size)
else
2010-03-28 18:58:01 +00:00
{
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);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
}
2009-05-08 13:28:41 +00:00
// store previous movement and ocf
C4Real oldxdir(xdir);
C4Real oldydir(ydir);
2009-05-08 13:28:41 +00:00
uint32_t old_ocf = OCF;
bool fMoved = false;
C4Real new_x = fix_x + xdir;
C4Real new_y = fix_y + ydir;
SideBounds(new_x);
2009-05-08 13:28:41 +00:00
if (!Action.t_attach) // Unattached movement = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Horizontal movement - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Move to target
while (fixtoi(new_x) != fixtoi(fix_x))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// 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)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Abort horizontal movement
new_x = fix_x;
// Vertical redirection (always)
RedirectForce(xdir, ydir, -1);
ApplyFriction(ydir, ContactVtxFriction(this));
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else // Free horizontal movement
2014-01-03 16:39:00 +00:00
{
2009-05-08 13:28:41 +00:00
DoMotion(step, 0);
2014-01-03 16:39:00 +00:00
fMoved = true;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Vertical movement - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Movement target
new_y = fix_y + ydir;
// Movement bounds (vertical)
VerticalBounds(new_y);
2009-05-08 13:28:41 +00:00
// Move to target
while (fixtoi(new_y) != fixtoi(fix_y))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Next step
int step = Sign(new_y - fix_y);
if ((iContact = ContactCheck(GetX(), GetY() + step, nullptr, ydir > 0)))
2010-03-28 18:58:01 +00:00
{
fAnyContact = true;
iContacts |= t_contact;
new_y = fix_y;
2009-05-08 13:28:41 +00:00
// Vertical contact horizontal friction
ApplyFriction(xdir, ContactVtxFriction(this));
2009-05-08 13:28:41 +00:00
// Redirection slide or rotate
if (!ContactVtxCNAT(this, CNAT_Left))
{
RedirectForce(ydir, xdir, -1);
}
else if (!ContactVtxCNAT(this, CNAT_Right))
{
RedirectForce(ydir, xdir, +1);
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// living things are always capable of keeping their rotation
if ((OCF & OCF_Rotate) && iContact == 1 && !Alive)
{
RedirectForce(ydir, rdir, -ContactVtxWeight(this));
fRedirectYR = 1;
}
ydir = 0;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else // Free vertical movement
2014-01-03 16:39:00 +00:00
{
DoMotion(0, step);
2014-01-03 16:39:00 +00:00
fMoved = true;
}
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
if (Action.t_attach) // Attached movement = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
2010-03-28 18:58:01 +00:00
{
VerticalBounds(new_y);
2009-05-08 13:28:41 +00:00
// Move to target
do
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Set next step target
int step_x = 0;
int 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;
2009-05-08 13:28:41 +00:00
// Attachment check
if (!Shape.Attach(ctx, cty, Action.t_attach))
{
fNoAttach = 1;
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
// 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);
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// 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)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Abort movement
if (ctx != GetX())
{
ctx = GetX();
new_x = fix_x;
}
if (cty != GetY())
{
cty = GetY();
new_y = fix_y;
}
2010-03-28 18:58:01 +00:00
}
DoMotion(ctx - GetX(), cty - GetY());
2014-01-03 16:39:00 +00:00
fMoved = true;
2009-05-08 13:28:41 +00:00
}
while (fixtoi(new_x) != GetX() || fixtoi(new_y) != GetY());
2010-03-28 18:58:01 +00:00
}
if (fix_x != new_x || fix_y != new_y)
{
fMoved = true;
RemoveSolidMask(true);
2014-01-03 16:39:00 +00:00
fix_x = new_x;
fix_y = new_y;
}
2009-05-08 13:28:41 +00:00
// Rotation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if ((OCF & OCF_Rotate) && !!rdir)
2010-03-28 18:58:01 +00:00
{
C4Real target_r = fix_r + rdir * 5;
2009-05-08 13:28:41 +00:00
// Rotation limit
if (Def->Rotateable>1)
2010-03-28 18:58:01 +00:00
{
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;
}
2010-03-28 18:58:01 +00:00
}
int32_t ctx = GetX();
int32_t cty = GetY();
2009-05-08 13:28:41 +00:00
// Move to target
while (fixtoi(fix_r) != fixtoi(target_r))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Save step undos
C4Real lcobjr = fix_r;
C4Shape lshape = Shape;
2009-05-08 13:28:41 +00:00
// Try next step
fix_r += Sign(target_r - fix_r);
2009-05-08 13:28:41 +00:00
UpdateShape();
// attached rotation: rotate around attachment pos
if (Action.t_attach && !fNoAttach)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// 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();
2009-05-08 13:28:41 +00:00
// 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);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// check for contact
if ((iContact = ContactCheck(ctx, cty))) // Contact
2010-03-28 18:58:01 +00:00
{
fAnyContact = true;
iContacts |= t_contact;
2009-05-08 13:28:41 +00:00
// Undo step and abort movement
Shape = lshape;
target_r = fix_r = lcobjr;
2009-05-08 13:28:41 +00:00
// last UpdateShape-call might have changed sector lists!
UpdatePos();
// Redirect to GetY()
if (iContact == 1 && !fRedirectYR)
{
RedirectForce(rdir, ydir, -1);
}
2009-05-08 13:28:41 +00:00
// Stop rotation
rdir = 0;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
fTurned = 1;
if (ctx != GetX() || cty != GetY())
{
fix_x = itofix(ctx);
fix_y = itofix(cty);
}
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Circle bounds
if (target_r < -FixHalfCircle)
{
target_r += FixFullCircle;
}
if (target_r > +FixHalfCircle)
{
target_r -= FixFullCircle;
}
fix_r = target_r;
2010-03-28 18:58:01 +00:00
}
// Reput solid mask if moved by motion
if (fMoved || fTurned)
{
UpdateSolidMask(true);
}
2009-05-08 13:28:41 +00:00
// Misc checks ===========================================================================================
// InLiquid check
// this equals C4Object::UpdateLiquid, but the "fNoAttach = false;"-line
2009-05-08 13:28:41 +00:00
if (IsInLiquidCheck()) // In Liquid
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (!InLiquid) // Enter liquid
2010-03-28 18:58:01 +00:00
{
if (OCF & OCF_HitSpeed2)
{
if (Mass > 3)
{
Splash();
}
}
fNoAttach = false;
InLiquid = true;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else // Out of liquid
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (InLiquid) // Leave liquid
{
InLiquid = false;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Contact Action
if (fAnyContact)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
t_contact = iContacts;
ContactAction();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Attachment Loss Action
if (fNoAttach)
{
2009-05-08 13:28:41 +00:00
NoAttachAction();
}
2009-05-08 13:28:41 +00:00
// Movement Script Execution
if (fAnyContact)
2010-03-28 18:58:01 +00:00
{
2014-05-30 23:03:23 +00:00
C4AulParSet pars(fixtoi(oldxdir, 100), 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);
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Rotation gfx
if (fTurned)
{
2009-05-08 13:28:41 +00:00
UpdateFace(true);
}
2009-05-08 13:28:41 +00:00
else
{
2009-05-08 13:28:41 +00:00
// pos changed?
if (fMoved)
{
UpdatePos();
}
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Object::Stabilize()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// def allows stabilization?
if (Def->NoStabilize)
{
return;
}
2009-05-08 13:28:41 +00:00
// normalize angle
C4Real nr = fix_r;
while (nr < itofix(-180))
{
nr += 360;
}
while (nr > itofix(180))
{
nr -= 360;
}
if (nr != Fix0)
{
if (Inside<C4Real>(nr, itofix(-StableRange), itofix(+StableRange)))
2010-03-28 18:58:01 +00:00
{
// Save step undos
C4Real lcobjr = fix_r;
C4Shape lshape = Shape;
// Try rotation
fix_r = Fix0;
UpdateShape();
if (ContactCheck(GetX(), GetY()))
2010-03-28 18:58:01 +00:00
{ // Undo rotation
Shape = lshape;
fix_r = lcobjr;
2010-03-28 18:58:01 +00:00
}
else
2010-03-28 18:58:01 +00:00
{ // Stabilization okay
UpdateFace(true);
}
2010-03-28 18:58:01 +00:00
}
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Object::CopyMotion(C4Object *from)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Designed for contained objects, no static
if (fix_x != from->fix_x || fix_y != from->fix_y)
2010-03-28 18:58:01 +00:00
{
fix_x = from->fix_x;
fix_y = from->fix_y;
2009-05-08 13:28:41 +00:00
// Resort into sectors
UpdatePos();
}
xdir = from->xdir;
ydir = from->ydir;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Object::ForcePosition(C4Real tx, C4Real ty)
2010-03-28 18:58:01 +00:00
{
fix_x = tx;
fix_y = ty;
UpdatePos();
UpdateSolidMask(false);
}
2009-05-08 13:28:41 +00:00
void C4Object::MovePosition(int32_t dx, int32_t dy)
{
MovePosition(itofix(dx), itofix(dy));
}
void C4Object::MovePosition(C4Real dx, C4Real dy)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// move object position; repositions SolidMask
RemoveSolidMask(true);
fix_x += dx;
fix_y += dy;
2009-05-08 13:28:41 +00:00
UpdatePos();
UpdateSolidMask(true);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Object::ExecMovement() // Every Tick1 by Execute
2010-03-28 18:58:01 +00:00
{
2010-09-12 14:03:49 +00:00
// update in which material this object is
UpdateInMat();
// Containment check
if (Contained)
2010-03-28 18:58:01 +00:00
{
CopyMotion(Contained);
return true;
2010-03-28 18:58:01 +00:00
}
// General mobility check
if (Category & C4D_StaticBack)
{
return false;
}
// Movement execution
if (Mobile) // Object is moving
2010-03-28 18:58:01 +00:00
{
// Move object
DoMovement();
// Demobilization check
if ((xdir == 0) && (ydir == 0) && (rdir == 0))
{
Mobile = false;
}
// Check for stabilization
if (rdir == 0)
{
Stabilize();
}
2010-03-28 18:58:01 +00:00
}
else // Object is static
2010-03-28 18:58:01 +00:00
{
// Check for stabilization
Stabilize();
// Check for mobilization
if (!::Game.iTick10)
2010-03-28 18:58:01 +00:00
{
// Gravity mobilization
xdir = ydir = rdir = 0;
Mobile = true;
}
2010-03-28 18:58:01 +00:00
}
// Enforce zero rotation
if (!Def->Rotateable)
{
fix_r = Fix0;
}
// Out of bounds check
if ((!Inside<int32_t>(GetX() + Shape.GetX(), -Shape.Wdt, ::Landscape.GetWidth()) && !(GetPropertyInt(P_BorderBound) & C4D_Border_Sides))
|| ((GetY() + Shape.GetY() > ::Landscape.GetHeight()) && !(GetPropertyInt(P_BorderBound) & C4D_Border_Bottom)))
2010-03-28 18:58:01 +00:00
{
C4PropList* pActionDef = GetAction();
2009-05-08 13:28:41 +00:00
// Never remove attached objects: If they are truly outside landscape, their target will be removed,
// and the attached objects follow one frame later
if (!pActionDef || !Action.Target || pActionDef->GetPropertyP(P_Procedure) != DFA_ATTACH)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
bool fRemove = true;
// never remove HUD objects
if (Category & C4D_Parallax)
2010-03-28 18:58:01 +00:00
{
int parX, parY;
GetParallaxity(&parX, &parY);
2009-05-08 13:28:41 +00:00
fRemove = false;
if (GetX() > ::Landscape.GetWidth() || GetY() > ::Landscape.GetHeight())
{
fRemove = true; // except if they are really out of the viewport to the right...
}
else if (GetX() < 0 && !!parX)
{
fRemove = true; // ...or it's not HUD horizontally and it's out to the left
}
else if (!parX && GetX() < -::Landscape.GetWidth())
{
fRemove = true; // ...or it's HUD horizontally and it's out to the left
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
if (fRemove)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
AssignDeath(true);
AssignRemoval();
}
}
}
2010-03-28 18:58:01 +00:00
return true;
}
2009-05-08 13:28:41 +00:00
bool SimFlight(C4Real &x, C4Real &y, C4Real &xdir, C4Real &ydir, int32_t iDensityMin, int32_t iDensityMax, int32_t &iIter)
2009-05-08 13:28:41 +00:00
{
bool hitOnTime = true;
bool fBreak = false;
int32_t ctcox, ctcoy;
int32_t cx = fixtoi(x);
int32_t cy = fixtoi(y);
int32_t i = iIter;
do
2010-03-28 18:58:01 +00:00
{
if (!--i)
{
hitOnTime = false;
break;
}
// If the object isn't moving and there is no gravity either, abort
if (xdir == 0 && ydir == 0 && GravAccel == 0)
{
return false;
}
// If the object is above the landscape flying upwards in no/negative gravity, abort
if (ydir <= 0 && GravAccel <= 0 && cy < 0)
{
return false;
}
// Set target position by momentum
x += xdir;
y += ydir;
// Movement to target
ctcox = fixtoi(x);
ctcoy = fixtoi(y);
// Bounds
if (!Inside<int32_t>(ctcox, 0, ::Landscape.GetWidth()) || (ctcoy >= ::Landscape.GetHeight()))
{
return false;
}
// Move to target
do
2010-03-28 18:58:01 +00:00
{
// Set next step target
cx += Sign(ctcox - cx);
cy += Sign(ctcoy - cy);
// Contact check
if (Inside(GBackDensity(cx, cy), iDensityMin, iDensityMax))
{
fBreak = true;
break;
}
2010-03-28 18:58:01 +00:00
}
while ((cx != ctcox) || (cy != ctcoy));
// Adjust GravAccel once per frame
ydir += GravAccel;
2010-03-28 18:58:01 +00:00
}
while (!fBreak);
2009-05-08 13:28:41 +00:00
// write position back
x = itofix(cx);
y = itofix(cy);
// how many steps did it take to get here?
2010-10-02 17:02:21 +00:00
iIter -= i;
return hitOnTime;
2009-05-08 13:28:41 +00:00
}
bool SimFlightHitsLiquid(C4Real fcx, C4Real fcy, C4Real xdir, C4Real ydir)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Start in water?
2010-03-28 18:58:01 +00:00
if (DensityLiquid(GBackDensity(fixtoi(fcx), fixtoi(fcy))))
{
int temp = 10;
if (!SimFlight(fcx, fcy, xdir, ydir, 0, C4M_Liquid - 1, temp))
{
return false;
}
}
2009-05-08 13:28:41 +00:00
// Hits liquid?
int temp = -1;
if (!SimFlight(fcx, fcy, xdir, ydir, C4M_Liquid, 100, temp))
{
return false;
}
2009-05-08 13:28:41 +00:00
// liquid & deep enough?
return GBackLiquid(fixtoi(fcx), fixtoi(fcy)) && GBackLiquid(fixtoi(fcx), fixtoi(fcy) + 9);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00