Initial commit
This commit is contained in:
471
engines/ultima/ultima1/maps/map_city_castle.cpp
Normal file
471
engines/ultima/ultima1/maps/map_city_castle.cpp
Normal file
@@ -0,0 +1,471 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima1/maps/map_city_castle.h"
|
||||
#include "ultima/ultima1/maps/map_tile.h"
|
||||
#include "ultima/ultima1/core/resources.h"
|
||||
#include "ultima/ultima1/game.h"
|
||||
#include "ultima/ultima1/spells/spell.h"
|
||||
#include "ultima/ultima1/widgets/urban_player.h"
|
||||
#include "ultima/ultima1/widgets/person.h"
|
||||
#include "ultima/ultima1/widgets/bard.h"
|
||||
#include "ultima/ultima1/widgets/guard.h"
|
||||
#include "ultima/ultima1/widgets/king.h"
|
||||
#include "ultima/ultima1/widgets/princess.h"
|
||||
#include "ultima/ultima1/widgets/wench.h"
|
||||
#include "ultima/ultima1/widgets/merchant_armour.h"
|
||||
#include "ultima/ultima1/widgets/merchant_grocer.h"
|
||||
#include "ultima/ultima1/widgets/merchant_magic.h"
|
||||
#include "ultima/ultima1/widgets/merchant_tavern.h"
|
||||
#include "ultima/ultima1/widgets/merchant_transport.h"
|
||||
#include "ultima/ultima1/widgets/merchant_weapons.h"
|
||||
#include "ultima/ultima1/u1dialogs/drop.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima1 {
|
||||
namespace Maps {
|
||||
|
||||
void MapCityCastle::load(Shared::Maps::MapId mapId) {
|
||||
clear();
|
||||
Shared::Maps::MapBase::load(mapId);
|
||||
|
||||
setDimensions(Point(38, 18));
|
||||
_tilesPerOrigTile = Point(1, 1);
|
||||
}
|
||||
|
||||
void MapCityCastle::clear() {
|
||||
Shared::Maps::MapBase::clear();
|
||||
_guardsHostile = false;
|
||||
}
|
||||
|
||||
void MapCityCastle::loadWidgets() {
|
||||
// Set up widget for the player
|
||||
_playerWidget = new Widgets::UrbanPlayer(_game, this);
|
||||
addWidget(_playerWidget);
|
||||
|
||||
for (int idx = 0; idx < 15; ++idx) {
|
||||
const int *lp = _game->_res->LOCATION_PEOPLE[_mapStyle * 15 + idx];
|
||||
if (lp[0] == -1)
|
||||
break;
|
||||
|
||||
Widgets::Person *person;
|
||||
switch (lp[0]) {
|
||||
case 17:
|
||||
person = new Widgets::Guard(_game, this, lp[3]);
|
||||
break;
|
||||
case 19:
|
||||
person = new Widgets::Bard(_game, this, lp[3]);
|
||||
break;
|
||||
case 20:
|
||||
person = new Widgets::King(_game, this, lp[3]);
|
||||
break;
|
||||
case 21: {
|
||||
U1MapTile tile;
|
||||
getTileAt(Point(lp[1], lp[2]), &tile);
|
||||
|
||||
switch (tile._tileId) {
|
||||
case 55:
|
||||
person = new Widgets::MerchantArmour(_game, this, lp[3]);
|
||||
break;
|
||||
case 57:
|
||||
person = new Widgets::MerchantGrocer(_game, this, lp[3]);
|
||||
break;
|
||||
case 59:
|
||||
person = new Widgets::MerchantWeapons(_game, this, lp[3]);
|
||||
break;
|
||||
case 60:
|
||||
person = new Widgets::MerchantMagic(_game, this, lp[3]);
|
||||
break;
|
||||
case 61:
|
||||
person = new Widgets::MerchantTavern(_game, this, lp[3]);
|
||||
break;
|
||||
case 62:
|
||||
person = new Widgets::MerchantTransport(_game, this, lp[3]);
|
||||
break;
|
||||
default:
|
||||
error("Invalid merchant");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 22:
|
||||
person = new Widgets::Princess(_game, this, lp[3]);
|
||||
break;
|
||||
case 50:
|
||||
person = new Widgets::Wench(_game, this, lp[3]);
|
||||
break;
|
||||
default:
|
||||
error("Unknown NPC type %d", lp[0]);
|
||||
}
|
||||
|
||||
person->_position = Point(lp[1], lp[2]);
|
||||
addWidget(person);
|
||||
}
|
||||
}
|
||||
|
||||
void MapCityCastle::getTileAt(const Point &pt, Shared::Maps::MapTile *tile, bool includePlayer) {
|
||||
MapBase::getTileAt(pt, tile, includePlayer);
|
||||
|
||||
// Special handling for the cells indicating various merchant talk/steal positions
|
||||
if (tile->_tileDisplayNum >= 51)
|
||||
tile->_tileDisplayNum = 1;
|
||||
}
|
||||
|
||||
Point MapCityCastle::getViewportPosition(const Point &viewportSize) {
|
||||
Point &topLeft = _viewportPos._topLeft;
|
||||
|
||||
if (!_viewportPos.isValid() || _viewportPos._size != viewportSize) {
|
||||
// Calculate the new position
|
||||
topLeft.x = _playerWidget->_position.x - (viewportSize.x - 1) / 2;
|
||||
topLeft.y = _playerWidget->_position.y - (viewportSize.y - 1) / 2;
|
||||
|
||||
// Fixed maps, so constrain top left corner so the map fills the viewport. This will accommodate
|
||||
// future renderings with more tiles, or greater tile size
|
||||
topLeft.x = CLIP((int)topLeft.x, 0, (int)(width() - viewportSize.x));
|
||||
topLeft.y = CLIP((int)topLeft.y, 0, (int)(height() - viewportSize.y));
|
||||
|
||||
_viewportPos._mapId = _mapId;
|
||||
_viewportPos._size = viewportSize;
|
||||
}
|
||||
|
||||
return topLeft;
|
||||
}
|
||||
|
||||
void MapCityCastle::loadTownCastleData() {
|
||||
// Load the contents of the map
|
||||
Shared::File f("tcd.bin");
|
||||
f.seek(_mapStyle * 684);
|
||||
for (int x = 0; x < _size.x; ++x) {
|
||||
for (int y = 0; y < _size.y; ++y)
|
||||
_data[y][x] = f.readByte();
|
||||
}
|
||||
}
|
||||
|
||||
Widgets::Merchant *MapCityCastle::getStealMerchant() {
|
||||
U1MapTile tile;
|
||||
getTileAt(getPosition(), &tile);
|
||||
|
||||
// Scan for the correct merchant depending on the tile player is on
|
||||
switch (tile._tileId) {
|
||||
case 55:
|
||||
return dynamic_cast<Widgets::MerchantArmour *>(_widgets.findByClass(Widgets::MerchantArmour::type()));
|
||||
break;
|
||||
case 57:
|
||||
return dynamic_cast<Widgets::MerchantGrocer *>(_widgets.findByClass(Widgets::MerchantGrocer::type()));
|
||||
break;
|
||||
case 59:
|
||||
return dynamic_cast<Widgets::MerchantWeapons *>(_widgets.findByClass(Widgets::MerchantWeapons::type()));
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Widgets::Person *MapCityCastle::getTalkPerson() {
|
||||
U1MapTile tile;
|
||||
getTileAt(getPosition(), &tile);
|
||||
|
||||
switch (tile._tileId) {
|
||||
case 54:
|
||||
case 55:
|
||||
return dynamic_cast<Widgets::Person *>(_widgets.findByClass(Widgets::MerchantArmour::type()));
|
||||
|
||||
case 56:
|
||||
case 57:
|
||||
return dynamic_cast<Widgets::Person *>(_widgets.findByClass(Widgets::MerchantGrocer::type()));
|
||||
|
||||
case 58:
|
||||
case 59:
|
||||
return dynamic_cast<Widgets::Person *>(_widgets.findByClass(Widgets::MerchantWeapons::type()));
|
||||
|
||||
case 60:
|
||||
return dynamic_cast<Widgets::Person *>(_widgets.findByClass(Widgets::MerchantMagic::type()));
|
||||
|
||||
case 61:
|
||||
return dynamic_cast<Widgets::Person *>(_widgets.findByClass(Widgets::MerchantTavern::type()));
|
||||
|
||||
case 62:
|
||||
return dynamic_cast<Widgets::Person *>(_widgets.findByClass(
|
||||
dynamic_cast<MapCity *>(this) ? Widgets::MerchantTransport::type() : Widgets::King::type() ));
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MapCityCastle::cast() {
|
||||
addInfoMsg(Common::String::format(" -- %s", _game->_res->NO_EFFECT));
|
||||
_game->playFX(6);
|
||||
}
|
||||
|
||||
void MapCityCastle::drop() {
|
||||
U1Dialogs::Drop *drop = new U1Dialogs::Drop(_game);
|
||||
drop->show();
|
||||
}
|
||||
|
||||
void MapCityCastle::inform() {
|
||||
addInfoMsg("");
|
||||
addInfoMsg(_name);
|
||||
}
|
||||
|
||||
void MapCityCastle::steal() {
|
||||
Widgets::Merchant *merchant = getStealMerchant();
|
||||
|
||||
if (merchant) {
|
||||
// Found a merchant, so call their steal handler
|
||||
merchant->steal();
|
||||
} else {
|
||||
addInfoMsg(_game->_res->NOTHING_HERE);
|
||||
_game->playFX(1);
|
||||
}
|
||||
}
|
||||
|
||||
void MapCityCastle::attack(int direction, int effectId, uint maxDistance, uint amount, uint agility, const Common::String &hitWidget) {
|
||||
_game->playFX(effectId);
|
||||
Point delta = getDirectionDelta();
|
||||
U1MapTile tile;
|
||||
Widgets::Person *person;
|
||||
//int currTile;
|
||||
|
||||
// Scan in the given direction for a person to attack
|
||||
uint distance = 1;
|
||||
do {
|
||||
Point pt = getPosition() + Point(delta.x * distance, delta.y * distance);
|
||||
getTileAt(pt, &tile);
|
||||
//currTile = tile._tileId == CTILE_63 ? -1 : tile._tileId;
|
||||
person = dynamic_cast<Widgets::Person *>(tile._widget);
|
||||
|
||||
} while (++distance <= maxDistance && !person && (tile._tileId == CTILE_GROUND || tile._tileId >= CTILE_POND_EDGE1));
|
||||
|
||||
if (person && _game->getRandomNumber(1, 100) <= agility) {
|
||||
addInfoMsg(Common::String::format(_game->_res->HIT_CREATURE, person->_name.c_str()), false);
|
||||
|
||||
// Damage the person
|
||||
if (person->subtractHitPoints(amount)) {
|
||||
// Killed them
|
||||
addInfoMsg(_game->_res->KILLED);
|
||||
} else {
|
||||
// Still alive
|
||||
addInfoMsg(Common::String::format("%u %s!", amount, _game->_res->DAMAGE));
|
||||
}
|
||||
} else {
|
||||
addInfoMsg(_game->_res->MISSED);
|
||||
}
|
||||
|
||||
_game->endOfTurn();
|
||||
}
|
||||
|
||||
bool MapCityCastle::isWenchNearby() const {
|
||||
Shared::Maps::MapWidget *widget = _widgets.findByClass(Widgets::Wench::type());
|
||||
if (!widget)
|
||||
return false;
|
||||
|
||||
const Point &playerPos = _playerWidget->_position;
|
||||
const Point &wenchPos = widget->_position;
|
||||
int distance = MAX(ABS(playerPos.x - wenchPos.x), ABS(playerPos.y - wenchPos.y));
|
||||
return distance == 1;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
void MapCity::load(Shared::Maps::MapId mapId) {
|
||||
MapCityCastle::load(mapId);
|
||||
|
||||
_mapStyle = ((_mapId - 1) % 8) + 2;
|
||||
_mapIndex = _mapId;
|
||||
_name = Common::String::format("%s %s", _game->_res->THE_CITY_OF, _game->_res->LOCATION_NAMES[_mapId - 1]);
|
||||
|
||||
loadTownCastleData();
|
||||
|
||||
// Load up the widgets for the given map
|
||||
loadWidgets();
|
||||
setPosition(Common::Point(width() / 2, height() - 1)); // Start at bottom center edge of map
|
||||
}
|
||||
|
||||
void MapCity::dropCoins(uint coins) {
|
||||
Shared::Character &c = *_game->_party;
|
||||
U1MapTile tile;
|
||||
getTileAt(getPosition(), &tile);
|
||||
|
||||
if (tile._tileId == CTILE_POND_EDGE1 || tile._tileId == CTILE_POND_EDGE2 || tile._tileId == CTILE_POND_EDGE3) {
|
||||
addInfoMsg(_game->_res->SHAZAM);
|
||||
_game->playFX(5);
|
||||
|
||||
switch (tile._tileId) {
|
||||
case CTILE_POND_EDGE1: {
|
||||
// Increase one of the attributes randomly
|
||||
uint *attrList[6] = { &c._strength, &c._agility, &c._stamina, &c._charisma, &c._wisdom, &c._intelligence };
|
||||
uint &attr = *attrList[_game->getRandomNumber(0, 5)];
|
||||
|
||||
attr = MIN(attr + coins / 10, 99U);
|
||||
break;
|
||||
}
|
||||
|
||||
case CTILE_POND_EDGE2: {
|
||||
// Increase the quantity of a random weapon
|
||||
uint weaponNum = _game->getRandomNumber(1, 15);
|
||||
Shared::Weapon &weapon = *c._weapons[weaponNum];
|
||||
weapon._quantity = MIN(weapon._quantity + 1, 255U);
|
||||
break;
|
||||
}
|
||||
|
||||
case CTILE_POND_EDGE3:
|
||||
// Increase food
|
||||
c._food += coins;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
addInfoMsg(_game->_res->OK);
|
||||
}
|
||||
}
|
||||
|
||||
void MapCity::get() {
|
||||
addInfoMsg(_game->_res->WHAT);
|
||||
_game->playFX(1);
|
||||
}
|
||||
|
||||
void MapCity::talk() {
|
||||
if (_guardsHostile) {
|
||||
addInfoMsg(_game->_res->NONE_WILL_TALK);
|
||||
} else {
|
||||
Widgets::Person *person = getTalkPerson();
|
||||
|
||||
if (person) {
|
||||
person->talk();
|
||||
} else {
|
||||
addInfoMsg("");
|
||||
addInfoMsg(_game->_res->NOT_BY_COUNTER);
|
||||
_game->endOfTurn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MapCity::unlock() {
|
||||
addInfoMsg(_game->_res->WHAT);
|
||||
_game->playFX(1);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
void MapCastle::load(Shared::Maps::MapId mapId) {
|
||||
MapCityCastle::load(mapId);
|
||||
|
||||
_mapIndex = _mapId - 33;
|
||||
_mapStyle = _mapIndex % 2;
|
||||
_name = _game->_res->LOCATION_NAMES[_mapId - 1];
|
||||
_castleKey = _game->getRandomNumber(255) & 1 ? 61 : 60;
|
||||
_getCounter = 0;
|
||||
|
||||
loadTownCastleData();
|
||||
|
||||
// Set up door locks
|
||||
_data[_mapStyle ? 4 : 14][35] = CTILE_GATE;
|
||||
_data[_mapStyle ? 4 : 14][31] = CTILE_GATE;
|
||||
|
||||
// Load up the widgets for the given map
|
||||
loadWidgets();
|
||||
setPosition(Common::Point(0, height() / 2)); // Start at center left edge of map
|
||||
}
|
||||
|
||||
void MapCastle::synchronize(Common::Serializer &s) {
|
||||
MapCityCastle::synchronize(s);
|
||||
s.syncAsByte(_castleKey);
|
||||
s.syncAsByte(_getCounter);
|
||||
s.syncAsByte(_freeingPrincess);
|
||||
}
|
||||
|
||||
void MapCastle::dropCoins(uint coins) {
|
||||
Shared::Character &c = *_game->_party;
|
||||
U1MapTile tile;
|
||||
getTileAt(getPosition(), &tile);
|
||||
|
||||
if (tile._tileId == CTILE_POND_EDGE1) {
|
||||
uint hp = coins * 3 / 2;
|
||||
c._hitPoints = MIN(c._hitPoints + hp, 9999U);
|
||||
|
||||
if (_game->getRandomNumber(1, 255) > 16) {
|
||||
addInfoMsg(_game->_res->SHAZAM);
|
||||
} else {
|
||||
uint spellNum = _game->getRandomNumber(1, 7);
|
||||
if (spellNum == Spells::SPELL_MAGIC_MISSILE)
|
||||
spellNum = Spells::SPELL_STEAL;
|
||||
|
||||
c._spells[spellNum]->incrQuantity();
|
||||
addInfoMsg(_game->_res->ALAKAZOT);
|
||||
}
|
||||
} else {
|
||||
addInfoMsg(_game->_res->OK);
|
||||
}
|
||||
}
|
||||
|
||||
void MapCastle::get() {
|
||||
Widgets::Merchant *merchant = getStealMerchant();
|
||||
|
||||
if (merchant) {
|
||||
// Found a merchant, so call their get handler
|
||||
merchant->get();
|
||||
} else {
|
||||
addInfoMsg(_game->_res->NOTHING_HERE);
|
||||
_game->playFX(1);
|
||||
}
|
||||
}
|
||||
|
||||
void MapCastle::talk() {
|
||||
addInfoMsg(_game->_res->WITH_KING);
|
||||
Widgets::Person *person = getTalkPerson();
|
||||
|
||||
if (person) {
|
||||
person->talk();
|
||||
} else {
|
||||
addInfoMsg(_game->_res->HE_IS_NOT_HERE);
|
||||
_game->endOfTurn();
|
||||
}
|
||||
}
|
||||
|
||||
void MapCastle::unlock() {
|
||||
U1MapTile tile;
|
||||
Point pt = getPosition();
|
||||
getTileAt(pt, &tile);
|
||||
|
||||
if (tile._tileId != CTILE_LOCK1 && tile._tileId != CTILE_LOCK2) {
|
||||
addInfoMsg(_game->_res->WHAT);
|
||||
_game->playFX(1);
|
||||
} else if (!_castleKey) {
|
||||
addInfoMsg(_game->_res->NO_KEY);
|
||||
} else if (tile._tileId != (int)_castleKey) {
|
||||
addInfoMsg(_game->_res->INCORRECT_KEY);
|
||||
} else {
|
||||
addInfoMsg(_game->_res->DOOR_IS_OPEN);
|
||||
_data[pt.y][pt.x] = CTILE_GROUND;
|
||||
_freeingPrincess = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool MapCastle::isLordBritishCastle() const {
|
||||
return getMapIndex() == 0;
|
||||
}
|
||||
|
||||
} // End of namespace Maps
|
||||
} // End of namespace Ultima1
|
||||
} // End of namespace Ultima
|
||||
Reference in New Issue
Block a user