scummvm/engines/lastexpress/game/fight.cpp

1587 lines
44 KiB
C++
Raw Normal View History

/* 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.
*
* $URL$
* $Id$
*
*/
#include "lastexpress/game/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()->drawEgg();
_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()->drawEgg();
_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