mirror of
https://github.com/k4zmu2a/SpaceCadetPinball.git
synced 2024-11-17 15:20:17 +01:00
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:
parent
466c875f8a
commit
ba470e8727
16 changed files with 489 additions and 396 deletions
|
@ -344,10 +344,11 @@ void DebugOverlay::DrawEdge(TEdgeSegment* edge)
|
|||
auto flip = dynamic_cast<TFlipperEdge*>(edge);
|
||||
if (flip)
|
||||
{
|
||||
flip->set_control_points(pb::time_now);
|
||||
if (flip->ControlPointDirtyFlag)
|
||||
flip->set_control_points(flip->CurrentAngle);
|
||||
|
||||
DrawLineType(flip->lineA);
|
||||
DrawLineType(flip->lineB);
|
||||
DrawLineType(flip->LineA);
|
||||
DrawLineType(flip->LineB);
|
||||
DrawCicleType(flip->circlebase);
|
||||
DrawCicleType(flip->circleT1);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ TBall::TBall(TPinballTable* table) : TPinballComponent(table, -1, false)
|
|||
Direction.X = 0.0;
|
||||
Position.X = 0.0;
|
||||
Position.Y = 0.0;
|
||||
HasGroupFlag = false;
|
||||
|
||||
ListBitmap = new std::vector<SpriteData>();
|
||||
|
||||
|
@ -81,11 +82,19 @@ void TBall::Repaint()
|
|||
|
||||
void TBall::not_again(TEdgeSegment* edge)
|
||||
{
|
||||
if (EdgeCollisionCount < 5)
|
||||
if (EdgeCollisionCount < 16)
|
||||
{
|
||||
Collisions[EdgeCollisionCount] = edge;
|
||||
++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)
|
||||
|
@ -119,15 +128,15 @@ int TBall::Message(MessageCode code, float value)
|
|||
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;
|
||||
ball->Direction = *direction;
|
||||
CollisionComp = nullptr;
|
||||
Direction = *direction;
|
||||
float rnd = RandFloat();
|
||||
float angle = (1.0f - (rnd + rnd)) * angleMult;
|
||||
maths::RotateVector(ball->Direction, angle);
|
||||
maths::RotateVector(Direction, angle);
|
||||
rnd = RandFloat();
|
||||
ball->Speed = (1.0f - (rnd + rnd)) * (speedMult1 * speedMult2) + speedMult1;
|
||||
Speed = (1.0f - (rnd + rnd)) * (speedMult1 * speedMult2) + speedMult1;
|
||||
}
|
||||
|
||||
vector2 TBall::get_coordinates()
|
||||
|
@ -138,5 +147,6 @@ vector2 TBall::get_coordinates()
|
|||
void TBall::Disable()
|
||||
{
|
||||
ActiveFlag = false;
|
||||
AsEdgeCollisionFlag = true;
|
||||
SpriteSet(-1);
|
||||
}
|
||||
|
|
|
@ -15,11 +15,10 @@ public :
|
|||
int Message(MessageCode code, float value) override;
|
||||
vector2 get_coordinates() override;
|
||||
void Disable();
|
||||
|
||||
static void throw_ball(TBall* ball, vector3* direction, float angleMult, float speedMult1,
|
||||
float speedMult2);
|
||||
void throw_ball(vector3* direction, float angleMult, float speedMult1, float speedMult2);
|
||||
|
||||
vector3 Position{};
|
||||
vector3 PrevPosition{};
|
||||
vector3 Direction{};
|
||||
float Speed;
|
||||
float RayMaxDistance;
|
||||
|
@ -28,10 +27,15 @@ public :
|
|||
vector2 RampFieldForce{};
|
||||
TCollisionComponent* CollisionComp;
|
||||
int CollisionMask;
|
||||
TEdgeSegment* Collisions[5]{};
|
||||
TEdgeSegment* Collisions[16]{};
|
||||
int EdgeCollisionCount;
|
||||
bool EdgeCollisionResetFlag{};
|
||||
vector3 CollisionOffset{};
|
||||
int CollisionFlag;
|
||||
float Offset;
|
||||
bool HasGroupFlag;
|
||||
int SomeCounter1 = 0;
|
||||
int time_ticks1{}, time_ticks2{};
|
||||
float VisualZArray[50]{};
|
||||
bool AsEdgeCollisionFlag{};
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "loader.h"
|
||||
#include "pb.h"
|
||||
#include "render.h"
|
||||
#include "TBall.h"
|
||||
#include "TFlipperEdge.h"
|
||||
#include "timer.h"
|
||||
#include "TPinballTable.h"
|
||||
|
@ -84,15 +85,17 @@ int TFlipper::Message(MessageCode code, float value)
|
|||
code = MessageCode::TFlipperRetract;
|
||||
}
|
||||
|
||||
MessageField = FlipperEdge->SetMotion(code, value);
|
||||
MessageField = FlipperEdge->SetMotion(code);
|
||||
break;
|
||||
case MessageCode::PlayerChanged:
|
||||
case MessageCode::Reset:
|
||||
if (MessageField)
|
||||
{
|
||||
FlipperEdge->CurrentAngle = 0;
|
||||
FlipperEdge->set_control_points(0);
|
||||
MessageField = 0;
|
||||
FlipperEdge->SetMotion(MessageCode::Reset, value);
|
||||
UpdateSprite(0);
|
||||
FlipperEdge->SetMotion(MessageCode::Reset);
|
||||
UpdateSprite();
|
||||
}
|
||||
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;
|
||||
|
||||
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);
|
||||
if (BmpIndex == newBmpIndex)
|
||||
return;
|
||||
|
@ -122,3 +125,87 @@ void TFlipper::UpdateSprite(float timeNow)
|
|||
BmpIndex = newBmpIndex;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,9 @@ public:
|
|||
void port_draw() override;
|
||||
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
|
||||
TEdgeSegment* edge) override;
|
||||
void UpdateSprite(float timeNow);
|
||||
void UpdateSprite();
|
||||
int GetFlipperAngleDistance(float dt, float* dst) const;
|
||||
void FlipperCollision(float deltaAngle);
|
||||
|
||||
int BmpIndex;
|
||||
TFlipperEdge* FlipperEdge;
|
||||
|
|
|
@ -8,9 +8,11 @@
|
|||
#include "TTableLayer.h"
|
||||
|
||||
|
||||
TFlipperEdge::TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup, TPinballTable* table,
|
||||
vector3* origin, vector3* vecT1, vector3* vecT2, float extendTime, float retractTime,
|
||||
float collMult, float elasticity, float smoothness): TEdgeSegment(collComp, activeFlag, collisionGroup)
|
||||
TFlipperEdge::TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup,
|
||||
TPinballTable* table,
|
||||
vector3* origin, vector3* vecT1, vector3* vecT2, float extendSpeed, float retractSpeed,
|
||||
float collMult, float elasticity, float smoothness): TEdgeSegment(
|
||||
collComp, activeFlag, collisionGroup)
|
||||
{
|
||||
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: Time it takes for flipper to go from source to destination, in sec.
|
||||
// FT: Flipper movement speed, in radians per sec.
|
||||
if (pb::FullTiltMode)
|
||||
if (!pb::FullTiltMode)
|
||||
{
|
||||
auto angleMax = std::abs(AngleMax);
|
||||
retractTime = angleMax / retractTime;
|
||||
extendTime = angleMax / extendTime;
|
||||
retractSpeed = angleMax / retractSpeed;
|
||||
extendSpeed = angleMax / extendSpeed;
|
||||
}
|
||||
ExtendTime = extendTime;
|
||||
RetractTime = retractTime;
|
||||
ExtendSpeed = extendSpeed;
|
||||
RetractSpeed = retractSpeed;
|
||||
|
||||
const vector2 perpOriginT1Cc = { -vecOriginT1.Y , vecOriginT1.X };
|
||||
const vector2 perpOriginT1Cc = {-vecOriginT1.Y, vecOriginT1.X};
|
||||
A2Src.X = perpOriginT1Cc.X * CirclebaseRadius + origin->X;
|
||||
A2Src.Y = perpOriginT1Cc.Y * CirclebaseRadius + origin->Y;
|
||||
A1Src.X = perpOriginT1Cc.X * CircleT1Radius + vecT1->X;
|
||||
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.Y = perpOriginT1C.Y * CirclebaseRadius + origin->Y;
|
||||
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 dy = vecT1->Y - RotOrigin.Y;
|
||||
auto distance1 = sqrt(dy * dy + dx * dx) + table->CollisionCompOffset + vecT1->Z;
|
||||
DistanceDiv = distance1;
|
||||
DistanceDivSq = distance1 * distance1;
|
||||
InvT1Radius = 1.0f / CircleT1Radius * 1.5f;
|
||||
|
||||
float minMoveTime = std::min(ExtendTime, RetractTime);
|
||||
auto distance = maths::Distance(*vecT1, *vecT2);
|
||||
CollisionTimeAdvance = minMoveTime / (distance / CircleT1Radius + distance / CircleT1Radius);
|
||||
|
||||
EdgeCollisionFlag = 0;
|
||||
InputTime = 0.0;
|
||||
CollisionFlag1 = 0;
|
||||
AngleStopTime = 0.0;
|
||||
AngleAdvanceTime = 0.0;
|
||||
if (AngleMax <= 0.0f)
|
||||
{
|
||||
ExtendSpeed = -ExtendSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
RetractSpeed = -RetractSpeed;
|
||||
}
|
||||
set_control_points(CurrentAngle);
|
||||
}
|
||||
|
||||
void TFlipperEdge::port_draw()
|
||||
{
|
||||
set_control_points(InputTime);
|
||||
set_control_points(CurrentAngle);
|
||||
}
|
||||
|
||||
float TFlipperEdge::FindCollisionDistance(ray_type* ray)
|
||||
{
|
||||
auto ogRay = ray;
|
||||
ray_type dstRay{}, srcRay{};
|
||||
ray_type dstRay{};
|
||||
if (ControlPointDirtyFlag)
|
||||
set_control_points(CurrentAngle);
|
||||
auto distance = maths::distance_to_flipper(this, *ray, dstRay);
|
||||
if (distance >= 1e9f)
|
||||
return 1e9f;
|
||||
|
||||
if (ogRay->TimeNow > AngleStopTime)
|
||||
{
|
||||
FlipperFlag = MessageCode::TFlipperNull;
|
||||
}
|
||||
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;
|
||||
NextBallPosition = dstRay.Origin;
|
||||
CollisionDirection = dstRay.Direction;
|
||||
return distance;
|
||||
}
|
||||
|
||||
void TFlipperEdge::EdgeCollision(TBall* ball, float distance)
|
||||
{
|
||||
EdgeCollisionFlag = 1;
|
||||
if (FlipperFlag == MessageCode::TFlipperNull || !CollisionFlag2 || CollisionFlag1)
|
||||
if (FlipperFlag == MessageCode::TFlipperNull)
|
||||
{
|
||||
float boost = 0.0;
|
||||
if (CollisionFlag1)
|
||||
maths::basic_collision(
|
||||
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;
|
||||
float dy = NextBallPosition.Y - RotOrigin.Y;
|
||||
float distanceSq = dy * dy + dx * dx;
|
||||
if (circlebase.RadiusSq * 1.01f < distanceSq)
|
||||
{
|
||||
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;
|
||||
}
|
||||
auto v21 = std::fabs(MoveSpeed) * std::sqrt(distanceSq / DistanceDivSq);
|
||||
auto dot1 = maths::DotProduct(CollisionLinePerp, CollisionDirection);
|
||||
if (dot1 >= 0)
|
||||
boost = CollisionMult * dot1 * v21;
|
||||
}
|
||||
|
||||
float threshold = boost <= 0.0f ? 1000000000.0f : -1.0f;
|
||||
auto threshold = boost <= 0.0f ? 1e9f : -1.0f;
|
||||
maths::basic_collision(
|
||||
ball,
|
||||
&NextBallPosition,
|
||||
|
@ -333,18 +180,14 @@ void TFlipperEdge::EdgeCollision(TBall* ball, float distance)
|
|||
Smoothness,
|
||||
threshold,
|
||||
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
|
||||
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)
|
||||
|
@ -360,12 +203,18 @@ void TFlipperEdge::place_in_grid(RectF* aabb)
|
|||
}
|
||||
|
||||
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;
|
||||
maths::SinCos(flipper_angle(timeNow), sin, cos);
|
||||
maths::SinCos(angle, sin, cos);
|
||||
A1 = A1Src;
|
||||
A2 = A2Src;
|
||||
B1 = B1Src;
|
||||
|
@ -376,83 +225,48 @@ void TFlipperEdge::set_control_points(float timeNow)
|
|||
maths::RotatePt(T1, sin, cos, RotOrigin);
|
||||
maths::RotatePt(B1, sin, cos, RotOrigin);
|
||||
maths::RotatePt(B2, sin, cos, RotOrigin);
|
||||
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(LineA, A1.X, A1.Y, A2.X, A2.Y);
|
||||
maths::line_init(LineB, B1.X, B1.Y, B2.X, B2.Y);
|
||||
circlebase = {RotOrigin, CirclebaseRadiusSq};
|
||||
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)
|
||||
return AngleDst;
|
||||
return 0.0f;
|
||||
|
||||
// How much time it takes to go from source to destination angle, in sec.
|
||||
auto arcDuration = std::abs((AngleDst - AngleSrc) / AngleMax * AngleAdvanceTime);
|
||||
|
||||
// How close the flipper is to destination, in [0, 1] range.
|
||||
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);
|
||||
const auto deltaAngle = MoveSpeed * timeDelta;
|
||||
if (std::fabs(deltaAngle) > AngleRemainder)
|
||||
return AngleDst - CurrentAngle;
|
||||
return deltaAngle;
|
||||
}
|
||||
|
||||
int TFlipperEdge::is_ball_inside(float x, float y)
|
||||
{
|
||||
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)
|
||||
int TFlipperEdge::SetMotion(MessageCode code)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case MessageCode::TFlipperExtend:
|
||||
AngleSrc = flipper_angle(value);
|
||||
AngleRemainder = std::fabs(AngleMax - CurrentAngle);
|
||||
AngleDst = AngleMax;
|
||||
AngleAdvanceTime = ExtendTime;
|
||||
MoveSpeed = ExtendSpeed;
|
||||
break;
|
||||
case MessageCode::TFlipperRetract:
|
||||
AngleSrc = flipper_angle(value);
|
||||
AngleRemainder = std::fabs(CurrentAngle);
|
||||
AngleDst = 0.0f;
|
||||
AngleAdvanceTime = RetractTime;
|
||||
MoveSpeed = RetractSpeed;
|
||||
break;
|
||||
case MessageCode::Reset:
|
||||
AngleSrc = 0.0f;
|
||||
AngleRemainder = 0.0f;
|
||||
AngleDst = 0.0f;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (AngleSrc == AngleDst)
|
||||
if (AngleRemainder == 0.0f)
|
||||
code = MessageCode::TFlipperNull;
|
||||
|
||||
InputTime = value;
|
||||
FlipperFlag = code;
|
||||
AngleStopTime = AngleAdvanceTime + InputTime;
|
||||
return static_cast<int>(code);
|
||||
}
|
||||
|
|
|
@ -9,16 +9,15 @@ class TFlipperEdge : public TEdgeSegment
|
|||
{
|
||||
public:
|
||||
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);
|
||||
void port_draw() override;
|
||||
float FindCollisionDistance(ray_type* ray) override;
|
||||
void EdgeCollision(TBall* ball, float distance) override;
|
||||
void place_in_grid(RectF* aabb) override;
|
||||
void set_control_points(float timeNow);
|
||||
float flipper_angle(float timeNow);
|
||||
int is_ball_inside(float x, float y);
|
||||
int SetMotion(MessageCode code, float value);
|
||||
void set_control_points(float angle);
|
||||
float flipper_angle_delta(float timeDelta);
|
||||
int SetMotion(MessageCode code);
|
||||
|
||||
MessageCode FlipperFlag{};
|
||||
float Elasticity;
|
||||
|
@ -31,10 +30,9 @@ public:
|
|||
float CirclebaseRadiusMSq;
|
||||
float CircleT1RadiusMSq;
|
||||
float AngleMax;
|
||||
float AngleSrc{};
|
||||
float AngleRemainder{};
|
||||
float AngleDst;
|
||||
int CollisionFlag1;
|
||||
int CollisionFlag2{};
|
||||
float CurrentAngle{};
|
||||
vector2 CollisionLinePerp{};
|
||||
vector2 A1Src{};
|
||||
vector2 A2Src{};
|
||||
|
@ -43,17 +41,16 @@ public:
|
|||
float CollisionMult;
|
||||
vector2 T1Src{};
|
||||
vector2 T2Src{};
|
||||
float DistanceDivSq;
|
||||
float CollisionTimeAdvance;
|
||||
float DistanceDiv, DistanceDivSq;
|
||||
vector2 CollisionDirection{};
|
||||
int EdgeCollisionFlag;
|
||||
float InputTime;
|
||||
float AngleStopTime;
|
||||
float AngleAdvanceTime;
|
||||
float ExtendTime;
|
||||
float RetractTime;
|
||||
float ExtendSpeed;
|
||||
float RetractSpeed;
|
||||
float MoveSpeed;
|
||||
vector2 NextBallPosition{};
|
||||
vector2 A1, A2, B1, B2, T1;
|
||||
line_type lineA, lineB;
|
||||
line_type LineA, LineB;
|
||||
circle_type circlebase, circleT1;
|
||||
float InvT1Radius;
|
||||
float YMin, YMax, XMin, XMax;
|
||||
bool ControlPointDirtyFlag{};
|
||||
};
|
||||
|
|
|
@ -80,6 +80,7 @@ void THole::Collision(TBall* ball, vector2* nextPosition, vector2* direction, fl
|
|||
ball->Position.X = Circle.Center.X;
|
||||
ball->Position.Y = Circle.Center.Y;
|
||||
ball->Direction.Z = 0.0;
|
||||
ball->AsEdgeCollisionFlag = true;
|
||||
|
||||
// Ramp hole has no delay in FT.
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DefaultCollision(ball, nextPosition, direction);
|
||||
}
|
||||
}
|
||||
|
||||
int THole::FieldEffect(TBall* ball, vector2* vecDst)
|
||||
|
|
|
@ -104,7 +104,8 @@ void TKickout::Collision(TBall* ball, vector2* nextPosition, vector2* direction,
|
|||
ball->Position.X = Circle.Center.X;
|
||||
ball->Position.Y = Circle.Center.Y;
|
||||
OriginalBallZ = ball->Position.Z;
|
||||
ball->Position.Z = CollisionBallSetZ;
|
||||
ball->Position.Z = CollisionBallSetZ;
|
||||
ball->AsEdgeCollisionFlag = true;
|
||||
if (PinballTable->TiltLockFlag)
|
||||
{
|
||||
Message(MessageCode::TKickoutRestartTimer, 0.1f);
|
||||
|
@ -115,6 +116,13 @@ void TKickout::Collision(TBall* ball, vector2* nextPosition, vector2* direction,
|
|||
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)
|
||||
|
@ -144,7 +152,7 @@ void TKickout::TimerExpired(int timerId, void* caller)
|
|||
{
|
||||
loader::play_sound(kick->HardHitSoundId, kick->Ball, "TKickout2");
|
||||
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->ActiveFlag = 0;
|
||||
kick->Ball = nullptr;
|
||||
|
|
|
@ -631,6 +631,9 @@ TBall* TPinballTable::AddBall(float x, float y)
|
|||
|
||||
ball->Position.X = x;
|
||||
ball->Position.Y = y;
|
||||
ball->PrevPosition = ball->Position;
|
||||
ball->SomeCounter1 = 0;
|
||||
ball->time_ticks1 = ball->time_ticks2 = pb::time_ticks;
|
||||
|
||||
return ball;
|
||||
}
|
||||
|
|
|
@ -88,7 +88,8 @@ void TSink::TimerExpired(int timerId, void* caller)
|
|||
{
|
||||
auto ball = table->AddBall(sink->BallPosition.X, sink->BallPosition.Y);
|
||||
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);
|
||||
if (sink->SoundIndex3)
|
||||
loader::play_sound(sink->SoundIndex3, ball, "TSink2");
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "midi.h"
|
||||
#include "pb.h"
|
||||
#include "TBall.h"
|
||||
#include "TBlocker.h"
|
||||
#include "TBumper.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 prevFlag1 = TableG->ScoreSpecial3Flag;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
class TCollisionComponent;
|
||||
class TBall;
|
||||
enum class MessageCode;
|
||||
class TSink;
|
||||
class TLight;
|
||||
|
@ -87,6 +89,8 @@ public:
|
|||
static void table_set_multiball(float time);
|
||||
static void table_bump_ball_sink_lock();
|
||||
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 int SpecialAddScore(int score);
|
||||
static int AddRankProgress(int rank);
|
||||
|
|
|
@ -217,6 +217,11 @@ float maths::magnitude(const vector3& vec)
|
|||
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)
|
||||
{
|
||||
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 distanceType = FlipperIntersect::none;
|
||||
auto newDistance = ray_intersect_line(ray1, flipper->lineA);
|
||||
auto newDistance = ray_intersect_line(ray1, flipper->LineA);
|
||||
if (newDistance < distance)
|
||||
{
|
||||
distance = newDistance;
|
||||
|
@ -330,7 +335,7 @@ float maths::distance_to_flipper(TFlipperEdge* flipper, const ray_type& ray1, ra
|
|||
distance = newDistance;
|
||||
distanceType = FlipperIntersect::circleT1;
|
||||
}
|
||||
newDistance = ray_intersect_line(ray1, flipper->lineB);
|
||||
newDistance = ray_intersect_line(ray1, flipper->LineB);
|
||||
if (newDistance < distance)
|
||||
{
|
||||
distance = newDistance;
|
||||
|
@ -340,12 +345,12 @@ float maths::distance_to_flipper(TFlipperEdge* flipper, const ray_type& ray1, ra
|
|||
switch (distanceType)
|
||||
{
|
||||
case FlipperIntersect::lineA:
|
||||
ray2.Direction = flipper->lineA.PerpendicularC;
|
||||
ray2.Origin = flipper->lineA.RayIntersect;
|
||||
ray2.Direction = flipper->LineA.PerpendicularC;
|
||||
ray2.Origin = flipper->LineA.RayIntersect;
|
||||
break;
|
||||
case FlipperIntersect::lineB:
|
||||
ray2.Direction = flipper->lineB.PerpendicularC;
|
||||
ray2.Origin = flipper->lineB.RayIntersect;
|
||||
ray2.Direction = flipper->LineB.PerpendicularC;
|
||||
ray2.Origin = flipper->LineB.RayIntersect;
|
||||
break;
|
||||
case FlipperIntersect::circlebase:
|
||||
case FlipperIntersect::circleT1:
|
||||
|
|
|
@ -114,6 +114,7 @@ public:
|
|||
static void cross(const vector3& vec1, const vector3& vec2, vector3& dstVec);
|
||||
static float cross(const vector2& vec1, const vector2& vec2);
|
||||
static float magnitude(const vector3& vec);
|
||||
static float magnitudeSq(const vector2& vec);
|
||||
static void vector_add(vector2& vec1Dst, const vector2& vec2);
|
||||
static vector2 vector_sub(const vector2& vec1, const vector2& vec2);
|
||||
static vector3 vector_sub(const vector3& vec1, const vector3& vec2);
|
||||
|
|
|
@ -324,13 +324,45 @@ void pb::timed_frame(float timeNow, float timeDelta, bool drawBalls)
|
|||
|
||||
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)
|
||||
{
|
||||
auto collComp = ball->CollisionComp;
|
||||
if (collComp)
|
||||
ball->TimeDelta = timeDelta;
|
||||
if (ball->TimeDelta > 0.01f && ball->Speed < 0.8f)
|
||||
ball->TimeDelta = 0.01f;
|
||||
ball->AsEdgeCollisionFlag = false;
|
||||
if (ball->CollisionComp)
|
||||
{
|
||||
ball->TimeDelta = timeDelta;
|
||||
collComp->FieldEffect(ball, &vec1);
|
||||
ball->CollisionComp->FieldEffect(ball, &vec1);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -345,23 +377,107 @@ void pb::timed_frame(float timeNow, float timeDelta, bool drawBalls)
|
|||
ball->Direction.Y = ball->Speed * ball->Direction.Y;
|
||||
maths::vector_add(ball->Direction, vec2);
|
||||
ball->Speed = maths::normalize_2d(ball->Direction);
|
||||
}
|
||||
if (ball->Speed > ball_speed_limit)
|
||||
ball->Speed = ball_speed_limit;
|
||||
|
||||
auto timeDelta2 = timeDelta;
|
||||
auto timeNow2 = timeNow;
|
||||
for (auto index = 10; timeDelta2 > 0.000001f && index; --index)
|
||||
{
|
||||
auto time = collide(timeNow2, timeDelta2, ball);
|
||||
timeDelta2 -= time;
|
||||
timeNow2 += time;
|
||||
distanceArray[index] = ball->Speed * ball->TimeDelta;
|
||||
auto distanceCoef = static_cast<int>(std::ceil(distanceArray[index] * ball_inv_smth)) - 1;
|
||||
distanceCoefArray[index] = distanceCoef;
|
||||
if (distanceCoef >= 0)
|
||||
{
|
||||
distanceArrayX[index] = ball->Direction.X * ball_min_smth;
|
||||
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)
|
||||
|
@ -423,7 +539,7 @@ void pb::InputUp(GameInput input)
|
|||
{
|
||||
if (game_mode != GameModes::InGame || winmain::single_step || demo_mode)
|
||||
return;
|
||||
|
||||
|
||||
const auto bindings = options::MapGameInput(input);
|
||||
for (const auto binding : bindings)
|
||||
{
|
||||
|
@ -454,7 +570,7 @@ void pb::InputUp(GameInput input)
|
|||
|
||||
void pb::InputDown(GameInput input)
|
||||
{
|
||||
if (options::WaitingForInput())
|
||||
if (options::WaitingForInput())
|
||||
{
|
||||
options::InputDown(input);
|
||||
return;
|
||||
|
@ -475,31 +591,31 @@ void pb::InputDown(GameInput input)
|
|||
for (const auto binding : bindings)
|
||||
{
|
||||
switch (binding)
|
||||
{
|
||||
case GameBindings::LeftFlipper:
|
||||
MainTable->Message(MessageCode::LeftFlipperInputPressed, time_now);
|
||||
break;
|
||||
case GameBindings::RightFlipper:
|
||||
MainTable->Message(MessageCode::RightFlipperInputPressed, time_now);
|
||||
break;
|
||||
case GameBindings::Plunger:
|
||||
MainTable->Message(MessageCode::PlungerInputPressed, time_now);
|
||||
break;
|
||||
case GameBindings::LeftTableBump:
|
||||
if (!MainTable->TiltLockFlag)
|
||||
nudge::nudge_right();
|
||||
break;
|
||||
case GameBindings::RightTableBump:
|
||||
if (!MainTable->TiltLockFlag)
|
||||
nudge::nudge_left();
|
||||
break;
|
||||
case GameBindings::BottomTableBump:
|
||||
if (!MainTable->TiltLockFlag)
|
||||
nudge::nudge_up();
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
{
|
||||
case GameBindings::LeftFlipper:
|
||||
MainTable->Message(MessageCode::LeftFlipperInputPressed, time_now);
|
||||
break;
|
||||
case GameBindings::RightFlipper:
|
||||
MainTable->Message(MessageCode::RightFlipperInputPressed, time_now);
|
||||
break;
|
||||
case GameBindings::Plunger:
|
||||
MainTable->Message(MessageCode::PlungerInputPressed, time_now);
|
||||
break;
|
||||
case GameBindings::LeftTableBump:
|
||||
if (!MainTable->TiltLockFlag)
|
||||
nudge::nudge_right();
|
||||
break;
|
||||
case GameBindings::RightTableBump:
|
||||
if (!MainTable->TiltLockFlag)
|
||||
nudge::nudge_left();
|
||||
break;
|
||||
case GameBindings::BottomTableBump:
|
||||
if (!MainTable->TiltLockFlag)
|
||||
nudge::nudge_up();
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cheat_mode && input.Type == InputTypes::Keyboard)
|
||||
{
|
||||
|
@ -510,12 +626,12 @@ void pb::InputDown(GameInput input)
|
|||
MainTable->MultiballCount++;
|
||||
break;
|
||||
case 'h':
|
||||
{
|
||||
high_score_struct entry{ {0}, 1000000000 };
|
||||
strncpy(entry.Name, get_rc_string(Msg::STRING127), sizeof entry.Name - 1);
|
||||
high_score::show_and_set_high_score_dialog({ entry, 1 });
|
||||
break;
|
||||
}
|
||||
{
|
||||
high_score_struct entry{{0}, 1000000000};
|
||||
strncpy(entry.Name, get_rc_string(Msg::STRING127), sizeof entry.Name - 1);
|
||||
high_score::show_and_set_high_score_dialog({entry, 1});
|
||||
break;
|
||||
}
|
||||
case 'r':
|
||||
control::cheat_bump_rank();
|
||||
break;
|
||||
|
@ -580,20 +696,24 @@ void pb::end_game()
|
|||
int position = high_score::get_score_position(scores[i]);
|
||||
if (position >= 0)
|
||||
{
|
||||
high_score_struct entry{ {0}, scores[i] };
|
||||
high_score_struct entry{{0}, scores[i]};
|
||||
const char* playerName;
|
||||
|
||||
switch(scoreIndex[i])
|
||||
switch (scoreIndex[i])
|
||||
{
|
||||
default:
|
||||
case 0: playerName = get_rc_string(Msg::STRING127); break;
|
||||
case 1: playerName = get_rc_string(Msg::STRING128); break;
|
||||
case 2: playerName = get_rc_string(Msg::STRING129); break;
|
||||
case 3: playerName = get_rc_string(Msg::STRING130); break;
|
||||
default:
|
||||
case 0: playerName = get_rc_string(Msg::STRING127);
|
||||
break;
|
||||
case 1: playerName = get_rc_string(Msg::STRING128);
|
||||
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);
|
||||
high_score::show_and_set_high_score_dialog({ entry, -1 });
|
||||
high_score::show_and_set_high_score_dialog({entry, -1});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue