aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/xeen/character.cpp18
-rw-r--r--engines/xeen/character.h10
-rw-r--r--engines/xeen/combat.cpp62
-rw-r--r--engines/xeen/combat.h4
-rw-r--r--engines/xeen/dialogs_error.cpp30
-rw-r--r--engines/xeen/dialogs_error.h11
-rw-r--r--engines/xeen/dialogs_spells.cpp142
-rw-r--r--engines/xeen/dialogs_spells.h15
-rw-r--r--engines/xeen/interface.cpp31
-rw-r--r--engines/xeen/resources.cpp15
-rw-r--r--engines/xeen/resources.h5
-rw-r--r--engines/xeen/spells.cpp31
-rw-r--r--engines/xeen/spells.h5
13 files changed, 356 insertions, 23 deletions
diff --git a/engines/xeen/character.cpp b/engines/xeen/character.cpp
index afe385448a..c91b860640 100644
--- a/engines/xeen/character.cpp
+++ b/engines/xeen/character.cpp
@@ -707,7 +707,7 @@ void Character::clear() {
_lloydMap = 0;
_hasSpells = false;
_currentSpell = 0;
- _quickOption = 0;
+ _quickOption = QUICK_ATTACK;
_lloydSide = 0;
Common::fill(&_conditions[0], &_conditions[16], 0);
_townUnknown = 0;
@@ -1822,4 +1822,20 @@ bool Character::hasSpecialItem() const {
return false;
}
+int Character::getClassCategory() const {
+ switch (_class) {
+ case CLASS_ARCHER:
+ case CLASS_SORCERER:
+ return 1;
+
+ case CLASS_DRUID:
+ case CLASS_RANGER:
+ return 2;
+
+ default:
+ return 0;
+ }
+}
+
+
} // End of namespace Xeen
diff --git a/engines/xeen/character.h b/engines/xeen/character.h
index be38ba5da1..bf77bef315 100644
--- a/engines/xeen/character.h
+++ b/engines/xeen/character.h
@@ -85,6 +85,10 @@ enum AttributeCategory {
ATTR_ARMOR_CLASS = 8, ATTR_THIEVERY = 9
};
+enum QuickAction {
+ QUICK_ATTACK = 0, QUICK_SPELL = 1, QUICK_BLOCK = 2, QUICK_RUN = 3
+};
+
class XeenEngine;
class Character;
@@ -235,8 +239,8 @@ public:
int _lloydMap;
Common::Point _lloydPosition;
bool _hasSpells;
- int _currentSpell;
- int _quickOption;
+ int8 _currentSpell;
+ QuickAction _quickOption;
InventoryItemsGroup _items;
WeaponItems _weapons;
ArmorItems _armor;
@@ -321,6 +325,8 @@ public:
void subtractHitPoints(int amount);
bool hasSpecialItem() const;
+
+ int getClassCategory() const;
};
} // End of namespace Xeen
diff --git a/engines/xeen/combat.cpp b/engines/xeen/combat.cpp
index c8f5824db9..b77ef6e95d 100644
--- a/engines/xeen/combat.cpp
+++ b/engines/xeen/combat.cpp
@@ -100,6 +100,8 @@ Combat::Combat(XeenEngine *vm): _vm(vm) {
_partyRan = false;
_monster2Attack = -1;
_whosSpeed = 0;
+ _damageType = DT_PHYSICAL;
+ _oldCharacter = nullptr;
}
void Combat::clear() {
@@ -658,17 +660,21 @@ bool Combat::allHaveGone() const {
return true;
}
+/**
+ * Returns true if all the characters of the party are disabled
+ */
bool Combat::charsCantAct() const {
for (uint idx = 0; idx < _combatParty.size(); ++idx) {
- Condition condition = _combatParty[idx]->worstCondition();
-
- if (!(condition == ASLEEP || (condition >= PARALYZED && condition != NO_CONDITION)))
+ if (!_combatParty[idx]->isDisabledOrDead())
return false;
}
return true;
}
+/**
+ * Return a description of the monsters being faced
+ */
Common::String Combat::getMonsterDescriptions() {
Map &map = *_vm->_map;
Common::String lines[3];
@@ -702,25 +708,61 @@ void Combat::attack(Character &c, int v2) {
error("TODO");
}
+/**
+ * Flag the currently active character as blocking/defending
+ */
void Combat::block() {
_charsBlocked[_whosTurn] = true;
}
-bool Combat::castSpell(bool flag) {
- error("TODO: castSpell");
-}
-
+/**
+ * Perform whatever the current combat character's quick action is
+ */
void Combat::quickFight() {
- error("TODO: quickFight");
+ Spells &spells = *_vm->_spells;
+ Character *c = _combatParty[_whosTurn];
+ int spellId;
+
+ switch (c->_quickOption) {
+ case QUICK_ATTACK:
+ attack(*c, 0);
+ break;
+ case QUICK_SPELL:
+ if (c->_currentSpell != -1) {
+ spells.castSpell(SPELLS_ALLOWED[c->getClassCategory()][c->_currentSpell]);
+ }
+ break;
+ case QUICK_BLOCK:
+ block();
+ break;
+ case QUICK_RUN:
+ run();
+ break;
+ default:
+ break;
+ }
}
void Combat::giveTreasure() {
error("TODO: giveTreasure");
}
+/**
+ * Current selected character is trying to run away
+ */
void Combat::run() {
- error("TODO: run");
-}
+ Map &map = *_vm->_map;
+ SoundManager &sound = *_vm->_sound;
+ if (_vm->getRandomNumber(1, 100) < map.mazeData()._difficulties._chance2Run) {
+ // Remove the character from the combat party
+ _combatParty.remove_at(_whosTurn);
+ setSpeedTable();
+ --_whosSpeed;
+ _whosTurn = -1;
+ _partyRan = true;
+ sound.playFX(51);
+ }
+}
} // End of namespace Xeen
diff --git a/engines/xeen/combat.h b/engines/xeen/combat.h
index dee15edf6c..5ff6beb16b 100644
--- a/engines/xeen/combat.h
+++ b/engines/xeen/combat.h
@@ -81,6 +81,8 @@ public:
int _monsterIndex;
bool _partyRan;
int _whosSpeed;
+ DamageType _damageType;
+ Character *_oldCharacter;
void monstersAttack();
@@ -121,8 +123,6 @@ public:
void block();
- bool castSpell(bool flag);
-
void quickFight();
void giveTreasure();
diff --git a/engines/xeen/dialogs_error.cpp b/engines/xeen/dialogs_error.cpp
index db2603ab87..7f649afd86 100644
--- a/engines/xeen/dialogs_error.cpp
+++ b/engines/xeen/dialogs_error.cpp
@@ -82,4 +82,34 @@ void ErrorScroll::show(XeenEngine *vm, const Common::String &msg, ErrorWaitType
ErrorDialog::show(vm, s, waitType);
}
+/*------------------------------------------------------------------------*/
+
+void CantCast::show(XeenEngine *vm, int spellId, int componentNum) {
+ CantCast *dlg = new CantCast(vm);
+ dlg->execute(spellId, componentNum);
+ delete dlg;
+}
+
+void CantCast::execute(int spellId, int componentNum) {
+ EventsManager &events = *_vm->_events;
+ SoundManager &sound = *_vm->_sound;
+ Window &w = _vm->_screen->_windows[6];
+ Mode oldMode = _vm->_mode;
+ _vm->_mode = MODE_FF;
+
+ sound.playFX(21);
+ w.open();
+ w.writeString(Common::String::format(NOT_ENOUGH_TO_CAST,
+ SPELL_CAST_COMPONENTS[componentNum - 1]));
+ w.update();
+
+ do {
+ events.pollEventsAndWait();
+ } while (!_vm->shouldQuit() && !events.isKeyMousePressed());
+ events.clearEvents();
+
+ w.close();
+ _vm->_mode = oldMode;
+}
+
} // End of namespace Xeen
diff --git a/engines/xeen/dialogs_error.h b/engines/xeen/dialogs_error.h
index 644b7e3b0e..46efdb1683 100644
--- a/engines/xeen/dialogs_error.h
+++ b/engines/xeen/dialogs_error.h
@@ -49,6 +49,17 @@ public:
ErrorWaitType waitType = WT_FREEZE_WAIT);
};
+class CantCast: public ButtonContainer {
+private:
+ XeenEngine *_vm;
+
+ CantCast(XeenEngine *vm) : ButtonContainer(), _vm(vm) {}
+
+ void execute(int spellId, int componentNum);
+public:
+ static void show(XeenEngine *vm, int spellId, int componentNum);
+};
+
} // End of namespace Xeen
#endif /* XEEN_DIALOGS_ERROR_H */
diff --git a/engines/xeen/dialogs_spells.cpp b/engines/xeen/dialogs_spells.cpp
index 0ce0259b45..fe249d263e 100644
--- a/engines/xeen/dialogs_spells.cpp
+++ b/engines/xeen/dialogs_spells.cpp
@@ -413,4 +413,146 @@ const char *SpellsScroll::setSpellText(Character *c, int v2) {
return nullptr;
}
+/*------------------------------------------------------------------------*/
+
+int CastSpell::show(XeenEngine *vm, int mode) {
+ Combat &combat = *vm->_combat;
+ Interface &intf = *vm->_interface;
+ Party &party = *vm->_party;
+ Spells &spells = *vm->_spells;
+ int charNum;
+
+ // Get which character is doing the casting
+ if (mode == MODE_COMBAT) {
+ charNum = combat._whosTurn;
+ } else if (spells._lastCaster >= 0 && spells._lastCaster < (int)party._activeParty.size()) {
+ charNum = spells._lastCaster;
+ } else {
+ for (charNum = (int)party._activeParty.size() - 1; charNum >= 0; --charNum) {
+ if (party._activeParty[charNum]._hasSpells) {
+ spells._lastCaster = charNum;
+ break;
+ }
+ }
+ }
+
+ Character &c = party._activeParty[charNum];
+ intf.highlightChar(charNum);
+
+ CastSpell *dlg = new CastSpell(vm);
+ int spellId = dlg->execute(&c, mode);
+ delete dlg;
+
+ return spellId;
+}
+
+int CastSpell::show(XeenEngine *vm, Character *c, int mode) {
+ CastSpell *dlg = new CastSpell(vm);
+ int spellId = dlg->execute(c, mode);
+ delete dlg;
+
+ return spellId;
+}
+
+int CastSpell::execute(Character *c, int mode) {
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Party &party = *_vm->_party;
+ Screen &screen = *_vm->_screen;
+ SoundManager &sound = *_vm->_sound;
+ Spells &spells = *_vm->_spells;
+ Window &w = screen._windows[10];
+ int charNum;
+
+ Mode oldMode = (Mode)mode;
+ int category = c->getClassCategory();
+
+ w.open();
+ loadButtons();
+ drawButtons(&screen);
+
+ int spellId = -1;
+ bool redrawFlag = true;
+ do {
+ if (redrawFlag) {
+ int spellIndex = c->_currentSpell != -1 ? c->_currentSpell : 39;
+ spellId = SPELLS_ALLOWED[category][spellIndex];
+ int gemCost = SPELL_GEM_COST[spellId];
+ int spCost = spells.calcSpellPoints(spellId, c->getCurrentLevel());
+
+ w.writeString(Common::String::format(CAST_SPELL_DETAILS,
+ c->_name.c_str(), spells._spellNames[spellId].c_str(),
+ spCost, gemCost, c->_currentSp));
+ w.update();
+
+ _vm->_mode = MODE_3;
+ redrawFlag = false;
+ }
+
+ events.updateGameCounter();
+ intf.draw3d(true);
+
+ // Wait for event or time expiry
+ do {
+ events.pollEventsAndWait();
+ checkEvents(_vm);
+ } while (!_vm->shouldQuit() && events.timeElapsed() < 1 && !_buttonValue);
+
+ switch (_buttonValue) {
+ case Common::KEYCODE_F1:
+ case Common::KEYCODE_F2:
+ case Common::KEYCODE_F3:
+ case Common::KEYCODE_F4:
+ case Common::KEYCODE_F5:
+ case Common::KEYCODE_F6:
+ // Only allow changing character if the party is not in combat
+ if (oldMode != MODE_COMBAT) {
+ _vm->_mode = oldMode;
+ _buttonValue -= Common::KEYCODE_F1;
+
+ if (_buttonValue < (int)party._activeParty.size()) {
+ c = &party._activeParty[_buttonValue];
+ intf.highlightChar(_buttonValue);
+ redrawFlag = true;
+ break;
+ }
+ }
+ break;
+
+ case Common::KEYCODE_ESCAPE:
+ spellId = -1;
+ break;
+
+ case Common::KEYCODE_c:
+ // Cast spell - return the selected spell Id to be cast
+ if (c->_currentSpell != -1 && !c->noActions())
+ _buttonValue = Common::KEYCODE_ESCAPE;
+ break;
+
+ case Common::KEYCODE_n:
+ // Select new spell
+ _vm->_mode = oldMode;
+ c = SpellsScroll::show(_vm, c, 1);
+ redrawFlag = true;
+ break;
+
+ default:
+ break;
+ }
+ } while (!_vm->shouldQuit() && _buttonValue != Common::KEYCODE_ESCAPE);
+
+ w.close();
+ intf.unhighlightChar();
+
+ return spellId;
+}
+
+void CastSpell::loadButtons() {
+ _iconSprites.load("cast.icn");
+ addButton(Common::Rect(234, 108, 259, 128), Common::KEYCODE_d, &_iconSprites);
+ addButton(Common::Rect(261, 108, 285, 128), Common::KEYCODE_w, &_iconSprites);
+ addButton(Common::Rect(288, 108, 312, 128), Common::KEYCODE_ESCAPE, &_iconSprites);
+ addPartyButtons(_vm);
+}
+
} // End of namespace Xeen
diff --git a/engines/xeen/dialogs_spells.h b/engines/xeen/dialogs_spells.h
index 69e339fa79..d0de44c932 100644
--- a/engines/xeen/dialogs_spells.h
+++ b/engines/xeen/dialogs_spells.h
@@ -56,6 +56,21 @@ public:
static Character *show(XeenEngine *vm, Character *c, int v2);
};
+class CastSpell : public ButtonContainer {
+private:
+ XeenEngine *_vm;
+ SpriteResource _iconSprites;
+
+ CastSpell(XeenEngine *vm) : ButtonContainer(), _vm(vm) {}
+
+ int execute(Character *c, int mode);
+
+ void loadButtons();
+public:
+ static int show(XeenEngine *vm, int mode);
+ static int show(XeenEngine *vm, Character *c, int mode);
+};
+
} // End of namespace Xeen
#endif /* XEEN_DIALOGS_SPELLS_H */
diff --git a/engines/xeen/interface.cpp b/engines/xeen/interface.cpp
index a8396288ee..3bd3ef12b6 100644
--- a/engines/xeen/interface.cpp
+++ b/engines/xeen/interface.cpp
@@ -31,6 +31,7 @@
#include "xeen/dialogs_query.h"
#include "xeen/dialogs_quests.h"
#include "xeen/dialogs_quick_ref.h"
+#include "xeen/dialogs_spells.h"
#include "xeen/resources.h"
#include "xeen/xeen.h"
@@ -267,6 +268,7 @@ void Interface::perform() {
Map &map = *_vm->_map;
Party &party = *_vm->_party;
Scripts &scripts = *_vm->_scripts;
+ Spells &spells = *_vm->_spells;
const Common::Rect waitBounds(8, 8, 224, 140);
events.updateGameCounter();
@@ -517,6 +519,14 @@ void Interface::perform() {
}
break;
+ case Common::KEYCODE_c: {
+ // Cast spell
+ int spellId = CastSpell::show(_vm, _vm->_mode);
+ if (spellId != -1)
+ spells.castSpell(spellId);
+ break;
+ }
+
case Common::KEYCODE_i:
// Show Info dialog
_vm->_moveMonsters = false;
@@ -585,8 +595,10 @@ void Interface::stepTime() {
}
void Interface::doStepCode() {
+ Combat &combat = *_vm->_combat;
Map &map = *_vm->_map;
Party &party = *_vm->_party;
+ Scripts &scripts = *_vm->_scripts;
int damage = 0;
party._stepped = true;
@@ -642,8 +654,11 @@ void Interface::doStepCode() {
_flipGround = !_flipGround;
draw3d(true);
- warning("TODO: apply damage");
+ int oldVal = scripts._v2;
+ scripts._v2 = 0;
+ combat.giveCharDamage(damage, combat._damageType, 0);
+ scripts._v2 = oldVal;
_flipGround = !_flipGround;
} else if (party._partyDead) {
draw3d(true);
@@ -1825,6 +1840,7 @@ void Interface::doCombat() {
Party &party = *_vm->_party;
Screen &screen = *_vm->_screen;
Scripts &scripts = *_vm->_scripts;
+ Spells &spells = *_vm->_spells;
SoundManager &sound = *_vm->_sound;
bool upDoorText = _upDoorText;
bool reloadMap = false;
@@ -1933,14 +1949,13 @@ void Interface::doCombat() {
nextChar();
break;
- case Common::KEYCODE_c:
- // Cast Spell
- if (combat.castSpell(false)) {
- nextChar();
- } else {
- highlightChar(combat._combatParty[combat._whosTurn]->_rosterId);
- }
+ case Common::KEYCODE_c: {
+ // Cast spell
+ int spellId = CastSpell::show(_vm, _vm->_mode);
+ if (spellId != -1)
+ spells.castSpell(spellId);
break;
+ }
case Common::KEYCODE_f:
// Quick Fight
diff --git a/engines/xeen/resources.cpp b/engines/xeen/resources.cpp
index c57b6a72cc..0fe20fb2c4 100644
--- a/engines/xeen/resources.cpp
+++ b/engines/xeen/resources.cpp
@@ -1498,4 +1498,19 @@ const int NEW_CHARACTER_SPELLS[10][4] = {
const char *const COMBAT_DETAILS = "\r\f00\x03c\v000\t000\x02Combat%s%s%s\x1";
+const char *NOT_ENOUGH_TO_CAST = "\x03c\v010Not enough %s to Cast %s";
+const char *SPELL_CAST_COMPONENTS[2] = { "Spell Points", "Gems" };
+
+const char *const CAST_SPELL_DETAILS =
+ "\r\x2\x3""c\v122\t013\f37C\fdast\t040\f37N\fdew"
+ "\t067ESC\x1\t000\v000\x3""cCast Spell\n"
+ "\n"
+ "%s\x3l\n"
+ "\n"
+ "Spell Ready:\x3""c\n"
+ "\n"
+ "\f09%s\fd\x2\x3l\n"
+ "\v082Cost\x3r\t000%u/%u\x3l\n"
+ "Cur SP\x3r\t000%u\x1";
+
} // End of namespace Xeen
diff --git a/engines/xeen/resources.h b/engines/xeen/resources.h
index d6902129e8..30eb4f5b63 100644
--- a/engines/xeen/resources.h
+++ b/engines/xeen/resources.h
@@ -524,6 +524,11 @@ extern const int NEW_CHARACTER_SPELLS[10][4];
extern const char *const COMBAT_DETAILS;
+extern const char *NOT_ENOUGH_TO_CAST;
+extern const char *SPELL_CAST_COMPONENTS[2];
+
+extern const char *const CAST_SPELL_DETAILS;
+
} // End of namespace Xeen
#endif /* XEEN_RESOURCES_H */
diff --git a/engines/xeen/spells.cpp b/engines/xeen/spells.cpp
index 4943d95d2a..20ba480a6b 100644
--- a/engines/xeen/spells.cpp
+++ b/engines/xeen/spells.cpp
@@ -104,6 +104,37 @@ void Spells::doSpell(int spellId) {
(this->*SPELL_LIST[spellId])();
}
+void Spells::castSpell(int spellId) {
+ // TODO
+ error("TODO: castSpell");
+}
+
+/**
+ * Subtract the requirements for a given spell if available, returning
+ * true if there was sufficient
+ */
+int Spells::subSpellCost(Character &c, int spellId) {
+ Party &party = *_vm->_party;
+ int gemCost = SPELL_GEM_COST[spellId];
+ int spCost = SPELL_COSTS[spellId];
+
+ // Negative SP costs indicate it's dependent on the character's level
+ if (spCost <= 0) {
+ spCost = c.getCurrentLevel() * (spCost * -1);
+ }
+
+ if (spCost > c._currentSp)
+ // Not enough SP
+ return 1;
+ if (gemCost > party._gems)
+ // Not enough gems
+ return 2;
+
+ c._currentSp -= spCost;
+ party._gems -= gemCost;
+ return 0;
+}
+
void Spells::light() { error("TODO: spell"); }
void Spells::awaken() { error("TODO: spell"); }
void Spells::magicArrow() { error("TODO: spell"); }
diff --git a/engines/xeen/spells.h b/engines/xeen/spells.h
index 15389be599..cffb302c51 100644
--- a/engines/xeen/spells.h
+++ b/engines/xeen/spells.h
@@ -29,6 +29,7 @@
namespace Xeen {
class XeenEngine;
+class Character;
#define MAX_SPELLS_PER_CLASS 40
@@ -158,6 +159,10 @@ public:
int calcSpellPoints(int spellId, int expenseFactor) const;
void doSpell(int spellId);
+
+ void castSpell(int spellId);
+
+ int subSpellCost(Character &c, int spellId);
};
} // End of namespace Xeen