diff --git a/SpaceCadetPinball/TBall.cpp b/SpaceCadetPinball/TBall.cpp index ab440a6..9c9493f 100644 --- a/SpaceCadetPinball/TBall.cpp +++ b/SpaceCadetPinball/TBall.cpp @@ -11,7 +11,8 @@ #include "TPinballTable.h" #include "TTableLayer.h" -TBall::TBall(TPinballTable* table) : TPinballComponent(table, -1, false) +TBall::TBall(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, false), + TEdgeSegment(this, &ActiveFlag, 0) { visualStruct visual{}; char ballGroupName[10]{"ball"}; @@ -22,19 +23,29 @@ TBall::TBall(TPinballTable* table) : TPinballComponent(table, -1, false) CollisionComp = nullptr; EdgeCollisionCount = 0; TimeDelta = 0.0; - CollisionMask = 1; CollisionFlag = 0; Speed = 0.0; Direction.Y = 0.0; Direction.X = 0.0; - Position.X = 0.0; - Position.Y = 0.0; - HasGroupFlag = false; - ListBitmap = new std::vector(); + if (groupIndex == -1) + { + HasGroupFlag = false; + Position = {0, 0, 0}; + CollisionMask = 1; + } + else + { + HasGroupFlag = true; + loader::query_visual(groupIndex, 0, &visual); + CollisionMask = visual.CollisionGroup; + auto floatArr = loader::query_float_attribute(groupIndex, 0, 408); + Position = {floatArr[0], floatArr[1], floatArr[3]}; + } + /*Full tilt: ball is ballN, where N[0,2] resolution*/ - auto groupIndex = loader::query_handle(ballGroupName); + groupIndex = loader::query_handle(ballGroupName); if (groupIndex < 0) { ballGroupName[4] = '0' + fullscrn::GetResolution(); @@ -70,7 +81,7 @@ void TBall::Repaint() auto pos2D = proj::xform_to_2d(Position); auto zDepth = proj::z_distance(Position); - + auto index = 0u; for (; index < ListBitmap->size() - 1; ++index) { @@ -112,7 +123,7 @@ int TBall::Message(MessageCode code, float value) { if (code == MessageCode::Reset) { - SpriteSetBall(-1, { 0,0 }, 0.0f); + SpriteSetBall(-1, {0, 0}, 0.0f); Position.X = 0.0; CollisionComp = nullptr; Position.Y = 0.0; @@ -139,6 +150,47 @@ void TBall::throw_ball(vector3* direction, float angleMult, float speedMult1, fl Speed = (1.0f - (rnd + rnd)) * (speedMult1 * speedMult2) + speedMult1; } +void TBall::EdgeCollision(TBall* ball, float distance) +{ + ball->AsEdgeCollisionFlag = true; + vector2 nextPos{ + ball->Position.X + ball->Direction.X * distance, + ball->Position.Y + ball->Direction.Y * distance + }, + collDir{nextPos.X - Position.X, nextPos.Y - Position.Y}; + maths::normalize_2d(collDir); + vector2 invCollDir{ -collDir.X, -collDir.Y }; + + ball->Position.X = nextPos.X; + ball->Position.Y = nextPos.Y; + ball->Direction.X *= ball->Speed; + ball->Direction.Y *= ball->Speed; + Direction.X *= Speed; + Direction.Y *= Speed; + + auto coef = -maths::DotProduct(ball->Direction, collDir); + vector2 v44{collDir.X * coef, collDir.Y * coef}; + vector2 v13{ball->Direction.X + v44.X, ball->Direction.Y + v44.Y}; + + coef = -maths::DotProduct(Direction, invCollDir); + vector2 v11{invCollDir.X * coef, invCollDir.Y * coef}; + vector2 v10{Direction.X + v11.X, Direction.Y + v11.Y}; + + ball->Direction.X = -v11.X + v13.X; + ball->Direction.Y = -v11.Y + v13.Y; + ball->Speed = maths::normalize_2d(ball->Direction); + Direction.X = -v44.X + v10.X; + Direction.Y = -v44.Y + v10.Y; + Speed = maths::normalize_2d(Direction); +} + +float TBall::FindCollisionDistance(const ray_type& ray) +{ + // Original inherits TCircle and aliases position. + const circle_type ballCircle{{Position.X, Position.Y}, Radius * Radius * 4.0f}; + return maths::ray_intersect_circle(ray, ballCircle); +} + vector2 TBall::get_coordinates() { return TTableLayer::edge_manager->NormalizeBox(Position); diff --git a/SpaceCadetPinball/TBall.h b/SpaceCadetPinball/TBall.h index 19a0c83..724cf49 100644 --- a/SpaceCadetPinball/TBall.h +++ b/SpaceCadetPinball/TBall.h @@ -1,14 +1,12 @@ #pragma once #include "maths.h" -#include "TPinballComponent.h" +#include "TCollisionComponent.h" +#include "TEdgeSegment.h" -class TCollisionComponent; -class TEdgeSegment; - -class TBall : public TPinballComponent +class TBall : public TCollisionComponent, public TEdgeSegment { public : - TBall(TPinballTable* table); + TBall(TPinballTable* table, int groupIndex); void Repaint(); void not_again(TEdgeSegment* edge); bool already_hit(TEdgeSegment* edge); @@ -16,6 +14,9 @@ public : vector2 get_coordinates() override; void Disable(); void throw_ball(vector3* direction, float angleMult, float speedMult1, float speedMult2); + void place_in_grid(RectF* aabb) override {} + void EdgeCollision(TBall* ball, float distance) override; + float FindCollisionDistance(const ray_type& ray) override; vector3 Position{}; vector3 PrevPosition{}; diff --git a/SpaceCadetPinball/TCircle.cpp b/SpaceCadetPinball/TCircle.cpp index a3bd49a..8752174 100644 --- a/SpaceCadetPinball/TCircle.cpp +++ b/SpaceCadetPinball/TCircle.cpp @@ -12,9 +12,9 @@ TCircle::TCircle(TCollisionComponent* collComp, char* activeFlag, unsigned colli Circle.Center = *center; } -float TCircle::FindCollisionDistance(ray_type* ray) +float TCircle::FindCollisionDistance(const ray_type& ray) { - return maths::ray_intersect_circle(*ray, Circle); + return maths::ray_intersect_circle(ray, Circle); } void TCircle::EdgeCollision(TBall* ball, float distance) diff --git a/SpaceCadetPinball/TCircle.h b/SpaceCadetPinball/TCircle.h index 7cac2e4..c763e4e 100644 --- a/SpaceCadetPinball/TCircle.h +++ b/SpaceCadetPinball/TCircle.h @@ -10,7 +10,7 @@ public: TCircle(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup, vector2* center, float radius); - float FindCollisionDistance(ray_type* ray) override; + float FindCollisionDistance(const ray_type& ray) override; void EdgeCollision(TBall* ball, float distance) override; void place_in_grid(RectF* aabb) override; }; diff --git a/SpaceCadetPinball/TEdgeManager.cpp b/SpaceCadetPinball/TEdgeManager.cpp index 8eeaa16..70e5fb7 100644 --- a/SpaceCadetPinball/TEdgeManager.cpp +++ b/SpaceCadetPinball/TEdgeManager.cpp @@ -76,7 +76,7 @@ int TEdgeManager::TestGridBox(int x, int y, float* distPtr, TEdgeSegment** edgeD for (auto it = edgeBox->EdgeList.rbegin(); it != edgeBox->EdgeList.rend(); ++it) { auto edge = *it; - if (!edge->ProcessedFlag && *edge->ActiveFlag && (edge->CollisionGroup & ray->CollisionMask)) + if (!edge->ProcessedFlag && *edge->ActiveFlagPtr && (edge->CollisionGroup & ray->CollisionMask)) { if (!ball->already_hit(edge)) { @@ -84,7 +84,7 @@ int TEdgeManager::TestGridBox(int x, int y, float* distPtr, TEdgeSegment** edgeD *edgePtr = edge; ++edgePtr; edge->ProcessedFlag = 1; - auto dist = edge->FindCollisionDistance(ray); + auto dist = edge->FindCollisionDistance(*ray); if (dist < *distPtr) { *distPtr = dist; diff --git a/SpaceCadetPinball/TEdgeSegment.cpp b/SpaceCadetPinball/TEdgeSegment.cpp index 2d78f8b..707a560 100644 --- a/SpaceCadetPinball/TEdgeSegment.cpp +++ b/SpaceCadetPinball/TEdgeSegment.cpp @@ -8,7 +8,7 @@ TEdgeSegment::TEdgeSegment(TCollisionComponent* collComp, char* activeFlag, unsigned collisionGroup) { CollisionComponent = collComp; - ActiveFlag = activeFlag; + ActiveFlagPtr = activeFlag; CollisionGroup = collisionGroup; ProcessedFlag = 0; } diff --git a/SpaceCadetPinball/TEdgeSegment.h b/SpaceCadetPinball/TEdgeSegment.h index 28e75aa..98f4da6 100644 --- a/SpaceCadetPinball/TEdgeSegment.h +++ b/SpaceCadetPinball/TEdgeSegment.h @@ -15,7 +15,7 @@ class TEdgeSegment { public: TCollisionComponent* CollisionComponent; - char* ActiveFlag; + char* ActiveFlagPtr; char ProcessedFlag; void* WallValue{}; unsigned int CollisionGroup; @@ -26,7 +26,7 @@ public: virtual void EdgeCollision(TBall* ball, float distance) = 0; virtual void port_draw(); virtual void place_in_grid(RectF* aabb) = 0; - virtual float FindCollisionDistance(ray_type* ray) = 0; + virtual float FindCollisionDistance(const ray_type& ray) = 0; static TEdgeSegment* install_wall(float* floatArr, TCollisionComponent* collComp, char* activeFlagPtr, unsigned int collisionGroup, float offset, size_t wallValue); diff --git a/SpaceCadetPinball/TFlipperEdge.cpp b/SpaceCadetPinball/TFlipperEdge.cpp index 1f5c18c..810dc1a 100644 --- a/SpaceCadetPinball/TFlipperEdge.cpp +++ b/SpaceCadetPinball/TFlipperEdge.cpp @@ -104,12 +104,12 @@ void TFlipperEdge::port_draw() set_control_points(CurrentAngle); } -float TFlipperEdge::FindCollisionDistance(ray_type* ray) +float TFlipperEdge::FindCollisionDistance(const ray_type& ray) { ray_type dstRay{}; if (ControlPointDirtyFlag) set_control_points(CurrentAngle); - auto distance = maths::distance_to_flipper(this, *ray, dstRay); + auto distance = maths::distance_to_flipper(this, ray, dstRay); if (distance >= 1e9f) return 1e9f; diff --git a/SpaceCadetPinball/TFlipperEdge.h b/SpaceCadetPinball/TFlipperEdge.h index 710f8d9..ab8124c 100644 --- a/SpaceCadetPinball/TFlipperEdge.h +++ b/SpaceCadetPinball/TFlipperEdge.h @@ -12,7 +12,7 @@ public: 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; + float FindCollisionDistance(const ray_type& ray) override; void EdgeCollision(TBall* ball, float distance) override; void place_in_grid(RectF* aabb) override; void set_control_points(float angle); diff --git a/SpaceCadetPinball/TLine.cpp b/SpaceCadetPinball/TLine.cpp index 3d95fb3..f79ee70 100644 --- a/SpaceCadetPinball/TLine.cpp +++ b/SpaceCadetPinball/TLine.cpp @@ -36,9 +36,9 @@ void TLine::Offset(float offset) maths::line_init(Line, X0, Y0, X1, Y1); } -float TLine::FindCollisionDistance(ray_type* ray) +float TLine::FindCollisionDistance(const ray_type& ray) { - return maths::ray_intersect_line(*ray, Line); + return maths::ray_intersect_line(ray, Line); } void TLine::EdgeCollision(TBall* ball, float distance) diff --git a/SpaceCadetPinball/TLine.h b/SpaceCadetPinball/TLine.h index 9a05508..dd6b911 100644 --- a/SpaceCadetPinball/TLine.h +++ b/SpaceCadetPinball/TLine.h @@ -11,7 +11,7 @@ public: TLine(TCollisionComponent* collCmp, char* activeFlag, unsigned int collisionGroup, float x0, float y0, float x1, float y1); TLine(TCollisionComponent* collCmp, char* activeFlag, unsigned int collisionGroup, const vector2& start, const vector2& end); void Offset(float offset); - float FindCollisionDistance(ray_type* ray) override; + float FindCollisionDistance(const ray_type& ray) override; void EdgeCollision(TBall* ball, float distance) override; void place_in_grid(RectF* aabb) override; }; diff --git a/SpaceCadetPinball/TPinballTable.cpp b/SpaceCadetPinball/TPinballTable.cpp index 28c73b4..58c1416 100644 --- a/SpaceCadetPinball/TPinballTable.cpp +++ b/SpaceCadetPinball/TPinballTable.cpp @@ -625,7 +625,7 @@ TBall* TPinballTable::AddBall(float x, float y) { if (BallList.size() >= 20) return nullptr; - ball = new TBall(this); + ball = new TBall(this, -1); BallList.push_back(ball); } @@ -655,6 +655,16 @@ int TPinballTable::BallCountInRect(const RectF& rect) return count; } +int TPinballTable::BallCountInRect(const vector2& pos, float margin) +{ + RectF rect{}; + rect.XMin = pos.X - margin; + rect.XMax = pos.X + margin; + rect.YMin = pos.Y - margin; + rect.YMax = pos.Y + margin; + return BallCountInRect(rect); +} + void TPinballTable::EndGame_timeout(int timerId, void* caller) { auto table = static_cast(caller); diff --git a/SpaceCadetPinball/TPinballTable.h b/SpaceCadetPinball/TPinballTable.h index 190f334..22e76ef 100644 --- a/SpaceCadetPinball/TPinballTable.h +++ b/SpaceCadetPinball/TPinballTable.h @@ -37,6 +37,7 @@ public: int Message(MessageCode code, float value) override; TBall* AddBall(float x, float y); int BallCountInRect(const RectF& rect); + int BallCountInRect(const vector2& pos, float margin); static void EndGame_timeout(int timerId, void* caller); static void LightShow_timeout(int timerId, void* caller); diff --git a/SpaceCadetPinball/pb.cpp b/SpaceCadetPinball/pb.cpp index 4da44b2..de33a59 100644 --- a/SpaceCadetPinball/pb.cpp +++ b/SpaceCadetPinball/pb.cpp @@ -33,7 +33,7 @@ DatFile* pb::record_table = nullptr; int pb::time_ticks = 0; GameModes pb::game_mode = GameModes::GameOver; float pb::time_now = 0, pb::time_next = 0, pb::time_ticks_remainder = 0; -float pb::BallMaxSpeed, pb::BallHalfRadius, pb::ball_collision_dist; +float pb::BallMaxSpeed, pb::BallHalfRadius, pb::BallToBallCollisionDistance; bool pb::FullTiltMode = false, pb::FullTiltDemoMode = false, pb::cheat_mode = false, pb::demo_mode = false; std::string pb::DatFileName, pb::BasePath; ImU32 pb::TextBoxColor; @@ -106,7 +106,7 @@ int pb::init() auto ball = MainTable->BallList.at(0); BallMaxSpeed = ball->Radius * 200.0f; BallHalfRadius = ball->Radius * 0.5f; - ball_collision_dist = (ball->Radius + BallHalfRadius) * 2.0f; + BallToBallCollisionDistance = (ball->Radius + BallHalfRadius) * 2.0f; int red = 255, green = 255, blue = 255; auto fontColor = get_rc_string(Msg::TextBoxColor); @@ -424,8 +424,7 @@ void pb::timed_frame(float timeNow, float timeDelta, bool drawBalls) auto distance = TTableLayer::edge_manager->FindCollisionDistance(&ray, ball, &edge); if (distance > 0.0f) { - // Todo: ball to ball collision - //distance = ball_to_ball_collision(); + distance = BallToBallCollision(ray, *ball, &edge, distance); } if (ball->EdgeCollisionResetFlag) { @@ -605,9 +604,13 @@ void pb::InputDown(GameInput input) switch (input.Value) { case 'b': - if (MainTable->AddBall(6.0f, 7.0f)) - MainTable->MultiballCount++; - break; + { + vector2 pos{6.0f, 7.0f}; + if (!MainTable->BallCountInRect(pos, MainTable->CollisionCompOffset * 1.2f) && MainTable->AddBall( + pos.X, pos.Y)) + MainTable->MultiballCount++; + break; + } case 'h': { high_score_struct entry{{0}, 1000000000}; @@ -754,3 +757,27 @@ void pb::ShowMessageBox(Uint32 flags, LPCSTR title, LPCSTR message) fprintf(flags == SDL_MESSAGEBOX_ERROR ? stderr : stdout, "BL error: %s\n%s\n", title, message); SDL_ShowSimpleMessageBox(flags, title, message, winmain::MainWindow); } + +float pb::BallToBallCollision(const ray_type& ray, const TBall& ball, TEdgeSegment** edge, float collisionDistance) +{ + for (auto curBall : MainTable->BallList) + { + if (curBall->ActiveFlagPtr && curBall != &ball && (curBall->CollisionMask & ball.CollisionMask) != 0 && + std::abs(curBall->Position.X - ball.Position.X) < BallToBallCollisionDistance && + std::abs(curBall->Position.Y - ball.Position.Y) < BallToBallCollisionDistance) + { + auto distance = curBall->FindCollisionDistance(ray); + if (distance < 1e9f) + { + distance = std::max(0.0f, distance - 0.002f); + if (collisionDistance > distance) + { + collisionDistance = distance; + *edge = curBall; + } + } + } + } + + return collisionDistance; +} diff --git a/SpaceCadetPinball/pb.h b/SpaceCadetPinball/pb.h index 3e79e56..2a5866c 100644 --- a/SpaceCadetPinball/pb.h +++ b/SpaceCadetPinball/pb.h @@ -1,6 +1,8 @@ #pragma once #include "high_score.h" +class TEdgeSegment; +struct ray_type; struct GameInput; class TPinballTable; class DatFile; @@ -43,7 +45,7 @@ class pb public: static int time_ticks; static float time_now, time_next, time_ticks_remainder; - static float BallMaxSpeed, BallHalfRadius, ball_collision_dist; + static float BallMaxSpeed, BallHalfRadius, BallToBallCollisionDistance; static GameModes game_mode; static bool cheat_mode; static DatFile* record_table; @@ -81,4 +83,5 @@ public: static void ShowMessageBox(Uint32 flags, LPCSTR title, LPCSTR message); private: static bool demo_mode; + static float BallToBallCollision(const ray_type& ray, const TBall& ball, TEdgeSegment** edge, float collisionDistance); };