scummvm/engines/hdb/ai-funcs.cpp
2019-09-03 17:16:59 +02:00

1440 lines
36 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "hdb/hdb.h"
namespace HDB {
AIEntity *AI::spawn(AIType type, AIDir dir, int x, int y, char *funcInit, char *funcAction, char *funcUse, AIDir dir2, int level, int value1, int value2, int callInit) {
AIEntity *e = new AIEntity;
e->type = type;
e->dir = dir;
// Set Co-ordinates & Speed
e->x = x * kTileWidth;
e->tileX = x;
e->y = y * kTileHeight;
e->tileY = y;
e->moveSpeed = kPlayerMoveSpeed; // Default Speed
if (!g_hdb->getActionMode()) {
e->moveSpeed /= 2;
}
// Other variables
e->dir2 = dir2;
if (!level)
level = 1;
e->level = level;
e->value1 = value1;
e->value2 = value2;
e->animCycle = 2; // Game frames to wait before animating graphic frames
e->animDelay = e->animCycle;
e->animFrame = 0;
if (funcInit) {
strcpy(e->luaFuncInit, funcInit);
}
if (funcAction) {
strcpy(e->luaFuncAction, funcAction);
}
if (funcUse) {
strcpy(e->luaFuncUse, funcUse);
}
if (e->luaFuncInit[0] == '*')
e->luaFuncInit[0] = 0;
if (e->luaFuncAction[0] == '*')
e->luaFuncAction[0] = 0;
if (e->luaFuncUse[0] == '*')
e->luaFuncUse[0] = 0;
e->standdownFrames = e->standupFrames = e->standleftFrames = e->standrightFrames = 0;
e->movedownFrames = e->moveupFrames = e->moveleftFrames = e->moverightFrames = 0;
e->blinkFrames = 0;
if (!cacheEntGfx(e, (bool)callInit)) {
return NULL;
} else {
_ents->push_back(e);
}
return e;
}
bool AI::cacheEntGfx(AIEntity *e, bool init) {
int i = 0;
while (true) {
if (aiEntList[i].type == END_AI_TYPES) {
return false;
}
// Load Gfx for corresponding Entity
if (aiEntList[i].type == e->type) {
int j = 0;
AIStateDef *list = aiEntList[i].stateDef;
while (list[j].state != STATE_ENDSTATES) {
Common::Array<const char *> *gfxFiles = g_hdb->_fileMan->findFiles(list[j].name, TYPE_TILE32);
uint32 size;
for (Common::Array<const char *>::iterator it = gfxFiles->begin(); it != gfxFiles->end(); it++) {
size = g_hdb->_fileMan->getLength((*it), TYPE_TILE32);
if (g_hdb->_drawMan->selectGfxType((*it))) {
Tile *gfx = g_hdb->_drawMan->getTileGfx((*it), size);
switch (list[j].state) {
case STATE_STANDDOWN:
e->standdownGfx[e->standdownFrames] = gfx;
e->standdownFrames++;
break;
case STATE_STANDUP:
e->standupGfx[e->standupFrames] = gfx;
e->standupFrames++;
break;
case STATE_STANDLEFT:
e->standleftGfx[e->standleftFrames] = gfx;
e->standleftFrames++;
break;
case STATE_STANDRIGHT:
e->standrightGfx[e->standrightFrames] = gfx;
e->standrightFrames++;
break;
case STATE_BLINK:
e->blinkGfx[e->blinkFrames] = gfx;
e->blinkFrames++;
break;
case STATE_MOVEDOWN:
e->movedownGfx[e->movedownFrames] = gfx;
e->movedownFrames++;
break;
case STATE_MOVEUP:
e->moveupGfx[e->moveupFrames] = gfx;
e->moveupFrames++;
break;
case STATE_MOVELEFT:
e->moveleftGfx[e->moveleftFrames] = gfx;
e->moveleftFrames++;
break;
case STATE_MOVERIGHT:
e->moverightGfx[e->moverightFrames] = gfx;
e->moverightFrames++;
break;
// Special Player Frames
case STATE_PUSHDOWN:
_pushdownGfx[_pushdownFrames] = gfx;
_pushdownFrames++;
break;
case STATE_PUSHUP:
_pushupGfx[_pushupFrames] = gfx;
_pushupFrames++;
break;
case STATE_PUSHLEFT:
_pushleftGfx[_pushleftFrames] = gfx;
_pushleftFrames++;
break;
case STATE_PUSHRIGHT:
_pushrightGfx[_pushrightFrames] = gfx;
_pushrightFrames++;
break;
case STATE_GRABUP:
_getGfx[DIR_UP] = gfx; break;
case STATE_GRABDOWN:
_getGfx[DIR_DOWN] = gfx; break;
case STATE_GRABLEFT:
_getGfx[DIR_LEFT] = gfx; break;
case STATE_GRABRIGHT:
_getGfx[DIR_RIGHT] = gfx; break;
case STATE_ATK_STUN_UP:
_stunUpGfx[_stunUpFrames] = gfx;
_stunUpFrames++;
break;
case STATE_ATK_STUN_DOWN:
_stunDownGfx[_stunDownFrames] = gfx;
_stunDownFrames++;
break;
case STATE_ATK_STUN_LEFT:
_stunLeftGfx[_stunLeftFrames] = gfx;
_stunLeftFrames++;
break;
case STATE_ATK_STUN_RIGHT:
_stunRightGfx[_stunRightFrames] = gfx;
_stunRightFrames++;
break;
case STATE_ATK_SLUG_UP:
_slugUpGfx[_slugUpFrames] = gfx;
_slugUpFrames++;
break;
case STATE_ATK_SLUG_DOWN:
_slugDownGfx[_slugDownFrames] = gfx;
_slugDownFrames++;
break;
case STATE_ATK_SLUG_LEFT:
_slugLeftGfx[_slugLeftFrames] = gfx;
_slugLeftFrames++;
break;
case STATE_ATK_SLUG_RIGHT:
_slugRightGfx[_slugRightFrames] = gfx;
_slugRightFrames++;
break;
// Maintenance Bot
case STATE_USEUP:
e->standupGfx[4] = gfx;
break;
case STATE_USEDOWN:
e->standdownGfx[4] = gfx;
break;
case STATE_USELEFT:
e->standleftGfx[4] = gfx;
break;
case STATE_USERIGHT:
e->standrightGfx[4] = gfx;
break;
// Death & Dying for Player
case STATE_DYING:
_dyingGfx[_dyingFrames] = gfx;
_dyingFrames++;
break;
case STATE_GOODJOB:
_goodjobGfx = gfx;
break;
case STATE_HORRIBLE1:
_horrible1Gfx[_horrible1Frames] = gfx;
_horrible1Frames++;
break;
case STATE_HORRIBLE2:
_horrible2Gfx[_horrible2Frames] = gfx;
_horrible2Frames++;
break;
case STATE_HORRIBLE3:
_horrible3Gfx[_horrible3Frames] = gfx;
_horrible3Frames++;
break;
case STATE_HORRIBLE4:
_horrible4Gfx[_horrible4Frames] = gfx;
_horrible4Frames++;
break;
case STATE_PLUMMET:
_plummetGfx[_plummetFrames] = gfx;
_plummetFrames++;
break;
// floating frames - overwrite "standup" info
case STATE_FLOATING:
e->blinkGfx[e->blinkFrames] = gfx;
e->blinkFrames++;
break;
// melted frames - go in the special area (lightbarrels)
// shocking frames - go in the special1 area (shockbots)
// exploding frames, same
case STATE_MELTED:
case STATE_SHOCKING:
case STATE_EXPLODING:
e->special1Gfx[e->special1Frames] = gfx;
e->special1Frames++;
break;
// ICEPUFF spawning states
case STATE_ICEP_PEEK:
e->blinkGfx[e->blinkFrames] = gfx;
e->blinkFrames++;
break;
case STATE_ICEP_APPEAR:
e->standupGfx[e->standupFrames] = gfx;
e->standupFrames++;
break;
case STATE_ICEP_THROWDOWN:
e->standdownGfx[e->standdownFrames] = gfx;
e->standdownFrames++;
break;
case STATE_ICEP_THROWRIGHT:
e->standrightGfx[e->standrightFrames] = gfx;
e->standrightFrames++;
break;
case STATE_ICEP_THROWLEFT:
e->standleftGfx[e->standleftFrames] = gfx;
e->standleftFrames++;
break;
case STATE_ICEP_DISAPPEAR:
e->special1Gfx[e->special1Frames] = gfx;
e->special1Frames++;
break;
// FATFROG spawning states
case STATE_LICKDOWN:
e->movedownGfx[e->movedownFrames] = gfx;
e->movedownFrames++;
break;
case STATE_LICKLEFT:
e->moveleftGfx[e->moveleftFrames] = gfx;
e->moveleftFrames++;
break;
case STATE_LICKRIGHT:
e->moverightGfx[e->moverightFrames] = gfx;
e->moverightFrames++;
break;
// MEERKAT spawning states
case STATE_MEER_MOVE:
e->standdownGfx[e->standdownFrames] = gfx;
e->standdownFrames++;
break;
case STATE_MEER_APPEAR:
e->standleftGfx[e->standleftFrames] = gfx;
e->standleftFrames++;
break;
case STATE_MEER_BITE:
e->standrightGfx[e->standrightFrames] = gfx;
e->standrightFrames++;
break;
case STATE_MEER_DISAPPEAR:
e->standupGfx[e->standupFrames] = gfx;
e->standupFrames++;
break;
case STATE_MEER_LOOK:
e->movedownGfx[e->movedownFrames] = gfx;
e->movedownFrames++;
break;
// DIVERTER spawning states
case STATE_DIVERTER_BL:
e->standdownGfx[e->standdownFrames] = gfx;
e->standdownFrames++;
break;
case STATE_DIVERTER_BR:
e->standupGfx[e->standupFrames] = gfx;
e->standupFrames++;
break;
case STATE_DIVERTER_TL:
e->standleftGfx[e->standleftFrames] = gfx;
e->standleftFrames++;
break;
case STATE_DIVERTER_TR:
e->standrightGfx[e->standrightFrames] = gfx;
e->standrightFrames++;
break;
// DOLLY states
// angry[4] = standright[4]
// kissright[4]/kissleft[4] = standleft[8]
// panic[4]/laugh[4] = standdown[8]
// dollyuseright[5] = special1[5]
case STATE_ANGRY:
e->standrightGfx[e->standrightFrames] = gfx;
e->standrightFrames++;
break;
case STATE_KISSRIGHT:
e->standleftGfx[e->standleftFrames] = gfx;
e->standleftFrames++;
break;
case STATE_KISSLEFT:
e->standleftGfx[4 + e->int1] = gfx;
e->int1++;
break;
case STATE_PANIC:
e->standdownGfx[e->standdownFrames] = gfx;
e->standdownFrames++;
break;
case STATE_LAUGH:
e->standdownGfx[4 + e->value1] = gfx;
e->value1++;
break;
case STATE_DOLLYUSERIGHT:
e->special1Gfx[e->special1Frames] = gfx;
e->special1Frames++;
break;
// SARGE yelling
case STATE_YELL:
e->special1Gfx[e->special1Frames] = gfx;
e->special1Frames++;
break;
default:
warning("AI-FUNCS: cacheEntGfx: Unintended State");
break;
}
} else {
Picture *gfx = g_hdb->_drawMan->getPicGfx((*it), size);
switch (list[j].state) {
case STATE_ATK_CLUB_UP:
_clubUpGfx[_clubUpFrames] = gfx;
_clubUpFrames++;
break;
case STATE_ATK_CLUB_DOWN:
_clubDownGfx[_clubDownFrames] = gfx;
_clubDownFrames++;
break;
case STATE_ATK_CLUB_LEFT:
_clubLeftGfx[_clubLeftFrames] = gfx;
_clubLeftFrames++;
break;
case STATE_ATK_CLUB_RIGHT:
_clubRightGfx[_clubRightFrames] = gfx;
_clubRightFrames++;
break;
default:
warning("AI-FUNCS: cacheEntGfx: Unintended State");
break;
}
}
}
j++;
}
e->aiInit = aiEntList[i].initFunc;
e->aiInit2 = aiEntList[i].initFunc2;
if (init) {
e->aiInit(e);
if (e->aiInit2) {
e->aiInit2(e);
}
if (e->luaFuncInit[0]) {
g_hdb->_lua->callFunction(e->luaFuncInit, 2);
const char *str1 = g_hdb->_lua->getStringOffStack();
const char *str2 = g_hdb->_lua->getStringOffStack();
if (str1) {
strcpy(e->entityName, str1);
}
if (str2) {
strcpy(e->printedName, str2);
}
}
} else {
if (e->aiInit2) {
e->aiInit2(e);
}
}
break; // Entity Initiated
}
i++;
}
return true;
}
// Stops the movement of an entity
void AI::stopEntity(AIEntity *e) {
if (e == _player) {
clearWaypoints();
// Reset Player speed
e->moveSpeed = kPlayerMoveSpeed;
}
// Reset animation
e->animFrame = 0;
// Align with tile boundaries
e->x = e->tileX * kTileWidth;
e->y = e->tileY * kTileHeight;
e->goalX = e->tileX;
e->goalY = e->tileY;
e->drawXOff = e->drawYOff = 0;
// Don't change the state of Diverters or Floating entities
switch (e->state) {
case STATE_FLOATLEFT:
case STATE_FLOATRIGHT:
case STATE_FLOATUP:
case STATE_FLOATDOWN:
e->state = STATE_FLOATING;
return;
default:
warning("AI-FUNCS: stopEntity: Unintended State");
}
if (e->type != AI_DIVERTER) {
switch (e->dir) {
case DIR_UP:
if (e->standupFrames)
e->state = STATE_STANDUP;
else
e->state = STATE_NONE;
break;
case DIR_DOWN:
if (e->standdownFrames)
e->state = STATE_STANDDOWN;
else
e->state = STATE_NONE;
break;
case DIR_LEFT:
if (e->standleftFrames)
e->state = STATE_STANDLEFT;
else
e->state = STATE_NONE;
break;
case DIR_RIGHT:
if (e->standrightFrames)
e->state = STATE_STANDRIGHT;
else
e->state = STATE_NONE;
break;
default:
warning("AI-FUNCS: stopEntity: DIR_NONE");
}
}
}
AIEntity *AI::locateEntity(const char *luaName) {
for (Common::Array<AIEntity *>::iterator it = _ents->begin(); it != _ents->end(); it++) {
if (Common::matchString((*it)->entityName, luaName)) {
return *it;
}
}
return NULL;
}
AIEntity *AI::findEntity(int x, int y) {
for (Common::Array<AIEntity *>::iterator it = _ents->begin(); it != _ents->end(); it++) {
if ((*it)->tileX == x && (*it)->tileY == y) {
return *it;
}
}
for (Common::Array<AIEntity *>::iterator it = _floats->begin(); it != _floats->end(); it++) {
if ((*it)->tileX == x && (*it)->tileY == y) {
return *it;
}
}
warning("STUB: findEntity: Check for Laser");
return NULL;
}
void AI::removeEntity(AIEntity *e) {
_ents->erase(&e);
}
void AI::setEntityGoal(AIEntity *e, int x, int y) {
int xv, yv;
e->xVel = e->yVel = 0;
xv = x - e->tileX;
if (xv < 0) {
e->xVel = -e->moveSpeed;
e->state = STATE_MOVELEFT;
e->dir = DIR_LEFT;
} else if (xv > 0) {
e->xVel = e->moveSpeed;
e->state = STATE_MOVERIGHT;
e->dir = DIR_RIGHT;
}
yv = y - e->tileY;
if (yv < 0) {
e->yVel = -e->moveSpeed;
e->state = STATE_MOVEUP;
e->dir = DIR_UP;
} else if (yv > 0) {
e->yVel = e->moveSpeed;
e->state = STATE_MOVEDOWN;
e->dir = DIR_DOWN;
}
if (e->type == AI_GUY && _playerRunning) {
e->xVel = e->xVel << 1;
e->yVel = e->yVel << 1;
}
e->goalX = x;
e->goalY = y;
e->animFrame = 0;
e->drawXOff = e->drawYOff = 0;
}
// Initializes each entity after map is loaded
void AI::initAllEnts() {
for (Common::Array<AIEntity *>::iterator it = _ents->begin(); it != _ents->end(); it++) {
(*it)->aiInit((*it));
if ((*it)->luaFuncInit[0]) {
if (g_hdb->_lua->callFunction((*it)->luaFuncInit, 2)) {
strcpy((*it)->entityName, g_hdb->_lua->getStringOffStack());
strcpy((*it)->printedName, g_hdb->_lua->getStringOffStack());
} else {
warning("'%s' doesn't exists", (*it)->luaFuncInit);
}
}
}
for (int i = 0; i < _numInventory; i++) {
AIEntity *temp = _inventory[i].ent;
// Clear out all ptrs in entity before writing
for (int j = 0; i < kMaxAnimFrames; i++) {
temp->blinkGfx[j] = NULL;
temp->movedownGfx[j] = NULL;
temp->moveupGfx[j] = NULL;
temp->moveleftGfx[j] = NULL;
temp->moverightGfx[j] = NULL;
temp->standdownGfx[j] = NULL;
temp->standupGfx[j] = NULL;
temp->standleftGfx[j] = NULL;
temp->standrightGfx[j] = NULL;
temp->special1Gfx[j] = NULL;
}
temp->blinkFrames = 0;
temp->movedownFrames = 0;
temp->moveupFrames = 0;
temp->moveleftFrames = 0;
temp->moverightFrames = 0;
temp->standdownFrames = 0;
temp->standupFrames = 0;
temp->standleftFrames = 0;
temp->standrightFrames = 0;
temp->draw = NULL;
temp->aiDraw = NULL;
temp->aiAction = temp->aiInit = temp->aiUse = NULL;
cacheEntGfx(temp, false);
}
warning("STUB: initAllEnts: Cache graphics for Deliveries");
warning("STUB: initAllEnts: LaserScan required");
}
void AI::killPlayer(Death method) {
int x = _player->x, y = _player->y;
stopEntity(_player);
_player->x = x;
_player->y = y;
_playerInvisible = false;
_playerDead = true;
warning("STUB: killPlayer: Close Dialogs");
switch (method) {
case DEATH_NORMAL:
_player->state = STATE_DYING;
warning("STUB: killPlayer: Play SND_GUY_DYING");
break;
case DEATH_FRIED:
_player->state = STATE_HORRIBLE1;
warning("STUB: killPlayer: Play SND_GUY_FRIED");
break;
case DEATH_SHOCKED:
_player->state = STATE_HORRIBLE2;
warning("STUB: killPlayer: Play SND_GUY_DYING");
warning("STUB: killPlayer: Play SND_SHOCKBOT_SHOCK");
break;
case DEATH_GRABBED:
_player->state = STATE_HORRIBLE3;
warning("STUB: killPlayer: Play SND_GUY_GRABBED");
break;
case DEATH_DROWNED:
_player->state = STATE_HORRIBLE4;
warning("STUB: killPlayer: Play SND_GUY_DROWN");
break;
case DEATH_PANICZONE:
_player->state = STATE_DYING;
warning("STUB: killPlayer: Play SND_PANIC_DEATH");
break;
case DEATH_PLUMMET:
_player->state = STATE_PLUMMET;
warning("STUB: killPlayer: Play SND_GUY_PLUMMET");
break;
}
warning("STUB: killPlayer: Stop Music");
}
/*
Note from original:
Moves the entity along toward its goal, sets current frame to draw
depending on its current state. Special checking is done for the
player in here to move him along his waypoints.
*/
void AI::animateEntity(AIEntity *e) {
int bgTileFlags, bgTileIndex;
int fgTileFlags, fgTileIndex;
// Move entity if player is not dead
debug(3, "Before animateEntity, e->x: %d, e->y: %d", e->x, e->y);
debug(3, "Before animateEntity, e->tileX: %d, e->tileY: %d", e->tileX, e->tileY);
if (!_playerDead) {
e->x += e->xVel;
e->y += e->yVel;
e->tileX = e->x / kTileWidth;
e->tileY = e->y / kTileHeight;
debug(3, "After animateEntity, e->x: %d, e->y: %d", e->x, e->y);
debug(3, "After animateEntity, e->tileX: %d, e->tileY: %d", e->tileX, e->tileY);
}
// For non-players, check for trigger being hit
if (onEvenTile(e->x, e->y)) {
warning("STUB: animateEntity: Check for Triggers being hit");
}
// If player, then scroll the screen with the player
if (e == _player && !_playerDead) {
if (!_cameraLock)
g_hdb->_map->centerMapXY(e->x + 16, e->y + 16);
// Check if player walked into teleporter
warning("STUB: animateEntity: Check Teleporter List");
// Check for bad tiles (DEATH)
int cx = (e->x + 16) / kTileWidth;
int cy = (e->y + 16) / kTileHeight;
bgTileFlags = g_hdb->_map->getMapBGTileFlags(cx, cy);
fgTileFlags = g_hdb->_map->getMapFGTileFlags(cx, cy);
if ((bgTileFlags & kFlagPlayerDie) && !(checkFloating(cx, cy)) && !(fgTileFlags & kFlagGrating)) {
if (bgTileFlags & kFlagEnergyFloor)
killPlayer(DEATH_SHOCKED);
else if ((bgTileFlags & kFlagPlasmaFloor) || (bgTileFlags & kFlagRadFloor))
killPlayer(DEATH_FRIED);
else
killPlayer(DEATH_NORMAL);
return;
}
// Check if player wants to stop
// If yes, sets last waypoint right in front of player
int xOff = 0, yOff = 0;
if (_numWaypoints > 1) {
switch (e->dir) {
case DIR_UP:
xOff = 0;
yOff = -1;
break;
case DIR_DOWN:
xOff = 0;
yOff = 1;
break;
case DIR_LEFT:
xOff = -1;
yOff = 0;
break;
case DIR_RIGHT:
xOff = 1;
yOff = 0;
break;
case DIR_NONE:
warning("AI-FUNCS: animateEntity: DIR_NONE found");
break;
}
if ((e->tileX+xOff == _waypoints[_numWaypoints-1].x && e->tileY + yOff == _waypoints[_numWaypoints - 1].y) && e->level == _waypoints[_numWaypoints - 1].level) {
memset(&_waypoints[0], 0, sizeof(_waypoints));
_numWaypoints = 1;
_waypoints[0].x = e->tileX + xOff;
_waypoints[0].y = e->tileY + yOff;
_waypoints[0].level = e->level;
e->goalX = e->tileX + xOff;
e->goalY = e->tileY + yOff;
}
}
}
// Check for moving up/down stair levels
bgTileIndex = g_hdb->_map->getMapBGTileIndex(e->tileX, e->tileY);
bgTileFlags = g_hdb->_map->getMapBGTileFlags(e->tileX, e->tileY);
fgTileFlags = g_hdb->_map->getMapFGTileFlags(e->tileX, e->tileY);
if (bgTileFlags & kFlagStairTop)
e->level = 2;
else if (bgTileFlags & kFlagStairBot)
e->level = 1;
// Reached goal?
// Cinematic require less accuracy for NPCs
int result;
if (_cineActive && e != _player)
result = (abs(e->x - (e->goalX * kTileWidth)) <= abs(e->xVel)) && (abs(e->y - (e->goalY * kTileHeight)) <= abs(e->yVel));
else
result = (e->x == e->goalX * kTileWidth) && (e->y == e->goalY * kTileHeight);
if (result) {
// If player, this is a waypoint goal.
// Drop one waypoint from list
if (e == _player) {
removeFirstWaypoint();
_playerEmerging = false;
}
// If entity not player, stop it here
// If entity is player and no waypoints are left, stop it here
if (e != _player || (!_numWaypoints && e == _player)) {
warning("STUB: animateEntity: Stop entity");
} else if (onEvenTile(e->x, e->y))
setEntityGoal(e, _waypoints[0].x, _waypoints[0].y);
}
animEntFrames(e);
}
void AI::animEntFrames(AIEntity *e) {
int max = 1;
// Set current graphic to draw
switch (e->state) {
case STATE_STANDDOWN:
e->draw = e->standdownGfx[0];
max = 1;
break;
case STATE_STANDUP:
e->draw = e->standupGfx[0];
max = 1;
break;
case STATE_STANDLEFT:
e->draw = e->standleftGfx[0];
max = 1;
break;
case STATE_STANDRIGHT:
e->draw = e->standrightGfx[0];
max = 1;
break;
case STATE_BLINK:
e->draw = e->blinkGfx[e->animFrame];
max = e->blinkFrames;
break;
case STATE_MOVEUP:
e->draw = e->moveupGfx[e->animFrame];
max = e->moveupFrames;
break;
case STATE_MOVEDOWN:
e->draw = e->movedownGfx[e->animFrame];
max = e->movedownFrames;
break;
case STATE_MOVELEFT:
e->draw = e->moveleftGfx[e->animFrame];
max = e->moveleftFrames;
break;
case STATE_MOVERIGHT:
e->draw = e->moverightGfx[e->animFrame];
max = e->moverightFrames;
break;
case STATE_PUSHDOWN:
e->draw = _pushdownGfx[e->animFrame];
max = _pushdownFrames;
break;
case STATE_PUSHUP:
e->draw = _pushupGfx[e->animFrame];
max = _pushupFrames;
break;
case STATE_PUSHLEFT:
e->draw = _pushleftGfx[e->animFrame];
max = _pushleftFrames;
break;
case STATE_PUSHRIGHT:
e->draw = _pushrightGfx[e->animFrame];
max = _pushrightFrames;
break;
case STATE_GRABUP:
e->draw = _getGfx[DIR_UP];
max = 1;
break;
case STATE_GRABDOWN:
e->draw = _getGfx[DIR_DOWN];
max = 1;
break;
case STATE_GRABLEFT:
e->draw = _getGfx[DIR_LEFT];
max = 1;
break;
case STATE_GRABRIGHT:
e->draw = _getGfx[DIR_RIGHT];
max = 1;
break;
case STATE_HORRIBLE1:
e->draw = _horrible1Gfx[e->animFrame];
max = _horrible1Frames;
if (e->animFrame == max - 1)
e->state = STATE_DEAD;
break;
case STATE_HORRIBLE2:
{
static int click = 0;
e->draw = _horrible2Gfx[e->animFrame];
max = _horrible2Frames;
click++;
if (click == 16) {
warning("STUB: Play SND_SHOCKBOT_SHOCK");
click = 0;
}
}
break;
case STATE_HORRIBLE3:
{
static int click = 0;
e->draw = _horrible3Gfx[e->animFrame];
max = _horrible3Frames;
click++;
if (click == 32) {
warning("STUB: Play SND_GUY_GRABBED");
click = 0;
}
}
break;
case STATE_HORRIBLE4:
e->draw = _horrible4Gfx[e->animFrame];
max = _horrible4Frames;
if (e->animFrame == max - 1)
e->state = STATE_DEAD;
break;
case STATE_PLUMMET:
e->draw = _plummetGfx[e->animFrame];
max = _plummetFrames;
if (e->animFrame == max - 1) {
e->state = STATE_NONE;
setPlayerInvisible(true);
}
break;
//
// maintenance bot uses stuff
//
case STATE_USEDOWN:
e->draw = e->standdownGfx[4];
return;
case STATE_USEUP:
e->draw = e->standupGfx[4];
return;
case STATE_USELEFT:
e->draw = e->standleftGfx[4];
return;
case STATE_USERIGHT:
e->draw = e->standrightGfx[4];
return;
//
// death!
//
case STATE_DYING:
e->draw = _dyingGfx[e->animFrame];
max = _dyingFrames;
if (e->animFrame == max - 1)
e->state = STATE_DEAD;
break;
case STATE_DEAD:
e->draw = _dyingGfx[_dyingFrames - 1];
max = _dyingFrames;
break;
case STATE_GOODJOB:
e->draw = _goodjobGfx;
max = 1;
break;
//
// floating stuff uses the "standup" frames for animating the float
//
case STATE_FLOATING:
case STATE_FLOATDOWN:
case STATE_FLOATUP:
case STATE_FLOATLEFT:
case STATE_FLOATRIGHT:
e->draw = e->blinkGfx[e->animFrame];
max = e->blinkFrames;
break;
case STATE_MELTED:
case STATE_EXPLODING:
e->draw = e->special1Gfx[e->animFrame];
max = e->special1Frames;
if (e->type == AI_BOOMBARREL) { // while exploding, call this function
aiBarrelExplodeSpread(e);
if (e->animFrame == max - 1) {
removeEntity(e);
return;
}
}
break;
//
// ICEPUFF states
//
case STATE_ICEP_PEEK:
e->draw = e->blinkGfx[e->animFrame];
max = e->blinkFrames;
break;
case STATE_ICEP_APPEAR:
e->draw = e->standupGfx[e->animFrame];
max = e->standupFrames;
break;
case STATE_ICEP_THROWDOWN:
e->draw = e->standdownGfx[e->animFrame];
max = e->standdownFrames;
break;
case STATE_ICEP_THROWRIGHT:
e->draw = e->standrightGfx[e->animFrame];
max = e->standrightFrames;
break;
case STATE_ICEP_THROWLEFT:
e->draw = e->standleftGfx[e->animFrame];
max = e->standleftFrames;
break;
case STATE_ICEP_DISAPPEAR:
e->draw = e->special1Gfx[e->animFrame];
max = e->special1Frames;
break;
//
// MEERKAT states
//
case STATE_MEER_MOVE:
e->draw = e->standdownGfx[e->animFrame];
max = e->standdownFrames;
break;
case STATE_MEER_APPEAR:
e->draw = e->standleftGfx[e->animFrame];
max = e->standleftFrames;
break;
case STATE_MEER_BITE:
e->draw = e->standrightGfx[e->animFrame];
max = e->standrightFrames;
break;
case STATE_MEER_DISAPPEAR:
e->draw = e->standupGfx[e->animFrame];
max = e->standupFrames;
break;
case STATE_MEER_LOOK:
e->draw = e->movedownGfx[e->animFrame];
max = e->movedownFrames;
break;
//
// DIVERTER spawning states
//
case STATE_DIVERTER_BL:
e->draw = e->standdownGfx[e->animFrame];
max = e->standdownFrames;
break;
case STATE_DIVERTER_BR:
e->draw = e->standupGfx[e->animFrame];
max = e->standupFrames;
break;
case STATE_DIVERTER_TL:
e->draw = e->standleftGfx[e->animFrame];
max = e->standleftFrames;
break;
case STATE_DIVERTER_TR:
e->draw = e->standrightGfx[e->animFrame];
max = e->standrightFrames;
break;
//
// DOLLY states
// angry[4] = standright[4]
// kissright[4]/kissleft[4] = standleft[8]
// panic[4]/laugh[4] = standdown[8]
// dollyuseright[5] = special1[5]
//
case STATE_ANGRY:
e->draw = e->standrightGfx[e->animFrame];
max = 2;
break;
case STATE_KISSRIGHT:
e->draw = e->standleftGfx[e->animFrame];
max = 4;
break;
case STATE_KISSLEFT:
e->draw = e->standleftGfx[e->animFrame + 4];
max = 4;
break;
case STATE_PANIC:
e->draw = e->standdownGfx[e->animFrame];
max = 2;
break;
case STATE_LAUGH:
e->draw = e->standdownGfx[e->animFrame + 4];
max = 2;
break;
case STATE_DOLLYUSERIGHT:
e->draw = e->special1Gfx[e->animFrame];
max = e->special1Frames;
break;
// SARGE yelling
case STATE_YELL:
e->draw = e->special1Gfx[e->animFrame];
max = e->special1Frames;
break;
default:
warning("AI-FUNCS: animEntFrames: Unintended State for entity %s", AIType2Str(e->type));
break;
}
// Cycle animation frames
if (e->animDelay-- > 0)
return;
e->animDelay = e->animCycle;
e->animFrame++;
if (e->animFrame == max) {
e->animFrame = 0;
}
}
void AI::animLuaEntity(const char *initName, AIState st) {
for (Common::Array<AIEntity *>::iterator it = _ents->begin(); it != _ents->end(); it++) {
if (Common::matchString((*it)->entityName, initName)) {
(*it)->state = st;
(*it)->animFrame = 0;
(*it)->animDelay = (*it)->animCycle;
}
}
}
void AI::drawEnts(int x, int y, int w, int h) {
static int stunAnim = 0;
static uint32 stunTimer = g_hdb->getTimeSlice();
// Draw Floating Entities
for (Common::Array<AIEntity *>::iterator it = _floats->begin(); it != _floats->end(); it++) {
AIEntity *e = (*it);
if (e->aiDraw) {
e->aiDraw(e, x, y);
}
if ((e->x > x - kTileWidth) && (e->x < x + w) && (e->y > y - kTileHeight) && (e->y < y + h)) {
e->draw->drawMasked(e->x - x + e->drawXOff, e->y - y + e->drawYOff);
e->onScreen = 1;
} else
e->onScreen = 0;
}
// Draw all other Ents
_numLevel2Ents = 0;
for (Common::Array<AIEntity *>::iterator it = _ents->begin(); it != _ents->end(); it++) {
AIEntity *e = (*it);
debugN(5, "AI::drawEnts: enity %s(%d) state %s(%d)...", AIType2Str(e->type), e->type, AIState2Str(e->state), e->state);
if (e->type == AI_LASER || e->type == AI_DIVERTER) {
if (e->aiDraw) {
if (e->level == 2 && _numLevel2Ents < kMaxLevel2Ents) {
_entsLevel2[_numLevel2Ents].aiDraw = e->aiDraw;
_entsLevel2[_numLevel2Ents].x = x;
_entsLevel2[_numLevel2Ents].y = y;
_entsLevel2[_numLevel2Ents].e = e;
_entsLevel2[_numLevel2Ents].stunnedWait = 0;
_numLevel2Ents++;
} else {
e->aiDraw(e, x, y);
}
}
}
if ((e->x > x - kTileWidth) && (e->x < x + w) && (e->y > y - kTileHeight) && (e->y < y + h)) {
// If extra drawing func is present, call it
if (e->aiDraw && e->type != AI_LASER && e->type != AI_DIVERTER) {
if (e->level == 2 && _numLevel2Ents < kMaxLevel2Ents) {
_entsLevel2[_numLevel2Ents].aiDraw = e->aiDraw;
_entsLevel2[_numLevel2Ents].draw = e->draw;
_entsLevel2[_numLevel2Ents].x = x;
_entsLevel2[_numLevel2Ents].y = y;
_entsLevel2[_numLevel2Ents].e = e;
_entsLevel2[_numLevel2Ents].stunnedWait = 0;
_numLevel2Ents++;
} else
e->aiDraw(e, x, y);
}
switch (e->type) {
case AI_VORTEXIAN:
if (e->draw)
e->draw->drawMasked(e->x - x + e->drawXOff, e->y - y + e->drawYOff);
debug(1, "STUB: AI::drawEnts: Replace Masked Blitting with Alpha Masked Blitting");
break;
case AI_GUY: // Draw Player Last
break;
default:
if (e->level == 2 && _numLevel2Ents < kMaxLevel2Ents) {
_entsLevel2[_numLevel2Ents].aiDraw = NULL;
_entsLevel2[_numLevel2Ents].draw = e->draw;
_entsLevel2[_numLevel2Ents].x = e->x - x + e->drawXOff;
_entsLevel2[_numLevel2Ents].y = e->y - y + e->drawYOff;
_entsLevel2[_numLevel2Ents].e = NULL;
_entsLevel2[_numLevel2Ents].stunnedWait = e->stunnedWait;
_numLevel2Ents++;
} else {
debugN(5, "trying to draw...");
if (e->draw) {
debugN(5, "at %d %d", e->x, e->y);
e->draw->drawMasked(e->x - x + e->drawXOff, e->y - y + e->drawYOff);
} else {
debugN(5, "no draw function");
}
if (e->stunnedWait)
g_hdb->_ai->_stunnedGfx[stunAnim]->drawMasked(e->x - x, e->y - y);
}
break;
}
e->onScreen = 1;
} else {
e->onScreen = 0;
debugN(5, "not on screen");
}
debug(5, ""); // newline
}
if (stunTimer < g_hdb->getTimeSlice()) {
stunAnim = (stunAnim + 1) & 3;
stunTimer = g_hdb->getTimeSlice();
}
// Draw player last
if (_player && _player->level < 2 && !_playerInvisible && _player->draw) {
_player->draw->drawMasked(_player->x - x + _player->drawXOff, _player->y - y + _player->drawYOff);
}
}
void AI::animGrabbing() {
if (_player->state == STATE_GRABUP ||
_player->state == STATE_GRABDOWN ||
_player->state == STATE_GRABLEFT ||
_player->state == STATE_GRABRIGHT)
return;
AIState s = STATE_NONE;
switch (_player->dir) {
case DIR_UP:
s = STATE_GRABUP;
_player->draw = _getGfx[DIR_UP];
break;
case DIR_DOWN:
s = STATE_GRABDOWN;
_player->draw = _getGfx[DIR_DOWN];
break;
case DIR_LEFT:
s = STATE_GRABLEFT;
_player->draw = _getGfx[DIR_LEFT];
break;
case DIR_RIGHT:
s = STATE_GRABRIGHT;
_player->draw = _getGfx[DIR_RIGHT];
break;
default:
warning("AI-FUNCS: animGrabbing: DIR_NONE found");
break;
}
_player->state = s;
_player->animFrame = 5;
}
void AI::moveEnts() {
static int frameDelay = kAnimFrameDelay;
AIEntity *e;
if (frameDelay-- > 0)
return;
// Call aiAction for Floating Entities
for (Common::Array<AIEntity *>::iterator it = _floats->begin(); it != _floats->end(); it++) {
if ((*it)->aiAction)
(*it)->aiAction((*it));
}
// Call aiAction for all other Entities
for (Common::Array<AIEntity *>::iterator it = _ents->begin(); it != _ents->end(); it++) {
e = (*it);
if (e->aiAction) {
// NPC Touchplate Counter
if (e != _player && e->touchpWait) {
e->touchpWait--;
if (!e->touchpWait) {
if (e->tileX == e->touchpX && e->tileY == e->touchpY && onEvenTile(e->x, e->y))
e->touchpWait = 1;
else {
checkActionList(e, e->touchpX, e->touchpY, false);
g_hdb->_map->setMapBGTileIndex(e->touchpX, e->touchpY, e->touchpTile);
e->touchpX = e->touchpY = e->touchpTile = 0;
}
}
}
// Stunned Entity Timer
if (!e->stunnedWait)
e->aiAction(e);
else {
if (e->stunnedWait < g_hdb->getTimeSlice())
e->stunnedWait = 0;
}
}
}
warning("STUB: moveEnts: Laser Rescan");
warning("STUB: moveEnts: Laser Looping Sound Channel");
}
int AI::checkForTouchplate(int x, int y) {
int tileIndex = g_hdb->_map->getMapBGTileIndex(x, y);
if (tileIndex == _touchplateOff || tileIndex == _templeTouchpOff)
return tileIndex;
return 0;
}
bool AI::checkFloating(int x, int y) {
for (Common::Array<AIEntity *>::iterator it = _floats->begin(); it != _floats->end(); it++) {
if ((*it)->tileX == x && (*it)->tileY == y)
return true;
}
return false;
}
// Check to see if we can get this entity
bool AI::getTableEnt(AIType type) {
switch (type) {
case ITEM_CELL:
case ITEM_ENV_WHITE:
case ITEM_ENV_RED:
case ITEM_ENV_BLUE:
case ITEM_ENV_GREEN:
case ITEM_TRANSCEIVER:
case ITEM_CLUB:
case ITEM_ROBOSTUNNER:
case ITEM_SLUGSLINGER:
case ITEM_MONKEYSTONE:
case ITEM_GOO_CUP:
case ITEM_TEACUP:
case ITEM_BURGER:
case ITEM_PDA:
case ITEM_BOOK:
case ITEM_CLIPBOARD:
case ITEM_NOTE:
case ITEM_KEYCARD_WHITE:
case ITEM_KEYCARD_BLUE:
case ITEM_KEYCARD_RED:
case ITEM_KEYCARD_GREEN:
case ITEM_KEYCARD_PURPLE:
case ITEM_KEYCARD_BLACK:
case ITEM_SEED:
case ITEM_SODA:
case ITEM_SLICER:
case ITEM_DOLLYTOOL1:
case ITEM_DOLLYTOOL2:
case ITEM_DOLLYTOOL3:
case ITEM_DOLLYTOOL4:
return true;
default:
return false;
}
}
// Check to see if it's okay to move through this entity
bool AI::walkThroughEnt(AIType type) {
switch (type) {
case AI_VORTEXIAN:
case AI_MEERKAT:
case AI_GOODFAIRY:
case AI_BADFAIRY:
case AI_GATEPUDDLE:
case AI_BUZZFLY:
case AI_OMNIBOT:
case AI_PUSHBOT:
case AI_TURNBOT:
case AI_RIGHTBOT:
case ITEM_GEM_WHITE:
case ITEM_GEM_BLUE:
case ITEM_GEM_RED:
case ITEM_GEM_GREEN:
return true;
default:
return getTableEnt(type);
}
}
// Play special sound for every item you get
void AI::getItemSound(AIType type) {
warning("STUB: AI: getItemSound required");
}
void AI::lookAtEntity(AIEntity *e) {
lookAtXY(e->tileX, e->tileY);
}
// Change player direction to XY
void AI::lookAtXY(int x, int y) {
int distX, distY;
distX = abs(_player->tileX - x);
distY = abs(_player->tileY - y);
if (distX > distY) {
// X takes precedence
if (x < _player->tileX) {
_player->dir = DIR_LEFT;
} else if (x > _player->tileX) {
_player->dir = DIR_RIGHT;
} else if (y < _player->tileY) {
_player->dir = DIR_UP;
} else {
_player->dir = DIR_DOWN;
}
} else {
// Y takes precedence
if (y < _player->tileY) {
_player->dir = DIR_UP;
} else if (y > _player->tileY) {
_player->dir = DIR_DOWN;
} else if (x < _player->tileX) {
_player->dir = DIR_LEFT;
} else {
_player->dir = DIR_RIGHT;
}
}
switch (_player->dir) {
case DIR_UP:
_player->state = STATE_STANDUP;
_player->draw = _getGfx[DIR_UP];
break;
case DIR_DOWN:
_player->state = STATE_STANDDOWN;
_player->draw = _getGfx[DIR_DOWN];
break;
case DIR_LEFT:
_player->state = STATE_STANDLEFT;
_player->draw = _getGfx[DIR_LEFT];
break;
case DIR_RIGHT:
_player->state = STATE_STANDRIGHT;
_player->draw = _getGfx[DIR_RIGHT];
break;
default:
warning("AI-FUNCS: lookAtXY: DIR_NONE");
}
}
} // End of Namespace