Render tweaks part 2: sprite set by index.

This commit is contained in:
Muzychenko Andrey 2022-09-22 17:46:00 +03:00
parent 9f0ae0434e
commit 7003b01e5d
21 changed files with 118 additions and 188 deletions

View File

@ -46,7 +46,6 @@ TBall::TBall(TPinballTable* table) : TPinballComponent(table, -1, false)
for (auto index = 0; index < visualCount; ++index)
{
loader::query_visual(groupIndex, index, &visual);
if (ListBitmap)
ListBitmap->push_back(visual.Bitmap);
auto visVec = reinterpret_cast<vector3*>(loader::query_float_attribute(groupIndex, index, 501));
auto zDepth = proj::z_distance(*visVec);
@ -70,19 +69,13 @@ void TBall::Repaint()
auto pos2D = proj::xform_to_2d(Position);
auto zDepth = proj::z_distance(Position);
auto zArrPtr = VisualZArray;
auto index = 0u;
for (; index < ListBitmap->size() - 1; ++index, zArrPtr++)
for (; index < ListBitmap->size() - 1; ++index)
{
if (*zArrPtr <= zDepth) break;
if (VisualZArray[index] <= zDepth) break;
}
auto bmp = ListBitmap->at(index);
RenderSprite->ball_set(
bmp,
zDepth,
pos2D.X - bmp->Width / 2,
pos2D.Y - bmp->Height / 2);
SpriteSetBall(index, pos2D, zDepth);
}
void TBall::not_again(TEdgeSegment* edge)
@ -109,7 +102,7 @@ int TBall::Message(MessageCode code, float value)
{
if (code == MessageCode::Reset)
{
RenderSprite->ball_set(nullptr, 0.0, 0, 0);
SpriteSetBall(-1, { 0,0 }, 0.0f);
Position.X = 0.0;
CollisionComp = nullptr;
Position.Y = 0.0;
@ -144,5 +137,5 @@ vector2 TBall::get_coordinates()
void TBall::Disable()
{
ActiveFlag = false;
RenderSprite->set_bitmap(nullptr);
SpriteSet(-1);
}

View File

@ -20,7 +20,7 @@ TBlocker::TBlocker(TPinballTable* table, int groupIndex) : TCollisionComponent(t
Timer = 0;
MessageField = 0;
ActiveFlag = 0;
RenderSprite->set_bitmap(nullptr);
SpriteSet(-1);
}
int TBlocker::Message(MessageCode code, float value)
@ -38,14 +38,14 @@ int TBlocker::Message(MessageCode code, float value)
}
MessageField = 0;
ActiveFlag = 0;
RenderSprite->set_bitmap(nullptr);
SpriteSet(-1);
if (code == MessageCode::TBlockerDisable)
loader::play_sound(SoundIndex3, this, "TBlocker1");
break;
case MessageCode::TBlockerEnable:
ActiveFlag = 1;
loader::play_sound(SoundIndex4, this, "TBlocker2");
RenderSprite->set_bitmap(ListBitmap->at(0));
SpriteSet(0);
if (Timer)
timer::kill(Timer);
Timer = timer::set(std::max(value, 0.0f), this, TimerExpired);

View File

@ -110,27 +110,14 @@ void TBumper::Collision(TBall* ball, vector2* nextPosition, vector2* direction,
void TBumper::TimerExpired(int timerId, void* caller)
{
auto bump = static_cast<TBumper*>(caller);
auto bmp = bump->ListBitmap->at(bump->BmpIndex * 2);
auto zMap = bump->ListZMap->at(bump->BmpIndex * 2);
bump->SpriteSet(bump->BmpIndex * 2);
bump->Timer = 0;
bump->RenderSprite->set(
bmp,
zMap,
bmp->XPosition - bump->PinballTable->XOffset,
bmp->YPosition - bump->PinballTable->YOffset);
bump->Threshold = bump->OriginalThreshold;
}
void TBumper::Fire()
{
int bmpIndex = 2 * BmpIndex + 1;
auto bmp = ListBitmap->at(bmpIndex);
auto zMap = ListZMap->at(bmpIndex);
RenderSprite->set(
bmp,
zMap,
bmp->XPosition - PinballTable->XOffset,
bmp->YPosition - PinballTable->YOffset);
SpriteSet(2 * BmpIndex + 1);
Timer = timer::set(TimerTime, this, TimerExpired);
Threshold = 1000000000.0;
}

View File

@ -60,13 +60,7 @@ int TFlagSpinner::Message(MessageCode code, float value)
Timer = 0;
}
BmpIndex = 0;
auto bmp = ListBitmap->at(0);
auto zMap = ListZMap->at(0);
RenderSprite->set(
bmp,
zMap,
bmp->XPosition - PinballTable->XOffset,
bmp->YPosition - PinballTable->YOffset);
SpriteSet(BmpIndex);
}
return 0;
}
@ -110,14 +104,7 @@ void TFlagSpinner::NextFrame()
control::handler(MessageCode::ControlSpinnerLoopReset, this);
}
auto bmp = ListBitmap->at(BmpIndex);
auto zMap = ListZMap->at(BmpIndex);
RenderSprite->set(
bmp,
zMap,
bmp->XPosition - PinballTable->XOffset,
bmp->YPosition - PinballTable->YOffset);
SpriteSet(BmpIndex);
Speed *= SpeedDecrement;
if (Speed >= MinSpeed)
{

View File

@ -119,11 +119,5 @@ void TFlipper::UpdateSprite(float timeNow)
return;
BmpIndex = newBmpIndex;
auto bmp = ListBitmap->at(BmpIndex);
auto zMap = ListZMap->at(BmpIndex);
RenderSprite->set(
bmp,
zMap,
bmp->XPosition - PinballTable->XOffset,
bmp->YPosition - PinballTable->YOffset);
SpriteSet(BmpIndex);
}

View File

@ -14,7 +14,7 @@ TGate::TGate(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
SoundIndex4 = visual.SoundIndex4;
SoundIndex3 = visual.SoundIndex3;
ActiveFlag = 1;
RenderSprite->set_bitmap(ListBitmap->at(0));
SpriteSet(0);
control::handler(MessageCode::Reset, this);
}
@ -24,13 +24,13 @@ int TGate::Message(MessageCode code, float value)
{
case MessageCode::TGateDisable:
ActiveFlag = 0;
RenderSprite->set_bitmap(nullptr);
SpriteSet(-1);
loader::play_sound(SoundIndex3, this, "TGate1");
break;
case MessageCode::Reset:
case MessageCode::TGateEnable:
ActiveFlag = 1;
RenderSprite->set_bitmap(ListBitmap->at(0));
SpriteSet(0);
if (code == MessageCode::TGateEnable)
loader::play_sound(SoundIndex4, this, "TGate2");
break;

View File

@ -24,8 +24,7 @@ int TKickback::Message(MessageCode code, float value)
if ((code == MessageCode::SetTiltLock || code == MessageCode::Reset) && Timer)
{
timer::kill(Timer);
if (ListBitmap)
RenderSprite->set_bitmap(nullptr);
SpriteSet(-1);
Timer = 0;
KickActiveFlag = 0;
Threshold = 1000000000.0;
@ -63,29 +62,11 @@ void TKickback::TimerExpired(int timerId, void* caller)
kick->Threshold = 0.0;
kick->Timer = timer::set(kick->TimerTime2, kick, TimerExpired);
loader::play_sound(kick->HardHitSoundId, kick, "TKickback");
if (kick->ListBitmap)
{
auto bmp = kick->ListBitmap->at(1);
auto zMap = kick->ListZMap->at(1);
kick->RenderSprite->set(
bmp,
zMap,
bmp->XPosition - kick->PinballTable->XOffset,
bmp->YPosition - kick->PinballTable->YOffset);
}
kick->SpriteSet(1);
}
else
{
if (kick->ListBitmap)
{
auto bmp = kick->ListBitmap->at(0);
auto zMap = kick->ListZMap->at(0);
kick->RenderSprite->set(
bmp,
zMap,
bmp->XPosition - kick->PinballTable->XOffset,
bmp->YPosition - kick->PinballTable->YOffset);
}
kick->SpriteSet(0);
kick->Timer = 0;
control::handler(MessageCode::ControlTimerExpired, kick);
}

View File

@ -143,8 +143,8 @@ int TLight::Message(MessageCode code, float value)
break;
case MessageCode::TLightSetOnStateBmpIndex:
LightOnBmpIndex = Clamp(static_cast<int>(floor(value)), 0, static_cast<int>(ListBitmap->size()) - 1);
BmpArr[0] = nullptr;
BmpArr[1] = ListBitmap->at(LightOnBmpIndex);
BmpArr[0] = -1;
BmpArr[1] = LightOnBmpIndex;
if (!FlasherOnFlag)
{
if (ToggledOffFlag)
@ -225,8 +225,7 @@ int TLight::Message(MessageCode code, float value)
case MessageCode::TLightFtTmpOverrideOn:
case MessageCode::TLightFtTmpOverrideOff:
// FT codes in negative to avoid overlap with 3DPB TLightGroup codes
if (ListBitmap)
RenderSprite->set_bitmap(BmpArr[code == MessageCode::TLightFtTmpOverrideOn]);
SpriteSet(BmpArr[code == MessageCode::TLightFtTmpOverrideOn]);
if (UndoOverrideTimer)
timer::kill(UndoOverrideTimer);
UndoOverrideTimer = 0;
@ -241,8 +240,7 @@ int TLight::Message(MessageCode code, float value)
timer::kill(UndoOverrideTimer);
UndoOverrideTimer = 0;
TemporaryOverrideFlag = false;
if (ListBitmap)
RenderSprite->set_bitmap(PreviousBitmap);
SpriteSet(PreviousBitmap);
break;
default:
break;
@ -268,13 +266,10 @@ void TLight::Reset()
FlasherOnFlag = false;
TemporaryOverrideFlag = false;
TurnOffAfterFlashingFg = false;
PreviousBitmap = nullptr;
BmpArr[0] = nullptr;
if (ListBitmap)
{
BmpArr[1] = ListBitmap->at(0);
RenderSprite->set_bitmap(nullptr);
}
PreviousBitmap = -1;
BmpArr[0] = -1;
BmpArr[1] = 0;
SetSpriteBmp(BmpArr[0]);
MessageField = 0;
}
@ -327,11 +322,11 @@ void TLight::flasher_start(bool bmpIndex)
flasher_callback(0, this);
}
void TLight::SetSpriteBmp(gdrv_bitmap8* bmp)
void TLight::SetSpriteBmp(int index)
{
PreviousBitmap = bmp;
if (!TemporaryOverrideFlag && RenderSprite)
RenderSprite->set_bitmap(bmp);
PreviousBitmap = index;
if (!TemporaryOverrideFlag)
SpriteSet(index);
}
void TLight::flasher_callback(int timerId, void* caller)

View File

@ -1,8 +1,6 @@
#pragma once
#include "TPinballComponent.h"
struct gdrv_bitmap8;
struct TLight_player_backup
{
int MessageField;
@ -22,17 +20,17 @@ public:
void schedule_timeout(float time);
void flasher_stop(int bmpIndex);
void flasher_start(bool bmpIndex);
void SetSpriteBmp(gdrv_bitmap8* bmp);
void SetSpriteBmp(int index);
bool light_on() const;
static void TimerExpired(int timerId, void* caller);
static void flasher_callback(int timerId, void* caller);
static void UndoTmpOverride(int timerId, void* caller);
gdrv_bitmap8* BmpArr[2];
float FlashDelay[2];
int BmpArr[2]{-1};
float FlashDelay[2]{};
int FlashTimer;
bool FlashLightOnFlag;
bool FlashLightOnFlag{};
bool LightOnFlag{};
bool FlasherOnFlag;
bool ToggledOffFlag{};
@ -43,6 +41,6 @@ public:
int TimeoutTimer;
int UndoOverrideTimer;
bool TemporaryOverrideFlag{};
gdrv_bitmap8* PreviousBitmap{};
int PreviousBitmap = -1;
TLight_player_backup PlayerData[4]{};
};

View File

@ -13,8 +13,7 @@ TLightRollover::TLightRollover(TPinballTable* table, int groupIndex) : TRollover
{
RolloverFlag = 0;
Timer = 0;
if (ListBitmap != nullptr)
RenderSprite->set_bitmap(nullptr);
SpriteSet(-1);
build_walls(groupIndex);
FloatArr = *loader::query_float_attribute(groupIndex, 0, 407);
}
@ -28,8 +27,7 @@ int TLightRollover::Message(MessageCode code, float value)
if (Timer)
timer::kill(Timer);
Timer = 0;
if (ListBitmap)
RenderSprite->set_bitmap(nullptr);
SpriteSet(-1);
}
return 0;
}
@ -56,8 +54,7 @@ void TLightRollover::Collision(TBall* ball, vector2* nextPosition, vector2* dire
loader::play_sound(SoftHitSoundId, this, "TLightRollover");
control::handler(MessageCode::ControlCollision, this);
RolloverFlag = RolloverFlag == 0;
if (ListBitmap)
RenderSprite->set_bitmap(ListBitmap->at(0));
SpriteSet(0);
}
}
}
@ -65,6 +62,6 @@ void TLightRollover::Collision(TBall* ball, vector2* nextPosition, vector2* dire
void TLightRollover::delay_expired(int timerId, void* caller)
{
auto roll = static_cast<TLightRollover*>(caller);
roll->RenderSprite->set_bitmap(nullptr);
roll->SpriteSet(-1);
roll->Timer = 0;
}

View File

@ -49,7 +49,6 @@ TPinballComponent::TPinballComponent(TPinballTable* table, int groupIndex, bool
}
}
auto zMap = ListZMap ? ListZMap->at(0) : nullptr;
if (ListBitmap)
{
rectangle_type bmp1Rect{}, tmpRect{};
@ -68,6 +67,8 @@ TPinballComponent::TPinballComponent(TPinballTable* table, int groupIndex, bool
maths::enclosing_box(bmp1Rect, tmpRect, bmp1Rect);
}
assertm(ListZMap, "All sprites should have bitmap/zMap pairs");
auto zMap = ListZMap ? ListZMap->at(0) : nullptr;
RenderSprite = new render_sprite(
VisualTypes::Sprite,
rootBmp,
@ -125,3 +126,43 @@ vector2 TPinballComponent::get_coordinates()
{
return {VisualPosNormX, VisualPosNormY};
}
void TPinballComponent::SpriteSet(int index) const
{
if (!ListBitmap)
return;
int xPos, yPos;
gdrv_bitmap8* bmp;
zmap_header_type* zMap;
if (index >= 0)
{
bmp = ListBitmap->at(index);
zMap = ListZMap->at(index);
xPos = bmp->XPosition - PinballTable->XOffset;
yPos = bmp->YPosition - PinballTable->YOffset;
}
else
{
bmp = nullptr;
zMap = nullptr;
xPos = RenderSprite->BmpRect.XPosition;
yPos = RenderSprite->BmpRect.YPosition;
}
RenderSprite->set(bmp, zMap, xPos, yPos);
}
void TPinballComponent::SpriteSetBall(int index, vector2i pos, float depth) const
{
if (ListBitmap)
{
auto bmp = index >= 0 ? ListBitmap->at(index) : nullptr;
if (bmp)
{
pos.X -= bmp->Width / 2;
pos.Y -= bmp->Height / 2;
}
RenderSprite->ball_set(bmp, depth, pos.X, pos.Y);
}
}

View File

@ -1,5 +1,6 @@
#pragma once
struct vector2i;
struct zmap_header_type;
struct gdrv_bitmap8;
struct render_sprite;
@ -138,6 +139,8 @@ public:
virtual void port_draw();
int get_scoring(unsigned int index) const;
virtual vector2 get_coordinates();
void SpriteSet(int index) const;
void SpriteSetBall(int index, vector2i pos, float depth) const;
char UnusedBaseFlag;
char ActiveFlag;

View File

@ -131,14 +131,7 @@ int TPlunger::Message(MessageCode code, float value)
timer::kill(PullbackTimer_);
PullbackTimer_ = 0;
loader::play_sound(SoundIndexP2, this, "TPlunger3");
auto bmp = ListBitmap->at(0);
auto zMap = ListZMap->at(0);
RenderSprite->set(
bmp,
zMap,
bmp->XPosition - PinballTable->XOffset,
bmp->YPosition - PinballTable->YOffset);
SpriteSet(0);
timer::set(PullbackDelay, this, ReleasedTimer);
}
break;
@ -153,13 +146,7 @@ int TPlunger::Message(MessageCode code, float value)
timer::kill(PullbackTimer);
timer::kill(ReleasedTimer);
auto bmp = ListBitmap->at(0);
auto zMap = ListZMap->at(0);
RenderSprite->set(
bmp,
zMap,
bmp->XPosition - PinballTable->XOffset,
bmp->YPosition - PinballTable->YOffset);
SpriteSet(0);
break;
}
default:
@ -200,13 +187,7 @@ void TPlunger::PullbackTimer(int timerId, void* caller)
int index = static_cast<int>(floor(
static_cast<float>(plunger->ListBitmap->size() - 1) *
(plunger->Boost / plunger->MaxPullback)));
auto bmp = plunger->ListBitmap->at(index);
auto zMap = plunger->ListZMap->at(index);
plunger->RenderSprite->set(
bmp,
zMap,
bmp->XPosition - plunger->PinballTable->XOffset,
bmp->YPosition - plunger->PinballTable->YOffset);
plunger->SpriteSet(index);
}
void TPlunger::ReleasedTimer(int timerId, void* caller)

View File

@ -20,7 +20,7 @@ int TPopupTarget::Message(MessageCode code, float value)
{
case MessageCode::TPopupTargetDisable:
ActiveFlag = 0;
RenderSprite->set_bitmap(nullptr);
SpriteSet(-1);
break;
case MessageCode::TPopupTargetEnable:
Timer = timer::set(TimerTime, this, TimerExpired);
@ -79,7 +79,7 @@ void TPopupTarget::TimerExpired(int timerId, void* caller)
auto target = static_cast<TPopupTarget*>(caller);
target->Timer = 0;
target->ActiveFlag = 1;
target->RenderSprite->set_bitmap(target->ListBitmap->at(0));
target->SpriteSet(0);
if (timerId)
{
if (target->SoftHitSoundId)

View File

@ -19,8 +19,7 @@ TRollover::TRollover(TPinballTable* table, int groupIndex, bool createWall) : TC
TRollover::TRollover(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, false)
{
if (ListBitmap)
RenderSprite->set_bitmap(ListBitmap->at(0));
SpriteSet(0);
build_walls(groupIndex);
}
@ -31,8 +30,7 @@ int TRollover::Message(MessageCode code, float value)
{
ActiveFlag = 1;
RolloverFlag = 0;
if (ListBitmap)
RenderSprite->set_bitmap(ListBitmap->at(0));
SpriteSet(0);
}
return 0;
}
@ -44,7 +42,7 @@ void TRollover::Collision(TBall* ball, vector2* nextPosition, vector2* direction
ball->Position.Y = nextPosition->Y;
ball->RayMaxDistance -= distance;
ball->not_again(edge);
gdrv_bitmap8* bmp = nullptr;
if (!PinballTable->TiltLockFlag)
{
if (RolloverFlag)
@ -58,12 +56,7 @@ void TRollover::Collision(TBall* ball, vector2* nextPosition, vector2* direction
control::handler(MessageCode::ControlCollision, this);
}
RolloverFlag = RolloverFlag == 0;
if (ListBitmap)
{
if (!RolloverFlag)
bmp = ListBitmap->at(0);
RenderSprite->set_bitmap(bmp);
}
SpriteSet(RolloverFlag ? -1 : 0);
}
}

View File

@ -37,18 +37,7 @@ int TSoloTarget::Message(MessageCode code, float value)
return 0;
}
if (ListBitmap)
{
auto index = 1 - ActiveFlag;
auto bmp = ListBitmap->at(index);
auto zMap = ListZMap->at(index);
RenderSprite->set(
bmp,
zMap,
bmp->XPosition - PinballTable->XOffset,
bmp->YPosition - PinballTable->YOffset);
}
SpriteSet(1 - ActiveFlag);
return 0;
}

View File

@ -9,9 +9,7 @@
TWall::TWall(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, true)
{
if (RenderSprite)
RenderSprite->set_bitmap(nullptr);
if (ListBitmap)
BmpPtr = ListBitmap->at(0);
SpriteSet(-1);
}
int TWall::Message(MessageCode code, float value)
@ -28,9 +26,9 @@ void TWall::Collision(TBall* ball, vector2* nextPosition, vector2* direction, fl
{
if (DefaultCollision(ball, nextPosition, direction))
{
if (BmpPtr)
if (ListBitmap)
{
RenderSprite->set_bitmap(BmpPtr);
SpriteSet(0);
Timer = timer::set(0.1f, this, TimerExpired);
}
control::handler(MessageCode::ControlCollision, this);
@ -40,7 +38,7 @@ void TWall::Collision(TBall* ball, vector2* nextPosition, vector2* direction, fl
void TWall::TimerExpired(int timerId, void* caller)
{
auto wall = static_cast<TWall*>(caller);
wall->RenderSprite->set_bitmap(nullptr);
wall->SpriteSet(-1);
wall->Timer = 0;
wall->MessageField = 0;
}

View File

@ -2,8 +2,6 @@
#include "TCollisionComponent.h"
struct gdrv_bitmap8;
class TWall :
public TCollisionComponent
{
@ -16,5 +14,4 @@ public:
static void TimerExpired(int timerId, void* caller);
int Timer{};
gdrv_bitmap8* BmpPtr{};
};

View File

@ -49,8 +49,8 @@ struct component_tag : component_tag_base
struct component_control
{
void (* ControlFunc)(MessageCode, TPinballComponent*);
unsigned int ScoreCount;
void (& ControlFunc)(MessageCode, TPinballComponent*);
const unsigned int ScoreCount;
const int* Scores;
};

View File

@ -45,8 +45,8 @@ render_sprite::render_sprite(VisualTypes visualType, gdrv_bitmap8* bmp, zmap_hea
BoundingRect.YPosition = 0;
}
BmpRect.YPosition = yPosition;
BmpRect.XPosition = xPosition;
BmpRect.YPosition = yPosition;
if (bmp)
{
BmpRect.Width = bmp->Width;
@ -59,6 +59,14 @@ render_sprite::render_sprite(VisualTypes visualType, gdrv_bitmap8* bmp, zmap_hea
}
DirtyRectPrev = BmpRect;
if (!ZMap && VisualType != VisualTypes::Ball)
{
assertm(false, "Background zMap should not be used");
ZMap = render::background_zmap;
ZMapOffestY = xPosition - render::zmap_offsetX;
ZMapOffestX = yPosition - render::zmap_offsetY;
}
render::AddSprite(*this);
}
@ -85,11 +93,6 @@ void render_sprite::set(gdrv_bitmap8* bmp, zmap_header_type* zMap, int xPos, int
}
}
void render_sprite::set_bitmap(gdrv_bitmap8* bmp)
{
set(bmp, ZMap, BmpRect.XPosition, BmpRect.YPosition);
}
void render_sprite::ball_set(gdrv_bitmap8* bmp, float depth, int xPos, int yPos)
{
set(bmp, ZMap,xPos, yPos);
@ -200,13 +203,6 @@ void render::update()
void render::AddSprite(render_sprite& sprite)
{
if (!sprite.ZMap && sprite.VisualType != VisualTypes::Ball)
{
sprite.ZMap = background_zmap;
sprite.ZMapOffestY = sprite.BmpRect.XPosition - zmap_offsetX;
sprite.ZMapOffestX = sprite.BmpRect.YPosition - zmap_offsetY;
}
auto& list = sprite.VisualType == VisualTypes::Ball ? ball_list : sprite_list;
list.push_back(&sprite);
}

View File

@ -30,7 +30,6 @@ struct render_sprite
int xPosition, int yPosition, rectangle_type* boundingRect);
~render_sprite();
void set(gdrv_bitmap8* bmp, zmap_header_type* zMap, int xPos, int yPos);
void set_bitmap(gdrv_bitmap8* bmp);
void ball_set(gdrv_bitmap8* bmp, float depth, int xPos, int yPos);
};
@ -39,6 +38,8 @@ class render
{
public:
static gdrv_bitmap8 *vscreen, *background_bitmap;
static zmap_header_type* background_zmap;
static int zmap_offsetX, zmap_offsetY;
static SDL_Rect DestinationRect;
static void init(gdrv_bitmap8* bmp, int width, int height);
@ -54,8 +55,7 @@ public:
static void PresentVScreen();
private:
static std::vector<render_sprite*> sprite_list, ball_list;
static zmap_header_type* background_zmap;
static int zmap_offsetX, zmap_offsetY, offset_x, offset_y;
static int offset_x, offset_y;
static rectangle_type vscreen_rect;
static gdrv_bitmap8* ball_bitmap[20];
static zmap_header_type* zscreen;