FT collision part2: added most of the FT collision system.

Aka "World's most expensive flippers".
This is an aggregate of collision-related changes made during 3DPB->FT transition.
The most important part is in flipper collision - a shift from monolithic iterative solver in TFlipperEdge::EdgeCollision to a distributed non-iterative solver.
Both 3DPB and FT data sets use FT collision, keeping two collision systems does not make much sense.
From user perspective, FT/3DPB systems should not have any major differences.
This commit is contained in:
Muzychenko Andrey 2023-03-04 17:31:23 +03:00
parent 466c875f8a
commit ba470e8727
16 changed files with 489 additions and 396 deletions

View File

@ -344,10 +344,11 @@ void DebugOverlay::DrawEdge(TEdgeSegment* edge)
auto flip = dynamic_cast<TFlipperEdge*>(edge); auto flip = dynamic_cast<TFlipperEdge*>(edge);
if (flip) if (flip)
{ {
flip->set_control_points(pb::time_now); if (flip->ControlPointDirtyFlag)
flip->set_control_points(flip->CurrentAngle);
DrawLineType(flip->lineA); DrawLineType(flip->LineA);
DrawLineType(flip->lineB); DrawLineType(flip->LineB);
DrawCicleType(flip->circlebase); DrawCicleType(flip->circlebase);
DrawCicleType(flip->circleT1); DrawCicleType(flip->circleT1);
} }

View File

@ -29,6 +29,7 @@ TBall::TBall(TPinballTable* table) : TPinballComponent(table, -1, false)
Direction.X = 0.0; Direction.X = 0.0;
Position.X = 0.0; Position.X = 0.0;
Position.Y = 0.0; Position.Y = 0.0;
HasGroupFlag = false;
ListBitmap = new std::vector<SpriteData>(); ListBitmap = new std::vector<SpriteData>();
@ -81,11 +82,19 @@ void TBall::Repaint()
void TBall::not_again(TEdgeSegment* edge) void TBall::not_again(TEdgeSegment* edge)
{ {
if (EdgeCollisionCount < 5) if (EdgeCollisionCount < 16)
{ {
Collisions[EdgeCollisionCount] = edge; Collisions[EdgeCollisionCount] = edge;
++EdgeCollisionCount; ++EdgeCollisionCount;
} }
else
{
for (int i = 0; i < 8; i++)
Collisions[i] = Collisions[i + 8];
Collisions[8] = edge;
EdgeCollisionCount = 9;
}
EdgeCollisionResetFlag = true;
} }
bool TBall::already_hit(TEdgeSegment* edge) bool TBall::already_hit(TEdgeSegment* edge)
@ -119,15 +128,15 @@ int TBall::Message(MessageCode code, float value)
return 0; return 0;
} }
void TBall::throw_ball(TBall* ball, vector3* direction, float angleMult, float speedMult1, float speedMult2) void TBall::throw_ball(vector3* direction, float angleMult, float speedMult1, float speedMult2)
{ {
ball->CollisionComp = nullptr; CollisionComp = nullptr;
ball->Direction = *direction; Direction = *direction;
float rnd = RandFloat(); float rnd = RandFloat();
float angle = (1.0f - (rnd + rnd)) * angleMult; float angle = (1.0f - (rnd + rnd)) * angleMult;
maths::RotateVector(ball->Direction, angle); maths::RotateVector(Direction, angle);
rnd = RandFloat(); rnd = RandFloat();
ball->Speed = (1.0f - (rnd + rnd)) * (speedMult1 * speedMult2) + speedMult1; Speed = (1.0f - (rnd + rnd)) * (speedMult1 * speedMult2) + speedMult1;
} }
vector2 TBall::get_coordinates() vector2 TBall::get_coordinates()
@ -138,5 +147,6 @@ vector2 TBall::get_coordinates()
void TBall::Disable() void TBall::Disable()
{ {
ActiveFlag = false; ActiveFlag = false;
AsEdgeCollisionFlag = true;
SpriteSet(-1); SpriteSet(-1);
} }

View File

@ -15,11 +15,10 @@ public :
int Message(MessageCode code, float value) override; int Message(MessageCode code, float value) override;
vector2 get_coordinates() override; vector2 get_coordinates() override;
void Disable(); void Disable();
void throw_ball(vector3* direction, float angleMult, float speedMult1, float speedMult2);
static void throw_ball(TBall* ball, vector3* direction, float angleMult, float speedMult1,
float speedMult2);
vector3 Position{}; vector3 Position{};
vector3 PrevPosition{};
vector3 Direction{}; vector3 Direction{};
float Speed; float Speed;
float RayMaxDistance; float RayMaxDistance;
@ -28,10 +27,15 @@ public :
vector2 RampFieldForce{}; vector2 RampFieldForce{};
TCollisionComponent* CollisionComp; TCollisionComponent* CollisionComp;
int CollisionMask; int CollisionMask;
TEdgeSegment* Collisions[5]{}; TEdgeSegment* Collisions[16]{};
int EdgeCollisionCount; int EdgeCollisionCount;
bool EdgeCollisionResetFlag{};
vector3 CollisionOffset{}; vector3 CollisionOffset{};
int CollisionFlag; int CollisionFlag;
float Offset; float Offset;
bool HasGroupFlag;
int SomeCounter1 = 0;
int time_ticks1{}, time_ticks2{};
float VisualZArray[50]{}; float VisualZArray[50]{};
bool AsEdgeCollisionFlag{};
}; };

View File

@ -6,6 +6,7 @@
#include "loader.h" #include "loader.h"
#include "pb.h" #include "pb.h"
#include "render.h" #include "render.h"
#include "TBall.h"
#include "TFlipperEdge.h" #include "TFlipperEdge.h"
#include "timer.h" #include "timer.h"
#include "TPinballTable.h" #include "TPinballTable.h"
@ -84,15 +85,17 @@ int TFlipper::Message(MessageCode code, float value)
code = MessageCode::TFlipperRetract; code = MessageCode::TFlipperRetract;
} }
MessageField = FlipperEdge->SetMotion(code, value); MessageField = FlipperEdge->SetMotion(code);
break; break;
case MessageCode::PlayerChanged: case MessageCode::PlayerChanged:
case MessageCode::Reset: case MessageCode::Reset:
if (MessageField) if (MessageField)
{ {
FlipperEdge->CurrentAngle = 0;
FlipperEdge->set_control_points(0);
MessageField = 0; MessageField = 0;
FlipperEdge->SetMotion(MessageCode::Reset, value); FlipperEdge->SetMotion(MessageCode::Reset);
UpdateSprite(0); UpdateSprite();
} }
break; break;
default: break; default: break;
@ -110,11 +113,11 @@ void TFlipper::Collision(TBall* ball, vector2* nextPosition, vector2* direction,
{ {
} }
void TFlipper::UpdateSprite(float timeNow) void TFlipper::UpdateSprite()
{ {
int bmpCountSub1 = ListBitmap->size() - 1; int bmpCountSub1 = ListBitmap->size() - 1;
auto newBmpIndex = static_cast<int>(floor(FlipperEdge->flipper_angle(timeNow) / FlipperEdge->AngleMax * bmpCountSub1 + 0.5f)); auto newBmpIndex = static_cast<int>(floor(FlipperEdge->CurrentAngle / FlipperEdge->AngleMax * bmpCountSub1 + 0.5f));
newBmpIndex = Clamp(newBmpIndex, 0, bmpCountSub1); newBmpIndex = Clamp(newBmpIndex, 0, bmpCountSub1);
if (BmpIndex == newBmpIndex) if (BmpIndex == newBmpIndex)
return; return;
@ -122,3 +125,87 @@ void TFlipper::UpdateSprite(float timeNow)
BmpIndex = newBmpIndex; BmpIndex = newBmpIndex;
SpriteSet(BmpIndex); SpriteSet(BmpIndex);
} }
int TFlipper::GetFlipperAngleDistance(float dt, float* dst) const
{
if (!MessageField)
return 0;
auto deltaAngle = FlipperEdge->flipper_angle_delta(dt);
auto distance = std::fabs(std::ceil(FlipperEdge->DistanceDiv * deltaAngle * FlipperEdge->InvT1Radius));
if (distance > 3.0f)
distance = 3.0f;
if (distance >= 2.0f)
{
*dst = deltaAngle / distance;
return static_cast<int>(distance);
}
*dst = deltaAngle;
return 1;
}
void TFlipper::FlipperCollision(float deltaAngle)
{
if (!MessageField)
return;
ray_type ray{}, rayDst{};
ray.MinDistance = 0.002f;
auto deltaAngleNeg = -deltaAngle;
bool collisionFlag = false;
for (auto ball : pb::MainTable->BallList)
{
if ((FlipperEdge->CollisionGroup & ball->CollisionMask) != 0 &&
FlipperEdge->YMax >= ball->Position.Y && FlipperEdge->YMin <= ball->Position.Y &&
FlipperEdge->XMax >= ball->Position.X && FlipperEdge->XMin <= ball->Position.X)
{
if (FlipperEdge->ControlPointDirtyFlag)
FlipperEdge->set_control_points(FlipperEdge->CurrentAngle);
ray.CollisionMask = ball->CollisionMask;
ray.Origin = ball->Position;
float sin, cos;
auto ballPosRot = ray.Origin;
maths::SinCos(deltaAngleNeg, sin, cos);
maths::RotatePt(ballPosRot, sin, cos, FlipperEdge->RotOrigin);
ray.Direction.X = ballPosRot.X - ray.Origin.X;
ray.Direction.Y = ballPosRot.Y - ray.Origin.Y;
ray.MaxDistance = maths::normalize_2d(ray.Direction);
auto distance = maths::distance_to_flipper(FlipperEdge, ray, rayDst);
if (distance < 1e9f)
{
FlipperEdge->NextBallPosition = ball->Position;
FlipperEdge->CollisionDirection = rayDst.Direction;
FlipperEdge->EdgeCollision(ball, distance);
collisionFlag = true;
}
}
}
if (collisionFlag)
{
auto angleAdvance = deltaAngle / (std::fabs(FlipperEdge->MoveSpeed) * 5.0f);
FlipperEdge->CurrentAngle -= angleAdvance;
FlipperEdge->AngleRemainder += std::fabs(angleAdvance);
if (FlipperEdge->AngleRemainder <= 0.0001f)
{
FlipperEdge->CurrentAngle = FlipperEdge->AngleDst;
FlipperEdge->FlipperFlag = MessageCode::TFlipperNull;
MessageField = 0;
}
FlipperEdge->ControlPointDirtyFlag = true;
}
else
{
FlipperEdge->CurrentAngle += deltaAngle;
FlipperEdge->AngleRemainder -= std::fabs(deltaAngle);
if (FlipperEdge->AngleRemainder <= 0.0001f)
{
FlipperEdge->CurrentAngle = FlipperEdge->AngleDst;
FlipperEdge->FlipperFlag = MessageCode::TFlipperNull;
MessageField = 0;
}
FlipperEdge->ControlPointDirtyFlag = true;
}
}

View File

@ -13,7 +13,9 @@ public:
void port_draw() override; void port_draw() override;
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance, void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
TEdgeSegment* edge) override; TEdgeSegment* edge) override;
void UpdateSprite(float timeNow); void UpdateSprite();
int GetFlipperAngleDistance(float dt, float* dst) const;
void FlipperCollision(float deltaAngle);
int BmpIndex; int BmpIndex;
TFlipperEdge* FlipperEdge; TFlipperEdge* FlipperEdge;

View File

@ -8,9 +8,11 @@
#include "TTableLayer.h" #include "TTableLayer.h"
TFlipperEdge::TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup, TPinballTable* table, TFlipperEdge::TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup,
vector3* origin, vector3* vecT1, vector3* vecT2, float extendTime, float retractTime, TPinballTable* table,
float collMult, float elasticity, float smoothness): TEdgeSegment(collComp, activeFlag, collisionGroup) vector3* origin, vector3* vecT1, vector3* vecT2, float extendSpeed, float retractSpeed,
float collMult, float elasticity, float smoothness): TEdgeSegment(
collComp, activeFlag, collisionGroup)
{ {
vector3 crossProd{}, vecOriginT1{}, vecOriginT2{}; vector3 crossProd{}, vecOriginT1{}, vecOriginT2{};
@ -52,22 +54,22 @@ TFlipperEdge::TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsi
// 3DPB and FT have different formats for flipper speed: // 3DPB and FT have different formats for flipper speed:
// 3DPB: Time it takes for flipper to go from source to destination, in sec. // 3DPB: Time it takes for flipper to go from source to destination, in sec.
// FT: Flipper movement speed, in radians per sec. // FT: Flipper movement speed, in radians per sec.
if (pb::FullTiltMode) if (!pb::FullTiltMode)
{ {
auto angleMax = std::abs(AngleMax); auto angleMax = std::abs(AngleMax);
retractTime = angleMax / retractTime; retractSpeed = angleMax / retractSpeed;
extendTime = angleMax / extendTime; extendSpeed = angleMax / extendSpeed;
} }
ExtendTime = extendTime; ExtendSpeed = extendSpeed;
RetractTime = retractTime; RetractSpeed = retractSpeed;
const vector2 perpOriginT1Cc = { -vecOriginT1.Y , vecOriginT1.X }; const vector2 perpOriginT1Cc = {-vecOriginT1.Y, vecOriginT1.X};
A2Src.X = perpOriginT1Cc.X * CirclebaseRadius + origin->X; A2Src.X = perpOriginT1Cc.X * CirclebaseRadius + origin->X;
A2Src.Y = perpOriginT1Cc.Y * CirclebaseRadius + origin->Y; A2Src.Y = perpOriginT1Cc.Y * CirclebaseRadius + origin->Y;
A1Src.X = perpOriginT1Cc.X * CircleT1Radius + vecT1->X; A1Src.X = perpOriginT1Cc.X * CircleT1Radius + vecT1->X;
A1Src.Y = perpOriginT1Cc.Y * CircleT1Radius + vecT1->Y; A1Src.Y = perpOriginT1Cc.Y * CircleT1Radius + vecT1->Y;
const vector2 perpOriginT1C = { vecOriginT1.Y , -vecOriginT1.X }; const vector2 perpOriginT1C = {vecOriginT1.Y, -vecOriginT1.X};
B1Src.X = perpOriginT1C.X * CirclebaseRadius + origin->X; B1Src.X = perpOriginT1C.X * CirclebaseRadius + origin->X;
B1Src.Y = perpOriginT1C.Y * CirclebaseRadius + origin->Y; B1Src.Y = perpOriginT1C.Y * CirclebaseRadius + origin->Y;
B2Src.X = perpOriginT1C.X * CircleT1Radius + vecT1->X; B2Src.X = perpOriginT1C.X * CircleT1Radius + vecT1->X;
@ -82,249 +84,94 @@ TFlipperEdge::TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsi
auto dx = vecT1->X - RotOrigin.X; auto dx = vecT1->X - RotOrigin.X;
auto dy = vecT1->Y - RotOrigin.Y; auto dy = vecT1->Y - RotOrigin.Y;
auto distance1 = sqrt(dy * dy + dx * dx) + table->CollisionCompOffset + vecT1->Z; auto distance1 = sqrt(dy * dy + dx * dx) + table->CollisionCompOffset + vecT1->Z;
DistanceDiv = distance1;
DistanceDivSq = distance1 * distance1; DistanceDivSq = distance1 * distance1;
InvT1Radius = 1.0f / CircleT1Radius * 1.5f;
float minMoveTime = std::min(ExtendTime, RetractTime); if (AngleMax <= 0.0f)
auto distance = maths::Distance(*vecT1, *vecT2); {
CollisionTimeAdvance = minMoveTime / (distance / CircleT1Radius + distance / CircleT1Radius); ExtendSpeed = -ExtendSpeed;
}
EdgeCollisionFlag = 0; else
InputTime = 0.0; {
CollisionFlag1 = 0; RetractSpeed = -RetractSpeed;
AngleStopTime = 0.0; }
AngleAdvanceTime = 0.0; set_control_points(CurrentAngle);
} }
void TFlipperEdge::port_draw() void TFlipperEdge::port_draw()
{ {
set_control_points(InputTime); set_control_points(CurrentAngle);
} }
float TFlipperEdge::FindCollisionDistance(ray_type* ray) float TFlipperEdge::FindCollisionDistance(ray_type* ray)
{ {
auto ogRay = ray; ray_type dstRay{};
ray_type dstRay{}, srcRay{}; if (ControlPointDirtyFlag)
set_control_points(CurrentAngle);
auto distance = maths::distance_to_flipper(this, *ray, dstRay);
if (distance >= 1e9f)
return 1e9f;
if (ogRay->TimeNow > AngleStopTime) NextBallPosition = dstRay.Origin;
{ CollisionDirection = dstRay.Direction;
FlipperFlag = MessageCode::TFlipperNull; return distance;
}
if (EdgeCollisionFlag == 0)
{
if (FlipperFlag == MessageCode::TFlipperNull)
{
CollisionFlag1 = 0;
CollisionFlag2 = 0;
set_control_points(ogRay->TimeNow);
auto ballInside = is_ball_inside(ogRay->Origin.X, ogRay->Origin.Y);
srcRay.MinDistance = ogRay->MinDistance;
if (ballInside == 0)
{
srcRay.Direction = ogRay->Direction;
srcRay.MaxDistance = ogRay->MaxDistance;
srcRay.Origin = ogRay->Origin;
auto distance = maths::distance_to_flipper(this, srcRay, dstRay);
if (distance == 0.0f)
{
NextBallPosition = dstRay.Origin;
NextBallPosition.X -= srcRay.Direction.X * 1e-05f;
NextBallPosition.Y -= srcRay.Direction.Y * 1e-05f;
}
else
{
NextBallPosition = dstRay.Origin;
}
CollisionDirection = dstRay.Direction;
return distance;
}
if (maths::Distance_Squared(ogRay->Origin, RotOrigin) >= CirclebaseRadiusMSq)
{
if (maths::Distance_Squared(ogRay->Origin, T1) >= CircleT1RadiusMSq)
{
srcRay.Direction.Y = lineB.PerpendicularC.Y;
srcRay.Direction.X = lineB.PerpendicularC.X;
if (ballInside == 4)
{
srcRay.Direction.Y = lineA.PerpendicularC.Y;
srcRay.Direction.X = lineA.PerpendicularC.X;
}
srcRay.Direction.X = -srcRay.Direction.X;
srcRay.Direction.Y = -srcRay.Direction.Y;
}
else
{
srcRay.Direction.X = T1.X - ogRay->Origin.X;
srcRay.Direction.Y = T1.Y - ogRay->Origin.Y;
maths::normalize_2d(srcRay.Direction);
}
}
else
{
srcRay.Direction.X = RotOrigin.X - ogRay->Origin.X;
srcRay.Direction.Y = RotOrigin.Y - ogRay->Origin.Y;
maths::normalize_2d(srcRay.Direction);
}
srcRay.Origin.X = ogRay->Origin.X - srcRay.Direction.X * 5.0f;
srcRay.Origin.Y = ogRay->Origin.Y - srcRay.Direction.Y * 5.0f;
srcRay.MaxDistance = ogRay->MaxDistance + 10.0f;
if (maths::distance_to_flipper(this, srcRay, dstRay) >= 1e+09f)
{
srcRay.Direction.X = RotOrigin.X - ogRay->Origin.X;
srcRay.Direction.Y = RotOrigin.Y - ogRay->Origin.Y;
maths::normalize_2d(srcRay.Direction);
srcRay.Origin.X = ogRay->Origin.X - srcRay.Direction.X * 5.0f;
srcRay.Origin.Y = ogRay->Origin.Y - srcRay.Direction.Y * 5.0f;
if (maths::distance_to_flipper(this, srcRay, dstRay) >= 1e+09f)
{
return 1e+09;
}
}
NextBallPosition = dstRay.Origin;
CollisionDirection = dstRay.Direction;
NextBallPosition.X -= srcRay.Direction.X * 1e-05f;
NextBallPosition.Y -= srcRay.Direction.Y * 1e-05f;
return 0.0;
}
auto posX = ogRay->Origin.X;
auto posY = ogRay->Origin.Y;
auto posXAdvance = ogRay->Direction.X * CollisionTimeAdvance;
auto posYAdvance = ogRay->Direction.Y * CollisionTimeAdvance;
auto rayMaxDistance = ogRay->MaxDistance * CollisionTimeAdvance;
auto timeNow = ogRay->TimeNow;
auto stopTime = ogRay->TimeDelta + ogRay->TimeNow;
while (timeNow < stopTime)
{
set_control_points(timeNow);
auto ballInside = is_ball_inside(posX, posY);
if (ballInside != 0)
{
vector2* linePtr;
if (FlipperFlag == MessageCode::TFlipperExtend && ballInside != 5)
{
linePtr = &lineA.PerpendicularC;
srcRay.Direction.Y = lineA.PerpendicularC.Y;
srcRay.Direction.X = lineA.PerpendicularC.X;
}
else
{
if (FlipperFlag != MessageCode::TFlipperRetract || ballInside == 4)
{
CollisionFlag1 = 0;
CollisionFlag2 = 1;
srcRay.Direction.X = RotOrigin.X - posX;
srcRay.Direction.Y = RotOrigin.Y - posY;
maths::normalize_2d(srcRay.Direction);
srcRay.Origin.X = posX - srcRay.Direction.X * 5.0f;
srcRay.Origin.Y = posY - srcRay.Direction.Y * 5.0f;
srcRay.MaxDistance = ogRay->MaxDistance + 10.0f;
if (maths::distance_to_flipper(this, srcRay, dstRay) >= 1e+09f)
{
NextBallPosition.X = posX;
NextBallPosition.Y = posY;
CollisionDirection.X = -srcRay.Direction.X;
CollisionDirection.Y = -srcRay.Direction.Y;
return 0.0;
}
NextBallPosition = dstRay.Origin;
CollisionDirection = dstRay.Direction;
NextBallPosition.X -= srcRay.Direction.X * 1e-05f;
NextBallPosition.Y -= srcRay.Direction.Y * 1e-05f;
return 0.0;
}
linePtr = &lineB.PerpendicularC;
srcRay.Direction.Y = lineB.PerpendicularC.Y;
srcRay.Direction.X = lineB.PerpendicularC.X;
}
CollisionLinePerp = *linePtr;
CollisionFlag2 = 0;
CollisionFlag1 = 1;
srcRay.Direction.X = -srcRay.Direction.X;
srcRay.Direction.Y = -srcRay.Direction.Y;
srcRay.MinDistance = 0.002f;
srcRay.Origin.X = ogRay->Origin.X - srcRay.Direction.X * 5.0f;
srcRay.Origin.Y = ogRay->Origin.Y - srcRay.Direction.Y * 5.0f;
srcRay.MaxDistance = ogRay->MaxDistance + 10.0f;
auto distance = maths::distance_to_flipper(this, srcRay, dstRay);
CollisionDirection = dstRay.Direction;
if (distance >= 1e+09f)
{
return 1e+09;
}
NextBallPosition = dstRay.Origin;
NextBallPosition.X -= srcRay.Direction.X * 1e-05f;
NextBallPosition.Y -= srcRay.Direction.Y * 1e-05f;
return 0.0;
}
srcRay.Direction = ogRay->Direction;
srcRay.MinDistance = ogRay->MinDistance;
srcRay.Origin = ogRay->Origin;
srcRay.MaxDistance = rayMaxDistance;
auto distance = maths::distance_to_flipper(this, srcRay, dstRay);
if (distance < 1e+09f)
{
NextBallPosition = dstRay.Origin;
NextBallPosition.X -= srcRay.Direction.X * 1e-05f;
NextBallPosition.Y -= srcRay.Direction.Y * 1e-05f;
vector2* linePtr;
if (FlipperFlag == MessageCode::TFlipperRetract)
{
linePtr = &lineB.PerpendicularC;
CollisionFlag1 = AngleMax <= 0.0f;
}
else
{
CollisionFlag1 = AngleMax > 0.0f;
linePtr = &lineA.PerpendicularC;
}
CollisionLinePerp = *linePtr;
CollisionDirection = dstRay.Direction;
return distance;
}
timeNow = timeNow + CollisionTimeAdvance;
posX = posX + posXAdvance;
posY = posY + posYAdvance;
}
}
else
{
EdgeCollisionFlag = 0;
}
return 1e+09;
} }
void TFlipperEdge::EdgeCollision(TBall* ball, float distance) void TFlipperEdge::EdgeCollision(TBall* ball, float distance)
{ {
EdgeCollisionFlag = 1; if (FlipperFlag == MessageCode::TFlipperNull)
if (FlipperFlag == MessageCode::TFlipperNull || !CollisionFlag2 || CollisionFlag1)
{ {
float boost = 0.0; maths::basic_collision(
if (CollisionFlag1) ball,
&NextBallPosition,
&CollisionDirection,
Elasticity,
Smoothness,
1e9f,
0);
return;
}
auto someProduct = (NextBallPosition.Y - T1.Y) * (RotOrigin.X - T1.X) -
(NextBallPosition.X - T1.X) * (RotOrigin.Y - T1.Y);
bool someFlag = false;
if (someProduct <= 0)
{
if (AngleMax > 0)
someFlag = true;
}
else if (AngleMax <= 0)
{
someFlag = true;
}
if (FlipperFlag == MessageCode::TFlipperRetract)
{
someFlag ^= true;
CollisionLinePerp = LineB.PerpendicularC;
}
else
{
CollisionLinePerp = LineA.PerpendicularC;
}
auto dx = NextBallPosition.X - RotOrigin.X;
auto dy = NextBallPosition.Y - RotOrigin.Y;
auto distanceSq = dy * dy + dx * dx;
if (someFlag)
{
float boost = 0;
if (circlebase.RadiusSq * 1.01f < distanceSq)
{ {
float dx = NextBallPosition.X - RotOrigin.X; auto v21 = std::fabs(MoveSpeed) * std::sqrt(distanceSq / DistanceDivSq);
float dy = NextBallPosition.Y - RotOrigin.Y; auto dot1 = maths::DotProduct(CollisionLinePerp, CollisionDirection);
float distanceSq = dy * dy + dx * dx; if (dot1 >= 0)
if (circlebase.RadiusSq * 1.01f < distanceSq) boost = CollisionMult * dot1 * v21;
{
float v11;
float v20 = sqrt(distanceSq / DistanceDivSq) * (fabs(AngleMax) / AngleAdvanceTime);
float dot1 = maths::DotProduct(CollisionLinePerp, CollisionDirection);
if (dot1 >= 0.0f)
v11 = dot1 * v20;
else
v11 = 0.0;
boost = v11 * CollisionMult;
}
} }
float threshold = boost <= 0.0f ? 1000000000.0f : -1.0f; auto threshold = boost <= 0.0f ? 1e9f : -1.0f;
maths::basic_collision( maths::basic_collision(
ball, ball,
&NextBallPosition, &NextBallPosition,
@ -333,18 +180,14 @@ void TFlipperEdge::EdgeCollision(TBall* ball, float distance)
Smoothness, Smoothness,
threshold, threshold,
boost); boost);
return;
} }
float elasticity;
float dx = NextBallPosition.X - RotOrigin.X;
float dy = NextBallPosition.Y - RotOrigin.Y;
float distanceSq = dy * dy + dx * dx;
if (circlebase.RadiusSq * 1.01f < distanceSq)
elasticity = (1.0f - sqrt(distanceSq / DistanceDivSq)) * Elasticity;
else else
elasticity = Elasticity; {
maths::basic_collision(ball, &NextBallPosition, &CollisionDirection, elasticity, Smoothness, 1000000000.0, 0.0); auto elasticity = Elasticity;
if (circlebase.RadiusSq * 1.01f < distanceSq)
elasticity = (1.0f - std::sqrt(distanceSq / DistanceDivSq)) * Elasticity;
maths::basic_collision(ball, &NextBallPosition, &CollisionDirection, elasticity, Smoothness, 1e9f, 0.0);
}
} }
void TFlipperEdge::place_in_grid(RectF* aabb) void TFlipperEdge::place_in_grid(RectF* aabb)
@ -360,12 +203,18 @@ void TFlipperEdge::place_in_grid(RectF* aabb)
} }
TTableLayer::edges_insert_square(yMin, xMin, yMax, xMax, this, nullptr); TTableLayer::edges_insert_square(yMin, xMin, yMax, xMax, this, nullptr);
auto offset = 1.0f / InvT1Radius + pb::ball_min_smth;
XMin = xMin - offset;
YMin = yMin - offset;
XMax = xMax + offset;
YMax = yMax + offset;
} }
void TFlipperEdge::set_control_points(float timeNow) void TFlipperEdge::set_control_points(float angle)
{ {
float sin, cos; float sin, cos;
maths::SinCos(flipper_angle(timeNow), sin, cos); maths::SinCos(angle, sin, cos);
A1 = A1Src; A1 = A1Src;
A2 = A2Src; A2 = A2Src;
B1 = B1Src; B1 = B1Src;
@ -376,83 +225,48 @@ void TFlipperEdge::set_control_points(float timeNow)
maths::RotatePt(T1, sin, cos, RotOrigin); maths::RotatePt(T1, sin, cos, RotOrigin);
maths::RotatePt(B1, sin, cos, RotOrigin); maths::RotatePt(B1, sin, cos, RotOrigin);
maths::RotatePt(B2, sin, cos, RotOrigin); maths::RotatePt(B2, sin, cos, RotOrigin);
maths::line_init(lineA, A1.X, A1.Y, A2.X, A2.Y); maths::line_init(LineA, A1.X, A1.Y, A2.X, A2.Y);
maths::line_init(lineB, B1.X, B1.Y, B2.X, B2.Y); maths::line_init(LineB, B1.X, B1.Y, B2.X, B2.Y);
circlebase = {RotOrigin, CirclebaseRadiusSq}; circlebase = {RotOrigin, CirclebaseRadiusSq};
circleT1 = {T1, CircleT1RadiusSq}; circleT1 = {T1, CircleT1RadiusSq};
ControlPointDirtyFlag = false;
} }
float TFlipperEdge::flipper_angle(float timeNow) float TFlipperEdge::flipper_angle_delta(float timeDelta)
{ {
// When not moving, flipper is at destination angle.
if (FlipperFlag == MessageCode::TFlipperNull) if (FlipperFlag == MessageCode::TFlipperNull)
return AngleDst; return 0.0f;
// How much time it takes to go from source to destination angle, in sec. const auto deltaAngle = MoveSpeed * timeDelta;
auto arcDuration = std::abs((AngleDst - AngleSrc) / AngleMax * AngleAdvanceTime); if (std::fabs(deltaAngle) > AngleRemainder)
return AngleDst - CurrentAngle;
// How close the flipper is to destination, in [0, 1] range. return deltaAngle;
auto t = arcDuration >= 0.0000001f ? (timeNow - InputTime) / arcDuration : 1.0f;
t = Clamp(t, 0.0f, 1.0f);
// Result = linear interpolation between source and destination angle.
return AngleSrc + t * (AngleDst - AngleSrc);
} }
int TFlipperEdge::is_ball_inside(float x, float y) int TFlipperEdge::SetMotion(MessageCode code)
{
vector2 testPoint{};
float dx = RotOrigin.X - x;
float dy = RotOrigin.Y - y;
if (((A2.X - A1.X) * (y - A1.Y) - (A2.Y - A1.Y) * (x - A1.X) >= 0.0f &&
(B1.X - A2.X) * (y - A2.Y) - (B1.Y - A2.Y) * (x - A2.X) >= 0.0f &&
(B2.X - B1.X) * (y - B1.Y) - (B2.Y - B1.Y) * (x - B1.X) >= 0.0f &&
(A1.X - B2.X) * (y - B2.Y) - (A1.Y - B2.Y) * (x - B2.X) >= 0.0f) ||
dy * dy + dx * dx <= CirclebaseRadiusSq ||
(T1.Y - y) * (T1.Y - y) + (T1.X - x) * (T1.X - x) < CircleT1RadiusSq)
{
float flipperLR = AngleMax < 0.0f ? -1.0f : 1.0f;
if (FlipperFlag == MessageCode::TFlipperExtend)
testPoint = AngleMax < 0.0f ? B1 : B2;
else if (FlipperFlag == MessageCode::TFlipperRetract)
testPoint = AngleMax < 0.0f ? A2 : A1;
else
testPoint = T1;
if (((y - testPoint.Y) * (RotOrigin.X - testPoint.X) -
(x - testPoint.X) * (RotOrigin.Y - testPoint.Y)) * flipperLR < 0.0f)
return 4;
return 5;
}
return 0;
}
int TFlipperEdge::SetMotion(MessageCode code, float value)
{ {
switch (code) switch (code)
{ {
case MessageCode::TFlipperExtend: case MessageCode::TFlipperExtend:
AngleSrc = flipper_angle(value); AngleRemainder = std::fabs(AngleMax - CurrentAngle);
AngleDst = AngleMax; AngleDst = AngleMax;
AngleAdvanceTime = ExtendTime; MoveSpeed = ExtendSpeed;
break; break;
case MessageCode::TFlipperRetract: case MessageCode::TFlipperRetract:
AngleSrc = flipper_angle(value); AngleRemainder = std::fabs(CurrentAngle);
AngleDst = 0.0f; AngleDst = 0.0f;
AngleAdvanceTime = RetractTime; MoveSpeed = RetractSpeed;
break; break;
case MessageCode::Reset: case MessageCode::Reset:
AngleSrc = 0.0f; AngleRemainder = 0.0f;
AngleDst = 0.0f; AngleDst = 0.0f;
break; break;
default: break; default: break;
} }
if (AngleSrc == AngleDst) if (AngleRemainder == 0.0f)
code = MessageCode::TFlipperNull; code = MessageCode::TFlipperNull;
InputTime = value;
FlipperFlag = code; FlipperFlag = code;
AngleStopTime = AngleAdvanceTime + InputTime;
return static_cast<int>(code); return static_cast<int>(code);
} }

View File

@ -9,16 +9,15 @@ class TFlipperEdge : public TEdgeSegment
{ {
public: public:
TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup, TPinballTable* table, TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup, TPinballTable* table,
vector3* origin, vector3* vecT1, vector3* vecT2, float extendTime, float retractTime, float collMult, vector3* origin, vector3* vecT1, vector3* vecT2, float extendSpeed, float retractSpeed, float collMult,
float elasticity, float smoothness); float elasticity, float smoothness);
void port_draw() override; void port_draw() override;
float FindCollisionDistance(ray_type* ray) override; float FindCollisionDistance(ray_type* ray) override;
void EdgeCollision(TBall* ball, float distance) override; void EdgeCollision(TBall* ball, float distance) override;
void place_in_grid(RectF* aabb) override; void place_in_grid(RectF* aabb) override;
void set_control_points(float timeNow); void set_control_points(float angle);
float flipper_angle(float timeNow); float flipper_angle_delta(float timeDelta);
int is_ball_inside(float x, float y); int SetMotion(MessageCode code);
int SetMotion(MessageCode code, float value);
MessageCode FlipperFlag{}; MessageCode FlipperFlag{};
float Elasticity; float Elasticity;
@ -31,10 +30,9 @@ public:
float CirclebaseRadiusMSq; float CirclebaseRadiusMSq;
float CircleT1RadiusMSq; float CircleT1RadiusMSq;
float AngleMax; float AngleMax;
float AngleSrc{}; float AngleRemainder{};
float AngleDst; float AngleDst;
int CollisionFlag1; float CurrentAngle{};
int CollisionFlag2{};
vector2 CollisionLinePerp{}; vector2 CollisionLinePerp{};
vector2 A1Src{}; vector2 A1Src{};
vector2 A2Src{}; vector2 A2Src{};
@ -43,17 +41,16 @@ public:
float CollisionMult; float CollisionMult;
vector2 T1Src{}; vector2 T1Src{};
vector2 T2Src{}; vector2 T2Src{};
float DistanceDivSq; float DistanceDiv, DistanceDivSq;
float CollisionTimeAdvance;
vector2 CollisionDirection{}; vector2 CollisionDirection{};
int EdgeCollisionFlag; float ExtendSpeed;
float InputTime; float RetractSpeed;
float AngleStopTime; float MoveSpeed;
float AngleAdvanceTime;
float ExtendTime;
float RetractTime;
vector2 NextBallPosition{}; vector2 NextBallPosition{};
vector2 A1, A2, B1, B2, T1; vector2 A1, A2, B1, B2, T1;
line_type lineA, lineB; line_type LineA, LineB;
circle_type circlebase, circleT1; circle_type circlebase, circleT1;
float InvT1Radius;
float YMin, YMax, XMin, XMax;
bool ControlPointDirtyFlag{};
}; };

View File

@ -80,6 +80,7 @@ void THole::Collision(TBall* ball, vector2* nextPosition, vector2* direction, fl
ball->Position.X = Circle.Center.X; ball->Position.X = Circle.Center.X;
ball->Position.Y = Circle.Center.Y; ball->Position.Y = Circle.Center.Y;
ball->Direction.Z = 0.0; ball->Direction.Z = 0.0;
ball->AsEdgeCollisionFlag = true;
// Ramp hole has no delay in FT. // Ramp hole has no delay in FT.
auto captureTime = pb::FullTiltMode ? 0 : 0.5f; auto captureTime = pb::FullTiltMode ? 0 : 0.5f;
@ -91,6 +92,10 @@ void THole::Collision(TBall* ball, vector2* nextPosition, vector2* direction, fl
control::handler(MessageCode::ControlBallCaptured, this); control::handler(MessageCode::ControlBallCaptured, this);
} }
} }
else
{
DefaultCollision(ball, nextPosition, direction);
}
} }
int THole::FieldEffect(TBall* ball, vector2* vecDst) int THole::FieldEffect(TBall* ball, vector2* vecDst)

View File

@ -104,7 +104,8 @@ void TKickout::Collision(TBall* ball, vector2* nextPosition, vector2* direction,
ball->Position.X = Circle.Center.X; ball->Position.X = Circle.Center.X;
ball->Position.Y = Circle.Center.Y; ball->Position.Y = Circle.Center.Y;
OriginalBallZ = ball->Position.Z; OriginalBallZ = ball->Position.Z;
ball->Position.Z = CollisionBallSetZ; ball->Position.Z = CollisionBallSetZ;
ball->AsEdgeCollisionFlag = true;
if (PinballTable->TiltLockFlag) if (PinballTable->TiltLockFlag)
{ {
Message(MessageCode::TKickoutRestartTimer, 0.1f); Message(MessageCode::TKickoutRestartTimer, 0.1f);
@ -115,6 +116,13 @@ void TKickout::Collision(TBall* ball, vector2* nextPosition, vector2* direction,
control::handler(MessageCode::ControlCollision, this); control::handler(MessageCode::ControlCollision, this);
} }
} }
else
{
ball->Position.X = nextPosition->X;
ball->Position.Y = nextPosition->Y;
ball->RayMaxDistance -= distance;
ball->not_again(edge);
}
} }
int TKickout::FieldEffect(TBall* ball, vector2* dstVec) int TKickout::FieldEffect(TBall* ball, vector2* dstVec)
@ -144,7 +152,7 @@ void TKickout::TimerExpired(int timerId, void* caller)
{ {
loader::play_sound(kick->HardHitSoundId, kick->Ball, "TKickout2"); loader::play_sound(kick->HardHitSoundId, kick->Ball, "TKickout2");
kick->Ball->Position.Z = kick->OriginalBallZ; kick->Ball->Position.Z = kick->OriginalBallZ;
TBall::throw_ball(kick->Ball, &kick->BallThrowDirection, kick->ThrowAngleMult, kick->ThrowSpeedMult1, kick->Ball->throw_ball(&kick->BallThrowDirection, kick->ThrowAngleMult, kick->ThrowSpeedMult1,
kick->ThrowSpeedMult2); kick->ThrowSpeedMult2);
kick->ActiveFlag = 0; kick->ActiveFlag = 0;
kick->Ball = nullptr; kick->Ball = nullptr;

View File

@ -631,6 +631,9 @@ TBall* TPinballTable::AddBall(float x, float y)
ball->Position.X = x; ball->Position.X = x;
ball->Position.Y = y; ball->Position.Y = y;
ball->PrevPosition = ball->Position;
ball->SomeCounter1 = 0;
ball->time_ticks1 = ball->time_ticks2 = pb::time_ticks;
return ball; return ball;
} }

View File

@ -88,7 +88,8 @@ void TSink::TimerExpired(int timerId, void* caller)
{ {
auto ball = table->AddBall(sink->BallPosition.X, sink->BallPosition.Y); auto ball = table->AddBall(sink->BallPosition.X, sink->BallPosition.Y);
assertm(ball, "Failure to create ball in sink"); assertm(ball, "Failure to create ball in sink");
TBall::throw_ball(ball, &sink->BallThrowDirection, sink->ThrowAngleMult, sink->ThrowSpeedMult1, ball->AsEdgeCollisionFlag = true;
ball->throw_ball(&sink->BallThrowDirection, sink->ThrowAngleMult, sink->ThrowSpeedMult1,
sink->ThrowSpeedMult2); sink->ThrowSpeedMult2);
if (sink->SoundIndex3) if (sink->SoundIndex3)
loader::play_sound(sink->SoundIndex3, ball, "TSink2"); loader::play_sound(sink->SoundIndex3, ball, "TSink2");

View File

@ -3,6 +3,7 @@
#include "midi.h" #include "midi.h"
#include "pb.h" #include "pb.h"
#include "TBall.h"
#include "TBlocker.h" #include "TBlocker.h"
#include "TBumper.h" #include "TBumper.h"
#include "TComponentGroup.h" #include "TComponentGroup.h"
@ -1062,6 +1063,36 @@ void control::cheat_bump_rank()
} }
} }
void control::BallThrowOrDisable(TBall& ball, int dt)
{
if (!CheckBallInControlBounds(ball, *flip1) &&
!CheckBallInControlBounds(ball, *flip2) &&
!CheckBallInControlBounds(ball, *plunger))
{
if (ball.SomeCounter1 <= 20)
{
vector3 throwDir{0.0f, -1.0f, 0.0f};
ball.throw_ball(&throwDir, 90.0f, 1.0f, 0.0f);
}
else
{
ball.Disable();
TableG->MultiballCount--;
plunger->Message(MessageCode::PlungerRelaunchBall, 0);
}
}
}
bool control::CheckBallInControlBounds(const TBall& ball, const TCollisionComponent& cmp)
{
auto offset = TableG->CollisionCompOffset / 2.0f;
return ball.ActiveFlag &&
ball.Position.X >= cmp.AABB.XMin - offset &&
ball.Position.X <= cmp.AABB.XMax + offset &&
ball.Position.Y >= cmp.AABB.YMin - offset &&
ball.Position.Y <= cmp.AABB.YMax + offset;
}
int control::SpecialAddScore(int score) int control::SpecialAddScore(int score)
{ {
int prevFlag1 = TableG->ScoreSpecial3Flag; int prevFlag1 = TableG->ScoreSpecial3Flag;

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
class TCollisionComponent;
class TBall;
enum class MessageCode; enum class MessageCode;
class TSink; class TSink;
class TLight; class TLight;
@ -87,6 +89,8 @@ public:
static void table_set_multiball(float time); static void table_set_multiball(float time);
static void table_bump_ball_sink_lock(); static void table_bump_ball_sink_lock();
static void table_set_replay(float value); static void table_set_replay(float value);
static void BallThrowOrDisable(TBall& ball, int dt);
static bool CheckBallInControlBounds(const TBall& ball, const TCollisionComponent& cmp);
static void cheat_bump_rank(); static void cheat_bump_rank();
static int SpecialAddScore(int score); static int SpecialAddScore(int score);
static int AddRankProgress(int rank); static int AddRankProgress(int rank);

View File

@ -217,6 +217,11 @@ float maths::magnitude(const vector3& vec)
return result; return result;
} }
float maths::magnitudeSq(const vector2& vec)
{
return vec.X * vec.X + vec.Y * vec.Y;
}
void maths::vector_add(vector2& vec1Dst, const vector2& vec2) void maths::vector_add(vector2& vec1Dst, const vector2& vec2)
{ {
vec1Dst.X += vec2.X; vec1Dst.X += vec2.X;
@ -312,7 +317,7 @@ float maths::distance_to_flipper(TFlipperEdge* flipper, const ray_type& ray1, ra
{ {
auto distance = 1000000000.0f; auto distance = 1000000000.0f;
auto distanceType = FlipperIntersect::none; auto distanceType = FlipperIntersect::none;
auto newDistance = ray_intersect_line(ray1, flipper->lineA); auto newDistance = ray_intersect_line(ray1, flipper->LineA);
if (newDistance < distance) if (newDistance < distance)
{ {
distance = newDistance; distance = newDistance;
@ -330,7 +335,7 @@ float maths::distance_to_flipper(TFlipperEdge* flipper, const ray_type& ray1, ra
distance = newDistance; distance = newDistance;
distanceType = FlipperIntersect::circleT1; distanceType = FlipperIntersect::circleT1;
} }
newDistance = ray_intersect_line(ray1, flipper->lineB); newDistance = ray_intersect_line(ray1, flipper->LineB);
if (newDistance < distance) if (newDistance < distance)
{ {
distance = newDistance; distance = newDistance;
@ -340,12 +345,12 @@ float maths::distance_to_flipper(TFlipperEdge* flipper, const ray_type& ray1, ra
switch (distanceType) switch (distanceType)
{ {
case FlipperIntersect::lineA: case FlipperIntersect::lineA:
ray2.Direction = flipper->lineA.PerpendicularC; ray2.Direction = flipper->LineA.PerpendicularC;
ray2.Origin = flipper->lineA.RayIntersect; ray2.Origin = flipper->LineA.RayIntersect;
break; break;
case FlipperIntersect::lineB: case FlipperIntersect::lineB:
ray2.Direction = flipper->lineB.PerpendicularC; ray2.Direction = flipper->LineB.PerpendicularC;
ray2.Origin = flipper->lineB.RayIntersect; ray2.Origin = flipper->LineB.RayIntersect;
break; break;
case FlipperIntersect::circlebase: case FlipperIntersect::circlebase:
case FlipperIntersect::circleT1: case FlipperIntersect::circleT1:

View File

@ -114,6 +114,7 @@ public:
static void cross(const vector3& vec1, const vector3& vec2, vector3& dstVec); static void cross(const vector3& vec1, const vector3& vec2, vector3& dstVec);
static float cross(const vector2& vec1, const vector2& vec2); static float cross(const vector2& vec1, const vector2& vec2);
static float magnitude(const vector3& vec); static float magnitude(const vector3& vec);
static float magnitudeSq(const vector2& vec);
static void vector_add(vector2& vec1Dst, const vector2& vec2); static void vector_add(vector2& vec1Dst, const vector2& vec2);
static vector2 vector_sub(const vector2& vec1, const vector2& vec2); static vector2 vector_sub(const vector2& vec1, const vector2& vec2);
static vector3 vector_sub(const vector3& vec1, const vector3& vec2); static vector3 vector_sub(const vector3& vec1, const vector3& vec2);

View File

@ -324,13 +324,45 @@ void pb::timed_frame(float timeNow, float timeDelta, bool drawBalls)
for (auto ball : MainTable->BallList) for (auto ball : MainTable->BallList)
{ {
if (!ball->ActiveFlag || ball->HasGroupFlag || ball->CollisionComp || ball->Speed >= 0.8f)
{
if (ball->SomeCounter1 > 0)
{
vector2 dxy{ball->Position.X - ball->PrevPosition.X, ball->Position.Y - ball->PrevPosition.Y};
auto offsetX2 = ball->Offset * 2.0f;
if (offsetX2 * offsetX2 < maths::magnitudeSq(dxy))
ball->SomeCounter1 = 0;
}
ball->time_ticks1 = ball->time_ticks2 = time_ticks;
}
else if (time_ticks - ball->time_ticks2 > 500)
{
vector2 dxy{ball->Position.X - ball->PrevPosition.X, ball->Position.Y - ball->PrevPosition.Y};
auto offsetD2 = ball->Offset / 2.0f;
ball->PrevPosition = ball->Position;
if (offsetD2 * offsetD2 < maths::magnitudeSq(dxy))
ball->SomeCounter1 = 0;
else
ball->SomeCounter1++;
control::BallThrowOrDisable(*ball, time_ticks - ball->time_ticks1);
}
}
int distanceCoefArray[20]{-1};
float distanceArray[20]{}, distanceArrayX[20]{}, distanceArrayY[20]{};
int minDistanceCoef = -1;
for (auto index = 0u; index < MainTable->BallList.size(); index++)
{
auto ball = MainTable->BallList[index];
if (ball->ActiveFlag != 0) if (ball->ActiveFlag != 0)
{ {
auto collComp = ball->CollisionComp; ball->TimeDelta = timeDelta;
if (collComp) if (ball->TimeDelta > 0.01f && ball->Speed < 0.8f)
ball->TimeDelta = 0.01f;
ball->AsEdgeCollisionFlag = false;
if (ball->CollisionComp)
{ {
ball->TimeDelta = timeDelta; ball->CollisionComp->FieldEffect(ball, &vec1);
collComp->FieldEffect(ball, &vec1);
} }
else else
{ {
@ -345,23 +377,107 @@ void pb::timed_frame(float timeNow, float timeDelta, bool drawBalls)
ball->Direction.Y = ball->Speed * ball->Direction.Y; ball->Direction.Y = ball->Speed * ball->Direction.Y;
maths::vector_add(ball->Direction, vec2); maths::vector_add(ball->Direction, vec2);
ball->Speed = maths::normalize_2d(ball->Direction); ball->Speed = maths::normalize_2d(ball->Direction);
} if (ball->Speed > ball_speed_limit)
ball->Speed = ball_speed_limit;
auto timeDelta2 = timeDelta; distanceArray[index] = ball->Speed * ball->TimeDelta;
auto timeNow2 = timeNow; auto distanceCoef = static_cast<int>(std::ceil(distanceArray[index] * ball_inv_smth)) - 1;
for (auto index = 10; timeDelta2 > 0.000001f && index; --index) distanceCoefArray[index] = distanceCoef;
{ if (distanceCoef >= 0)
auto time = collide(timeNow2, timeDelta2, ball); {
timeDelta2 -= time; distanceArrayX[index] = ball->Direction.X * ball_min_smth;
timeNow2 += time; distanceArrayY[index] = ball->Direction.Y * ball_min_smth;
if (distanceCoef > minDistanceCoef)
minDistanceCoef = distanceCoef;
}
} }
} }
} }
} }
for (auto flipper : MainTable->FlipperList) float deltaAngle[4]{};
for (auto index = 0u; index < MainTable->FlipperList.size(); index++)
{ {
flipper->UpdateSprite(timeNow); auto distanceCoef = MainTable->FlipperList[index]->GetFlipperAngleDistance(timeDelta, &deltaAngle[index]) - 1;
distanceArrayY[index] = static_cast<float>(distanceCoef);
if (distanceCoef > minDistanceCoef)
minDistanceCoef = distanceCoef;
}
ray_type ray{};
ray.MinDistance = 0.002f;
for (auto index4 = 0; index4 <= minDistanceCoef; index4++)
{
for (auto index5 = 0u; index5 < MainTable->BallList.size(); index5++)
{
auto ball = MainTable->BallList[index5];
if (!ball->AsEdgeCollisionFlag && index4 <= distanceCoefArray[index5])
{
float distanceSum = 0.0f;
ray.CollisionMask = ball->CollisionMask;
ball->TimeNow = timeNow;
if (ball_min_smth > 0.0f)
{
while (true)
{
ray.Origin = ball->Position;
ray.Direction = ball->Direction;
if (index4 >= distanceCoefArray[index5])
{
ray.MaxDistance = distanceArray[index5] - distanceCoefArray[index5] * ball_min_smth;
}
else
{
ray.MaxDistance = ball_min_smth;
}
ray.TimeNow = ball->TimeNow;
TEdgeSegment* edge = nullptr;
auto distance = TTableLayer::edge_manager->FindCollisionDistance(&ray, ball, &edge);
if (distance > 0.0f)
{
// Todo: ball to ball collision
//distance = ball_to_ball_collision();
}
if (ball->EdgeCollisionResetFlag)
{
ball->EdgeCollisionResetFlag = false;
}
else
{
ball->EdgeCollisionCount = 0;
ball->EdgeCollisionResetFlag = true;
}
if (distance >= 1e9f)
{
ball->Position.X += ray.MaxDistance * ray.Direction.X;
ball->Position.Y += ray.MaxDistance * ray.Direction.Y;
break;
}
edge->EdgeCollision(ball, distance);
if (distance > 0.0f && !ball->AsEdgeCollisionFlag)
{
distanceSum += distance;
if (distanceSum < ball_min_smth)
continue;
}
break;
}
}
}
}
for (auto index = 0u; index < MainTable->FlipperList.size(); index++)
{
if (distanceArrayY[index] >= index4)
MainTable->FlipperList[index]->FlipperCollision(deltaAngle[index]);
}
}
for (const auto flipper : MainTable->FlipperList)
{
flipper->UpdateSprite();
} }
if (drawBalls) if (drawBalls)
@ -423,7 +539,7 @@ void pb::InputUp(GameInput input)
{ {
if (game_mode != GameModes::InGame || winmain::single_step || demo_mode) if (game_mode != GameModes::InGame || winmain::single_step || demo_mode)
return; return;
const auto bindings = options::MapGameInput(input); const auto bindings = options::MapGameInput(input);
for (const auto binding : bindings) for (const auto binding : bindings)
{ {
@ -454,7 +570,7 @@ void pb::InputUp(GameInput input)
void pb::InputDown(GameInput input) void pb::InputDown(GameInput input)
{ {
if (options::WaitingForInput()) if (options::WaitingForInput())
{ {
options::InputDown(input); options::InputDown(input);
return; return;
@ -475,31 +591,31 @@ void pb::InputDown(GameInput input)
for (const auto binding : bindings) for (const auto binding : bindings)
{ {
switch (binding) switch (binding)
{ {
case GameBindings::LeftFlipper: case GameBindings::LeftFlipper:
MainTable->Message(MessageCode::LeftFlipperInputPressed, time_now); MainTable->Message(MessageCode::LeftFlipperInputPressed, time_now);
break; break;
case GameBindings::RightFlipper: case GameBindings::RightFlipper:
MainTable->Message(MessageCode::RightFlipperInputPressed, time_now); MainTable->Message(MessageCode::RightFlipperInputPressed, time_now);
break; break;
case GameBindings::Plunger: case GameBindings::Plunger:
MainTable->Message(MessageCode::PlungerInputPressed, time_now); MainTable->Message(MessageCode::PlungerInputPressed, time_now);
break; break;
case GameBindings::LeftTableBump: case GameBindings::LeftTableBump:
if (!MainTable->TiltLockFlag) if (!MainTable->TiltLockFlag)
nudge::nudge_right(); nudge::nudge_right();
break; break;
case GameBindings::RightTableBump: case GameBindings::RightTableBump:
if (!MainTable->TiltLockFlag) if (!MainTable->TiltLockFlag)
nudge::nudge_left(); nudge::nudge_left();
break; break;
case GameBindings::BottomTableBump: case GameBindings::BottomTableBump:
if (!MainTable->TiltLockFlag) if (!MainTable->TiltLockFlag)
nudge::nudge_up(); nudge::nudge_up();
break; break;
default: break; default: break;
}
} }
}
if (cheat_mode && input.Type == InputTypes::Keyboard) if (cheat_mode && input.Type == InputTypes::Keyboard)
{ {
@ -510,12 +626,12 @@ void pb::InputDown(GameInput input)
MainTable->MultiballCount++; MainTable->MultiballCount++;
break; break;
case 'h': case 'h':
{ {
high_score_struct entry{ {0}, 1000000000 }; high_score_struct entry{{0}, 1000000000};
strncpy(entry.Name, get_rc_string(Msg::STRING127), sizeof entry.Name - 1); strncpy(entry.Name, get_rc_string(Msg::STRING127), sizeof entry.Name - 1);
high_score::show_and_set_high_score_dialog({ entry, 1 }); high_score::show_and_set_high_score_dialog({entry, 1});
break; break;
} }
case 'r': case 'r':
control::cheat_bump_rank(); control::cheat_bump_rank();
break; break;
@ -580,20 +696,24 @@ void pb::end_game()
int position = high_score::get_score_position(scores[i]); int position = high_score::get_score_position(scores[i]);
if (position >= 0) if (position >= 0)
{ {
high_score_struct entry{ {0}, scores[i] }; high_score_struct entry{{0}, scores[i]};
const char* playerName; const char* playerName;
switch(scoreIndex[i]) switch (scoreIndex[i])
{ {
default: default:
case 0: playerName = get_rc_string(Msg::STRING127); break; case 0: playerName = get_rc_string(Msg::STRING127);
case 1: playerName = get_rc_string(Msg::STRING128); break; break;
case 2: playerName = get_rc_string(Msg::STRING129); break; case 1: playerName = get_rc_string(Msg::STRING128);
case 3: playerName = get_rc_string(Msg::STRING130); break; break;
case 2: playerName = get_rc_string(Msg::STRING129);
break;
case 3: playerName = get_rc_string(Msg::STRING130);
break;
} }
strncpy(entry.Name, playerName, sizeof entry.Name - 1); strncpy(entry.Name, playerName, sizeof entry.Name - 1);
high_score::show_and_set_high_score_dialog({ entry, -1 }); high_score::show_and_set_high_score_dialog({entry, -1});
} }
} }
} }