📄 pacmandriver.cpp
字号:
// PacmanDriver.cpp
//
/////////////////////////////////////////////////////////////////////////////
#include "IDrawPlugin.h"
#include "GameDataEntity.h"
#include "GameDriver.h"
#include "GfxData.h"
#include "InputHandler.h"
#include "Pacmandriver.h"
#include "IPalette.h"
#include "pacman/GameState.h"
#include "pacman/Ghost.h"
#include "pacman/Pacman.h"
/////////////////////////////////////////////////////////////////////////////
// initialization and cleanup
/////////////////////////////////////////////////////////////////////////////
PacmanDriver::PacmanDriver() : GameDriver("puckman")
{
_videoInfo.width = 224;
_videoInfo.height = 288;
_videoInfo.visibleArea = Rect(_videoInfo.width, _videoInfo.height);
_videoInfo.colors = 256;
_videoInfo.refreshRate = 60;
_innerInfoMode = 0;
_pacmanGame = 0;
_oldVRAM = _oldCRAM = 0;
_cacheBitmap = -1;
createGameDataEntities();
createGameGfxDescriptions();
createGameInputsAndDips();
}
PacmanDriver::~PacmanDriver()
{
}
/////////////////////////////////////////////////////////////////////////////
// creates the necessary file info, graphics specifications and inputs
/////////////////////////////////////////////////////////////////////////////
void PacmanDriver::createGameDataEntities()
{
// tiles
GameDataEntity *tiles = new GameDataEntity(GRAPHICS, "Tiles");
tiles->addFile(new GameFile("pacman.5e", 0x0000, 0x1000, 0x0c944964, 0));
_gameFiles.push_back(tiles);
// sprites
GameDataEntity *sprites = new GameDataEntity(GRAPHICS, "Sprites");
sprites->addFile(new GameFile("pacman.5f", 0x0000, 0x1000, 0x958fedf9, 0));
_gameFiles.push_back(sprites);
// palette proms + palette look up table
GameDataEntity *proms = new GameDataEntity(PALETTE, "PROMs");
proms->addFile(new GameFile("82s123.7f", 0x0000, 0x0020, 0x2fc650bd, 0));
proms->addFile(new GameFile("82s126.4a", 0x0020, 0x0100, 0x3eb3a8e4, 0));
_gameFiles.push_back(proms);
}
void PacmanDriver::createGameGfxDescriptions()
{
// tiles
GfxEncoding tileDesc = {
8, 8, // 8x8 tiles
0x1000/16, // 256 tiles
2, { 0, 4 }, // 2 bpp
{ 7*8, 6*8, 5*8, 4*8, 3*8, 2*8, 1*8, 0*8 }, // x offsets
{ 8*8+0, 8*8+1, 8*8+2, 8*8+3, 0, 1, 2, 3 }, // y offsets
16*8 // 16 bytes/tile
};
_gfxEncoding.push_back(GfxEncoding::clone(&tileDesc));
// sprites
GfxEncoding spriteDesc = {
16, 16, // 16x16 sprites
0x1000/64, // 64 sprites
2, { 0, 4 }, // 2 bpp
{ // x offsets
39*8, 38*8, 37*8, 36*8,
35*8, 34*8, 33*8, 32*8,
7*8, 6*8, 5*8, 4*8,
3*8, 2*8, 1*8, 0*8
},
{ // y offsets
8*8+0, 8*8+1, 8*8+2, 8*8+3,
16*8+0, 16*8+1, 16*8+2, 16*8+3,
24*8+0, 24*8+1, 24*8+2, 24*8+3,
0, 1, 2, 3
},
64*8 // 64 bytes/sprite
};
_gfxEncoding.push_back(GfxEncoding::clone(&spriteDesc));
}
void PacmanDriver::createGameInputsAndDips()
{
InputPort *ip0 = new InputPort(8);
ip0->addBit(0, P1_UP, ACTIVE_LOW);
ip0->addBit(1, P1_LEFT, ACTIVE_LOW);
ip0->addBit(2, P1_RIGHT, ACTIVE_LOW);
ip0->addBit(3, P1_DOWN, ACTIVE_LOW);
ip0->addBit(4, SERVICE_2, ACTIVE_LOW); // rack test
ip0->addBit(5, COIN_1, ACTIVE_LOW);
ip0->addBit(6, COIN_2, ACTIVE_LOW);
ip0->addBit(7, SERVICE_1, ACTIVE_LOW);
_inputs.push_back(ip0);
InputPort *ip1 = new InputPort(8);
ip1->addBit(0, P2_UP, ACTIVE_LOW); // used in cocktail mode only
ip1->addBit(1, P2_LEFT, ACTIVE_LOW);
ip1->addBit(2, P2_RIGHT, ACTIVE_LOW);
ip1->addBit(3, P2_DOWN, ACTIVE_LOW);
ip1->addBit(4, UNMAPPED, ACTIVE_LOW); // test mode
ip1->addBit(5, START_1, ACTIVE_LOW);
ip1->addBit(6, START_2, ACTIVE_LOW);
ip1->addBit(7, P1_BUTTON1, ACTIVE_LOW); // was cabinet mode but used for cheat
_inputs.push_back(ip1);
}
/////////////////////////////////////////////////////////////////////////////
// template method overrides to customize initialization
/////////////////////////////////////////////////////////////////////////////
void PacmanDriver::filesLoaded()
{
// decode the palette and process the color lookup table
UINT8 *palData = _gameFiles[2]->getData();
// pacman has a 32x8 palette PROM and a 256x4 color lookup table PROM
// temporary palette
UINT8 pal[16][3];
// fill the temporary palette (colors 16..31 are not used)
for (int i = 0; i < 16; i++){
int bit0, bit1, bit2, r, g, b;
// extract red component
bit0 = (palData[i] >> 0) & 0x01;
bit1 = (palData[i] >> 1) & 0x01;
bit2 = (palData[i] >> 2) & 0x01;
r = 0x21*bit0 + 0x47*bit1 + 0x97*bit2;
// extract green component
bit0 = (palData[i] >> 3) & 0x01;
bit1 = (palData[i] >> 4) & 0x01;
bit2 = (palData[i] >> 5) & 0x01;
g = 0x21*bit0 + 0x47*bit1 + 0x97*bit2;
// extract blue component
bit0 = 0;
bit1 = (palData[i] >> 6) & 0x01;
bit2 = (palData[i] >> 7) & 0x01;
b = 0x21*bit0 + 0x47*bit1 + 0x97*bit2;
// set palette entry
pal[i][0] = r;
pal[i][1] = g;
pal[i][2] = b;
}
// fill the final game palette mapping the color look up table
for (int i = 0; i < 256; i++){
int palColor = palData[0x20 + i] & 0x0f;
_palette->setColor(i, pal[palColor][0], pal[palColor][1], pal[palColor][2]);
}
}
void PacmanDriver::finishInit()
{
// create the game object
_pacmanGame = new Game();
}
void PacmanDriver::videoInitEnd(IDrawPlugin *dp)
{
// allocate cache
_oldVRAM = new UINT8[0x400];
_oldCRAM = new UINT8[0x400];
_cacheBitmap = dp->createBitmap(_videoInfo.width, _videoInfo.height);
}
/////////////////////////////////////////////////////////////////////////////
// template method overrides to customize cleanup
/////////////////////////////////////////////////////////////////////////////
void PacmanDriver::videoEndStart(IDrawPlugin *dp)
{
// deallocate chache
dp->destroyBitmap(_cacheBitmap);
delete[] _oldVRAM;
delete[] _oldCRAM;
}
void PacmanDriver::startEnd()
{
// delete the game object
delete _pacmanGame;
_pacmanGame = 0;
}
/////////////////////////////////////////////////////////////////////////////
// run and refresh methods
/////////////////////////////////////////////////////////////////////////////
void PacmanDriver::run()
{
bool cheat = false;
UINT32 movPattern1, movPattern2;
// detect if we can cheat
if (theInputHandler->isPressed(P1_BUTTON1)){
if ((_pacmanGame->state == PLAYING) && (_pacmanGame->states[PLAYING]->substate == 3)){
cheat = true;
}
}
// if we're cheating, save actual movement patterns and set ultrafast patterns
if (cheat){
movPattern1 = _pacmanGame->pacman->movNormalPattern;
movPattern2 = _pacmanGame->pacman->movSuperPillPattern;
_pacmanGame->pacman->movNormalPattern = 0xffffffff;
_pacmanGame->pacman->movSuperPillPattern = 0xffffffff;
}
// execute game logic
_pacmanGame->run();
// if we've cheated this frame, restore original movement patterns
if (cheat){
_pacmanGame->pacman->movNormalPattern = movPattern1;
_pacmanGame->pacman->movSuperPillPattern = movPattern2;
}
}
void PacmanDriver::render(IDrawPlugin *dp)
{
// activate bitmap cache
dp->setActiveBitmap(_cacheBitmap);
for (int offs = 0; offs < 0x400; offs++){
int tileVRAM = _pacmanGame->VRAM[offs];
int tileCRAM = _pacmanGame->CRAM[offs];
// if the tile isn't dirty, continue
if ((_oldVRAM[offs] == tileVRAM) && (_oldCRAM[offs] == tileCRAM)){
continue;
}
_oldVRAM[offs] = tileVRAM;
_oldCRAM[offs] = tileCRAM;
int mx,my,sx,sy;
mx = offs % 32;
my = offs / 32;
if (my < 2) {
if (mx < 2 || mx >= 30) continue; // not visible
sx = my + 34;
sy = mx - 2;
} else if (my >= 30){
if (mx < 2 || mx >= 30) continue; // not visible
sx = my - 30;
sy = mx - 2;
} else {
sx = mx + 2;
sy = my - 2;
}
dp->drawGfx(_gfx[0],
tileVRAM,
tileCRAM & 0x1f,
224-8 - sy*8, sx*8, 0);
}
// activate main bitmap
dp->setActiveBitmap(0);
// copy cache to destination bitmap
dp->compose(_cacheBitmap, 0, 0);
static Rect spriteClip(0, 16, _videoInfo.width - 1, _videoInfo.height - 16 - 1);
// draw sprite list
SpriteList::const_iterator i;
// lowest priority sprites
for (i = _pacmanGame->sprites.begin(); i != _pacmanGame->sprites.end(); i++){
Sprite spr = (*i);
if ((spr.priority == 0)){
if (!((spr.x == 0) && (spr.y == 0))){
int spriteAttr = TRANSPARENCY_COLOR;
if (spr.flipX) spriteAttr |= FLIP_X;
if (spr.flipY) spriteAttr |= FLIP_Y;
dp->drawGfxClipTrans(_gfx[1],
spr.code, spr.color & 0x1f,
224 - (spr.x - 8), spr.y + 8,
spriteAttr, &spriteClip, 0);
}
}
}
// highest priority sprites
for (i = _pacmanGame->sprites.begin(); i != _pacmanGame->sprites.end(); i++){
Sprite spr = (*i);
if (spr.priority == 1){
if (!((spr.x == 0) && (spr.y == 0))){
int spriteAttr = TRANSPARENCY_COLOR;
if (spr.flipX) spriteAttr |= FLIP_X;
if (spr.flipY) spriteAttr |= FLIP_Y;
dp->drawGfxClipTrans(_gfx[1],
spr.code, spr.color & 0x1f,
224 - (spr.x - 8), spr.y + 8,
spriteAttr, &spriteClip, 0);
}
}
}
}
/////////////////////////////////////////////////////////////////////////////
// display internal game information
/////////////////////////////////////////////////////////////////////////////
void PacmanDriver::showGameLogic(IDrawPlugin *dp)
{
static bool showLevels[][2] = {
{ false, false }, // 0 -> don't show anything
{ true, true }, // 1 -> show everything
{ false, true }, // 2 -> show ghosts' destination position only
{ true, false }, // 3 -> show grid only
};
// update info mode if necessary
if (theInputHandler->hasBeenPressed(FUNCTION_5)){
_innerInfoMode = (_innerInfoMode + 1) % 4;
}
bool gridVisible = showLevels[_innerInfoMode][0];
bool destPosVisible = showLevels[_innerInfoMode][1];
if (_pacmanGame->states[PLAYING]->substate == 3){
if (gridVisible){
drawGrid(dp);
}
if (destPosVisible){
drawDestPos(dp);
}
}
}
void PacmanDriver::drawGrid(IDrawPlugin *dp)
{
// show logical tiles
for (int x = 0; x < _videoInfo.width; x += 8){
dp->drawLine(x, 24, x, _videoInfo.height - 16 - 1, 5);
}
for (int y = 24; y < _videoInfo.height - 16; y += 8){
dp->drawLine(0, y, _videoInfo.width - 1, y, 5);
}
}
void PacmanDriver::drawDestPos(IDrawPlugin *dp)
{
// if the ghosts are alive and going for pacman, show where they want to go
for (int i = 3; i >= 0; i--){
Ghost *g = _pacmanGame->ghost[i];
if ((g->state == 0) && (g->substate == 1) && !(g->panicMode)){
int tilePosX = 0x1b - (g->destTilePosX - 0x20);
int tilePosY = g->destTilePosY - 0x1e;
dp->fillRect(tilePosX*8, tilePosY*8, 8, 8, (i + 1)*8 - 1);
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -