⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pacmandriver.cpp

📁 VIGASOCO (VIdeo GAmes SOurce COde) Windows port (v0.01)
💻 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 + -