762 lines
23 KiB
C++
762 lines
23 KiB
C++
/* 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/ultima4/game/item.h"
|
|
#include "ultima/ultima4/game/codex.h"
|
|
#include "ultima/ultima4/game/game.h"
|
|
#include "ultima/ultima4/game/names.h"
|
|
#include "ultima/ultima4/game/player.h"
|
|
#include "ultima/ultima4/game/portal.h"
|
|
#include "ultima/ultima4/game/context.h"
|
|
#include "ultima/ultima4/game/weapon.h"
|
|
#include "ultima/ultima4/controllers/alpha_action_controller.h"
|
|
#include "ultima/ultima4/controllers/combat_controller.h"
|
|
#include "ultima/ultima4/core/utils.h"
|
|
#include "ultima/ultima4/filesys/savegame.h"
|
|
#include "ultima/ultima4/gfx/screen.h"
|
|
#include "ultima/ultima4/map/annotation.h"
|
|
#include "ultima/ultima4/map/dungeon.h"
|
|
#include "ultima/ultima4/map/location.h"
|
|
#include "ultima/ultima4/map/map.h"
|
|
#include "ultima/ultima4/map/mapmgr.h"
|
|
#include "ultima/ultima4/map/tileset.h"
|
|
#include "ultima/ultima4/ultima4.h"
|
|
|
|
namespace Ultima {
|
|
namespace Ultima4 {
|
|
|
|
Items *g_items;
|
|
|
|
const ItemLocation Items::ITEMS[N_ITEMS] = {
|
|
{
|
|
"Mandrake Root", nullptr, "mandrake1",
|
|
&Items::isReagentInInventory, &Items::putReagentInInventory, nullptr, REAG_MANDRAKE, SC_NEWMOONS | SC_REAGENTDELAY
|
|
},
|
|
{
|
|
"Mandrake Root", nullptr, "mandrake2",
|
|
&Items::isReagentInInventory, &Items::putReagentInInventory, nullptr, REAG_MANDRAKE, SC_NEWMOONS | SC_REAGENTDELAY
|
|
},
|
|
{
|
|
"Nightshade", nullptr, "nightshade1",
|
|
&Items::isReagentInInventory, &Items::putReagentInInventory, nullptr, REAG_NIGHTSHADE, SC_NEWMOONS | SC_REAGENTDELAY
|
|
},
|
|
{
|
|
"Nightshade", nullptr, "nightshade2",
|
|
&Items::isReagentInInventory, &Items::putReagentInInventory, nullptr, REAG_NIGHTSHADE, SC_NEWMOONS | SC_REAGENTDELAY
|
|
},
|
|
{
|
|
"the Bell of Courage", "bell", "bell",
|
|
&Items::isItemInInventory, &Items::putItemInInventory, &Items::useBBC, ITEM_BELL, 0
|
|
},
|
|
{
|
|
"the Book of Truth", "book", "book",
|
|
&Items::isItemInInventory, &Items::putItemInInventory, &Items::useBBC, ITEM_BOOK, 0
|
|
},
|
|
{
|
|
"the Candle of Love", "candle", "candle",
|
|
&Items::isItemInInventory, &Items::putItemInInventory, &Items::useBBC, ITEM_CANDLE, 0
|
|
},
|
|
{
|
|
"A Silver Horn", "horn", "horn",
|
|
&Items::isItemInInventory, &Items::putItemInInventory, &Items::useHorn, ITEM_HORN, 0
|
|
},
|
|
{
|
|
"the Wheel from the H.M.S. Cape", "wheel", "wheel",
|
|
&Items::isItemInInventory, &Items::putItemInInventory, &Items::useWheel, ITEM_WHEEL, 0
|
|
},
|
|
{
|
|
"the Skull of Modain the Wizard", "skull", "skull",
|
|
&Items::isSkullInInventory, &Items::putItemInInventory, &Items::useSkull, ITEM_SKULL, SC_NEWMOONS
|
|
},
|
|
{
|
|
"the Red Stone", "red", "redstone",
|
|
&Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_RED, 0
|
|
},
|
|
{
|
|
"the Orange Stone", "orange", "orangestone",
|
|
&Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_ORANGE, 0
|
|
},
|
|
{
|
|
"the Yellow Stone", "yellow", "yellowstone",
|
|
&Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_YELLOW, 0
|
|
},
|
|
{
|
|
"the Green Stone", "green", "greenstone",
|
|
&Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_GREEN, 0
|
|
},
|
|
{
|
|
"the Blue Stone", "blue", "bluestone",
|
|
&Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_BLUE, 0
|
|
},
|
|
{
|
|
"the Purple Stone", "purple", "purplestone",
|
|
&Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_PURPLE, 0
|
|
},
|
|
{
|
|
"the Black Stone", "black", "blackstone",
|
|
&Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_BLACK, SC_NEWMOONS
|
|
},
|
|
{
|
|
"the White Stone", "white", "whitestone",
|
|
&Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_WHITE, 0
|
|
},
|
|
|
|
/* handlers for using generic objects */
|
|
{ nullptr, "stone", nullptr, &Items::isStoneInInventory, nullptr, &Items::useStone, -1, 0 },
|
|
{ nullptr, "stones", nullptr, &Items::isStoneInInventory, nullptr, &Items::useStone, -1, 0 },
|
|
{ nullptr, "key", nullptr, &Items::isItemInInventory, nullptr, &Items::useKey, (ITEM_KEY_C | ITEM_KEY_L | ITEM_KEY_T), 0 },
|
|
{ nullptr, "keys", nullptr, &Items::isItemInInventory, nullptr, &Items::useKey, (ITEM_KEY_C | ITEM_KEY_L | ITEM_KEY_T), 0 },
|
|
|
|
/* Lycaeum telescope */
|
|
{ nullptr, nullptr, "telescope", nullptr, &Items::useTelescope, nullptr, 0, 0 },
|
|
|
|
{
|
|
"Mystic Armor", nullptr, "mysticarmor",
|
|
&Items::isMysticInInventory, &Items::putMysticInInventory, nullptr, ARMR_MYSTICROBES, SC_FULLAVATAR
|
|
},
|
|
{
|
|
"Mystic Swords", nullptr, "mysticswords",
|
|
&Items::isMysticInInventory, &Items::putMysticInInventory, nullptr, WEAP_MYSTICSWORD, SC_FULLAVATAR
|
|
},
|
|
{
|
|
"the sulfury remains of an ancient Sosarian Laser Gun. It turns to ash in your fingers", nullptr, "lasergun", // lol, where'd that come from?
|
|
//Looks like someone was experimenting with "maps.xml". It effectively increments sulfur ash by one due to '16' being an invalid weapon index.
|
|
&Items::isWeaponInInventory, &Items::putWeaponInInventory, nullptr, 16, 0
|
|
},
|
|
{
|
|
"the rune of Honesty", nullptr, "honestyrune",
|
|
&Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_HONESTY, 0
|
|
},
|
|
{
|
|
"the rune of Compassion", nullptr, "compassionrune",
|
|
&Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_COMPASSION, 0
|
|
},
|
|
{
|
|
"the rune of Valor", nullptr, "valorrune",
|
|
&Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_VALOR, 0
|
|
},
|
|
{
|
|
"the rune of Justice", nullptr, "justicerune",
|
|
&Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_JUSTICE, 0
|
|
},
|
|
{
|
|
"the rune of Sacrifice", nullptr, "sacrificerune",
|
|
&Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_SACRIFICE, 0
|
|
},
|
|
{
|
|
"the rune of Honor", nullptr, "honorrune",
|
|
&Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_HONOR, 0
|
|
},
|
|
{
|
|
"the rune of Spirituality", nullptr, "spiritualityrune",
|
|
&Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_SPIRITUALITY, 0
|
|
},
|
|
{
|
|
"the rune of Humility", nullptr, "humilityrune",
|
|
&Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_HUMILITY, 0
|
|
}
|
|
};
|
|
|
|
Items::Items() : destroyAllCreaturesCallback(nullptr),
|
|
needStoneNames(0), stoneMask(0) {
|
|
g_items = this;
|
|
}
|
|
|
|
Items::~Items() {
|
|
g_items = nullptr;
|
|
}
|
|
|
|
void Items::setDestroyAllCreaturesCallback(DestroyAllCreaturesCallback callback) {
|
|
destroyAllCreaturesCallback = callback;
|
|
}
|
|
|
|
bool Items::isRuneInInventory(int virt) {
|
|
return g_ultima->_saveGame->_runes & virt;
|
|
}
|
|
|
|
void Items::putRuneInInventory(int virt) {
|
|
g_context->_party->member(0)->awardXp(100);
|
|
g_context->_party->adjustKarma(KA_FOUND_ITEM);
|
|
g_ultima->_saveGame->_runes |= virt;
|
|
#ifdef IOS_ULTIMA4
|
|
Common::String virtueName;
|
|
switch (virt) {
|
|
default:
|
|
case RUNE_HONESTY:
|
|
virtueName = "Honesty";
|
|
break;
|
|
case RUNE_HONOR:
|
|
virtueName = "Honor";
|
|
break;
|
|
case RUNE_HUMILITY:
|
|
virtueName = "Humility";
|
|
break;
|
|
case RUNE_JUSTICE:
|
|
virtueName = "Justice";
|
|
break;
|
|
case RUNE_SACRIFICE:
|
|
virtueName = "Sacrifice";
|
|
break;
|
|
case RUNE_SPIRITUALITY:
|
|
virtueName = "Spirituality";
|
|
break;
|
|
case RUNE_VALOR:
|
|
virtueName = "Valor";
|
|
break;
|
|
case RUNE_COMPASSION:
|
|
virtueName = "Compassion";
|
|
break;
|
|
}
|
|
U4IOS::testFlightPassCheckPoint("Player got stone: " + virtueName);
|
|
#endif
|
|
g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0;
|
|
}
|
|
|
|
bool Items::isStoneInInventory(int virt) {
|
|
/* generic test: does the party have any stones yet? */
|
|
if (virt == -1)
|
|
return (g_ultima->_saveGame->_stones > 0);
|
|
/* specific test: does the party have a specific stone? */
|
|
else
|
|
return g_ultima->_saveGame->_stones & virt;
|
|
}
|
|
|
|
void Items::putStoneInInventory(int virt) {
|
|
g_context->_party->member(0)->awardXp(200);
|
|
g_context->_party->adjustKarma(KA_FOUND_ITEM);
|
|
g_ultima->_saveGame->_stones |= virt;
|
|
#ifdef IOS_ULTIMA4
|
|
Common::String stoneName;
|
|
switch (virt) {
|
|
default:
|
|
case STONE_BLACK:
|
|
stoneName = "Black";
|
|
break;
|
|
case STONE_BLUE:
|
|
stoneName = "Blue";
|
|
break;
|
|
case STONE_GREEN:
|
|
stoneName = "Green";
|
|
break;
|
|
case STONE_ORANGE:
|
|
stoneName = "Orange";
|
|
break;
|
|
case STONE_PURPLE:
|
|
stoneName = "Purple";
|
|
break;
|
|
case STONE_RED:
|
|
stoneName = "Red";
|
|
break;
|
|
case STONE_WHITE:
|
|
stoneName = "White";
|
|
break;
|
|
case STONE_YELLOW:
|
|
stoneName = "Yellow";
|
|
break;
|
|
}
|
|
U4IOS::testFlightPassCheckPoint("Player got rune: " + stoneName);
|
|
#endif
|
|
g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0;
|
|
}
|
|
|
|
bool Items::isItemInInventory(int item) {
|
|
return g_ultima->_saveGame->_items & item;
|
|
}
|
|
|
|
bool Items::isSkullInInventory(int unused) {
|
|
return (g_ultima->_saveGame->_items & (ITEM_SKULL | ITEM_SKULL_DESTROYED));
|
|
}
|
|
|
|
void Items::putItemInInventory(int item) {
|
|
g_context->_party->member(0)->awardXp(400);
|
|
g_context->_party->adjustKarma(KA_FOUND_ITEM);
|
|
g_ultima->_saveGame->_items |= item;
|
|
#ifdef IOS_ULTIMA4
|
|
Common::String itemName;
|
|
switch (item) {
|
|
default:
|
|
case ITEM_BELL:
|
|
itemName = "Bell";
|
|
break;
|
|
case ITEM_BOOK:
|
|
itemName = "Book";
|
|
break;
|
|
case ITEM_CANDLE:
|
|
itemName = "Candle";
|
|
break;
|
|
case ITEM_HORN:
|
|
itemName = "Horn";
|
|
break;
|
|
case ITEM_KEY_C:
|
|
itemName = "Key Courage";
|
|
break;
|
|
case ITEM_KEY_L:
|
|
itemName = "Key Love";
|
|
break;
|
|
case ITEM_KEY_T:
|
|
itemName = "Key Truth";
|
|
break;
|
|
case ITEM_SKULL:
|
|
itemName = "Skull";
|
|
break;
|
|
case ITEM_WHEEL:
|
|
itemName = "Wheel";
|
|
break;
|
|
|
|
}
|
|
U4IOS::testFlightPassCheckPoint("Player got rune: " + itemName);
|
|
#endif
|
|
g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0;
|
|
}
|
|
|
|
void Items::useBBC(int item) {
|
|
Coords abyssEntrance(0xe9, 0xe9);
|
|
/* on top of the Abyss entrance */
|
|
if (g_context->_location->_coords == abyssEntrance) {
|
|
/* must use bell first */
|
|
if (item == ITEM_BELL) {
|
|
#ifdef IOS_ULTIMA4
|
|
U4IOS::testFlightPassCheckPoint("The Bell rings on and on!");
|
|
#endif
|
|
g_screen->screenMessage("\nThe Bell rings on and on!\n");
|
|
g_ultima->_saveGame->_items |= ITEM_BELL_USED;
|
|
}
|
|
/* then the book */
|
|
else if ((item == ITEM_BOOK) && (g_ultima->_saveGame->_items & ITEM_BELL_USED)) {
|
|
#ifdef IOS_ULTIMA4
|
|
U4IOS::testFlightPassCheckPoint("The words resonate with the ringing!");
|
|
#endif
|
|
g_screen->screenMessage("\nThe words resonate with the ringing!\n");
|
|
g_ultima->_saveGame->_items |= ITEM_BOOK_USED;
|
|
}
|
|
/* then the candle */
|
|
else if ((item == ITEM_CANDLE) && (g_ultima->_saveGame->_items & ITEM_BOOK_USED)) {
|
|
g_screen->screenMessage("\nAs you light the Candle the Earth Trembles!\n");
|
|
#ifdef IOS_ULTIMA4
|
|
U4IOS::testFlightPassCheckPoint("As you light the Candle the Earth Trembles!");
|
|
#endif
|
|
g_ultima->_saveGame->_items |= ITEM_CANDLE_USED;
|
|
} else {
|
|
g_screen->screenMessage("\nHmm...No effect!\n");
|
|
}
|
|
}
|
|
/* somewhere else */
|
|
else {
|
|
g_screen->screenMessage("\nHmm...No effect!\n");
|
|
}
|
|
}
|
|
|
|
void Items::useHorn(int item) {
|
|
g_screen->screenMessage("\nThe Horn sounds an eerie tone!\n");
|
|
g_context->_aura->set(Aura::HORN, 10);
|
|
}
|
|
|
|
void Items::useWheel(int item) {
|
|
if ((g_context->_transportContext == TRANSPORT_SHIP) && (g_ultima->_saveGame->_shipHull == 50)) {
|
|
g_screen->screenMessage("\nOnce mounted, the Wheel glows with a blue light!\n");
|
|
g_context->_party->setShipHull(99);
|
|
} else {
|
|
g_screen->screenMessage("\nHmm...No effect!\n");
|
|
}
|
|
}
|
|
|
|
void Items::useSkull(int item) {
|
|
/* FIXME: check to see if the abyss must be opened first
|
|
for the skull to be *able* to be destroyed */
|
|
|
|
/* We do the check here instead of in the table, because we need to distinguish between a
|
|
never-found skull and a destroyed skull. */
|
|
if (g_ultima->_saveGame->_items & ITEM_SKULL_DESTROYED) {
|
|
g_screen->screenMessage("\nNone owned!\n");
|
|
return;
|
|
}
|
|
|
|
/* destroy the skull! pat yourself on the back */
|
|
if (g_context->_location->_coords.x == 0xe9 && g_context->_location->_coords.y == 0xe9) {
|
|
g_screen->screenMessage("\n\nYou cast the Skull of Mondain into the Abyss!\n");
|
|
#ifdef IOS_ULTIMA4
|
|
U4IOS::testFlightPassCheckPoint("You cast the Skull of Mondain into the Abyss!");
|
|
#endif
|
|
|
|
g_ultima->_saveGame->_items = (g_ultima->_saveGame->_items & ~ITEM_SKULL) | ITEM_SKULL_DESTROYED;
|
|
g_context->_party->adjustKarma(KA_DESTROYED_SKULL);
|
|
}
|
|
|
|
/* use the skull... bad, very bad */
|
|
else {
|
|
g_screen->screenMessage("\n\nYou hold the evil Skull of Mondain the Wizard aloft...\n");
|
|
#ifdef IOS_ULTIMA4
|
|
U4IOS::testFlightPassCheckPoint("You hold the evil Skull of Mondain the Wizard aloft...");
|
|
#endif
|
|
|
|
/* destroy all creatures */
|
|
(*destroyAllCreaturesCallback)();
|
|
|
|
/* we don't lose the skull until we toss it into the abyss */
|
|
//c->saveGame->_items = (c->saveGame->_items & ~ITEM_SKULL);
|
|
g_context->_party->adjustKarma(KA_USED_SKULL);
|
|
}
|
|
}
|
|
|
|
void Items::useStone(int item) {
|
|
MapCoords coords;
|
|
byte stone = static_cast<byte>(item);
|
|
|
|
static const byte truth = STONE_WHITE | STONE_PURPLE | STONE_GREEN | STONE_BLUE;
|
|
static const byte love = STONE_WHITE | STONE_YELLOW | STONE_GREEN | STONE_ORANGE;
|
|
static const byte courage = STONE_WHITE | STONE_RED | STONE_PURPLE | STONE_ORANGE;
|
|
static const byte *attr = nullptr;
|
|
|
|
g_context->_location->getCurrentPosition(&coords);
|
|
|
|
/**
|
|
* Named a specific stone (after using "stone" or "stones")
|
|
*/
|
|
if (item != -1) {
|
|
CombatMap *cm = getCombatMap();
|
|
|
|
if (needStoneNames) {
|
|
/* named a stone while in a dungeon altar room */
|
|
if (g_context->_location->_context & CTX_ALTAR_ROOM) {
|
|
needStoneNames--;
|
|
|
|
switch (cm->getAltarRoom()) {
|
|
case VIRT_TRUTH:
|
|
attr = &truth;
|
|
break;
|
|
case VIRT_LOVE:
|
|
attr = &love;
|
|
break;
|
|
case VIRT_COURAGE:
|
|
attr = &courage;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* make sure we're in an altar room */
|
|
if (attr) {
|
|
/* we need to use the stone, and we haven't used it yet */
|
|
if ((*attr & stone) && (stone & ~stoneMask))
|
|
stoneMask |= stone;
|
|
/* we already used that stone! */
|
|
else if (stone & stoneMask) {
|
|
g_screen->screenMessage("\nAlready used!\n");
|
|
needStoneNames = 0;
|
|
stoneMask = 0; /* reset the mask so you can try again */
|
|
return;
|
|
}
|
|
} else {
|
|
error("Not in an altar room!");
|
|
}
|
|
/* see if we have all the stones, if not, get more names! */
|
|
if (attr && needStoneNames) {
|
|
g_screen->screenMessage("\n%c:", 'E' - needStoneNames);
|
|
#ifdef IOS_ULTIMA4
|
|
U4IOS::IOSConversationHelper::setIntroString("Which Color?");
|
|
#endif
|
|
itemHandleStones(gameGetInput());
|
|
}
|
|
/* all the stones have been entered, verify them! */
|
|
else {
|
|
unsigned short key = 0xFFFF;
|
|
switch (cm->getAltarRoom()) {
|
|
case VIRT_TRUTH:
|
|
key = ITEM_KEY_T;
|
|
break;
|
|
case VIRT_LOVE:
|
|
key = ITEM_KEY_L;
|
|
break;
|
|
case VIRT_COURAGE:
|
|
key = ITEM_KEY_C;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* in an altar room, named all of the stones, and don't have the key yet... */
|
|
if (attr && (stoneMask == *attr) && !(g_ultima->_saveGame->_items & key)) {
|
|
#ifdef IOS_ULTIMA4
|
|
Common::String keyName;
|
|
switch (key) {
|
|
case ITEM_KEY_C:
|
|
keyName = "Key Courage";
|
|
break;
|
|
case ITEM_KEY_L:
|
|
keyName = "Key Love";
|
|
break;
|
|
case ITEM_KEY_T:
|
|
keyName = "Key Truth";
|
|
break;
|
|
}
|
|
U4IOS::testFlightPassCheckPoint("Receive a key: " + keyName);
|
|
#endif
|
|
g_screen->screenMessage("\nThou doth find one third of the Three Part Key!\n");
|
|
g_ultima->_saveGame->_items |= key;
|
|
} else {
|
|
g_screen->screenMessage("\nHmm...No effect!\n");
|
|
}
|
|
|
|
stoneMask = 0; /* reset the mask so you can try again */
|
|
}
|
|
} else {
|
|
/* Otherwise, we're asking for a stone while in the abyss on top of an altar */
|
|
/* see if they entered the correct stone */
|
|
if (stone == (1 << g_context->_location->_coords.z)) {
|
|
if (g_context->_location->_coords.z < 7) {
|
|
/* replace the altar with a down-ladder */
|
|
MapCoords pos;
|
|
g_screen->screenMessage("\n\nThe altar changes before thyne eyes!\n");
|
|
g_context->_location->getCurrentPosition(&pos);
|
|
g_context->_location->_map->_annotations->add(pos, g_context->_location->_map->_tileSet->getByName("down_ladder")->getId());
|
|
} else {
|
|
// Start chamber of the codex sequence...
|
|
g_codex->start();
|
|
}
|
|
} else {
|
|
g_screen->screenMessage("\nHmm...No effect!\n");
|
|
}
|
|
}
|
|
} else {
|
|
g_screen->screenMessage("\nNot a Usable Item!\n");
|
|
stoneMask = 0; /* reset the mask so you can try again */
|
|
}
|
|
}
|
|
|
|
/**
|
|
* in the abyss, on an altar to place the stones
|
|
*/
|
|
else if ((g_context->_location->_map->_id == MAP_ABYSS) &&
|
|
(g_context->_location->_context & CTX_DUNGEON) &&
|
|
(static_cast<Dungeon *>(g_context->_location->_map)->currentToken() == DUNGEON_ALTAR)) {
|
|
|
|
int virtueMask = getBaseVirtues((Virtue)g_context->_location->_coords.z);
|
|
if (virtueMask > 0)
|
|
g_screen->screenMessage("\n\nAs thou doth approach, a voice rings out: What virtue dost stem from %s?\n\n", getBaseVirtueName(virtueMask));
|
|
else
|
|
g_screen->screenMessage("\n\nA voice rings out: What virtue exists independently of Truth, Love, and Courage?\n\n");
|
|
#ifdef IOS_ULTIMA4
|
|
U4IOS::IOSConversationHelper::setIntroString("Which virtue?");
|
|
#endif
|
|
Common::String virtue = gameGetInput();
|
|
|
|
if (scumm_strnicmp(virtue.c_str(), getVirtueName((Virtue)g_context->_location->_coords.z), 6) == 0) {
|
|
/* now ask for stone */
|
|
g_screen->screenMessage("\n\nThe Voice says: Use thy Stone.\n\nColor:\n");
|
|
needStoneNames = 1;
|
|
#ifdef IOS_ULTIMA4
|
|
U4IOS::IOSConversationHelper::setIntroString("Which color?");
|
|
#endif
|
|
itemHandleStones(gameGetInput());
|
|
} else {
|
|
g_screen->screenMessage("\nHmm...No effect!\n");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* in a dungeon altar room, on the altar
|
|
*/
|
|
else if ((g_context->_location->_context & CTX_ALTAR_ROOM) &&
|
|
coords.x == 5 && coords.y == 5) {
|
|
needStoneNames = 4;
|
|
g_screen->screenMessage("\n\nThere are holes for 4 stones.\nWhat colors:\nA:");
|
|
#ifdef IOS_ULTIMA4
|
|
U4IOS::IOSConversationHelper::setIntroString("Which color?");
|
|
#endif
|
|
itemHandleStones(gameGetInput());
|
|
} else {
|
|
g_screen->screenMessage("\nNo place to Use them!\n");
|
|
// This used to say "\nNo place to Use them!\nHmm...No effect!\n"
|
|
// That doesn't match U4DOS; does it match another?
|
|
}
|
|
}
|
|
|
|
void Items::useKey(int item) {
|
|
g_screen->screenMessage("\nNo place to Use them!\n");
|
|
}
|
|
|
|
bool Items::isMysticInInventory(int mystic) {
|
|
/* FIXME: you could feasibly get more mystic weapons and armor if you
|
|
have 8 party members and equip them all with everything,
|
|
then search for Mystic Weapons/Armor again
|
|
|
|
or, you could just sell them all and search again. What an easy
|
|
way to make some cash!
|
|
|
|
This would be a good candidate for an xu4 "extended" savegame
|
|
format.
|
|
*/
|
|
if (mystic == WEAP_MYSTICSWORD)
|
|
return g_ultima->_saveGame->_weapons[WEAP_MYSTICSWORD] > 0;
|
|
else if (mystic == ARMR_MYSTICROBES)
|
|
return g_ultima->_saveGame->_armor[ARMR_MYSTICROBES] > 0;
|
|
else
|
|
error("Invalid mystic item was tested in isMysticInInventory()");
|
|
return false;
|
|
}
|
|
|
|
void Items::putMysticInInventory(int mystic) {
|
|
g_context->_party->member(0)->awardXp(400);
|
|
g_context->_party->adjustKarma(KA_FOUND_ITEM);
|
|
if (mystic == WEAP_MYSTICSWORD)
|
|
g_ultima->_saveGame->_weapons[WEAP_MYSTICSWORD] += 8;
|
|
else if (mystic == ARMR_MYSTICROBES)
|
|
g_ultima->_saveGame->_armor[ARMR_MYSTICROBES] += 8;
|
|
else
|
|
error("Invalid mystic item was added in putMysticInInventory()");
|
|
g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0;
|
|
}
|
|
|
|
bool Items::isWeaponInInventory(int weapon) {
|
|
if (g_ultima->_saveGame->_weapons[weapon])
|
|
return true;
|
|
else {
|
|
for (int i = 0; i < g_context->_party->size(); i++) {
|
|
if (g_context->_party->member(i)->getWeapon()->getType() == weapon)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Items::putWeaponInInventory(int weapon) {
|
|
g_ultima->_saveGame->_weapons[weapon]++;
|
|
}
|
|
|
|
void Items::useTelescope(int notused) {
|
|
g_screen->screenMessage("You see a knob\non the telescope\nmarked A-P\nYou Select:");
|
|
#ifdef IOS_ULTIMA4
|
|
U4IOS::IOSConversationChoiceHelper telescopeHelper;
|
|
telescopeHelper.updateChoices("abcdefghijklmnop ");
|
|
#endif
|
|
int choice = AlphaActionController::get('p', "You Select:");
|
|
|
|
if (choice == -1)
|
|
return;
|
|
|
|
gamePeerCity(choice, nullptr);
|
|
}
|
|
|
|
bool Items::isReagentInInventory(int reag) {
|
|
return false;
|
|
}
|
|
|
|
void Items::putReagentInInventory(int reag) {
|
|
g_context->_party->adjustKarma(KA_FOUND_ITEM);
|
|
g_ultima->_saveGame->_reagents[reag] += xu4_random(8) + 2;
|
|
g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0;
|
|
|
|
if (g_ultima->_saveGame->_reagents[reag] > 99) {
|
|
g_ultima->_saveGame->_reagents[reag] = 99;
|
|
g_screen->screenMessage("Dropped some!\n");
|
|
}
|
|
}
|
|
|
|
bool Items::itemConditionsMet(byte conditions) {
|
|
if ((conditions & SC_NEWMOONS) &&
|
|
!(g_ultima->_saveGame->_trammelPhase == 0 && g_ultima->_saveGame->_feluccaPhase == 0))
|
|
return false;
|
|
|
|
if (conditions & SC_FULLAVATAR) {
|
|
for (int i = 0; i < VIRT_MAX; i++) {
|
|
if (g_ultima->_saveGame->_karma[i] != 0)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ((conditions & SC_REAGENTDELAY) &&
|
|
(g_ultima->_saveGame->_moves & 0xF0) == g_ultima->_saveGame->_lastReagent)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
const ItemLocation *Items::itemAtLocation(const Map *map, const Coords &coords) {
|
|
for (uint i = 0; i < N_ITEMS; i++) {
|
|
if (!ITEMS[i]._locationLabel)
|
|
continue;
|
|
if (map->getLabel(ITEMS[i]._locationLabel) == coords &&
|
|
itemConditionsMet(ITEMS[i]._conditions))
|
|
return &(ITEMS[i]);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void Items::itemUse(const Common::String &shortName) {
|
|
const ItemLocation *item = nullptr;
|
|
|
|
for (uint i = 0; i < N_ITEMS; i++) {
|
|
if (ITEMS[i]._shortName &&
|
|
scumm_stricmp(ITEMS[i]._shortName, shortName.c_str()) == 0) {
|
|
|
|
item = &ITEMS[i];
|
|
|
|
/* item name found, see if we have that item in our inventory */
|
|
if (!ITEMS[i]._isItemInInventory || (this->*(ITEMS[i]._isItemInInventory))(ITEMS[i]._data)) {
|
|
|
|
/* use the item, if we can! */
|
|
if (!item || !item->_useItem)
|
|
g_screen->screenMessage("\nNot a Usable item!\n");
|
|
else
|
|
(this->*(item->_useItem))(ITEMS[i]._data);
|
|
} else
|
|
g_screen->screenMessage("\nNone owned!\n");
|
|
|
|
/* we found the item, no need to keep searching */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* item was not found */
|
|
if (!item)
|
|
g_screen->screenMessage("\nNot a Usable item!\n");
|
|
}
|
|
|
|
void Items::itemHandleStones(const Common::String &color) {
|
|
bool found = false;
|
|
|
|
for (int i = 0; i < 8; i++) {
|
|
if (scumm_stricmp(color.c_str(), getStoneName((Virtue)i)) == 0 &&
|
|
isStoneInInventory(1 << i)) {
|
|
found = true;
|
|
itemUse(color.c_str());
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
g_screen->screenMessage("\nNone owned!\n");
|
|
stoneMask = 0; /* make sure stone mask is reset */
|
|
}
|
|
}
|
|
|
|
bool Items::isAbyssOpened(const Portal *p) {
|
|
/* make sure the bell, book and candle have all been used */
|
|
int items = g_ultima->_saveGame->_items;
|
|
int isopened = (items & ITEM_BELL_USED) && (items & ITEM_BOOK_USED) && (items & ITEM_CANDLE_USED);
|
|
|
|
if (!isopened)
|
|
g_screen->screenMessage("Enter Can't!\n");
|
|
return isopened;
|
|
}
|
|
|
|
} // End of namespace Ultima4
|
|
} // End of namespace Ultima
|