aboutsummaryrefslogtreecommitdiff
path: root/engines/lastexpress/fight
diff options
context:
space:
mode:
authorLittleboy2011-06-21 23:50:01 -0400
committerLittleboy2011-06-22 19:31:30 -0400
commit3c2a9b292f5a851529fb0196eab662fd9996a459 (patch)
treec6ab5fbb47fe5d5d75b0e0a2ed680bbf445a65a4 /engines/lastexpress/fight
parent8e42ee4c983167f0e2589b1401ba90a5df99fcc6 (diff)
downloadscummvm-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.cpp1583
-rw-r--r--engines/lastexpress/fight/fight.h266
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