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
Armin Burgmeier 2010-03-18 18:05:02 +01:00
parent 084992a605
commit 8ba23c3598
4 changed files with 29 additions and 26 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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:

View File

@ -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