From 5fa3985bcbd96558d8adb0e306f1cfd0b10b2e03 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Mon, 29 May 2006 08:12:07 +0000 Subject: 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 --- engines/lure/game.cpp | 2 +- engines/lure/hotspots.cpp | 193 ++++++++++++++++++++++++++++++++++---------- engines/lure/hotspots.h | 35 +++++++- engines/lure/res_struct.cpp | 5 ++ engines/lure/res_struct.h | 9 ++- engines/lure/room.cpp | 4 +- engines/lure/room.h | 2 +- 7 files changed, 201 insertions(+), 49 deletions(-) diff --git a/engines/lure/game.cpp b/engines/lure/game.cpp index 8f5eaeb449..5c3fb365b1 100644 --- a/engines/lure/game.cpp +++ b/engines/lure/game.cpp @@ -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()) diff --git a/engines/lure/hotspots.cpp b/engines/lure/hotspots.cpp index 83ae3995cf..52ec0eb4f1 100644 --- a/engines/lure/hotspots.cpp +++ b/engines/lure/hotspots.cpp @@ -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 diff --git a/engines/lure/hotspots.h b/engines/lure/hotspots.h index 4f5649b3c8..8e18827092 100644 --- a/engines/lure/hotspots.h +++ b/engines/lure/hotspots.h @@ -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(); diff --git a/engines/lure/res_struct.cpp b/engines/lure/res_struct.cpp index 807aeebb24..7ecee018c1 100644 --- a/engines/lure/res_struct.cpp +++ b/engines/lure/res_struct.cpp @@ -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 diff --git a/engines/lure/res_struct.h b/engines/lure/res_struct.h index 0d02ef3f18..177b83f986 100644 --- a/engines/lure/res_struct.h +++ b/engines/lure/res_struct.h @@ -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; } diff --git a/engines/lure/room.cpp b/engines/lure/room.cpp index a543db4207..7d7d569a0c 100644 --- a/engines/lure/room.cpp +++ b/engines/lure/room.cpp @@ -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 diff --git a/engines/lure/room.h b/engines/lure/room.h index d8011d0300..3eb90a714a 100644 --- a/engines/lure/room.h +++ b/engines/lure/room.h @@ -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: -- cgit v1.2.3