Multiball part 1: control and component changes from FT.

The result is 3DPB/FT hybrid, with control closer to 3DPB and components closer to FT.
This commit is contained in:
Muzychenko Andrey 2022-08-25 17:09:17 +03:00
parent 14a8d64b67
commit c1c74878df
15 changed files with 354 additions and 154 deletions

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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<TPinballTable*>(caller);

View File

@ -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;

View File

@ -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<int>(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<float>(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<TPlunger*>(caller);
plunger->PullbackTimer_ = 0;
plunger->Message(1015, 0.0);
}
void TPlunger::PullbackTimer(int timerId, void* caller)
{
auto plunger = static_cast<TPlunger*>(caller);
plunger->Boost += static_cast<float>(plunger->PullbackIncrement);
if (plunger->Boost <= static_cast<float>(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<float>(plunger->MaxPullback);
plunger->Boost = plunger->MaxPullback;
}
int index = static_cast<int>(floor(
static_cast<float>(plunger->ListBitmap->size() - 1) *
(plunger->Boost / static_cast<float>(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<TPlunger*>(caller);
plunger->Threshold = 1000000000.0;

View File

@ -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{};
};

View File

@ -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<int>(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<TSink*>(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");
}
}

View File

@ -13,7 +13,6 @@ public:
static void TimerExpired(int timerId, void* caller);
int Timer;
float TimerTime;
vector2 BallPosition{};
vector3 BallThrowDirection{};

View File

@ -108,6 +108,9 @@ component_tag<TLight> control_lite27_tag = {"lite27"};
component_tag<TLight> control_lite28_tag = {"lite28"};
component_tag<TLight> control_lite29_tag = {"lite29"};
component_tag<TLight> control_lite30_tag = {"lite30"};
component_tag<TLight> control_lite38_tag = {"lite38"};
component_tag<TLight> control_lite39_tag = {"lite39"};
component_tag<TLight> control_lite40_tag = {"lite40"};
component_tag<TLight> control_lite54_tag = {"lite54"};
component_tag<TLight> control_lite55_tag = {"lite55"};
component_tag<TLight> 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<float>(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<float>(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
{

View File

@ -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();

View File

@ -85,6 +85,11 @@ struct ramp_plane_type
vector2 FieldForce;
};
struct RectF
{
float XMax, YMax, XMin, YMin;
};
enum class FlipperIntersect
{
none = -1,

View File

@ -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':
{

View File

@ -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)

View File

@ -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();