diff options
author | Littleboy | 2011-06-21 23:50:01 -0400 |
---|---|---|
committer | Littleboy | 2011-06-22 19:31:30 -0400 |
commit | 3c2a9b292f5a851529fb0196eab662fd9996a459 (patch) | |
tree | c6ab5fbb47fe5d5d75b0e0a2ed680bbf445a65a4 /engines/lastexpress/fight | |
parent | 8e42ee4c983167f0e2589b1401ba90a5df99fcc6 (diff) | |
download | scummvm-rg350-3c2a9b292f5a851529fb0196eab662fd9996a459.tar.gz scummvm-rg350-3c2a9b292f5a851529fb0196eab662fd9996a459.tar.bz2 scummvm-rg350-3c2a9b292f5a851529fb0196eab662fd9996a459.zip |
LASTEXPRESS: Move Fight class to its own folder
Diffstat (limited to 'engines/lastexpress/fight')
-rw-r--r-- | engines/lastexpress/fight/fight.cpp | 1583 | ||||
-rw-r--r-- | engines/lastexpress/fight/fight.h | 266 |
2 files changed, 1849 insertions, 0 deletions
diff --git a/engines/lastexpress/fight/fight.cpp b/engines/lastexpress/fight/fight.cpp new file mode 100644 index 0000000000..5b3530f488 --- /dev/null +++ b/engines/lastexpress/fight/fight.cpp @@ -0,0 +1,1583 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "lastexpress/fight/fight.h" + +#include "lastexpress/data/cursor.h" +#include "lastexpress/data/scene.h" +#include "lastexpress/data/sequence.h" + +#include "lastexpress/game/entities.h" +#include "lastexpress/game/inventory.h" +#include "lastexpress/game/logic.h" +#include "lastexpress/game/object.h" +#include "lastexpress/game/scenes.h" +#include "lastexpress/game/sound.h" +#include "lastexpress/game/state.h" + +#include "lastexpress/graphics.h" +#include "lastexpress/helpers.h" +#include "lastexpress/lastexpress.h" +#include "lastexpress/resource.h" + +#include "common/func.h" + +namespace LastExpress { + +#define CALL_FUNCTION0(fighter, name) \ + (*fighter->name)(fighter) + +#define CALL_FUNCTION1(fighter, name, a) \ + (*fighter->name)(fighter, a) + +#define REGISTER_PLAYER_FUNCTIONS(name) \ + if (!_data) \ + error("Fight::load##namePlayer - invalid data!"); \ + _data->player->handleAction = new Common::Functor2Mem<Fighter *, FightAction, void, Fight>(this, &Fight::handleAction##name); \ + _data->player->update = new Common::Functor1Mem<Fighter *, void, Fight>(this, &Fight::update##name); \ + _data->player->canInteract = new Common::Functor2Mem<Fighter const *, FightAction, bool, Fight>(this, &Fight::canInteract##name); + +#define REGISTER_OPPONENT_FUNCTIONS(name) \ + if (!_data) \ + error("Fight::load##nameOpponent - invalid data!"); \ + _data->opponent->handleAction = new Common::Functor2Mem<Fighter *, FightAction, void, Fight>(this, &Fight::handleOpponentAction##name); \ + _data->opponent->update = new Common::Functor1Mem<Fighter *, void, Fight>(this, &Fight::updateOpponent##name); \ + _data->opponent->canInteract = new Common::Functor2Mem<Fighter const *, FightAction, bool, Fight>(this, &Fight::canInteract); + +#define CHECK_SEQUENCE2(fighter, value) \ + (fighter->frame->getInfo()->field_33 & value) + +Fight::Fight(LastExpressEngine *engine) : _engine(engine), _data(NULL), _endType(kFightEndLost), _state(0), _handleTimer(false) {} + +Fight::~Fight() { + clearData(); + _data = NULL; + + // Zero passed pointers + _engine = NULL; +} + +////////////////////////////////////////////////////////////////////////// +// Events +////////////////////////////////////////////////////////////////////////// + +void Fight::eventMouse(const Common::Event &ev) { + if (!_data || _data->index) + return; + + // TODO move all the egg handling to inventory functions + + getFlags()->mouseLeftClick = false; + getFlags()->shouldRedraw = false; + getFlags()->mouseRightClick = false; + + if (ev.mouse.x < 608 || ev.mouse.y < 448 || ev.mouse.x >= 640 || ev.mouse.x >= 480) { + + // Handle right button click + if (ev.type == Common::EVENT_RBUTTONUP) { + getSound()->removeFromQueue(kEntityTables0); + setStopped(); + + getGlobalTimer() ? _state = 0 : ++_state; + + getFlags()->mouseRightClick = true; + } + + if (_handleTimer) { + // Timer expired => show with full brightness + if (!getGlobalTimer()) + getInventory()->drawBlinkingEgg(); + + _handleTimer = false; + } + + // Check hotspots + Scene *scene = getScenes()->get(getState()->scene); + SceneHotspot *hotspot = NULL; + + if (!scene->checkHotSpot(ev.mouse, &hotspot)) { + _engine->getCursor()->setStyle(kCursorNormal); + } else { + _engine->getCursor()->setStyle((CursorStyle)hotspot->cursor); + + // Call player function + if (CALL_FUNCTION1(_data->player, canInteract, (FightAction)hotspot->action)) { + if (ev.type == Common::EVENT_LBUTTONUP) + CALL_FUNCTION1(_data->player, handleAction, (FightAction)hotspot->action); + } else { + _engine->getCursor()->setStyle(kCursorNormal); + } + } + } else { + // Handle clicks on menu icon + + if (!_handleTimer) { + // Timer expired => show with full brightness + if (!getGlobalTimer()) + getInventory()->drawBlinkingEgg(); + + _handleTimer = true; + } + + // Stop fight if clicked + if (ev.type == Common::EVENT_LBUTTONUP) { + _handleTimer = false; + getSound()->removeFromQueue(kEntityTables0); + bailout(kFightEndExit); + } + + // Reset timer on right click + if (ev.type == Common::EVENT_RBUTTONUP) { + if (getGlobalTimer()) { + if (getSound()->isBuffered("TIMER")) + getSound()->removeFromQueue("TIMER"); + + setGlobalTimer(900); + } + } + } + + getFlags()->shouldRedraw = true; +} + +void Fight::eventTick(const Common::Event &ev) { + handleTick(ev, true); +} + +void Fight::handleTick(const Common::Event &ev, bool isProcessing) { + // TODO move all the egg handling to inventory functions + + // Blink egg + if (getGlobalTimer()) { + warning("Fight::handleMouseMove - egg blinking not implemented!"); + } + + if (!_data || _data->index) + return; + + SceneHotspot *hotspot = NULL; + if (!getScenes()->get(getState()->scene)->checkHotSpot(ev.mouse, &hotspot) || !CALL_FUNCTION1(_data->player, canInteract, (FightAction)hotspot->action)) { + _engine->getCursor()->setStyle(kCursorNormal); + } else { + _engine->getCursor()->setStyle((CursorStyle)hotspot->cursor); + } + + CALL_FUNCTION0(_data->player, update); + CALL_FUNCTION0(_data->opponent, update); + + // Draw sequences + if (!_data->isRunning) + return; + + if (isProcessing) + getScenes()->drawFrames(true); + + if (_data->index) { + // Set next sequence name index + _data->index--; + _data->sequences[_data->index] = loadSequence(_data->names[_data->index]); + } +} + +////////////////////////////////////////////////////////////////////////// +// Setup +////////////////////////////////////////////////////////////////////////// + +Fight::FightEndType Fight::setup(FightType type) { + if (_data) + error("Fight::setup - calling fight setup again while a fight is already in progress!"); + + ////////////////////////////////////////////////////////////////////////// + // Prepare UI & state + if (_state >= 5 && (type == kFightSalko || type == kFightVesna)) { + _state = 0; + return kFightEndWin; + } + + getInventory()->showHourGlass(); + // TODO events function + getFlags()->flag_0 = false; + getFlags()->mouseRightClick = false; + getEntities()->reset(); + + // Compute scene to use + SceneIndex sceneIndex; + switch(type) { + default: + sceneIndex = kSceneFightDefault; + break; + + case kFightMilos: + sceneIndex = (getObjects()->get(kObjectCompartment1).location2 < kObjectLocation3) ? kSceneFightMilos : kSceneFightMilosBedOpened; + break; + + case kFightAnna: + sceneIndex = kSceneFightAnna; + break; + + case kFightIvo: + sceneIndex = kSceneFightIvo; + break; + + case kFightSalko: + sceneIndex = kSceneFightSalko; + break; + + case kFightVesna: + sceneIndex = kSceneFightVesna; + break; + } + + if (getFlags()->shouldRedraw) { + getFlags()->shouldRedraw = false; + askForRedraw(); + //redrawScreen(); + } + + // Load the scene object + Scene *scene = getScenes()->get(sceneIndex); + + // Update game entities and state + getEntityData(kEntityPlayer)->entityPosition = scene->entityPosition; + getEntityData(kEntityPlayer)->location = scene->location; + + getState()->scene = sceneIndex; + + getFlags()->flag_3 = true; + + // Draw the scene + _engine->getGraphicsManager()->draw(scene, GraphicsManager::kBackgroundC); + // FIXME move to start of fight? + askForRedraw(); + redrawScreen(); + + ////////////////////////////////////////////////////////////////////////// + // Setup the fight + _data = new FightData; + loadData(type); + + // Show opponents & egg button + Common::Event emptyEvent; + handleTick(emptyEvent, false); + getInventory()->drawEgg(); + + // Start fight + _endType = kFightEndLost; + while (_data->isRunning) { + if (_engine->handleEvents()) + continue; + + getSound()->updateQueue(); + } + + // Cleanup after fight is over + clearData(); + + return _endType; +} + +////////////////////////////////////////////////////////////////////////// +// Status +////////////////////////////////////////////////////////////////////////// + +void Fight::setStopped() { + if (_data) + _data->isRunning = false; +} + +void Fight::bailout(FightEndType type) { + _state = 0; + _endType = type; + setStopped(); +} + +////////////////////////////////////////////////////////////////////////// +// Cleanup +////////////////////////////////////////////////////////////////////////// + +void Fight::clearData() { + if (!_data) + return; + + // Clear data + clearSequences(_data->player); + clearSequences(_data->opponent); + + SAFE_DELETE(_data->player); + SAFE_DELETE(_data->opponent); + + SAFE_DELETE(_data); + + _engine->restoreEventHandlers(); +} + +void Fight::clearSequences(Fighter *combatant) const { + if (!combatant) + return; + + // The original game resets the function pointers to default values, just before deleting the struct + getScenes()->removeAndRedraw(&combatant->frame, false); + + // Free sequences + for (int i = 0; i < (int)combatant->sequences.size(); i++) + SAFE_DELETE(combatant->sequences[i]); +} + +////////////////////////////////////////////////////////////////////////// +// Drawing +////////////////////////////////////////////////////////////////////////// + +void Fight::setSequenceAndDraw(Fighter *combatant, uint32 sequenceIndex, FightSequenceType type) const { + if (combatant->sequences.size() < sequenceIndex) + return; + + switch (type) { + default: + break; + + case kFightSequenceType0: + if (combatant->sequenceIndex) + return; + + combatant->sequence = combatant->sequences[sequenceIndex]; + combatant->sequenceIndex = sequenceIndex; + draw(combatant); + break; + + case kFightSequenceType1: + combatant->sequence = combatant->sequences[sequenceIndex]; + combatant->sequenceIndex = sequenceIndex; + combatant->sequenceIndex2 = 0; + draw(combatant); + break; + + case kFightSequenceType2: + combatant->sequenceIndex2 = sequenceIndex; + break; + } +} + +void Fight::draw(Fighter *combatant) const { + getScenes()->removeAndRedraw(&combatant->frame, false); + + combatant->frameIndex = 0; + combatant->field_24 = 0; +} + +////////////////////////////////////////////////////////////////////////// +// Loading +////////////////////////////////////////////////////////////////////////// + +void Fight::loadData(FightType type) { + if (!_data) + error("Fight::loadData - invalid data!"); + + switch (type) { + default: + break; + + case kFightMilos: + loadMilosPlayer(); + loadMilosOpponent(); + break; + + case kFightAnna: + loadAnnaPlayer(); + loadAnnaOpponent(); + break; + + case kFightIvo: + loadIvoPlayer(); + loadIvoOpponent(); + break; + + case kFightSalko: + loadSalkoPlayer(); + loadSalkoOpponent(); + break; + + case kFightVesna: + loadVesnaPlayer(); + loadVesnaOpponent(); + break; + } + + if (!_data->player || !_data->opponent) + error("Fight::loadData - error loading fight data (type=%d)", type); + + ////////////////////////////////////////////////////////////////////////// + // Start running the fight + _data->isRunning = true; + + if (_state < 5) { + setSequenceAndDraw(_data->player, 0, kFightSequenceType0); + setSequenceAndDraw(_data->opponent, 0, kFightSequenceType0); + goto end_load; + } + + switch(type) { + default: + break; + + case kFightMilos: + _data->opponent->countdown = 1; + setSequenceAndDraw(_data->player, 4, kFightSequenceType0); + setSequenceAndDraw(_data->opponent, 0, kFightSequenceType0); + break; + + case kFightIvo: + _data->opponent->countdown = 1; + setSequenceAndDraw(_data->player, 3, kFightSequenceType0); + setSequenceAndDraw(_data->opponent, 6, kFightSequenceType0); + break; + + case kFightVesna: + _data->opponent->countdown = 1; + setSequenceAndDraw(_data->player, 0, kFightSequenceType0); + setSequenceAndDraw(_data->player, 3, kFightSequenceType2); + setSequenceAndDraw(_data->opponent, 5, kFightSequenceType0); + break; + } + +end_load: + // Setup event handlers + _engine->backupEventHandlers(); + SET_EVENT_HANDLERS(Fight, this); +} + +////////////////////////////////////////////////////////////////////////// +// Shared +////////////////////////////////////////////////////////////////////////// +void Fight::processFighter(Fighter *fighter) { + if (!_data) + error("Fight::processFighter - invalid data!"); + + if (!fighter->sequence) { + if (fighter->frame) { + getScenes()->removeFromQueue(fighter->frame); + getScenes()->setCoordinates(fighter->frame); + } + SAFE_DELETE(fighter->frame); + return; + } + + if (fighter->sequence->count() <= fighter->frameIndex) { + switch(fighter->action) { + default: + break; + + case kFightAction101: + setSequenceAndDraw(fighter, fighter->sequenceIndex2, kFightSequenceType1); + fighter->sequenceIndex2 = 0; + break; + + case kFightActionResetFrame: + fighter->frameIndex = 0; + break; + + case kFightAction103: + setSequenceAndDraw(fighter, 0, kFightSequenceType1); + CALL_FUNCTION1(fighter, handleAction, kFightAction101); + setSequenceAndDraw(fighter->opponent, 0, kFightSequenceType1); + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction101); + CALL_FUNCTION0(fighter->opponent, update); + break; + + case kFightActionWin: + bailout(kFightEndWin); + break; + + case kFightActionLost: + bailout(kFightEndLost); + break; + } + } + + if (_data->isRunning) { + + // Get the current sequence frame + SequenceFrame *frame = new SequenceFrame(fighter->sequence, (uint16)fighter->frameIndex); + frame->getInfo()->location = 1; + + if (fighter->frame == frame) { + delete frame; + return; + } + + getSound()->playFightSound(frame->getInfo()->soundAction, frame->getInfo()->field_31); + + // Add current frame to queue and advance + getScenes()->addToQueue(frame); + fighter->frameIndex++; + + if (fighter->frame) { + getScenes()->removeFromQueue(fighter->frame); + + if (!frame->getInfo()->field_2E) + getScenes()->setCoordinates(fighter->frame); + } + + // Replace by new frame + delete fighter->frame; + fighter->frame = frame; + } +} + +void Fight::handleAction(Fighter *fighter, FightAction action) { + switch (action) { + default: + return; + + case kFightAction101: + break; + + case kFightActionResetFrame: + fighter->countdown--; + break; + + case kFightAction103: + CALL_FUNCTION1(fighter->opponent, handleAction, kFightActionResetFrame); + break; + + case kFightActionWin: + _endType = kFightEndWin; + CALL_FUNCTION1(fighter->opponent, handleAction, kFightActionResetFrame); + break; + + case kFightActionLost: + _endType = kFightEndLost; + CALL_FUNCTION1(fighter->opponent, handleAction, kFightActionResetFrame); + break; + } + + // Update action + fighter->action = action; +} + +bool Fight::canInteract(Fighter const *fighter, FightAction /*= (FightAction)0*/ ) { + return (fighter->action == kFightAction101 && !fighter->sequenceIndex); +} + +void Fight::update(Fighter *fighter) { + + processFighter(fighter); + + if (fighter->frame) + fighter->frame->getInfo()->location = (fighter->action == kFightActionResetFrame ? 2 : 0); +} + +void Fight::updateOpponent(Fighter *fighter) { + + // This is an opponent struct! + Opponent *opponent = (Opponent *)fighter; + + processFighter(opponent); + + if (opponent->field_38 && !opponent->sequenceIndex) + opponent->field_38--; + + if (fighter->frame) + fighter->frame->getInfo()->location = 1; +} + +////////////////////////////////////////////////////////////////////////// +// Milos +////////////////////////////////////////////////////////////////////////// + +void Fight::loadMilosPlayer() { + REGISTER_PLAYER_FUNCTIONS(Milos) + + _data->player->sequences.push_back(loadSequence("2001cr.seq")); + _data->player->sequences.push_back(loadSequence("2001cdl.seq")); + _data->player->sequences.push_back(loadSequence("2001cdr.seq")); + _data->player->sequences.push_back(loadSequence("2001cdm.seq")); + _data->player->sequences.push_back(loadSequence("2001csgr.seq")); + _data->player->sequences.push_back(loadSequence("2001csgl.seq")); + _data->player->sequences.push_back(loadSequence("2001dbk.seq")); +} + +void Fight::loadMilosOpponent() { + REGISTER_OPPONENT_FUNCTIONS(Milos) + + _data->opponent->sequences.push_back(loadSequence("2001or.seq")); + _data->opponent->sequences.push_back(loadSequence("2001oal.seq")); + _data->opponent->sequences.push_back(loadSequence("2001oam.seq")); + _data->opponent->sequences.push_back(loadSequence("2001okl.seq")); + _data->opponent->sequences.push_back(loadSequence("2001okm.seq")); + _data->opponent->sequences.push_back(loadSequence("2001dbk.seq")); + _data->opponent->sequences.push_back(loadSequence("2001wbk.seq")); + + getSound()->playSound(kEntityTables0, "MUS027", SoundManager::kFlagDefault); + + _data->opponent->field_38 = 35; +} + +void Fight::handleActionMilos(Fighter *fighter, FightAction action) { + switch (action) { + default: + handleAction(fighter, action); + return; + + case kFightAction1: + if (fighter->sequenceIndex != 1 || CHECK_SEQUENCE2(fighter, 4)) { + setSequenceAndDraw(fighter, 6, kFightSequenceType1); + setSequenceAndDraw(fighter->opponent, 3, kFightSequenceType1); + + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103); + CALL_FUNCTION0(fighter, update); + } else { + fighter->field_34++; + } + break; + + case kFightAction2: + if ((fighter->sequenceIndex != 2 && fighter->sequenceIndex != 3) || CHECK_SEQUENCE2(fighter, 4)) { + setSequenceAndDraw(fighter, 6, kFightSequenceType1); + setSequenceAndDraw(fighter->opponent, 4, kFightSequenceType1); + + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103); + CALL_FUNCTION0(fighter, update); + } else { + fighter->field_34++; + } + break; + + case kFightAction128: + if (fighter->sequenceIndex != 1 || CHECK_SEQUENCE2(fighter, 4) || fighter->opponent->sequenceIndex != 1) { + switch (fighter->opponent->sequenceIndex) { + default: + setSequenceAndDraw(fighter, rnd(3) + 1, kFightSequenceType0); + break; + + case 1: + setSequenceAndDraw(fighter, 1, kFightSequenceType0); + break; + + case 2: + setSequenceAndDraw(fighter, 3, kFightSequenceType0); + break; + } + } else { + setSequenceAndDraw(fighter, 4, kFightSequenceType1); + CALL_FUNCTION0(fighter, update); + } + break; + } +} + +void Fight::updateMilos(Fighter *fighter) { + if (fighter->frame && CHECK_SEQUENCE2(fighter, 2)) { + + // Draw sequences + if (fighter->opponent->countdown <= 0) { + setSequenceAndDraw(fighter, 5, kFightSequenceType1); + setSequenceAndDraw(fighter->opponent, 6, kFightSequenceType1); + + getSound()->removeFromQueue(kEntityTables0); + getSound()->playSound(kEntityTrain, "MUS029", SoundManager::kFlagDefault); + + CALL_FUNCTION1(fighter, handleAction, kFightActionWin); + } + + if (fighter->sequenceIndex == 4) { + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction4); + _endType = kFightEndLost; + } + } + + update(fighter); +} + +bool Fight::canInteractMilos(Fighter const *fighter, FightAction action) { + if (!_data) + error("Fight::canInteractMilos - invalid data!"); + + if (action != kFightAction128 + || _data->player->sequenceIndex != 1 + || !fighter->frame + || CHECK_SEQUENCE2(fighter, 4) + || fighter->opponent->sequenceIndex != 1) { + return canInteract(fighter); + } + + _engine->getCursor()->setStyle(kCursorHand); + + return true; +} + +void Fight::handleOpponentActionMilos(Fighter *fighter, FightAction action) { + if (action == kFightAction4) { + setSequenceAndDraw(fighter, 5, kFightSequenceType1); + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103); + } else { + if (action != kFightAction131) + handleAction(fighter, action); + } +} + +void Fight::updateOpponentMilos(Fighter *fighter) { + // This is an opponent struct! + Opponent *opponent = (Opponent *)fighter; + + if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) { + + if (opponent->opponent->field_34 >= 2) { + switch (rnd(5)) { + default: + break; + + case 0: + setSequenceAndDraw(opponent, 1, kFightSequenceType0); + break; + + case 1: + setSequenceAndDraw(opponent, 2, kFightSequenceType0); + break; + + case 2: + setSequenceAndDraw(opponent, 2, kFightSequenceType0); + setSequenceAndDraw(opponent, 2, kFightSequenceType1); + break; + + case 3: + setSequenceAndDraw(opponent, 1, kFightSequenceType0); + setSequenceAndDraw(opponent, 2, kFightSequenceType2); + break; + + case 4: + setSequenceAndDraw(opponent, 1, kFightSequenceType0); + setSequenceAndDraw(opponent, 1, kFightSequenceType2); + break; + } + } else { + setSequenceAndDraw(opponent, 2, kFightSequenceType0); + } + + // Update field_38 + if (opponent->opponent->field_34 < 5) + opponent->field_38 = 6 * (5 - opponent->opponent->field_34); + else + opponent->field_38 = 0; + } + + if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) { + if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2) + CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex); + + if (opponent->opponent->countdown <= 0) { + getSound()->removeFromQueue(kEntityTables0); + CALL_FUNCTION1(opponent, handleAction, kFightActionLost); + } + } + + updateOpponent(opponent); +} + +////////////////////////////////////////////////////////////////////////// +// Anna +////////////////////////////////////////////////////////////////////////// + +void Fight::loadAnnaPlayer() { + if (!_data) + error("Fight::loadAnnaPlayer - invalid data!"); + + // Special case: we are using some shared functions directly + _data->player->handleAction = new Common::Functor2Mem<Fighter *, FightAction, void, Fight>(this, &Fight::handleActionAnna); + _data->player->update = new Common::Functor1Mem<Fighter *, void, Fight>(this, &Fight::update); + _data->player->canInteract = new Common::Functor2Mem<Fighter const *, FightAction, bool, Fight>(this, &Fight::canInteract); + + _data->player->sequences.push_back(loadSequence("2002cr.seq")); + _data->player->sequences.push_back(loadSequence("2002cdl.seq")); + _data->player->sequences.push_back(loadSequence("2002cdr.seq")); + _data->player->sequences.push_back(loadSequence("2002cdm.seq")); + _data->player->sequences.push_back(loadSequence("2002lbk.seq")); +} + +void Fight::loadAnnaOpponent() { + if (!_data) + error("Fight::loadAnnaOpponent - invalid data!"); + + // Special case: we are using some shared functions directly + _data->opponent->handleAction = new Common::Functor2Mem<Fighter *, FightAction, void, Fight>(this, &Fight::handleAction); + _data->opponent->update = new Common::Functor1Mem<Fighter *, void, Fight>(this, &Fight::updateOpponentAnna); + _data->opponent->canInteract = new Common::Functor2Mem<Fighter const *, FightAction, bool, Fight>(this, &Fight::canInteract); + + _data->opponent->sequences.push_back(loadSequence("2002or.seq")); + _data->opponent->sequences.push_back(loadSequence("2002oal.seq")); + _data->opponent->sequences.push_back(loadSequence("2002oam.seq")); + _data->opponent->sequences.push_back(loadSequence("2002oar.seq")); + _data->opponent->sequences.push_back(loadSequence("2002okr.seq")); + _data->opponent->sequences.push_back(loadSequence("2002okml.seq")); + _data->opponent->sequences.push_back(loadSequence("2002okm.seq")); + + getSound()->playSound(kEntityTables0, "MUS030", SoundManager::kFlagDefault); + + _data->opponent->field_38 = 30; +} + +void Fight::handleActionAnna(Fighter *fighter, FightAction action) { + switch (action) { + default: + handleAction(fighter, action); + return; + + case kFightAction1: + if ((fighter->sequenceIndex != 1 && fighter->sequenceIndex != 3) || CHECK_SEQUENCE2(fighter, 4)) { + setSequenceAndDraw(fighter, 4, kFightSequenceType1); + setSequenceAndDraw(fighter->opponent, 4, kFightSequenceType1); + + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103); + CALL_FUNCTION0(fighter, update); + } else { + fighter->field_34++; + } + break; + + case kFightAction2: + if ((fighter->sequenceIndex != 2 && fighter->sequenceIndex != 3) || CHECK_SEQUENCE2(fighter, 4)) { + setSequenceAndDraw(fighter, 4, kFightSequenceType1); + setSequenceAndDraw(fighter->opponent, 5, kFightSequenceType1); + + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103); + CALL_FUNCTION0(fighter, update); + } else { + fighter->field_34++; + } + break; + + case kFightAction3: + if ((fighter->sequenceIndex != 2 && fighter->sequenceIndex != 1) || CHECK_SEQUENCE2(fighter, 4)) { + setSequenceAndDraw(fighter, 4, kFightSequenceType1); + setSequenceAndDraw(fighter->opponent, 6, kFightSequenceType1); + + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103); + CALL_FUNCTION0(fighter, update); + } else { + fighter->field_34++; + } + break; + + case kFightAction128: + switch (fighter->opponent->sequenceIndex) { + default: + setSequenceAndDraw(fighter, 3, kFightSequenceType0); + break; + + case 1: + setSequenceAndDraw(fighter, 1, kFightSequenceType0); + break; + + case 2: + setSequenceAndDraw(fighter, 3, kFightSequenceType0); + break; + + case 3: + setSequenceAndDraw(fighter, 2, kFightSequenceType0); + break; + } + break; + } + + if (fighter->field_34 > 4) { + getSound()->removeFromQueue(kEntityTables0); + bailout(kFightEndWin); + } +} + +void Fight::updateOpponentAnna(Fighter *fighter) { + // This is an opponent struct! + Opponent *opponent = (Opponent *)fighter; + + if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) { + + if (opponent->opponent->field_34 >= 2) { + switch (rnd(6)) { + default: + break; + + case 0: + setSequenceAndDraw(opponent, 1, kFightSequenceType0); + break; + + case 1: + setSequenceAndDraw(opponent, 2, kFightSequenceType0); + break; + + case 2: + setSequenceAndDraw(opponent, 3, kFightSequenceType0); + break; + + case 3: + setSequenceAndDraw(opponent, 3, kFightSequenceType0); + setSequenceAndDraw(opponent, 2, kFightSequenceType2); + break; + + case 4: + setSequenceAndDraw(opponent, 1, kFightSequenceType0); + setSequenceAndDraw(opponent, 2, kFightSequenceType2); + break; + + case 5: + setSequenceAndDraw(opponent, 3, kFightSequenceType0); + setSequenceAndDraw(opponent, 2, kFightSequenceType2); + break; + } + } + + // Update field_38 + opponent->field_38 = (int32)rnd(15); + } + + if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) { + if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2 || opponent->sequenceIndex == 3) + CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex); + + if (opponent->opponent->countdown <= 0) { + getSound()->removeFromQueue(kEntityTables0); + CALL_FUNCTION1(opponent, handleAction, kFightActionLost); + } + } + + updateOpponent(opponent); +} + +////////////////////////////////////////////////////////////////////////// +// Ivo +////////////////////////////////////////////////////////////////////////// + +void Fight::loadIvoPlayer() { + REGISTER_PLAYER_FUNCTIONS(Ivo) + + _data->player->sequences.push_back(loadSequence("2003cr.seq")); + _data->player->sequences.push_back(loadSequence("2003car.seq")); + _data->player->sequences.push_back(loadSequence("2003cal.seq")); + _data->player->sequences.push_back(loadSequence("2003cdr.seq")); + _data->player->sequences.push_back(loadSequence("2003cdm.seq")); + _data->player->sequences.push_back(loadSequence("2003chr.seq")); + _data->player->sequences.push_back(loadSequence("2003chl.seq")); + _data->player->sequences.push_back(loadSequence("2003ckr.seq")); + _data->player->sequences.push_back(loadSequence("2003lbk.seq")); + _data->player->sequences.push_back(loadSequence("2003fbk.seq")); + + _data->player->countdown = 5; +} + +void Fight::loadIvoOpponent() { + REGISTER_OPPONENT_FUNCTIONS(Ivo) + + _data->opponent->sequences.push_back(loadSequence("2003or.seq")); + _data->opponent->sequences.push_back(loadSequence("2003oal.seq")); + _data->opponent->sequences.push_back(loadSequence("2003oar.seq")); + _data->opponent->sequences.push_back(loadSequence("2003odm.seq")); + _data->opponent->sequences.push_back(loadSequence("2003okl.seq")); + _data->opponent->sequences.push_back(loadSequence("2003okj.seq")); + _data->opponent->sequences.push_back(loadSequence("blank.seq")); + _data->opponent->sequences.push_back(loadSequence("csdr.seq")); + _data->opponent->sequences.push_back(loadSequence("2003l.seq")); + + getSound()->playSound(kEntityTables0, "MUS032", SoundManager::kFlagDefault); + + _data->opponent->countdown = 5; + _data->opponent->field_38 = 15; +} + +void Fight::handleActionIvo(Fighter *fighter, FightAction action) { + switch (action) { + default: + handleAction(fighter, action); + return; + + case kFightAction1: + if (fighter->sequenceIndex != 1 || CHECK_SEQUENCE2(fighter, 4)) { + setSequenceAndDraw(fighter, 7, kFightSequenceType1); + setSequenceAndDraw(fighter->opponent, 4, kFightSequenceType1); + + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103); + CALL_FUNCTION0(fighter, update); + } + break; + + case kFightAction2: + if ((fighter->sequenceIndex != 2 && fighter->sequenceIndex != 3) || CHECK_SEQUENCE2(fighter, 4)) { + setSequenceAndDraw(fighter, 7, kFightSequenceType1); + setSequenceAndDraw(fighter->opponent, 5, kFightSequenceType1); + + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103); + CALL_FUNCTION0(fighter, update); + } + break; + + case kFightAction128: + switch (fighter->opponent->sequenceIndex) { + default: + case 1: + setSequenceAndDraw(fighter, 1, kFightSequenceType0); + break; + + case 2: + setSequenceAndDraw(fighter, 2, kFightSequenceType0); + break; + } + break; + + case kFightAction129: + setSequenceAndDraw(fighter, (fighter->opponent->countdown > 1) ? 4 : 3, fighter->sequenceIndex ? kFightSequenceType2 : kFightSequenceType0); + break; + + case kFightAction130: + setSequenceAndDraw(fighter, 3, fighter->sequenceIndex ? kFightSequenceType2 : kFightSequenceType0); + break; + } +} + +void Fight::updateIvo(Fighter *fighter) { + + if ((fighter->sequenceIndex == 3 || fighter->sequenceIndex == 4) && !fighter->frameIndex) + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction131); + + if (fighter->frame && CHECK_SEQUENCE2(fighter, 2)) { + + // Draw sequences + if (fighter->opponent->countdown <= 0) { + setSequenceAndDraw(fighter, 9, kFightSequenceType1); + setSequenceAndDraw(fighter->opponent, 8, kFightSequenceType1); + getSound()->removeFromQueue(kEntityTables0); + + CALL_FUNCTION1(fighter, handleAction, kFightActionWin); + return; + } + + if (fighter->sequenceIndex == 3 || fighter->sequenceIndex == 4) + CALL_FUNCTION1(fighter->opponent, handleAction, (FightAction)fighter->sequenceIndex); + } + + update(fighter); +} + +bool Fight::canInteractIvo(Fighter const *fighter, FightAction action) { + if (action == kFightAction129 || action == kFightAction130) + return (fighter->sequenceIndex >= 8); + + return canInteract(fighter); +} + +void Fight::handleOpponentActionIvo(Fighter *fighter, FightAction action) { + // This is an opponent struct! + Opponent *opponent = (Opponent *)fighter; + + switch (action) { + default: + handleAction(fighter, action); + break; + + case kFightAction3: + if ((opponent->sequenceIndex != 1 && opponent->sequenceIndex != 3) || CHECK_SEQUENCE2(opponent, 4)) { + setSequenceAndDraw(opponent, 6, kFightSequenceType1); + setSequenceAndDraw(opponent->opponent, 6, kFightSequenceType1); + CALL_FUNCTION1(opponent->opponent, handleAction, kFightAction103); + } + break; + + case kFightAction4: + if ((opponent->sequenceIndex != 2 && opponent->sequenceIndex != 3) || CHECK_SEQUENCE2(opponent, 4)) { + setSequenceAndDraw(opponent, 6, kFightSequenceType1); + setSequenceAndDraw(opponent->opponent, 5, kFightSequenceType1); + CALL_FUNCTION1(opponent->opponent, handleAction, kFightAction103); + } + break; + + case kFightAction131: + if (opponent->sequenceIndex) + break; + + if (rnd(100) <= (unsigned int)(opponent->countdown > 2 ? 60 : 75)) { + setSequenceAndDraw(opponent, 3 , kFightSequenceType1); + if (opponent->opponent->sequenceIndex == 4) + setSequenceAndDraw(opponent, 2, kFightSequenceType2); + } + break; + } +} + +void Fight::updateOpponentIvo(Fighter *fighter) { + // This is an opponent struct! + Opponent *opponent = (Opponent *)fighter; + + if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) { + + if (opponent->opponent->field_34 >= 2) { + switch (rnd(5)) { + default: + break; + + case 0: + setSequenceAndDraw(opponent, 1, kFightSequenceType0); + break; + + case 1: + setSequenceAndDraw(opponent, 2, kFightSequenceType0); + break; + + case 2: + setSequenceAndDraw(opponent, 1, kFightSequenceType0); + setSequenceAndDraw(opponent, 2, kFightSequenceType2); + break; + + case 3: + setSequenceAndDraw(opponent, 0, kFightSequenceType2); + setSequenceAndDraw(opponent, 1, kFightSequenceType2); + break; + + case 4: + setSequenceAndDraw(opponent, 0, kFightSequenceType1); + setSequenceAndDraw(opponent, 1, kFightSequenceType2); + break; + } + } + + // Update field_38 + opponent->field_38 = 3 * opponent->countdown + (int32)rnd(10); + } + + if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) { + + if (opponent->opponent->countdown <= 0) { + setSequenceAndDraw(opponent, 7, kFightSequenceType1); + setSequenceAndDraw(opponent->opponent, 8, kFightSequenceType1); + getSound()->removeFromQueue(kEntityTables0); + + CALL_FUNCTION1(opponent->opponent, handleAction, kFightActionWin); + + return; + } + + if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2) + CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex); + } + + updateOpponent(opponent); +} + +////////////////////////////////////////////////////////////////////////// +// Salko +////////////////////////////////////////////////////////////////////////// + +void Fight::loadSalkoPlayer() { + REGISTER_PLAYER_FUNCTIONS(Salko) + + _data->player->sequences.push_back(loadSequence("2004cr.seq")); + _data->player->sequences.push_back(loadSequence("2004cdr.seq")); + _data->player->sequences.push_back(loadSequence("2004chj.seq")); + _data->player->sequences.push_back(loadSequence("2004bk.seq")); + + _data->player->countdown = 2; +} + +void Fight::loadSalkoOpponent() { + REGISTER_OPPONENT_FUNCTIONS(Salko) + + _data->opponent->sequences.push_back(loadSequence("2004or.seq")); + _data->opponent->sequences.push_back(loadSequence("2004oam.seq")); + _data->opponent->sequences.push_back(loadSequence("2004oar.seq")); + _data->opponent->sequences.push_back(loadSequence("2004okr.seq")); + _data->opponent->sequences.push_back(loadSequence("2004ohm.seq")); + _data->opponent->sequences.push_back(loadSequence("blank.seq")); + + getSound()->playSound(kEntityTables0, "MUS035", SoundManager::kFlagDefault); + + _data->opponent->countdown = 3; + _data->opponent->field_38 = 30; +} + +void Fight::handleActionSalko(Fighter *fighter, FightAction action) { + switch (action) { + default: + handleAction(fighter, action); + return; + + case kFightAction1: + case kFightAction2: + if (fighter->sequenceIndex != 1 && CHECK_SEQUENCE2(fighter, 4)) { + fighter->field_34 = 0; + + setSequenceAndDraw(fighter, 3, kFightSequenceType1); + setSequenceAndDraw(fighter->opponent, (action == kFightAction1 ? 3 : 4), kFightSequenceType1); + + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103); + + if (action == kFightAction2) + fighter->countdown= 0; + + CALL_FUNCTION0(fighter, update); + } else { + fighter->field_34++; + } + break; + + case kFightAction5: + if (fighter->sequenceIndex != 3) { + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103); + CALL_FUNCTION0(fighter, update); + } + break; + + case kFightAction128: + setSequenceAndDraw(fighter, 1, kFightSequenceType0); + fighter->field_34 = 0; + break; + + case kFightAction131: + setSequenceAndDraw(fighter, 2, (fighter->sequenceIndex ? kFightSequenceType2 : kFightSequenceType0)); + break; + } +} + +void Fight::updateSalko(Fighter *fighter) { + update(fighter); + + // The original doesn't check for currentSequence2 != NULL (might not happen when everything is working properly, but crashes with our current implementation) + if (fighter->frame && CHECK_SEQUENCE2(fighter, 2)) { + + if (fighter->opponent->countdown <= 0) { + getSound()->removeFromQueue(kEntityTables0); + bailout(kFightEndWin); + + return; + } + + if (fighter->sequenceIndex == 2) + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction2); + } +} + +bool Fight::canInteractSalko(Fighter const *fighter, FightAction action) { + if (action == kFightAction131) { + if (fighter->sequenceIndex == 1) { + if (fighter->opponent->countdown <= 0) + _engine->getCursor()->setStyle(kCursorHand); + + return true; + } + + return false; + } + + return canInteract(fighter); +} + +void Fight::handleOpponentActionSalko(Fighter *fighter, FightAction action) { + if (action == kFightAction2) { + setSequenceAndDraw(fighter, 5, kFightSequenceType1); + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103); + } else { + handleAction(fighter, action); + } +} + +void Fight::updateOpponentSalko(Fighter *fighter) { + // This is an opponent struct + Opponent *opponent = (Opponent *)fighter; + + if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) { + + switch (rnd(5)) { + default: + break; + + case 0: + setSequenceAndDraw(opponent, 1, kFightSequenceType0); + break; + + case 1: + setSequenceAndDraw(opponent, 2, kFightSequenceType0); + break; + + case 2: + setSequenceAndDraw(opponent, 1, kFightSequenceType0); + setSequenceAndDraw(opponent, 2, kFightSequenceType2); + break; + + case 3: + setSequenceAndDraw(opponent, 2, kFightSequenceType0); + setSequenceAndDraw(opponent, 1, kFightSequenceType2); + break; + + case 4: + setSequenceAndDraw(opponent, 1, kFightSequenceType0); + setSequenceAndDraw(opponent, 1, kFightSequenceType2); + break; + } + + // Update field_38 + opponent->field_38 = 4 * opponent->countdown; + } + + if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) { + if (opponent->opponent->countdown <= 0) { + getSound()->removeFromQueue(kEntityTables0); + bailout(kFightEndLost); + + // Stop processing + return; + } + + if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2) + CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex); + } + + updateOpponent(opponent); +} + +////////////////////////////////////////////////////////////////////////// +// Vesna +////////////////////////////////////////////////////////////////////////// + +void Fight::loadVesnaPlayer() { + REGISTER_PLAYER_FUNCTIONS(Vesna) + + _data->player->sequences.push_back(loadSequence("2005cr.seq")); + _data->player->sequences.push_back(loadSequence("2005cdr.seq")); + _data->player->sequences.push_back(loadSequence("2005cbr.seq")); + _data->player->sequences.push_back(loadSequence("2005bk.seq")); + _data->player->sequences.push_back(loadSequence("2005cdm1.seq")); + _data->player->sequences.push_back(loadSequence("2005chl.seq")); +} + +void Fight::loadVesnaOpponent() { + REGISTER_OPPONENT_FUNCTIONS(Vesna) + + _data->opponent->sequences.push_back(loadSequence("2005or.seq")); + _data->opponent->sequences.push_back(loadSequence("2005oam.seq")); + _data->opponent->sequences.push_back(loadSequence("2005oar.seq")); + _data->opponent->sequences.push_back(loadSequence("2005okml.seq")); + _data->opponent->sequences.push_back(loadSequence("2005okr.seq")); + _data->opponent->sequences.push_back(loadSequence("2005odm1.seq")); + _data->opponent->sequences.push_back(loadSequence("2005csbm.seq")); + _data->opponent->sequences.push_back(loadSequence("2005oam4.seq")); + + getSound()->playSound(kEntityTables0, "MUS038", SoundManager::kFlagDefault); + + _data->opponent->countdown = 4; + _data->opponent->field_38 = 30; +} + +void Fight::handleActionVesna(Fighter *fighter, FightAction action) { + switch (action) { + default: + handleAction(fighter, action); + return; + + case kFightAction1: + if (fighter->sequenceIndex != 1) { + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103); + CALL_FUNCTION0(fighter, update); + } else { + fighter->field_34++; + } + break; + + case kFightAction2: + if (fighter->sequenceIndex != 2) { + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103); + CALL_FUNCTION0(fighter, update); + } else { + fighter->field_34++; + } + break; + + case kFightAction5: + if (fighter->sequenceIndex != 3) { + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103); + CALL_FUNCTION0(fighter, update); + } + break; + + case kFightAction128: + if (fighter->sequenceIndex == 1 && fighter->opponent->sequenceIndex == 1 && CHECK_SEQUENCE2(fighter, 4)) { + setSequenceAndDraw(fighter, 5, kFightSequenceType1); + } else { + setSequenceAndDraw(fighter, (fighter->opponent->sequenceIndex == 5) ? 3 : 1, kFightSequenceType0); + } + break; + + case kFightAction132: + setSequenceAndDraw(fighter, 2, kFightSequenceType0); + break; + } + + if (fighter->field_34 > 10) { + setSequenceAndDraw(fighter->opponent, 5, kFightSequenceType2); + fighter->opponent->countdown = 1; + fighter->field_34 = 0; + } +} + +void Fight::updateVesna(Fighter *fighter) { + if (fighter->frame && CHECK_SEQUENCE2(fighter, 2)) { + + if (fighter->sequenceIndex == 3) + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction3); + + if (fighter->opponent->countdown <= 0) { + getSound()->removeFromQueue(kEntityTables0); + bailout(kFightEndWin); + return; + } + + if (fighter->sequenceIndex == 5) + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction5); + } + + update(fighter); +} + +bool Fight::canInteractVesna(Fighter const *fighter, FightAction action) { + if (action != kFightAction128) + return canInteract(fighter); + + if (fighter->sequenceIndex != 1) { + + if (fighter->opponent->sequenceIndex == 5) { + _engine->getCursor()->setStyle(kCursorDown); + return true; + } + + return canInteract(fighter); + } + + if (fighter->opponent->sequenceIndex == 1 && CHECK_SEQUENCE2(fighter, 4)) { + _engine->getCursor()->setStyle(kCursorPunchLeft); + return true; + } + + return false; +} + +void Fight::handleOpponentActionVesna(Fighter *fighter, FightAction action) { + switch (action) { + default: + handleAction(fighter, action); + break; + + case kFightAction3: + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103); + break; + + case kFightAction5: + setSequenceAndDraw(fighter, 7, kFightSequenceType1); + CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103); + if (fighter->countdown <= 1) + fighter->countdown = 1; + break; + + case kFightAction131: + break; + } +} + +void Fight::updateOpponentVesna(Fighter *fighter) { + // This is an opponent struct + Opponent *opponent = (Opponent *)fighter; + + if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) { + + if (opponent->opponent->field_34 == 1) { + setSequenceAndDraw(opponent, 2, kFightSequenceType0); + } else { + switch (rnd(6)) { + default: + break; + + case 0: + setSequenceAndDraw(opponent, 1, kFightSequenceType0); + break; + + case 1: + setSequenceAndDraw(opponent, 1, kFightSequenceType0); + setSequenceAndDraw(opponent, 1, kFightSequenceType2); + break; + + case 2: + setSequenceAndDraw(opponent, 2, kFightSequenceType0); + break; + + case 3: + setSequenceAndDraw(opponent, 2, kFightSequenceType0); + setSequenceAndDraw(opponent, 2, kFightSequenceType2); + break; + + case 4: + setSequenceAndDraw(opponent, 1, kFightSequenceType0); + setSequenceAndDraw(opponent, 2, kFightSequenceType2); + break; + + case 5: + setSequenceAndDraw(opponent, 2, kFightSequenceType0); + setSequenceAndDraw(opponent, 1, kFightSequenceType2); + break; + } + } + + // Update field_38 + opponent->field_38 = 4 * opponent->countdown; + } + + if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) { + if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2 || opponent->sequenceIndex == 5) + CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex); + + if (opponent->opponent->countdown <= 0) { + + switch (opponent->sequenceIndex) { + default: + break; + + case 1: + setSequenceAndDraw(opponent, 3, kFightSequenceType1); + break; + + case 2: + setSequenceAndDraw(opponent, 4, kFightSequenceType1); + break; + + case 5: + setSequenceAndDraw(opponent, 6, kFightSequenceType1); + break; + } + + setSequenceAndDraw(opponent->opponent, 4, kFightSequenceType1); + + CALL_FUNCTION1(opponent, handleAction, kFightActionLost); + CALL_FUNCTION0(opponent->opponent, update); + CALL_FUNCTION0(opponent, update); + + getSound()->removeFromQueue(kEntityTables0); + + // Stop processing + return; + } + } + + updateOpponent(opponent); +} + +} // End of namespace LastExpress diff --git a/engines/lastexpress/fight/fight.h b/engines/lastexpress/fight/fight.h new file mode 100644 index 0000000000..a33cc93a29 --- /dev/null +++ b/engines/lastexpress/fight/fight.h @@ -0,0 +1,266 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef LASTEXPRESS_FIGHT_H +#define LASTEXPRESS_FIGHT_H + +/* + Fight structure + --------------- + uint32 {4} - player struct + uint32 {4} - opponent struct + uint32 {4} - hasLost flag + + byte {1} - isRunning + + Fight participant structure + --------------------------- + uint32 {4} - function pointer + uint32 {4} - pointer to fight structure + uint32 {4} - pointer to opponent (fight participant structure) + uint32 {4} - array of sequences + uint32 {4} - number of sequences + uint32 {4} - ?? + uint32 {4} - ?? + uint32 {4} - ?? + uint32 {4} - ?? + uint32 {4} - ?? + uint32 {4} - ?? + uint32 {4} - ?? + uint32 {4} - ?? + uint16 {2} - ?? + uint16 {2} - ?? - only for opponent structure + uint32 {4} - ?? - only for opponent structure + +*/ + +#include "lastexpress/shared.h" + +#include "lastexpress/eventhandler.h" + +#include "common/array.h" + +namespace LastExpress { + +class LastExpressEngine; +class Sequence; +class SequenceFrame; + +////////////////////////////////////////////////////////////////////////// +// TODO : objectify! +class Fight : public EventHandler { +public: + enum FightEndType { + kFightEndWin = 0, + kFightEndLost = 1, + kFightEndExit = 2 + }; + + Fight(LastExpressEngine *engine); + ~Fight(); + + FightEndType setup(FightType type); + + void eventMouse(const Common::Event &ev); + void eventTick(const Common::Event &ev); + + void setStopped(); + void resetState() { _state = 0; } + +private: + enum FightSequenceType { + kFightSequenceType0 = 0, + kFightSequenceType1 = 1, + kFightSequenceType2 = 2 + }; + + enum FightAction { + kFightAction1 = 1, + kFightAction2 = 2, + kFightAction3 = 3, + kFightAction4 = 4, + kFightAction5 = 5, + kFightAction101 = 101, + kFightActionResetFrame = 102, + kFightAction103 = 103, + kFightActionWin = 104, + kFightActionLost = 105, + kFightAction128 = 128, + kFightAction129 = 129, + kFightAction130 = 130, + kFightAction131 = 131, + kFightAction132 = 132 + }; + + struct Fighter { + Common::Functor2<Fighter *, FightAction, void> *handleAction; + Common::Functor1<Fighter *, void> *update; + Common::Functor2<Fighter const *, FightAction, bool> *canInteract; + Fighter *opponent; + Common::Array<Sequence *> sequences; + uint32 sequenceIndex; + Sequence *sequence; + SequenceFrame *frame; + uint32 frameIndex; + uint32 field_24; + FightAction action; + uint32 sequenceIndex2; + int32 countdown; // countdown before loosing ? + uint32 field_34; + + Fighter() { + handleAction = NULL; + update = NULL; + canInteract = NULL; + + opponent = NULL; + + sequenceIndex = 0; + sequence = NULL; + frame = NULL; + frameIndex = 0; + + field_24 = 0; + + action = kFightAction101; + sequenceIndex2 = 0; + + countdown = 1; + + field_34 = 0; + } + }; + + // Opponent struct + struct Opponent : Fighter { + int32 field_38; + + Opponent() : Fighter() { + field_38 = 0; + } + }; + + struct FightData { + Fighter *player; + Opponent *opponent; + int32 index; + + Sequence *sequences[20]; + Common::String names[20]; + + bool isRunning; + + FightData() { + player = new Fighter(); + opponent = new Opponent(); + + // Set opponents + player->opponent = opponent; + opponent->opponent = player; + + index = 0; + + isRunning = false; + } + }; + + LastExpressEngine *_engine; + FightData *_data; + FightEndType _endType; + int _state; + + bool _handleTimer; + + // Events + void handleTick(const Common::Event &ev, bool unknown); + + // State + void bailout(FightEndType type); + + + // Drawing + void setSequenceAndDraw(Fighter *fighter, uint32 sequenceIndex, FightSequenceType type) const; + void draw(Fighter *fighter) const; + + // Cleanup + void clearData(); + void clearSequences(Fighter *fighter) const; + + ////////////////////////////////////////////////////////////////////////// + // Loading + void loadData(FightType type); + + // Shared + void processFighter(Fighter *fighter); + + // Default functions + void handleAction(Fighter *fighter, FightAction action); + void update(Fighter *fighter); + bool canInteract(Fighter const *fighter, FightAction = (FightAction)0); + void updateOpponent(Fighter *fighter); + + // Milos + void loadMilosPlayer(); + void loadMilosOpponent(); + void handleActionMilos(Fighter *fighter, FightAction action); + void updateMilos(Fighter *fighter); + bool canInteractMilos(Fighter const *fighter, FightAction action); + void handleOpponentActionMilos(Fighter *fighter, FightAction action); + void updateOpponentMilos(Fighter *fighter); + + // Anna + void loadAnnaPlayer(); + void loadAnnaOpponent(); + void handleActionAnna(Fighter *fighter, FightAction action); + void updateOpponentAnna(Fighter *fighter); + + // Ivo + void loadIvoPlayer(); + void loadIvoOpponent(); + void handleActionIvo(Fighter *fighter, FightAction action); + void updateIvo(Fighter *fighter); + bool canInteractIvo(Fighter const *fighter, FightAction action); + void handleOpponentActionIvo(Fighter *fighter, FightAction action); + void updateOpponentIvo(Fighter *fighter); + + // Salko + void loadSalkoPlayer(); + void loadSalkoOpponent(); + void handleActionSalko(Fighter *fighter, FightAction action); + void updateSalko(Fighter *fighter); + bool canInteractSalko(Fighter const *fighter, FightAction action); + void handleOpponentActionSalko(Fighter *fighter, FightAction action); + void updateOpponentSalko(Fighter *fighter); + + // Vesna + void loadVesnaPlayer(); + void loadVesnaOpponent(); + void handleActionVesna(Fighter *fighter, FightAction action); + void updateVesna(Fighter *fighter); + bool canInteractVesna(Fighter const *fighter, FightAction action); + void handleOpponentActionVesna(Fighter *fighter, FightAction action); + void updateOpponentVesna(Fighter *fighter); +}; + +} // End of namespace LastExpress + +#endif // LASTEXPRESS_FIGHT_H |