ULTIMA1: Refactored Character & Party classes to allow for derived children

This commit is contained in:
dreammaster 2020-01-21 07:05:42 +00:00 committed by Paul Gilbert
parent e11a0b7791
commit ff26ae7bae
33 changed files with 322 additions and 133 deletions

View File

@ -22,6 +22,7 @@ MODULE_OBJS += \
shared/core/message_target.o \
shared/core/mouse_cursor.o \
shared/core/named_item.o \
shared/core/party.o \
shared/core/tree_item.o \
shared/core/utils.o \
shared/core/widgets.o \
@ -67,6 +68,7 @@ MODULE_OBJS += \
ultima1/actions/quit.o \
ultima1/actions/ready.o \
ultima1/actions/stats.o \
ultima1/core/party.o \
ultima1/core/resources.o \
ultima1/maps/map.o \
ultima1/maps/map_base.o \

View File

@ -20,7 +20,7 @@
*
*/
#include "ultima/shared/core/character.h"
#include "ultima/shared/core/party.h"
namespace Ultima {
namespace Shared {

View File

@ -30,8 +30,6 @@
namespace Ultima {
namespace Shared {
class Game;
/**
* Base class for class types that have a quantity
*/
@ -195,8 +193,6 @@ public:
uint getLevel() const { return (_experience / 1000) + 1; }
};
typedef Common::Array<Character> CharacterArray;
} // End of namespace Shared
} // End of namespace Ultima

View File

@ -0,0 +1,68 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "ultima/shared/core/party.h"
namespace Ultima {
namespace Shared {
Party::~Party() {
for (uint idx = 0; idx < _characters.size(); ++idx)
delete _characters[idx];
}
void Party::add(Character *c) {
_characters.push_back(c);
}
void Party::synchronize(Common::Serializer &s) {
uint partyCount = _characters.size();
s.syncAsByte(partyCount);
if (s.isLoading())
assert(partyCount == _characters.size());
// Iterate through the characters of the party
for (uint idx = 0; idx < _characters.size(); ++idx)
_characters[idx]->synchronize(s);
}
bool Party::isDead() const {
for (uint idx = 0; idx < _characters.size(); ++idx) {
if ((*this)[idx]._hitPoints > 0)
return false;
}
return true;
}
bool Party::isFoodless() const {
for (uint idx = 0; idx < _characters.size(); ++idx) {
if ((*this)[idx]._food > 0)
return false;
}
return true;
}
} // End of namespace Shared
} // End of namespace Ultima

View File

@ -0,0 +1,77 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef ULTIMA_SHARED_CORE_PARTY_H
#define ULTIMA_SHARED_CORE_PARTY_H
#include "common/array.h"
#include "common/str.h"
#include "common/serializer.h"
#include "ultima/shared/core/character.h"
namespace Ultima {
namespace Shared {
/**
* Base class for the player's party
*/
class Party {
private:
Common::Array<Character *> _characters;
public:
~Party();
/**
* Add a character to the party
*/
void add(Character *c);
/**
* Casting operator for convenient access to the first character
*/
operator Character &() const { return *_characters.front(); }
/**
* Casting operator for convenient access to the first character
*/
operator Character *() const { return _characters.front(); }
/**
* Synchronize data
*/
void synchronize(Common::Serializer &s);
/**
* Returns true if the party is dead
*/
bool isDead() const;
/**
* Returns true if the party has no food
*/
bool isFoodless() const;
};
} // End of namespace Shared
} // End of namespace Ultima
#endif

View File

@ -36,7 +36,7 @@ void Creature::update(bool isPreUpdate) {
// Check whether creature can attack
movement();
_isAttacking = attackDistance() != 0;
} else if (_isAttacking && !_game->_party.isDead()) {
} else if (_isAttacking && !_game->_party->isDead()) {
attack();
}
}

View File

@ -125,46 +125,9 @@ void Game::endOfTurn() {
}
void Game::synchronize(Common::Serializer &s) {
_party.synchronize(s);
_party->synchronize(s);
_map->synchronize(s);
}
/*-------------------------------------------------------------------*/
Party::Party() : CharacterArray() {
resize(1);
_currentCharacter = &(*this)[0];
}
bool Party::isDead() const {
for (uint idx = 0; idx < size(); ++idx) {
if ((*this)[idx]._hitPoints > 0)
return false;
}
return true;
}
bool Party::isFoodless() const {
for (uint idx = 0; idx < size(); ++idx) {
if ((*this)[idx]._food > 0)
return false;
}
return true;
}
void Party::synchronize(Common::Serializer &s) {
// Store the party count
uint partyCount = size();
s.syncAsByte(partyCount);
if (s.isLoading() && size() != partyCount)
resize(partyCount);
// Synchronize the data for each character
for (uint idx = 0; idx < size(); ++idx)
(*this)[idx].synchronize(s);
}
} // End of namespace Shared
} // End of namespace Ultima

View File

@ -24,7 +24,7 @@
#define ULTIMA_SHARED_EARLY_GAME_H
#include "ultima/shared/early/game_base.h"
#include "ultima/shared/core/character.h"
#include "ultima/shared/core/party.h"
namespace Ultima {
namespace Shared {
@ -37,34 +37,6 @@ namespace Maps {
class Map;
}
/**
* Party definition
*/
class Party : public CharacterArray {
public:
Party();
/**
* Currently active character. In earlier Ultima games, this is the single party member
*/
Character *_currentCharacter;
/**
* Returns true if the party is dead
*/
bool isDead() const;
/**
* Returns true if the party has no food
*/
bool isFoodless() const;
/**
* Handles loading and saving games
*/
void synchronize(Common::Serializer &s);
};
/**
* More specialized base class for earlier Ultima games
*/
@ -95,7 +67,7 @@ public:
/**
* Player party. In the earlier Ultima games, this is a single character
*/
Party _party;
Party *_party;
/**
* Pointer to the map manager for the game

View File

@ -36,7 +36,7 @@ void Creature::update(bool isPreUpdate) {
// Check whether creature can attack
movement();
_isAttacking = attackDistance() != 0;
} else if (_isAttacking && !_gameRef->_party.isDead()) {
} else if (_isAttacking && !_gameRef->_party->isDead()) {
attackParty();
}
}

View File

@ -36,7 +36,7 @@ END_MESSAGE_MAP()
bool Attack::AttackMsg(CAttackMsg &msg) {
Ultima1Game *game = static_cast<Ultima1Game *>(getGame());
const Shared::Character &c = *getGame()->_party._currentCharacter;
const Shared::Character &c = *getGame()->_party;
const Shared::Weapon &weapon = c._weapons[c._equippedWeapon];
addInfoMsg(Common::String::format("%s %s", game->_res->ACTION_NAMES[0], weapon._shortName.c_str()), false);

View File

@ -0,0 +1,34 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "ultima/ultima1/core/party.h"
#include "ultima/ultima1/game.h"
namespace Ultima {
namespace Ultima1 {
Party::Party(Ultima1Game *game) {
add(new Character(game));
}
} // End of namespace Ultima1
} // End of namespace Ultima

View File

@ -0,0 +1,65 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef ULTIMA_ULTIMA1_CORE_PARTY_H
#define ULTIMA_ULTIMA1_CORE_PARTY_H
#include "ultima/shared/core/party.h"
namespace Ultima {
namespace Ultima1 {
class Ultima1Game;
/**
* Implements the data for a playable character within the game
*/
class Character : public Shared::Character {
private:
Ultima1Game *_game;
public:
/**
* Constructor
*/
Character(Ultima1Game *game) : Shared::Character(), _game(game) {}
/**
* Gets the damage a given weapon does
*/
uint getWeaponDamage() const;
};
/**
* Implements the party
*/
class Party : public Shared::Party {
public:
/**
* Constructor
*/
Party(Ultima1Game *game);
};
} // End of namespace Ultima1
} // End of namespace Ultima
#endif

View File

@ -21,6 +21,7 @@
*/
#include "ultima/ultima1/game.h"
#include "ultima/ultima1/core/party.h"
#include "ultima/ultima1/core/resources.h"
#include "ultima/ultima1/maps/map.h"
#include "ultima/ultima1/u1gfx/view_game.h"
@ -42,6 +43,8 @@ EMPTY_MESSAGE_MAP(Ultima1Game, Shared::Game);
Ultima1Game::Ultima1Game() : Shared::Game() {
_res = new GameResources();
_map = new Maps::Ultima1Map(this);
_party = new Party(this);
_textCursor = new U1Gfx::U1TextCursor(_textColor, _bgColor);
g_vm->_screen->setCursor(_textCursor);
@ -70,6 +73,7 @@ Ultima1Game::~Ultima1Game() {
delete _map;
delete _gameView;
delete _party;
}
void Ultima1Game::synchronize(Common::Serializer &s) {
@ -90,7 +94,7 @@ void Ultima1Game::starting(bool isLoading) {
}
void Ultima1Game::setup() {
Shared::Character &c = *_party._currentCharacter;
Shared::Character &c = *_party;
// Weapons setup
c._weapons.resize(16);

View File

@ -82,7 +82,7 @@ void MapBase::attack(int direction) {
}
void MapBase::cast() {
const Shared::Character &c = *_game->_party._currentCharacter;
const Shared::Character &c = *_game->_party;
Shared::Spell &spell = *c._spells[c._equippedSpell];
addInfoMsg(Common::String::format(" %s", spell._name.c_str()), false);
@ -99,7 +99,7 @@ void MapBase::cast() {
}
void MapBase::castSpell(uint spellId) {
const Shared::Character &c = *_game->_party._currentCharacter;
const Shared::Character &c = *_game->_party;
static_cast<Spells::Spell *>(c._spells[spellId])->cast(this);
}

View File

@ -222,7 +222,7 @@ void MapCity::load(Shared::Maps::MapId mapId) {
}
void MapCity::dropCoins(uint coins) {
Shared::Character &c = *_game->_party._currentCharacter;
Shared::Character &c = *_game->_party;
U1MapTile tile;
getTileAt(getPosition(), &tile);
@ -300,7 +300,7 @@ void MapCastle::synchronize(Common::Serializer &s) {
}
void MapCastle::dropCoins(uint coins) {
Shared::Character &c = *_game->_party._currentCharacter;
Shared::Character &c = *_game->_party;
U1MapTile tile;
getTileAt(getPosition(), &tile);

View File

@ -230,7 +230,7 @@ void MapDungeon::update() {
Point pt;
// Widgets in the dungeon are updated by row
for (pt.y = 1; pt.y < ((int)height() - 1) && !_game->_party.isFoodless(); pt.y++) {
for (pt.y = 1; pt.y < ((int)height() - 1) && !_game->_party->isFoodless(); pt.y++) {
for (pt.x = 1; pt.x < ((int)width() - 1); pt.x++) {
// Check for a widget at the given position
getTileAt(pt, &tile);
@ -294,12 +294,12 @@ void MapDungeon::climb() {
}
void MapDungeon::castSpell(uint spellId) {
const Shared::Character &c = *_game->_party._currentCharacter;
const Shared::Character &c = *_game->_party;
static_cast<Spells::Spell *>(c._spells[spellId])->dungeonCast(this);
}
void MapDungeon::leavingDungeon() {
Shared::Character &c = *_game->_party._currentCharacter;
Shared::Character &c = *_game->_party;
// Don't allow the hit points addition to push the hit point total beyond 9999
if (c._hitPoints + _dungeonExitHitPoints > 9999)

View File

@ -34,7 +34,7 @@ Prayer::Prayer() : Spell(SPELL_PRAYER) {
}
void Prayer::cast(Maps::MapBase *map) {
Shared::Character &c = *_game->_party._currentCharacter;
Shared::Character &c = *_game->_party;
addInfoMsg("");
addInfoMsg(_game->_res->SPELL_PHRASES[0], false);
@ -78,7 +78,7 @@ void Prayer::dungeonCast(Maps::MapDungeon *map) {
if (spellId == SPELL_STEAL)
spellId = SPELL_LADDER_DOWN;
const Shared::Character &c = *_game->_party._currentCharacter;
const Shared::Character &c = *_game->_party;
static_cast<Spell *>(c._spells[spellId])->dungeonCast(map);
}

View File

@ -23,7 +23,7 @@
#ifndef ULTIMA_ULTIMA1_U1DIALOGS_SPELL_H
#define ULTIMA_ULTIMA1_U1DIALOGS_SPELL_H
#include "ultima/shared/core/character.h"
#include "ultima/shared/core/party.h"
#include "ultima/ultima1/maps/map_base.h"
#include "ultima/ultima1/maps/map_dungeon.h"

View File

@ -39,7 +39,7 @@ Drop::Drop(Ultima1Game *game) : FullScreenDialog(game), _mode(SELECT), _textInpu
}
bool Drop::KeypressMsg(CKeypressMsg &msg) {
Shared::Character &c = *_game->_party._currentCharacter;
Shared::Character &c = *_game->_party;
switch (_mode) {
case SELECT:
@ -99,7 +99,7 @@ bool Drop::KeypressMsg(CKeypressMsg &msg) {
}
bool Drop::TextInputMsg(CTextInputMsg &msg) {
Shared::Character &c = *_game->_party._currentCharacter;
Shared::Character &c = *_game->_party;
assert(_mode == DROP_PENCE);
Ultima1Game *game = _game;
Maps::Ultima1Map *map = getMap();
@ -130,7 +130,7 @@ void Drop::setMode(Mode mode) {
setDirty();
_mode = mode;
const Shared::Character &c = *_game->_party._currentCharacter;
const Shared::Character &c = *_game->_party;
switch (mode) {
case DROP_WEAPON:
if (c._weapons.hasNothing()) {
@ -198,7 +198,7 @@ void Drop::drawDropWeapon() {
drawFrame(_game->_res->ACTION_NAMES[3]);
// Count the number of different types of weapons
const Shared::Character &c = *_game->_party._currentCharacter;
const Shared::Character &c = *_game->_party;
int numLines = 0;
for (uint idx = 1; idx < c._weapons.size(); ++idx) {
if (c._weapons[idx]._quantity)
@ -229,7 +229,7 @@ void Drop::drawDropArmor() {
drawFrame(_game->_res->ACTION_NAMES[3]);
// Count the number of different types of armor
const Shared::Character &c = *_game->_party._currentCharacter;
const Shared::Character &c = *_game->_party;
int numLines = 0;
for (uint idx = 1; idx < c._armor.size(); ++idx) {
if (c._armor[idx]._quantity)

View File

@ -39,7 +39,7 @@ Ready::Ready(Ultima1Game *game) : FullScreenDialog(game), _mode(SELECT) {
}
bool Ready::KeypressMsg(CKeypressMsg &msg) {
Shared::Character &c = *_game->_party._currentCharacter;
Shared::Character &c = *_game->_party;
switch (_mode) {
case SELECT:
@ -99,7 +99,7 @@ void Ready::setMode(Mode mode) {
setDirty();
_mode = mode;
const Shared::Character &c = *_game->_party._currentCharacter;
const Shared::Character &c = *_game->_party;
switch (mode) {
case READY_WEAPON:
if (c._weapons.hasNothing()) {
@ -158,7 +158,7 @@ void Ready::drawReadyWeapon() {
drawFrame(_game->_res->ACTION_NAMES[17]);
// Count the number of different types of weapons
const Shared::Character &c = *_game->_party._currentCharacter;
const Shared::Character &c = *_game->_party;
int numLines = 0;
for (uint idx = 0; idx < c._weapons.size(); ++idx) {
if (c._weapons[idx]._quantity)
@ -190,7 +190,7 @@ void Ready::drawReadyArmor() {
drawFrame(_game->_res->ACTION_NAMES[17]);
// Count the number of different types of weapons
const Shared::Character &c = *_game->_party._currentCharacter;
const Shared::Character &c = *_game->_party;
int numLines = 0;
for (uint idx = 0; idx < c._armor.size(); ++idx) {
if (c._armor[idx]._quantity)
@ -222,7 +222,7 @@ void Ready::drawReadySpell() {
drawFrame(_game->_res->ACTION_NAMES[17]);
// Count the number of different types of spells
const Shared::Character &c = *_game->_party._currentCharacter;
const Shared::Character &c = *_game->_party;
int numLines = 0;
for (uint idx = 0; idx < c._spells.size(); ++idx) {
if (c._spells[idx]->_quantity)

View File

@ -56,7 +56,7 @@ void countTransport(Maps::MapOverworld *overworldMap, Common::Array<Stats::StatE
}
void Stats::load() {
const Shared::Character &c = *_game->_party._currentCharacter;
const Shared::Character &c = *_game->_party;
Maps::MapOverworld *overworld = getMap()->getOverworldMap();
// Basic attributes
@ -127,7 +127,7 @@ Common::String Stats::formatStat(const char *name, uint value) {
void Stats::draw() {
drawFrame(_game->_res->INVENTORY);
Shared::Gfx::VisualSurface s = getSurface();
const Shared::Character &c = *_game->_party._currentCharacter;
const Shared::Character &c = *_game->_party;
// Player name and description
s.writeString(Common::String::format(_game->_res->PLAYER, c._name.c_str()),

View File

@ -39,7 +39,7 @@ Status::Status(Shared::TreeItem *parent) : Shared::Gfx::VisualItem("Status", Tex
bool Status::FrameMsg(CFrameMsg &msg) {
// If any of the figures have changed, mark the display as dirty
const Ultima1Game *game = static_cast<const Ultima1Game *>(getGame());
const Shared::Character &c = *game->_party._currentCharacter;
const Shared::Character &c = *game->_party;
if (c._hitPoints != _hitPoints || c._food != _food || c._experience != _experience || c._coins != _coins)
setDirty(true);
@ -49,7 +49,7 @@ bool Status::FrameMsg(CFrameMsg &msg) {
void Status::draw() {
Ultima1Game *game = static_cast<Ultima1Game *>(getGame());
const Shared::Character &c = *game->_party._currentCharacter;
const Shared::Character &c = *game->_party;
// Update the local copy of the fields
_hitPoints = c._hitPoints;

View File

@ -54,12 +54,13 @@ void ViewCharacterGeneration::setMode(uint flags) {
setDirty();
Ultima1Game *game = static_cast<Ultima1Game *>(getGame());
Shared::Character &c = *game->_party;
Shared::Gfx::TextCursor *textCursor = game->_textCursor;
textCursor->setVisible(false);
if (flags & FLAG_FRAME) {
// Set up character and attributes pointers
_character = game->_party._currentCharacter;
_character = &c;
_attributes[0] = &_character->_strength;
_attributes[1] = &_character->_agility;
_attributes[2] = &_character->_stamina;

View File

@ -25,7 +25,7 @@
#include "ultima/shared/gfx/visual_container.h"
#include "ultima/shared/gfx/text_input.h"
#include "ultima/shared/core/character.h"
#include "ultima/shared/core/party.h"
#include "graphics/managed_surface.h"
namespace Ultima {

View File

@ -52,12 +52,12 @@ void Bard::movement() {
}
bool Bard::stealWeapon() {
Shared::Character *c = _game->_party._currentCharacter;
for (uint idx = 1; idx < c->_weapons.size(); ++idx) {
if (c->_weapons[idx]._quantity > 0 && (int)idx != c->_equippedWeapon) {
c->_weapons[idx].decrQuantity();
Shared::Character &c = *_game->_party;
for (uint idx = 1; idx < c._weapons.size(); ++idx) {
if (c._weapons[idx]._quantity > 0 && (int)idx != c._equippedWeapon) {
c._weapons[idx].decrQuantity();
if (_game->getRandomNumber(1, 255) < (c->_agility + 128)) {
if (_game->getRandomNumber(1, 255) < (c._agility + 128)) {
}

View File

@ -41,7 +41,7 @@ DungeonChest::DungeonChest(Ultima1Game *game, Maps::MapBase *map) : DungeonItem(
bool DungeonChest::open() {
Ultima1Game *game = static_cast<Ultima1Game *>(_game);
Maps::MapDungeon *map = static_cast<Maps::MapDungeon *>(getMap());
Shared::Character &c = *_game->_party._currentCharacter;
Shared::Character &c = *_game->_party;
if (_game->getRandomNumber(1, 75) <= c._agility || c._class == CLASS_THIEF) {
// Successfully opened

View File

@ -126,7 +126,7 @@ void DungeonMonster::attackParty() {
Ultima1Game *game = static_cast<Ultima1Game *>(_game);
Point playerPos = _map->_playerWidget->_position;
//Point delta = playerPos - _position;
Shared::Character *c = _game->_party._currentCharacter;
Shared::Character &c = *_game->_party;
uint threshold, damage;
bool isHit = true;
@ -142,7 +142,7 @@ void DungeonMonster::attackParty() {
addInfoMsg(Common::String::format(game->_res->ATTACKED_BY, _name.c_str()));
_game->playFX(3);
threshold = (c->_stamina / 2) + (c->_equippedArmor * 8) + 56;
threshold = (c._stamina / 2) + (c._equippedArmor * 8) + 56;
if (_game->getRandomNumber(1, 255) > threshold) {
threshold = _game->getRandomNumber(1, 255);
@ -151,28 +151,28 @@ void DungeonMonster::attackParty() {
damage = _game->getRandomNumber(_monsterId + 1, 255);
}
if (_monsterId == MONSTER_GELATINOUS_CUBE && c->isArmorEquipped()) {
if (_monsterId == MONSTER_GELATINOUS_CUBE && c.isArmorEquipped()) {
addInfoMsg(game->_res->ARMOR_DESTROYED);
c->_armor[c->_equippedArmor].decrQuantity();
c->removeArmor();
c._armor[c._equippedArmor].decrQuantity();
c.removeArmor();
isHit = false;
} else if (_monsterId == MONSTER_GREMLIN) {
addInfoMsg(game->_res->GREMLIN_STOLE);
c->_food /= 2;
c._food /= 2;
isHit = false;
} else if (_monsterId == MONSTER_MIND_WHIPPER && threshold < 128) {
addInfoMsg(game->_res->MENTAL_ATTACK);
c->_intelligence = (c->_intelligence / 2) + 5;
c._intelligence = (c._intelligence / 2) + 5;
isHit = false;
} else if (_monsterId == MONSTER_THIEF) {
// Thief will steal the first spare weapon player has that isn't equipped
for (int weaponNum = 1; weaponNum < (int)c->_weapons.size(); ++weaponNum) {
if (weaponNum != c->_equippedWeapon && c->_weapons[weaponNum]._quantity > 0) {
for (int weaponNum = 1; weaponNum < (int)c._weapons.size(); ++weaponNum) {
if (weaponNum != c._equippedWeapon && c._weapons[weaponNum]._quantity > 0) {
// TODO: May need to worry about word wrapping long line
addInfoMsg(Common::String::format(game->_res->THIEF_STOLE,
Shared::isVowel(c->_weapons[weaponNum]._longName.firstChar()) ? game->_res->AN : game->_res->A
Shared::isVowel(c._weapons[weaponNum]._longName.firstChar()) ? game->_res->AN : game->_res->A
));
c->_weapons[weaponNum].decrQuantity();
c._weapons[weaponNum].decrQuantity();
break;
}
}
@ -180,7 +180,7 @@ void DungeonMonster::attackParty() {
if (isHit) {
addInfoMsg(Common::String::format("%s %2d %s", game->_res->HIT, damage, game->_res->DAMAGE));
c->_hitPoints -= damage;
c._hitPoints -= damage;
}
} else {
addInfoMsg(game->_res->MISSED);
@ -222,7 +222,7 @@ void DungeonMonster::attackMonster(uint effectNum, uint agility, uint damage) {
game->giveTreasure(amount, 0);
// Give experience
Shared::Character &c = *game->_party._currentCharacter;
Shared::Character &c = *game->_party;
uint experience = game->getRandomNumber(2, map->getLevel() * map->getLevel() + 10);
c._experience += experience;
map->_dungeonExitHitPoints = MIN(map->_dungeonExitHitPoints + experience * 2, 9999U);

View File

@ -54,16 +54,16 @@ void Guard::movement() {
void Guard::attackParty() {
Ultima1Game *game = static_cast<Ultima1Game *>(_game);
Shared::Character *c = game->_party._currentCharacter;
Shared::Character &c = *game->_party;
addInfoMsg(Common::String::format(game->_res->ATTACKED_BY, _name.c_str()));
game->playFX(7);
uint threshold = (c->_stamina / 2) + (c->_equippedArmor * 8) + 56;
uint threshold = (c._stamina / 2) + (c._equippedArmor * 8) + 56;
if (_game->getRandomNumber(1, 255) > threshold) {
int damage = _game->getRandomNumber(2, c->_hitPoints / 128 + 15);
int damage = _game->getRandomNumber(2, c._hitPoints / 128 + 15);
addInfoMsg(Common::String::format("%s...%2d %s", game->_res->HIT, damage, game->_res->DAMAGE));
game->playFX(2);
c->_hitPoints -= damage;
c._hitPoints -= damage;
} else {
addInfoMsg(game->_res->MISSED);
}

View File

@ -32,10 +32,11 @@ namespace Widgets {
EMPTY_MESSAGE_MAP(Merchant, Person);
bool Merchant::checkCuaghtStealing() {
Shared::Character &c = *_game->_party;
int randVal = _game->getRandomNumber(1, 255);
bool flag = areGuardsHostile() || randVal < 38;
if (!flag && _game->_party._currentCharacter->_class == CLASS_THIEF)
if (!flag && c._class == CLASS_THIEF)
return false;
if (!flag && randVal > 77)
return false;

View File

@ -46,10 +46,12 @@ void MerchantArmor::steal() {
}
void MerchantArmor::findArmor(bool checkStealing) {
Shared::Character &c = *_game->_party;
if (!checkStealing || !checkCuaghtStealing()) {
uint armorNum = _game->getRandomNumber(1, 5);
Common::String armorStr = _game->_res->ARMOR_NAMES[armorNum];
_game->_party._currentCharacter->_armor[armorNum].incrQuantity();
c._armor[armorNum].incrQuantity();
if (armorNum == 5)
armorStr = Common::String::format("%s %s", _game->_res->A, armorStr.c_str());

View File

@ -46,9 +46,11 @@ void MerchantGrocer::steal() {
}
void MerchantGrocer::findFood(bool checkStealing) {
Shared::Character &c = *_game->_party;
if (!checkStealing || !checkCuaghtStealing()) {
uint food = _game->getRandomNumber(2, 31);
_game->_party._currentCharacter->_food += food;
c._food += food;
addInfoMsg("");
addInfoMsg(Common::String::format(_game->_res->GROCERY_FIND_PACKS, food));
}

View File

@ -46,11 +46,12 @@ void MerchantWeapons::steal() {
}
void MerchantWeapons::findWeapon(bool checkStealing) {
Shared::Character &c = *_game->_party;
if (!checkStealing || !checkCuaghtStealing()) {
uint weaponNum = _game->getRandomNumber(1, 15);
const char *weaponStr = _game->_res->WEAPON_NAMES_ARTICLE[weaponNum];
_game->_party._currentCharacter->_weapons[weaponNum].incrQuantity();
c._weapons[weaponNum].incrQuantity();
addInfoMsg("");
addInfoMsg(Common::String::format(_game->_res->FIND, weaponStr));
}

View File

@ -22,6 +22,7 @@
#include "ultima/ultima1/widgets/overworld_monster.h"
#include "ultima/ultima1/widgets/hit.h"
#include "ultima/ultima1/core/party.h"
#include "ultima/ultima1/core/resources.h"
#include "ultima/ultima1/game.h"
#include "ultima/shared/maps/map_widget.h"
@ -69,7 +70,7 @@ void OverworldMonster::attackParty() {
Point tempDiff;
int maxDistance = attackDistance();
Shared::Maps::MapTile mapTile;
Shared::Character *c = _game->_party._currentCharacter;
Shared::Character &c = *_game->_party;
uint threshold, damage;
// Print out the monster attacking
@ -91,14 +92,14 @@ void OverworldMonster::attackParty() {
} while (++distance <= maxDistance && mapTile._tileId != 3 && (tempDiff.x != 0 || tempDiff.y != 0));
// Calculate damage threshold
threshold = (c->_stamina / 2) + (c->_equippedArmor * 8) + 56;
threshold = (c._stamina / 2) + (c._equippedArmor * 8) + 56;
if (tempDiff.x == 0 && tempDiff.y == 0 && _game->getRandomNumber(1, 255) > threshold) {
hit->_position = playerPos;
_game->playFX(2);
damage = _game->getRandomNumber(1, _attackStrength * 2 + 1);
game->_party._currentCharacter->_hitPoints -= damage;
c._hitPoints -= damage;
if (_name.size() > 8) {
addInfoMsg("");