diff --git a/SpaceCadetPinball/TBall.cpp b/SpaceCadetPinball/TBall.cpp index a7fad97..5f46c6f 100644 --- a/SpaceCadetPinball/TBall.cpp +++ b/SpaceCadetPinball/TBall.cpp @@ -141,3 +141,9 @@ vector2 TBall::get_coordinates() { return TTableLayer::edge_manager->NormalizeBox(Position); } + +void TBall::Disable() +{ + ActiveFlag = false; + render::sprite_set_bitmap(RenderSprite, nullptr); +} diff --git a/SpaceCadetPinball/TBall.h b/SpaceCadetPinball/TBall.h index 2d58424..87d6d41 100644 --- a/SpaceCadetPinball/TBall.h +++ b/SpaceCadetPinball/TBall.h @@ -14,6 +14,7 @@ public : bool already_hit(TEdgeSegment* edge); int Message(int code, float value) override; vector2 get_coordinates() override; + void Disable(); static void throw_ball(TBall* ball, vector3* direction, float angleMult, float speedMult1, float speedMult2); diff --git a/SpaceCadetPinball/TDrain.cpp b/SpaceCadetPinball/TDrain.cpp index 3aa8099..25d6736 100644 --- a/SpaceCadetPinball/TDrain.cpp +++ b/SpaceCadetPinball/TDrain.cpp @@ -23,16 +23,21 @@ int TDrain::Message(int code, float value) timer::kill(Timer); Timer = 0; } - PinballTable->BallInSink = 0; + PinballTable->BallInDrainFlag = 0; } return 0; } void TDrain::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance, TEdgeSegment* edge) { - ball->Message(1024, 0.0); - PinballTable->BallInSink = 1; - Timer = timer::set(TimerTime, this, TimerCallback); + ball->Disable(); + --PinballTable->MultiballCount; + if (PinballTable->MultiballCount <= 0) + { + PinballTable->MultiballCount = 0; + PinballTable->BallInDrainFlag = 1; + Timer = timer::set(TimerTime, this, TimerCallback); + } control::handler(63, this); } diff --git a/SpaceCadetPinball/TPinballTable.cpp b/SpaceCadetPinball/TPinballTable.cpp index a806920..8cc380f 100644 --- a/SpaceCadetPinball/TPinballTable.cpp +++ b/SpaceCadetPinball/TPinballTable.cpp @@ -49,20 +49,19 @@ TPinballTable::TPinballTable(): TPinballComponent(nullptr, -1, false) CurScoreStruct = nullptr; ScoreBallcount = nullptr; ScorePlayerNumber1 = nullptr; - BallInSink = 0; + BallInDrainFlag = 0; ActiveFlag = 1; TiltLockFlag = 0; EndGameTimeoutTimer = 0; LightShowTimer = 0; ReplayTimer = 0; TiltTimeoutTimer = 0; - MultiballFlag = 0; + MultiballFlag = false; PlayerCount = 0; - auto ballObj = new TBall(this); - BallList.push_back(ballObj); - if (ballObj) - ballObj->ActiveFlag = 0; + auto ball = AddBall(0.0f, 0.0f); + ball->Disable(); + new TTableLayer(this); LightGroup = new TLightGroup(this, 0); @@ -287,7 +286,7 @@ void TPinballTable::ChangeBallCount(int count) void TPinballTable::tilt(float time) { - if (!TiltLockFlag && !BallInSink) + if (!TiltLockFlag && !BallInDrainFlag) { pinball::InfoTextBox->Clear(); pinball::MissTextBox->Clear(); @@ -452,6 +451,9 @@ int TPinballTable::Message(int code, float value) LightShowTimer = timer::set(time, this, LightShow_timeout); } + // Multi-ball is FT exclusive feature, at least for now. + if (pb::FullTiltMode) + MultiballFlag = true; midi::play_track(MidiTracks::Track1, true); break; case 1018: @@ -573,9 +575,9 @@ int TPinballTable::Message(int code, float value) ScoreSpecial3Flag = 0; UnknownP71 = 0; ExtraBalls = 0; - UnknownP75 = 0; + MultiballCount = 0; BallLockedCounter = 0; - MultiballFlag = 0; + MultiballFlag = false; UnknownP78 = 0; ReplayActiveFlag = 0; ReplayTimer = 0; @@ -589,6 +591,63 @@ int TPinballTable::Message(int code, float value) return 0; } +TBall* TPinballTable::AddBall(float x, float y) +{ + TBall* ball = nullptr; + + for (auto curBall : BallList) + { + if (!curBall->ActiveFlag) + { + ball = curBall; + break; + } + } + + if (ball != nullptr) + { + ball->ActiveFlag = 1; + ball->Position.Z = ball->Offset; + ball->Direction = {}; + ball->Speed = 0; + ball->TimeDelta = 0; + ball->TimeNow = 0; + ball->EdgeCollisionCount = 0; + ball->CollisionFlag = 0; + ball->CollisionMask = 1; + ball->CollisionComp = nullptr; + } + else + { + if (BallList.size() >= 20) + return nullptr; + ball = new TBall(this); + BallList.push_back(ball); + } + + ball->Position.X = x; + ball->Position.Y = y; + + return ball; +} + +int TPinballTable::BallCountInRect(const RectF& rect) +{ + int count = 0; + for (const auto ball : BallList) + { + if (ball->ActiveFlag && + ball->Position.X >= rect.XMin && + ball->Position.Y >= rect.YMin && + ball->Position.X <= rect.XMax && + ball->Position.Y <= rect.YMax) + { + count++; + } + } + return count; +} + void TPinballTable::EndGame_timeout(int timerId, void* caller) { auto table = static_cast(caller); diff --git a/SpaceCadetPinball/TPinballTable.h b/SpaceCadetPinball/TPinballTable.h index e4b27a4..3cd0f31 100644 --- a/SpaceCadetPinball/TPinballTable.h +++ b/SpaceCadetPinball/TPinballTable.h @@ -9,6 +9,7 @@ class TPlunger; class TDrain; class TDemo; class TLightGroup; +struct RectF; struct score_struct_super { @@ -34,6 +35,8 @@ public: void tilt(float time); void port_draw() override; int Message(int code, float value) override; + TBall* AddBall(float x, float y); + int BallCountInRect(const RectF& rect); static void EndGame_timeout(int timerId, void* caller); static void LightShow_timeout(int timerId, void* caller); @@ -49,7 +52,7 @@ public: int SoundIndex1{}; int SoundIndex2{}; int SoundIndex3{}; - int BallInSink; + int BallInDrainFlag; int CurScore{}; int CurScoreE9{}; int LightShowTimer; @@ -86,9 +89,9 @@ public: int BallCount{}; int MaxBallCount; int ExtraBalls{}; - int UnknownP75{}; + int MultiballCount{}; int BallLockedCounter{}; - int MultiballFlag; + bool MultiballFlag; int UnknownP78{}; int ReplayActiveFlag{}; int ReplayTimer; diff --git a/SpaceCadetPinball/TPlunger.cpp b/SpaceCadetPinball/TPlunger.cpp index 619f03a..4f8076b 100644 --- a/SpaceCadetPinball/TPlunger.cpp +++ b/SpaceCadetPinball/TPlunger.cpp @@ -23,11 +23,17 @@ TPlunger::TPlunger(TPinballTable* table, int groupIndex) : TCollisionComponent(t SoundIndexP2 = visual.SoundIndex3; HardHitSoundId = visual.Kicker.HardHitSoundId; Threshold = 1000000000.0; - MaxPullback = 100; + + // In FT, default max pullback is 50. + if (pb::FullTiltMode) + MaxPullback = 50; + else + MaxPullback = 100; + Elasticity = 0.5f; Smoothness = 0.5f; - PullbackIncrement = static_cast(100.0 / (ListBitmap->size() * 8.0)); - Unknown4F = 0.025f; + PullbackIncrement = MaxPullback / (ListBitmap->size() * 8.0f); + PullbackDelay = 0.025f; float* floatArr = loader::query_float_attribute(groupIndex, 0, 601); table->PlungerPositionX = floatArr[0]; table->PlungerPositionY = floatArr[1]; @@ -35,10 +41,19 @@ TPlunger::TPlunger(TPinballTable* table, int groupIndex) : TCollisionComponent(t void TPlunger::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance, TEdgeSegment* edge) { - if (PinballTable->TiltLockFlag) - Message(1017, 0.0); - auto boost = RandFloat() * Boost * 0.1f + Boost; - maths::basic_collision(ball, nextPosition, direction, Elasticity, Smoothness, Threshold, boost); + if (PinballTable->TiltLockFlag || SomeCounter > 0) + { + auto boost = RandFloat() * MaxPullback * 0.1f + MaxPullback; + maths::basic_collision(ball, nextPosition, direction, Elasticity, Smoothness, 0, boost); + if (SomeCounter) + SomeCounter--; + Message(1005, 0.0); + } + else + { + auto boost = RandFloat() * Boost * 0.1f + Boost; + maths::basic_collision(ball, nextPosition, direction, Elasticity, Smoothness, Threshold, boost); + } } int TPlunger::Message(int code, float value) @@ -46,56 +61,76 @@ int TPlunger::Message(int code, float value) switch (code) { case 1004: - if (!PullbackTimer_) + if (!PullbackStartedFlag && PinballTable->MultiballCount > 0 && !PinballTable->TiltLockFlag) { + PullbackStartedFlag = true; Boost = 0.0; Threshold = 1000000000.0; loader::play_sound(HardHitSoundId, this, "TPlunger1"); PullbackTimer(0, this); } - return 0; + break; case 1015: { - auto ball = PinballTable->BallList.at(0); - ball->Message(1024, 0.0); - ball->Position.X = PinballTable->PlungerPositionX; - ball->Position.Y = PinballTable->PlungerPositionY; - ball->ActiveFlag = 1; - PinballTable->BallInSink = 0; - pb::tilt_no_more(); - control::handler(code, this); - return 0; + RectF rect{}; + rect.XMin = PinballTable->CollisionCompOffset * -1.2f + PinballTable->PlungerPositionX; + rect.XMax = PinballTable->CollisionCompOffset * 1.2f + PinballTable->PlungerPositionX; + rect.YMin = PinballTable->CollisionCompOffset * -1.2f + PinballTable->PlungerPositionY; + rect.YMax = PinballTable->CollisionCompOffset * 1.2f + PinballTable->PlungerPositionY; + if(PinballTable->BallCountInRect(rect)) + { + timer::set(1.0f, this, BallFeedTimer); + } + else + { + auto ball = PinballTable->AddBall(PinballTable->PlungerPositionX, PinballTable->PlungerPositionY); + assertm(ball, "Failure to create ball in plunger"); + PinballTable->MultiballCount++; + PinballTable->BallInDrainFlag = 0; + pb::tilt_no_more(); + } + break; } case 1016: - if (BallFeedTimer_) - timer::kill(BallFeedTimer_); - BallFeedTimer_ = timer::set(0.95999998f, this, BallFeedTimer); + timer::set(0.95999998f, this, BallFeedTimer); loader::play_sound(SoundIndexP1, this, "TPlunger2"); - control::handler(code, this); - return 0; + break; case 1017: - Threshold = 0.0; - Boost = static_cast(MaxPullback); - timer::set(0.2f, this, PlungerReleasedTimer); + PullbackStartedFlag = true; + Boost = MaxPullback; + Message(1005, 0.0f); + break; + case 1018: + SomeCounter++; + timer::set(value, this, BallFeedTimer); + loader::play_sound(SoundIndexP1, this, "TPlunger2_1"); + PullbackStartedFlag = true; + PullbackTimer(0, this); + break; + case 1020: + PullbackStartedFlag = false; + Boost = 0.0f; + Threshold = 1000000000.0f; + SomeCounter = 0; + timer::kill(BallFeedTimer); + timer::kill(PullbackTimer); + timer::kill(ReleasedTimer); + break; + case 1011: + SomeCounter = 0; + timer::kill(BallFeedTimer); break; case 1005: case 1009: case 1010: - case 1024: + if (PullbackStartedFlag && !SomeCounter) { - if (code == 1024) - { - if (BallFeedTimer_) - timer::kill(BallFeedTimer_); - BallFeedTimer_ = 0; - } - + PullbackStartedFlag = false; Threshold = 0.0; if (PullbackTimer_) timer::kill(PullbackTimer_); PullbackTimer_ = 0; - if (code == 1005) - loader::play_sound(SoundIndexP2, this, "TPlunger3"); + loader::play_sound(SoundIndexP2, this, "TPlunger3"); auto bmp = ListBitmap->at(0); auto zMap = ListZMap->at(0); render::sprite_set( @@ -105,38 +140,68 @@ int TPlunger::Message(int code, float value) bmp->XPosition - PinballTable->XOffset, bmp->YPosition - PinballTable->YOffset); - timer::set(Unknown4F, this, PlungerReleasedTimer); - break; + timer::set(PullbackDelay, this, ReleasedTimer); } + break; + case 1024: + { + PullbackStartedFlag = false; + Boost = 0.0f; + Threshold = 1000000000.0f; + SomeCounter = 0; + + timer::kill(BallFeedTimer); + timer::kill(PullbackTimer); + timer::kill(ReleasedTimer); + + auto bmp = ListBitmap->at(0); + auto zMap = ListZMap->at(0); + render::sprite_set( + RenderSprite, + bmp, + zMap, + bmp->XPosition - PinballTable->XOffset, + bmp->YPosition - PinballTable->YOffset); + break; + } default: break; } + + control::handler(code, this); 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->Boost += static_cast(plunger->PullbackIncrement); - if (plunger->Boost <= static_cast(plunger->MaxPullback)) + plunger->Boost += plunger->PullbackIncrement; + if (plunger->Boost <= plunger->MaxPullback) { - plunger->PullbackTimer_ = timer::set(plunger->Unknown4F, plunger, PullbackTimer); + if (plunger->SomeCounter) + { + plunger->PullbackTimer_ = timer::set(plunger->PullbackDelay / 4.0f, plunger, PullbackTimer); + } + else + { + plunger->PullbackTimer_ = timer::set(plunger->PullbackDelay, plunger, PullbackTimer); + } } else { plunger->PullbackTimer_ = 0; - plunger->Boost = static_cast(plunger->MaxPullback); + plunger->Boost = plunger->MaxPullback; } + int index = static_cast(floor( static_cast(plunger->ListBitmap->size() - 1) * - (plunger->Boost / static_cast(plunger->MaxPullback)))); + (plunger->Boost / plunger->MaxPullback))); auto bmp = plunger->ListBitmap->at(index); auto zMap = plunger->ListZMap->at(index); render::sprite_set( @@ -147,7 +212,7 @@ void TPlunger::PullbackTimer(int timerId, void* caller) bmp->YPosition - plunger->PinballTable->YOffset); } -void TPlunger::PlungerReleasedTimer(int timerId, void* caller) +void TPlunger::ReleasedTimer(int timerId, void* caller) { auto plunger = static_cast(caller); plunger->Threshold = 1000000000.0; diff --git a/SpaceCadetPinball/TPlunger.h b/SpaceCadetPinball/TPlunger.h index 2b909dd..cdd4cff 100644 --- a/SpaceCadetPinball/TPlunger.h +++ b/SpaceCadetPinball/TPlunger.h @@ -13,13 +13,15 @@ public: static void BallFeedTimer(int timerId, void* caller); static void PullbackTimer(int timerId, void* caller); - static void PlungerReleasedTimer(int timerId, void* caller); + static void ReleasedTimer(int timerId, void* caller); int PullbackTimer_; int BallFeedTimer_; - int MaxPullback; - int PullbackIncrement; - float Unknown4F; + float MaxPullback; + float PullbackIncrement; + float PullbackDelay; int SoundIndexP1; int SoundIndexP2; + bool PullbackStartedFlag{}; + int SomeCounter{}; }; diff --git a/SpaceCadetPinball/TSink.cpp b/SpaceCadetPinball/TSink.cpp index 16f144d..ed3d5e9 100644 --- a/SpaceCadetPinball/TSink.cpp +++ b/SpaceCadetPinball/TSink.cpp @@ -14,13 +14,12 @@ TSink::TSink(TPinballTable* table, int groupIndex) : TCollisionComponent(table, visualStruct visual{}; MessageField = 0; - Timer = 0; - loader::query_visual(groupIndex, 0, &visual); + loader::query_visual(groupIndex, 0, &visual); BallThrowDirection = visual.Kicker.ThrowBallDirection; ThrowAngleMult = visual.Kicker.ThrowBallAngleMult; ThrowSpeedMult1 = visual.Kicker.Boost; ThrowSpeedMult2 = visual.Kicker.ThrowBallMult * 0.01f; - SoundIndex4 = visual.SoundIndex4; + SoundIndex4 = visual.SoundIndex4; SoundIndex3 = visual.SoundIndex3; auto floatArr = loader::query_float_attribute(groupIndex, 0, 601); BallPosition.X = floatArr[0]; @@ -35,27 +34,19 @@ int TSink::Message(int code, float value) case 56: if (value < 0.0f) value = TimerTime; - Timer = timer::set(value, this, TimerExpired); + timer::set(value, this, TimerExpired); break; case 1020: + timer::kill(TimerExpired); PlayerMessagefieldBackup[PinballTable->CurrentPlayer] = MessageField; MessageField = PlayerMessagefieldBackup[static_cast(floor(value))]; break; case 1024: { - if (Timer) - timer::kill(Timer); - Timer = 0; + timer::kill(TimerExpired); MessageField = 0; - - auto playerPtr = PlayerMessagefieldBackup; - for (auto index = 0; index < PinballTable->PlayerCount; ++index) - { - *playerPtr = 0; - - ++playerPtr; - } - + for (auto &msgBackup : PlayerMessagefieldBackup) + msgBackup = 0; break; } default: @@ -66,15 +57,13 @@ int TSink::Message(int code, float value) void TSink::Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance, TEdgeSegment* edge) { - Timer = 0; if (PinballTable->TiltLockFlag) { maths::basic_collision(ball, nextPosition, direction, Elasticity, Smoothness, 1000000000.0, 0.0); } else { - ball->ActiveFlag = 0; - render::sprite_set_bitmap(ball->RenderSprite, nullptr); + ball->Disable(); loader::play_sound(SoundIndex4, ball, "TSink1"); control::handler(63, this); } @@ -82,15 +71,26 @@ void TSink::Collision(TBall* ball, vector2* nextPosition, vector2* direction, fl void TSink::TimerExpired(int timerId, void* caller) { + RectF rect{}; + auto sink = static_cast(caller); - auto ball = sink->PinballTable->BallList.at(0); - ball->CollisionComp = nullptr; - ball->ActiveFlag = 1; - ball->Position.X = sink->BallPosition.X; - ball->Position.Y = sink->BallPosition.Y; - TBall::throw_ball(ball, &sink->BallThrowDirection, sink->ThrowAngleMult, sink->ThrowSpeedMult1, - sink->ThrowSpeedMult2); - if (sink->SoundIndex3) - loader::play_sound(sink->SoundIndex3, ball, "TSink2"); - sink->Timer = 0; + auto table = sink->PinballTable; + + rect.XMin = table->CollisionCompOffset * -2.0f + sink->BallPosition.X; + rect.XMax = table->CollisionCompOffset * 2.0f + sink->BallPosition.X; + rect.YMin = table->CollisionCompOffset * -2.0f + sink->BallPosition.Y; + rect.YMax = table->CollisionCompOffset * 2.0f + sink->BallPosition.Y; + if (table->BallCountInRect(rect)) + { + timer::set(0.5f, sink, TimerExpired); + } + else + { + 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, + sink->ThrowSpeedMult2); + if (sink->SoundIndex3) + loader::play_sound(sink->SoundIndex3, ball, "TSink2"); + } } diff --git a/SpaceCadetPinball/TSink.h b/SpaceCadetPinball/TSink.h index fd9e099..9b1e7cc 100644 --- a/SpaceCadetPinball/TSink.h +++ b/SpaceCadetPinball/TSink.h @@ -13,7 +13,6 @@ public: static void TimerExpired(int timerId, void* caller); - int Timer; float TimerTime; vector2 BallPosition{}; vector3 BallThrowDirection{}; diff --git a/SpaceCadetPinball/control.cpp b/SpaceCadetPinball/control.cpp index 0492164..0b3ea95 100644 --- a/SpaceCadetPinball/control.cpp +++ b/SpaceCadetPinball/control.cpp @@ -108,6 +108,9 @@ component_tag control_lite27_tag = {"lite27"}; component_tag control_lite28_tag = {"lite28"}; component_tag control_lite29_tag = {"lite29"}; component_tag control_lite30_tag = {"lite30"}; +component_tag control_lite38_tag = {"lite38"}; +component_tag control_lite39_tag = {"lite39"}; +component_tag control_lite40_tag = {"lite40"}; component_tag control_lite54_tag = {"lite54"}; component_tag control_lite55_tag = {"lite55"}; component_tag control_lite56_tag = {"lite56"}; @@ -341,6 +344,9 @@ TLight*& lite27 = control_lite27_tag.Component; TLight*& lite28 = control_lite28_tag.Component; TLight*& lite29 = control_lite29_tag.Component; TLight*& lite30 = control_lite30_tag.Component; +TLight*& lite38 = control_lite38_tag.Component; +TLight*& lite39 = control_lite39_tag.Component; +TLight*& lite40 = control_lite40_tag.Component; TLight*& lite54 = control_lite54_tag.Component; TLight*& lite55 = control_lite55_tag.Component; TLight*& lite56 = control_lite56_tag.Component; @@ -615,7 +621,7 @@ component_info control::score_components[88] }; -component_tag_base* control::simple_components[142] +component_tag_base* control::simple_components[145] { &control_lite8_tag, &control_lite9_tag, @@ -758,7 +764,10 @@ component_tag_base* control::simple_components[142] &control_lite322_tag, &control_goal_lights_tag, &control_soundwave25_tag, - &control_soundwave7_tag + &control_soundwave7_tag, + &control_lite38_tag, + &control_lite39_tag, + &control_lite40_tag, }; int control::waiting_deployment_flag; @@ -953,25 +962,40 @@ void control::table_set_flag_lights() info_text_box->Display(pinball::get_rc_string(51, 0), 2.0); } -void control::table_set_multiball() +void control::table_set_multiball(float time) { - info_text_box->Display(pinball::get_rc_string(16, 0), 2.0); - midi::play_track(MidiTracks::Track3, true); + if (TableG->MultiballCount <= 1) + { + TableG->MultiballCount += 3; + sink1->Message(56, time); + sink2->Message(56, time); + sink3->Message(56, time); + lite38->Message(7, -1.0f); + lite39->Message(7, -1.0f); + lite40->Message(7, -1.0f); + info_text_box->Display(pinball::get_rc_string(16, 0), 2.0); + midi::play_track(MidiTracks::Track3, true); + } } void control::table_bump_ball_sink_lock() { - if (TableG->BallLockedCounter == 2) + if (TableG->MultiballCount <= 1) { - table_set_multiball(); - TableG->BallLockedCounter = 0; - } - else - { - TableG->BallLockedCounter = TableG->BallLockedCounter + 1; - soundwave44->Play(nullptr, "table_bump_ball_sink_lock"); - info_text_box->Display(pinball::get_rc_string(1, 0), 2.0); - TableG->Plunger->Message(1016, 0.0); + TableG->MultiballCount--; + if (TableG->BallLockedCounter == 2) + { + soundwave41->Play(nullptr, "table_bump_ball_sink_lock_set_multiball"); + table_set_multiball(2.0); + TableG->BallLockedCounter = 0; + } + else + { + TableG->BallLockedCounter = TableG->BallLockedCounter + 1; + soundwave44->Play(nullptr, "table_bump_ball_sink_lock"); + info_text_box->Display(pinball::get_rc_string(1, 0), 2.0); + TableG->Plunger->Message(1018, 2.0f); + } } } @@ -1630,19 +1654,30 @@ void control::WormHoleControl(int code, TPinballComponent* caller) { if (TableG->MultiballFlag) { - table_bump_ball_sink_lock(); - TableG->AddScore(10000); + if (TableG->MultiballCount == 1) + { + table_bump_ball_sink_lock(); + TableG->AddScore(10000); + return; + } + else + { + table_set_replay(4.0); + TableG->AddScore(50000); + } } else { - info_text_box->Display(pinball::get_rc_string(49, 0), 2.0); + table_set_replay(4.0); TableG->AddScore(sink->get_scoring(1)); - wormhole_tag_array2[sinkFlag]->GetComponent()->Message(16, sink->TimerTime); - wormhole_tag_array3[sinkFlag]->GetComponent()->Message(11, static_cast(2 - sinkFlag)); - wormhole_tag_array3[sinkFlag]->GetComponent()->Message(16, sink->TimerTime); - wormhole_tag_array1[sinkFlag]->GetComponent()->Message(56, sink->TimerTime); } + + info_text_box->Display(pinball::get_rc_string(49, 0), 2.0); + wormhole_tag_array2[sinkFlag]->GetComponent()->Message(16, sink->TimerTime); + wormhole_tag_array3[sinkFlag]->GetComponent()->Message(11, static_cast(2 - sinkFlag)); + wormhole_tag_array3[sinkFlag]->GetComponent()->Message(16, sink->TimerTime); + wormhole_tag_array1[sinkFlag]->GetComponent()->Message(56, sink->TimerTime); return; } TableG->AddScore(sink->get_scoring(2)); @@ -2352,7 +2387,8 @@ void control::HyperspaceKickOutControl(int code, TPinballComponent* caller) if (TableG->MultiballFlag) { - table_set_multiball(); + auto duration = soundwave41->Play(nullptr, "HyperspaceKickOutControl_setMultiball"); + table_set_multiball(duration); } if (TableG->ScoreSpecial3 < 100000) TableG->ScoreSpecial3 = 100000; @@ -2580,10 +2616,18 @@ void control::BallDrainControl(int code, TPinballComponent* caller) soundwave59->Play(nullptr, "BallDrainControl5"); --TableG->UnknownP78; } - else if (TableG->UnknownP75) + else if (TableG->MultiballCount) { - soundwave27->Play(nullptr, "BallDrainControl6"); - --TableG->UnknownP75; + if (TableG->MultiballCount == 1) + { + lite38->Message(20, 0.0f); + lite39->Message(20, 0.0f); + midi::play_track(MidiTracks::Track1, false); + } + else if (TableG->MultiballCount == 2) + { + lite40->Message(20, 0.0f); + } } else { diff --git a/SpaceCadetPinball/control.h b/SpaceCadetPinball/control.h index e608060..6e09224 100644 --- a/SpaceCadetPinball/control.h +++ b/SpaceCadetPinball/control.h @@ -63,7 +63,7 @@ class control public: static TPinballTable* TableG; static component_info score_components[88]; - static component_tag_base* simple_components[142]; + static component_tag_base* simple_components[145]; static int waiting_deployment_flag; static bool table_unlimited_balls; static int RankRcArray[9], MissionRcArray[17], mission_select_scores[17]; @@ -79,7 +79,7 @@ public: static void table_set_bonus(); static void table_set_jackpot(); static void table_set_flag_lights(); - static void table_set_multiball(); + static void table_set_multiball(float time); static void table_bump_ball_sink_lock(); static void table_set_replay(float value); static void cheat_bump_rank(); diff --git a/SpaceCadetPinball/maths.h b/SpaceCadetPinball/maths.h index 0ad31ad..49dfc19 100644 --- a/SpaceCadetPinball/maths.h +++ b/SpaceCadetPinball/maths.h @@ -85,6 +85,11 @@ struct ramp_plane_type vector2 FieldForce; }; +struct RectF +{ + float XMax, YMax, XMin, YMin; +}; + enum class FlipperIntersect { none = -1, diff --git a/SpaceCadetPinball/pb.cpp b/SpaceCadetPinball/pb.cpp index 87670ab..8e841e6 100644 --- a/SpaceCadetPinball/pb.cpp +++ b/SpaceCadetPinball/pb.cpp @@ -241,10 +241,16 @@ void pb::ballset(float dx, float dy) { // dx and dy are normalized to window, ideally in [-1, 1] static constexpr float sensitivity = 7000; - TBall* ball = MainTable->BallList.at(0); - ball->Direction.X = dx * sensitivity; - ball->Direction.Y = dy * sensitivity; - ball->Speed = maths::normalize_2d(ball->Direction); + + for (auto ball : MainTable->BallList) + { + if (ball->ActiveFlag) + { + ball->Direction.X = dx * sensitivity; + ball->Direction.Y = dy * sensitivity; + ball->Speed = maths::normalize_2d(ball->Direction); + } + } } void pb::frame(float dtMilliSec) @@ -469,33 +475,8 @@ void pb::InputDown(GameInput input) switch (input.Value) { case 'b': - TBall* ball; - if (MainTable->BallList.empty()) - { - ball = new TBall(MainTable); - } - else - { - for (auto index = 0u; ;) - { - ball = MainTable->BallList.at(index); - if (!ball->ActiveFlag) - break; - ++index; - if (index >= MainTable->BallList.size()) - { - ball = new TBall(MainTable); - break; - } - } - } - ball->ActiveFlag = 1; - ball->Position.X = 1.0; - ball->Position.Z = ball->Offset; - ball->Position.Y = 1.0; - ball->Direction.Z = 0.0; - ball->Direction.Y = 0.0; - ball->Direction.X = 0.0; + if (MainTable->AddBall(6.0f, 7.0f)) + MainTable->MultiballCount++; break; case 'h': { diff --git a/SpaceCadetPinball/timer.cpp b/SpaceCadetPinball/timer.cpp index bfbd25c..b30e9dd 100644 --- a/SpaceCadetPinball/timer.cpp +++ b/SpaceCadetPinball/timer.cpp @@ -61,6 +61,35 @@ int timer::kill(int timerId) return timerId; } +int timer::kill(void(* callback)(int, void*)) +{ + auto count = 0; + timer_struct* current = ActiveList, * prev = nullptr; + for (auto index = 0; index < Count; index++) + { + if (current->Callback == callback) + { + count++; + if (prev) + prev->NextTimer = current->NextTimer; + else + ActiveList = current->NextTimer; + current->NextTimer = FreeList; + FreeList = current; + if (--Count == index) + break; + current = current->NextTimer; + } + else + { + prev = current; + current = current->NextTimer; + } + } + + return count; +} + int timer::set(float time, void* caller, void (* callback)(int, void*)) { if (Count >= MaxCount) diff --git a/SpaceCadetPinball/timer.h b/SpaceCadetPinball/timer.h index 2d073d6..01d30d9 100644 --- a/SpaceCadetPinball/timer.h +++ b/SpaceCadetPinball/timer.h @@ -15,6 +15,7 @@ public: static int init(int count); static void uninit(); static int kill(int timerId); + static int kill(void (*callback)(int, void*)); static int set(float time, void* caller, void (* callback)(int, void*)); static int check();