forked from Mirrors/openclonk
Don't rely on FIXED rounding for movement (#200)
There was an assumption that when rounding a FIXED to an integer A then adding 1 to the FIXED and rounding the result to an integer B then B=A+1. This is not true in one special case though: As rounding is done away from zero -0.5 is rounded to -1 and +0.5 is rounded to +1, breaking the assumption mentioned above.stable-5.1
parent
084992a605
commit
8ba23c3598
|
@ -133,14 +133,14 @@ void C4Object::DoMotion(int32_t mx, int32_t my)
|
|||
fix_x += mx; fix_y += my;
|
||||
}
|
||||
|
||||
static inline int32_t ForceLimits(int32_t &rVal, int32_t iLow, int32_t iHi)
|
||||
static inline int32_t ForceLimits(FIXED &rVal, int32_t iLow, int32_t iHi)
|
||||
{
|
||||
if (rVal<iLow) { rVal=iLow; return -1; }
|
||||
if (rVal>iHi) { rVal=iHi; return +1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
void C4Object::TargetBounds(int32_t &ctco, int32_t limit_low, int32_t limit_hi, int32_t cnat_low, int32_t cnat_hi)
|
||||
void C4Object::TargetBounds(FIXED &ctco, int32_t limit_low, int32_t limit_hi, int32_t cnat_low, int32_t cnat_hi)
|
||||
{
|
||||
switch (ForceLimits(ctco,limit_low,limit_hi))
|
||||
{
|
||||
|
@ -178,7 +178,7 @@ int32_t C4Object::ContactCheck(int32_t iAtX, int32_t iAtY)
|
|||
return Shape.ContactCount;
|
||||
}
|
||||
|
||||
void C4Object::SideBounds(int32_t &ctcox)
|
||||
void C4Object::SideBounds(FIXED &ctcox)
|
||||
{
|
||||
// layer bounds
|
||||
if (pLayer) if (pLayer->Def->BorderBound & C4D_Border_Layer)
|
||||
|
@ -194,7 +194,7 @@ void C4Object::SideBounds(int32_t &ctcox)
|
|||
TargetBounds(ctcox,0-Shape.GetX(),GBackWdt+Shape.GetX(),CNAT_Left,CNAT_Right);
|
||||
}
|
||||
|
||||
void C4Object::VerticalBounds(int32_t &ctcoy)
|
||||
void C4Object::VerticalBounds(FIXED &ctcoy)
|
||||
{
|
||||
// layer bounds
|
||||
if (pLayer) if (pLayer->Def->BorderBound & C4D_Border_Layer)
|
||||
|
@ -248,16 +248,17 @@ void C4Object::DoMovement()
|
|||
|
||||
FIXED new_x = fix_x + xdir;
|
||||
FIXED new_y = fix_y + ydir;
|
||||
ctcox=fixtoi(new_x);
|
||||
SideBounds(ctcox);
|
||||
SideBounds(new_x);
|
||||
ctcox = fixtoi(new_x);
|
||||
|
||||
if (!Action.t_attach) // Unattached movement = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
{
|
||||
// Horizontal movement - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Move to target
|
||||
while (GetX()!=ctcox)
|
||||
while (Abs<FIXED>(fix_x - ctcox) > FIXED10(5))
|
||||
{
|
||||
// Next step
|
||||
int step = Sign(ctcox - GetX());
|
||||
int step = Sign<FIXED>(new_x - fix_x);
|
||||
if ((iContact=ContactCheck(GetX() + step, GetY())))
|
||||
{
|
||||
fAnyContact=true; iContacts |= t_contact;
|
||||
|
@ -273,15 +274,15 @@ void C4Object::DoMovement()
|
|||
}
|
||||
// Vertical movement - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Movement target
|
||||
ctcoy=fixtoi(fix_y + ydir);
|
||||
new_y = fix_y + ydir;
|
||||
// Movement bounds (vertical)
|
||||
VerticalBounds(ctcoy);
|
||||
VerticalBounds(new_y);
|
||||
ctcoy=fixtoi(new_y);
|
||||
// Move to target
|
||||
while (GetY()!=ctcoy)
|
||||
while (Abs<FIXED>(fix_y - ctcoy) > FIXED10(5))
|
||||
{
|
||||
// Next step
|
||||
int step = Sign(ctcoy - GetY());
|
||||
int step = Sign<FIXED>(new_y - fix_y);
|
||||
if ((iContact=ContactCheck(GetX(), GetY() + step)))
|
||||
{
|
||||
fAnyContact=true; iContacts |= t_contact;
|
||||
|
@ -310,16 +311,18 @@ void C4Object::DoMovement()
|
|||
}
|
||||
if (Action.t_attach) // Attached movement = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
{
|
||||
VerticalBounds(new_y);
|
||||
ctcoy=fixtoi(new_y);
|
||||
VerticalBounds(ctcoy);
|
||||
bool at_xovr,at_yovr;
|
||||
// Move to target
|
||||
do
|
||||
{
|
||||
at_xovr=at_yovr=0;
|
||||
bool at_xovr = false, at_yovr = false;
|
||||
// Set next step target
|
||||
int step_x = Sign(ctcox-GetX());
|
||||
int step_y = Sign(ctcoy-GetY());
|
||||
int step_x = 0, step_y = 0;
|
||||
if(Abs<FIXED>(fix_x - ctcox) > FIXED10(5))
|
||||
step_x = Sign<FIXED>(new_x - fix_x);
|
||||
if(Abs<FIXED>(fix_y - ctcoy) > FIXED10(5))
|
||||
step_y = Sign<FIXED>(new_y - fix_y);
|
||||
int32_t ctx = GetX() + step_x; int32_t cty = GetY() + step_y;
|
||||
// Attachment check
|
||||
if (!Shape.Attach(ctx,cty,Action.t_attach))
|
||||
|
@ -343,7 +346,7 @@ void C4Object::DoMovement()
|
|||
if (at_xovr) { ctcox=GetX(); xdir=Fix0; new_x = fix_x; }
|
||||
if (at_yovr) { ctcoy=GetY(); ydir=Fix0; new_y = fix_y; }
|
||||
}
|
||||
while ((GetX()!=ctcox) || (GetY()!=ctcoy));
|
||||
while (Abs<FIXED>(fix_x - ctcox) > FIXED10(5) || Abs<FIXED>(fix_y - ctcoy) > FIXED10(5));
|
||||
}
|
||||
fix_x = new_x;
|
||||
fix_y = new_y;
|
||||
|
|
|
@ -1504,9 +1504,9 @@ bool C4Object::Exit(int32_t iX, int32_t iY, int32_t iR, FIXED iXDir, FIXED iYDir
|
|||
// No container
|
||||
Contained=NULL;
|
||||
// Position/motion
|
||||
BoundsCheck(iX, iY);
|
||||
r=iR;
|
||||
fix_x=itofix(iX); fix_y=itofix(iY); fix_r=itofix(r);
|
||||
BoundsCheck(fix_x, fix_y);
|
||||
xdir=iXDir; ydir=iYDir; rdir=iRDir;
|
||||
// Misc updates
|
||||
Mobile=1;
|
||||
|
|
|
@ -283,7 +283,7 @@ class C4Object: public C4PropList
|
|||
void AutoContextMenu(int32_t iMenuSelect);
|
||||
int32_t ContactCheck(int32_t atx, int32_t aty);
|
||||
bool Contact(int32_t cnat);
|
||||
void TargetBounds(int32_t &ctco, int32_t limit_low, int32_t limit_hi, int32_t cnat_low, int32_t cnat_hi);
|
||||
void TargetBounds(FIXED &ctco, int32_t limit_low, int32_t limit_hi, int32_t cnat_low, int32_t cnat_hi);
|
||||
enum { SAC_StartCall = 1, SAC_EndCall = 2, SAC_AbortCall = 4 };
|
||||
bool SetAction(C4PropList * Act, C4Object *pTarget=NULL, C4Object *pTarget2=NULL, int32_t iCalls = SAC_StartCall | SAC_AbortCall, bool fForce = false);
|
||||
bool SetActionByName(C4String * ActName, C4Object *pTarget=NULL, C4Object *pTarget2=NULL, int32_t iCalls = SAC_StartCall | SAC_AbortCall, bool fForce = false);
|
||||
|
@ -358,11 +358,11 @@ class C4Object: public C4PropList
|
|||
bool GetDragImage(C4Object **drag_object, C4ID *drag_id); // return true if object is draggable; assign drag_object/drag_id to gfx to be used for dragging
|
||||
|
||||
protected:
|
||||
void SideBounds(int32_t &ctcox); // apply bounds at side; regarding bourder bound and pLayer
|
||||
void VerticalBounds(int32_t &ctcoy); // apply bounds at top and bottom; regarding border bound and pLayer
|
||||
void SideBounds(FIXED &ctcox); // apply bounds at side; regarding bourder bound and pLayer
|
||||
void VerticalBounds(FIXED &ctcoy); // apply bounds at top and bottom; regarding border bound and pLayer
|
||||
|
||||
public:
|
||||
void BoundsCheck(int32_t &ctcox, int32_t &ctcoy) // do bound checks, correcting target positions as necessary and doing contact-calls
|
||||
void BoundsCheck(FIXED &ctcox, FIXED &ctcoy) // do bound checks, correcting target positions as necessary and doing contact-calls
|
||||
{ SideBounds(ctcox); VerticalBounds(ctcoy); }
|
||||
|
||||
public:
|
||||
|
|
|
@ -474,10 +474,10 @@ static C4Void FnSetPosition(C4AulObjectContext *cthr, long iX, long iY, bool fCh
|
|||
{
|
||||
if (fCheckBounds)
|
||||
{
|
||||
// BoundsCheck takes ref to int and not to long
|
||||
int32_t i_x = iX, i_y = iY;
|
||||
// BoundsCheck takes ref to FIXED and not to long
|
||||
FIXED i_x = itofix(iX), i_y = itofix(iY);
|
||||
cthr->Obj->BoundsCheck(i_x, i_y);
|
||||
iX = i_x; iY = i_y;
|
||||
iX = fixtoi(i_x); iY = fixtoi(i_y);
|
||||
}
|
||||
cthr->Obj->ForcePosition(iX,iY);
|
||||
// update liquid
|
||||
|
|
Loading…
Reference in New Issue