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

📄 pacman.cpp

📁 VIGASOCO (VIdeo GAmes SOurce COde) Windows port (v0.01)
💻 CPP
字号:
// Pacman.cpp
//
/////////////////////////////////////////////////////////////////////////////

#include "Pacman.h"
#include "PacmanDeadSubstates.h"
#include "GameState.h"
#include "Ghost.h"
#include "../InputHandler.h"
#include "../Operations.h"
#include "Path.h"
#include "Task.h"
#include "Video.h"
#include "Writer.h"
#include "Game.h"

/////////////////////////////////////////////////////////////////////////////
// initialization and cleanup
/////////////////////////////////////////////////////////////////////////////

Pacman::Pacman()
{
	resetState();

	// creates dead substates
	deadSubstates[0x00] = new PacmanAliveSubstate();
	deadSubstates[0x01] = new PacmanWaitABitSubstate();
	deadSubstates[0x02] = new PacmanWaitABitSubstate();
	deadSubstates[0x03] = new PacmanWaitABitSubstate();
	deadSubstates[0x04] = new PacmanWaitABitSubstate();
	deadSubstates[0x05] = new PacmanDead0Substate();
	deadSubstates[0x06] = new PacmanDead1Substate();
	deadSubstates[0x07] = new PacmanDead2Substate();
	deadSubstates[0x08] = new PacmanDead3Substate();
	deadSubstates[0x09] = new PacmanDead4Substate();
	deadSubstates[0x0a] = new PacmanDead5Substate();
	deadSubstates[0x0b] = new PacmanDead6Substate();
	deadSubstates[0x0c] = new PacmanDead7Substate();
	deadSubstates[0x0d] = new PacmanDead8Substate();
	deadSubstates[0x0e] = new PacmanDead9Substate();
	deadSubstates[0x0f] = new PacmanDead10Substate();
	deadSubstates[0x10] = new PacmanDead11Substate();
}

Pacman::~Pacman()
{
	// deletes dead substates
	for (int i = 0; i < 0x11; i++){
		delete deadSubstates[i];
	}
}

void Pacman::resetState()
{
	Character::resetState();

	wantedOrientation = (Orientation)0;
	demoTilePosX = demoTilePosY = 0;
	wantedMovOffsX = wantedMovOffsY = 0;

	superPillEffect = nearSides = hasToChangeOrientation = false;

	movNormalPattern = movSuperPillPattern = 0;
	movementDelay = pillsInLastMove = 0;

	deadAnimState = deadAnimCnt = 0;
}

void Pacman::initState(bool special)
{
	sprite = 0x2c;
	color = 9;

	if (!special){
		posX = 0x80;
		posY = 0xc4;
		tilePosX = demoTilePosX = 0x2e;
		tilePosY = demoTilePosY = 0x38;
		movOffsX = wantedMovOffsX = 1;
		movOffsY = wantedMovOffsY = 0;
		orientation = wantedOrientation = LEFT;
	} else {
		posX = 0x08;
		posY = 0x94;
		tilePosX = demoTilePosX = 0x1f;
		tilePosY = demoTilePosY = 0x32;
		movOffsX = wantedMovOffsX = 1;
		movOffsY = wantedMovOffsY = 0;
		orientation = wantedOrientation = LEFT;
	}
}

/////////////////////////////////////////////////////////////////////////////
// movement
/////////////////////////////////////////////////////////////////////////////

void Pacman::run()
{
	// pacman only moves when movement delay is -1
	if (movementDelay != -1){
		movementDelay--;

		return;
	}

	// check if we have to move according to the movement bit pattern
	if (superPillEffect){
		movSuperPillPattern = Operations::rotateLeft<UINT32, 32>(movSuperPillPattern);
		if ((movSuperPillPattern & 0x01) == 0) return;
	} else {
		movNormalPattern = Operations::rotateLeft<UINT32, 32>(movNormalPattern);
		if ((movNormalPattern & 0x01) == 0) return;
	}

	// keep track of the number of pills before the move
	pillsInLastMove = theGame->levelState[0].eatenPills;

	nearSides = (tilePosX < 0x21) || (tilePosX >= 0x3b);

	// if we're not in a demo or a cutscene
	if (!((theGame->state == DEMO) || (theGame->states[PLAYING]->substate >= 0x10))){
		moveNormal();
	} else {
		moveDemo();
	}
}

void Pacman::moveNormal()
{
	UINT32 input = theInputHandler->getInput(0);

	// if the position is near a tunnel, we can move only in the X axis
	if (nearSides){
		if ((input & 0x02) == 0){
			// left pressed
			orientation = LEFT;
			movOffsX = movementOffsets[LEFT][0];
			movOffsY = movementOffsets[LEFT][1];
		} else if ((input & 0x04) == 0){
			// right pressed
			orientation = RIGHT;
			movOffsX = movementOffsets[RIGHT][0];
			movOffsY = movementOffsets[RIGHT][1];
		}

		advanceAndAdjustPacman();
	} else {
		bool noDirectionPressed = false;

		if ((input & 0x02) == 0){
			// left pressed
			wantedOrientation = LEFT;
			wantedMovOffsX = movementOffsets[LEFT][0];
			wantedMovOffsY = movementOffsets[LEFT][1];
		} else if ((input & 0x04) == 0){
			// right pressed
			wantedOrientation = RIGHT;
			wantedMovOffsX = movementOffsets[RIGHT][0];
			wantedMovOffsY = movementOffsets[RIGHT][1];
		} else if ((input & 0x01) == 0){
			// up pressed
			wantedOrientation = UP;
			wantedMovOffsX = movementOffsets[UP][0];
			wantedMovOffsY = movementOffsets[UP][1];
		} else if ((input & 0x08) == 0){
			// down pressed
			wantedOrientation = DOWN;
			wantedMovOffsX = movementOffsets[DOWN][0];
			wantedMovOffsY = movementOffsets[DOWN][1];
		} else {
			// nothing pressed
			wantedMovOffsX = movOffsX;
			wantedMovOffsY = movOffsY;
			wantedOrientation = orientation;
			noDirectionPressed = true;
		}

		// gets tile in pacman desired destination location
		int data = theGame->VRAM[Video::tilePos2ScreenCoord(tilePosX + wantedMovOffsX, tilePosY + wantedMovOffsY)];

		// it's a blocking tile
		if (data >= 0xc0) {
			// if the player hasn't pressed a direction
			if (noDirectionPressed) {
				// if pacman has reached the blocking tile, exit
				if ((orientation & 0x01) != 0x01){
					if ((posX & 0x07) == 0x04) return;
				} else {
					if ((posY & 0x07) == 0x04) return;
				}

				// otherwise advance
				advanceAndAdjustPacman();
			} else {
				// if the player has pressed a direction

				// get the associated tile if pacman continues moving as if no direction was pressed
				data = theGame->VRAM[Video::tilePos2ScreenCoord(tilePosX + movOffsX, tilePosY + movOffsY)];

				// if it's a blocking tile
				if (data >= 0xc0){
					// if pacman has reached the middle of the tile, it can't move more, exit
					if ((orientation & 0x01) != 0x01){
						if ((posX & 0x07) == 0x04) return;
					} else {
						if ((posY & 0x07) == 0x04) return;
					}
				}
				
				// otherwise advance
				advanceAndAdjustPacman();
			}
		} else {
			// if it wasn't a blocking tile, update pacman orientation and movement offset
			movOffsX = wantedMovOffsX;
			movOffsY = wantedMovOffsY;
			orientation = wantedOrientation;

			// advance
			advanceAndAdjustPacman();
		}
	}
}

/////////////////////////////////////////////////////////////////////////////
// animation
/////////////////////////////////////////////////////////////////////////////

void Pacman::selectProperAnimation()
{
	static int oriAnim[4][8] = {
		{ 0x2e, 0x2e, 0x2c, 0x2c, 0x2e, 0x2e, 0x30, 0x30 },
		{ 0x30, 0x30, 0x2f, 0x2f, 0x2d, 0x2d, 0x2f, 0x2f },
		{ 0x30, 0x30, 0x2e, 0x2e, 0x2c, 0x2c, 0x2e, 0x2e },
		{ 0x30, 0x30, 0x2f, 0x2f, 0x2d, 0x2d, 0x2f, 0x2f }
	};

	static int bigAnim[16] = {
		0x14, 0x14, 0x14, 0x14, 0x14, 0x10, 0x10, 0x10, 0x10, 0x14, 0x14, 0x14, 0x14, 0x18, 0x18, 0x18
	};

	// select proper animation based on orientation and position

	// in the first cutscene, animate big pacman
	if (theGame->cutsceneState[0] >= 5){
		sprite = bigAnim[posX & 0x0f];
		color = 0x16;
	} else {
		// animate normal pacman
		int pos = ((orientation & 0x01) == 0x01) ? posY : posX;
		sprite = oriAnim[orientation][pos & 0x07];
		flipX = orientation == LEFT;
		flipY = orientation == UP;
	}
}

void Pacman::animateDead()
{
	// execute specific code for pacman dead state
	deadSubstates[deadAnimState]->run();
}

void Pacman::draw()
{
	// in the first cutscene, show big pacman
	if (theGame->cutsceneState[0] >= 5){
		// draw pacman's sprites
		Sprite spr1(posX + 8, posY - 16, sprite + 0, color);
		Sprite spr2(posX - 8, posY - 16, sprite + 1, color);
		Sprite spr3(posX + 8, posY, sprite + 2, color);
		Sprite spr4(posX - 8, posY, sprite + 3, color);
		theGame->sprites.push_back(spr1);
		theGame->sprites.push_back(spr2);
		theGame->sprites.push_back(spr3);
		theGame->sprites.push_back(spr4);
	} else {	
		// otherwise, show normal pacman

		// draw pacman's sprites
		Sprite spr(posX, posY, sprite, color, flipX, flipY, superPillEffect ? 1 : 0);
		theGame->sprites.push_back(spr);
	}

	// clear sprite attributes
	flipX = flipY = false;
	priority = 0;
}

/////////////////////////////////////////////////////////////////////////////
// IA in demo mode
/////////////////////////////////////////////////////////////////////////////

void Pacman::moveDemo()
{
	// if pacman is moving in the Y axis
	if (movOffsY != 0){
		// if we are in the middle of a tile in the Y axis
		if ((posY & 0x07) == 0x04){
			moveDemoMiddle();
		} else {
			moveDemoNoMiddle();
		}
	} else {
		// if we are in the middle of a tile in the Y axis
		if ((posX & 0x07) == 0x04){
			moveDemoMiddle();
		} else {
			moveDemoNoMiddle();
		}
	}
	
}

// updates position and tile position if it's in the middle of a tile
void Pacman::moveDemoMiddle()
{
	// if wraparound has happened or it's going to happen, don't search a better move
	if (!checkWrapAround()){
		// search a better movement
		moveInDemoState();
	}

	// updates tile position
	demoTilePosX += wantedMovOffsX;
	demoTilePosY += wantedMovOffsY;

	movOffsX = wantedMovOffsX;
	movOffsY = wantedMovOffsY;

	orientation = wantedOrientation;

	moveDemoNoMiddle();
}

// updates position and tile position if it's not in the middle of a tile
void Pacman::moveDemoNoMiddle()
{
	// moves
	posX += movOffsX;
	posY += movOffsY;

	posX &= 0xff;
	posY &= 0xff;

	checkForEatedItems();
}

void Pacman::moveInDemoState()
{
	// if red ghost is in panic mode
	if (theGame->ghost[RED]->panicMode){
		// try to go to pink ghost position
		Path::calcPath(demoTilePosX, demoTilePosY, wantedOrientation, 
					theGame->ghost[PINK]->tilePosX, theGame->ghost[PINK]->tilePosY,
					wantedMovOffsX, wantedMovOffsY, wantedOrientation);
	} else {
		// try to go away from pink ghost
		Path::calcPath(demoTilePosX, demoTilePosY, wantedOrientation, 
					2*tilePosX - theGame->ghost[PINK]->tilePosX,
					2*tilePosY - theGame->ghost[PINK]->tilePosY,
					wantedMovOffsX, wantedMovOffsY, wantedOrientation);
	}
}

/////////////////////////////////////////////////////////////////////////////
// helper methods
/////////////////////////////////////////////////////////////////////////////

void Pacman::advanceAndAdjustPacman()
{
	// moves pacman
	posX += movOffsX;
	posY += movOffsY;

	posX &= 0xff;
	posY &= 0xff;

	// if pacman is moving through the X axis, center its Y position or viceversa
	if ((orientation & 0x01) != 0x01){
		if ((posY & 0x07) < 0x04) posY++;
		if ((posY & 0x07) > 0x04) posY--;
	} else {
		if ((posX & 0x07) < 0x04) posX++;
		if ((posX & 0x07) > 0x04) posX--;
	}

	// check if has eaten a pill or a fruit
	checkForEatedItems();
}

// check if pacman has eaten a fruit or a pill and update score accordingly
void Pacman::checkForEatedItems()
{
	// set tile position
	Video::pos2TilePos(posX, posY, tilePosX, tilePosY);

	// if pacman was near the sides, return
	if (nearSides){
		nearSides = false;

		return;
	}

	// if there's a fruit displayed
	if ((theGame->fruitPosition != 0) && (theGame->fruitEntry != 0)){
		// if we're on the fruit position
		if ((posX == 0x80) && (posY == 0x94)){
			// update the score
			theGame->updateScore(theGame->fruitEntry);

			// print fruit points in screen
			Writer::printString(0x15 + theGame->fruitEntry);
			
			theGame->fruitPosition = 0;

			// schedules a new task to clean the fruit points in 2 seconds
			theScheduler->addScheduledTask(new Task(5, Task::TEN_HUNDREDTHS, 20));

			// TODOSOUND: play eaten fruit sound in channel 2
		}
	}

	// sets no delay to move pacman
	movementDelay = -1;

	// gets current tile
	int data = theGame->VRAM[Video::tilePos2ScreenCoord(tilePosX, tilePosY)];

	// if it's a pill
	if ((data == 0x10) || (data == 0x14)){
		// update eaten pills
		theGame->levelState[0].eatenPills++;

		// clears eaten pill tile
		theGame->VRAM[Video::tilePos2ScreenCoord(tilePosX, tilePosY)] = 0x40;

		// update score
		theGame->updateScore((data == 0x10) ? 0 : 1);

		// set movement delay
		movementDelay = (data == 0x10) ? 1 : 6;

		// check if a ghost has to leave home
		theGame->checkGhostsExitHomeCounters();

		// if pacman has eaten a super pill, notify the world of the state change
		if (movementDelay == 6){
			theGame->initSuperPillVars();
		}

		// TODOSOUND: depending if current number of eaten pills it's even or odd changes eated pill sound
	}
}

// checks if pacman has to change it's orientation (because pacman has eaten a big pill in the demo)
void Pacman::checkOrientationChanges()
{
	if (hasToChangeOrientation){
		hasToChangeOrientation = false;

		// set the opposite orientation and update movement offsets
		wantedOrientation = (Orientation) (orientation ^ 0x02);
		wantedMovOffsX = movementOffsets[wantedOrientation][0];
		wantedMovOffsY = movementOffsets[wantedOrientation][1];
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -