DRY spellcasting rules, be clear why can't cast
This is a big refactor of complicated code, and needs extensive testing.
This commit is contained in:
@@ -39,7 +39,7 @@ poison a monster twice, it will do well over twice the damage the first spell wo
|
||||
caused. If one fear spell doesn't make a monster flee, the next one will have a much
|
||||
better chance of working. Casting two light spells makes the light last twice as long.</p>
|
||||
<p>Mage spells require great delicacy of movement to cast. For this reason, they cannot be
|
||||
cast when when armor with total encumbrance of more than 1 is being worn. High defense
|
||||
cast during combat when when armor with total encumbrance of more than 1 is being worn. High defense
|
||||
skill can sometimes offset this effect, but it never will when any single item has an
|
||||
encumbrance value of more than 2.</p>
|
||||
<p>Mage Spells and Encumbrance: If you are wearing armor with a total encumbrance of
|
||||
|
@@ -3445,15 +3445,18 @@ void increase_age(bool eating_trigger_autosave) {
|
||||
}
|
||||
else {
|
||||
if(univ.party.age % 50 == 0) {
|
||||
for(cPlayer& pc : univ.party)
|
||||
for(cPlayer& pc : univ.party){
|
||||
// Bonus HP wears off
|
||||
if(pc.main_status == eMainStatus::ALIVE && pc.cur_health > pc.max_health)
|
||||
pc.cur_health--; // Bonus HP wears off
|
||||
pc.cur_health--;
|
||||
}
|
||||
univ.party.heal(1);
|
||||
}
|
||||
}
|
||||
if(is_out()) {
|
||||
if(univ.party.age % 80 == 0) {
|
||||
univ.party.restore_sp(2);
|
||||
// Enlightenment wears off
|
||||
for(cPlayer& pc : univ.party)
|
||||
if(pc.status[eStatus::DUMB] < 0)
|
||||
pc.status[eStatus::DUMB]++;
|
||||
@@ -3462,8 +3465,10 @@ void increase_age(bool eating_trigger_autosave) {
|
||||
else {
|
||||
if(univ.party.age % 40 == 0) {
|
||||
for(cPlayer& pc : univ.party) {
|
||||
// Bonus SP wears off
|
||||
if(pc.main_status == eMainStatus::ALIVE && pc.cur_sp > pc.max_sp)
|
||||
pc.cur_sp--; // Bonus SP wears off
|
||||
pc.cur_sp--;
|
||||
// Enlightenment wears off
|
||||
if(pc.status[eStatus::DUMB] < 0)
|
||||
pc.status[eStatus::DUMB]++;
|
||||
}
|
||||
|
@@ -4486,35 +4486,24 @@ void end_combat() {
|
||||
|
||||
|
||||
bool combat_cast_mage_spell() {
|
||||
short store_sp;
|
||||
eSpell spell_num;
|
||||
cMonster get_monst;
|
||||
|
||||
if(univ.current_pc().traits[eTrait::ANAMA]) {
|
||||
add_string_to_buf("Cast: You're an Anama!");
|
||||
return false;
|
||||
}
|
||||
short store_sp = univ.current_pc().cur_sp;
|
||||
|
||||
if(univ.town.is_antimagic(univ.current_pc().combat_pos.x,univ.current_pc().combat_pos.y)) {
|
||||
add_string_to_buf(" Not in antimagic field.");
|
||||
return false;
|
||||
}
|
||||
store_sp = univ.current_pc().cur_sp;
|
||||
if(univ.current_pc().cur_sp == 0)
|
||||
add_string_to_buf("Cast: No spell points.");
|
||||
else if(univ.current_pc().skill(eSkill::MAGE_SPELLS) == 0)
|
||||
add_string_to_buf("Cast: No mage skill.");
|
||||
else if(univ.current_pc().total_encumbrance(hit_chance) > 1) {
|
||||
add_string_to_buf("Cast: Too encumbered.");
|
||||
take_ap(6);
|
||||
give_help(40,0);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
||||
eCastStatus status = pc_can_cast_spell(univ.current_pc(), eSkill::MAGE_SPELLS);
|
||||
if(status != CAST_OK){
|
||||
print_cast_status(status, eSkill::MAGE_SPELLS);
|
||||
|
||||
if(status == NO_CAST_ENCUMBERED){
|
||||
// Oops, trying to cast a mage spell while encumbered takes your AP!
|
||||
take_ap(6);
|
||||
give_help(40,0);
|
||||
return true;
|
||||
}
|
||||
}else{
|
||||
if(!spell_forced)
|
||||
spell_num = pick_spell(univ.cur_pc,eSkill::MAGE_SPELLS);
|
||||
spell_num = pick_spell(univ.cur_pc,eSkill::MAGE_SPELLS, true);
|
||||
else {
|
||||
if(!repeat_cast_ok(eSkill::MAGE_SPELLS))
|
||||
return false;
|
||||
@@ -4566,8 +4555,8 @@ bool combat_cast_mage_spell() {
|
||||
draw_terrain(2);
|
||||
combat_immed_mage_cast(univ.cur_pc,spell_num);
|
||||
}
|
||||
put_pc_screen();
|
||||
}
|
||||
put_pc_screen();
|
||||
combat_posing_monster = current_working_monster = -1;
|
||||
// Did anything actually get cast?
|
||||
if(store_sp == univ.current_pc().cur_sp)
|
||||
@@ -4725,60 +4714,51 @@ void combat_immed_mage_cast(short current_pc, eSpell spell_num, bool freebie) {
|
||||
}
|
||||
|
||||
bool combat_cast_priest_spell() {
|
||||
short store_sp;
|
||||
eSpell spell_num;
|
||||
|
||||
if(univ.town.is_antimagic(univ.current_pc().combat_pos.x,univ.current_pc().combat_pos.y)) {
|
||||
add_string_to_buf(" Not in antimagic field.");
|
||||
return false;
|
||||
}
|
||||
if(!spell_forced)
|
||||
spell_num = pick_spell(univ.cur_pc,eSkill::PRIEST_SPELLS);
|
||||
else {
|
||||
if(!repeat_cast_ok(eSkill::PRIEST_SPELLS))
|
||||
return false;
|
||||
spell_num = univ.current_pc().last_cast[eSkill::PRIEST_SPELLS];
|
||||
}
|
||||
|
||||
store_sp = univ.current_pc().cur_sp;
|
||||
if(univ.current_pc().cur_sp == 0) {
|
||||
add_string_to_buf("Cast: No spell points.");
|
||||
return false;
|
||||
} else if(univ.current_pc().skill(eSkill::PRIEST_SPELLS) == 0) {
|
||||
add_string_to_buf("Cast: No priest skill.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(univ.current_pc().traits[eTrait::PACIFIST] && spell_num != eSpell::NONE && !(*spell_num).peaceful) {
|
||||
add_string_to_buf("Cast: You're a pacifist!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(spell_num == eSpell::NONE) return false;
|
||||
|
||||
combat_posing_monster = current_working_monster = univ.cur_pc;
|
||||
|
||||
if(spell_num != eSpell::NONE) {
|
||||
print_spell_cast(spell_num,eSkill::PRIEST_SPELLS);
|
||||
if((*spell_num).refer == REFER_YES) {
|
||||
take_ap(5);
|
||||
draw_terrain(2);
|
||||
do_priest_spell(univ.cur_pc,spell_num);
|
||||
}
|
||||
else if((*spell_num).refer == REFER_TARGET) {
|
||||
start_spell_targeting(spell_num);
|
||||
}
|
||||
else if((*spell_num).refer == REFER_FANCY) {
|
||||
start_fancy_spell_targeting(spell_num);
|
||||
}
|
||||
short store_sp = univ.current_pc().cur_sp;
|
||||
|
||||
eCastStatus status = pc_can_cast_spell(univ.current_pc(), eSkill::PRIEST_SPELLS);
|
||||
if(status != CAST_OK){
|
||||
print_cast_status(status, eSkill::PRIEST_SPELLS);
|
||||
}else{
|
||||
if(!spell_forced)
|
||||
spell_num = pick_spell(univ.cur_pc,eSkill::PRIEST_SPELLS, true);
|
||||
else {
|
||||
take_ap(5);
|
||||
draw_terrain(2);
|
||||
combat_immed_priest_cast(univ.cur_pc, spell_num);
|
||||
if(!repeat_cast_ok(eSkill::MAGE_SPELLS))
|
||||
return false;
|
||||
spell_num = univ.current_pc().last_cast[eSkill::PRIEST_SPELLS];
|
||||
}
|
||||
|
||||
if(univ.current_pc().traits[eTrait::PACIFIST] && spell_num != eSpell::NONE && !(*spell_num).peaceful) {
|
||||
add_string_to_buf("Cast: You're a pacifist!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(spell_num == eSpell::NONE) return false;
|
||||
|
||||
combat_posing_monster = current_working_monster = univ.cur_pc;
|
||||
|
||||
if(spell_num != eSpell::NONE) {
|
||||
print_spell_cast(spell_num,eSkill::PRIEST_SPELLS);
|
||||
if((*spell_num).refer == REFER_YES) {
|
||||
take_ap(5);
|
||||
draw_terrain(2);
|
||||
do_priest_spell(univ.cur_pc,spell_num);
|
||||
}
|
||||
else if((*spell_num).refer == REFER_TARGET) {
|
||||
start_spell_targeting(spell_num);
|
||||
}
|
||||
else if((*spell_num).refer == REFER_FANCY) {
|
||||
start_fancy_spell_targeting(spell_num);
|
||||
}
|
||||
else {
|
||||
take_ap(5);
|
||||
draw_terrain(2);
|
||||
combat_immed_priest_cast(univ.cur_pc, spell_num);
|
||||
}
|
||||
}
|
||||
put_pc_screen();
|
||||
}
|
||||
|
||||
combat_posing_monster = current_working_monster = -1;
|
||||
// Did anything actually get cast?
|
||||
if(store_sp == univ.current_pc().cur_sp)
|
||||
|
@@ -471,7 +471,7 @@ void cast_spell(eSkill type) {
|
||||
eSpell spell;
|
||||
|
||||
if((is_town()) && (univ.town.is_antimagic(univ.party.town_loc.x,univ.party.town_loc.y))) {
|
||||
add_string_to_buf(" Not in antimagic field.");
|
||||
add_string_to_buf("Cast: Not in antimagic field.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1570,26 +1570,58 @@ void dispel_fields(short i,short j,short mode) {
|
||||
break_force_cage(loc(i,j));
|
||||
}
|
||||
|
||||
bool pc_can_cast_spell(const cPlayer& pc,eSkill type) {
|
||||
extern std::array<short, 51> hit_chance;
|
||||
eCastStatus pc_can_cast_spell(const cPlayer& pc,const eSkill type) {
|
||||
if(type == eSkill::MAGE_SPELLS && pc.traits[eTrait::ANAMA]) {
|
||||
return NO_CAST_ANAMA;
|
||||
}
|
||||
if(pc.skill(type) == 0) {
|
||||
return NO_CAST_SKILL;
|
||||
}
|
||||
if(pc.cur_sp == 0) {
|
||||
return NO_CAST_SP;
|
||||
}
|
||||
|
||||
if(is_combat() && univ.town.is_antimagic(pc.combat_pos.x,pc.combat_pos.y)) {
|
||||
return NO_CAST_ANTIMAGIC;
|
||||
}
|
||||
if(is_combat() && type == eSkill::MAGE_SPELLS && pc.total_encumbrance(hit_chance) > 1) {
|
||||
return NO_CAST_ENCUMBERED;
|
||||
}
|
||||
|
||||
if(type == eSkill::MAGE_SPELLS && pc_can_cast_spell(pc, eSpell::LIGHT))
|
||||
return true;
|
||||
return CAST_OK;
|
||||
if(type == eSkill::PRIEST_SPELLS && pc_can_cast_spell(pc, eSpell::HEAL_MINOR))
|
||||
return true;
|
||||
return CAST_OK;
|
||||
|
||||
// If they can't cast the most basic level 1 spell, let's just make sure they can't cast any spells.
|
||||
// Find a spell they definitely know, and see if they can cast that.
|
||||
if(type == eSkill::MAGE_SPELLS && pc.mage_spells.any()) {
|
||||
for(int i = 0; i < 62; i++)
|
||||
if(pc.mage_spells[i])
|
||||
return pc_can_cast_spell(pc, eSpell(i));
|
||||
if(type == eSkill::MAGE_SPELLS && pc.mage_spells.any()){
|
||||
for(int i = 0; i < 62; i++){
|
||||
if(pc.mage_spells[i]){
|
||||
if(pc_can_cast_spell(pc, eSpell(i))) return CAST_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(type == eSkill::PRIEST_SPELLS && pc.priest_spells.any()) {
|
||||
for(int i = 0; i < 62; i++)
|
||||
if(pc.priest_spells[i])
|
||||
return pc_can_cast_spell(pc, eSpell(i + 100));
|
||||
if(type == eSkill::PRIEST_SPELLS && pc.priest_spells.any()){
|
||||
for(int i = 0; i < 62; i++){
|
||||
if(pc.priest_spells[i]){
|
||||
if(pc_can_cast_spell(pc, eSpell(i + 100))) return CAST_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we get this far, either they don't know any spells (very unlikely) or they can't cast any of the spells they know.
|
||||
return false;
|
||||
if(pc.status[eStatus::DUMB] > 0){
|
||||
return NO_CAST_DUMBFOUNDED;
|
||||
}
|
||||
if(pc.status[eStatus::PARALYZED] != 0){
|
||||
return NO_CAST_PARALYZED;
|
||||
}
|
||||
if(pc.status[eStatus::ASLEEP] > 0){
|
||||
return NO_CAST_ASLEEP;
|
||||
}
|
||||
return NO_CAST_UNKNOWN;
|
||||
}
|
||||
|
||||
bool pc_can_cast_spell(const cPlayer& pc,eSpell spell_num) {
|
||||
@@ -1665,7 +1697,7 @@ static void draw_caster_buttons(cDialog& me, const eSkill store_situation) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(pc_can_cast_spell(univ.party[i],store_situation)) {
|
||||
if(pc_can_cast_spell(univ.party[i],store_situation) == CAST_OK) {
|
||||
me[id].show();
|
||||
}
|
||||
else {
|
||||
@@ -2038,10 +2070,51 @@ static bool finish_pick_spell(cDialog& me, bool spell_toast, const short store_s
|
||||
return true;
|
||||
}
|
||||
|
||||
eCastStatus check_can_cast(const cPlayer& pc, eSkill type) {
|
||||
return CAST_OK;
|
||||
}
|
||||
|
||||
void print_cast_status(eCastStatus status, eSkill type, std::string pc_name) {
|
||||
std::string prefix = "Cast";
|
||||
// When multiple PCs are checked, explain why each one can't cast.
|
||||
if(!pc_name.empty()) prefix += " (" + pc_name + ")";
|
||||
prefix += ": ";
|
||||
switch(status){
|
||||
case NO_CAST_ANAMA:
|
||||
add_string_to_buf(prefix + "You're an Anama!");
|
||||
break;
|
||||
case NO_CAST_SKILL:
|
||||
if(type == eSkill::MAGE_SPELLS) add_string_to_buf(prefix + "No mage skill.");
|
||||
else add_string_to_buf(prefix + "No priest skill.");
|
||||
break;
|
||||
case NO_CAST_ENCUMBERED:
|
||||
add_string_to_buf(prefix + "Too encumbered.");
|
||||
break;
|
||||
case NO_CAST_SP:
|
||||
add_string_to_buf(prefix + "No spell points.");
|
||||
break;
|
||||
case NO_CAST_ANTIMAGIC:
|
||||
add_string_to_buf(prefix + "Not in antimagic field.");
|
||||
break;
|
||||
case NO_CAST_DUMBFOUNDED:
|
||||
add_string_to_buf(prefix + "You're dumbfounded!");
|
||||
break;
|
||||
case NO_CAST_PARALYZED:
|
||||
add_string_to_buf(prefix + "You're paralyzed!");
|
||||
break;
|
||||
case NO_CAST_ASLEEP:
|
||||
add_string_to_buf(prefix + "You're asleep!");
|
||||
break;
|
||||
case NO_CAST_UNKNOWN:
|
||||
add_string_to_buf(prefix + "You can't!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//short pc_num; // if 6, anyone
|
||||
//short type; // 0 - mage 1 - priest
|
||||
//short situation; // 0 - out 1 - town 2 - combat
|
||||
eSpell pick_spell(short pc_num,eSkill type) { // 70 - no spell OW spell num
|
||||
eSpell pick_spell(short pc_num,const eSkill type, bool check_done) { // 70 - no spell OW spell num
|
||||
using namespace std::placeholders;
|
||||
eSpell default_spell = type == eSkill::MAGE_SPELLS ? store_mage : store_priest;
|
||||
short former_target = store_spell_target;
|
||||
@@ -2053,11 +2126,21 @@ eSpell pick_spell(short pc_num,eSkill type) { // 70 - no spell OW spell num
|
||||
|
||||
if(pc_num == 6) { // See if can keep same caster
|
||||
can_choose_caster = true;
|
||||
if(!pc_can_cast_spell(univ.party[pc_casting],type)) {
|
||||
using namespace std::placeholders;
|
||||
eCastStatus same_caster_status = pc_can_cast_spell(univ.party[pc_casting],type);
|
||||
// If the party is in an antimagic field, individual statuses won't matter
|
||||
if(same_caster_status == NO_CAST_ANTIMAGIC){
|
||||
print_cast_status(NO_CAST_ANTIMAGIC, type);
|
||||
}
|
||||
else if(same_caster_status != CAST_OK) {
|
||||
|
||||
auto iter = std::find_if(univ.party.begin(), univ.party.end(), [type](const cPlayer& who) {
|
||||
return pc_can_cast_spell(who, type);
|
||||
eCastStatus status = pc_can_cast_spell(who, type);
|
||||
if(status == CAST_OK) return true;
|
||||
if(status >= NO_CAST_SP && status <= NO_CAST_ASLEEP)
|
||||
print_cast_status(status, type, who.name);
|
||||
return false;
|
||||
});
|
||||
|
||||
if(iter == univ.party.end()) {
|
||||
add_string_to_buf("Cast: Nobody can.");
|
||||
return eSpell::NONE;
|
||||
@@ -2070,21 +2153,12 @@ eSpell pick_spell(short pc_num,eSkill type) { // 70 - no spell OW spell num
|
||||
pc_casting = pc_num;
|
||||
}
|
||||
|
||||
if(!can_choose_caster) {
|
||||
if(type == eSkill::MAGE_SPELLS && univ.party[pc_casting].traits[eTrait::ANAMA]) {
|
||||
add_string_to_buf("Cast: You're an Anama!");
|
||||
if(!can_choose_caster && !check_done) {
|
||||
eCastStatus status = check_can_cast(univ.party[pc_casting], type);
|
||||
if(status != CAST_OK){
|
||||
print_cast_status(status, type);
|
||||
return eSpell::NONE;
|
||||
}
|
||||
if(univ.party[pc_num].skill(type) == 0) {
|
||||
if(type == eSkill::MAGE_SPELLS) add_string_to_buf("Cast: No mage skill.");
|
||||
else add_string_to_buf("Cast: No priest skill.");
|
||||
return eSpell::NONE;
|
||||
}
|
||||
if(univ.party[pc_casting].cur_sp == 0) {
|
||||
add_string_to_buf("Cast: No spell points.");
|
||||
return eSpell::NONE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If in combat, make the spell being cast this PCs most recent spell
|
||||
|
@@ -25,14 +25,36 @@ bool cast_spell_on_space(location where, eSpell spell);
|
||||
void crumble_wall(location where);
|
||||
void do_mindduel(short pc_num,cCreature *monst);
|
||||
void dispel_fields(short i,short j,short mode);
|
||||
|
||||
// Reasons the player can't cast any Mage or Priest spells
|
||||
enum eCastStatus {
|
||||
CAST_OK,
|
||||
// Immutable reasons, shouldn't be printed when multiple PCs are checked (peace mode)
|
||||
NO_CAST_SKILL,
|
||||
NO_CAST_ANAMA,
|
||||
// Should only be printed once when multiple PCs are checked (peace mode)
|
||||
NO_CAST_ANTIMAGIC,
|
||||
// Should be printed for each character when multiple PCs are checked (peace mode)
|
||||
NO_CAST_SP,
|
||||
NO_CAST_ENCUMBERED,
|
||||
NO_CAST_DUMBFOUNDED,
|
||||
NO_CAST_PARALYZED,
|
||||
NO_CAST_ASLEEP,
|
||||
// Fallthrough: If there are any ways this can be reached, they should be given their own enum values!
|
||||
NO_CAST_UNKNOWN,
|
||||
// Pacifism is intentionally not included here because it applies only to specific spells,
|
||||
// and disables them in the spell picker or when you use the item.
|
||||
};
|
||||
eCastStatus pc_can_cast_spell(const cPlayer& pc,const eSkill type);
|
||||
bool pc_can_cast_spell(const cPlayer& pc,eSpell spell_num);
|
||||
bool pc_can_cast_spell(const cPlayer& pc,eSkill spell_num);
|
||||
eSpell pick_spell(short pc_num,eSkill type);
|
||||
void print_cast_status(eCastStatus status, eSkill type, std::string pc_name = "");
|
||||
eSpell pick_spell(short pc_num, eSkill type, bool check_done = false);
|
||||
|
||||
void start_town_targeting(eSpell s_num,short who_c,bool freebie,eSpellPat pat = PAT_SINGLE);
|
||||
void do_alchemy();
|
||||
eAlchemy alch_choice(short pc_num);
|
||||
bool pick_pc_graphic(short pc_num,short mode,cDialog* parent_num);
|
||||
bool pick_pc_name(short pc_num,cDialog* parent) ;
|
||||
bool pick_pc_name(short pc_num,cDialog* parent);
|
||||
bool has_trapped_monst();
|
||||
mon_num_t pick_trapped_monst();
|
||||
bool flying() ;
|
||||
|
Reference in New Issue
Block a user