Add special nodes that hook into the spell targeting system

- Node to initiate targeting mode and call a special once targets are chosen
- Nodes to place a spell pattern (one for fields and one for booms)
This commit is contained in:
2015-01-17 02:04:23 -05:00
parent 4ae1bf1d24
commit 544cc80e56
11 changed files with 207 additions and 36 deletions

View File

@@ -197,9 +197,9 @@ set-lighting
set-attitude
set-center
lift-fog
start-target
spell-pat-field
spell-pat-boom

View File

@@ -478,3 +478,51 @@ Unused
Unused
Special to Jump To
--------------------
Start Targeting
Unused
Unused
First part of message
Second part of message
Unused
Unused
Unused
Which spell pattern? (0 - single space)
Max range? (ignored outside combat mode)
Max targets? (>1 only in combat)
Unused
Unused
Unused
Special to Call for Each Target
--------------------
Place Fields in Spell Pattern
Unused
Unused
First part of message
Second part of message
Unused
Unused
Unused
X Coordinate of pattern center
Y Coordinate of pattern center
Which spell pattern? (-1 for targeted pattern)
Which field type?
Unused
Unused
Special to Jump To
--------------------
Deal Damage in Spell Pattern
Unused
Unused
First part of message
Second part of message
Unused
Unused
Unused
X Coordinate of pattern center
Y Coordinate of pattern center
Which spell pattern? (-1 for targeted pattern)
Which damage type?
How many dice? (calculated in d6's)
0 - sequential simple booms, 1 - simultaneous animated booms
Special to Jump To
--------------------

View File

@@ -904,7 +904,8 @@ void do_combat_cast(location target) {
num_targets = 8;
}
spell_caster = current_pc;
if(spell_being_cast != eSpell::NONE)
spell_caster = current_pc;
// assign monster summoned, if summoning
if(spell_being_cast == eSpell::SUMMON_BEAST) {
@@ -954,7 +955,10 @@ void do_combat_cast(location target) {
}
boom_targ[i] = target;
switch(spell_being_cast) {
case eSpell::NONE: // Not a spell but a special node targeting
run_special(eSpecCtx::TARGET, which_combat_type + 1, spell_caster, target, &r1, &r2, &item);
if(item > 0) redraw_screen(REFRESH_ALL);
break;
case eSpell::GOO: case eSpell::WEB: case eSpell::GOO_BOMB:
place_spell_pattern(current_pat,target,FIELD_WEB,current_pc);
break;
@@ -4614,7 +4618,7 @@ void combat_immed_priest_cast(short current_pc, eSpell spell_num, bool freebie)
end_missile_anim();
}
void start_spell_targeting(eSpell num, bool freebie) {
void start_spell_targeting(eSpell num, bool freebie, int spell_range, eSpellPat pat) {
// First, remember what spell was cast.
spell_being_cast = num;
@@ -4625,7 +4629,7 @@ void start_spell_targeting(eSpell num, bool freebie) {
add_string_to_buf(" (Hit 'm' to cancel.)");
else add_string_to_buf(" (Hit 'p' to cancel.)");
overall_mode = MODE_SPELL_TARGET;
current_spell_range = (*num).range;
current_spell_range = num == eSpell::NONE ? spell_range : (*num).range;
current_pat = single;
switch(num) { // Different spells have different messages and diff. target shapes
@@ -4649,10 +4653,26 @@ void start_spell_targeting(eSpell num, bool freebie) {
force_wall_position = 0;
current_pat = field[0];
break;
case eSpell::NONE:
switch(pat) {
case PAT_SINGLE: current_pat = single; break;
case PAT_SQ: current_pat = square; break;
case PAT_SMSQ: current_pat = small_square; break;
case PAT_OPENSQ: current_pat = open_square; break;
case PAT_PLUS: current_pat = t; break;
case PAT_RAD2: current_pat = radius2; break;
case PAT_RAD3: current_pat = radius3; break;
case PAT_WALL:
add_string_to_buf(" (Hit space to rotate.)");
force_wall_position = 0;
current_pat = field[0];
break;
}
break;
}
}
void start_fancy_spell_targeting(eSpell num, bool freebie) {
void start_fancy_spell_targeting(eSpell num, bool freebie, int spell_range, eSpellPat pat, int targets) {
short i;
location null_loc(120,0);
@@ -4669,7 +4689,7 @@ void start_fancy_spell_targeting(eSpell num, bool freebie) {
add_string_to_buf(" (Hit space to cast.)");
overall_mode = MODE_FANCY_TARGET;
current_pat = single;
current_spell_range = (*num).range;
current_spell_range = num == eSpell::NONE ? spell_range : (*num).range;
switch(num) { // Assign special targeting shapes and number of targets
case eSpell::SMITE:
@@ -4703,6 +4723,19 @@ void start_fancy_spell_targeting(eSpell num, bool freebie) {
case eSpell::SUMMON_MAJOR:
num_targets_left = minmax(1,5,univ.party[current_pc].level / 8 + stat_adj(current_pc,eSkill::INTELLIGENCE) / 2);
break;
case eSpell::NONE:
num_targets_left = minmax(1,8,targets);
switch(pat) {
case PAT_SINGLE: current_pat = single; break;
case PAT_SQ: current_pat = square; break;
case PAT_SMSQ: current_pat = small_square; break;
case PAT_OPENSQ: current_pat = open_square; break;
case PAT_PLUS: current_pat = t; break;
case PAT_RAD2: current_pat = radius2; break;
case PAT_RAD3: current_pat = radius3; break;
case PAT_WALL: current_pat = single; break; // Fancy targeting doesn't support the rotateable pattern
}
break;
}
num_targets_left = minmax(1,8,num_targets_left);

View File

@@ -6,6 +6,7 @@
#include "monster.h"
#include "outdoors.h"
#include "boe.global.h"
#include "spell.hpp"
void start_outdoor_combat(cOutdoors::cCreature encounter,ter_num_t in_which_terrain,short num_walls);
bool pc_combat_move(location destination);
@@ -56,8 +57,8 @@ bool combat_cast_mage_spell();
bool combat_cast_priest_spell();
void combat_immed_mage_cast(short current_pc, eSpell spell_num, bool freebie = false);
void combat_immed_priest_cast(short current_pc, eSpell spell_num, bool freebie = false);
void start_spell_targeting(eSpell num, bool freebie = false);
void start_fancy_spell_targeting(eSpell num, bool freebie = false);
void start_spell_targeting(eSpell num, bool freebie = false, int spell_range = 4, eSpellPat pat = PAT_SINGLE);
void start_fancy_spell_targeting(eSpell num, bool freebie = false, int spell_range = 4, eSpellPat pat = PAT_SINGLE, int targets = 1);
void spell_cast_hit_return();
void process_fields();
void scloud_space(short m,short n);

View File

@@ -61,7 +61,7 @@ extern bool spell_forced,save_maps,suppress_stat_screen,boom_anim_active;
extern eSpell store_mage, store_priest;
extern short store_mage_lev, store_priest_lev;
extern short store_spell_target,pc_casting,stat_screen_mode;
extern effect_pat_type null_pat,single,t,square,radius2,radius3;
extern effect_pat_type null_pat,single,t,square,radius2,radius3,small_square,open_square;
extern effect_pat_type current_pat;
extern short current_spell_range;
extern short hit_chance[21],combat_active_pc;
@@ -962,8 +962,7 @@ void do_mage_spell(short pc_num,eSpell spell_num,bool freebie) {
break;
case eSpell::DISPEL_SQUARE:
current_pat = square;
start_town_targeting(spell_num,pc_num,freebie);
start_town_targeting(spell_num,pc_num,freebie,PAT_SQ);
break;
case eSpell::LIGHT_LONG:
@@ -1005,13 +1004,11 @@ void do_mage_spell(short pc_num,eSpell spell_num,bool freebie) {
case eSpell::SCRY_MONSTER: case eSpell::UNLOCK: case eSpell::CAPTURE_SOUL: case eSpell::DISPEL_BARRIER:
case eSpell::BARRIER_FIRE: case eSpell::BARRIER_FORCE: case eSpell::QUICKFIRE:
current_pat = single;
start_town_targeting(spell_num,pc_num,freebie);
break;
case eSpell::ANTIMAGIC:
current_pat = radius2;
start_town_targeting(spell_num,pc_num,freebie);
start_town_targeting(spell_num,pc_num,freebie,PAT_RAD2);
break;
case eSpell::FLIGHT:
@@ -1099,7 +1096,6 @@ void do_priest_spell(short pc_num,eSpell spell_num,bool freebie) {
case eSpell::RITUAL_SANCTIFY:
add_string_to_buf(" Sanctify which space? ");
current_pat = single;
start_town_targeting(spell_num,pc_num,freebie);
break;
@@ -1150,13 +1146,11 @@ void do_priest_spell(short pc_num,eSpell spell_num,bool freebie) {
case eSpell::MOVE_MOUNTAINS: case eSpell::MOVE_MOUNTAINS_MASS:
add_string_to_buf(" Destroy what? ");
current_pat = (spell_num == eSpell::MOVE_MOUNTAINS) ? single : square;
start_town_targeting(spell_num,pc_num,freebie);
start_town_targeting(spell_num,pc_num,freebie, spell_num == eSpell::MOVE_MOUNTAINS ? PAT_SINGLE : PAT_SQ);
break;
case eSpell::DISPEL_SPHERE: case eSpell::DISPEL_FIELD:
current_pat = (spell_num == eSpell::DISPEL_SPHERE) ? radius2 : single;
start_town_targeting(spell_num,pc_num,freebie);
start_town_targeting(spell_num,pc_num,freebie, spell_num == eSpell::DISPEL_SPHERE ? PAT_RAD2 : PAT_SINGLE);
break;
case eSpell::DETECT_LIFE:
@@ -1456,6 +1450,7 @@ void do_priest_spell(short pc_num,eSpell spell_num,bool freebie) {
}
}
extern short spell_caster;
void cast_town_spell(location where) {
short adjust,r1,targ,store;
location loc;
@@ -1482,6 +1477,10 @@ void cast_town_spell(location where) {
if(adjust > 4)
add_string_to_buf(" Can't see target. ");
else switch(town_spell) {
case eSpell::NONE: // Not a spell but a special node targeting
run_special(eSpecCtx::TARGET, 2, spell_caster, where, &r1, &targ, &store);
if(store > 0) redraw_screen(REFRESH_ALL);
break;
case eSpell::SCRY_MONSTER: case eSpell::CAPTURE_SOUL:
targ = monst_there(where);
if(targ < univ.town->max_monst()) {
@@ -2293,12 +2292,22 @@ short stat_adj(short pc_num,eSkill which) {
return tr;
}
void start_town_targeting(eSpell s_num,short who_c,bool freebie) {
void start_town_targeting(eSpell s_num,short who_c,bool freebie,eSpellPat pat) {
add_string_to_buf(" Target spell.");
overall_mode = MODE_TOWN_TARGET;
town_spell = s_num;
who_cast = who_c;
spell_freebie = freebie;
switch(pat) {
case PAT_SINGLE: current_pat = single; break;
case PAT_SQ: current_pat = square; break;
case PAT_SMSQ: current_pat = small_square; break;
case PAT_OPENSQ: current_pat = open_square; break;
case PAT_PLUS: current_pat = t; break;
case PAT_RAD2: current_pat = radius2; break;
case PAT_RAD3: current_pat = radius3; break;
case PAT_WALL: current_pat = single; break; // Rotateable wall not supported by town targeting
}
}
extern short alch_difficulty[20];

View File

@@ -2,6 +2,8 @@
#ifndef BOE_GAME_PARTY_H
#define BOE_GAME_PARTY_H
#include "spell.hpp"
class cDialog;
void make_boats();
bool create_pc(short spot,cDialog* parent_num);
@@ -39,7 +41,7 @@ bool pc_can_cast_spell(short pc_num,eSpell spell_num);
bool pc_can_cast_spell(short pc_num,eSkill spell_num);
eSpell pick_spell(short pc_num,eSkill type);
short stat_adj(short pc_num,eSkill which);
void start_town_targeting(eSpell s_num,short who_c,bool freebie);
void start_town_targeting(eSpell s_num,short who_c,bool freebie,eSpellPat pat = PAT_SINGLE);
void do_alchemy();
short alch_choice(short pc_num);
bool pick_pc_graphic(short pc_num,short mode,cDialog* parent_num);

View File

@@ -36,9 +36,11 @@ extern eGameMode overall_mode;
extern short which_combat_type,current_pc,stat_window;
extern location center;
extern bool in_scen_debug,belt_present,processing_fields,monsters_going,suppress_stat_screen,boom_anim_active;
extern effect_pat_type single,t,square,radius2,radius3,small_square,open_square,field[8];
extern effect_pat_type current_pat;
extern cOutdoors::cWandering store_wandering_special;
extern eSpell spell_being_cast, town_spell;
extern short spell_caster;
extern sf::RenderWindow mini_map;
extern short fast_bang;
extern bool end_scenario;
@@ -3681,6 +3683,7 @@ void townmode_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
location l;
ter_num_t ter;
cItem store_i;
effect_pat_type pat;
spec = cur_node;
*next_spec = cur_node.jumpto;
@@ -4076,6 +4079,72 @@ void townmode_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
fog_lifted = spec.ex1a;
redraw_screen(REFRESH_TERRAIN);
break;
case eSpecType::TOWN_START_TARGETING:
if(spec.ex1a < 0 || spec.ex1a > 7) {
giveError("Invalid spell pattern (0 - 7).");
break;
}
if(spec.ex1c > 1 && !is_combat()) {
add_string_to_buf(" Target: Only in combat");
break;
}
if(!is_combat())
start_town_targeting(eSpell::NONE, spec.jumpto, true, eSpellPat(spec.ex1a));
else if(spec.ex1c > 1)
start_fancy_spell_targeting(eSpell::NONE, true, spec.ex1b, eSpellPat(spec.ex1a), spec.ex1c);
else start_spell_targeting(eSpell::NONE, true, spec.ex1b, eSpellPat(spec.ex1a));
spell_caster = spec.jumpto;
*next_spec = -1;
break;
case eSpecType::TOWN_SPELL_PAT_FIELD:
if(spec.ex1c < -1 || spec.ex1c > 14) {
giveError("Invalid spell pattern (-1 - 14).");
break;
}
if((spec.ex2a < 1 || spec.ex2a == 9 || spec.ex2a > 24) && spec.ex2a != 32 && spec.ex2a != 33) {
giveError("Invalid field type (see docs).");
break;
}
switch(spec.ex1c) {
case -1: pat = current_pat; break;
case PAT_SINGLE: pat = single; break;
case PAT_SQ: pat = square; break;
case PAT_SMSQ: pat = small_square; break;
case PAT_OPENSQ: pat = open_square; break;
case PAT_PLUS: pat = t; break;
case PAT_RAD2: pat = radius2; break;
case PAT_RAD3: pat = radius3; break;
default: pat = field[spec.ex1c - 7];
}
place_spell_pattern(pat, l, eFieldType(spec.ex2a), 6);
break;
case eSpecType::TOWN_SPELL_PAT_BOOM:
if(spec.ex1c < -1 || spec.ex1c > 14) {
giveError("Invalid spell pattern (-1 - 14).");
break;
}
if(spec.ex2a < 0 || spec.ex2a > 7) {
giveError("Invalid damage type (0 - 7).");
break;
}
switch(spec.ex1c) {
case -1: pat = current_pat; break;
case PAT_SINGLE: pat = single; break;
case PAT_SQ: pat = square; break;
case PAT_SMSQ: pat = small_square; break;
case PAT_OPENSQ: pat = open_square; break;
case PAT_PLUS: pat = t; break;
case PAT_RAD2: pat = radius2; break;
case PAT_RAD3: pat = radius3; break;
default: pat = field[spec.ex1c - 7];
}
if(spec.ex2c) start_missile_anim();
place_spell_pattern(pat, l, eDamageType(spec.ex2a), spec.ex2b, 6);
if(spec.ex2c) {
do_explosion_anim(0, 0);
end_missile_anim();
}
break;
}
if(check_mess) {
handle_message(which_mode,cur_spec_type,cur_node.m1,cur_node.m2,a,b);

View File

@@ -643,6 +643,9 @@ enum class eSpecType {
TOWN_SET_ATTITUDE = 197,
TOWN_SET_CENTER = 198,
TOWN_LIFT_FOG = 199,
TOWN_START_TARGETING = 200,
TOWN_SPELL_PAT_FIELD = 201,
TOWN_SPELL_PAT_BOOM = 202,
RECT_PLACE_FIELD = 210,
RECT_SET_EXPLORED = 211,
RECT_MOVE_ITEMS = 212,

View File

@@ -370,6 +370,8 @@ std::istream& operator >> (std::istream& in, eSpecType& e) {
// L - Choose button to select a town lighting type
// & - Choose button to select shop type
// % - Choose button to select shop cost adjustment
// { - Choose button to select a spell pattern
// } - As above, but allows you to select which version of the rotateable field
// e - Choose button to select a status effect
// E - Choose button to select a party status effect
// w - Choose button to select main party status effect
@@ -425,17 +427,17 @@ static const char*const button_dict[7][11] = {
"s ss s + s==+s =", // ex2b
" = s", // ex2c
}, { // town nodes
"mmmmmmmmmmmmmmm dddmmmmmmmmm", // msg1
" ", // msg2
" ", // msg3
" 8 ppp ", // pic
" ??? ", // pictype
" c L ", // ex1a
" s s s s @ ", // ex1b
" ", // ex1c
"@ D ! c T T i ", // ex2a
" DD / ", // ex2b
" x x : : ", // ex2c
"mmmmmmmmmmmmmmm dddmmmmmmmmmmmm", // msg1
" ", // msg2
" ", // msg3
" 8 ppp ", // pic
" ??? ", // pictype
" c L { ", // ex1a
" s s s s @ ", // ex1b
" }}", // ex1c
"@ D ! c T T i FD", // ex2a
" DD / ", // ex2b
" x x : : ", // ex2c
}, { // rectangle nodes
"mm mmmmmmm", // msg1
" ", // msg2

View File

@@ -272,6 +272,9 @@ short choose_text(eStrType list, unsigned short cur_choice, cDialog* parent, con
case STRT_STATUS:
strings = {"Alive", "Dead", "Dust", "Petrified", "Fled Outdoor Combat", "Absent", "Deleted"};
break;
case STRT_SPELL_PAT:
strings = {"Single Space", "3x3 Square", "2x2 Square", "3x3 Open Square", "Radius 2 Circle", "Radius 3 Circle", "Cross", "Rotateable 2x8 Wall"};
break;
}
if(cur_choice < 0 || cur_choice >= strings.size())
cur_choice = 0;
@@ -722,6 +725,7 @@ static bool edit_spec_enc_value(cDialog& me, std::string item_hit, node_stack_t&
case '!': choose_string = false; store = choose_boom_type(val, &me); break;
case 'e': choose_string = false; store = choose_status_effect(val, false, &me); break;
case 'E': choose_string = false; store = choose_status_effect(val, true, &me); break;
case '{': case '}': strt = STRT_SPELL_PAT; title = "Which spell pattern?"; break;
case 'i': strt = STRT_ITEM; title = "Which item?"; break;
case 'I': strt = STRT_SPEC_ITEM; title = "Which special item?"; break;
case 't': strt = STRT_TER; title = "Which terrain?"; break;

View File

@@ -10,7 +10,7 @@ enum eStrType {
STRT_PICT, STRT_SND, STRT_CMP, STRT_ACCUM, STRT_TRAP,
STRT_ATTITUDE, STRT_STAIR, STRT_LIGHT, STRT_CONTEXT,
STRT_SHOP, STRT_COST_ADJ, STRT_STAIR_MODE, STRT_TALK_NODE,
STRT_STATUS,
STRT_STATUS, STRT_SPELL_PAT,
};
bool cre(short val,short min,short max,const char *text1,const char *text2,cDialog* parent) ;