Refactor movement code (#87)

Improve readability by adding appropriate parentheses and braces, and fixing misleading/confusing whitespace. Extract SolidMask removal to a function since it's used in several places.
master
gitMarky 2019-03-05 21:15:39 +01:00 committed by isilkor
parent 765d9a24e9
commit 4a2048575f
3 changed files with 363 additions and 162 deletions

View File

@ -28,33 +28,43 @@
/* Some physical constants */ /* Some physical constants */
const C4Real FRedirect=C4REAL100(50); const C4Real FRedirect = C4REAL100(50);
const C4Real FFriction=C4REAL100(30); const C4Real FFriction = C4REAL100(30);
const C4Real FixFullCircle=itofix(360),FixHalfCircle=FixFullCircle/2; const C4Real FixFullCircle = itofix(360);
const C4Real FloatFriction=C4REAL100(2); const C4Real FixHalfCircle = FixFullCircle / 2;
const C4Real RotateAccel=C4REAL100(20); const C4Real FloatFriction = C4REAL100(2);
const C4Real HitSpeed1=C4REAL100(150); // Hit Event const C4Real RotateAccel = C4REAL100(20);
const C4Real HitSpeed2=itofix(2); // Cross Check Hit const C4Real HitSpeed1 = C4REAL100(150); // Hit Event
const C4Real HitSpeed3=itofix(6); // Scale disable, kneel const C4Real HitSpeed2 = itofix(2); // Cross Check Hit
const C4Real HitSpeed4=itofix(8); // Flat const C4Real HitSpeed3 = itofix(6); // Scale disable, kneel
const C4Real DefaultGravAccel=C4REAL100(20); const C4Real HitSpeed4 = itofix(8); // Flat
const C4Real DefaultGravAccel = C4REAL100(20);
/* Some helper functions */ /* Some helper functions */
void RedirectForce(C4Real &from, C4Real &to, int32_t tdir) void RedirectForce(C4Real &from, C4Real &to, int32_t tdir)
{ {
C4Real fred; C4Real fred;
fred=std::min(Abs(from), FRedirect); fred = std::min(Abs(from), FRedirect);
from-=fred*Sign(from); from -= fred * Sign(from);
to+=fred*tdir; to += fred * tdir;
} }
void ApplyFriction(C4Real &tval, int32_t percent) void ApplyFriction(C4Real &tval, int32_t percent)
{ {
C4Real ffric=FFriction*percent/100; C4Real ffric = FFriction * percent / 100;
if (tval>+ffric) { tval-=ffric; return; } if (tval > +ffric)
if (tval<-ffric) { tval+=ffric; return; } {
tval=0; tval -= ffric;
}
else if (tval < -ffric)
{
tval += ffric;
}
else
{
tval = 0;
}
} }
// Compares all Shape.VtxContactCNAT[] CNAT flags to search flag. // Compares all Shape.VtxContactCNAT[] CNAT flags to search flag.
@ -62,12 +72,14 @@ void ApplyFriction(C4Real &tval, int32_t percent)
bool ContactVtxCNAT(C4Object *cobj, BYTE cnat_dir) bool ContactVtxCNAT(C4Object *cobj, BYTE cnat_dir)
{ {
int32_t cnt; for (int32_t cnt = 0; cnt < cobj->Shape.VtxNum; cnt++)
bool fcontact=false; {
for (cnt=0; cnt<cobj->Shape.VtxNum; cnt++)
if (cobj->Shape.VtxContactCNAT[cnt] & cnat_dir) if (cobj->Shape.VtxContactCNAT[cnt] & cnat_dir)
fcontact=true; {
return fcontact; return true;
}
}
return false;
} }
// Finds first vertex with contact flag set. // Finds first vertex with contact flag set.
@ -75,13 +87,20 @@ bool ContactVtxCNAT(C4Object *cobj, BYTE cnat_dir)
int32_t ContactVtxWeight(C4Object *cobj) int32_t ContactVtxWeight(C4Object *cobj)
{ {
int32_t cnt; for (int32_t cnt = 0; cnt < cobj->Shape.VtxNum; cnt++)
for (cnt=0; cnt<cobj->Shape.VtxNum; cnt++) {
if (cobj->Shape.VtxContactCNAT[cnt]) if (cobj->Shape.VtxContactCNAT[cnt])
{ {
if (cobj->Shape.VtxX[cnt]<0) return -1; if (cobj->Shape.VtxX[cnt] < 0)
if (cobj->Shape.VtxX[cnt]>0) return +1; {
return -1;
}
if (cobj->Shape.VtxX[cnt] > 0)
{
return +1;
}
} }
}
return 0; return 0;
} }
@ -90,10 +109,13 @@ int32_t ContactVtxWeight(C4Object *cobj)
int32_t ContactVtxFriction(C4Object *cobj) int32_t ContactVtxFriction(C4Object *cobj)
{ {
int32_t cnt; for (int32_t cnt = 0; cnt < cobj->Shape.VtxNum; cnt++)
for (cnt=0; cnt<cobj->Shape.VtxNum; cnt++) {
if (cobj->Shape.VtxContactCNAT[cnt]) if (cobj->Shape.VtxContactCNAT[cnt])
{
return cobj->Shape.VtxFriction[cnt]; return cobj->Shape.VtxFriction[cnt];
}
}
return 0; return 0;
} }
@ -122,8 +144,9 @@ bool C4Object::Contact(int32_t iCNAT)
void C4Object::DoMotion(int32_t mx, int32_t my) void C4Object::DoMotion(int32_t mx, int32_t my)
{ {
if (pSolidMaskData) pSolidMaskData->Remove(true); RemoveSolidMask(true);
fix_x += mx; fix_y += my; fix_x += mx;
fix_y += my;
} }
void C4Object::StopAndContact(C4Real & ctco, C4Real limit, C4Real & speed, int32_t cnat) void C4Object::StopAndContact(C4Real & ctco, C4Real limit, C4Real & speed, int32_t cnat)
@ -136,17 +159,25 @@ void C4Object::StopAndContact(C4Real & ctco, C4Real limit, C4Real & speed, int32
int32_t C4Object::ContactCheck(int32_t iAtX, int32_t iAtY, uint32_t *border_hack_contacts, bool collide_halfvehic) int32_t C4Object::ContactCheck(int32_t iAtX, int32_t iAtY, uint32_t *border_hack_contacts, bool collide_halfvehic)
{ {
// Check shape contact at given position // Check shape contact at given position
Shape.ContactCheck(iAtX,iAtY,border_hack_contacts,collide_halfvehic); Shape.ContactCheck(iAtX, iAtY, border_hack_contacts, collide_halfvehic);
// Store shape contact values in object t_contact // Store shape contact values in object t_contact
t_contact=Shape.ContactCNAT; t_contact = Shape.ContactCNAT;
// Contact script call for the first contacted cnat // Contact script call for the first contacted cnat
if (Shape.ContactCNAT) if (Shape.ContactCNAT)
for (int32_t ccnat=0; ccnat<4; ccnat++) // Left, right, top bottom {
if (Shape.ContactCNAT & (1<<ccnat)) for (int32_t ccnat = 0; ccnat < 4; ccnat++) // Left, right, top bottom
if (Contact(1<<ccnat)) {
if (Shape.ContactCNAT & (1 << ccnat))
{
if (Contact(1 << ccnat))
{
break; // Will stop on first positive return contact call! break; // Will stop on first positive return contact call!
}
}
}
}
// Return shape contact count // Return shape contact count
return Shape.ContactCount; return Shape.ContactCount;
@ -156,80 +187,114 @@ int32_t C4Object::ContactCheck(int32_t iAtX, int32_t iAtY, uint32_t *border_hack
void C4Object::SideBounds(C4Real &ctcox) void C4Object::SideBounds(C4Real &ctcox)
{ {
// layer bounds // layer bounds
if (Layer && Layer->GetPropertyInt(P_BorderBound) & C4D_Border_Layer) if (Layer && (Layer->GetPropertyInt(P_BorderBound) & C4D_Border_Layer))
{ {
C4PropList* pActionDef = GetAction(); C4PropList* pActionDef = GetAction();
if (!pActionDef || pActionDef->GetPropertyP(P_Procedure) != DFA_ATTACH) if (!pActionDef || pActionDef->GetPropertyP(P_Procedure) != DFA_ATTACH)
{ {
C4Real lbound = itofix(Layer->GetX() + Layer->Shape.GetX() - Shape.GetX()), C4Real lbound = itofix(Layer->GetX() + Layer->Shape.GetX() - Shape.GetX());
rbound = itofix(Layer->GetX() + Layer->Shape.GetX() + Layer->Shape.Wdt - (Shape.GetX() + Shape.Wdt)); 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 < lbound)
if (ctcox > rbound) StopAndContact(ctcox, rbound, xdir, CNAT_Right); {
StopAndContact(ctcox, lbound, xdir, CNAT_Left);
}
if (ctcox > rbound)
{
StopAndContact(ctcox, rbound, xdir, CNAT_Right);
}
} }
} }
// landscape bounds // landscape bounds
C4Real lbound = itofix(0 - Shape.GetX()), C4Real lbound = itofix(0 - Shape.GetX());
rbound = itofix(::Landscape.GetWidth() - (Shape.GetX() + Shape.Wdt)); C4Real rbound = itofix(::Landscape.GetWidth() - (Shape.GetX() + Shape.Wdt));
if (ctcox < lbound && GetPropertyInt(P_BorderBound) & C4D_Border_Sides) if (ctcox < lbound && (GetPropertyInt(P_BorderBound) & C4D_Border_Sides))
{
StopAndContact(ctcox, lbound, xdir, CNAT_Left); StopAndContact(ctcox, lbound, xdir, CNAT_Left);
if (ctcox > rbound && GetPropertyInt(P_BorderBound) & C4D_Border_Sides) }
if (ctcox > rbound && (GetPropertyInt(P_BorderBound) & C4D_Border_Sides))
{
StopAndContact(ctcox, rbound, xdir, CNAT_Right); StopAndContact(ctcox, rbound, xdir, CNAT_Right);
}
} }
void C4Object::VerticalBounds(C4Real &ctcoy) void C4Object::VerticalBounds(C4Real &ctcoy)
{ {
// layer bounds // layer bounds
if (Layer && Layer->GetPropertyInt(P_BorderBound) & C4D_Border_Layer) if (Layer && (Layer->GetPropertyInt(P_BorderBound) & C4D_Border_Layer))
{ {
C4PropList* pActionDef = GetAction(); C4PropList* pActionDef = GetAction();
if (!pActionDef || pActionDef->GetPropertyP(P_Procedure) != DFA_ATTACH) if (!pActionDef || pActionDef->GetPropertyP(P_Procedure) != DFA_ATTACH)
{ {
C4Real tbound = itofix(Layer->GetY() + Layer->Shape.GetY() - Shape.GetY()), C4Real tbound = itofix(Layer->GetY() + Layer->Shape.GetY() - Shape.GetY());
bbound = itofix(Layer->GetY() + Layer->Shape.GetY() + Layer->Shape.Hgt - (Shape.GetY() + Shape.Hgt)); 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 < tbound)
if (ctcoy > bbound) StopAndContact(ctcoy, bbound, ydir, CNAT_Bottom); {
StopAndContact(ctcoy, tbound, ydir, CNAT_Top);
}
if (ctcoy > bbound)
{
StopAndContact(ctcoy, bbound, ydir, CNAT_Bottom);
}
} }
} }
// landscape bounds // landscape bounds
C4Real tbound = itofix(0 - Shape.GetY()), C4Real tbound = itofix(0 - Shape.GetY());
bbound = itofix(::Landscape.GetHeight() - (Shape.GetY() + Shape.Hgt)); C4Real bbound = itofix(::Landscape.GetHeight() - (Shape.GetY() + Shape.Hgt));
if (ctcoy < tbound && GetPropertyInt(P_BorderBound) & C4D_Border_Top) if (ctcoy < tbound && (GetPropertyInt(P_BorderBound) & C4D_Border_Top))
{
StopAndContact(ctcoy, tbound, ydir, CNAT_Top); StopAndContact(ctcoy, tbound, ydir, CNAT_Top);
if (ctcoy > bbound && GetPropertyInt(P_BorderBound) & C4D_Border_Bottom) }
if (ctcoy > bbound && (GetPropertyInt(P_BorderBound) & C4D_Border_Bottom))
{
StopAndContact(ctcoy, bbound, ydir, CNAT_Bottom); StopAndContact(ctcoy, bbound, ydir, CNAT_Bottom);
}
} }
void C4Object::DoMovement() void C4Object::DoMovement()
{ {
int32_t iContact=0; int32_t iContact = 0;
bool fAnyContact=false; int iContacts = 0; bool fAnyContact = false;
BYTE fTurned=0,fRedirectYR=0,fNoAttach=0; int iContacts = 0;
BYTE fTurned = 0;
BYTE fRedirectYR = 0;
BYTE fNoAttach = 0;
// Restrictions // Restrictions
if (Def->NoHorizontalMove) xdir=0; if (Def->NoHorizontalMove)
{
xdir = 0;
}
// Dig free target area // Dig free target area
C4PropList* pActionDef = GetAction(); C4PropList* pActionDef = GetAction();
if (pActionDef) if (pActionDef)
{
if (pActionDef->GetPropertyInt(P_DigFree)) if (pActionDef->GetPropertyInt(P_DigFree))
{ {
int ctcox, ctcoy; int ctcox, ctcoy;
// Shape size square // Shape size square
if (pActionDef->GetPropertyInt(P_DigFree)==1) if (pActionDef->GetPropertyInt(P_DigFree) == 1)
{ {
ctcox=fixtoi(fix_x+xdir); ctcoy=fixtoi(fix_y+ydir); ctcox = fixtoi(fix_x + xdir);
::Landscape.DigFreeRect(ctcox+Shape.GetX(),ctcoy+Shape.GetY(),Shape.Wdt,Shape.Hgt,this); ctcoy = fixtoi(fix_y + ydir);
::Landscape.DigFreeRect(ctcox + Shape.GetX(), ctcoy + Shape.GetY(), Shape.Wdt, Shape.Hgt, this);
} }
// Free size round (variable size) // Free size round (variable size)
else else
{ {
ctcox=fixtoi(fix_x+xdir); ctcoy=fixtoi(fix_y+ydir); ctcox = fixtoi(fix_x + xdir);
ctcoy = fixtoi(fix_y + ydir);
int32_t rad = pActionDef->GetPropertyInt(P_DigFree); int32_t rad = pActionDef->GetPropertyInt(P_DigFree);
if (Con<FullCon) rad = rad*6*Con/5/FullCon; if (Con < FullCon)
::Landscape.DigFree(ctcox,ctcoy-1,rad,this); {
rad = rad * 6 * Con / 5 / FullCon;
}
::Landscape.DigFree(ctcox, ctcoy - 1, rad, this);
} }
} }
}
// store previous movement and ocf // store previous movement and ocf
C4Real oldxdir(xdir), oldydir(ydir); C4Real oldxdir(xdir);
C4Real oldydir(ydir);
uint32_t old_ocf = OCF; uint32_t old_ocf = OCF;
bool fMoved = false; bool fMoved = false;
@ -246,18 +311,19 @@ void C4Object::DoMovement()
// Next step // Next step
int step = Sign(new_x - fix_x); int step = Sign(new_x - fix_x);
uint32_t border_hack_contacts = 0; uint32_t border_hack_contacts = 0;
iContact=ContactCheck(GetX() + step, GetY(), &border_hack_contacts); iContact = ContactCheck(GetX() + step, GetY(), &border_hack_contacts);
if (iContact || border_hack_contacts) if (iContact || border_hack_contacts)
{ {
fAnyContact=true; iContacts |= t_contact | border_hack_contacts; fAnyContact = true;
iContacts |= t_contact | border_hack_contacts;
} }
if (iContact) if (iContact)
{ {
// Abort horizontal movement // Abort horizontal movement
new_x = fix_x; new_x = fix_x;
// Vertical redirection (always) // Vertical redirection (always)
RedirectForce(xdir,ydir,-1); RedirectForce(xdir, ydir, -1);
ApplyFriction(ydir,ContactVtxFriction(this)); ApplyFriction(ydir, ContactVtxFriction(this));
} }
else // Free horizontal movement else // Free horizontal movement
{ {
@ -275,31 +341,36 @@ void C4Object::DoMovement()
{ {
// Next step // Next step
int step = Sign(new_y - fix_y); int step = Sign(new_y - fix_y);
if ((iContact=ContactCheck(GetX(), GetY() + step, nullptr, ydir > 0))) if ((iContact = ContactCheck(GetX(), GetY() + step, nullptr, ydir > 0)))
{ {
fAnyContact=true; iContacts |= t_contact; fAnyContact = true;
iContacts |= t_contact;
new_y = fix_y; new_y = fix_y;
// Vertical contact horizontal friction // Vertical contact horizontal friction
ApplyFriction(xdir,ContactVtxFriction(this)); ApplyFriction(xdir, ContactVtxFriction(this));
// Redirection slide or rotate // Redirection slide or rotate
if (!ContactVtxCNAT(this,CNAT_Left)) if (!ContactVtxCNAT(this, CNAT_Left))
RedirectForce(ydir,xdir,-1); {
else if (!ContactVtxCNAT(this,CNAT_Right)) RedirectForce(ydir, xdir, -1);
RedirectForce(ydir,xdir,+1); }
else if (!ContactVtxCNAT(this, CNAT_Right))
{
RedirectForce(ydir, xdir, +1);
}
else else
{ {
// living things are always capable of keeping their rotation // living things are always capable of keeping their rotation
if (OCF & OCF_Rotate) if (iContact==1) if (!Alive) if ((OCF & OCF_Rotate) && iContact == 1 && !Alive)
{ {
RedirectForce(ydir,rdir,-ContactVtxWeight(this)); RedirectForce(ydir, rdir, -ContactVtxWeight(this));
fRedirectYR=1; fRedirectYR = 1;
} }
ydir=0; ydir = 0;
} }
} }
else // Free vertical movement else // Free vertical movement
{ {
DoMotion(0,step); DoMotion(0, step);
fMoved = true; fMoved = true;
} }
} }
@ -311,45 +382,57 @@ void C4Object::DoMovement()
do do
{ {
// Set next step target // Set next step target
int step_x = 0, step_y = 0; int step_x = 0;
int step_y = 0;
if (fixtoi(new_x) != GetX()) if (fixtoi(new_x) != GetX())
{
step_x = Sign(fixtoi(new_x) - GetX()); step_x = Sign(fixtoi(new_x) - GetX());
}
else if (fixtoi(new_y) != GetY()) else if (fixtoi(new_y) != GetY())
{
step_y = Sign(fixtoi(new_y) - GetY()); step_y = Sign(fixtoi(new_y) - GetY());
}
int32_t ctx = GetX() + step_x; int32_t ctx = GetX() + step_x;
int32_t cty = GetY() + step_y; int32_t cty = GetY() + step_y;
// Attachment check // Attachment check
if (!Shape.Attach(ctx,cty,Action.t_attach)) if (!Shape.Attach(ctx, cty, Action.t_attach))
fNoAttach=1; {
fNoAttach = 1;
}
else else
{ {
// Attachment change to ctx/cty overrides target // Attachment change to ctx/cty overrides target
if (ctx != GetX() + step_x) if (ctx != GetX() + step_x)
{ {
xdir = Fix0; new_x = itofix(ctx); xdir = Fix0;
new_x = itofix(ctx);
} }
if (cty != GetY() + step_y) if (cty != GetY() + step_y)
{ {
ydir = Fix0; new_y = itofix(cty); ydir = Fix0;
new_y = itofix(cty);
} }
} }
// Contact check & evaluation // Contact check & evaluation
uint32_t border_hack_contacts = 0; uint32_t border_hack_contacts = 0;
iContact=ContactCheck(ctx,cty,&border_hack_contacts); iContact = ContactCheck(ctx, cty, &border_hack_contacts);
if (iContact || border_hack_contacts) if (iContact || border_hack_contacts)
{ {
fAnyContact=true; iContacts |= border_hack_contacts | t_contact; fAnyContact = true;
iContacts |= border_hack_contacts | t_contact;
} }
if (iContact) if (iContact)
{ {
// Abort movement // Abort movement
if (ctx != GetX()) if (ctx != GetX())
{ {
ctx = GetX(); new_x = fix_x; ctx = GetX();
new_x = fix_x;
} }
if (cty != GetY()) if (cty != GetY())
{ {
cty = GetY(); new_y = fix_y; cty = GetY();
new_y = fix_y;
} }
} }
DoMotion(ctx - GetX(), cty - GetY()); DoMotion(ctx - GetX(), cty - GetY());
@ -358,31 +441,39 @@ void C4Object::DoMovement()
while (fixtoi(new_x) != GetX() || fixtoi(new_y) != GetY()); while (fixtoi(new_x) != GetX() || fixtoi(new_y) != GetY());
} }
if(fix_x != new_x || fix_y != new_y) if (fix_x != new_x || fix_y != new_y)
{ {
fMoved = true; fMoved = true;
if (pSolidMaskData) pSolidMaskData->Remove(true); RemoveSolidMask(true);
fix_x = new_x; fix_x = new_x;
fix_y = new_y; fix_y = new_y;
} }
// Rotation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Rotation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if (OCF & OCF_Rotate && !!rdir) if ((OCF & OCF_Rotate) && !!rdir)
{ {
C4Real target_r = fix_r + rdir * 5; C4Real target_r = fix_r + rdir * 5;
// Rotation limit // Rotation limit
if (Def->Rotateable>1) if (Def->Rotateable>1)
{ {
if (target_r > itofix(Def->Rotateable)) if (target_r > itofix(Def->Rotateable))
{ target_r = itofix(Def->Rotateable); rdir=0; } {
target_r = itofix(Def->Rotateable);
rdir = 0;
}
if (target_r < itofix(-Def->Rotateable)) if (target_r < itofix(-Def->Rotateable))
{ target_r = itofix(-Def->Rotateable); rdir=0; } {
target_r = itofix(-Def->Rotateable);
rdir = 0;
}
} }
int32_t ctx=GetX(); int32_t cty=GetY(); int32_t ctx = GetX();
int32_t cty = GetY();
// Move to target // Move to target
while (fixtoi(fix_r) != fixtoi(target_r)) while (fixtoi(fix_r) != fixtoi(target_r))
{ {
// Save step undos // Save step undos
C4Real lcobjr = fix_r; C4Shape lshape=Shape; C4Real lcobjr = fix_r;
C4Shape lshape = Shape;
// Try next step // Try next step
fix_r += Sign(target_r - fix_r); fix_r += Sign(target_r - fix_r);
UpdateShape(); UpdateShape();
@ -392,59 +483,80 @@ void C4Object::DoMovement()
// more accurately, attachment should be evaluated by a rotation around the attachment vertex // 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, // 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 // it's enough to assume rotation around the center
ctx=GetX(); cty=GetY(); ctx = GetX();
cty = GetY();
// evaluate attachment, but do not bother about attachment loss // evaluate attachment, but do not bother about attachment loss
// that will then be done in next execution cycle // that will then be done in next execution cycle
Shape.Attach(ctx,cty,Action.t_attach); Shape.Attach(ctx, cty, Action.t_attach);
} }
// check for contact // check for contact
if ((iContact=ContactCheck(ctx,cty))) // Contact if ((iContact = ContactCheck(ctx, cty))) // Contact
{ {
fAnyContact=true; iContacts |= t_contact; fAnyContact = true;
iContacts |= t_contact;
// Undo step and abort movement // Undo step and abort movement
Shape=lshape; Shape = lshape;
target_r = fix_r = lcobjr; target_r = fix_r = lcobjr;
// last UpdateShape-call might have changed sector lists! // last UpdateShape-call might have changed sector lists!
UpdatePos(); UpdatePos();
// Redirect to GetY() // Redirect to GetY()
if (iContact==1) if (!fRedirectYR) if (iContact == 1 && !fRedirectYR)
RedirectForce(rdir,ydir,-1); {
RedirectForce(rdir, ydir, -1);
}
// Stop rotation // Stop rotation
rdir=0; rdir = 0;
} }
else else
{ {
fTurned=1; fTurned = 1;
if (ctx != GetX() || cty != GetY()) if (ctx != GetX() || cty != GetY())
{ {
fix_x = itofix(ctx); fix_y = itofix(cty); fix_x = itofix(ctx);
fix_y = itofix(cty);
} }
} }
} }
// Circle bounds // Circle bounds
if (target_r < -FixHalfCircle) { target_r += FixFullCircle; } if (target_r < -FixHalfCircle)
if (target_r > +FixHalfCircle) { target_r -= FixFullCircle; } {
target_r += FixFullCircle;
}
if (target_r > +FixHalfCircle)
{
target_r -= FixFullCircle;
}
fix_r = target_r; fix_r = target_r;
} }
// Reput solid mask if moved by motion // Reput solid mask if moved by motion
if (fMoved || fTurned) UpdateSolidMask(true); if (fMoved || fTurned)
{
UpdateSolidMask(true);
}
// Misc checks =========================================================================================== // Misc checks ===========================================================================================
// InLiquid check // InLiquid check
// this equals C4Object::UpdateLiquid, but the "fNoAttach=false;"-line // this equals C4Object::UpdateLiquid, but the "fNoAttach = false;"-line
if (IsInLiquidCheck()) // In Liquid if (IsInLiquidCheck()) // In Liquid
{ {
if (!InLiquid) // Enter liquid if (!InLiquid) // Enter liquid
{ {
if (OCF & OCF_HitSpeed2) if (OCF & OCF_HitSpeed2)
if (Mass>3) Splash(); {
fNoAttach=false; if (Mass > 3)
InLiquid=true; {
Splash();
}
}
fNoAttach = false;
InLiquid = true;
} }
} }
else // Out of liquid else // Out of liquid
{ {
if (InLiquid) // Leave liquid if (InLiquid) // Leave liquid
InLiquid=false; {
InLiquid = false;
}
} }
// Contact Action // Contact Action
if (fAnyContact) if (fAnyContact)
@ -454,50 +566,79 @@ void C4Object::DoMovement()
} }
// Attachment Loss Action // Attachment Loss Action
if (fNoAttach) if (fNoAttach)
{
NoAttachAction(); NoAttachAction();
}
// Movement Script Execution // Movement Script Execution
if (fAnyContact) if (fAnyContact)
{ {
C4AulParSet pars(fixtoi(oldxdir, 100), fixtoi(oldydir, 100)); C4AulParSet pars(fixtoi(oldxdir, 100), fixtoi(oldydir, 100));
if (old_ocf & OCF_HitSpeed1) Call(PSF_Hit, &pars); if (old_ocf & OCF_HitSpeed1)
if (old_ocf & OCF_HitSpeed2) Call(PSF_Hit2, &pars); {
if (old_ocf & OCF_HitSpeed3) Call(PSF_Hit3, &pars); Call(PSF_Hit, &pars);
}
if (old_ocf & OCF_HitSpeed2)
{
Call(PSF_Hit2, &pars);
}
if (old_ocf & OCF_HitSpeed3)
{
Call(PSF_Hit3, &pars);
}
} }
// Rotation gfx // Rotation gfx
if (fTurned) if (fTurned)
{
UpdateFace(true); UpdateFace(true);
}
else else
{
// pos changed? // pos changed?
if (fMoved) UpdatePos(); if (fMoved)
{
UpdatePos();
}
}
} }
void C4Object::Stabilize() void C4Object::Stabilize()
{ {
// def allows stabilization? // def allows stabilization?
if (Def->NoStabilize) return; if (Def->NoStabilize)
{
return;
}
// normalize angle // normalize angle
C4Real nr = fix_r; C4Real nr = fix_r;
while (nr < itofix(-180)) nr += 360; while (nr < itofix(-180))
while (nr > itofix(180)) nr -= 360; {
nr += 360;
}
while (nr > itofix(180))
{
nr -= 360;
}
if (nr != Fix0) if (nr != Fix0)
if (Inside<C4Real>(nr,itofix(-StableRange),itofix(+StableRange))) {
if (Inside<C4Real>(nr, itofix(-StableRange), itofix(+StableRange)))
{ {
// Save step undos // Save step undos
C4Real lcobjr=fix_r; C4Real lcobjr = fix_r;
C4Shape lshape=Shape; C4Shape lshape = Shape;
// Try rotation // Try rotation
fix_r=Fix0; fix_r = Fix0;
UpdateShape(); UpdateShape();
if (ContactCheck(GetX(),GetY())) if (ContactCheck(GetX(), GetY()))
{ // Undo rotation { // Undo rotation
Shape=lshape; Shape = lshape;
fix_r=lcobjr; fix_r = lcobjr;
} }
else else
{ // Stabilization okay { // Stabilization okay
UpdateFace(true); UpdateFace(true);
} }
} }
}
} }
void C4Object::CopyMotion(C4Object *from) void C4Object::CopyMotion(C4Object *from)
@ -505,16 +646,19 @@ void C4Object::CopyMotion(C4Object *from)
// Designed for contained objects, no static // Designed for contained objects, no static
if (fix_x != from->fix_x || fix_y != from->fix_y) if (fix_x != from->fix_x || fix_y != from->fix_y)
{ {
fix_x=from->fix_x; fix_y=from->fix_y; fix_x = from->fix_x;
fix_y = from->fix_y;
// Resort into sectors // Resort into sectors
UpdatePos(); UpdatePos();
} }
xdir=from->xdir; ydir=from->ydir; xdir = from->xdir;
ydir = from->ydir;
} }
void C4Object::ForcePosition(C4Real tx, C4Real ty) void C4Object::ForcePosition(C4Real tx, C4Real ty)
{ {
fix_x=tx; fix_y=ty; fix_x = tx;
fix_y = ty;
UpdatePos(); UpdatePos();
UpdateSolidMask(false); UpdateSolidMask(false);
} }
@ -527,9 +671,9 @@ void C4Object::MovePosition(int32_t dx, int32_t dy)
void C4Object::MovePosition(C4Real dx, C4Real dy) void C4Object::MovePosition(C4Real dx, C4Real dy)
{ {
// move object position; repositions SolidMask // move object position; repositions SolidMask
if (pSolidMaskData) pSolidMaskData->Remove(true); RemoveSolidMask(true);
fix_x+=dx; fix_x += dx;
fix_y+=dy; fix_y += dy;
UpdatePos(); UpdatePos();
UpdateSolidMask(true); UpdateSolidMask(true);
} }
@ -549,7 +693,10 @@ bool C4Object::ExecMovement() // Every Tick1 by Execute
} }
// General mobility check // General mobility check
if (Category & C4D_StaticBack) return false; if (Category & C4D_StaticBack)
{
return false;
}
// Movement execution // Movement execution
if (Mobile) // Object is moving if (Mobile) // Object is moving
@ -557,9 +704,15 @@ bool C4Object::ExecMovement() // Every Tick1 by Execute
// Move object // Move object
DoMovement(); DoMovement();
// Demobilization check // Demobilization check
if ((xdir==0) && (ydir==0) && (rdir==0)) Mobile=false; if ((xdir == 0) && (ydir == 0) && (rdir == 0))
{
Mobile = false;
}
// Check for stabilization // Check for stabilization
if (rdir==0) Stabilize(); if (rdir == 0)
{
Stabilize();
}
} }
else // Object is static else // Object is static
{ {
@ -569,13 +722,16 @@ bool C4Object::ExecMovement() // Every Tick1 by Execute
if (!::Game.iTick10) if (!::Game.iTick10)
{ {
// Gravity mobilization // Gravity mobilization
xdir=ydir=rdir=0; xdir = ydir = rdir = 0;
Mobile=true; Mobile = true;
} }
} }
// Enforce zero rotation // Enforce zero rotation
if (!Def->Rotateable) fix_r=Fix0; if (!Def->Rotateable)
{
fix_r = Fix0;
}
// Out of bounds check // Out of bounds check
if ((!Inside<int32_t>(GetX() + Shape.GetX(), -Shape.Wdt, ::Landscape.GetWidth()) && !(GetPropertyInt(P_BorderBound) & C4D_Border_Sides)) if ((!Inside<int32_t>(GetX() + Shape.GetX(), -Shape.Wdt, ::Landscape.GetWidth()) && !(GetPropertyInt(P_BorderBound) & C4D_Border_Sides))
@ -593,9 +749,18 @@ bool C4Object::ExecMovement() // Every Tick1 by Execute
int parX, parY; int parX, parY;
GetParallaxity(&parX, &parY); GetParallaxity(&parX, &parY);
fRemove = false; fRemove = false;
if (GetX()>::Landscape.GetWidth() || GetY()>::Landscape.GetHeight()) fRemove = true; // except if they are really out of the viewport to the right... if (GetX() > ::Landscape.GetWidth() || GetY() > ::Landscape.GetHeight())
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 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
}
} }
if (fRemove) if (fRemove)
{ {
@ -611,41 +776,59 @@ bool SimFlight(C4Real &x, C4Real &y, C4Real &xdir, C4Real &ydir, int32_t iDensit
{ {
bool hitOnTime = true; bool hitOnTime = true;
bool fBreak = false; bool fBreak = false;
int32_t ctcox,ctcoy,cx,cy,i; int32_t ctcox, ctcoy;
cx = fixtoi(x); cy = fixtoi(y); int32_t cx = fixtoi(x);
i = iIter; int32_t cy = fixtoi(y);
int32_t i = iIter;
do do
{ {
if (!--i) {hitOnTime = false; break;} if (!--i)
{
hitOnTime = false;
break;
}
// If the object isn't moving and there is no gravity either, abort // If the object isn't moving and there is no gravity either, abort
if (xdir == 0 && ydir == 0 && GravAccel == 0) if (xdir == 0 && ydir == 0 && GravAccel == 0)
{
return false; return false;
}
// If the object is above the landscape flying upwards in no/negative gravity, abort // If the object is above the landscape flying upwards in no/negative gravity, abort
if (ydir <= 0 && GravAccel <= 0 && cy < 0) if (ydir <= 0 && GravAccel <= 0 && cy < 0)
{
return false; return false;
}
// Set target position by momentum // Set target position by momentum
x+=xdir; y+=ydir; x += xdir;
y += ydir;
// Movement to target // Movement to target
ctcox=fixtoi(x); ctcoy=fixtoi(y); ctcox = fixtoi(x);
ctcoy = fixtoi(y);
// Bounds // Bounds
if (!Inside<int32_t>(ctcox,0,::Landscape.GetWidth()) || (ctcoy>=::Landscape.GetHeight())) if (!Inside<int32_t>(ctcox, 0, ::Landscape.GetWidth()) || (ctcoy >= ::Landscape.GetHeight()))
{
return false; return false;
}
// Move to target // Move to target
do do
{ {
// Set next step target // Set next step target
cx+=Sign(ctcox-cx); cy+=Sign(ctcoy-cy); cx += Sign(ctcox - cx);
cy += Sign(ctcoy - cy);
// Contact check // Contact check
if (Inside(GBackDensity(cx,cy), iDensityMin, iDensityMax)) if (Inside(GBackDensity(cx, cy), iDensityMin, iDensityMax))
{ fBreak = true; break; } {
fBreak = true;
break;
}
} }
while ((cx!=ctcox) || (cy!=ctcoy)); while ((cx != ctcox) || (cy != ctcoy));
// Adjust GravAccel once per frame // Adjust GravAccel once per frame
ydir+=GravAccel; ydir += GravAccel;
} }
while (!fBreak); while (!fBreak);
// write position back // write position back
x = itofix(cx); y = itofix(cy); x = itofix(cx);
y = itofix(cy);
// how many steps did it take to get here? // how many steps did it take to get here?
iIter -= i; iIter -= i;
@ -656,13 +839,20 @@ bool SimFlight(C4Real &x, C4Real &y, C4Real &xdir, C4Real &ydir, int32_t iDensit
bool SimFlightHitsLiquid(C4Real fcx, C4Real fcy, C4Real xdir, C4Real ydir) bool SimFlightHitsLiquid(C4Real fcx, C4Real fcy, C4Real xdir, C4Real ydir)
{ {
// Start in water? // Start in water?
int temp;
if (DensityLiquid(GBackDensity(fixtoi(fcx), fixtoi(fcy)))) if (DensityLiquid(GBackDensity(fixtoi(fcx), fixtoi(fcy))))
if (!SimFlight(fcx, fcy, xdir, ydir, 0, C4M_Liquid - 1, temp=10)) {
int temp = 10;
if (!SimFlight(fcx, fcy, xdir, ydir, 0, C4M_Liquid - 1, temp))
{
return false; return false;
}
}
// Hits liquid? // Hits liquid?
if (!SimFlight(fcx, fcy, xdir, ydir, C4M_Liquid, 100, temp=-1)) int temp = -1;
if (!SimFlight(fcx, fcy, xdir, ydir, C4M_Liquid, 100, temp))
{
return false; return false;
}
// liquid & deep enough? // liquid & deep enough?
return GBackLiquid(fixtoi(fcx), fixtoi(fcy)) && GBackLiquid(fixtoi(fcx), fixtoi(fcy) + 9); return GBackLiquid(fixtoi(fcx), fixtoi(fcy)) && GBackLiquid(fixtoi(fcx), fixtoi(fcy) + 9);
} }

View File

@ -4125,7 +4125,7 @@ void C4Object::SetRotation(int32_t nr)
while (nr<0) nr+=360; while (nr<0) nr+=360;
nr%=360; nr%=360;
// remove solid mask // remove solid mask
if (pSolidMaskData) pSolidMaskData->Remove(false); RemoveSolidMask(false);
// set rotation // set rotation
fix_r=itofix(nr); fix_r=itofix(nr);
// Update face // Update face
@ -4156,6 +4156,14 @@ void C4Object::DrawSolidMask(C4TargetFacet &cgo) const
pSolidMaskData->Draw(cgo); pSolidMaskData->Draw(cgo);
} }
void C4Object::RemoveSolidMask(bool fBackupAttachment)
{
if (pSolidMaskData)
{
pSolidMaskData->Remove(fBackupAttachment);
}
}
void C4Object::UpdateSolidMask(bool fRestoreAttachedObjects) void C4Object::UpdateSolidMask(bool fRestoreAttachedObjects)
{ {
// solidmask doesn't make sense with non-existant objects // solidmask doesn't make sense with non-existant objects
@ -4172,7 +4180,9 @@ void C4Object::UpdateSolidMask(bool fRestoreAttachedObjects)
pSolidMaskData = new C4SolidMask(this); pSolidMaskData = new C4SolidMask(this);
} }
else else
{
pSolidMaskData->Remove(false); pSolidMaskData->Remove(false);
}
pSolidMaskData->Put(true, nullptr, fRestoreAttachedObjects); pSolidMaskData->Put(true, nullptr, fRestoreAttachedObjects);
SetHalfVehicleSolidMask(HalfVehicleSolidMask); SetHalfVehicleSolidMask(HalfVehicleSolidMask);
} }

View File

@ -102,6 +102,7 @@ class C4Object: public C4PropListNumbered
private: private:
void UpdateInMat(); void UpdateInMat();
void Splash(); void Splash();
void RemoveSolidMask(bool fBackupAttachment); // Remove solid mask data, if existing
public: public:
C4Object(); C4Object();
~C4Object() override; ~C4Object() override;