diff options
author | Paul Gilbert | 2006-07-08 08:42:58 +0000 |
---|---|---|
committer | Paul Gilbert | 2006-07-08 08:42:58 +0000 |
commit | a20214e43a0fdaa99b6874f9ce132a9827f61023 (patch) | |
tree | 1e48a4b83f2e81075a96569f3400503e520e5686 | |
parent | 258a7b528fe85043507743a179d8edbf44b9a0af (diff) | |
download | scummvm-rg350-a20214e43a0fdaa99b6874f9ce132a9827f61023.tar.gz scummvm-rg350-a20214e43a0fdaa99b6874f9ce132a9827f61023.tar.bz2 scummvm-rg350-a20214e43a0fdaa99b6874f9ce132a9827f61023.zip |
Rewrote the talk handling code
Added lots of debugging information
Partial support for characters bumping into each other
Player/NPC action handling generally improved
Added hard-coding for variable width frame animation of Ratpouch being released
Basic support for Ratpouch wondering around
svn-id: r23416
-rw-r--r-- | engines/lure/hotspots.cpp | 1192 | ||||
-rw-r--r-- | engines/lure/hotspots.h | 121 |
2 files changed, 1054 insertions, 259 deletions
diff --git a/engines/lure/hotspots.cpp b/engines/lure/hotspots.cpp index 4639770b93..8a585a6f6c 100644 --- a/engines/lure/hotspots.cpp +++ b/engines/lure/hotspots.cpp @@ -50,6 +50,8 @@ Hotspot::Hotspot(HotspotData *res): _pathFinder(this) { _destX = res->startX; _destY = res->startY; _destHotspotId = 0; + _frameWidth = res->width; + _frameStartsUsed = false; _height = res->height; _width = res->width; _heightCopy = res->heightCopy; @@ -72,11 +74,10 @@ Hotspot::Hotspot(HotspotData *res): _pathFinder(this) { _frameCtr = 0; _skipFlag = false; _charRectY = 0; - _actionCtr = 0; + _voiceCtr = 0; _blockedOffset = 0; _exitCtr = 0; - _blockedState = BS_NONE; - _unknownFlag = false; + _walkFlag = true; if (_data->npcSchedule != 0) { CharacterScheduleEntry *entry = resources.charSchedules().getEntry(_data->npcSchedule); @@ -98,8 +99,8 @@ Hotspot::Hotspot(Hotspot *character, uint16 objType): _pathFinder(this) { _destHotspotId = character->hotspotId(); _blockedOffset = 0; _exitCtr = 0; - _blockedState = BS_NONE; - _unknownFlag = false; + _voiceCtr = 0; + _walkFlag = false; switch (objType) { case VOICE_ANIM_ID: @@ -114,17 +115,42 @@ Hotspot::Hotspot(Hotspot *character, uint16 objType): _pathFinder(this) { _width = 32; _heightCopy = character->height() + 14; _widthCopy = 24; - _layer = 2; - - _tickHandler = HotspotTickHandlers::getHandler(VOICE_TICK_PROC_ID); + _yCorrection = 1; + _tickCtr = 0; + _voiceCtr = 40; + + _tickHandler = HotspotTickHandlers::getHandler(VOICE_TICK_PROC_ID); + setAnimation(VOICE_ANIM_ID); + break; + + case PUZZLED_ANIM_ID: + case EXCLAMATION_ANIM_ID: + _roomNumber = character->roomNumber(); + _hotspotId = 0xfffe; + _width = 32; + _height = 18; + _widthCopy = 19; + _heightCopy = 18 + character->heightCopy(); + _layer = 1; + _persistant = false; + _yCorrection = 1; + _voiceCtr = CONVERSE_COUNTDOWN_SIZE; + _destHotspotId = character->hotspotId(); + _tickHandler = HotspotTickHandlers::getHandler(PUZZLED_TICK_PROC_ID); setAnimation(VOICE_ANIM_ID); + setFrameNumber(objType == PUZZLED_ANIM_ID ? 1 : 2); + + character->setFrameCtr(_voiceCtr); break; default: break; } + + _frameWidth = _width; + _frameStartsUsed = false; } Hotspot::~Hotspot() { @@ -142,6 +168,9 @@ void Hotspot::setAnimation(uint16 newAnimId) { void Hotspot::setAnimation(HotspotAnimData *newRecord) { Disk &r = Disk::getReference(); + uint16 tempWidth, tempHeight; + int16 xStart; + if (_frames) { delete _frames; _frames = NULL; @@ -157,16 +186,7 @@ void Hotspot::setAnimation(HotspotAnimData *newRecord) { uint16 *numEntries = (uint16 *) src->data(); uint16 *headerEntry = (uint16 *) (src->data() + 2); - - if ((*numEntries > 99) || (*numEntries == 0)) { - // Wobbly, likely something wrong with the resoure - _width = 1; - _numFrames = 1; - _frameNumber = 0; - _frames = new Surface(1, 1); - _frames->data().setBytes(_colourOffset, 0, 1); - return; - } + assert((*numEntries >= 1) && (*numEntries < 100)); // Calculate total needed size for output and create memory block to hold it uint32 totalSize = 0; @@ -182,35 +202,77 @@ void Hotspot::setAnimation(HotspotAnimData *newRecord) { _numFrames = *numEntries; _frameNumber = 0; - _frames = new Surface(_width * _numFrames, _height); - + if (newRecord->animRecordId == SERF_ANIM_ID) { + _frameStartsUsed = true; + _frames = new Surface(416, 27); + } + else { + _frames = new Surface(_width * _numFrames, _height); + _frameStartsUsed = false; + } _frames->data().setBytes(_colourOffset, 0, _frames->data().size()); byte *pSrc = dest->data() + 0x40; byte *pDest; headerEntry = (uint16 *) (src->data() + 2); MemoryBlock &mDest = _frames->data(); + uint16 frameOffset = 0x40; + uint16 *offsetPtr = (uint16 *) src->data(); + + tempWidth = _width; + tempHeight = _height; for (uint16 frameNumCtr = 0; frameNumCtr < _numFrames; ++frameNumCtr, ++headerEntry) { if ((newRecord->flags & PIXELFLAG_HAS_TABLE) != 0) { - // For animations with an offset table, set the source point for each frame - uint16 frameOffset = *((uint16 *) (src->data() + ((frameNumCtr + 1) * sizeof(uint16)))) + 0x40; - if ((uint32) frameOffset + _height * (_width / 2) > dest->size()) - error("Invalid frame offset in animation %x", newRecord->animRecordId); + // For animations with an offset table, set the source pointer pSrc = dest->data() + frameOffset; } + + if (newRecord->animRecordId == SERF_ANIM_ID) { + // Save the start of each frame for serf, since the size varies + xStart = (frameNumCtr == 0) ? 0 : _frameStarts[frameNumCtr - 1] + tempWidth; + _frameStarts[frameNumCtr] = xStart; + + // Switch statement to handle varying size for different frames + switch (frameNumCtr) { + case 3: + tempWidth = 48; + tempHeight = 25; + break; + case 4: + tempHeight = 26; + break; + case 5: + tempWidth = 32; + break; + case 6: + tempHeight = 27; + break; + case 7: + tempWidth = 16; + break; + default: + break; + } + } else { + // Set the X Start based on the frame size + xStart = frameNumCtr * _width; + } // Copy over the frame, applying the colour offset to each nibble - for (uint16 yPos = 0; yPos < _height; ++yPos) { - pDest = mDest.data() + (yPos * _numFrames + frameNumCtr) * _width; + for (uint16 yPos = 0; yPos < tempHeight; ++yPos) { + pDest = mDest.data() + yPos * _frames->width() + xStart; - for (uint16 ctr = 0; ctr < _width / 2; ++ctr) { + for (uint16 xPos = 0; xPos < tempWidth / 2; ++xPos) { *pDest++ = _colourOffset + (*pSrc >> 4); *pDest++ = _colourOffset + (*pSrc & 0xf); ++pSrc; } } + + if ((newRecord->flags & PIXELFLAG_HAS_TABLE) != 0) + frameOffset += (*++offsetPtr >> 1); } delete src; @@ -220,11 +282,17 @@ void Hotspot::setAnimation(HotspotAnimData *newRecord) { void Hotspot::copyTo(Surface *dest) { int16 xPos = _startX; int16 yPos = _startY; - uint16 hWidth = _width; + uint16 hWidth = _frameWidth; uint16 hHeight = _height; - Rect r(_frameNumber * hWidth, 0, (_frameNumber + 1) * hWidth - 1, - hHeight - 1); + Rect r(_frameNumber * hWidth, 0, (_frameNumber + 1) * hWidth - 1, hHeight - 1); + if (_frameStartsUsed) { + assert(_frameNumber < MAX_NUM_FRAMES); + r.left = _frameStarts[_frameNumber]; + r.right = (_frameNumber == _numFrames - 1) ? _frames->width() - 1 : + _frameStarts[_frameNumber + 1] - 1; + r.bottom = _height - 1; + } // Handle clipping for X position if (xPos < 0) { @@ -302,7 +370,9 @@ bool Hotspot::executeScript() { } void Hotspot::tick() { + debugC(ERROR_BASIC, kLureDebugAnimations, "Hotspot %xh tick begin", _hotspotId); _tickHandler(*this); + debugC(ERROR_BASIC, kLureDebugAnimations, "Hotspot %xh tick end", _hotspotId); } void Hotspot::setTickProc(uint16 newVal) { @@ -325,45 +395,52 @@ void Hotspot::walkTo(int16 endPosX, int16 endPosY, uint16 destHotspot) { } void Hotspot::stopWalking() { - // TODO: voiceCtr = 0 - _actionCtr = 0; + _voiceCtr = 0; + setActionCtr(0); _currentActions.clear(); Room::getReference().setCursorState(CS_NONE); } void Hotspot::endAction() { - // TODO: voiceCtr = 0 - _actionCtr = 0; + Room &room = Room::getReference(); + + _voiceCtr = 0; + setActionCtr(0); if (_hotspotId == PLAYER_ID) - Room::getReference().setCursorState(CS_NONE); + //Room::getReference().setCursorState(CS_NONE); **DEBUG** + room.setCursorState((CursorState) ((int) room.cursorState() & 2)); if (_currentActions.top().hasSupportData()) _currentActions.top().setSupportData(_currentActions.top().supportData().next()); } void Hotspot::setDirection(Direction dir) { - _direction = dir; + if (_numFrames == 0) return; + uint8 newFrameNumber = 0; switch (dir) { case UP: - setFrameNumber(_anim->upFrame); + newFrameNumber = _anim->upFrame; _charRectY = 4; break; case DOWN: - setFrameNumber(_anim->downFrame); + newFrameNumber = _anim->downFrame; _charRectY = 4; break; case LEFT: - setFrameNumber(_anim->leftFrame); + newFrameNumber = _anim->leftFrame; _charRectY = 0; break; case RIGHT: - setFrameNumber(_anim->rightFrame); + newFrameNumber = _anim->rightFrame; _charRectY = 0; break; default: break; } + + setFrameNumber(newFrameNumber); + _direction = dir; } // Makes the character face the given hotspot @@ -393,13 +470,19 @@ void Hotspot::faceHotspot(HotspotData *hotspot) { } } +void Hotspot::faceHotspot(uint16 id) { + HotspotData *hotspot = Resources::getReference().getHotspot(id); + assert(hotspot != NULL); + faceHotspot(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; + Common::RandomSource rnd; int tryCtr = 0; int16 xp, yp; @@ -407,11 +490,13 @@ void Hotspot::setRandomDest() { _currentActions.addFront(START_WALKING, roomNumber()); else _currentActions.top().setAction(START_WALKING); - + _walkFlag = true; + while (tryCtr ++ <= 20) { - xp = rect.left + _rnd.getRandomNumber(rect.right - rect.left); - yp = rect.left + _rnd.getRandomNumber(rect.right - rect.left); + xp = rect.left + rnd.getRandomNumber(rect.right - rect.left); + yp = rect.left + rnd.getRandomNumber(rect.right - rect.left); setDestPosition(xp, yp); + setDestHotspot(0); if (!roomData->paths.isOccupied(xp, yp) && !roomData->paths.isOccupied(xp, yp)) break; @@ -421,6 +506,9 @@ void Hotspot::setRandomDest() { // Sets or clears the hotspot as occupying an area in its room's pathfinding data void Hotspot::setOccupied(bool occupiedFlag) { + if (occupiedFlag == coveredFlag()) return; + setCoveredFlag(occupiedFlag); + int xp = x() >> 3; int yp = (y() - 8 + heightCopy() - 4) >> 3; int widthVal = MAX((widthCopy() >> 3), 1); @@ -517,6 +605,88 @@ void Hotspot::resetPosition() { setDirection(direction()); } +void Hotspot::converse(uint16 destHotspot, uint16 messageId, bool standStill) { + assert(_data); + _data->talkDestHotspot = destHotspot; + _data->talkMessageId = messageId; + _data->talkCountdown = CONVERSE_COUNTDOWN_SIZE; + + if ((destHotspot != 0) && (destHotspot != NOONE_ID)) { + // Talking to a destination - add in any talk countdown from the destination, + // in case the destination is already in process of talking + HotspotData *hotspot = Resources::getReference().getHotspot(destHotspot); + _data->talkCountdown += hotspot->talkCountdown; + } + + if (standStill) { + setDelayCtr(_data->talkCountdown); + _data->characterMode = CHARMODE_CONVERSING; + //TODO: HS[3Eh]=use_hotspot_id, HS[40h]=active_hotspot_id + } +} + +void Hotspot::handleTalkDialog() { + assert(_data); + Resources &res = Resources::getReference(); + ValueTableData &fields = res.fieldList(); + Room &room = Room::getReference(); + + // Return if no talk dialog is necessary + if (_data->talkCountdown == 0) return; + debugC(ERROR_DETAILED, kLureDebugAnimations, "Talk countdown = %d", _data->talkCountdown); + + if (_data->talkCountdown == CONVERSE_COUNTDOWN_SIZE) { + // Time to set up the dialog for the character + --_data->talkCountdown; + debugC(ERROR_DETAILED, kLureDebugAnimations, "Talk dialog opening"); + startTalkDialog(); + + if ((_data->talkDestHotspot != NOONE_ID) && (_data->talkDestHotspot != 0) && + (_hotspotId < FIRST_NONCHARACTER_ID)) { + // Speaking to a hotspot + fields.setField(ACTIVE_HOTSPOT_ID, _data->talkDestHotspot); + + // Face the character to the hotspot + HotspotData *destHotspot = res.getHotspot(_data->talkDestHotspot); + assert(destHotspot != NULL); + faceHotspot(destHotspot); + + // If the hotspot is also a character, then face it to the speaker + if (_data->talkDestHotspot < FIRST_NONCHARACTER_ID) { + Hotspot *charHotspot = res.getActiveHotspot(_data->talkDestHotspot); + if (charHotspot != NULL) + charHotspot->faceHotspot(resource()); + } + } + + } else if ((fields.flags() & GAMEFLAG_FAST_TEXTSPEED) != 0) { + // Fast text speed + --_data->talkCountdown; + } else if ((fields.flags() & (GAMEFLAG_8 | GAMEFLAG_4)) != 0) { + fields.flags() |= GAMEFLAG_4; + --_data->talkCountdown; + } else { + --_data->talkCountdown; + fields.flags() -= GAMEFLAG_4; + } + + if (_data->talkCountdown == 0) { + // Talking is finish - stop talking and free voice animation + debugC(ERROR_DETAILED, kLureDebugAnimations, "Talk dialog close"); + room.setTalkDialog(0, 0); + res.setTalkingCharacter(0); + } + + debugC(ERROR_DETAILED, kLureDebugAnimations, "Talk handler method end"); +} + +void Hotspot::startTalkDialog() { + Room &room = Room::getReference(); + if (room.roomNumber() != roomNumber()) return; + + room.setTalkDialog(hotspotId(), _data->talkMessageId); +} + /*-------------------------------------------------------------------------*/ /* Hotspot action handling */ /* */ @@ -533,65 +703,71 @@ bool Hotspot::isRoomExit(uint16 id) { } HotspotPrecheckResult Hotspot::actionPrecheck(HotspotData *hotspot) { + Resources &res = Resources::getReference(); + ValueTableData &fields = res.fieldList(); + if ((hotspot->hotspotId == 0x420) || (hotspot->hotspotId == 0x436) || (hotspot->hotspotId == 0x429)) { - // TODO: figure out specific handling code - actionPrecheck3(hotspot); - return PC_EXECUTE; - } else { - return actionPrecheck2(hotspot); - } -} - -HotspotPrecheckResult Hotspot::actionPrecheck2(HotspotData *hotspot) { - ValueTableData fields = Resources::getReference().fieldList(); - - if (hotspot->roomNumber != roomNumber()) { - // Hotspot isn't in same room as character - if (_actionCtr == 0) + // TODO: figure out specific handling code - sub_213 + if (0) + return PC_INITIAL; + } else if (hotspot->roomNumber != roomNumber()) { + // loc_884 + if (actionCtr() == 0) Dialog::showMessage(0, hotspotId()); - _actionCtr = 0; + setActionCtr(0); return PC_NOT_IN_ROOM; - } else if (_actionCtr != 0) { + } else if (actionCtr() != 0) { // loc_883 - ++_actionCtr; - - if (_actionCtr >= 6) { + setActionCtr(actionCtr() + 1); + if (actionCtr() >= 6) { warning("actionCtr exceeded"); - _actionCtr = 0; + setActionCtr(0); Dialog::showMessage(0xD, hotspotId()); return PC_EXCESS; - } - - if (hotspot->hotspotId < FIRST_NONCHARACTER_ID) { - // TODO: Does other checks on HS[44] -> loc_886 - _actionCtr = 0; + } + + if ((hotspot->hotspotId >= FIRST_NONCHARACTER_ID) || + (hotspot->characterMode == CHARMODE_8) || + (hotspot->characterMode == CHARMODE_4) || + (hotspot->characterMode == CHARMODE_7)) { + // loc_880 + if (characterWalkingCheck(hotspot)) + return PC_INITIAL; + } else { + // loc_886 + setActionCtr(0); Dialog::showMessage(0xE, hotspotId()); return PC_UNKNOWN; } } else { - _actionCtr = 1; - - if (hotspot->hotspotId < FIRST_NONCHARACTER_ID) { - // TODO: Extra Checks - Dialog::showMessage(5, hotspotId()); - return PC_INITIAL; - } - } - - if (characterWalkingCheck(hotspot)) { - return PC_INITIAL; - } else { - actionPrecheck3(hotspot); - return PC_EXECUTE; + setActionCtr(1); + if ((hotspot->hotspotId >= FIRST_NONCHARACTER_ID) || + ((hotspot->v50 != _hotspotId) && (hotspot->characterMode == CHARMODE_4))) { + // loc_880 + if (characterWalkingCheck(hotspot)) + return PC_INITIAL; + } else if (hotspot->v50 != _hotspotId) { + if (fields.getField(88) == 2) { + // loc_882 + hotspot->v2b = 0x2A; + hotspot->useHotspotId = _hotspotId; + return PC_INITIAL; + } else { + Dialog::showMessage(5, hotspotId()); + setDelayCtr(4); + } + } } -} -void Hotspot::actionPrecheck3(HotspotData *hotspot) { - _actionCtr = 0; + // loc_888 + setActionCtr(0); if (hotspot->hotspotId < FIRST_NONCHARACTER_ID) { - // TODO: HS[44]=8, HS[42]=1E, HS[50]=ID + hotspot->characterMode = CHARMODE_8; + hotspot->delayCtr = 30; + hotspot->v50 = hotspot->hotspotId; } + return PC_EXECUTE; } bool Hotspot::characterWalkingCheck(HotspotData *hotspot) { @@ -600,14 +776,18 @@ bool Hotspot::characterWalkingCheck(HotspotData *hotspot) { if (hotspot == NULL) { // DEBUG for now - hardcoded value for 3E7h (NULL) xp = 78; yp = 162; + _walkFlag = true; } else if ((hotspot->walkX == 0) && (hotspot->walkY == 0)) { // The hotspot doesn't have any walk co-ordinates xp = hotspot->startX; yp = hotspot->startY + hotspot->heightCopy - 4; + _walkFlag = false; } else { xp = hotspot->walkX; yp = hotspot->walkY & 0x7fff; + _walkFlag = true; + if ((hotspot->walkY & 0x8000) != 0) { // Special handling for walking // if (((xp >> 3) != (x() >> 3)) || @@ -732,14 +912,14 @@ void Hotspot::doAction(Action action, HotspotData *hotspot) { &Hotspot::npcSetRoomAndBlockedOffset, &Hotspot::npcUnknown1, &Hotspot::npcExecScript, - &Hotspot::npcUnknown2, + &Hotspot::npcResetPausedList, &Hotspot::npcSetRandomDest, &Hotspot::npcWalkingCheck, &Hotspot::npcSetSupportOffset, &Hotspot::npcSupportOffsetConditional, &Hotspot::npcDispatchAction, &Hotspot::npcUnknown3, - &Hotspot::npcUnknown4, + &Hotspot::npcPause, &Hotspot::npcStartTalking, &Hotspot::npcJumpAddress}; @@ -804,7 +984,7 @@ void Hotspot::doOperate(HotspotData *hotspot) { return; } - _actionCtr = 0; + setActionCtr(0); faceHotspot(hotspot); endAction(); @@ -840,7 +1020,7 @@ void Hotspot::doOpen(HotspotData *hotspot) { } faceHotspot(hotspot); - _actionCtr = 0; + setActionCtr(0); endAction(); uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, OPEN); @@ -894,13 +1074,14 @@ void Hotspot::doClose(HotspotData *hotspot) { } faceHotspot(hotspot); - _actionCtr = 0; + setActionCtr(0); endAction(); uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, CLOSE); if (sequenceOffset >= 0x8000) { // Message to display Dialog::showMessage(sequenceOffset, hotspotId()); + return; } else if (sequenceOffset != 0) { // Otherwise handle script sequenceOffset = Script::execute(sequenceOffset); @@ -965,6 +1146,8 @@ void Hotspot::doUse(HotspotData *hotspot) { } } +uint16 giveTalkIds[6] = {0xCF5E, 0xCF14, 0xCF90, 0xCFAA, 0xCFD0, 0xCFF6}; + void Hotspot::doGive(HotspotData *hotspot) { Resources &res = Resources::getReference(); uint16 usedId = _currentActions.top().supportData().param(1); @@ -990,7 +1173,7 @@ void Hotspot::doGive(HotspotData *hotspot) { faceHotspot(hotspot); endAction(); - if ((hotspotId() != 0x412) || (usedId != 0x2710)) + if ((hotspot->hotspotId != PRISONER_ID) || (usedId != BOTTLE_HOTSPOT_ID)) Dialog::showMessage(7, hotspotId()); uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, GIVE); @@ -1000,7 +1183,11 @@ void Hotspot::doGive(HotspotData *hotspot) { } else if (sequenceOffset != 0) { sequenceOffset = Script::execute(sequenceOffset); if (sequenceOffset == NOONE_ID) { - // TODO + // Start a conversation based on the index of field #6 + uint16 index = fields.getField(GIVE_TALK_INDEX); + assert(index < 6); + startTalk(hotspot, giveTalkIds[index]); + } else if (sequenceOffset == 0) { // Move item into character's inventory HotspotData *usedItem = res.getHotspot(usedId); @@ -1013,10 +1200,9 @@ void Hotspot::doGive(HotspotData *hotspot) { void Hotspot::doTalkTo(HotspotData *hotspot) { Resources &res = Resources::getReference(); - uint16 usedId = _currentActions.top().supportData().param(1); ValueTableData &fields = res.fieldList(); fields.setField(ACTIVE_HOTSPOT_ID, hotspot->hotspotId); - fields.setField(USE_HOTSPOT_ID, usedId); + fields.setField(USE_HOTSPOT_ID, hotspot->hotspotId); if ((hotspot->hotspotId != SKORL_ID) && ((hotspot->roomNumber != 28) || (hotspot->hotspotId != 0x3EB))) { @@ -1033,7 +1219,6 @@ void Hotspot::doTalkTo(HotspotData *hotspot) { endAction(); uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, TALK_TO); - if (sequenceOffset >= 0x8000) { Dialog::showMessage(sequenceOffset, hotspotId()); return; @@ -1049,7 +1234,7 @@ void Hotspot::doTalkTo(HotspotData *hotspot) { } // Start talking with character - startTalk(hotspot); + startTalk(hotspot, getTalkId(hotspot)); } void Hotspot::doTell(HotspotData *hotspot) { @@ -1085,7 +1270,7 @@ void Hotspot::doLookAt(HotspotData *hotspot) { } faceHotspot(hotspot); - _actionCtr = 0; + setActionCtr(0); endAction(); if (sequenceOffset >= 0x8000) { @@ -1122,7 +1307,7 @@ void Hotspot::doLookThrough(HotspotData *hotspot) { } faceHotspot(hotspot); - _actionCtr = 0; + setActionCtr(0); endAction(); if (sequenceOffset >= 0x8000) { @@ -1148,13 +1333,14 @@ void Hotspot::doDrink(HotspotData *hotspot) { fields.setField(ACTIVE_HOTSPOT_ID, hotspot->hotspotId); fields.setField(USE_HOTSPOT_ID, hotspot->hotspotId); + endAction(); + // Make sure item is in character's inventory - if (hotspot->hotspotId != hotspotId()) { + if (hotspot->roomNumber != hotspotId()) { Dialog::showMessage(0xF, hotspotId()); return; } - endAction(); uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, DRINK); if (sequenceOffset >= 0x8000) { @@ -1268,7 +1454,7 @@ void Hotspot::doBribe(HotspotData *hotspot) { // TODO: call to talk_setup faceHotspot(hotspot); - _actionCtr = 0; + setActionCtr(0); endAction(); sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, BRIBE); @@ -1338,7 +1524,8 @@ void Hotspot::npcSetRoomAndBlockedOffset(HotspotData *hotspot) { } void Hotspot::npcUnknown1(HotspotData *hotspot) { - error("Not yet implemented"); + warning("Not yet implemented"); + endAction(); } void Hotspot::npcExecScript(HotspotData *hotspot) { @@ -1348,8 +1535,12 @@ void Hotspot::npcExecScript(HotspotData *hotspot) { Script::execute(offset); } -void Hotspot::npcUnknown2(HotspotData *hotspot) { - warning("npcUnknown2 - Not yet implemented"); +void Hotspot::npcResetPausedList(HotspotData *hotspot) { + Resources &res = Resources::getReference(); + setCharacterMode(CHARMODE_HESITATE); + setDelayCtr(16); + + res.pausedList().reset(hotspotId()); endAction(); } @@ -1430,46 +1621,86 @@ void Hotspot::npcUnknown3(HotspotData *hotspot) { endAction(); } -void Hotspot::npcUnknown4(HotspotData *hotspot) { - warning("npcUnknown4: Not yet implemented"); +void Hotspot::npcPause(HotspotData *hotspot) { + uint16 delayAmount = _currentActions.top().supportData().param(0); endAction(); + + setCharacterMode(CHARMODE_PAUSED); + setDelayCtr(delayAmount); } void Hotspot::npcStartTalking(HotspotData *hotspot) { - warning("npcStartTalking: Not yet implemented"); + CharacterScheduleEntry &entry = _currentActions.top().supportData(); + uint16 stringId = entry.param(0); + uint16 destHotspot = entry.param(1); + + converse(destHotspot, stringId, false); endAction(); } void Hotspot::npcJumpAddress(HotspotData *hotspot) { - warning("npcJumpAddress: Not yet implemented"); + Resources &res = Resources::getReference(); + ValueTableData &fields = res.fieldList(); + int procIndex = _currentActions.top().supportData().param(0); + HotspotData *player; + CharacterScheduleEntry *newEntry; endAction(); + + switch (procIndex) { + case 0: + if (fields.getField(OLD_ROOM_NUMBER) == 19) { + fields.setField(TALK_INDEX, 24); + res.getHotspot(0x3F1)->nameId = 0x154; + Dialog::show(0xAB9); + } + break; + + case 1: + player = res.getHotspot(PLAYER_ID); + newEntry = res.charSchedules().getEntry(JUMP_ADDR_2_SUPPORT_ID, NULL); + _currentActions.top().setSupportData(newEntry); + break; + + default: + error("Hotspot::npcJumpAddress - invalid method index %d", procIndex); + break; + } } /*------------------------------------------------------------------------*/ -void Hotspot::startTalk(HotspotData *charHotspot) { +uint16 Hotspot::getTalkId(HotspotData *charHotspot) { Resources &res = Resources::getReference(); uint16 talkIndex; - setTickProc(TALK_TICK_PROC_ID); // Set for providing talk listing - // Get offset of talk set to use TalkHeaderData *headerEntry = res.getTalkHeader(charHotspot->hotspotId); - uint16 talkOffset; // Calculate talk index to use if (charHotspot->nameId == STRANGER_ID) talkIndex = 0; else talkIndex = res.fieldList().getField(TALK_INDEX) + 1; - talkOffset = headerEntry->getEntry(talkIndex); + + return headerEntry->getEntry(talkIndex); +} + +void Hotspot::startTalk(HotspotData *charHotspot, uint16 id) { + Resources &res = Resources::getReference(); + + // Set for providing talk listing + setTickProc(TALK_TICK_PROC_ID); + + // Signal the character that they're being talked to + charHotspot->useHotspotId = _hotspotId; + setUseHotspotId(charHotspot->hotspotId); // Set the active talk data res.setTalkStartEntry(0); - res.setTalkData(talkOffset); + res.setTalkData(id); if (!res.getTalkData()) - error("Talk failed - invalid offset: Character=%xh, index=%d, offset=%xh", - charHotspot->hotspotId, talkIndex, talkOffset); + error("Talk failed - invalid offset: Character=%xh, offset=%xh", + charHotspot->hotspotId, id); } /*------------------------------------------------------------------------*/ @@ -1478,22 +1709,32 @@ HandlerMethodPtr HotspotTickHandlers::getHandler(uint16 procOffset) { switch (procOffset) { case 0x4F82: return standardCharacterAnimHandler; + case VOICE_TICK_PROC_ID: + return voiceBubbleAnimHandler; + case PUZZLED_TICK_PROC_ID: + return puzzledAnimHandler; case 0x7F3A: return standardAnimHandler; case 0x7207: return roomExitAnimHandler; case PLAYER_TICK_PROC_ID: return playerAnimHandler; + case 0x7C14: + return followerAnimHandler; case 0x7EFA: return skorlAnimHandler; case 0x7F69: return droppingTorchAnimHandler; case 0x8009: return fireAnimHandler; + case 0x81B3: + return prisonerAnimHandler; + case 0x8241: + return headAnimHandler; + case 0x882A: + return rackSerfAnimHandler; case TALK_TICK_PROC_ID: return talkAnimHandler; - case 0x8241: - return headAnimationHandler; default: return defaultHandler; } @@ -1512,6 +1753,7 @@ void HotspotTickHandlers::standardAnimHandler(Hotspot &h) { void HotspotTickHandlers::standardCharacterAnimHandler(Hotspot &h) { Resources &res = Resources::getReference(); + ValueTableData &fields = res.fieldList(); RoomPathsData &paths = Resources::getReference().getRoom(h.roomNumber())->paths; PathFinder &pathFinder = h.pathFinder(); CurrentActionStack &actions = h.currentActions(); @@ -1519,20 +1761,49 @@ void HotspotTickHandlers::standardCharacterAnimHandler(Hotspot &h) { int numImpinging; bool bumpedPlayer; - // TODO: handle talk dialogs countdown if necessary + if (h.currentActions().action() != WALKING) { + char buffer[MAX_DESC_SIZE]; + h.currentActions().list(buffer); + debugC(ERROR_DETAILED, kLureDebugAnimations, "Hotspot standard character p=(%d,%d,%d) bs=%d\n%s", + h.x(), h.y(), h.roomNumber(), h.blockedState(), buffer); + } + + // Handle any active talk dialog + h.handleTalkDialog(); + + // Handle any active hotspot the character is using (for example, if the player is + // talking to a character, this stops them from moving for the duration) + if (h.useHotspotId() != 0) { + debugC(ERROR_DETAILED, kLureDebugAnimations, "Use Hotspot Id = %xh, v2b = %d", + h.useHotspotId(), h.v2b()); + if (h.v2b() == 0x2A) { + fields.setField(ACTIVE_HOTSPOT_ID, h.v2b()); + fields.setField(USE_HOTSPOT_ID, h.useHotspotId()); + Script::execute(h.script()); + h.setUseHotspotId(0); + } else { + h.updateMovement(); + return; + } + } // If a frame countdown is in progress, then decrement and exit if (h.frameCtr() > 0) { + debugC(ERROR_DETAILED, kLureDebugAnimations, "Frame ctr = %d", h.frameCtr()); h.decrFrameCtr(); return; } + debugC(ERROR_DETAILED, kLureDebugAnimations, "Hotspot standard character point 2"); numImpinging = Support::findIntersectingCharacters(h, impingingList); bumpedPlayer = (numImpinging == 0) ? false : Support::isCharacterInList(impingingList, numImpinging, PLAYER_ID); // Check for character having just changed room + debugC(ERROR_DETAILED, kLureDebugAnimations, "Hotspot standard character point 3"); if (h.skipFlag()) { + debugC(ERROR_DETAILED, kLureDebugAnimations, "Skip flag was set"); + if (numImpinging > 0) { // Scan to check if the character has bumped into player Hotspot *player = res.getActiveHotspot(PLAYER_ID); @@ -1541,14 +1812,17 @@ void HotspotTickHandlers::standardCharacterAnimHandler(Hotspot &h) { // Signal the player to move out of the way automatically player->setBlockedState(BS_INITIAL); player->setDestHotspot(0); + player->setRandomDest(); Room::getReference().setCursorState(CS_BUMPED); - player->setRandomDest(); + debugC(ERROR_DETAILED, kLureDebugAnimations, "Player bumped code starting"); + } 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); + debugC(ERROR_DETAILED, kLureDebugAnimations, "Pausing after bumping into character"); } return; } @@ -1559,7 +1833,28 @@ void HotspotTickHandlers::standardCharacterAnimHandler(Hotspot &h) { // TODO: Handling of any set Tick Script Offset, as well as certain other // as of yet unknown hotspot flags + debugC(ERROR_DETAILED, kLureDebugAnimations, "Hotspot standard character point 4"); + if (h.pauseCtr() != 0) { + debugC(ERROR_DETAILED, kLureDebugAnimations, "pause ctr = %d", h.pauseCtr()); + h.updateMovement(); + h.pathFinder().clear(); + if (h.pauseCtr() > 1) { + res.pausedList().scan(h); + return; + } else { + h.setPauseCtr(0); + if (h.characterMode() == CHARMODE_NONE) { + h.pathFinder().clear(); + return; + } + } + } + + debugC(ERROR_DETAILED, kLureDebugAnimations, "Hotspot standard character point 5"); if (h.characterMode() != CHARMODE_NONE) { + debugC(ERROR_DETAILED, kLureDebugAnimations, "char mode = %d, delay ctr = %d", + h.characterMode(), h.delayCtr()); + if (h.characterMode() == CHARMODE_6) { // TODO: Figure out what mode 6 is h.updateMovement(); @@ -1596,6 +1891,7 @@ void HotspotTickHandlers::standardCharacterAnimHandler(Hotspot &h) { return; } + debugC(ERROR_DETAILED, kLureDebugAnimations, "Hotspot standard character point 6"); CurrentAction action = actions.action(); switch (action) { @@ -1605,6 +1901,8 @@ void HotspotTickHandlers::standardCharacterAnimHandler(Hotspot &h) { case DISPATCH_ACTION: // Dispatch an action + debugC(ERROR_DETAILED, kLureDebugAnimations, "Hotspot standard character dispatch action"); + if (actions.top().roomNumber() == 0) actions.top().setRoomNumber(h.roomNumber()); if (actions.top().roomNumber() == h.roomNumber()) { @@ -1614,11 +1912,13 @@ void HotspotTickHandlers::standardCharacterAnimHandler(Hotspot &h) { } else { // NPC in wrong room for action npcRoomChange(h); + debugC(ERROR_DETAILED, kLureDebugAnimations, "Hotspot standard character change room request"); } break; case EXEC_HOTSPOT_SCRIPT: // A hotspot script is in progress for the player, so don't interrupt + debugC(ERROR_DETAILED, kLureDebugAnimations, "Hotspot standard character exec hotspot script"); if (h.executeScript()) { // Script is finished actions.top().setAction(DISPATCH_ACTION); @@ -1628,6 +1928,9 @@ void HotspotTickHandlers::standardCharacterAnimHandler(Hotspot &h) { case START_WALKING: // Start the player walking to the given destination + debugC(ERROR_DETAILED, kLureDebugAnimations, + "Hotspot standard character exec start walking => (%d,%d)", + h.destX(), h.destY()); h.setOccupied(false); pathFinder.reset(paths); h.currentActions().top().setAction(PROCESSING_PATH); @@ -1635,17 +1938,75 @@ void HotspotTickHandlers::standardCharacterAnimHandler(Hotspot &h) { // Deliberate fall through to processing walking path case PROCESSING_PATH: - // TODO: Call to sub_105 + // Handle processing pathfinding + debugC(ERROR_DETAILED, kLureDebugAnimations, "Hotspot standard character processing path"); + res.pausedList().scan(h); + if (!pathFinder.process()) break; - // Pathfinding is now complete - actions.top().setAction(WALKING); + debugC(ERROR_DETAILED, kLureDebugAnimations, + "pathFinder done: result = %d", pathFinder.result()); - // TODO: Lots of checks to unknown hotspot fields - break; + // Post-processing checks + if ((pathFinder.result() == PF_OK) || + ((h.destHotspotId() == 0) && (pathFinder.result() == PF_DEST_OCCUPIED))) { + // Standard processing + debugC(ERROR_DETAILED, kLureDebugAnimations, "Standard result handling"); + + h.setBlockedState(BS_NONE); + if (h.pathFinder().isEmpty()) { + // No path was defined + h.currentActions().top().setAction(DISPATCH_ACTION); + return; + } + h.currentActions().top().setAction(WALKING); + h.setPosition(h.x(), h.y() & 0xfff8); + } else if (h.blockedState() == BS_FINAL) { + debugC(ERROR_DETAILED, kLureDebugAnimations, "BS_FINAL result handling"); + + res.pausedList().reset(h.hotspotId()); + h.updateMovement(); + assert(!h.currentActions().isEmpty()); + h.currentActions().pop(); + + // TODO: HS[4Dh] = 0 + h.setBlockedState(BS_NONE); + h.setCharacterMode(CHARMODE_PAUSED); + h.setDelayCtr(2); + + assert(!h.currentActions().isEmpty()); + if (h.roomNumber() != h.currentActions().top().roomNumber()) + h.setDestHotspot(0xffff); + + if (bumpedPlayer) + h.setCharacterMode(CHARMODE_6); + } else { + debugC(ERROR_DETAILED, kLureDebugAnimations, "Non-standard result handling"); + CharacterScheduleEntry *newEntry = res.charSchedules().getEntry(RETURN_SUPPORT_ID); + assert(newEntry); + + h.setBlockedState((BlockedState) ((int) h.blockedState() + 1)); + + // TODO: Figure out usage of HS[4Dh] == 0 + if (1) + h.currentActions().addFront(DISPATCH_ACTION, h.roomNumber()); + + CurrentActionEntry &entry = h.currentActions().top(); + entry.setAction(DISPATCH_ACTION); + entry.setSupportData(newEntry); + entry.setRoomNumber(h.roomNumber()); + } + + // If the top action is now walking, deliberately fall through to the case entry; + // otherwise break out to exit method + if (h.currentActions().isEmpty() || h.currentActions().top().action() != WALKING) + break; case WALKING: // The character is currently moving + debugC(ERROR_DETAILED, kLureDebugAnimations, "Hotspot standard character walking"); + h.setOccupied(false); + // If the character is walking to an exit hotspot, make sure it's still open if ((h.destHotspotId() != 0) && (h.destHotspotId() != 0xffff)) { // Player is walking to a room exit hotspot @@ -1658,23 +2019,72 @@ void HotspotTickHandlers::standardCharacterAnimHandler(Hotspot &h) { } } - // TODO: Call to sub_41, exit if returns true - - h.setOccupied(false); - bool result = h.walkingStep(); - - if (result) - // Walking done - h.currentActions().top().setAction(DISPATCH_ACTION); - - if (h.destHotspotId() != 0) { - // Walking to an exit, check for any required room change - Support::checkRoomChange(h); + if (res.pausedList().check(h.hotspotId(), numImpinging, impingingList) == 0) { + if (h.walkingStep()) + // Walking done + h.currentActions().top().setAction(DISPATCH_ACTION); + + if (h.destHotspotId() != 0) + // Walking to an exit, check for any required room change + Support::checkRoomChange(h); } h.setOccupied(true); break; } + debugC(ERROR_DETAILED, kLureDebugAnimations, "Hotspot standard character point 7"); +} + +void HotspotTickHandlers::voiceBubbleAnimHandler(Hotspot &h) { + Resources &res = Resources::getReference(); + debugC(ERROR_DETAILED, kLureDebugAnimations, + "Voice Bubble anim handler: char = %xh, ctr = %d, char speaking ctr = %d", + h.hotspotId(), h.voiceCtr(), + res.getActiveHotspot(res.getTalkingCharacter())->resource()->talkCountdown); + + if (h.voiceCtr() != 0) + h.setVoiceCtr(h.voiceCtr() - 1); + + if (h.voiceCtr() != 0) { + // Countdown not yet ended + Hotspot *charHotspot = res.getActiveHotspot(res.getTalkingCharacter()); + if (charHotspot->roomNumber() == h.roomNumber()) { + // Character is still in the same room as when it began speaking + if (charHotspot->resource()->talkCountdown != 0) { + // Character still talking + if (!res.checkHotspotExtent(charHotspot->resource())) { + // Set voice bubble off screen to hide it + h.setPosition(h.x(), -100); + } else { + // Keep voice bubble in track with character + h.setPosition(charHotspot->x() + charHotspot->talkX() + 12, + charHotspot->y() + charHotspot->talkY() - 18); + } + return; + } + } + } + + // End of voice time, so unload + res.deactivateHotspot(&h); + return; +} + +void HotspotTickHandlers::puzzledAnimHandler(Hotspot &h) { + Resources &res = Resources::getReference(); + HotspotData *charHotspot = res.getHotspot(h.destHotspotId()); + assert(charHotspot); + + h.setVoiceCtr(h.voiceCtr() - 1); + if ((charHotspot->roomNumber != h.roomNumber()) || (h.voiceCtr() == 0) || + !res.checkHotspotExtent(charHotspot)) { + // Remove the animation + res.deactivateHotspot(&h); + return; + } + + h.setPosition(charHotspot->startX + charHotspot->talkX + 12, + charHotspot->startY + charHotspot->talkY - 20); } void HotspotTickHandlers::roomExitAnimHandler(Hotspot &h) { @@ -1713,28 +2123,81 @@ void HotspotTickHandlers::roomExitAnimHandler(Hotspot &h) { void HotspotTickHandlers::playerAnimHandler(Hotspot &h) { Resources &res = Resources::getReference(); + Room &room = Room::getReference(); Mouse &mouse = Mouse::getReference(); RoomPathsData &paths = Resources::getReference().getRoom(h.roomNumber())->paths; PathFinder &pathFinder = h.pathFinder(); CurrentActionStack &actions = h.currentActions(); + ValueTableData &fields = res.fieldList(); uint16 impingingList[MAX_NUM_IMPINGING]; int numImpinging; Action hsAction; uint16 hotspotId; HotspotData *hotspot; + char buffer[MAX_DESC_SIZE]; + + if (h.currentActions().action() != WALKING) { + h.currentActions().list(buffer); + debugC(ERROR_DETAILED, kLureDebugAnimations, + "Hotspot player anim handler p=(%d,%d,%d) bs=%d\n%s", + h.x(), h.y(), h.roomNumber(), h.blockedState(), buffer); + } + + h.handleTalkDialog(); + + // If a frame countdown is in progress, then decrement and exit + if (h.frameCtr() > 0) { + debugC(ERROR_DETAILED, kLureDebugAnimations, "Frame countdown = %d", h.frameCtr()); + h.decrFrameCtr(); + return; + } - // TODO: handle talk dialogs countdown if necessary - numImpinging = Support::findIntersectingCharacters(h, impingingList); + if (h.skipFlag()) { + debugC(ERROR_DETAILED, kLureDebugAnimations, "Skip flag set: numImpinging = %d", numImpinging); if (numImpinging > 0) return; h.setSkipFlag(false); } + // TODO: HS[58h] check - // If a frame countdown is in progress, then decrement and exit - if (h.frameCtr() > 0) { - h.decrFrameCtr(); + if (h.pauseCtr() > 0) { + debugC(ERROR_DETAILED, kLureDebugAnimations, "Pause countdown = %d", h.pauseCtr()); + h.updateMovement(); + h.pathFinder().clear(); + + if (h.pauseCtr() == 1) { + h.setPauseCtr(0); + if (h.characterMode() == 0) { + h.setOccupied(false); + return; + } + } else { + res.pausedList().scan(h); + return; + } + } + + if ((h.characterMode() != CHARMODE_NONE) && (h.characterMode() != CHARMODE_IDLE)) { + if (h.delayCtr() != 0) { + debugC(ERROR_DETAILED, kLureDebugAnimations, "Delay countdown = %d", h.delayCtr()); + h.updateMovement(); + h.pathFinder().clear(); + h.setDelayCtr(h.delayCtr() - 1); + return; + } + + debugC(ERROR_DETAILED, kLureDebugAnimations, "Character mode = %d", h.characterMode()); + h.setOccupied(false); + h.setCharacterMode(CHARMODE_NONE); + if (fields.playerPendingPos().isSet) { + // Start walking to the previously set destination + fields.playerPendingPos().isSet = false; + h.setDestPosition(fields.playerPendingPos().pos.x, fields.playerPendingPos().pos.y); + h.currentActions().addFront(START_WALKING, h.roomNumber()); + h.setWalkFlag(false); + } return; } @@ -1745,19 +2208,23 @@ void HotspotTickHandlers::playerAnimHandler(Hotspot &h) { // Make sure there is no longer any destination h.setDestHotspot(0); h.updateMovement2(CHARMODE_IDLE); + strcpy(room.statusLine(), ""); break; case DISPATCH_ACTION: // Dispatch an action h.setDestHotspot(0); + hotspot = NULL; if (actions.top().hasSupportData()) { hsAction = actions.top().supportData().action(); - hotspotId = actions.top().supportData().param(0); - hotspot = res.getHotspot(hotspotId); + + if (actions.top().supportData().numParams() > 0) { + hotspotId = actions.top().supportData().param(0); + hotspot = res.getHotspot(hotspotId); + } } else { hsAction = NONE; - hotspot = NULL; } h.doAction(hsAction, hotspot); @@ -1785,56 +2252,133 @@ void HotspotTickHandlers::playerAnimHandler(Hotspot &h) { case PROCESSING_PATH: h.setCharacterMode(CHARMODE_NONE); + res.pausedList().scan(h); + if (!pathFinder.process()) break; // Pathfinding is now complete -/* - if ((pathFinder.result() != PF_OK) && (h.unknownFlag() || - (pathFinder.result() != PF_DEST_OCCUPIED))) { - // TODO: occupiedFlag + pathFinder.list(buffer); + debugC(ERROR_DETAILED, kLureDebugAnimations, + "Pathfind processing done; result=%d, walkFlag=%d\n%s", + pathFinder.result(), h.walkFlag(), buffer); + + if ((pathFinder.result() != PF_OK) && + (h.walkFlag() || (pathFinder.result() != PF_DEST_OCCUPIED))) { + debugC(ERROR_DETAILED, kLureDebugAnimations, "Blocked state checking"); + if (h.blockedState() == BS_FINAL) { + res.pausedList().reset(h.hotspotId()); + h.setBlockedState(BS_NONE); + h.currentActions().pop(); + h.setCharacterMode(CHARMODE_6); + h.setDelayCtr(7); + return; + + } else if (h.blockedState() != BS_NONE) { + fields.playerPendingPos().pos.x = h.destX(); + fields.playerPendingPos().pos.y = h.destY(); + fields.playerPendingPos().isSet = true; + h.setBlockedState((BlockedState) ((int) h.blockedState() + 1)); + h.setRandomDest(); + return; + } } -*/ - actions.pop(); + + h.setCharacterMode(CHARMODE_NONE); + h.setPosition(h.x(), h.y() & 0xFFF8); if (pathFinder.isEmpty()) { - mouse.setCursorNum(CURSOR_ARROW); + mouse.setCursorNum(CURSOR_ARROW); break; } + h.currentActions().top().setAction(WALKING); if (mouse.getCursorNum() != CURSOR_CAMERA) mouse.setCursorNum(CURSOR_ARROW); - h.currentActions().addFront(WALKING, h.roomNumber()); - h.setPosition(h.x(), h.y() & 0xFFF8); // Deliberate fall through to walking case WALKING: // The character is currently moving + h.setOccupied(false); + if ((h.destHotspotId() != 0) && (h.destHotspotId() != 0xffff)) { // Player is walking to a room exit hotspot RoomExitJoinData *joinRec = res.getExitJoin(h.destHotspotId()); if (joinRec->blocked) { // Exit now blocked, so stop walking actions.pop(); + h.setOccupied(true); break; } } - if (h.walkingStep()) { - // Walking done - Room &room = Room::getReference(); - if (room.cursorState() == CS_BUMPED) - room.setCursorState(CS_NONE); - h.currentActions().pop(); + if (res.pausedList().check(PLAYER_ID, numImpinging, impingingList) == 0) { + if (h.walkingStep()) { + // Walking done + if (room.cursorState() == CS_BUMPED) + room.setCursorState(CS_NONE); + if (fields.playerPendingPos().isSet) { + h.setCharacterMode(CHARMODE_6); + h.setDelayCtr(15); + return; + } + h.currentActions().top().setAction(DISPATCH_ACTION); + } + + // Check for whether need to change room + Support::checkRoomChange(h); } - - // Check for whether need to change room - Support::checkRoomChange(h); + h.setOccupied(true); break; } } +struct RoomTranslationRecord { + uint8 srcRoom; + uint8 destRoom; +}; + +RoomTranslationRecord roomTranslations[] = { + {0x1E, 0x13}, {0x07, 0x08}, {0x1C, 0x12}, {0x26, 0x0F}, + {0x27, 0x0F}, {0x28, 0x0F}, {0x29, 0x0F}, {0x22, 0x0A}, + {0x23, 0x13}, {0x24, 0x14}, {0x31, 0x2C}, {0x2F, 0x2C}, + {0, 0}}; + +void HotspotTickHandlers::followerAnimHandler(Hotspot &h) { + Resources &res = Resources::getReference(); + ValueTableData &fields = res.fieldList(); + Hotspot *player = res.getActiveHotspot(PLAYER_ID); + + if ((fields.getField(37) == 0) && h.currentActions().isEmpty()) { + + if (h.roomNumber() == h.currentActions().top().roomNumber()) { + // In room - et a random destination + h.setRandomDest(); + + } else { + if (h.hotspotId() == GOEWIN_ID) + h.currentActions().addFront(DISPATCH_ACTION, player->roomNumber()); + else { + // Scan through the translation list for an alternate destination room + RoomTranslationRecord *p = &roomTranslations[0]; + while ((p->srcRoom != 0) && (p->srcRoom != player->roomNumber())) + ++p; + h.currentActions().top().setRoomNumber( + (p->srcRoom != 0) ? p->destRoom : player->roomNumber()); + } + } + } + + if (h.characterMode() == CHARMODE_IDLE) { + // TODO: Checks on ds:[4f8ah] to figure out + standardCharacterAnimHandler(h); + return; + } + + standardCharacterAnimHandler(h); +} + void HotspotTickHandlers::skorlAnimHandler(Hotspot &h) { Resources &res = Resources::getReference(); ValueTableData &fields = res.fieldList(); @@ -1854,8 +2398,8 @@ void HotspotTickHandlers::skorlAnimHandler(Hotspot &h) { } void HotspotTickHandlers::droppingTorchAnimHandler(Hotspot &h) { - if (h.tickCtr() > 0) - h.setTickCtr(h.tickCtr() - 1); + if (h.frameCtr() > 0) + h.setFrameCtr(h.frameCtr() - 1); else { bool result = h.executeScript(); if (result) { @@ -1878,20 +2422,45 @@ void HotspotTickHandlers::fireAnimHandler(Hotspot &h) { h.setOccupied(true); } +void HotspotTickHandlers::prisonerAnimHandler(Hotspot &h) { + ValueTableData &fields = Resources::getReference().fieldList(); + Common::RandomSource rnd; + + h.handleTalkDialog(); + if (h.frameCtr() > 0) { + h.setFrameCtr(h.frameCtr() - 1); + return; + } + + if (h.actionCtr() != 0) { + if (h.executeScript() == 0) { + h.setActionCtr(0); + h.setScript(0x3E0); + } + return; + } + + if ((fields.getField(PRISONER_DEAD) == 0) && (rnd.getRandomNumber(65536) >= 6)) { + h.setActionCtr(1); + h.setScript(0x3F6); + } +} + + // Special variables used across multiple calls to talkAnimHandler static TalkEntryData *_talkResponse; +static uint16 talkDestCharacter; void HotspotTickHandlers::talkAnimHandler(Hotspot &h) { // Talk handler Resources &res = Resources::getReference(); - Room &room = Room::getReference(); - ValueTableData &fields = res.fieldList(); StringData &strings = StringData::getReference(); Screen &screen = Screen::getReference(); Mouse &mouse = Mouse::getReference(); TalkSelections &talkSelections = res.getTalkSelections(); TalkData *data = res.getTalkData(); TalkEntryList &entries = data->entries; + Hotspot *charHotspot; char buffer[MAX_DESC_SIZE]; Rect r; int lineNum, numLines; @@ -1899,10 +2468,17 @@ void HotspotTickHandlers::talkAnimHandler(Hotspot &h) { bool showSelections, keepTalkingFlag; TalkEntryList::iterator i; TalkEntryData *entry; - uint16 result, descId, charId; + uint16 result, descId; + debugC(ERROR_DETAILED, kLureDebugAnimations, "Player talk anim handler state = %d", res.getTalkState()); switch (res.getTalkState()) { case TALK_NONE: + talkDestCharacter = h.useHotspotId(); + h.setUseHotspotId(0); + assert(talkDestCharacter != 0); + // Fall through to TALK_START + + case TALK_START: // Handle initial setup of talking options // Reset talk entry pointer list for (lineNum = 0; lineNum < MAX_TALK_SELECTIONS; ++lineNum) @@ -1926,8 +2502,12 @@ void HotspotTickHandlers::talkAnimHandler(Hotspot &h) { uint16 sequenceOffset = entry->preSequenceId & 0x3fff; bool showLine = sequenceOffset == 0; - if (!showLine) + if (!showLine) { + debugC(ERROR_DETAILED, kLureDebugAnimations, + "Checking whether to display line: script=%xh, descId=%d", + sequenceOffset, entry->descId); showLine = Script::execute(sequenceOffset) != 0; + } if (showLine) { talkSelections[numLines++] = entry; @@ -1979,50 +2559,78 @@ void HotspotTickHandlers::talkAnimHandler(Hotspot &h) { case TALK_RESPOND: // Handle initial response to show the question in a talk dialog if needed + + // Get the original question for display selectedLine = res.getTalkSelection(); entry = talkSelections[selectedLine-1]; descId = entry->descId & 0x3fff; entry->descId |= 0x4000; - - if (descId != TALK_MAGIC_ID) { - // Set up to display the question in a talk dialog - room.setTalkDialog(PLAYER_ID, descId); - res.setTalkState(TALK_RESPONSE_WAIT); - } else { - res.setTalkState(TALK_RESPOND_2); - } + debugC(ERROR_DETAILED, kLureDebugAnimations, "Talk line set: line=#%d, desc=%xh", + selectedLine, descId); + + // Get the response the destination character will say + if (descId != TALK_MAGIC_ID) + // Set up to display the question and response in talk dialogs + h.converse(talkDestCharacter, descId, false); + res.setTalkState(TALK_RESPOND_2); break; case TALK_RESPOND_2: - if (!_talkResponse) { - // Handles bringing up the response talk dialog - charId = fields.getField(ACTIVE_HOTSPOT_ID); - selectedLine = res.getTalkSelection(); - entry = talkSelections[selectedLine-1]; - - responseNumber = entry->postSequenceId; - if ((responseNumber & 0x8000) != 0) - responseNumber = Script::execute(responseNumber & 0x7fff); - - do { - _talkResponse = res.getTalkData()->getResponse(responseNumber); - if (!_talkResponse->preSequenceId) break; - responseNumber = Script::execute(_talkResponse->preSequenceId); - } while (responseNumber != TALK_RESPONSE_MAGIC_ID); - - descId = _talkResponse->descId; - if ((descId & 0x8000) != 0) - descId = Script::execute(descId & 0x7fff); - - if (descId != TALK_MAGIC_ID) { - room.setTalkDialog(charId, descId); - res.setTalkState(TALK_RESPONSE_WAIT); - return; - } + // Wait until the question dialog is no longer active + h.handleTalkDialog(); + debugC(ERROR_DETAILED, kLureDebugAnimations, "Player talk dialog countdown %d", + h.resource()->talkCountdown); + + if (res.getTalkingCharacter() != 0) + return; + + selectedLine = res.getTalkSelection(); + entry = talkSelections[selectedLine-1]; + + responseNumber = entry->postSequenceId; + debugC(ERROR_DETAILED, kLureDebugAnimations, "Post sequence Id = %xh", responseNumber); + + if ((responseNumber & 0x8000) != 0) { + responseNumber = Script::execute(responseNumber & 0x7fff); + debugC(ERROR_DETAILED, kLureDebugAnimations, "Post sequence Id = %xh", responseNumber); } - // Handle checking whether to keep talking + do { + _talkResponse = res.getTalkData()->getResponse(responseNumber); + debugC(ERROR_DETAILED, kLureDebugAnimations, "Character response pre id = %xh", + _talkResponse->preSequenceId); + + if (!_talkResponse->preSequenceId) break; + responseNumber = Script::execute(_talkResponse->preSequenceId); + debugC(ERROR_DETAILED, kLureDebugAnimations, "Character response new response = %d", + responseNumber); + } while (responseNumber != TALK_RESPONSE_MAGIC_ID); + + descId = _talkResponse->descId; + if ((descId & 0x8000) != 0) + descId = Script::execute(descId & 0x7fff); + + if (descId != TALK_MAGIC_ID) { + charHotspot = res.getActiveHotspot(talkDestCharacter); + + if (charHotspot != NULL) + charHotspot->converse(PLAYER_ID, descId, true); + } + res.setTalkState(TALK_RESPONSE_WAIT); + break; + + case TALK_RESPONSE_WAIT: + // Wait until the character's response has finished being displayed + charHotspot = res.getActiveHotspot(talkDestCharacter); + debugC(ERROR_DETAILED, kLureDebugAnimations, "Player talk dialog countdown %d", + (charHotspot) ? charHotspot->resource()->talkCountdown : 0); + + if (res.getTalkingCharacter() != 0) + return; + result = _talkResponse->postSequenceId; + debugC(ERROR_DETAILED, kLureDebugAnimations, "Character response post id = %xh", result); + if (result == 0xffff) keepTalkingFlag = false; else { @@ -2033,27 +2641,27 @@ void HotspotTickHandlers::talkAnimHandler(Hotspot &h) { keepTalkingFlag = result != 0xffff; } } - + + debugC(ERROR_DETAILED, kLureDebugAnimations, "Keep Talking flag = %d", keepTalkingFlag); + if (keepTalkingFlag) { // Reset for loading the next set of talking options res.setTalkStartEntry(result); - res.setTalkState(TALK_NONE); + res.setTalkState(TALK_START); } else { // End the conversation res.getActiveHotspot(PLAYER_ID)->setTickProc(PLAYER_TICK_PROC_ID); + if (charHotspot) + charHotspot->setUseHotspotId(0); res.setTalkData(0); res.setCurrentAction(NONE); res.setTalkState(TALK_NONE); } break; - - case TALK_RESPONSE_WAIT: - // Keeps waiting while a talk dialog is active - break; } } -void HotspotTickHandlers::headAnimationHandler(Hotspot &h) { +void HotspotTickHandlers::headAnimHandler(Hotspot &h) { Resources &res = Resources::getReference(); Hotspot *character = res.getActiveHotspot(PLAYER_ID); uint16 frameNumber = 0; @@ -2070,6 +2678,48 @@ void HotspotTickHandlers::headAnimationHandler(Hotspot &h) { h.setFrameNumber(frameNumber); } +void HotspotTickHandlers::rackSerfAnimHandler(Hotspot &h) { + Resources &res = Resources::getReference(); + + // Handle any talking + h.handleTalkDialog(); + + if (h.frameCtr() > 0) { + h.decrFrameCtr(); + return; + } + + switch (h.actionCtr()) { + case 1: + h.setScript(RACK_SERF_SCRIPT_ID_1); + h.setActionCtr(2); + break; + + case 2: + if (HotspotScript::execute(&h)) + h.setActionCtr(0); + break; + + case 3: + h.setScript(RACK_SERF_SCRIPT_ID_2); + h.setActionCtr(4); + h.setLayer(2); + + case 4: + if (HotspotScript::execute(&h)) { + h.setLayer(255); + res.deactivateHotspot(h.hotspotId()); + Hotspot *newHotspot = res.activateHotspot(RATPOUCH_ID); + newHotspot->setRoomNumber(4); + newHotspot->converse(PLAYER_ID, 0x9C, true); + } + break; + + default: + break; + } +} + /*-------------------------------------------------------------------------*/ // support method for the standard character tick proc routine - it gets called @@ -2120,7 +2770,6 @@ void HotspotTickHandlers::npcRoomChange(Hotspot &h) { } if (numCharacters >= 4) { -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()); @@ -2145,7 +2794,8 @@ warning("XYZZY npcChangeRoom - too many characters - yet to be tested"); } // No exit hotspot, or it has one that's not blocked. So start the walking - h.currentActions().top().setAction(START_WALKING); + h.currentActions().top().setAction(START_WALKING); + h.setWalkFlag(true); } /*-------------------------------------------------------------------------*/ @@ -2319,9 +2969,10 @@ bool PathFinder::process() { } // ****DEBUG**** - int ctr; - for (ctr = 0; ctr < DECODED_PATHS_WIDTH * DECODED_PATHS_HEIGHT; ++ctr) - Room::getReference().tempLayer[ctr] = _layer[ctr]; + if (_hotspot->hotspotId() == PLAYER_ID) { + for (int ctr = 0; ctr < DECODED_PATHS_WIDTH * DECODED_PATHS_HEIGHT; ++ctr) + Room::getReference().tempLayer[ctr] = _layer[ctr]; + } // Determine the walk path by working backwards from the destination, adding in the // walking steps in reverse order until source is reached @@ -2409,12 +3060,24 @@ final_step: return true; } -void PathFinder::list() { - printf("Pathfinder::list\n"); +void PathFinder::list(char *buffer) { + if (buffer) { + sprintf(buffer, "Pathfinder::list\n"); + buffer += strlen(buffer); + } + else { + printf("Pathfinder::list\n"); + } + ManagedList<WalkingActionEntry *>::iterator i; for (i = _list.begin(); i != _list.end(); ++i) { WalkingActionEntry *e = *i; - printf("Direction=%d, numSteps=%d\n", e->direction(), e->numSteps()); + if (buffer) { + sprintf(buffer, "Direction=%d, numSteps=%d\n", e->direction(), e->numSteps()); + buffer += strlen(buffer); + } + else + printf("Direction=%d, numSteps=%d\n", e->direction(), e->numSteps()); } } @@ -2517,35 +3180,84 @@ void PathFinder::initVars() { if (_yDestCurrent >= (FULL_SCREEN_HEIGHT - MENUBAR_Y_SIZE)) _yDestCurrent = FULL_SCREEN_HEIGHT - MENUBAR_Y_SIZE - 1; + _result = PF_OK; + // Subtract an amount from the countdown counter to compensate for // the time spent decompressing the walkable areas set for the room _countdownCtr -= 700; } -// Current action stack class +// Current action entry class methods + +void CurrentActionEntry::setSupportData(uint16 entryId) { + CharacterScheduleEntry &entry = supportData(); + + CharacterScheduleEntry *newEntry = Resources::getReference(). + charSchedules().getEntry(entryId, entry.parent()); + setSupportData(newEntry); +} -void CurrentActionStack::list() { +void CurrentActionStack::list(char *buffer) { ManagedList<CurrentActionEntry *>::iterator i; - printf("CurrentActionStack::list num_actions=%d\n", size()); + if (buffer) { + sprintf(buffer, "CurrentActionStack::list num_actions=%d\n", size()); + buffer += strlen(buffer); + } + else + printf("CurrentActionStack::list num_actions=%d\n", size()); for (i = _actions.begin(); i != _actions.end(); ++i) { CurrentActionEntry *entry = *i; - printf("style=%d room#=%d", entry->action(), entry->roomNumber()); + if (buffer) { + sprintf(buffer, "style=%d room#=%d", entry->action(), entry->roomNumber()); + buffer += strlen(buffer); + } + else + printf("style=%d room#=%d", entry->action(), entry->roomNumber()); + if (entry->hasSupportData()) { CharacterScheduleEntry &rec = entry->supportData(); - printf(", action=%d params=", rec.action()); + if (buffer) { + sprintf(buffer, ", action=%d params=", rec.action()); + buffer += strlen(buffer); + } + else + printf(", action=%d params=", rec.action()); + if (rec.numParams() == 0) - printf("none"); + if (buffer) { + strcat(buffer, "none"); + buffer += strlen(buffer); + } + else + printf("none"); else { for (int ctr = 0; ctr < rec.numParams(); ++ctr) { - if (ctr != 0) printf(", "); - printf("%d", rec.param(ctr)); + if (ctr != 0) { + if (buffer) { + strcpy(buffer, ", "); + buffer += strlen(buffer); + } + else + printf(", "); + } + + if (buffer) { + sprintf(buffer, "%d", rec.param(ctr)); + buffer += strlen(buffer); + } else + printf("%d", rec.param(ctr)); } } } - printf("\n"); + if (buffer) { + sprintf(buffer, "\n"); + buffer += strlen(buffer); + } + else + printf("\n"); } } @@ -2580,9 +3292,8 @@ int Support::findIntersectingCharacters(Hotspot &h, uint16 *charList) { // 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) || - (hotspotY + hotspot.charRectY() < r.top) || + if ((hotspot.x() >= r.right) || (hotspot.x() + hotspot.widthCopy() <= r.left) || + (hotspotY + hotspot.charRectY() <= r.top) || (hotspotY - hotspot.charRectY() - hotspot.yCorrection() >= r.bottom)) continue; @@ -2625,10 +3336,12 @@ void Support::checkRoomChange(Hotspot &h) { void Support::characterChangeRoom(Hotspot &h, uint16 roomNumber, int16 newX, int16 newY, Direction dir) { Resources &res = Resources::getReference(); + Room &room = Room::getReference(); ValueTableData &fields = res.fieldList(); if (h.hotspotId() == PLAYER_ID) { // Room change code for the player + if (room.cursorState() != CS_NONE) return; h.setDirection(dir); PlayerNewPosition &p = fields.playerNewPos(); @@ -2636,7 +3349,19 @@ void Support::characterChangeRoom(Hotspot &h, uint16 roomNumber, p.position.x = newX; p.position.y = newY - 48; - // TODO: Call sub_136, and if !ZF reset new room number back to 0 + // TODO: Double-check.. is it impinging in leaving room (right now) or entering room + if (checkForIntersectingCharacter(h)) { + fields.playerPendingPos().pos.x = h.destX(); + fields.playerPendingPos().pos.y = h.destY(); + fields.playerPendingPos().isSet = true; + Room::getReference().setCursorState(CS_BUMPED); + h.setActionCtr(0); + h.setBlockedState((BlockedState) ((int) h.blockedState() + 1)); + h.setDestHotspot(0); + h.setRandomDest(); + p.roomNumber = 0; + } + } else { // Any other character changing room @@ -2673,4 +3398,5 @@ bool Support::isCharacterInList(uint16 *lst, int numEntries, uint16 charId) { return false; } + } // end of namespace Lure diff --git a/engines/lure/hotspots.h b/engines/lure/hotspots.h index 8e18827092..071695f78d 100644 --- a/engines/lure/hotspots.h +++ b/engines/lure/hotspots.h @@ -35,6 +35,8 @@ namespace Lure { class Hotspot; class Support { +private: + static bool changeRoomCheckBumped(Hotspot &h); public: static int findIntersectingCharacters(Hotspot &h, uint16 *charList); static bool checkForIntersectingCharacter(Hotspot &h); @@ -54,15 +56,20 @@ private: // Handler methods static void defaultHandler(Hotspot &h); + static void voiceBubbleAnimHandler(Hotspot &h); static void standardAnimHandler(Hotspot &h); static void standardCharacterAnimHandler(Hotspot &h); + static void puzzledAnimHandler(Hotspot &h); static void roomExitAnimHandler(Hotspot &h); static void playerAnimHandler(Hotspot &h); + static void followerAnimHandler(Hotspot &h); static void skorlAnimHandler(Hotspot &h); static void droppingTorchAnimHandler(Hotspot &h); static void fireAnimHandler(Hotspot &h); + static void prisonerAnimHandler(Hotspot &h); static void talkAnimHandler(Hotspot &h); - static void headAnimationHandler(Hotspot &h); + static void headAnimHandler(Hotspot &h); + static void rackSerfAnimHandler(Hotspot &h); public: static HandlerMethodPtr getHandler(uint16 procOffset); @@ -98,6 +105,7 @@ public: void setAction(CurrentAction newAction) { _action = newAction; } void setRoomNumber(uint16 roomNum) { _roomNumber = roomNum; } void setSupportData(CharacterScheduleEntry *newRec) { _supportData = newRec; } + void setSupportData(uint16 entryId); }; class CurrentActionStack { @@ -112,7 +120,8 @@ public: CurrentAction action() { return isEmpty() ? NO_ACTION : top().action(); } void pop() { _actions.erase(_actions.begin()); } int size() { return _actions.size(); } - void list(); + void list(char *buffer); + void list() { list(NULL); } void addBack(CurrentAction newAction, uint16 roomNum) { _actions.push_back(new CurrentActionEntry(newAction, roomNum)); @@ -177,7 +186,8 @@ public: void clear(); void reset(RoomPathsData &src); bool process(); - void list(); + void list(char *buffer); + void list() { list(NULL); } void pop() { _list.erase(_list.begin()); } WalkingActionEntry &top() { return **_list.begin(); } @@ -188,15 +198,13 @@ public: enum HotspotPrecheckResult {PC_EXECUTE, PC_NOT_IN_ROOM, PC_UNKNOWN, PC_INITIAL, PC_EXCESS}; -enum BlockedState {BS_NONE, BS_INITIAL, BS_UNKNOWN}; +#define MAX_NUM_FRAMES 16 class Hotspot { private: HotspotData *_data; HotspotAnimData *_anim; -public: HandlerMethodPtr _tickHandler; -private: Surface *_frames; uint16 _hotspotId; uint16 _roomNumber; @@ -220,23 +228,25 @@ private: CurrentActionStack _currentActions; CharacterScheduleEntry _npcSupportData; PathFinder _pathFinder; + uint16 _frameWidth; + bool _frameStartsUsed; + uint16 _frameStarts[MAX_NUM_FRAMES]; uint16 _frameCtr; - uint8 _actionCtr; + uint8 _voiceCtr; int16 _destX, _destY; uint16 _destHotspotId; uint16 _blockedOffset; uint8 _exitCtr; - BlockedState _blockedState; - bool _unknownFlag; + bool _walkFlag; // Support methods - void startTalk(HotspotData *charHotspot); + uint16 getTalkId(HotspotData *charHotspot); + void startTalk(HotspotData *charHotspot, uint16 id); + void startTalkDialog(); // Action support methods HotspotPrecheckResult actionPrecheck(HotspotData *hotspot); - HotspotPrecheckResult actionPrecheck2(HotspotData *hotspot); - void actionPrecheck3(HotspotData *hotspot); bool characterWalkingCheck(HotspotData *hotspot); bool doorCloseCheck(uint16 doorId); void resetDirection(); @@ -265,14 +275,14 @@ private: void npcSetRoomAndBlockedOffset(HotspotData *hotspot); void npcUnknown1(HotspotData *hotspot); void npcExecScript(HotspotData *hotspot); - void npcUnknown2(HotspotData *hotspot); + void npcResetPausedList(HotspotData *hotspot); void npcSetRandomDest(HotspotData *hotspot); void npcWalkingCheck(HotspotData *hotspot); void npcSetSupportOffset(HotspotData *hotspot); void npcSupportOffsetConditional(HotspotData *hotspot); void npcDispatchAction(HotspotData *hotspot); void npcUnknown3(HotspotData *hotspot); - void npcUnknown4(HotspotData *hotspot); + void npcPause(HotspotData *hotspot); void npcStartTalking(HotspotData *hotspot); void npcJumpAddress(HotspotData *hotspot); public: @@ -288,7 +298,10 @@ public: HotspotData *resource() { return _data; } uint16 numFrames() { return _numFrames; } uint16 frameNumber() { return _frameNumber; } - void setFrameNumber(uint16 v) { _frameNumber = v; } + void setFrameNumber(uint16 frameNum) { + assert(frameNum < _numFrames); + _frameNumber = frameNum; + } void incFrameNumber(); Direction direction() { return _direction; } uint16 frameWidth() { return _width; } @@ -301,8 +314,7 @@ public: uint16 destHotspotId() { return _destHotspotId; } uint16 blockedOffset() { return _blockedOffset; } uint8 exitCtr() { return _exitCtr; } - BlockedState blockedState() { return _blockedState; } - bool unknownFlag() { return _unknownFlag; } + bool walkFlag() { return _walkFlag; } uint16 width() { return _width; } uint16 height() { return _height; } uint16 widthCopy() { return _widthCopy; } @@ -328,14 +340,26 @@ 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; } + BlockedState blockedState() { + assert(_data); + return _data->blockedState; + } + void setBlockedState(BlockedState newState) { + assert(_data); + _data->blockedState = newState; + } + void setWalkFlag(bool value) { _walkFlag = value; } void setSize(uint16 newWidth, uint16 newHeight); void setScript(uint16 offset) { assert(_data != NULL); _sequenceOffset = offset; _data->sequenceOffset = offset; } + void setLayer(uint8 newLayer) { + assert(_data != NULL); + _layer = newLayer; + _data->layer = newLayer; + } void setActions(uint32 newActions) { _actions = newActions; } void setCharRectY(uint16 value) { _charRectY = value; } void setSkipFlag(bool value) { _skipFlag = value; } @@ -348,12 +372,44 @@ public: _data->characterMode = value; } uint16 delayCtr() { - assert(_data != NULL); - return _data->delayCtr; + assert(_data); + return _data->delayCtr; } void setDelayCtr(uint16 value) { - assert(_data != NULL); - _data->delayCtr = value; + assert(_data); + _data->delayCtr = value; + } + uint16 pauseCtr() { + assert(_data); + return _data->pauseCtr; + } + void setPauseCtr(uint16 value) { + assert(_data); + _data->pauseCtr = value; + } + bool coveredFlag() { + assert(_data); + return _data->coveredFlag; + } + void setCoveredFlag(bool value) { + assert(_data); + _data->coveredFlag = value; + } + uint16 useHotspotId() { + assert(_data); + return _data->useHotspotId; + } + void setUseHotspotId(uint16 value) { + assert(_data); + _data->useHotspotId = value; + } + uint16 v2b() { + assert(_data); + return _data->v2b; + } + void setV2b(uint16 value) { + assert(_data); + _data->v2b = value; } void copyTo(Surface *dest); @@ -367,6 +423,7 @@ public: void endAction(); void setDirection(Direction dir); void faceHotspot(HotspotData *hotspot); + void faceHotspot(uint16 hotspotId); void setRandomDest(); void setOccupied(bool occupiedFlag); bool walkingStep(); @@ -374,7 +431,6 @@ public: void updateMovement2(CharacterMode value); void resetPosition(); - // Actions void doAction(); void doAction(Action action, HotspotData *hotspot); CurrentActionStack ¤tActions() { return _currentActions; } @@ -383,8 +439,21 @@ public: uint16 frameCtr() { return _frameCtr; } void setFrameCtr(uint16 value) { _frameCtr = value; } void decrFrameCtr() { if (_frameCtr > 0) --_frameCtr; } - uint8 actionCtr() { return _actionCtr; } - void setActionCtr(uint8 v) { _actionCtr = v; } + uint8 actionCtr() { + assert(_data); + return _data->actionCtr; + } + void setActionCtr(uint8 v) { + assert(_data); + _data->actionCtr = v; + } + uint8 voiceCtr() { return _voiceCtr; } + void setVoiceCtr(uint8 v) { _voiceCtr = v; } + + // Miscellaneous + void converse(uint16 destHotspot, uint16 messageId, bool standStill); + void scheduleConverse(uint16 destHotspot, uint16 messageId); + void handleTalkDialog(); }; typedef ManagedList<Hotspot *> HotspotList; |