[Scummvm-git-logs] scummvm master -> e31c7ff1d4c53e0643c72e980c255c414725879e

athrxx noreply at scummvm.org
Wed Oct 23 19:58:42 UTC 2024


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
e31c7ff1d4 KYRA: (EOB) - Fix projectile weapon damage


Commit: e31c7ff1d4c53e0643c72e980c255c414725879e
    https://github.com/scummvm/scummvm/commit/e31c7ff1d4c53e0643c72e980c255c414725879e
Author: Vladimir Vrzić (vvrzic at gmail.com)
Date: 2024-10-23T21:58:38+02:00

Commit Message:
KYRA: (EOB) - Fix projectile weapon damage

Note: All changes are behind the "Faithful AD&D rules" flag.

Fix a bug in the original gmaes, where projectiles would deal
an incorrect amount of damage (always 1d1):

- projectile weapon id is attached to a FlyingObject and checked
  when FlyingObject hits a monster or the party

- bump the save format version to add a new field to FlyingObject
  (the new 16 bit field replaces the existing unused 8 bit field)

- fix a bug in the original code where a projectile weapon thrown into
  the 3D view would inflict projectile damage (should be 1d1)

- patch EotB 1 Bow and Sling damage according to AD&D 2nd Ed rules

Elves get +1 to hit bonus with bows and swords, according to manual

Patch EotB 1 NPCs Beohram (paladin) and Ileria (female)

Changed paths:
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/engine/items_eob.cpp
    engines/kyra/engine/kyra_rpg.h
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/gui/saveload.cpp
    engines/kyra/gui/saveload_eob.cpp


diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 797bcf5200a..b03c73d1a9e 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -1037,6 +1037,7 @@ int EoBCoreEngine::rollHitDie(int charIndex, int levelIndex) {
 	int classOffset = getCharacterClassType(c->cClass, levelIndex);
 	int die = classOffset >= 0 ? _hpIncrPerLevel[classOffset] : 1;
 	int roll = rollDice(1, die);
+	debugC(1, kDebugLevelMain, "Rolled Hit Die d%d: %d", die, roll);
 	return roll;
 }
 
@@ -1420,6 +1421,27 @@ void EoBCoreEngine::initNpc(int npcIndex) {
 	makeFaceShapes(i);
 	makeNameShapes(i);
 
+	/*
+	 * Patch minor issues with Eye of the Beholder 1 NPCs:
+	 *
+	 * Near Beohram's bones, a paladin's holy symbol can be found.
+	 *
+	 * Ileria is indicated to be female in the dialog (calls herself
+	 * "sister"), by the portrait and by the character sheet in the
+	 * official Clue Book.
+	 */
+	if (_configADDRuleEnhancements) {
+		if (_flags.gameID == GI_EOB1) {
+			if (npcIndex == 1) {
+				debugC(1, kDebugLevelMain, "Patching Beohram to be a paladin");
+				c->cClass = 2;
+			} else if (npcIndex == 3) {
+				debugC(1, kDebugLevelMain, "Patching Ileria to be a half-elf female");
+				c->raceSex = 5;
+			}
+		}
+	}
+
 	for (i = 0; i < 25; i++) {
 		if (!c->inventory[i])
 			continue;
@@ -2252,26 +2274,33 @@ int EoBCoreEngine::thrownAttack(int charIndex, int slotIndex, Item item) {
 	return 0;
 }
 
+int EoBCoreEngine::normalizeProjectileWeaponType(int itemType) {
+	if (_flags.gameID == GI_EOB1) {
+		assert(itemType >= 7);
+		itemType -= 7;
+	}
+	return itemType;
+}
+
 int EoBCoreEngine::projectileWeaponAttack(int charIndex, Item item) {
-	int tp = _items[item].type;
+	int itemType = normalizeProjectileWeaponType(_items[item].type);
 
-	if (_flags.gameID == GI_EOB1)
-		assert(tp >= 7);
+	int ammoItemType = _projectileWeaponAmmoTypes[itemType];
 
-	int t = _projectileWeaponAmmoTypes[_flags.gameID == GI_EOB1 ? tp - 7 : tp];
 	Item ammoItem = 0;
 
-	if (t == 16) {
-		if (_characters[charIndex].inventory[0] && _items[_characters[charIndex].inventory[0]].type == 16)
+	if (ammoItemType == ITEM_TYPE_ARROW) {
+		/* Fire arrow in hand first, then take from quiver. */
+		if (_characters[charIndex].inventory[0] && _items[_characters[charIndex].inventory[0]].type == ITEM_TYPE_ARROW)
 			SWAP(ammoItem, _characters[charIndex].inventory[0]);
-		else if (_characters[charIndex].inventory[1] && _items[_characters[charIndex].inventory[1]].type == 16)
+		else if (_characters[charIndex].inventory[1] && _items[_characters[charIndex].inventory[1]].type == ITEM_TYPE_ARROW)
 			SWAP(ammoItem, _characters[charIndex].inventory[1]);
 		else if (_characters[charIndex].inventory[16])
 			ammoItem = getQueuedItem(&_characters[charIndex].inventory[16], 0, -1);
 
 	} else {
 		for (int i = 0; i < 27; i++) {
-			if (_items[_characters[charIndex].inventory[i]].type == t) {
+			if (_items[_characters[charIndex].inventory[i]].type == ammoItemType) {
 				SWAP(ammoItem, _characters[charIndex].inventory[i]);
 				if (i < 2)
 					gui_drawCharPortraitWithStats(charIndex);
@@ -2287,8 +2316,8 @@ int EoBCoreEngine::projectileWeaponAttack(int charIndex, Item item) {
 	if (c > 3)
 		c -= 2;
 
-	if (launchObject(charIndex, ammoItem, _currentBlock, _dropItemDirIndex[(_currentDirection << 2) + c], _currentDirection, tp)) {
-		snd_playSoundEffect(tp == 7 ? 26 : 11);
+	if (launchObject(charIndex, ammoItem, _currentBlock, _dropItemDirIndex[(_currentDirection << 2) + c], _currentDirection, itemType, item)) {
+		snd_playSoundEffect(itemType == 7 ? 26 : 11);
 		_sceneUpdateRequired = true;
 	}
 
@@ -2296,6 +2325,7 @@ int EoBCoreEngine::projectileWeaponAttack(int charIndex, Item item) {
 }
 
 void EoBCoreEngine::inflictMonsterDamage(EoBMonsterInPlay *m, int damage, bool giveExperience) {
+	debugC(1, kDebugLevelMain, "Inflicted damage to monster: %d", damage);
 	m->hitPointsCur -= damage;
 	m->flags = (m->flags & 0xF7) | 1;
 
@@ -2321,20 +2351,20 @@ void EoBCoreEngine::inflictMonsterDamage(EoBMonsterInPlay *m, int damage, bool g
 	}
 }
 
-void EoBCoreEngine::calcAndInflictMonsterDamage(EoBMonsterInPlay *m, int times, int pips, int offs, int flags, int savingThrowType, int savingThrowEffect) {
-	int dmg = calcMonsterDamage(m, times, pips, offs, flags, savingThrowType, savingThrowEffect);
+void EoBCoreEngine::calcAndInflictMonsterDamage(EoBMonsterInPlay *m, int charIndex, int itemType, int offs, int flags, int savingThrowType, int savingThrowEffect, Item projectileWeapon) {
+	int dmg = calcMonsterDamage(m, charIndex, itemType, offs, flags, savingThrowType, savingThrowEffect, projectileWeapon);
 	if (dmg > 0)
 		inflictMonsterDamage(m, dmg, flags & 0x800 ? true : false);
 }
 
-void EoBCoreEngine::calcAndInflictCharacterDamage(int charIndex, int times, int itemOrPips, int useStrModifierOrBase, int flags, int savingThrowType, int savingThrowEffect) {
-	int dmg = calcCharacterDamage(charIndex, times, itemOrPips, useStrModifierOrBase, flags, savingThrowType, savingThrowEffect);
+void EoBCoreEngine::calcAndInflictCharacterDamage(int charIndex, int times, int itemOrPips, int useStrModifierOrBase, int flags, int savingThrowType, int savingThrowEffect, Item projectileWeapon) {
+	int dmg = calcCharacterDamage(charIndex, times, itemOrPips, useStrModifierOrBase, flags, savingThrowType, savingThrowEffect, projectileWeapon);
 	if (dmg)
 		inflictCharacterDamage(charIndex, dmg);
 }
 
-int EoBCoreEngine::calcCharacterDamage(int charIndex, int times, int itemOrPips, int useStrModifierOrBase, int flags, int savingThrowType, int savingThrowEffect) {
-	int s = (flags & 0x100) ? calcDamageModifers(times, 0, itemOrPips, _items[itemOrPips].type, useStrModifierOrBase) : rollDice(times, itemOrPips, useStrModifierOrBase);
+int EoBCoreEngine::calcCharacterDamage(int charIndex, int times, int itemOrPips, int useStrModifierOrBase, int flags, int savingThrowType, int savingThrowEffect, Item projectileWeapon) {
+	int s = (flags & 0x100) ? calcDamageModifers(times, 0, itemOrPips, _items[itemOrPips].type, useStrModifierOrBase, projectileWeapon) : rollDice(times, itemOrPips, useStrModifierOrBase);
 	EoBCharacter *c = &_characters[charIndex];
 
 	if (savingThrowType != 5) {
@@ -2342,7 +2372,7 @@ int EoBCoreEngine::calcCharacterDamage(int charIndex, int times, int itemOrPips,
 			s = savingThrowReduceDamage(savingThrowEffect, s);
 	}
 
-	if ((flags & 0x110) == 0x110) {
+	if ((flags & 0x110) == 0x110) { /* damage is inflicted by a flying object */
 		if (!calcDamageCheckItemType(_items[itemOrPips].type))
 			s = 1;
 	}
@@ -2363,6 +2393,7 @@ int EoBCoreEngine::calcCharacterDamage(int charIndex, int times, int itemOrPips,
 }
 
 void EoBCoreEngine::inflictCharacterDamage(int charIndex, int damage) {
+	debugC(1, kDebugLevelMain, "Inflicted damage to character %d: %d", charIndex, damage);
 	EoBCharacter *c = &_characters[charIndex];
 	if (!testCharacter(charIndex, 3))
 		return;
@@ -2404,16 +2435,36 @@ void EoBCoreEngine::inflictCharacterDamage(int charIndex, int damage) {
 	setCharEventTimer(charIndex, 18, 6, 1);
 }
 
-bool EoBCoreEngine::characterAttackHitTest(int charIndex, int monsterIndex, int item, int attackType) {
+bool EoBCoreEngine::isElf(int charIndex)
+{
+	const uint8 kRaceElf = 1;
+	return kRaceElf == _characters[charIndex].raceSex >> 1;
+}
+
+bool EoBCoreEngine::isSword(Item item)
+{
+	return _items[item].type == ITEM_TYPE_LONG_SWORD || _items[item].type == ITEM_TYPE_SHORT_SWORD;
+}
+
+bool EoBCoreEngine::isBow(Item projectileWeapon)
+{
+	if (projectileWeapon == kItemNone) return false;
+	int projectileWeaopnType = normalizeProjectileWeaponType(_items[projectileWeapon].type);
+	return projectileWeaopnType == ITEM_TYPE_BOW;
+}
+
+bool EoBCoreEngine::characterAttackHitTest(int charIndex, int monsterIndex, int item, int attackType, Item projectileWeapon) {
 	if (charIndex < 0)
 		return true;
 
+	// TODO double check disassembly here:
 	int p = item ? (_flags.gameID == GI_EOB1 ? _items[item].type : (_itemTypes[_items[item].type].extraProperties & 0x7F)) : 0;
 
 	if (_monsters[monsterIndex].flags & 0x20)
 		return true;// EOB 2 only ?
 
 	int t = _monsters[monsterIndex].type;
+	// TODO double check disassembly here:
 	int d = (p < 1 || p > 3) ? 0 : _items[item].value;
 
 	if (_flags.gameID == GI_EOB2) {
@@ -2423,7 +2474,7 @@ bool EoBCoreEngine::characterAttackHitTest(int charIndex, int monsterIndex, int
 		}
 	}
 
-	d += (attackType ? getStrHitChanceModifier(charIndex) : getDexHitChanceModifier(charIndex));
+	d += attackType ? getStrHitChanceModifier(charIndex) : getDexHitChanceModifier(charIndex);
 
 	int m = getMonsterAcHitChanceModifier(charIndex, _monsterProps[t].armorClass) - d;
 	int s = rollDice(1, 20);
@@ -2433,12 +2484,23 @@ bool EoBCoreEngine::characterAttackHitTest(int charIndex, int monsterIndex, int
 	if (_flags.gameID == GI_EOB1) {
 		if (_partyEffectFlags & 0x30)
 			s++;
-		if (_characters[charIndex].effectFlags & 0x40)
+		if (_characters[charIndex].effectFlags & 0x40) /* invisibility */
 			s++;
 	} else if ((_partyEffectFlags & 0x8400) || (_characters[charIndex].effectFlags & 0x1000)) {
 		s++;
 	}
 
+	/*
+	 * According to the official game manual, Elves should get a +1 to hit
+	 * bonus when using swords or bows.
+	 */
+	if (_configADDRuleEnhancements) {
+		if (isElf(charIndex) && (isSword(item) || isBow(projectileWeapon))) {
+		   debugC(1, kDebugLevelMain, "Applying elven +1 to hit bonus");
+		   s += 1;
+		}
+	}
+
 	s = CLIP(s, 1, 20);
 
 	return s >= m;
@@ -2466,10 +2528,10 @@ bool EoBCoreEngine::monsterAttackHitTest(EoBMonsterInPlay *m, int charIndex) {
 
 bool EoBCoreEngine::flyingObjectMonsterHit(EoBFlyingObject *fo, int monsterIndex) {
 	if (fo->attackerId != -1) {
-		if (!characterAttackHitTest(fo->attackerId, monsterIndex, fo->item, 0))
+		if (!characterAttackHitTest(fo->attackerId, monsterIndex, fo->item, 0, fo->projectileWeapon))
 			return false;
 	}
-	calcAndInflictMonsterDamage(&_monsters[monsterIndex], fo->attackerId, fo->item, 0, (fo->attackerId == -1) ? 0x110 : 0x910, 5, 3);
+	calcAndInflictMonsterDamage(&_monsters[monsterIndex], fo->attackerId, fo->item, 0, (fo->attackerId == -1) ? 0x110 : 0x910, 5, 3, fo->projectileWeapon);
 	return true;
 }
 
@@ -2489,7 +2551,7 @@ bool EoBCoreEngine::flyingObjectPartyHit(EoBFlyingObject *fo) {
 		s ^= 1;
 		if (!testCharacter(c, 3))
 			continue;
-		calcAndInflictCharacterDamage(c, -1, fo->item, 0, 0x110, 5, 3);
+		calcAndInflictCharacterDamage(c, -1, fo->item, 0, 0x110, 5, 3, fo->projectileWeapon);
 		res = true;
 		if (ps < 2 || b == 0)
 			break;
@@ -2599,8 +2661,8 @@ void EoBCoreEngine::statusAttack(int charIndex, int attackStatusFlags, const cha
 	_txt->printMessage(_characterStatusStrings13[0], -1, c->name, attackStatusString);
 }
 
-int EoBCoreEngine::calcMonsterDamage(EoBMonsterInPlay *m, int times, int pips, int offs, int flags, int savingThrowType, int savingThrowEffect) {
-	int s = flags & 0x100 ? calcDamageModifers(times, m, pips, _items[pips].type, offs) : rollDice(times, pips, offs);
+int EoBCoreEngine::calcMonsterDamage(EoBMonsterInPlay *m, int charIndexOrTimes, int itemOrPips, int offs, int flags, int savingThrowType, int savingThrowEffect, Item projectileWeapon) {
+	int s = flags & 0x100 ? calcDamageModifers(charIndexOrTimes, m, itemOrPips, _items[itemOrPips].type, offs, projectileWeapon) : rollDice(charIndexOrTimes, itemOrPips, offs);
 	EoBMonsterProperty *p = &_monsterProps[m->type];
 
 	if (savingThrowType != 5) {
@@ -2608,23 +2670,23 @@ int EoBCoreEngine::calcMonsterDamage(EoBMonsterInPlay *m, int times, int pips, i
 			s = savingThrowReduceDamage(savingThrowEffect, s);
 	}
 
-	if ((flags & 0x110) == 0x110) {
-		if (!calcDamageCheckItemType(_items[pips].type))
+	if ((flags & 0x110) == 0x110) { /* damage is inflicted by a flying object */
+		if (!calcDamageCheckItemType(_items[itemOrPips].type))
 			s = 1;
 	}
 
-	if ((flags & 0x100) && (!(_itemTypes[_items[pips].type].allowedClasses & 4 /* bug in original code ??*/))
+	if ((flags & 0x100) && (!(_itemTypes[_items[itemOrPips].type].allowedClasses & 4 /* bug in original code? */))
 	    && ((_flags.gameID == GI_EOB2 && (p->immunityFlags & 0x100)) || (_flags.gameID == GI_EOB1 && (p->capsFlags & 4))))
 		s >>= 1;
 
 	if (p->immunityFlags & 0x2000) {
 		if (flags & 0x100) {
-			if (_items[pips].value < 3)
+			if (_items[itemOrPips].value < 3)
 				s >>= 2;
-			if (_items[pips].value == 3)
+			if (_items[itemOrPips].value == 3)
 				s >>= 1;
 			if (s == 0)
-				s = _items[pips].value;
+				s = _items[itemOrPips].value;
 
 		} else {
 			s >>= 1;
@@ -2648,14 +2710,28 @@ int EoBCoreEngine::calcMonsterDamage(EoBMonsterInPlay *m, int times, int pips, i
 	return s;
 }
 
-int EoBCoreEngine::calcDamageModifers(int charIndex, EoBMonsterInPlay *m, int item, int itemType, int useStrModifier) {
+int EoBCoreEngine::calcDamageModifers(int charIndex, EoBMonsterInPlay *m, int item, int itemType, int useStrModifier, Item projectileWeapon) {
 	int s = (useStrModifier && (charIndex != -1)) ? getStrDamageModifier(charIndex) : 0;
 	if (item) {
+		/*
+		 * If the damaging item was launched from a projectile weapon,
+		 * roll damage dice from that projectile weapon type and
+		 * add any damage bonus from that particular projectile weapon.
+		 */
+		if (_configADDRuleEnhancements && projectileWeapon != kItemNone) {
+			itemType = normalizeProjectileWeaponType(_items[projectileWeapon].type);
+			s += _items[projectileWeapon].value;
+			debugC(1, kDebugLevelMain, "calcDamageModifers: damage bonus from projectile weapon: %d", _items[projectileWeapon].value);
+		}
 		EoBItemType *p = &_itemTypes[itemType];
-		int t = m ? m->type : 0;
-		s += ((m && (_monsterProps[t].capsFlags & 1)) ? rollDice(p->dmgNumDiceL, p->dmgNumPipsL, p->dmgIncS /* bug in original code ? */) :
-		      rollDice(p->dmgNumDiceS, p->dmgNumPipsS, p->dmgIncS));
+		int monsterType = m ? m->type : 0;
+		bool largeMonster = m && (_monsterProps[monsterType].capsFlags & 1);
+		int dieRoll = largeMonster ? rollDice(p->dmgNumDiceL, p->dmgNumPipsL, p->dmgIncS /* bug in original code ? */) :
+		              rollDice(p->dmgNumDiceS, p->dmgNumPipsS, p->dmgIncS);
+		s += dieRoll;
+		debugC(1, kDebugLevelMain, "calcDamageModifers: damage roll (d%d): %d", largeMonster ? p->dmgNumPipsL : p->dmgNumPipsS, dieRoll);
 		s += _items[item].value;
+		debugC(1, kDebugLevelMain, "calcDamageModifers: item damage bonus: %d", _items[item].value);
 	} else {
 		s += rollDice(1, 2);
 	}
@@ -2693,8 +2769,28 @@ int EoBCoreEngine::getSaveThrowModifier(int hpModifier, int level, int type) {
 }
 
 bool EoBCoreEngine::calcDamageCheckItemType(int itemType) {
+	/*
+	 * This check signals to the damage calculation code if the damage dice
+	 * from the flying object item type should be used for damage calculation.
+	 *
+	 * Projectile damage dice is recorded on the projectile weapon type,
+	 * rather than on the projectile ammo item type, possibly in an attempt
+	 * to differentiate between e.g. a rock thrown by hand from a rock fired
+	 * from a sling.
+	 *
+	 * Since in the original games this check permits both projectiles and
+	 * projectile weapons, when a projectile weapon is thrown by dropping it
+	 * onto the 3-D view, it may inflict the damage that the projectile itself
+	 * should inflict.
+	 *
+	 * Excluding projectile weapons from this check makes a thrown projectile
+	 * weapon deal 1 damage.
+	 */
+	const unsigned int kItemTypeAmmo = 2;
+	const unsigned int kItemTypeProjectileWeapon = 3;
 	itemType = _itemTypes[itemType].extraProperties & 0x7F;
-	return (itemType == 2 || itemType == 3) ? true : false;
+	if (_configADDRuleEnhancements) return itemType == kItemTypeAmmo;
+	return itemType == kItemTypeAmmo || itemType == kItemTypeProjectileWeapon;
 }
 
 int EoBCoreEngine::savingThrowReduceDamage(int savingThrowEffect, int damage) {
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 611fad43fa9..27e286e412b 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -34,6 +34,14 @@ class Keymap;
 
 namespace Kyra {
 
+const int8 ITEM_TYPE_BOW = 0;
+const int8 ITEM_TYPE_LONG_SWORD = 1;
+const int8 ITEM_TYPE_SHORT_SWORD = 2;
+const int8 ITEM_TYPE_SLING = 7;
+const int8 ITEM_TYPE_ARROW = 16;
+const int8 ITEM_TYPE_ROCK= 18;
+const int8 ITEM_TYPE_RATIONS = 31;
+
 #define releaseShpArr(shapes, num) \
 if (shapes) { \
 	for (int iii = 0; iii < num; iii++) { \
@@ -481,7 +489,7 @@ protected:
 	bool checkInventoryForRings(int charIndex, int itemValue);
 	void eatItemInHand(int charIndex);
 
-	bool launchObject(int charIndex, Item item, uint16 startBlock, int startPos, int dir, int type);
+	bool launchObject(int charIndex, Item item, uint16 startBlock, int startPos, int dir, int type, Item projectileWeapon = kItemNone);
 	void launchMagicObject(int charIndex, int type, uint16 startBlock, int startPos, int dir);
 	bool updateObjectFlight(EoBFlyingObject *fo, int block, int pos);
 	bool updateFlyingObjectHitTest(EoBFlyingObject *fo, int block, int pos);
@@ -988,16 +996,21 @@ protected:
 	void useSlotWeapon(int charIndex, int slotIndex, Item item);
 	int closeDistanceAttack(int charIndex, Item item);
 	int thrownAttack(int charIndex, int slotIndex, Item item);
+	int normalizeProjectileWeaponType(int itemType);
 	int projectileWeaponAttack(int charIndex, Item item);
 	virtual void playStrikeAnimation(uint8 pos, Item itm) {}
 
 	void inflictMonsterDamage(EoBMonsterInPlay *m, int damage, bool giveExperience);
-	void calcAndInflictMonsterDamage(EoBMonsterInPlay *m, int times, int pips, int offs, int flags, int savingThrowType, int savingThrowEffect);
-	void calcAndInflictCharacterDamage(int charIndex, int times, int itemOrPips, int useStrModifierOrBase, int flags, int savingThrowType, int savingThrowEffect);
-	int calcCharacterDamage(int charIndex, int times, int itemOrPips, int useStrModifierOrBase, int flags, int savingThrowType, int damageType);
+	void calcAndInflictMonsterDamage(EoBMonsterInPlay *m, int times, int pips, int offs, int flags, int savingThrowType, int savingThrowEffect, Item projectileWeapon = kItemNone);
+	void calcAndInflictCharacterDamage(int charIndex, int times, int itemOrPips, int useStrModifierOrBase, int flags, int savingThrowType, int savingThrowEffect, Item projectileWeapon = kItemNone);
+	int calcCharacterDamage(int charIndex, int times, int itemOrPips, int useStrModifierOrBase, int flags, int savingThrowType, int damageType, Item projectileWeapon = kItemNone);
 	void inflictCharacterDamage(int charIndex, int damage);
 
-	bool characterAttackHitTest(int charIndex, int monsterIndex, int item, int attackType);
+	bool isElf(int charIndex);
+	bool isBow(Item projectileWeapon);
+	bool isSword(Item item);
+
+	bool characterAttackHitTest(int charIndex, int monsterIndex, int item, int attackType, Item projectileWeapon = kItemNone);
 	bool monsterAttackHitTest(EoBMonsterInPlay *m, int charIndex);
 	bool flyingObjectMonsterHit(EoBFlyingObject *fo, int monsterIndex);
 	bool flyingObjectPartyHit(EoBFlyingObject *fo);
@@ -1006,8 +1019,8 @@ protected:
 	void monsterSpellCast(EoBMonsterInPlay *m, int type);
 	void statusAttack(int charIndex, int attackStatusFlags, const char *attackStatusString, int savingThrowType, uint32 effectDuration, int restoreEvent, int noRefresh);
 
-	int calcMonsterDamage(EoBMonsterInPlay *m, int times, int pips, int offs, int flags, int savingThrowType, int savingThrowEffect);
-	int calcDamageModifers(int charIndex, EoBMonsterInPlay *m, int item, int itemType, int useStrModifier);
+	int calcMonsterDamage(EoBMonsterInPlay *m, int times, int pips, int offs, int flags, int savingThrowType, int savingThrowEffect, Item projectileWeapon = kItemNone);
+	int calcDamageModifers(int charIndex, EoBMonsterInPlay *m, int item, int itemType, int useStrModifier, Item projectileWeapon);
 	bool trySavingThrow(void *target, int hpModifier, int level, int type, int race);
 	bool specialAttackSavingThrow(int charIndex, int type);
 	int getSaveThrowModifier(int hpModifier, int level, int type);
diff --git a/engines/kyra/engine/items_eob.cpp b/engines/kyra/engine/items_eob.cpp
index a2a9949a68a..b8788efb353 100644
--- a/engines/kyra/engine/items_eob.cpp
+++ b/engines/kyra/engine/items_eob.cpp
@@ -98,7 +98,8 @@ void EoBCoreEngine::loadItemDefs() {
 	uint16 numTypes = s->readUint16();
 
 	delete[] _itemTypes;
-	_itemTypes = new EoBItemType[65]();
+	size_t itemTypeTableSize = 65;
+	_itemTypes = new EoBItemType[itemTypeTableSize]();
 
 	for (int i = 0; i < numTypes; i++) {
 		_itemTypes[i].invFlags = s->readUint16();
@@ -116,6 +117,24 @@ void EoBCoreEngine::loadItemDefs() {
 		_itemTypes[i].extraProperties = s->readUint16();
 	}
 
+	/*
+	 * In the original Eye of the Beholder 1 data files, bow and sling item
+	 * types have damage dice that are not in line with AD&D 2nd edition rules:
+	 *
+	 * Bow:   dmgNumPipsS = 8, dmgNumPipsL = 10
+	 * Sling: dmgNumPipsS = 6, dmgNumPipsL = 6
+	 */
+	if (_flags.gameID == GI_EOB1 && _configADDRuleEnhancements) {
+		debugC(1, kDebugLevelMain, "patching EotB 1 bow   (%d), old dice S d%d, L d%d, new dice S d6, L d6",
+			   ITEM_TYPE_BOW, _itemTypes[ITEM_TYPE_BOW].dmgNumPipsS, _itemTypes[ITEM_TYPE_BOW].dmgNumPipsL);
+		_itemTypes[ITEM_TYPE_BOW].dmgNumPipsS = 6;
+		_itemTypes[ITEM_TYPE_BOW].dmgNumPipsL = 6;
+		debugC(1, kDebugLevelMain, "patching EotB 1 sling (%d), old dice S d%d, L  d%d, new dice S d4, L d4",
+			   ITEM_TYPE_SLING, _itemTypes[ITEM_TYPE_SLING].dmgNumPipsS, _itemTypes[ITEM_TYPE_SLING].dmgNumPipsL);
+		_itemTypes[ITEM_TYPE_SLING].dmgNumPipsS = 4;
+		_itemTypes[ITEM_TYPE_SLING].dmgNumPipsL = 4;
+	}
+
 	delete s;
 }
 
@@ -594,7 +613,7 @@ void EoBCoreEngine::eatItemInHand(int charIndex) {
 	}
 }
 
-bool EoBCoreEngine::launchObject(int charIndex, Item item, uint16 startBlock, int startPos, int dir, int type) {
+bool EoBCoreEngine::launchObject(int charIndex, Item item, uint16 startBlock, int startPos, int dir, int type, Item projectileWeapon) {
 	EoBFlyingObject *t = _flyingObjects;
 	int slot = 0;
 	for (; slot < 10; slot++) {
@@ -620,6 +639,8 @@ bool EoBCoreEngine::launchObject(int charIndex, Item item, uint16 startBlock, in
 	t->attackerId = charIndex;
 	t->callBackIndex = 0;
 
+	t->projectileWeapon = projectileWeapon;
+
 	snd_playSoundEffect(type == 7 ? 26 : 11);
 	return true;
 }
diff --git a/engines/kyra/engine/kyra_rpg.h b/engines/kyra/engine/kyra_rpg.h
index d132e613572..ab3bdb87dd4 100644
--- a/engines/kyra/engine/kyra_rpg.h
+++ b/engines/kyra/engine/kyra_rpg.h
@@ -83,7 +83,7 @@ struct EoBFlyingObject {
 	int8 callBackIndex;
 	uint8 curPos;
 	uint8 flags;
-	uint8 unused;
+	Item projectileWeapon;
 };
 
 struct KyraRpgGUISettings {
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 3a75a34cf1c..2c4522cf43b 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -2255,7 +2255,7 @@ int GUI_EoB::simpleMenu_getMouseItem(int sd) {
 
 	if (column == _menuColumns)
 		return -1;
-	
+
 	int yrelcol = yrel - _menuColumnOffset[column] * lineH;
 
 	if (yrelcol >= 0 && yrelcol < _menuLines[column] * lineH)
@@ -4510,7 +4510,7 @@ void GUI_EoB::displayTextBox(int id, int, bool) {
 	Common::Point txtPos((dm->sx << 3) + 5, dm->sy + 5);
 	if (_vm->game() == GI_EOB2 && _vm->gameFlags().platform == Common::kPlatformPC98) {
 		for (size_t p = tmp.find("  "); p != Common::String::npos; p = tmp.find("  "))
-			tmp.deleteChar(p);		
+			tmp.deleteChar(p);
 		txtPos = Common::Point(dm->sx << 3, (dm->sy + 16) & ~7);
 	}
 
diff --git a/engines/kyra/gui/saveload.cpp b/engines/kyra/gui/saveload.cpp
index 19907ea3074..61665019061 100644
--- a/engines/kyra/gui/saveload.cpp
+++ b/engines/kyra/gui/saveload.cpp
@@ -28,7 +28,7 @@
 #include "graphics/thumbnail.h"
 #include "graphics/surface.h"
 
-#define CURRENT_SAVE_VERSION 22
+#define CURRENT_SAVE_VERSION 23
 
 #define GF_FLOPPY  (1 <<  0)
 #define GF_TALKIE  (1 <<  1)
diff --git a/engines/kyra/gui/saveload_eob.cpp b/engines/kyra/gui/saveload_eob.cpp
index 879323132d4..935fd15afa3 100644
--- a/engines/kyra/gui/saveload_eob.cpp
+++ b/engines/kyra/gui/saveload_eob.cpp
@@ -262,7 +262,11 @@ Common::Error EoBCoreEngine::loadGameState(int slot) {
 			m->callBackIndex = in.readSByte();
 			m->curPos = in.readByte();
 			m->flags = in.readByte();
-			m->unused = in.readByte();
+			if (header.version >= 23) {
+				m->projectileWeapon = in.readSint16BE();
+			} else {
+				m->projectileWeapon = in.readByte();
+			}
 		}
 
 		for (int ii = 0; ii < 5; ii++) {
@@ -519,7 +523,7 @@ Common::Error EoBCoreEngine::saveGameStateIntern(int slot, const char *saveName,
 			out->writeSByte(m->callBackIndex);
 			out->writeByte(m->curPos);
 			out->writeByte(m->flags);
-			out->writeByte(m->unused);
+			out->writeSint16BE(m->projectileWeapon);
 		}
 
 		for (int ii = 0; ii < 5; ii++) {




More information about the Scummvm-git-logs mailing list