From f771fa40ad474d78d32ce5bff67afc937547e5de Mon Sep 17 00:00:00 2001 From: Evgeny Grechnikov Date: Sat, 25 Aug 2018 15:11:24 +0300 Subject: LASTEXPRESS: multiple fixes in NPC logic Checked the logic against the original game (to be precise, DOS English version from GOG, although I think AI logic has no significant differences with other versions). Fixed a *lot* of errors with varying visibility for the user. Also, save+exit+load sometimes resulted in memory corruption like ((EntityParametersSSII*)(new EntityParametersIIII))->param8 = 0; load operation did not restore the correct type of NPC logic context, the default one was used (which also has the smallest sizeof). Should be fixed now. Save+load is still unusable because it locks everybody waiting for kActionEndSound (the sound state is not restored), but, at least, it should not corrupt the memory. Hopefully. --- engines/lastexpress/entities/anna.cpp | 121 ++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 56 deletions(-) (limited to 'engines/lastexpress/entities/anna.cpp') diff --git a/engines/lastexpress/entities/anna.cpp b/engines/lastexpress/entities/anna.cpp index 52e975086a..2b31c7b5c8 100644 --- a/engines/lastexpress/entities/anna.cpp +++ b/engines/lastexpress/entities/anna.cpp @@ -22,6 +22,8 @@ #include "lastexpress/entities/anna.h" +#include "lastexpress/entities/vesna.h" + #include "lastexpress/fight/fight.h" #include "lastexpress/game/action.h" @@ -41,23 +43,23 @@ namespace LastExpress { Anna::Anna(LastExpressEngine *engine) : Entity(engine, kEntityAnna) { ADD_CALLBACK_FUNCTION(Anna, reset); - ADD_CALLBACK_FUNCTION(Anna, draw); - ADD_CALLBACK_FUNCTION(Anna, updatePosition); - ADD_CALLBACK_FUNCTION(Anna, enterExitCompartment); + ADD_CALLBACK_FUNCTION_S(Anna, draw); + ADD_CALLBACK_FUNCTION_SII(Anna, updatePosition); + ADD_CALLBACK_FUNCTION_SI(Anna, enterExitCompartment); ADD_CALLBACK_FUNCTION(Anna, callbackActionOnDirection); - ADD_CALLBACK_FUNCTION(Anna, callSavepoint); - ADD_CALLBACK_FUNCTION(Anna, playSound); + ADD_CALLBACK_FUNCTION_SIIS(Anna, callSavepoint); + ADD_CALLBACK_FUNCTION_S(Anna, playSound); ADD_CALLBACK_FUNCTION(Anna, callbackActionRestaurantOrSalon); - ADD_CALLBACK_FUNCTION(Anna, savegame); - ADD_CALLBACK_FUNCTION(Anna, updateEntity); - ADD_CALLBACK_FUNCTION(Anna, updateFromTime); + ADD_CALLBACK_FUNCTION_II(Anna, savegame); + ADD_CALLBACK_FUNCTION_II(Anna, updateEntity); + ADD_CALLBACK_FUNCTION_I(Anna, updateFromTime); ADD_CALLBACK_FUNCTION(Anna, practiceMusic); - ADD_CALLBACK_FUNCTION(Anna, draw2); - ADD_CALLBACK_FUNCTION(Anna, updateFromTicks); - ADD_CALLBACK_FUNCTION(Anna, compartmentLogic); + ADD_CALLBACK_FUNCTION_SSI(Anna, draw2); + ADD_CALLBACK_FUNCTION_I(Anna, updateFromTicks); + ADD_CALLBACK_FUNCTION_IS(Anna, compartmentLogic); ADD_CALLBACK_FUNCTION(Anna, chapter1); - ADD_CALLBACK_FUNCTION(Anna, doWalkP1); - ADD_CALLBACK_FUNCTION(Anna, diningLogic); + ADD_CALLBACK_FUNCTION_II(Anna, doWalkP1); + ADD_CALLBACK_FUNCTION_I(Anna, diningLogic); ADD_CALLBACK_FUNCTION(Anna, fleeTyler); ADD_CALLBACK_FUNCTION(Anna, waitDinner); ADD_CALLBACK_FUNCTION(Anna, goDinner); @@ -78,13 +80,13 @@ Anna::Anna(LastExpressEngine *engine) : Entity(engine, kEntityAnna) { ADD_CALLBACK_FUNCTION(Anna, goVassili); ADD_CALLBACK_FUNCTION(Anna, function37); ADD_CALLBACK_FUNCTION(Anna, speakTatiana); - ADD_CALLBACK_FUNCTION(Anna, doWalk1019); + ADD_CALLBACK_FUNCTION_II(Anna, doWalk1019); ADD_CALLBACK_FUNCTION(Anna, leaveTatiana); ADD_CALLBACK_FUNCTION(Anna, goBackToSleep); ADD_CALLBACK_FUNCTION(Anna, chapter2); ADD_CALLBACK_FUNCTION(Anna, inPart2); ADD_CALLBACK_FUNCTION(Anna, chapter3); - ADD_CALLBACK_FUNCTION(Anna, exitCompartment); + ADD_CALLBACK_FUNCTION_I(Anna, exitCompartment); ADD_CALLBACK_FUNCTION(Anna, practicing); ADD_CALLBACK_FUNCTION(Anna, goLunch); ADD_CALLBACK_FUNCTION(Anna, lunch); @@ -111,7 +113,7 @@ Anna::Anna(LastExpressEngine *engine) : Entity(engine, kEntityAnna) { ADD_CALLBACK_FUNCTION(Anna, goSalon4); ADD_CALLBACK_FUNCTION(Anna, returnCompartment4); ADD_CALLBACK_FUNCTION(Anna, enterCompartmentCathFollowsAnna); - ADD_CALLBACK_FUNCTION(Anna, doWalkCathFollowsAnna); + ADD_CALLBACK_FUNCTION_II(Anna, doWalkCathFollowsAnna); ADD_CALLBACK_FUNCTION(Anna, letDownHair); ADD_CALLBACK_FUNCTION(Anna, chapter5); ADD_CALLBACK_FUNCTION(Anna, tiedUp); @@ -125,7 +127,7 @@ Anna::Anna(LastExpressEngine *engine) : Entity(engine, kEntityAnna) { ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(1, Anna, reset) - Entity::reset(savepoint, true, true); + Entity::reset(savepoint, kClothes3, true); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// @@ -311,7 +313,7 @@ IMPLEMENT_FUNCTION(12, Anna, practiceMusic) } params->param4 = 0; - params->param5 = 0; + params->param5 = 1; } else { getSoundQueue()->removeFromQueue(kEntityAnna); @@ -335,7 +337,7 @@ IMPLEMENT_FUNCTION(12, Anna, practiceMusic) getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand); getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue); - if (getEntities()->isPlayerPosition(kCarRedSleeping, 49)) + if (getEntities()->isPlayerPosition(kCarRedSleeping, 78)) getScenes()->loadSceneFromPosition(kCarRedSleeping, 49); getEntities()->drawSequenceLeft(kEntityAnna, "418C"); @@ -357,7 +359,7 @@ IMPLEMENT_FUNCTION(12, Anna, practiceMusic) if (getEntities()->isPlayerPosition(kCarRedSleeping, 60)) { ++params->param3; if (params->param3 == 2) { - setCallback(2); + setCallback(5); setup_draw("418B"); } } @@ -606,6 +608,7 @@ IMPLEMENT_FUNCTION_II(17, Anna, doWalkP1, uint32, uint32) break; case kActionDefault: + getData()->inventoryItem = kItemNone; if (getProgress().jacket == kJacketGreen) { if (!getEvent(kEventGotALight) && !getEvent(kEventGotALightD) && !getEvent(kEventAugustPresentAnna) && !getEvent(kEventAugustPresentAnnaFirstIntroduction)) params->param3 = kItemInvalid; @@ -729,7 +732,7 @@ IMPLEMENT_FUNCTION_I(18, Anna, diningLogic, TimeValue) case 2: getAction()->playAnimation(kEventDinerMindJoin); - params->param2 &= 0xFFFFFFF7; + params->param2 &= 0xFFFFFF7F; if (getProgress().jacket == kJacketGreen && !getEvent(kEventAnnaGiveScarfAsk) @@ -992,6 +995,7 @@ IMPLEMENT_FUNCTION(25, Anna, eatingDinner) break; case 2: + setCallback(3); setup_callbackActionRestaurantOrSalon(); break; @@ -1021,10 +1025,10 @@ IMPLEMENT_FUNCTION(26, Anna, leaveDinner) case kActionDefault: getData()->location = kLocationOutsideCompartment; - getEntities()->updatePositionExit(kEntityAnna, kCarRestaurant, 62); + getEntities()->updatePositionEnter(kEntityAnna, kCarRestaurant, 62); setCallback(1); - setup_callSavepoint("001L", kEntityTables0, kActionDrawTablesWithChairs, "001H"); + setup_callSavepoint("001L", kEntityTables0, kActionDrawTablesWithChairs, "001M"); break; case kActionCallback: @@ -1222,7 +1226,7 @@ IMPLEMENT_FUNCTION(29, Anna, waitAugust) case 2: getAction()->playAnimation((getEvent(kEventAugustPresentAnna) || getEvent(kEventAugustPresentAnnaFirstIntroduction)) ? kEventAnnaConversationGoodNight : kEventAnnaIntroductionRejected); - getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem & kItemToggleLow); + getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem & kItemToggleHigh); if (getProgress().jacket == kJacketGreen && !getEvent(kEventAnnaGiveScarfAsk) @@ -1698,7 +1702,7 @@ IMPLEMENT_FUNCTION_II(39, Anna, doWalk1019, CarIndex, EntityPosition) case kActionCallback: if (getCallback() == 1) { - getAction()->playAnimation(getData()->direction == kDirectionNone ? kEventAnnaGoodNight : kEventAnnaGoodNightInverse); + getAction()->playAnimation(getData()->direction == kDirectionUp ? kEventAnnaGoodNight : kEventAnnaGoodNightInverse); getData()->inventoryItem = kItemNone; getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp); @@ -1807,8 +1811,8 @@ IMPLEMENT_FUNCTION(41, Anna, goBackToSleep) if (!Entity::updateParameter(params->param2, getState()->time, 2700)) break; - params->param5++; - switch (params->param5) { + params->param1++; + switch (params->param1) { default: break; @@ -2127,7 +2131,7 @@ label_callback_4: break; case kActionDefault: - getEntities()->drawSequenceLeft(kEntityAnna, "026C"); + getEntities()->drawSequenceLeft(kEntityAnna, "026c"); getData()->location = kLocationInsideCompartment; setCallback(1); @@ -2220,7 +2224,7 @@ IMPLEMENT_FUNCTION(50, Anna, leaveLunch) case kActionDefault: setCallback(1); - setup_playSound("ann3141"); + setup_playSound("Ann3141"); break; case kActionCallback: @@ -2266,7 +2270,7 @@ IMPLEMENT_FUNCTION(51, Anna, afterLunch) break; case kActionDefault: - getSound()->playSound(kEntityAnna, "Aug3142", kFlagInvalid, 30); + getSound()->playSound(kEntityAnna, "Ann3142", kFlagInvalid, 30); getEntities()->updatePositionEnter(kEntityAnna, kCarRestaurant, 57); getEntities()->drawSequenceRight(kEntityAnna, "112A"); if (getEntities()->isInRestaurant(kEntityPlayer)) @@ -2401,7 +2405,7 @@ IMPLEMENT_FUNCTION(53, Anna, dressing) if (params->param3) { if (Entity::updateParameter(params->param6, getState()->time, 9000)) { params->param4 = !params->param4; - getEntities()->drawSequenceLeft(kEntityAnna, params->param4 ? "417B" : "417A"); + getEntities()->drawSequenceLeft(kEntityAnna, params->param4 ? "417A" : "417B"); params->param6 = 0; } } @@ -2549,7 +2553,7 @@ IMPLEMENT_FUNCTION(54, Anna, giveMaxToConductor2) if (Entity::updateParameter(params->param6, getState()->time, 9000)) { params->param4 = !params->param4; - getEntities()->drawSequenceLeft(kEntityAnna, params->param4 ? "417B" : "417A"); + getEntities()->drawSequenceLeft(kEntityAnna, params->param4 ? "417A" : "417B"); params->param6 = 0; } } @@ -2729,7 +2733,7 @@ IMPLEMENT_FUNCTION(55, Anna, goConcert) case 1: getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand); setCallback(2); - setup_updateEntity(kCarRedSleeping, kPosition_9270); + setup_updateEntity(kCarKronos, kPosition_9270); break; case 2: @@ -3072,7 +3076,7 @@ IMPLEMENT_FUNCTION(61, Anna, goBaggageCompartment) getState()->timeDelta = 3; setCallback(1); - setup_savegame(kSavegameTypeIndex, 0); + setup_savegame(kSavegameTypeTime, 0); break; case kActionCallback: @@ -3132,7 +3136,7 @@ IMPLEMENT_FUNCTION(62, Anna, function62) break; case kActionNone: - if (getState()->time > kTime2259000 && !params->param2) { + if (params->param1 && getState()->time > kTime2259000 && !params->param2) { params->param2 = 1; getSavePoints()->push(kEntityAnna, kEntityVesna, kAction189299008); setup_deadBaggageCompartment(); @@ -3222,6 +3226,7 @@ IMPLEMENT_FUNCTION(64, Anna, baggageFight) getScenes()->loadSceneFromPosition(kCarBaggage, 96); getProgress().field_54 = 0; + RESET_ENTITY_STATE(kEntityVesna, Vesna, setup_inCompartment); getState()->time = kTime2266200; setup_prepareVienna(); @@ -3302,7 +3307,7 @@ label_next: case kAction1: getData()->inventoryItem = kItemNone; - getData()->location = kLocationInsideCompartment; + getEntityData(kEntityPlayer)->location = kLocationInsideCompartment; setCallback(1); setup_savegame(kSavegameTypeEvent, kEventAnnaConversation_34); @@ -3329,9 +3334,11 @@ label_next: break; case kActionDrawScene: - getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand); - params->param1 = 0; - params->param2 = 0; + if (params->param1 || params->param2) { + getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand); + params->param1 = 0; + params->param2 = 0; + } break; case kActionCallback: @@ -3743,7 +3750,7 @@ IMPLEMENT_FUNCTION(75, Anna, tiedUp) else getAction()->playAnimation(getEvent(kEventAnnaKissTrainHijacked) ? kEventAnnaBaggageTies3 : kEventAnnaBaggageTies4); - getScenes()->loadSceneFromPosition(kCarBaggage, 8); + getScenes()->loadSceneFromPosition(kCarBaggageRear, 88); setup_function76(); } break; @@ -3806,7 +3813,7 @@ IMPLEMENT_FUNCTION(77, Anna, readyToScore) getObjects()->update(kObject106, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); getAction()->playAnimation(kEventAnnaDialogGoToJerusalem); - getState()->time = kTimeCityConstantinople; + getState()->time = kTime4914000; getState()->timeDelta = 0; getSavePoints()->push(kEntityAnna, kEntityTatiana, kAction236060709); @@ -3837,10 +3844,12 @@ IMPLEMENT_FUNCTION(78, Anna, kidnapped) break; } - getState()->time = kTimeInvalid2; + if (getEntities()->isInSalon(kEntityPlayer)) { + getState()->time = kTime4920300; - setCallback(getInventory()->get(kItemFirebird)->location == kObjectLocation4 ? 2 : 1); - setup_savegame(kSavegameTypeEvent, getInventory()->get(kItemFirebird)->location == kObjectLocation4 ? kEventKronosHostageAnna : kEventKronosHostageAnnaNoFirebird); + setCallback(getInventory()->get(kItemFirebird)->location == kObjectLocation4 ? 2 : 1); + setup_savegame(kSavegameTypeEvent, getInventory()->get(kItemFirebird)->location == kObjectLocation4 ? kEventKronosHostageAnna : kEventKronosHostageAnnaNoFirebird); + } break; case kActionCallback: @@ -3871,9 +3880,9 @@ IMPLEMENT_FUNCTION(79, Anna, waiting) break; case kActionEndSound: - getState()->time = kTime5933; + getState()->time = kTime4923000; setCallback(1); - setup_savegame(kSavegameTypeEvent, kEventKahinaPunch); + setup_savegame(kSavegameTypeEvent, kEventKahinaPunchBaggageCarEntrance); break; case kActionDrawScene: @@ -3883,7 +3892,7 @@ IMPLEMENT_FUNCTION(79, Anna, waiting) } if (getEntities()->isInSalon(kEntityPlayer) && !getEvent(kEventKahinaPunch)) { - getState()->time = kTime5933; + getState()->time = kTime4923000; setCallback(2); setup_savegame(kSavegameTypeEvent, kEventKahinaPunch); } @@ -3908,7 +3917,7 @@ IMPLEMENT_FUNCTION(79, Anna, waiting) break; case 2: - getAction()->playAnimation(kEventKahinaPunchSalon); + getAction()->playAnimation(kEventKahinaPunch); break; } @@ -3932,17 +3941,17 @@ IMPLEMENT_FUNCTION(80, Anna, finalSequence) case kActionEndSound: getSound()->playSound(kEntityPlayer, "Kro5002", kFlagDefault); - getState()->time = kTime4923000; + getState()->time = kTime4929300; - setCallback(1); - setup_savegame(kSavegameTypeEvent, kEventKronosBringFirebird); + setCallback(2); + setup_savegame(kSavegameTypeEvent, kEventKahinaPunch); break; case kActionDefault: - getState()->time = kTime4929300; + getState()->time = kTime4923000; - setCallback(2); - setup_savegame(kSavegameTypeEvent, kEventKahinaPunch); + setCallback(1); + setup_savegame(kSavegameTypeEvent, kEventKronosBringFirebird); break; case kActionCallback: @@ -3997,8 +4006,8 @@ IMPLEMENT_FUNCTION(81, Anna, openFirebird) if (!Entity::updateParameter(params->param1, getState()->timeTicks, 180)) break; - getSound()->playSound(kEntityTrain, "LIB069"); - getLogic()->gameOver(kSavegameTypeIndex, 2, kSceneNone, true); + getSound()->playSound(kEntityTrain, "LIB069", kFlagDefault); + getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, true); break; case kActionCallback: -- cgit v1.2.3