ULTIMA4: Cast spell to keybinder

This commit is contained in:
Paul Gilbert 2020-04-13 19:38:03 -07:00
parent 7bb3369c2c
commit f2bd9f0f92
9 changed files with 215 additions and 187 deletions

View File

@ -50,6 +50,7 @@ Debugger::Debugger() : Shared::Debugger() {
registerCmd("attack", WRAP_METHOD(Debugger, cmdAttack));
registerCmd("board", WRAP_METHOD(Debugger, cmdBoard));
registerCmd("cast", WRAP_METHOD(Debugger, cmdCastSpell));
registerCmd("climb", WRAP_METHOD(Debugger, cmdClimb));
registerCmd("enter", WRAP_METHOD(Debugger, cmdEnter));
registerCmd("fire", WRAP_METHOD(Debugger, cmdFire));
registerCmd("get", WRAP_METHOD(Debugger, cmdGet));
@ -62,6 +63,7 @@ Debugger::Debugger() : Shared::Debugger() {
registerCmd("order", WRAP_METHOD(Debugger, cmdNewOrder));
registerCmd("pass", WRAP_METHOD(Debugger, cmdPass));
registerCmd("peer", WRAP_METHOD(Debugger, cmdPeer));
registerCmd("quitAndSave", WRAP_METHOD(Debugger, cmdQuitAndSave));
registerCmd("3d", WRAP_METHOD(Debugger, cmd3d));
registerCmd("collisions", WRAP_METHOD(Debugger, cmdCollisions));
@ -145,6 +147,14 @@ void Debugger::getChest(int player) {
cmdGet(2, argv);
}
void Debugger::castSpell(int player) {
Common::String param = Common::String::format("%d", player);
const char *argv[2] = { "cast", param.c_str() };
cmdCastSpell(2, argv);
}
bool Debugger::cmdMove(int argc, const char **argv) {
@ -247,7 +257,161 @@ bool Debugger::cmdBoard(int argc, const char **argv) {
}
bool Debugger::cmdCastSpell(int argc, const char **argv) {
// TODO
int player = -1;
if (argc == 2)
player = strToInt(argv[1]);
if (player == -1) {
printN("Cast Spell!\nPlayer: ");
player = gameGetPlayer(false, true);
}
if (player == -1)
return isDebuggerActive();
// get the spell to cast
g_context->_stats->setView(STATS_MIXTURES);
printN("Spell: ");
// ### Put the iPad thing too.
#ifdef IOS
U4IOS::IOSCastSpellHelper castSpellController;
#endif
int spell = AlphaActionController::get('z', "Spell: ");
if (spell == -1)
return isDebuggerActive();
screenMessage("%s!\n", spellGetName(spell)); //Prints spell name at prompt
g_context->_stats->setView(STATS_PARTY_OVERVIEW);
// if we can't really cast this spell, skip the extra parameters
if (spellCheckPrerequisites(spell, player) != CASTERR_NOERROR) {
gameCastSpell(spell, player, 0);
return isDebuggerActive();
}
// Get the final parameters for the spell
switch (spellGetParamType(spell)) {
case Spell::PARAM_NONE:
gameCastSpell(spell, player, 0);
break;
case Spell::PARAM_PHASE: {
screenMessage("To Phase: ");
#ifdef IOS
U4IOS::IOSConversationChoiceHelper choiceController;
choiceController.fullSizeChoicePanel();
choiceController.updateGateSpellChoices();
#endif
int choice = ReadChoiceController::get("12345678 \033\n");
if (choice < '1' || choice > '8')
print("None");
else {
print("");
gameCastSpell(spell, player, choice - '1');
}
break;
}
case Spell::PARAM_PLAYER: {
printN("Who: ");
int subject = gameGetPlayer(true, false);
if (subject != -1)
gameCastSpell(spell, player, subject);
break;
}
case Spell::PARAM_DIR:
if (g_context->_location->_context == CTX_DUNGEON)
gameCastSpell(spell, player, g_ultima->_saveGame->_orientation);
else {
printN("Dir: ");
Direction dir = gameGetDirection();
if (dir != DIR_NONE)
gameCastSpell(spell, player, (int)dir);
}
break;
case Spell::PARAM_TYPEDIR: {
printN("Energy type? ");
#ifdef IOS
U4IOS::IOSConversationChoiceHelper choiceController;
choiceController.fullSizeChoicePanel();
choiceController.updateEnergyFieldSpellChoices();
#endif
EnergyFieldType fieldType = ENERGYFIELD_NONE;
char key = ReadChoiceController::get("flps \033\n\r");
switch (key) {
case 'f':
fieldType = ENERGYFIELD_FIRE;
break;
case 'l':
fieldType = ENERGYFIELD_LIGHTNING;
break;
case 'p':
fieldType = ENERGYFIELD_POISON;
break;
case 's':
fieldType = ENERGYFIELD_SLEEP;
break;
default:
break;
}
if (fieldType != ENERGYFIELD_NONE) {
print("");
Direction dir;
if (g_context->_location->_context == CTX_DUNGEON)
dir = (Direction)g_ultima->_saveGame->_orientation;
else {
printN("Dir: ");
dir = gameGetDirection();
}
if (dir != DIR_NONE) {
/* Need to pack both dir and fieldType into param */
int param = fieldType << 4;
param |= (int)dir;
gameCastSpell(spell, player, param);
}
} else {
/* Invalid input here = spell failure */
print("Failed!");
/*
* Confirmed both mixture loss and mp loss in this situation in the
* original Ultima IV (at least, in the Amiga version.)
*/
//c->saveGame->_mixtures[castSpell]--;
g_context->_party->member(player)->adjustMp(-spellGetRequiredMP(spell));
}
break;
}
case Spell::PARAM_FROMDIR: {
printN("From Dir: ");
Direction dir = gameGetDirection();
if (dir != DIR_NONE)
gameCastSpell(spell, player, (int)dir);
break;
}
}
return isDebuggerActive();
}
bool Debugger::cmdClimb(int argc, const char **argv) {
if (!usePortalAt(g_context->_location, g_context->_location->_coords, ACTION_KLIMB)) {
if (g_context->_transportContext == TRANSPORT_BALLOON) {
g_ultima->_saveGame->_balloonState = 1;
g_context->_opacity = 0;
print("Klimb altitude");
} else
print("%cKlimb what?%c", FG_GREY, FG_WHITE);
}
return isDebuggerActive();
}
@ -532,7 +696,7 @@ bool Debugger::cmdOpenDoor(int argc, const char **argv) {
}
print("%cNot Here!%c", FG_GREY, FG_WHITE);
return isDebuggerActive();
}
bool Debugger::cmdPass(int argc, const char **argv) {
@ -552,6 +716,18 @@ bool Debugger::cmdPeer(int argc, const char **argv) {
return isDebuggerActive();
}
bool Debugger::cmdQuitAndSave(int argc, const char **argv) {
print("Quit & Save...\n%d moves", g_ultima->_saveGame->_moves);
if (g_context->_location->_context & CTX_CAN_SAVE_GAME) {
if (g_ultima->saveGameDialog())
print("Press Alt-x to quit");
} else {
print("%cNot here!%c", FG_GREY, FG_WHITE);
}
return isDebuggerActive();
}
bool Debugger::cmd3d(int argc, const char **argv) {
if (g_context->_location->_context == CTX_DUNGEON) {

View File

@ -98,6 +98,11 @@ private:
*/
bool cmdCastSpell(int argc, const char **argv);
/**
* Climb
*/
bool cmdClimb(int argc, const char **argv);
/**
* Enter location
*/
@ -159,6 +164,11 @@ private:
*/
bool cmdPeer(int argc, const char **argv);
/**
* Save and quit
*/
bool cmdQuitAndSave(int argc, const char **argv);
private:
/**
* Collision detection on/off
@ -297,6 +307,11 @@ public:
* user. Otherwise, a non-negative player number is expected
*/
void getChest(int player = -2);
/**
* Cast a spell
*/
void castSpell(int player);
};
extern Debugger *g_debugger;

View File

@ -331,5 +331,16 @@ bool DebuggerActions::openAt(const Coords &coords) {
return true;
}
void DebuggerActions::gameCastSpell(unsigned int spell, int caster, int param) {
SpellCastError spellError;
Common::String msg;
if (!spellCast(spell, caster, param, &spellError, true)) {
msg = spellGetErrorMessage(spell, spellError);
if (!msg.empty())
screenMessage("%s", msg.c_str());
}
}
} // End of namespace Ultima4
} // End of namespace Ultima

View File

@ -108,6 +108,8 @@ public:
* replaced by a temporary annotation of a floor tile for 4 turns.
*/
bool openAt(const Coords &coords);
void gameCastSpell(unsigned int spell, int caster, int param);
};
} // End of namespace Ultima4

View File

@ -84,9 +84,6 @@ void gameLostEighth(Virtue virtue);
void gamePartyStarving(void);
uint32 gameTimeSinceLastCommand(void);
/* spell functions */
void gameCastSpell(unsigned int spell, int caster, int param);
void mixReagentsSuper();
/* conversation functions */
@ -530,17 +527,6 @@ void gameSpellEffect(int spell, int player, Sound sound) {
}
}
void gameCastSpell(unsigned int spell, int caster, int param) {
SpellCastError spellError;
Common::String msg;
if (!spellCast(spell, caster, param, &spellError, true)) {
msg = spellGetErrorMessage(spell, spellError);
if (!msg.empty())
screenMessage("%s", msg.c_str());
}
}
void GameController::keybinder(KeybindingAction action) {
MetaEngine::executeAction(action);
}
@ -681,10 +667,6 @@ bool GameController::keyPressed(int key) {
endTurn = false;
break;
case 'c':
castSpell();
break;
case 'd': {
// unload the map for the second level of Lord British's Castle. The reason
// why is that Lord British's farewell is dependent on the number of party members.
@ -708,32 +690,6 @@ bool GameController::keyPressed(int key) {
break;
}
case 'k':
if (!usePortalAt(g_context->_location, g_context->_location->_coords, ACTION_KLIMB)) {
if (g_context->_transportContext == TRANSPORT_BALLOON) {
g_ultima->_saveGame->_balloonState = 1;
g_context->_opacity = 0;
screenMessage("Klimb altitude\n");
} else
screenMessage("%cKlimb what?%c\n", FG_GREY, FG_WHITE);
}
break;
case 'p':
peer();
break;
case 'q':
screenMessage("Quit & Save...\n%d moves\n", g_ultima->_saveGame->_moves);
if (g_context->_location->_context & CTX_CAN_SAVE_GAME) {
if (g_ultima->saveGameDialog())
screenMessage("Press Alt-x to quit\n");
} else {
screenMessage("%cNot here!%c\n", FG_GREY, FG_WHITE);
}
break;
case 'r':
readyWeapon();
break;
@ -1101,140 +1057,6 @@ bool ZtatsController::keyPressed(int key) {
}
}
void castSpell(int player) {
if (player == -1) {
screenMessage("Cast Spell!\nPlayer: ");
player = gameGetPlayer(false, true);
}
if (player == -1)
return;
// get the spell to cast
g_context->_stats->setView(STATS_MIXTURES);
screenMessage("Spell: ");
// ### Put the iPad thing too.
#ifdef IOS
U4IOS::IOSCastSpellHelper castSpellController;
#endif
int spell = AlphaActionController::get('z', "Spell: ");
if (spell == -1)
return;
screenMessage("%s!\n", spellGetName(spell)); //Prints spell name at prompt
g_context->_stats->setView(STATS_PARTY_OVERVIEW);
// if we can't really cast this spell, skip the extra parameters
if (spellCheckPrerequisites(spell, player) != CASTERR_NOERROR) {
gameCastSpell(spell, player, 0);
return;
}
// Get the final parameters for the spell
switch (spellGetParamType(spell)) {
case Spell::PARAM_NONE:
gameCastSpell(spell, player, 0);
break;
case Spell::PARAM_PHASE: {
screenMessage("To Phase: ");
#ifdef IOS
U4IOS::IOSConversationChoiceHelper choiceController;
choiceController.fullSizeChoicePanel();
choiceController.updateGateSpellChoices();
#endif
int choice = ReadChoiceController::get("12345678 \033\n");
if (choice < '1' || choice > '8')
screenMessage("None\n");
else {
screenMessage("\n");
gameCastSpell(spell, player, choice - '1');
}
break;
}
case Spell::PARAM_PLAYER: {
screenMessage("Who: ");
int subject = gameGetPlayer(true, false);
if (subject != -1)
gameCastSpell(spell, player, subject);
break;
}
case Spell::PARAM_DIR:
if (g_context->_location->_context == CTX_DUNGEON)
gameCastSpell(spell, player, g_ultima->_saveGame->_orientation);
else {
screenMessage("Dir: ");
Direction dir = gameGetDirection();
if (dir != DIR_NONE)
gameCastSpell(spell, player, (int) dir);
}
break;
case Spell::PARAM_TYPEDIR: {
screenMessage("Energy type? ");
#ifdef IOS
U4IOS::IOSConversationChoiceHelper choiceController;
choiceController.fullSizeChoicePanel();
choiceController.updateEnergyFieldSpellChoices();
#endif
EnergyFieldType fieldType = ENERGYFIELD_NONE;
char key = ReadChoiceController::get("flps \033\n\r");
switch (key) {
case 'f':
fieldType = ENERGYFIELD_FIRE;
break;
case 'l':
fieldType = ENERGYFIELD_LIGHTNING;
break;
case 'p':
fieldType = ENERGYFIELD_POISON;
break;
case 's':
fieldType = ENERGYFIELD_SLEEP;
break;
default:
break;
}
if (fieldType != ENERGYFIELD_NONE) {
screenMessage("\n");
Direction dir;
if (g_context->_location->_context == CTX_DUNGEON)
dir = (Direction)g_ultima->_saveGame->_orientation;
else {
screenMessage("Dir: ");
dir = gameGetDirection();
}
if (dir != DIR_NONE) {
/* Need to pack both dir and fieldType into param */
int param = fieldType << 4;
param |= (int) dir;
gameCastSpell(spell, player, param);
}
} else {
/* Invalid input here = spell failure */
screenMessage("Failed!\n");
/*
* Confirmed both mixture loss and mp loss in this situation in the
* original Ultima IV (at least, in the Amiga version.)
*/
//c->saveGame->_mixtures[castSpell]--;
g_context->_party->member(player)->adjustMp(-spellGetRequiredMP(spell));
}
break;
}
case Spell::PARAM_FROMDIR: {
screenMessage("From Dir: ");
Direction dir = gameGetDirection();
if (dir != DIR_NONE)
gameCastSpell(spell, player, (int) dir);
break;
}
}
}
bool fireAt(const Coords &coords, bool originAvatar) {
bool validObject = false;

View File

@ -235,7 +235,6 @@ void gameSetViewMode(ViewMode newMode);
void gameUpdateScreen(void);
/* spell functions */
void castSpell(int player = -1);
void gameSpellEffect(int spell, int player, Sound sound);
/* action functions */

View File

@ -917,7 +917,7 @@ bool CombatController::keyPressed(int key) {
case 'c':
screenMessage("Cast Spell!\n");
castSpell(_focus);
g_debugger->castSpell(_focus);
break;
#ifdef IOS

View File

@ -46,6 +46,7 @@ static const KeybindingRecord KEYS[] = {
{ KEYBIND_ATTACK, "ATTACK", "Attack", "attack", "a", nullptr },
{ KEYBIND_BOARD, "BOARD", "Board", "board", "b", nullptr },
{ KEYBIND_CAST, "CAST", "Cast", "cast", "c", nullptr },
{ KEYBIND_CLIMB, "CLIMB", "Climb", "climb", "k", nullptr },
{ KEYBIND_ENTER, "ENTER", "Enter", "enter", "e", nullptr },
{ KEYBIND_FIRE, "FIRE", "Fire", "fire", "f", nullptr },
{ KEYBIND_GET, "GET", "Get Chest", "get", "g", nullptr },
@ -58,8 +59,9 @@ static const KeybindingRecord KEYS[] = {
{ KEYBIND_OPEN_DOOR, "OPEN-DOOR", "Open Door", "open", "o", nullptr },
{ KEYBIND_PASS, "PASS", "Pass", "pass", "SPACE", nullptr },
{ KEYBIND_PEER, "PEER", "Peer", "peer", "p", nullptr },
{ KEYBIND_QUIT_SAVE, "QUIT-SAVE", "Quit and Save", "quitAndSave", "q", nullptr },
{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr, nullptr }
{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr, nullptr }
};
static const KeybindingRecord CHEAT_KEYS[] = {

View File

@ -30,10 +30,11 @@ namespace Ultima4 {
enum KeybindingAction {
KEYBIND_UP, KEYBIND_DOWN, KEYBIND_LEFT, KEYBIND_RIGHT,
KEYBIND_ATTACK, KEYBIND_BOARD, KEYBIND_CAST, KEYBIND_ENTER,
KEYBIND_FIRE, KEYBIND_GET, KEYBIND_HOLE_UP, KEYBIND_IGNITE,
KEYBIND_JIMMY, KEYBIND_LOCATE, KEYBIND_MIX, KEYBIND_NEW_ORDER,
KEYBIND_OPEN_DOOR, KEYBIND_PASS, KEYBIND_PEER,
KEYBIND_ATTACK, KEYBIND_BOARD, KEYBIND_CAST, KEYBIND_CLIMB,
KEYBIND_ENTER, KEYBIND_FIRE, KEYBIND_GET, KEYBIND_HOLE_UP,
KEYBIND_IGNITE, KEYBIND_JIMMY, KEYBIND_LOCATE, KEYBIND_MIX,
KEYBIND_NEW_ORDER, KEYBIND_OPEN_DOOR, KEYBIND_PASS,
KEYBIND_PEER, KEYBIND_QUIT_SAVE,
KEYBIND_NONE
};