From 9bd064bf159a8c34dbd486568081da4f08566452 Mon Sep 17 00:00:00 2001 From: oz Date: Thu, 7 Jan 2021 19:00:38 +0300 Subject: [PATCH] TPlunger ready. TEdgeManager v1. --- Doc/FuncStats.xlsx | Bin 38023 -> 38203 bytes SpaceCadetPinball/SpaceCadetPinball.vcxproj | 2 + .../SpaceCadetPinball.vcxproj.filters | 6 + SpaceCadetPinball/TBall.cpp | 8 +- SpaceCadetPinball/TBall.h | 2 +- SpaceCadetPinball/TCircle.cpp | 16 +- SpaceCadetPinball/TCircle.h | 7 +- SpaceCadetPinball/TCollisionComponent.cpp | 32 +-- SpaceCadetPinball/TCollisionComponent.h | 11 +- SpaceCadetPinball/TEdgeBox.cpp | 16 ++ SpaceCadetPinball/TEdgeBox.h | 13 + SpaceCadetPinball/TEdgeManager.cpp | 266 +++++++++++++++++- SpaceCadetPinball/TEdgeManager.h | 32 ++- SpaceCadetPinball/TEdgeSegment.cpp | 2 +- SpaceCadetPinball/TEdgeSegment.h | 4 +- SpaceCadetPinball/TLightGroup.cpp | 2 +- SpaceCadetPinball/TLine.cpp | 182 +++++++++++- SpaceCadetPinball/TLine.h | 12 +- SpaceCadetPinball/TPinballTable.h | 10 +- SpaceCadetPinball/TPlunger.cpp | 169 +++++++++++ SpaceCadetPinball/TPlunger.h | 19 +- SpaceCadetPinball/TTableLayer.cpp | 78 ++++- SpaceCadetPinball/TTableLayer.h | 16 +- SpaceCadetPinball/control.cpp | 36 +++ SpaceCadetPinball/control.h | 1 + SpaceCadetPinball/maths.cpp | 28 +- SpaceCadetPinball/maths.h | 8 +- SpaceCadetPinball/pb.cpp | 6 +- 28 files changed, 866 insertions(+), 118 deletions(-) create mode 100644 SpaceCadetPinball/TEdgeBox.cpp create mode 100644 SpaceCadetPinball/TEdgeBox.h diff --git a/Doc/FuncStats.xlsx b/Doc/FuncStats.xlsx index ed22afb5506446b4d545dab2ddfa8f55a06c1ea3..31388c38e3087233252b48ddf9e51eba504cd591 100644 GIT binary patch delta 30063 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 z%0A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%0AY9YJT?#7GU@Y;tR2E7l&(tXaqveM5R}2Jt5I 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 RSXfbUCh@FzZ87Ly{eQA^R;B;| delta 29847 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 diff --git a/SpaceCadetPinball/SpaceCadetPinball.vcxproj b/SpaceCadetPinball/SpaceCadetPinball.vcxproj index 56c65af..91c58c3 100644 --- a/SpaceCadetPinball/SpaceCadetPinball.vcxproj +++ b/SpaceCadetPinball/SpaceCadetPinball.vcxproj @@ -184,6 +184,7 @@ + @@ -252,6 +253,7 @@ + diff --git a/SpaceCadetPinball/SpaceCadetPinball.vcxproj.filters b/SpaceCadetPinball/SpaceCadetPinball.vcxproj.filters index 4088f83..57f0cf1 100644 --- a/SpaceCadetPinball/SpaceCadetPinball.vcxproj.filters +++ b/SpaceCadetPinball/SpaceCadetPinball.vcxproj.filters @@ -216,6 +216,9 @@ Header Files\TPinballComponent + + Header Files\TPinballComponent + @@ -398,6 +401,9 @@ Source Files\TPinballComponent + + Source Files\TPinballComponent + diff --git a/SpaceCadetPinball/TBall.cpp b/SpaceCadetPinball/TBall.cpp index 574873b..1fda769 100644 --- a/SpaceCadetPinball/TBall.cpp +++ b/SpaceCadetPinball/TBall.cpp @@ -20,7 +20,7 @@ TBall::TBall(TPinballTable* table) : TPinballComponent(table, -1, false) CollisionComp = nullptr; EdgeCollisionCount = 0; TimeDelta = 0.0; - Unknown17F = 1; + FieldFlag = 1; CollisionFlag = 0; Speed = 0.0; Acceleration.Y = 0.0; @@ -83,8 +83,8 @@ void TBall::Repaint() RenderSprite, bmp, zDepth, - bmp->Width / 2 - pos2D[0], - bmp->Height / 2 - pos2D[1]); + pos2D[0] - bmp->Width / 2, + pos2D[1] - bmp->Height / 2); } void TBall::not_again(TEdgeSegment* edge) @@ -117,7 +117,7 @@ int TBall::Message(int code, float value) Position.Y = 0.0; UnknownBaseFlag2 = 0; CollisionFlag = 0; - Unknown17F = 1; + FieldFlag = 1; Acceleration.Y = 0.0; Position.Z = Offset; Acceleration.X = 0.0; diff --git a/SpaceCadetPinball/TBall.h b/SpaceCadetPinball/TBall.h index e7ef2a3..f64366d 100644 --- a/SpaceCadetPinball/TBall.h +++ b/SpaceCadetPinball/TBall.h @@ -25,7 +25,7 @@ public : int Unknown14; int Unknown15; TCollisionComponent* CollisionComp; - float Unknown17F; + int FieldFlag; TEdgeSegment* Collisions[5]; int EdgeCollisionCount; vector_type CollisionOffset; diff --git a/SpaceCadetPinball/TCircle.cpp b/SpaceCadetPinball/TCircle.cpp index c8d00b6..8800cdc 100644 --- a/SpaceCadetPinball/TCircle.cpp +++ b/SpaceCadetPinball/TCircle.cpp @@ -8,7 +8,19 @@ TCircle::TCircle(TCollisionComponent* collComp, char* someFlagPtr, unsigned visu Circle.Center = *center; } -double TCircle::FindCollisionDistance(ray_type* ray) +float TCircle::FindCollisionDistance(ray_type* ray) { return maths::ray_intersect_circle(ray, &Circle); -} \ No newline at end of file +} + +void TCircle::EdgeCollision(TBall* ball, float coef) +{ + vector_type direction{}, nextPosition{}; + + nextPosition.X = coef * ball->Acceleration.X + ball->Position.X; + nextPosition.Y = coef * ball->Acceleration.Y + ball->Position.Y; + direction.X = nextPosition.X - Circle.Center.X; + direction.Y = nextPosition.Y - Circle.Center.Y; + maths::normalize_2d(&direction); + CollisionComponent->Collision(ball, &nextPosition, &direction, coef, this); +} diff --git a/SpaceCadetPinball/TCircle.h b/SpaceCadetPinball/TCircle.h index ce17f1a..779128c 100644 --- a/SpaceCadetPinball/TCircle.h +++ b/SpaceCadetPinball/TCircle.h @@ -10,11 +10,8 @@ public: TCircle(TCollisionComponent* collComp, char* someFlagPtr, unsigned int visualFlag, vector_type* center, float radius); - double FindCollisionDistance(ray_type* ray) override; - - void EdgeCollision(TBall* ball, float coef) override - { - } + float FindCollisionDistance(ray_type* ray) override; + void EdgeCollision(TBall* ball, float coef) override; void place_in_grid() override { diff --git a/SpaceCadetPinball/TCollisionComponent.cpp b/SpaceCadetPinball/TCollisionComponent.cpp index 762b136..efb9aff 100644 --- a/SpaceCadetPinball/TCollisionComponent.cpp +++ b/SpaceCadetPinball/TCollisionComponent.cpp @@ -29,10 +29,10 @@ TCollisionComponent::TCollisionComponent(TPinballTable* table, int groupIndex, b } } - UnknownC7F = visual.Kicker.Unknown1F; + MaxCollisionSpeed = visual.Kicker.Unknown1F; UnknownC4F = visual.Unknown2F; UnknownC5F = visual.Unknown1F; - UnknownC6F = visual.Kicker.Unknown2F; + CollisionMultiplier = visual.Kicker.Unknown2F; SoundIndex1 = visual.Kicker.SoundIndex; SoundIndex2 = visual.SoundIndex2; GroupIndex = groupIndex; @@ -58,17 +58,17 @@ void TCollisionComponent::port_draw() } } -int TCollisionComponent::DefaultCollision(TBall* ball, vector_type* ballPosition, vector_type* vec2) +int TCollisionComponent::DefaultCollision(TBall* ball, vector_type* nextPosition, vector_type* direction) { if (PinballTable->TiltLockFlag) { - maths::basic_collision(ball, ballPosition, vec2, UnknownC4F, UnknownC5F, 1000000000.0, 0.0); + maths::basic_collision(ball, nextPosition, direction, UnknownC4F, UnknownC5F, 1000000000.0, 0.0); return 0; } - auto projSpeed = maths::basic_collision(ball, ballPosition, vec2, UnknownC4F, UnknownC5F, - UnknownC7F, - UnknownC6F); - if (projSpeed <= UnknownC7F) + auto projSpeed = maths::basic_collision(ball, nextPosition, direction, UnknownC4F, UnknownC5F, + MaxCollisionSpeed, + CollisionMultiplier); + if (projSpeed <= MaxCollisionSpeed) { if (projSpeed > 0.2) { @@ -82,25 +82,25 @@ int TCollisionComponent::DefaultCollision(TBall* ball, vector_type* ballPosition return 1; } -void TCollisionComponent::Collision(TBall* ball, struct vector_type* ballPosition, struct vector_type* vec2, - float someVal, TEdgeSegment* edge) +void TCollisionComponent::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, + float coef, TEdgeSegment* edge) { int soundIndex; if (PinballTable->TiltLockFlag) { - maths::basic_collision(ball, ballPosition, vec2, UnknownC4F, UnknownC5F, 1000000000.0, 0.0); + maths::basic_collision(ball, nextPosition, direction, UnknownC4F, UnknownC5F, 1000000000.0, 0.0); return; } double projSpeed = maths::basic_collision( ball, - ballPosition, - vec2, + nextPosition, + direction, UnknownC4F, UnknownC5F, - UnknownC7F, - UnknownC6F); - if (projSpeed <= UnknownC7F) + MaxCollisionSpeed, + CollisionMultiplier); + if (projSpeed <= MaxCollisionSpeed) { if (projSpeed <= 0.2) return; diff --git a/SpaceCadetPinball/TCollisionComponent.h b/SpaceCadetPinball/TCollisionComponent.h index dc66cd2..ba097e7 100644 --- a/SpaceCadetPinball/TCollisionComponent.h +++ b/SpaceCadetPinball/TCollisionComponent.h @@ -2,6 +2,7 @@ #include "objlist_class.h" #include "TPinballComponent.h" +struct vector_type; class TEdgeSegment; class TBall; @@ -13,16 +14,16 @@ public: __int16 UnknownC3; float UnknownC4F; float UnknownC5F; - float UnknownC6F; - float UnknownC7F; + float CollisionMultiplier; + float MaxCollisionSpeed; int SoundIndex2; int SoundIndex1; TCollisionComponent(TPinballTable* table, int groupIndex, bool createWall); ~TCollisionComponent(); void port_draw() override; - virtual void Collision(TBall* ball, struct vector_type* ballPosition, struct vector_type* vec2, float someVal, + virtual void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef, TEdgeSegment* edge); - virtual int FieldEffect(TBall* ball, struct vector_type* vecDst); - int DefaultCollision(TBall* ball, struct vector_type* ballPosition, struct vector_type* vec2); + virtual int FieldEffect(TBall* ball, vector_type* vecDst); + int DefaultCollision(TBall* ball, vector_type* nextPosition, vector_type* direction); }; diff --git a/SpaceCadetPinball/TEdgeBox.cpp b/SpaceCadetPinball/TEdgeBox.cpp new file mode 100644 index 0000000..6d998b5 --- /dev/null +++ b/SpaceCadetPinball/TEdgeBox.cpp @@ -0,0 +1,16 @@ +#include "pch.h" +#include "TEdgeBox.h" + +#include "objlist_class.h" + +TEdgeBox::TEdgeBox() +{ + EdgeList = new objlist_class(0, 4); + FieldList = new objlist_class(0, 1); +} + +TEdgeBox::~TEdgeBox() +{ + delete EdgeList; + delete FieldList; +} diff --git a/SpaceCadetPinball/TEdgeBox.h b/SpaceCadetPinball/TEdgeBox.h new file mode 100644 index 0000000..4a25313 --- /dev/null +++ b/SpaceCadetPinball/TEdgeBox.h @@ -0,0 +1,13 @@ +#pragma once +class objlist_class; + +class TEdgeBox +{ +public: + TEdgeBox(); + ~TEdgeBox(); + + objlist_class* EdgeList; + objlist_class* FieldList; +}; + diff --git a/SpaceCadetPinball/TEdgeManager.cpp b/SpaceCadetPinball/TEdgeManager.cpp index b22c884..0d98a37 100644 --- a/SpaceCadetPinball/TEdgeManager.cpp +++ b/SpaceCadetPinball/TEdgeManager.cpp @@ -1,16 +1,274 @@ #include "pch.h" #include "TEdgeManager.h" -int TEdgeManager::FieldEffects(TBall* ball, vector_type* vecDst) +#include "TEdgeBox.h" +#include "TTableLayer.h" + +TEdgeManager::TEdgeManager(float posX, float posY, float width, float height) { - return 0; + X = posX; + Y = posY; + MaxBoxX = 10; + MaxBoxY = 15; + AdvanceX = width / 10.0f; + AdvanceY = height / 15.0f; + AdvanceXInv = 1.0f / AdvanceX; + AdvanceYInv = 1.0f / AdvanceY; + BoxArray = new TEdgeBox[150]; } -void TEdgeManager::edges_insert_square(float a1, float a2, float a3, float a4, TEdgeSegment* a5, field_effect_type* a6) +TEdgeManager::~TEdgeManager() { + delete[] BoxArray; +} + +int TEdgeManager::box_x(float x) +{ + return static_cast((max(0, min(floor((x - X) * AdvanceXInv), (MaxBoxX - 1))))); +} + +int TEdgeManager::box_y(float y) +{ + return static_cast((max(0, min(floor((y - Y) * AdvanceYInv), (MaxBoxY - 1))))); +} + +int TEdgeManager::increment_box_x(int x) +{ + return min(x + 1, MaxBoxX - 1); +} + +int TEdgeManager::increment_box_y(int y) +{ + return min(y + 1, MaxBoxY - 1); +} + +void TEdgeManager::add_edge_to_box(int x, int y, TEdgeSegment* edge) +{ + BoxArray[x + y * MaxBoxX].EdgeList->Add(edge); +} + +void TEdgeManager::add_field_to_box(int x, int y, field_effect_type* field) +{ + BoxArray[x + y * MaxBoxX].FieldList->Add(field); +} + +int TEdgeManager::TestGridBox(int x, int y, float* distPtr, TEdgeSegment** edgeDst, ray_type* ray, TBall* ball, + int edgeIndex) +{ + if (x >= 0 && x < 10 && y >= 0 && y < 15) + { + TEdgeBox* edgeBox = &BoxArray[x + y * MaxBoxX]; + TEdgeSegment** edgePtr = &EdgeArray[edgeIndex]; + for (auto index = edgeBox->EdgeList->Count() - 1; index >= 0; --index) + { + auto edge = static_cast(edgeBox->EdgeList->Get(index)); + if (!edge->ProcessedFlag && *edge->PinbCompFlag2Ptr && (edge->VisualFlag & ray->FieldFlag)) + { + if (!ball->already_hit(edge)) + { + ++edgeIndex; + *edgePtr = edge; + ++edgePtr; + edge->ProcessedFlag = 1; + auto dist = edge->FindCollisionDistance(ray); + if (dist < *distPtr) + { + *distPtr = dist; + *edgeDst = edge; + } + } + } + } + } + return edgeIndex; +} + +void TEdgeManager::FieldEffects(TBall* ball, vector_type* dstVec) +{ + vector_type vec{}; + TEdgeBox* edgeBox = &BoxArray[box_x(ball->Position.X) + box_y(ball->Position.Y) * + MaxBoxX]; + + for (int index = edgeBox->FieldList->Count() - 1; index >= 0; --index) + { + auto field = static_cast(edgeBox->FieldList->Get(index)); + if (*field->Flag2Ptr && ball->FieldFlag & field->Mask) + { + if (field->CollisionComp->FieldEffect(ball, &vec)) + { + maths::vector_add(dstVec, &vec); + } + } + } } float TEdgeManager::FindCollisionDistance(ray_type* ray, TBall* ball, TEdgeSegment** edge) { - return 1000000000.0; + auto distance = 1000000000.0f; + auto edgeIndex = 0; + + auto rayX = ray->Origin.X; + auto rayY = ray->Origin.Y; + auto rayBoxX = box_x(rayX); + auto rayBoxY = box_y(rayY); + + auto rayEndX = ray->Direction.X * ray->MaxDistance + ray->Origin.X; + auto rayEndY = ray->Direction.Y * ray->MaxDistance + ray->Origin.Y; + auto rayEndBoxX = box_x(rayEndX); + auto rayEndBoxY = box_y(rayEndY); + + auto rayDirX = rayX >= rayEndX ? -1 : 1; + auto rayDirY = rayY >= rayEndY ? -1 : 1; + + if (rayBoxY == rayEndBoxY) + { + if (rayDirX == 1) + { + for (auto indexX = rayBoxX; indexX <= rayEndBoxX; indexX++) + { + edgeIndex = TestGridBox(indexX, rayBoxY, &distance, edge, ray, ball, edgeIndex); + } + } + else + { + for (auto indexX = rayBoxX; indexX >= rayEndBoxX; indexX--) + { + edgeIndex = TestGridBox(indexX, rayBoxY, &distance, edge, ray, ball, edgeIndex); + } + } + } + else + { + if (rayBoxX == rayEndBoxX) + { + if (rayDirY == 1) + { + for (auto indexY = rayBoxY; indexY <= rayEndBoxY; indexY++) + { + edgeIndex = TestGridBox(rayBoxX, indexY, &distance, edge, ray, ball, edgeIndex); + } + } + else + { + for (auto indexY = rayBoxY; indexY >= rayEndBoxY; indexY--) + { + edgeIndex = TestGridBox(rayBoxX, indexY, &distance, edge, ray, ball, edgeIndex); + } + } + } + else + { + auto rayDyDX = (rayY - rayEndY) / (rayX - rayEndX); + auto indexX = rayBoxX; + auto indexY = rayBoxY; + auto bresIndexX = rayBoxX + 1; + auto bresIndexY = rayBoxY + 1; + auto bresXAdd = rayY - rayDyDX * rayX; + edgeIndex = TestGridBox(rayBoxX, rayBoxY, &distance, edge, ray, ball, 0); + if (rayDirX == 1) + { + if (rayDirY == 1) + { + do + { + auto yCoord = bresIndexY * AdvanceY + Y; + auto xCoord = (bresIndexX * AdvanceX + X) * rayDyDX + bresXAdd; + if (xCoord >= yCoord) + { + if (xCoord == yCoord) + { + ++indexX; + ++bresIndexX; + } + ++indexY; + ++bresIndexY; + } + else + { + ++indexX; + ++bresIndexX; + } + edgeIndex = TestGridBox(indexX, indexY, &distance, edge, ray, ball, edgeIndex); + } + while (indexX < rayEndBoxX || indexY < rayEndBoxY); + } + else + { + do + { + auto yCoord = indexY * AdvanceY + Y; + auto xCoord = (bresIndexX * AdvanceX + X) * rayDyDX + bresXAdd; + if (xCoord <= yCoord) + { + if (xCoord == yCoord) + { + ++indexX; + ++bresIndexX; + } + --indexY; + } + else + { + ++indexX; + ++bresIndexX; + } + edgeIndex = TestGridBox(indexX, indexY, &distance, edge, ray, ball, edgeIndex); + } + while (indexX < rayEndBoxX || indexY > rayEndBoxY); + } + } + else + { + if (rayDirY == 1) + { + do + { + auto yCoord = bresIndexY * AdvanceY + Y; + auto xCoord = (indexX * AdvanceX + X) * rayDyDX + bresXAdd; + if (xCoord >= yCoord) + { + if (xCoord == yCoord) + --indexX; + ++indexY; + ++bresIndexY; + } + else + { + --indexX; + } + edgeIndex = TestGridBox(indexX, indexY, &distance, edge, ray, ball, edgeIndex); + } + while (indexX > rayEndBoxX || indexY < rayEndBoxY); + } + else + { + do + { + auto yCoord = indexY * AdvanceY + Y; + auto xCoord = (indexX * AdvanceX + X) * rayDyDX + bresXAdd; + if (xCoord <= yCoord) + { + if (xCoord == yCoord) + --indexX; + --indexY; + } + else + { + --indexX; + } + edgeIndex = TestGridBox(indexX, indexY, &distance, edge, ray, ball, edgeIndex); + } + while (indexX > rayEndBoxX || indexY > rayEndBoxY); + } + } + } + } + + + for (auto edgePtr = EdgeArray; edgeIndex > 0; --edgeIndex, ++edgePtr) + { + (*edgePtr)->ProcessedFlag = 0; + } + + return distance; } diff --git a/SpaceCadetPinball/TEdgeManager.h b/SpaceCadetPinball/TEdgeManager.h index 5e34342..d203862 100644 --- a/SpaceCadetPinball/TEdgeManager.h +++ b/SpaceCadetPinball/TEdgeManager.h @@ -2,22 +2,38 @@ #include "TCollisionComponent.h" #include "TEdgeSegment.h" +class TEdgeBox; + struct field_effect_type { char* Flag2Ptr; - int Unknown1; + int Mask; TCollisionComponent* CollisionComp; }; class TEdgeManager { public: - TEdgeManager(float a2, float a3, float a4, float a5) - { - } - - int FieldEffects(TBall* ball, struct vector_type* vecDst); - - static void edges_insert_square(float a1, float a2, float a3, float a4, TEdgeSegment* a5, field_effect_type* a6); + TEdgeManager(float posX, float posY, float width, float height); + ~TEdgeManager(); + void FieldEffects(TBall* ball, struct vector_type* dstVec); + int box_x(float x); + int box_y(float y); + int increment_box_x(int x); + int increment_box_y(int y); + void add_edge_to_box(int x, int y, TEdgeSegment* edge); + void add_field_to_box(int x, int y, field_effect_type* field); + int TestGridBox(int x, int y, float* distPtr, TEdgeSegment** edgeDst, ray_type* ray, TBall* ball, int edgeIndex); float FindCollisionDistance(ray_type* ray, TBall* ball, TEdgeSegment** edge); + + float AdvanceX; + float AdvanceY; + float AdvanceXInv; + float AdvanceYInv; + int MaxBoxX; + int MaxBoxY; + float X; + float Y; + TEdgeBox* BoxArray; + TEdgeSegment* EdgeArray[1000]; }; diff --git a/SpaceCadetPinball/TEdgeSegment.cpp b/SpaceCadetPinball/TEdgeSegment.cpp index 95235b7..2ab031a 100644 --- a/SpaceCadetPinball/TEdgeSegment.cpp +++ b/SpaceCadetPinball/TEdgeSegment.cpp @@ -8,7 +8,7 @@ TEdgeSegment::TEdgeSegment(TCollisionComponent* collComp, char* someFlag, unsign this->CollisionComponent = collComp; this->PinbCompFlag2Ptr = someFlag; this->VisualFlag = visualFlag; - this->Unknown3_0 = 0; + this->ProcessedFlag = 0; } void TEdgeSegment::port_draw() diff --git a/SpaceCadetPinball/TEdgeSegment.h b/SpaceCadetPinball/TEdgeSegment.h index 286a218..cf2ac91 100644 --- a/SpaceCadetPinball/TEdgeSegment.h +++ b/SpaceCadetPinball/TEdgeSegment.h @@ -14,7 +14,7 @@ class TEdgeSegment public: TCollisionComponent* CollisionComponent; char* PinbCompFlag2Ptr; - char Unknown3_0; + char ProcessedFlag; int WallValue; int VisualFlag; @@ -24,7 +24,7 @@ public: virtual void EdgeCollision(TBall* ball, float coef) = 0; virtual void port_draw(); virtual void place_in_grid() = 0; - virtual double FindCollisionDistance(ray_type* ray) = 0; + virtual float FindCollisionDistance(ray_type* ray) = 0; static TEdgeSegment* install_wall(float* floatArr, TCollisionComponent* collComp, char* flagPtr, unsigned int visual_flag, float offset, int someValue); diff --git a/SpaceCadetPinball/TLightGroup.cpp b/SpaceCadetPinball/TLightGroup.cpp index f9cb24c..bc7eef5 100644 --- a/SpaceCadetPinball/TLightGroup.cpp +++ b/SpaceCadetPinball/TLightGroup.cpp @@ -9,7 +9,7 @@ #include "TLight.h" #include "TPinballTable.h" -TLightGroup::TLightGroup(TPinballTable* table, int groupIndex) : TPinballComponent(table, -1, false) +TLightGroup::TLightGroup(TPinballTable* table, int groupIndex) : TPinballComponent(table, groupIndex, false) { List = new objlist_class(4, 4); Timer = 0; diff --git a/SpaceCadetPinball/TLine.cpp b/SpaceCadetPinball/TLine.cpp index 66b5a7c..e67707b 100644 --- a/SpaceCadetPinball/TLine.cpp +++ b/SpaceCadetPinball/TLine.cpp @@ -1,24 +1,26 @@ #include "pch.h" #include "TLine.h" +#include "TTableLayer.h" + TLine::TLine(TCollisionComponent* collCmp, char* flagPtr, unsigned int visualFlag, float x0, float y0, float x1, float y1): TEdgeSegment(collCmp, flagPtr, visualFlag) { - this->X0 = x0; - this->Y0 = y0; - this->X1 = x1; - this->Y1 = y1; + X0 = x0; + Y0 = y0; + X1 = x1; + Y1 = y1; maths::line_init(&Line, x0, y0, x1, y1); } TLine::TLine(TCollisionComponent* collCmp, char* flagPtr, unsigned int visualFlag, struct vector_type* start, struct vector_type* end) : TEdgeSegment(collCmp, flagPtr, visualFlag) { - this->X0 = start->X; - this->Y0 = start->Y; - this->X1 = end->X; - this->Y1 = end->Y; + X0 = start->X; + Y0 = start->Y; + X1 = end->X; + Y1 = end->Y; maths::line_init(&Line, X0, Y0, X1, Y1); } @@ -34,7 +36,169 @@ void TLine::Offset(float offset) maths::line_init(&Line, X0, Y0, X1, Y1); } -double TLine::FindCollisionDistance(ray_type* ray) +float TLine::FindCollisionDistance(ray_type* ray) { return maths::ray_intersect_line(ray, &Line); } + +void TLine::EdgeCollision(TBall* ball, float coef) +{ + CollisionComponent->Collision( + ball, + &Line.RayIntersect, + &Line.PerpendicularL, + coef, + this); +} + +void TLine::place_in_grid() +{ + auto xBox0 = TTableLayer::edge_manager->box_x(X0); + auto yBox0 = TTableLayer::edge_manager->box_y(Y0); + auto xBox1 = TTableLayer::edge_manager->box_x(X1); + auto yBox1 = TTableLayer::edge_manager->box_y(Y1); + + int dirX = X0 >= X1 ? -1 : 1; + int dirY = Y0 >= Y1 ? -1 : 1; + + if (yBox0 == yBox1) + { + if (dirX == 1) + { + while (xBox0 <= xBox1) + TTableLayer::edge_manager->add_edge_to_box(xBox0++, yBox0, this); + } + else + { + while (xBox0 >= xBox1) + TTableLayer::edge_manager->add_edge_to_box(xBox0--, yBox0, this); + } + } + else if (xBox0 == xBox1) + { + if (dirY == 1) + { + if (yBox0 <= yBox1) + { + do + TTableLayer::edge_manager->add_edge_to_box(xBox0, yBox0++, this); + while (yBox0 <= yBox1); + } + } + else if (yBox0 >= yBox1) + { + do + TTableLayer::edge_manager->add_edge_to_box(xBox0, yBox0--, this); + while (yBox0 >= yBox1); + } + } + else + { + float yCoord, xCoord; + int indexX1 = xBox0, indexY1 = yBox0; + int bresIndexX = xBox0 + 1, bresIndexY = yBox0 + 1; + auto bresDyDx = (Y0 - Y1) / (X0 - X1); + auto bresXAdd = Y0 - bresDyDx * X0; + TTableLayer::edge_manager->add_edge_to_box(xBox0, yBox0, this); + if (dirX == 1) + { + if (dirY == 1) + { + do + { + yCoord = bresIndexY * TTableLayer::edge_manager->AdvanceY + TTableLayer::edge_manager->Y; + xCoord = (bresIndexX * TTableLayer::edge_manager->AdvanceX + TTableLayer::edge_manager->X) * + bresDyDx + bresXAdd; + if (xCoord >= yCoord) + { + if (xCoord == yCoord) + { + ++indexX1; + ++bresIndexX; + } + ++indexY1; + ++bresIndexY; + } + else + { + ++indexX1; + ++bresIndexX; + } + TTableLayer::edge_manager->add_edge_to_box(indexX1, indexY1, this); + } + while (indexX1 != xBox1 || indexY1 != yBox1); + } + else + { + do + { + yCoord = indexY1 * TTableLayer::edge_manager->AdvanceY + TTableLayer::edge_manager->Y; + xCoord = (bresIndexX * TTableLayer::edge_manager->AdvanceX + TTableLayer::edge_manager->X) * + bresDyDx + bresXAdd; + if (xCoord <= yCoord) + { + if (xCoord == yCoord) + { + ++indexX1; + ++bresIndexX; + } + --indexY1; + } + else + { + ++indexX1; + ++bresIndexX; + } + TTableLayer::edge_manager->add_edge_to_box(indexX1, indexY1, this); + } + while (indexX1 != xBox1 || indexY1 != yBox1); + } + } + else + { + if (dirY == 1) + { + do + { + xCoord = bresIndexY * TTableLayer::edge_manager->AdvanceY + TTableLayer::edge_manager->Y; + yCoord = (indexX1 * TTableLayer::edge_manager->AdvanceX + TTableLayer::edge_manager->X) * + bresDyDx + bresXAdd; + if (yCoord >= xCoord) + { + if (yCoord == xCoord) + --indexX1; + ++indexY1; + ++bresIndexY; + } + else + { + --indexX1; + } + TTableLayer::edge_manager->add_edge_to_box(indexX1, indexY1, this); + } + while (indexX1 != xBox1 || indexY1 != yBox1); + } + else + { + do + { + yCoord = indexY1 * TTableLayer::edge_manager->AdvanceY + TTableLayer::edge_manager->Y; + xCoord = (indexX1 * TTableLayer::edge_manager->AdvanceX + TTableLayer::edge_manager->X) * + bresDyDx + bresXAdd; + if (xCoord <= yCoord) + { + if (xCoord == yCoord) + --indexX1; + --indexY1; + } + else + { + --indexX1; + } + TTableLayer::edge_manager->add_edge_to_box(indexX1, indexY1, this); + } + while (indexX1 != xBox1 || indexY1 != yBox1); + } + } + } +} diff --git a/SpaceCadetPinball/TLine.h b/SpaceCadetPinball/TLine.h index ffe88c4..d4fd798 100644 --- a/SpaceCadetPinball/TLine.h +++ b/SpaceCadetPinball/TLine.h @@ -12,13 +12,7 @@ public: TLine(TCollisionComponent* collCmp, char* flagPtr, unsigned int visualFlag, struct vector_type* start, struct vector_type* end); void Offset(float offset); - double FindCollisionDistance(ray_type* ray) override; - - void EdgeCollision(TBall* ball, float coef) override - { - } - - void place_in_grid() override - { - } + float FindCollisionDistance(ray_type* ray) override; + void EdgeCollision(TBall* ball, float coef) override; + void place_in_grid() override; }; diff --git a/SpaceCadetPinball/TPinballTable.h b/SpaceCadetPinball/TPinballTable.h index 39e7897..1b538b3 100644 --- a/SpaceCadetPinball/TPinballTable.h +++ b/SpaceCadetPinball/TPinballTable.h @@ -68,12 +68,12 @@ public: objlist_class* ComponentList; objlist_class* BallList; TLightGroup* LightGroup; - float TableAngleMult; - float TableAngle1; - float TableAngle2; + float GravityDirVectMult; + float GravityAngleX; + float GravityAnglY; float CollisionCompOffset; - int UnknownP62; - int UnknownP63; + float PlungerPositionX; + float PlungerPositionY; int ScoreMultiplier; int ScoreAdded; int ScoreSpecial1; diff --git a/SpaceCadetPinball/TPlunger.cpp b/SpaceCadetPinball/TPlunger.cpp index f32574c..209910a 100644 --- a/SpaceCadetPinball/TPlunger.cpp +++ b/SpaceCadetPinball/TPlunger.cpp @@ -1,2 +1,171 @@ #include "pch.h" #include "TPlunger.h" + + +#include "control.h" +#include "loader.h" +#include "maths.h" +#include "pb.h" +#include "render.h" +#include "TBall.h" +#include "timer.h" +#include "TPinballTable.h" +#include "TZmapList.h" + +TPlunger::TPlunger(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, true) +{ + visualStruct visual{}; + + loader::query_visual(groupIndex, 0, &visual); + CollisionMultiplier = 0.0; + BallFeedTimer_ = 0; + PullbackTimer_ = 0; + SoundIndexP1 = visual.SoundIndex4; + SoundIndexP2 = visual.SoundIndex3; + SoundIndex1 = visual.Kicker.SoundIndex; + MaxCollisionSpeed = 1000000000.0; + MaxPullback = 100; + UnknownC4F = 0.5f; + UnknownC5F = 0.5f; + PullbackIncrement = static_cast(100.0 / (ListBitmap->Count() * 8.0)); + Unknown4F = 0.025f; + float* floatArr = loader::query_float_attribute(groupIndex, 0, 601); + table->PlungerPositionX = floatArr[0]; + table->PlungerPositionY = floatArr[1]; +} + +void TPlunger::Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef, TEdgeSegment* edge) +{ + if (PinballTable->TiltLockFlag) + Message(1017, 0.0); + coef = static_cast(rand()) * 0.00003051850947599719f * CollisionMultiplier * 0.1f + CollisionMultiplier; + maths::basic_collision(ball, nextPosition, direction, UnknownC4F, UnknownC5F, MaxCollisionSpeed, coef); +} + +int TPlunger::Message(int code, float value) +{ + switch (code) + { + case 1004: + if (!PullbackTimer_) + { + CollisionMultiplier = 0.0; + MaxCollisionSpeed = 1000000000.0; + loader::play_sound(SoundIndex1); + PullbackTimer(0, this); + } + return 0; + case 1005: + case 1009: + case 1010: + { + MaxCollisionSpeed = 0.0; + if (PullbackTimer_) + timer::kill(PullbackTimer_); + PullbackTimer_ = 0; + if (code == 1005) + loader::play_sound(SoundIndexP2); + auto bmp = static_cast(ListBitmap->Get(0)); + auto zMap = static_cast(ListZMap->Get(0)); + render::sprite_set( + RenderSprite, + bmp, + zMap, + bmp->XPosition - PinballTable->XOffset, + bmp->YPosition - PinballTable->YOffset); + + timer::set(Unknown4F, this, PlungerReleasedTimer); + break; + } + case 1015: + { + auto ball = static_cast(PinballTable->ComponentList->Get(0)); + ball->Message(1024, 0.0); + ball->Position.X = PinballTable->PlungerPositionX; + ball->Position.Y = PinballTable->PlungerPositionY; + ball->UnknownBaseFlag2 = 1; + PinballTable->UnknownP10 = 0; + pb::tilt_no_more(); + control::handler(code, this); + return 0; + } + case 1016: + if (BallFeedTimer_) + timer::kill(BallFeedTimer_); + BallFeedTimer_ = timer::set(0.95999998f, this, BallFeedTimer); + loader::play_sound(SoundIndexP1); + control::handler(code, this); + return 0; + case 1017: + MaxCollisionSpeed = 0.0; + CollisionMultiplier = static_cast(MaxPullback); + timer::set(0.2f, this, PlungerReleasedTimer); + break; + case 1024: + { + if (BallFeedTimer_) + timer::kill(BallFeedTimer_); + BallFeedTimer_ = 0; + MaxCollisionSpeed = 0.0; + if (PullbackTimer_) + timer::kill(PullbackTimer_); + PullbackTimer_ = 0; + if (code == 1005) + loader::play_sound(SoundIndexP2); + auto bmp = static_cast(ListBitmap->Get(0)); + auto zMap = static_cast(ListZMap->Get(0)); + render::sprite_set( + RenderSprite, + bmp, + zMap, + bmp->XPosition - PinballTable->XOffset, + bmp->YPosition - PinballTable->YOffset); + + timer::set(Unknown4F, this, PlungerReleasedTimer); + break; + } + default: + break; + } + return 0; +} + +void TPlunger::BallFeedTimer(int timerId, void* caller) +{ + auto plunger = static_cast(caller); + plunger->PullbackTimer_ = 0; + plunger->Message(1015, 0.0); +} + +void TPlunger::PullbackTimer(int timerId, void* caller) +{ + auto plunger = static_cast(caller); + plunger->CollisionMultiplier += static_cast(plunger->PullbackIncrement); + if (plunger->CollisionMultiplier <= static_cast(plunger->MaxPullback)) + { + plunger->PullbackTimer_ = timer::set(plunger->Unknown4F, plunger, PullbackTimer); + } + else + { + plunger->PullbackTimer_ = 0; + plunger->CollisionMultiplier = static_cast(plunger->MaxPullback); + } + int index = static_cast(floor( + static_cast(plunger->ListBitmap->Count() - 1) * + (plunger->CollisionMultiplier / static_cast(plunger->MaxPullback)))); + auto bmp = static_cast(plunger->ListBitmap->Get(index)); + auto zMap = static_cast(plunger->ListZMap->Get(index)); + render::sprite_set( + plunger->RenderSprite, + bmp, + zMap, + bmp->XPosition - plunger->PinballTable->XOffset, + bmp->YPosition - plunger->PinballTable->YOffset); +} + +void TPlunger::PlungerReleasedTimer(int timerId, void* caller) +{ + auto plunger = static_cast(caller); + plunger->MaxCollisionSpeed = 1000000000.0; + plunger->CollisionMultiplier = 0.0; +} diff --git a/SpaceCadetPinball/TPlunger.h b/SpaceCadetPinball/TPlunger.h index bd7ae7a..5ce1bdc 100644 --- a/SpaceCadetPinball/TPlunger.h +++ b/SpaceCadetPinball/TPlunger.h @@ -5,7 +5,20 @@ class TPlunger : public TCollisionComponent { public: - TPlunger(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, true) - { - } + TPlunger(TPinballTable* table, int groupIndex); + void Collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float coef, + TEdgeSegment* edge) override; + int Message(int code, float value) override; + + static void BallFeedTimer(int timerId, void* caller); + static void PullbackTimer(int timerId, void* caller); + static void PlungerReleasedTimer(int timerId, void* caller); + + int PullbackTimer_; + int BallFeedTimer_; + int MaxPullback; + int PullbackIncrement; + float Unknown4F; + int SoundIndexP1; + int SoundIndexP2; }; diff --git a/SpaceCadetPinball/TTableLayer.cpp b/SpaceCadetPinball/TTableLayer.cpp index d100c53..f3bd644 100644 --- a/SpaceCadetPinball/TTableLayer.cpp +++ b/SpaceCadetPinball/TTableLayer.cpp @@ -36,32 +36,32 @@ TTableLayer::TTableLayer(TPinballTable* table): TCollisionComponent(table, -1, f auto tableAngleArr = loader::query_float_attribute(groupIndex, 0, 305); if (tableAngleArr) { - PinballTable->TableAngleMult = tableAngleArr[0]; - PinballTable->TableAngle1 = tableAngleArr[1]; - PinballTable->TableAngle2 = tableAngleArr[2]; + PinballTable->GravityDirVectMult = tableAngleArr[0]; + PinballTable->GravityAngleX = tableAngleArr[1]; + PinballTable->GravityAnglY = tableAngleArr[2]; } else { - PinballTable->TableAngleMult = 25.0f; - PinballTable->TableAngle1 = 0.5f; - PinballTable->TableAngle2 = 1.570796f; + PinballTable->GravityDirVectMult = 25.0f; + PinballTable->GravityAngleX = 0.5f; + PinballTable->GravityAnglY = 1.570796f; } auto table3 = PinballTable; - Angle1 = cos(table3->TableAngle2) * sin(table3->TableAngle1) * table3->TableAngleMult; - Angle2 = sin(table3->TableAngle2) * sin(table3->TableAngle1) * table3->TableAngleMult; + GraityDirX = cos(table3->GravityAnglY) * sin(table3->GravityAngleX) * table3->GravityDirVectMult; + GraityDiY = sin(table3->GravityAnglY) * sin(table3->GravityAngleX) * table3->GravityDirVectMult; auto angleMultArr = loader::query_float_attribute(groupIndex, 0, 701); if (angleMultArr) - AngleMult = *angleMultArr; + GraityMult = *angleMultArr; else - AngleMult = 0.2f; + GraityMult = 0.2f; table->XOffset = bmp->XPosition; table->YOffset = bmp->YPosition; table->Width = bmp->Width; table->Height = bmp->Height; - UnknownC7F = visual.Kicker.Unknown1F; - UnknownC6F = 15.0f; + MaxCollisionSpeed = visual.Kicker.Unknown1F; + CollisionMultiplier = 15.0f; auto visArrPtr = visual.FloatArr; Unknown1F = min(visArrPtr[0], min(visArrPtr[2], visArrPtr[4])); @@ -90,11 +90,11 @@ TTableLayer::TTableLayer(TPinballTable* table): TCollisionComponent(table, -1, f visArrPtr += 2; } - Field.Unknown1 = -1; + Field.Mask = -1; Field.Flag2Ptr = &UnknownBaseFlag2; Field.CollisionComp = this; - TEdgeManager::edges_insert_square(Unknown2F, Unknown1F, Unknown4F, Unknown3F, nullptr, - &Field); + edges_insert_square(Unknown2F, Unknown1F, Unknown4F, Unknown3F, nullptr, + &Field); } TTableLayer::~TTableLayer() @@ -102,3 +102,51 @@ TTableLayer::~TTableLayer() if (edge_manager) delete edge_manager; } + +int TTableLayer::FieldEffect(TBall* ball, vector_type* vecDst) +{ + vecDst->X = GraityDirX - (0.5f - static_cast(rand()) * 0.00003051850947599719f + ball->Acceleration.X) * + ball->Speed * GraityMult; + vecDst->Y = GraityDiY - ball->Acceleration.Y * ball->Speed * GraityMult; + return 1; +} + +void TTableLayer::edges_insert_square(float y0, float x0, float y1, float x1, TEdgeSegment* edge, + field_effect_type* field) +{ + float widthM = edge_manager->AdvanceX * 0.001f; + float heightM = edge_manager->AdvanceY * 0.001f; + float xMin = x0 - widthM; + float xMax = x1 + widthM; + float yMin = y0 - heightM; + float yMax = y1 + heightM; + + int xMinBox = edge_manager->box_x(xMin); + int yMinBox = edge_manager->box_y(yMin); + int xMaxBox = edge_manager->box_x(xMax); + int yMaxBox = edge_manager->box_y(yMax); + + float boxX = static_cast(xMinBox) * edge_manager->AdvanceX + edge_manager->X; + float boxY = static_cast(yMinBox) * edge_manager->AdvanceY + edge_manager->Y; + + for (int indexX = xMinBox; indexX <= xMaxBox; ++indexX) + { + for (int indexY = yMinBox; indexY <= yMaxBox; ++indexY) + { + if (xMax >= boxX && xMin <= boxX + edge_manager->AdvanceX && + yMax >= boxY && yMin <= boxY + edge_manager->AdvanceY) + { + if (edge) + { + edge_manager->add_edge_to_box(indexX, indexY, edge); + } + if (field) + { + edge_manager->add_field_to_box(indexX, indexY, field); + } + } + boxY += edge_manager->AdvanceY; + } + boxX += edge_manager->AdvanceX; + } +} diff --git a/SpaceCadetPinball/TTableLayer.h b/SpaceCadetPinball/TTableLayer.h index 869f2b5..d8bf9a9 100644 --- a/SpaceCadetPinball/TTableLayer.h +++ b/SpaceCadetPinball/TTableLayer.h @@ -10,20 +10,24 @@ struct gdrv_bitmap8; class TTableLayer : public TCollisionComponent { -public: - static TEdgeManager* edge_manager; - +public: TTableLayer(TPinballTable* table); ~TTableLayer() override; + int FieldEffect(TBall* ball, vector_type* vecDst) override; + static void edges_insert_square(float y0, float x0, float y1, float x1, TEdgeSegment* edge, + field_effect_type* field); + gdrv_bitmap8* VisBmp; float Unknown1F; float Unknown2F; float Unknown3F; float Unknown4F; - float Angle1; - float Angle2; + float GraityDirX; + float GraityDiY; int Unknown7; - float AngleMult; + float GraityMult; field_effect_type Field; + + static TEdgeManager* edge_manager; }; diff --git a/SpaceCadetPinball/control.cpp b/SpaceCadetPinball/control.cpp index 21d95fc..d6735df 100644 --- a/SpaceCadetPinball/control.cpp +++ b/SpaceCadetPinball/control.cpp @@ -3,6 +3,7 @@ #include "objlist_class.h" #include "pb.h" +#include "TLight.h" #include "TPinballTable.h" #include "TSound.h" @@ -795,6 +796,12 @@ int control::cheat_bump_rank() return 0; } +BOOL control::light_on(component_tag* tag) +{ + auto light = static_cast(tag->Component); + return light->BmpIndex1 || light->FlasherFlag2 || light->FlasherActive; +} + void control::FlipperRebounderControl1(int code, TPinballComponent* caller) { } @@ -1013,6 +1020,35 @@ void control::HyperspaceKickOutControl(int code, TPinballComponent* caller) void control::PlungerControl(int code, TPinballComponent* caller) { + if (code == 1015) + { + MissionControl(67, nullptr); + } + else if (code == 1016) + { + table_unlimited_balls = 0; + if (!control_middle_circle_tag.Component->Message(37, 0.0)) + control_middle_circle_tag.Component->Message(32, 0.0); + if (!light_on(&control_lite200_tag)) + { + control_skill_shot_lights_tag.Component->Message(20, 0.0); + control_lite67_tag.Component->Message(19, 0.0); + control_skill_shot_lights_tag.Component->Message(26, 0.25f); + control_l_trek_lights_tag.Component->Message(20, 0.0); + control_l_trek_lights_tag.Component->Message(32, 0.2f); + control_l_trek_lights_tag.Component->Message(26, 0.2f); + control_r_trek_lights_tag.Component->Message(20, 0.0); + control_r_trek_lights_tag.Component->Message(32, 0.2f); + control_r_trek_lights_tag.Component->Message(26, 0.2f); + TableG->ScoreSpecial1 = 25000; + MultiplierLightGroupControl(65, control_top_target_lights_tag.Component); + control_fuel_bargraph_tag.Component->Message(19, 0.0); + control_lite200_tag.Component->Message(19, 0.0); + control_gate1_tag.Component->Message(53, 0.0); + control_gate2_tag.Component->Message(53, 0.0); + } + control_lite200_tag.Component->MessageField = 0; + } } void control::MedalTargetControl(int code, TPinballComponent* caller) diff --git a/SpaceCadetPinball/control.h b/SpaceCadetPinball/control.h index cf73b0b..e4cc65f 100644 --- a/SpaceCadetPinball/control.h +++ b/SpaceCadetPinball/control.h @@ -38,6 +38,7 @@ public: static void pbctrl_bdoor_controller(int key); static void table_add_extra_ball(float count); static int cheat_bump_rank(); + static BOOL light_on(struct component_tag* tag); static void FlipperRebounderControl1(int code, TPinballComponent* caller); static void FlipperRebounderControl2(int code, TPinballComponent* caller); diff --git a/SpaceCadetPinball/maths.cpp b/SpaceCadetPinball/maths.cpp index 2532479..317eb0f 100644 --- a/SpaceCadetPinball/maths.cpp +++ b/SpaceCadetPinball/maths.cpp @@ -216,9 +216,9 @@ float maths::ray_intersect_line(ray_type* ray, line_type* line) / perpDot); if (result >= -ray->MinDistance && result <= ray->MaxDistance) { - line->CompTmp1 = result * ray->Direction.X + ray->Origin.X; + line->RayIntersect.X = result * ray->Direction.X + ray->Origin.X; float v4 = result * ray->Direction.Y + ray->Origin.Y; - line->Unknown10 = v4; + line->RayIntersect.Y = v4; if (0.0 == line->Direction.X) { if (v4 >= line->OriginX) @@ -230,9 +230,9 @@ float maths::ray_intersect_line(ray_type* ray, line_type* line) return 1000000000.0; } } - else if (line->OriginX <= line->CompTmp1) + else if (line->OriginX <= line->RayIntersect.X) { - float v7 = line->CompTmp1; + float v7 = line->RayIntersect.X; v5 = v7 < line->OriginY; v6 = v7 == line->OriginY; if (v5 || v6) @@ -268,20 +268,20 @@ void maths::vector_add(vector_type* vec1Dst, vector_type* vec2) vec1Dst->Y += vec2->Y; } -float maths::basic_collision(TBall* ball, vector_type* ballPosition, vector_type* vec2, float a4, float a5, float a6, - float a7) +float maths::basic_collision(TBall* ball, vector_type* nextPosition, vector_type* direction, float a4, float a5, float maxSpeed, + float multiplier) { - ball->Position.X = ballPosition->X; - ball->Position.Y = ballPosition->Y; - float proj = -(vec2->Y * ball->Acceleration.Y + vec2->X * ball->Acceleration.X); + ball->Position.X = nextPosition->X; + ball->Position.Y = nextPosition->Y; + float proj = -(direction->Y * ball->Acceleration.Y + direction->X * ball->Acceleration.X); if (proj < 0) { proj = -proj; } else { - float dx1 = proj * vec2->X; - float dy1 = proj * vec2->Y; + float dx1 = proj * direction->X; + float dy1 = proj * direction->Y; float v17 = dx1 + ball->Acceleration.X; float v18 = dy1 + ball->Acceleration.Y; ball->Acceleration.X = v17 * a5 + dx1 * a4; @@ -291,10 +291,10 @@ float maths::basic_collision(TBall* ball, vector_type* ballPosition, vector_type float projSpeed = proj * ball->Speed; float newSpeed = ball->Speed - (1.0f - a4) * projSpeed; ball->Speed = newSpeed; - if (projSpeed >= a6) + if (projSpeed >= maxSpeed) { - ball->Acceleration.X = newSpeed * ball->Acceleration.X + vec2->X * a7; - ball->Acceleration.Y = newSpeed * ball->Acceleration.Y + vec2->Y * a7; + ball->Acceleration.X = newSpeed * ball->Acceleration.X + direction->X * multiplier; + ball->Acceleration.Y = newSpeed * ball->Acceleration.Y + direction->Y * multiplier; ball->Speed = normalize_2d(&ball->Acceleration); } return projSpeed; diff --git a/SpaceCadetPinball/maths.h b/SpaceCadetPinball/maths.h index 3d93bff..ed5752f 100644 --- a/SpaceCadetPinball/maths.h +++ b/SpaceCadetPinball/maths.h @@ -32,7 +32,7 @@ struct __declspec(align(4)) ray_type float MinDistance; float TimeNow; float TimeDelta; - float Unknown2; + int FieldFlag; }; struct __declspec(align(4)) line_type @@ -42,9 +42,7 @@ struct __declspec(align(4)) line_type float PreComp1; float OriginX; float OriginY; - float CompTmp1; - float Unknown10; - float Unknown11; + vector_type RayIntersect; }; @@ -61,5 +59,5 @@ public: static void cross(vector_type* vec1, vector_type* vec2, vector_type* dstVec); static float magnitude(vector_type* vec); static void vector_add(vector_type* vec1Dst, vector_type* vec2); - static float basic_collision(TBall* ball, struct vector_type* ballPosition, struct vector_type* vec2, float a4, float a5, float a6, float a7); + static float basic_collision(TBall* ball, struct vector_type* nextPosition, struct vector_type* direction, float a4, float a5, float maxSpeed, float multiplier); }; diff --git a/SpaceCadetPinball/pb.cpp b/SpaceCadetPinball/pb.cpp index e78c12f..371e460 100644 --- a/SpaceCadetPinball/pb.cpp +++ b/SpaceCadetPinball/pb.cpp @@ -518,8 +518,8 @@ void pb::launch_ball() void pb::end_game() { - int scores[4]; - int scoreIndex[4]; + int scores[4]{}; + int scoreIndex[4]{}; char String1[200]; mode_change(2); @@ -616,7 +616,7 @@ float pb::collide(float timeNow, float timeDelta, TBall* ball) ray.Direction.Y = ball->Acceleration.Y; ray.Direction.Z = ball->Acceleration.Z; ray.MaxDistance = maxDistance; - ray.Unknown2 = ball->Unknown17F; + ray.FieldFlag = ball->FieldFlag; ray.TimeNow = timeNow; ray.TimeDelta = timeDelta; ray.MinDistance = 0.0020000001f;