Player now moves out of the way if he's blocking an entrance when an NPC enters. Also changed errors in unimplemented NPC actions to warnings

svn-id: r22731
This commit is contained in:
Paul Gilbert 2006-05-29 08:12:07 +00:00
parent 5d562eb3c5
commit 5fa3985bcb
7 changed files with 201 additions and 49 deletions

View File

@ -310,7 +310,7 @@ void Game::handleClick() {
if (response != MENUITEM_NONE)
handleMenuResponse(response);
} else if ((room.cursorState() == CS_SEQUENCE) ||
(room.cursorState() == CS_UNKNOWN)) {
(room.cursorState() == CS_BUMPED)) {
// No action necessary
} else {
if (mouse.lButton())

View File

@ -75,6 +75,8 @@ Hotspot::Hotspot(HotspotData *res): _pathFinder(this) {
_actionCtr = 0;
_blockedOffset = 0;
_exitCtr = 0;
_blockedState = BS_NONE;
_unknownFlag = false;
if (_data->npcSchedule != 0) {
CharacterScheduleEntry *entry = resources.charSchedules().getEntry(_data->npcSchedule);
@ -96,6 +98,8 @@ Hotspot::Hotspot(Hotspot *character, uint16 objType): _pathFinder(this) {
_destHotspotId = character->hotspotId();
_blockedOffset = 0;
_exitCtr = 0;
_blockedState = BS_NONE;
_unknownFlag = false;
switch (objType) {
case VOICE_ANIM_ID:
@ -389,6 +393,31 @@ void Hotspot::faceHotspot(HotspotData *hotspot) {
}
}
// Sets a character walking to a random destination position
void Hotspot::setRandomDest() {
Resources &res = Resources::getReference();
RoomData *roomData = res.getRoom(roomNumber());
Common::Rect &rect = roomData->walkBounds;
Common::RandomSource _rnd;
int tryCtr = 0;
int16 xp, yp;
if (_currentActions.isEmpty())
_currentActions.addFront(START_WALKING, roomNumber());
else
_currentActions.top().setAction(START_WALKING);
while (tryCtr ++ <= 20) {
xp = rect.left + _rnd.getRandomNumber(rect.right - rect.left);
yp = rect.left + _rnd.getRandomNumber(rect.right - rect.left);
setDestPosition(xp, yp);
if (!roomData->paths.isOccupied(xp, yp) && !roomData->paths.isOccupied(xp, yp))
break;
}
}
// Sets or clears the hotspot as occupying an area in its room's pathfinding data
void Hotspot::setOccupied(bool occupiedFlag) {
@ -464,6 +493,30 @@ bool Hotspot::walkingStep() {
return false;
}
void Hotspot::updateMovement() {
assert(_data != NULL);
if (_currentActions.action() == EXEC_HOTSPOT_SCRIPT) {
if (_data->coveredFlag) {
// Reset position and direction
resetPosition();
} else {
// Make sure the cell occupied by character is covered
_data->coveredFlag = true;
setOccupied(true);
}
}
}
void Hotspot::updateMovement2(CharacterMode value) {
setCharacterMode(value);
updateMovement();
}
void Hotspot::resetPosition() {
setPosition(x() & 0xf8 | 5, y());
setDirection(direction());
}
/*-------------------------------------------------------------------------*/
/* Hotspot action handling */
/* */
@ -1302,7 +1355,7 @@ void Hotspot::npcUnknown2(HotspotData *hotspot) {
void Hotspot::npcSetRandomDest(HotspotData *hotspot) {
endAction();
Support::setRandomDest(*this);
setRandomDest();
}
void Hotspot::npcWalkingCheck(HotspotData *hotspot) {
@ -1373,19 +1426,23 @@ void Hotspot::npcDispatchAction(HotspotData *hotspot) {
}
void Hotspot::npcUnknown3(HotspotData *hotspot) {
error("npcUnknown3: Not yet implemented");
warning("npcUnknown3: Not yet implemented");
endAction();
}
void Hotspot::npcUnknown4(HotspotData *hotspot) {
error("npcUnknown4: Not yet implemented");
warning("npcUnknown4: Not yet implemented");
endAction();
}
void Hotspot::npcStartTalking(HotspotData *hotspot) {
error("npcStartTalking: Not yet implemented");
warning("npcStartTalking: Not yet implemented");
endAction();
}
void Hotspot::npcJumpAddress(HotspotData *hotspot) {
error("npcJumpAddress: Not yet implemented");
warning("npcJumpAddress: Not yet implemented");
endAction();
}
/*------------------------------------------------------------------------*/
@ -1460,7 +1517,7 @@ void HotspotTickHandlers::standardCharacterAnimHandler(Hotspot &h) {
CurrentActionStack &actions = h.currentActions();
uint16 impingingList[MAX_NUM_IMPINGING];
int numImpinging;
int index;
bool bumpedPlayer;
// TODO: handle talk dialogs countdown if necessary
@ -1471,26 +1528,74 @@ void HotspotTickHandlers::standardCharacterAnimHandler(Hotspot &h) {
}
numImpinging = Support::findIntersectingCharacters(h, impingingList);
bumpedPlayer = (numImpinging == 0) ? false :
Support::isCharacterInList(impingingList, numImpinging, PLAYER_ID);
// Check for character having just changed room
if (h.skipFlag()) {
if (numImpinging > 0) {
index = 0;
while ((index < numImpinging) && (impingingList[index] != PLAYER_ID))
++index;
if (index != numImpinging) {
// Character has bumped into player
// TODO: Figure out handling code
error("Unimplemented - character bumping into player");
// Scan to check if the character has bumped into player
Hotspot *player = res.getActiveHotspot(PLAYER_ID);
if (bumpedPlayer && (player->characterMode() == CHARMODE_IDLE)) {
// Signal the player to move out of the way automatically
player->setBlockedState(BS_INITIAL);
player->setDestHotspot(0);
Room::getReference().setCursorState(CS_BUMPED);
player->setRandomDest();
} else {
// Signal the character to pause briefly to allow bumped
// character time to start moving out of the way
h.setDelayCtr(10);
h.setCharacterMode(CHARMODE_PAUSED);
}
return;
}
h.setSkipFlag(false);
}
// TODO: Handling of any set Tick Script Offset, as well as certain other
// as of yet unknown hotspot flags
if (h.characterMode() != CHARMODE_NONE) {
if (h.characterMode() == CHARMODE_6) {
// TODO: Figure out what mode 6 is
h.updateMovement();
if (bumpedPlayer) return;
} else {
// All other character modes
if (h.delayCtr() > 0) {
// There is some countdown left to do
bool decrementFlag = true; //TODO: = HS[50h] == 0
if (!decrementFlag) {
HotspotData *hotspot = res.getHotspot(0); // TODO: HS[50h]
decrementFlag = (hotspot->roomNumber != h.roomNumber()) ? false :
Support::charactersIntersecting(hotspot, h.resource());
}
if (decrementFlag) {
h.setDelayCtr(h.delayCtr() - 1);
return;
}
}
}
// TODO: HS[50h]=0
CharacterMode currentMode = h.characterMode();
h.setCharacterMode(CHARMODE_NONE);
h.pathFinder().clear();
if ((currentMode == CHARMODE_4) || (currentMode == CHARMODE_7)) {
// TODO: HS[33h]=0
Dialog::showMessage(1, h.hotspotId());
}
return;
}
CurrentAction action = actions.action();
switch (action) {
@ -1639,6 +1744,7 @@ void HotspotTickHandlers::playerAnimHandler(Hotspot &h) {
case NO_ACTION:
// Make sure there is no longer any destination
h.setDestHotspot(0);
h.updateMovement2(CHARMODE_IDLE);
break;
case DISPATCH_ACTION:
@ -1678,9 +1784,17 @@ void HotspotTickHandlers::playerAnimHandler(Hotspot &h) {
// Deliberate fall through to processing walking path
case PROCESSING_PATH:
h.setCharacterMode(CHARMODE_NONE);
if (!pathFinder.process()) break;
// Pathfinding is now complete
/*
if ((pathFinder.result() != PF_OK) && (h.unknownFlag() ||
(pathFinder.result() != PF_DEST_OCCUPIED))) {
// TODO: occupiedFlag
}
*/
actions.pop();
if (pathFinder.isEmpty()) {
@ -1709,6 +1823,9 @@ void HotspotTickHandlers::playerAnimHandler(Hotspot &h) {
if (h.walkingStep()) {
// Walking done
Room &room = Room::getReference();
if (room.cursorState() == CS_BUMPED)
room.setCursorState(CS_NONE);
h.currentActions().pop();
}
@ -2003,7 +2120,7 @@ void HotspotTickHandlers::npcRoomChange(Hotspot &h) {
}
if (numCharacters >= 4) {
error("npcChangeRoom - too many characters - yet to be tested");
warning("XYZZY npcChangeRoom - too many characters - yet to be tested");
uint16 dataId = res.getCharOffset(0);
CharacterScheduleEntry *entry = res.charSchedules().getEntry(dataId);
h.currentActions().addFront(DISPATCH_ACTION, entry, h.roomNumber());
@ -2062,14 +2179,18 @@ PathFinder::PathFinder(Hotspot *h) {
_stepCtr = 0;
}
void PathFinder::reset(RoomPathsData &src) {
void PathFinder::clear() {
_stepCtr = 0;
_list.clear();
src.decompress(_layer, _hotspot->widthCopy());
_inProgress = false;
_countdownCtr = PATHFIND_COUNTDOWN;
}
void PathFinder::reset(RoomPathsData &src) {
clear();
src.decompress(_layer, _hotspot->widthCopy());
}
// Does the next stage of processing to figure out a path to take to a given
// destination. Returns true if the path finding has been completed
@ -2439,6 +2560,7 @@ int Support::findIntersectingCharacters(Hotspot &h, uint16 *charList) {
int numImpinging = 0;
Resources &res = Resources::getReference();
Rect r;
uint16 hotspotY;
r.left = h.x();
r.right = h.x() + h.widthCopy();
@ -2456,12 +2578,13 @@ int Support::findIntersectingCharacters(Hotspot &h, uint16 *charList) {
hotspot.skipFlag()) continue;
// TODO: See why si+ANIM_HOTSPOT_OFFSET compared aganst di+ANIM_VOICE_CTR
hotspotY = hotspot.y() + hotspot.heightCopy();
if ((hotspot.x() > r.right) || (hotspot.x() + hotspot.widthCopy() <= r.left) ||
(hotspot.y() + hotspot.heightCopy() + hotspot.charRectY() > r.bottom) ||
(hotspot.y() + hotspot.heightCopy() - hotspot.charRectY()
- hotspot.yCorrection() <= r.top))
(hotspotY + hotspot.charRectY() < r.top) ||
(hotspotY - hotspot.charRectY() - hotspot.yCorrection() >= r.bottom))
continue;
// Add hotspot Id to list
if (numImpinging == MAX_NUM_IMPINGING)
error("Exceeded maximum allowable number of impinging characters");
@ -2534,26 +2657,6 @@ void Support::characterChangeRoom(Hotspot &h, uint16 roomNumber,
}
}
void Support::setRandomDest(Hotspot &h) {
Resources &res = Resources::getReference();
RoomData *roomData = res.getRoom(h.roomNumber());
Common::Rect &rect = roomData->walkBounds;
Common::RandomSource _rnd;
int tryCtr = 0;
int16 xp, yp;
h.currentActions().top().setAction(DISPATCH_ACTION);
while (tryCtr ++ <= 20) {
xp = rect.left + _rnd.getRandomNumber(rect.right - rect.left);
yp = rect.left + _rnd.getRandomNumber(rect.right - rect.left);
h.setDestPosition(xp, yp);
if (!roomData->paths.isOccupied(xp, yp) && !roomData->paths.isOccupied(xp, yp))
break;
}
}
bool Support::charactersIntersecting(HotspotData *hotspot1, HotspotData *hotspot2) {
return !((hotspot1->startX + hotspot1->widthCopy + 4 < hotspot2->startX) ||
(hotspot2->startX + hotspot2->widthCopy + 4 < hotspot1->startX) ||
@ -2563,4 +2666,10 @@ bool Support::charactersIntersecting(HotspotData *hotspot1, HotspotData *hotspot
hotspot1->startY + hotspot1->heightCopy - hotspot1->yCorrection - 2));
}
bool Support::isCharacterInList(uint16 *lst, int numEntries, uint16 charId) {
while (numEntries-- > 0)
if (*lst++ == charId) return true;
return false;
}
} // end of namespace Lure

View File

@ -41,8 +41,8 @@ public:
static void checkRoomChange(Hotspot &h);
static void characterChangeRoom(Hotspot &h, uint16 roomNumber,
int16 newX, int16 newY, Direction dir);
static void setRandomDest(Hotspot &h);
static bool charactersIntersecting(HotspotData *hotspot1, HotspotData *hotspot2);
static bool isCharacterInList(uint16 *lst, int numEntries, uint16 charId);
};
typedef void(*HandlerMethodPtr)(Hotspot &h);
@ -172,9 +172,9 @@ private:
void addBack(Direction dir, int steps) {
_list.push_back(new WalkingActionEntry(dir, steps));
}
void clear() { _list.clear(); }
public:
PathFinder(Hotspot *h);
void clear();
void reset(RoomPathsData &src);
bool process();
void list();
@ -183,10 +183,13 @@ public:
WalkingActionEntry &top() { return **_list.begin(); }
bool isEmpty() { return _list.empty(); }
int &stepCtr() { return _stepCtr; }
PathFinderResult result() { return _result; }
};
enum HotspotPrecheckResult {PC_EXECUTE, PC_NOT_IN_ROOM, PC_UNKNOWN, PC_INITIAL, PC_EXCESS};
enum BlockedState {BS_NONE, BS_INITIAL, BS_UNKNOWN};
class Hotspot {
private:
HotspotData *_data;
@ -224,6 +227,8 @@ private:
uint16 _destHotspotId;
uint16 _blockedOffset;
uint8 _exitCtr;
BlockedState _blockedState;
bool _unknownFlag;
// Support methods
void startTalk(HotspotData *charHotspot);
@ -234,6 +239,7 @@ private:
void actionPrecheck3(HotspotData *hotspot);
bool characterWalkingCheck(HotspotData *hotspot);
bool doorCloseCheck(uint16 doorId);
void resetDirection();
// Action set
void doNothing(HotspotData *hotspot);
@ -295,6 +301,8 @@ public:
uint16 destHotspotId() { return _destHotspotId; }
uint16 blockedOffset() { return _blockedOffset; }
uint8 exitCtr() { return _exitCtr; }
BlockedState blockedState() { return _blockedState; }
bool unknownFlag() { return _unknownFlag; }
uint16 width() { return _width; }
uint16 height() { return _height; }
uint16 widthCopy() { return _widthCopy; }
@ -320,14 +328,33 @@ public:
void setDestPosition(int16 newX, int16 newY) { _destX = newX; _destY = newY; }
void setDestHotspot(uint16 id) { _destHotspotId = id; }
void setExitCtr(uint8 value) { _exitCtr = value; }
void setBlockedState(BlockedState newState) { _blockedState = newState; }
void setUnknownFlag(bool value) { _unknownFlag = value; }
void setSize(uint16 newWidth, uint16 newHeight);
void setScript(uint16 offset) {
assert(_data != NULL);
_sequenceOffset = offset;
_data->sequenceOffset = offset;
}
void setActions(uint32 newActions) { _actions = newActions; }
void setCharRectY(uint16 value) { _charRectY = value; }
void setSkipFlag(bool value) { _skipFlag = value; }
CharacterMode characterMode() {
assert(_data != NULL);
return _data->characterMode;
}
void setCharacterMode(CharacterMode value) {
assert(_data != NULL);
_data->characterMode = value;
}
uint16 delayCtr() {
assert(_data != NULL);
return _data->delayCtr;
}
void setDelayCtr(uint16 value) {
assert(_data != NULL);
_data->delayCtr = value;
}
void copyTo(Surface *dest);
bool executeScript();
@ -340,8 +367,12 @@ public:
void endAction();
void setDirection(Direction dir);
void faceHotspot(HotspotData *hotspot);
void setRandomDest();
void setOccupied(bool occupiedFlag);
bool walkingStep();
void updateMovement();
void updateMovement2(CharacterMode value);
void resetPosition();
// Actions
void doAction();

View File

@ -291,6 +291,11 @@ HotspotData::HotspotData(HotspotResource *rec) {
tickTimeout = READ_LE_UINT16(&rec->tickTimeout);
tickSequenceOffset = READ_LE_UINT16(&rec->tickSequenceOffset);
npcSchedule = READ_LE_UINT16(&rec->npcSchedule);
// Initialise dynamic fields
delayCtr = 0;
characterMode = CHARMODE_NONE;
coveredFlag = false;
}
// Hotspot override data

View File

@ -373,6 +373,9 @@ public:
HotspotActionList *getActions(uint16 recordId);
};
enum CharacterMode {CHARMODE_NONE, CHARMODE_1, CHARMODE_IDLE, CHARMODE_PAUSED,
CHARMODE_4, CHARMODE_5, CHARMODE_6, CHARMODE_7};
class HotspotData {
public:
HotspotData(HotspotResource *rec);
@ -405,7 +408,11 @@ public:
uint16 tickProcOffset;
uint16 tickTimeout;
uint16 tickSequenceOffset;
uint16 npcSchedule;
uint16 npcSchedule;
uint16 delayCtr;
CharacterMode characterMode;
bool coveredFlag;
void enable() { flags |= 0x80; }
void disable() { flags &= 0x7F; }

View File

@ -535,7 +535,7 @@ void Room::checkCursor() {
newCursor = CURSOR_TALK;
} else if (res.getTalkData()) {
newCursor = CURSOR_ARROW;
} else if (_cursorState == CS_UNKNOWN) {
} else if (_cursorState == CS_BUMPED) {
newCursor = CURSOR_CAMERA;
} else if (_cursorState == CS_TALKING) {
newCursor = CURSOR_ARROW;
@ -547,7 +547,7 @@ void Room::checkCursor() {
newCursor = CURSOR_MENUBAR;
} else if (_cursorState != CS_NONE) {
// Currently in a special mode
// checkRoomHotspots();
checkRoomHotspots();
newCursor = CURSOR_CAMERA;
} else {
// Check for a highlighted hotspot

View File

@ -51,7 +51,7 @@ public:
}
};
enum CursorState {CS_NONE, CS_ACTION, CS_SEQUENCE, CS_TALKING, CS_UNKNOWN};
enum CursorState {CS_NONE, CS_ACTION, CS_SEQUENCE, CS_TALKING, CS_BUMPED};
class Room {
private: