aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Gilbert2015-02-28 12:45:33 -0500
committerPaul Gilbert2015-02-28 12:45:33 -0500
commit973c5a0df5bf9f86cc589a7a138899b96684bf23 (patch)
treeaee0806e0aa17c11582a0c817628942031a57e4f
parent302b3cdc63fe24b4c702c260935879fd7f477bd5 (diff)
downloadscummvm-rg350-973c5a0df5bf9f86cc589a7a138899b96684bf23.tar.gz
scummvm-rg350-973c5a0df5bf9f86cc589a7a138899b96684bf23.tar.bz2
scummvm-rg350-973c5a0df5bf9f86cc589a7a138899b96684bf23.zip
XEEN: Implemented multiAttack
-rw-r--r--engines/xeen/character.cpp10
-rw-r--r--engines/xeen/character.h2
-rw-r--r--engines/xeen/combat.cpp276
-rw-r--r--engines/xeen/combat.h13
-rw-r--r--engines/xeen/interface.cpp2
-rw-r--r--engines/xeen/spells.cpp37
6 files changed, 324 insertions, 16 deletions
diff --git a/engines/xeen/character.cpp b/engines/xeen/character.cpp
index e005a35f13..d95e061515 100644
--- a/engines/xeen/character.cpp
+++ b/engines/xeen/character.cpp
@@ -1848,6 +1848,16 @@ bool Character::hasSpecialItem() const {
return false;
}
+bool Character::hasMissileWeapon() const {
+ for (uint idx = 0; idx < INV_ITEMS_TOTAL; ++idx) {
+ if (_weapons[idx]._frame == 4) {
+ return !isDisabledOrDead();
+ }
+ }
+
+ return false;
+}
+
int Character::getClassCategory() const {
switch (_class) {
case CLASS_ARCHER:
diff --git a/engines/xeen/character.h b/engines/xeen/character.h
index 9e858da7fa..77ab882305 100644
--- a/engines/xeen/character.h
+++ b/engines/xeen/character.h
@@ -326,6 +326,8 @@ public:
bool hasSpecialItem() const;
+ bool hasMissileWeapon() const;
+
int getClassCategory() const;
};
diff --git a/engines/xeen/combat.cpp b/engines/xeen/combat.cpp
index fca7fcb3ee..763daa7d03 100644
--- a/engines/xeen/combat.cpp
+++ b/engines/xeen/combat.cpp
@@ -116,7 +116,7 @@ Combat::Combat(XeenEngine *vm): _vm(vm), _missVoc("miss.voc"), _pow1Voc("pow1.vo
_whosSpeed = 0;
_damageType = DT_PHYSICAL;
_oldCharacter = nullptr;
- _shootType = 0;
+ _shootType = ST_0;
_monsterDamage = 0;
_weaponDamage = 0;
_weaponDie = _weaponDice = 0;
@@ -125,6 +125,7 @@ Combat::Combat(XeenEngine *vm): _vm(vm), _missVoc("miss.voc"), _pow1Voc("pow1.vo
_hitChanceBonus = 0;
_dangerPresent = false;
_moveMonsters = false;
+ _rangeType = RT_SINGLE;
}
void Combat::clear() {
@@ -1198,7 +1199,7 @@ void Combat::attack(Character &c, RangeType rangeType) {
MonsterStruct &monsterData = map._monsterData[monsterDataIndex];
if (rangeType) {
- if (_shootType != 1 || _damageType == DT_MAGIC_ARROW) {
+ if (_shootType != ST_1 || _damageType == DT_MAGIC_ARROW) {
if (!monsterData._magicResistence || monsterData._magicResistence <=
_vm->getRandomNumber(1, 100 + _oldCharacter->getCurrentLevel())) {
if (_monsterDamage != 0) {
@@ -1216,25 +1217,25 @@ void Combat::attack(Character &c, RangeType rangeType) {
if ((monsterData._monsterType == MONSTER_ANIMAL || monsterData._monsterType == MONSTER_HUMANOID)
&& !monsterSavingThrow(monsterDataIndex)) {
damage = MIN(monster._hp, 50);
- attack2(damage, RT_2);
+ attack2(damage, RT_ALL);
setSpeedTable();
}
break;
case DT_HOLYWORD:
if (monsterData._monsterType == MONSTER_UNDEAD) {
- attack2(monster._hp, RT_2);
+ attack2(monster._hp, RT_ALL);
setSpeedTable();
}
break;
case DT_MASS_DISTORTION:
- attack2(MAX(monster._hp / 2, 1), RT_2);
+ attack2(MAX(monster._hp / 2, 1), RT_ALL);
setSpeedTable();
break;
case DT_UNDEAD:
if (monsterData._monsterType == MONSTER_UNDEAD)
damage = 25;
else
- rangeType = RT_2;
+ rangeType = RT_ALL;
attack2(damage, rangeType);
setSpeedTable();
break;
@@ -1346,7 +1347,7 @@ void Combat::attack(Character &c, RangeType rangeType) {
damage = 0;
while (numberOfAttacks-- > 0) {
- if (hitMonster(c, RT_CLOSE))
+ if (hitMonster(c, RT_SINGLE))
damage += getMonsterDamage(c);
}
@@ -1593,7 +1594,7 @@ void Combat::quickFight() {
switch (c->_quickOption) {
case QUICK_ATTACK:
- attack(*c, RT_CLOSE);
+ attack(*c, RT_SINGLE);
break;
case QUICK_SPELL:
if (c->_currentSpell != -1) {
@@ -1739,7 +1740,7 @@ int Combat::getMonsterResistence(RangeType rangeType) {
MonsterStruct &monsterData = *monster._monsterData;
int resistence = 0, damage = 0;
- if (rangeType != RT_CLOSE && rangeType != RT_3) {
+ if (rangeType != RT_SINGLE && rangeType != RT_3) {
switch (_damageType) {
case DT_PHYSICAL:
resistence = monsterData._phsyicalResistence;
@@ -1825,4 +1826,261 @@ void Combat::giveExperience(int experience) {
}
}
+void Combat::multiAttack(int powNum) {
+ Interface &intf = *_vm->_interface;
+ Map &map = *_vm->_map;
+ Party &party = *_vm->_party;
+ SoundManager &sound = *_vm->_sound;
+
+ if (_damageType == DT_POISON_VOLLEY) {
+ _damageType = DT_POISON;
+ _shootType = ST_1;
+ Common::fill(&_shooting[0], &_shooting[6], 1);
+ } else if (powNum == 11) {
+ _shootType = ST_1;
+ bool flag = false;
+
+ if (_damageType == DT_PHYSICAL) {
+ for (uint idx = 0; idx < party._activeParty.size(); ++idx) {
+ Character &c = party._activeParty[idx];
+ if (c.hasMissileWeapon()) {
+ _shooting[idx] = 1;
+ flag = true;
+ }
+ }
+ } else {
+ _shooting[0] = 1;
+ flag = true;
+ }
+
+ if (!flag) {
+ sound.playFX(21);
+ return;
+ }
+ } else {
+ _shooting[0] = 1;
+ _shootType = ST_0;
+ }
+
+ intf._charsShooting = true;
+ _powSprites.load(Common::String::format("pow%d.icn", powNum));
+ int monsterIndex = _monsterIndex;
+ int monster2Attack = _monster2Attack;
+ bool attackedFlag = false;
+
+ Common::Array<int> attackMonsters;
+ for (int idx = 0; idx < 3; ++idx) {
+ if (_attackMonsters[idx] != -1)
+ attackMonsters.push_back(_attackMonsters[idx]);
+ }
+
+ _monsterIndex = -1;
+ if (_monster2Attack != -1) {
+ _monsterIndex--;
+ if (attackMonsters.empty())
+ attackMonsters.resize(1);
+ attackMonsters[0] = monster2Attack;
+ }
+
+ for (uint idx = 0; idx < party._activeParty.size(); ++idx) {
+ Character &c = party._activeParty[idx];
+ if (_shooting[idx]) {
+ if (map._isOutdoors) {
+ intf._outdoorList._attackImgs1[idx]._scale = 0;
+ intf._outdoorList._attackImgs2[idx]._scale = 4;
+ intf._outdoorList._attackImgs3[idx]._scale = 8;
+ intf._outdoorList._attackImgs4[idx]._scale = 12;
+ intf._outdoorList._attackImgs1[idx]._sprites = &_powSprites;
+ intf._outdoorList._attackImgs2[idx]._sprites = nullptr;
+ intf._outdoorList._attackImgs3[idx]._sprites = nullptr;
+ intf._outdoorList._attackImgs4[idx]._sprites = nullptr;
+ } else {
+ intf._indoorList._attackImgs1[idx]._scale = 0;
+ intf._indoorList._attackImgs2[idx]._scale = 4;
+ intf._indoorList._attackImgs3[idx]._scale = 8;
+ intf._indoorList._attackImgs4[idx]._scale = 12;
+ intf._indoorList._attackImgs1[idx]._sprites = &_powSprites;
+ intf._indoorList._attackImgs2[idx]._sprites = nullptr;
+ intf._indoorList._attackImgs3[idx]._sprites = nullptr;
+ intf._indoorList._attackImgs4[idx]._sprites = nullptr;
+ }
+ }
+ }
+
+ intf.draw3d(true);
+
+ ++_monsterIndex;
+ for (uint monIdx = 0; monIdx < attackMonsters.size(); ++monIdx, ++_monsterIndex) {
+ Common::fill(&_missedShot[0], &_missedShot[8], false);
+ _monster2Attack = attackMonsters[monIdx];
+ attack(*_oldCharacter, RT_GROUP);
+ attackedFlag = true;
+
+ if (_rangeType == RT_SINGLE)
+ // Only single shot, so exit now that the attack is done
+ goto finished;
+ }
+
+ if (attackedFlag && _rangeType == RT_GROUP)
+ // Finished group attack, so exit
+ goto finished;
+
+ if (map._isOutdoors) {
+ map.getCell(7);
+ switch (map._currentWall) {
+ case 1:
+ case 3:
+ case 6:
+ case 7:
+ case 9:
+ case 10:
+ case 12:
+ sound.playFX(46);
+ goto finished;
+ default:
+ break;
+ }
+ } else {
+ int cell = map.getCell(2);
+ if (cell < map.mazeData()._difficulties._wallNoPass) {
+ sound.playFX(46);
+ goto finished;
+ }
+ }
+ if (!intf._isAttacking)
+ goto finished;
+
+ intf.draw3d(true);
+
+ // Start handling second teir of monsters in the back
+ attackMonsters.clear();
+ for (uint idx = 3; idx < 6; ++idx) {
+ if (_attackMonsters[idx] != -1)
+ attackMonsters.push_back(_attackMonsters[idx]);
+ }
+
+ ++_monsterIndex;
+ for (uint monIdx = 0; monIdx < attackMonsters.size(); ++monIdx, ++_monsterIndex) {
+ Common::fill(&_missedShot[0], &_missedShot[8], false);
+ _monster2Attack = attackMonsters[monIdx];
+ attack(*_oldCharacter, RT_GROUP);
+ attackedFlag = true;
+
+ if (_rangeType == RT_SINGLE)
+ // Only single shot, so exit now that the attack is done
+ goto finished;
+ }
+
+ if (attackedFlag && _rangeType == RT_GROUP)
+ // Finished group attack, so exit
+ goto finished;
+
+ if (map._isOutdoors) {
+ map.getCell(14);
+ switch (map._currentWall) {
+ case 1:
+ case 3:
+ case 6:
+ case 7:
+ case 9:
+ case 10:
+ case 12:
+ sound.playFX(46);
+ goto finished;
+ default:
+ break;
+ }
+ } else {
+ int cell = map.getCell(7);
+ if (cell < map.mazeData()._difficulties._wallNoPass) {
+ sound.playFX(46);
+ goto finished;
+ }
+ }
+ if (!intf._isAttacking)
+ goto finished;
+
+ intf.draw3d(true);
+
+ // Start handling third teir of monsters in the back
+ attackMonsters.clear();
+ for (uint idx = 6; idx < 9; ++idx) {
+ if (_attackMonsters[idx] != -1)
+ attackMonsters.push_back(_attackMonsters[idx]);
+ }
+
+ ++_monsterIndex;
+ for (uint monIdx = 0; monIdx < attackMonsters.size(); ++monIdx, ++_monsterIndex) {
+ Common::fill(&_missedShot[0], &_missedShot[8], false);
+ _monster2Attack = attackMonsters[monIdx];
+ attack(*_oldCharacter, RT_GROUP);
+ attackedFlag = true;
+
+ if (_rangeType == RT_SINGLE)
+ // Only single shot, so exit now that the attack is done
+ goto finished;
+ }
+
+ if (attackedFlag && _rangeType == RT_GROUP)
+ // Finished group attack, so exit
+ goto finished;
+
+ if (map._isOutdoors) {
+ map.getCell(27);
+ switch (map._currentWall) {
+ case 1:
+ case 3:
+ case 6:
+ case 7:
+ case 9:
+ case 10:
+ case 12:
+ sound.playFX(46);
+ goto finished;
+ default:
+ break;
+ }
+ } else {
+ int cell = map.getCell(14);
+ if (cell < map.mazeData()._difficulties._wallNoPass) {
+ sound.playFX(46);
+ goto finished;
+ }
+ }
+ if (!intf._isAttacking)
+ goto finished;
+
+ intf.draw3d(true);
+
+ // Fourth tier
+ attackMonsters.clear();
+ for (uint idx = 9; idx < 12; ++idx) {
+ if (_attackMonsters[idx] != -1)
+ attackMonsters.push_back(_attackMonsters[idx]);
+ }
+
+ ++_monsterIndex;
+ for (uint monIdx = 0; monIdx < attackMonsters.size(); ++monIdx, ++_monsterIndex) {
+ Common::fill(&_missedShot[0], &_missedShot[8], false);
+ _monster2Attack = attackMonsters[monIdx];
+ attack(*_oldCharacter, RT_GROUP);
+ attackedFlag = true;
+
+ if (_rangeType == RT_SINGLE)
+ // Only single shot, so exit now that the attack is done
+ goto finished;
+ }
+
+ if (!(attackedFlag && _rangeType == RT_GROUP))
+ goto done;
+
+finished:
+ endAttack();
+done:
+ Common::fill(&_shooting[0], &_shooting[MAX_PARTY_COUNT], 0);
+ _monster2Attack = monster2Attack;
+ _monsterIndex = monsterIndex;
+ party.giveTreasure();
+}
+
} // End of namespace Xeen
diff --git a/engines/xeen/combat.h b/engines/xeen/combat.h
index bf35ca819d..9831f90f25 100644
--- a/engines/xeen/combat.h
+++ b/engines/xeen/combat.h
@@ -39,7 +39,7 @@ enum DamageType {
DT_FINGEROFDEATH = 8, DT_HOLYWORD = 9, DT_MASS_DISTORTION = 10,
DT_UNDEAD = 11, DT_BEASTMASTER = 12, DT_DRAGONSLEEP = 13,
DT_GOLEMSTOPPER = 14, DT_HYPNOTIZE = 15, DT_INSECT_SPRAY = 16,
- DT_POISON_VALLEY = 17, DT_MAGIC_ARROW = 18
+ DT_POISON_VOLLEY = 17, DT_MAGIC_ARROW = 18
};
enum SpecialAttack {
@@ -57,7 +57,11 @@ enum ElementalCategory {
};
enum RangeType {
- RT_CLOSE = 0, RT_1 = 1, RT_2 = 2, RT_3 = 3
+ RT_SINGLE = 0, RT_GROUP = 1, RT_ALL = 2, RT_3 = 3
+};
+
+enum ShootType {
+ ST_0 = 0, ST_1 = 1
};
class XeenEngine;
@@ -110,7 +114,6 @@ public:
int _whosSpeed;
DamageType _damageType;
Character *_oldCharacter;
- int _shootType;
int _monsterDamage;
int _weaponDamage;
int _weaponDie, _weaponDice;
@@ -120,6 +123,8 @@ public:
int _hitChanceBonus;
bool _dangerPresent;
bool _moveMonsters;
+ RangeType _rangeType;
+ ShootType _shootType;
public:
Combat(XeenEngine *vm);
@@ -165,6 +170,8 @@ public:
void monsterOvercome();
int stopAttack(const Common::Point &diffPt);
+
+ void multiAttack(int powNum);
};
} // End of namespace Xeen
diff --git a/engines/xeen/interface.cpp b/engines/xeen/interface.cpp
index 0354732b2b..7339e1014f 100644
--- a/engines/xeen/interface.cpp
+++ b/engines/xeen/interface.cpp
@@ -1967,7 +1967,7 @@ void Interface::doCombat() {
case Common::KEYCODE_a:
// Attack
- combat.attack(*combat._combatParty[combat._whosTurn], RT_CLOSE);
+ combat.attack(*combat._combatParty[combat._whosTurn], RT_SINGLE);
nextChar();
break;
diff --git a/engines/xeen/spells.cpp b/engines/xeen/spells.cpp
index 25aae11af2..53c43b5233 100644
--- a/engines/xeen/spells.cpp
+++ b/engines/xeen/spells.cpp
@@ -198,10 +198,41 @@ void Spells::addSpellCost(Character &c, int spellId) {
party._gems += gemCost;
}
+void Spells::light() {
+ Interface &intf = *_vm->_interface;
+ Party &party = *_vm->_party;
+ SoundManager &sound = *_vm->_sound;
+
+ ++party._lightCount;
+ if (intf._intrIndex1)
+ party._stepped = true;
+ sound.playFX(39);
+}
+
+void Spells::awaken() {
+ Interface &intf = *_vm->_interface;
+ Party &party = *_vm->_party;
+ SoundManager &sound = *_vm->_sound;
+
+ for (uint idx = 0; idx < party._activeParty.size(); ++idx) {
+ Character &c = party._activeParty[idx];
+ c._conditions[ASLEEP] = 0;
+ if (c._currentHp > 0)
+ c._conditions[UNCONSCIOUS] = 0;
+ }
+
+ intf.drawParty(true);
+ sound.playFX(30);
+}
+
+void Spells::magicArrow() {
+ Combat &combat = *_vm->_combat;
+ combat._monsterDamage = 0;
+ combat._damageType = DT_MAGIC_ARROW;
+ combat._rangeType = RT_SINGLE;
+ combat.multiAttack(11);
+}
-void Spells::light() { error("TODO: spell"); }
-void Spells::awaken() { error("TODO: spell"); }
-void Spells::magicArrow() { error("TODO: spell"); }
void Spells::firstAid() { error("TODO: spell"); }
void Spells::flyingFist() { error("TODO: spell"); }
void Spells::energyBlast() { error("TODO: spell"); }