- Add all objects, fields, and sfx to place_spell_pattern - Add move mountains effect to place_spell_pattern - Most of the implementation for force cages and stone blocks is now done (still untested) - Properly implement MOVE_MOUNTAINS_MASS spell
5032 lines
162 KiB
C++
5032 lines
162 KiB
C++
|
|
#include <cstdio>
|
|
|
|
//#include "item.h"
|
|
|
|
#include "boe.global.h"
|
|
|
|
#include "classes.h"
|
|
#include "boe.monster.h"
|
|
#include "boe.graphics.h"
|
|
#include "boe.locutils.h"
|
|
#include "boe.newgraph.h"
|
|
#include "boe.infodlg.h"
|
|
#include "boe.text.h"
|
|
#include "boe.items.h"
|
|
#include "boe.party.h"
|
|
#include "boe.combat.h"
|
|
#include "soundtool.h"
|
|
#include "boe.town.h"
|
|
#include "boe.specials.h"
|
|
#include "boe.graphutil.h"
|
|
#include "boe.main.h"
|
|
#include "mathutil.h"
|
|
#include "dlogutil.h"
|
|
#include "boe.menus.h"
|
|
#include "spell.hpp"
|
|
|
|
//extern party_record_type party;
|
|
//extern current_town_type univ.town;
|
|
//extern big_tr_type t_d;
|
|
//extern town_item_list t_i;
|
|
extern eGameMode overall_mode;
|
|
extern bool ghost_mode;
|
|
extern short which_combat_type;
|
|
extern short stat_window;
|
|
extern location center;
|
|
extern ter_num_t combat_terrain[64][64];
|
|
extern short current_pc;
|
|
extern short combat_active_pc;
|
|
extern bool monsters_going,spell_forced;
|
|
extern bool flushingInput;
|
|
extern sf::RenderWindow mainPtr;
|
|
extern eSpell store_mage, store_priest;
|
|
extern short store_mage_lev, store_priest_lev,store_item_spell_level;
|
|
extern short store_spell_target,pc_casting,current_spell_range;
|
|
extern effect_pat_type current_pat;
|
|
//extern short town_size[3];
|
|
//extern short monst_target[60]; // 0-5 target that pc 6 - no target 100 + x - target monster x
|
|
extern short num_targets_left;
|
|
extern location spell_targets[8];
|
|
extern bool in_scen_debug;
|
|
extern bool fast_bang;
|
|
//extern unsigned char misc_i[64][64],sfx[64][64];
|
|
extern short store_current_pc;
|
|
//extern location monster_targs[60];
|
|
extern short combat_posing_monster , current_working_monster ; // 0-5 PC 100 + x - monster x
|
|
extern cScenario scenario;
|
|
extern short spell_caster, missile_firer,current_monst_tactic;
|
|
char create_line[60];
|
|
eSpell spell_being_cast;
|
|
bool spell_freebie;
|
|
short missile_inv_slot, ammo_inv_slot;
|
|
short force_wall_position = 10; // 10 -> no force wall
|
|
bool processing_fields = true;
|
|
short futzing;
|
|
m_num_t store_sum_monst;
|
|
short store_sum_monst_cost;
|
|
extern cUniverse univ;
|
|
|
|
location out_start_loc(20,23);
|
|
short hit_chance[51] = {
|
|
20,30,40,45,50,55,60,65,69,73,
|
|
77,81,84,87,90,92,94,96,97,98,99
|
|
,99,99,99,99,99,99,99,99,99,99
|
|
,99,99,99,99,99,99,99,99,99,99,
|
|
99,99,99,99,99,99,99,99,99,99};
|
|
|
|
short monst_mage_spell[55] = {
|
|
1,1,1,1,1,1,2,2,2,2,
|
|
2,2,3,3,3,3,3,4,4,4,
|
|
4,4,4,5,5,5,5,5,4,4,
|
|
6,6,6,6,7,7,7,7,7,8,
|
|
8,8,8,8,9,9,9,10,10,10,
|
|
11,11,11,12,12};
|
|
short monst_cleric_spell[55] = {
|
|
1,1,1,1,1,1,1,1,2,2,
|
|
2,2,2,3,3,3,11,11,11,4,
|
|
4,4,4,5,5,5,11,11,6,6,
|
|
6,6,6,6,7,7,7,7,7,7,
|
|
8,8,8,8,8,7,7,7,7,7,
|
|
7,9,9,10,10};
|
|
short monst_mage_cost[27] = {1,1,1,1,2, 2,2,2,2,4, 2,4,4,3,4, 4,4,5,5,5, 5,6,6,6,7, 7,7};
|
|
short monst_mage_area_effect[27] = {0,0,0,0,0, 0,0,0,1,0, 1,1,0,1,0, 0,0,0,1,0, 1,0,0,0,0, 0,0};
|
|
short monst_priest_cost[26] = {1,1,1,1,2, 2,2,4,2,3, 3,3,4,4,4, 5,5,5,10,6, 6,10,8,8,8, 8};
|
|
short monst_priest_area_effect[26] = {0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0,0, 0,0,0,0,0, 1};
|
|
|
|
extern short boom_gr[8];
|
|
|
|
const char *d_string[] = {"North", "NorthEast", "East", "SouthEast", "South", "SouthWest", "West", "NorthWest"};
|
|
|
|
short monst_marked_damage[60];
|
|
|
|
location hor_vert_place[14] = {
|
|
loc(0,0),loc(-1,1),loc(1,1),loc(-2,2),loc(0,2),
|
|
loc(2,2),loc(0,1),loc(-1,2),loc(1,2),loc(-1,3),
|
|
loc(1,3),loc(0,3),loc(0,4),loc(0,5)};
|
|
location diag_place[14] = {
|
|
loc(0,0),loc(-1,0),loc(0,1),loc(-1,1),loc(-2,0),
|
|
loc(0,2),loc(-2,1),loc(-1,2),loc(-2,2),loc(-3,2),
|
|
loc(-2,3),loc(-3,3),loc(-4,3),loc(-3,4)};
|
|
|
|
|
|
effect_pat_type null_pat = {{
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0}}};
|
|
effect_pat_type single = {{
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,1,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0}}};
|
|
effect_pat_type t = {{
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,1,0,0,0,0},
|
|
{0,0,0,1,1,1,0,0,0},
|
|
{0,0,0,0,1,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0}}};
|
|
effect_pat_type small_square = {{
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,1,1,0,0,0},
|
|
{0,0,0,0,1,1,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0}}};
|
|
effect_pat_type square = {{
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,1,1,1,0,0,0},
|
|
{0,0,0,1,1,1,0,0,0},
|
|
{0,0,0,1,1,1,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0}}};
|
|
effect_pat_type open_square = {{
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,1,1,1,0,0,0},
|
|
{0,0,0,1,0,1,0,0,0},
|
|
{0,0,0,1,1,1,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0}}};
|
|
effect_pat_type rad2 = {{
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,1,1,1,0,0,0},
|
|
{0,0,1,1,1,1,1,0,0},
|
|
{0,0,1,1,1,1,1,0,0},
|
|
{0,0,1,1,1,1,1,0,0},
|
|
{0,0,0,1,1,1,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0}}};
|
|
effect_pat_type rad3 = {{
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,1,1,1,0,0,0},
|
|
{0,0,1,1,1,1,1,0,0},
|
|
{0,1,1,1,1,1,1,1,0},
|
|
{0,1,1,1,1,1,1,1,0},
|
|
{0,1,1,1,1,1,1,1,0},
|
|
{0,0,1,1,1,1,1,0,0},
|
|
{0,0,0,1,1,1,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0}}};
|
|
effect_pat_type field[8] = {
|
|
{{
|
|
{0,0,0,0,1,1,0,0,0},
|
|
{0,0,0,0,1,1,0,0,0},
|
|
{0,0,0,0,1,1,0,0,0},
|
|
{0,0,0,0,1,1,0,0,0},
|
|
{0,0,0,0,1,1,0,0,0},
|
|
{0,0,0,0,1,1,0,0,0},
|
|
{0,0,0,0,1,1,0,0,0},
|
|
{0,0,0,0,1,1,0,0,0},
|
|
{0,0,0,0,1,1,0,0,0}}},
|
|
|
|
{{
|
|
{0,0,0,0,0,0,0,0,1},
|
|
{0,0,0,0,0,0,0,1,1},
|
|
{0,0,0,0,0,0,1,1,0},
|
|
{0,0,0,0,0,1,1,0,0},
|
|
{0,0,0,0,1,1,0,0,0},
|
|
{0,0,0,1,1,0,0,0,0},
|
|
{0,0,1,1,0,0,0,0,0},
|
|
{0,1,1,0,0,0,0,0,0},
|
|
{1,1,0,0,0,0,0,0,0}}},
|
|
|
|
{{
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{1,1,1,1,1,1,1,1,1},
|
|
{1,1,1,1,1,1,1,1,1},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0}}},
|
|
|
|
{{
|
|
{1,0,0,0,0,0,0,0,0},
|
|
{1,1,0,0,0,0,0,0,0},
|
|
{0,1,1,0,0,0,0,0,0},
|
|
{0,0,1,1,0,0,0,0,0},
|
|
{0,0,0,1,1,0,0,0,0},
|
|
{0,0,0,0,1,1,0,0,0},
|
|
{0,0,0,0,0,1,1,0,0},
|
|
{0,0,0,0,0,0,1,1,0},
|
|
{0,0,0,0,0,0,0,1,1}}},
|
|
|
|
{{
|
|
{0,0,0,1,1,0,0,0,0},
|
|
{0,0,0,1,1,0,0,0,0},
|
|
{0,0,0,1,1,0,0,0,0},
|
|
{0,0,0,1,1,0,0,0,0},
|
|
{0,0,0,1,1,0,0,0,0},
|
|
{0,0,0,1,1,0,0,0,0},
|
|
{0,0,0,1,1,0,0,0,0},
|
|
{0,0,0,1,1,0,0,0,0},
|
|
{0,0,0,1,1,0,0,0,0}}},
|
|
|
|
{{
|
|
{0,0,0,0,0,0,0,1,1},
|
|
{0,0,0,0,0,0,1,1,0},
|
|
{0,0,0,0,0,1,1,0,0},
|
|
{0,0,0,0,1,1,0,0,0},
|
|
{0,0,0,1,1,0,0,0,0},
|
|
{0,0,1,1,0,0,0,0,0},
|
|
{0,1,1,0,0,0,0,0,0},
|
|
{1,1,0,0,0,0,0,0,0},
|
|
{1,0,0,0,0,0,0,0,0}}},
|
|
|
|
{{
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{1,1,1,1,1,1,1,1,1},
|
|
{1,1,1,1,1,1,1,1,1},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0},
|
|
{0,0,0,0,0,0,0,0,0}}},
|
|
|
|
{{
|
|
{1,1,0,0,0,0,0,0,0},
|
|
{0,1,1,0,0,0,0,0,0},
|
|
{0,0,1,1,0,0,0,0,0},
|
|
{0,0,0,1,1,0,0,0,0},
|
|
{0,0,0,0,1,1,0,0,0},
|
|
{0,0,0,0,0,1,1,0,0},
|
|
{0,0,0,0,0,0,1,1,0},
|
|
{0,0,0,0,0,0,0,1,1},
|
|
{0,0,0,0,0,0,0,0,1}}}};
|
|
|
|
bool center_on_monst;
|
|
|
|
|
|
|
|
|
|
|
|
void start_outdoor_combat(cOutdoors::cCreature encounter,ter_num_t in_which_terrain,short num_walls)
|
|
{
|
|
short i,j,how_many,num_tries = 0;
|
|
// short low[10] = {15,7,3,3,1,1,1,7,2,1};
|
|
// short high[10] = {30,10,5,5,3,2,1,10,4,1};
|
|
short low[10] = {15,7,4,3,2,1,1,7,2,1};
|
|
short high[10] = {30,10,6,5,3,2,1,10,4,1};
|
|
rectangle town_rect(0,0,47,47);
|
|
short nums[10];
|
|
|
|
for (i = 0; i < 7; i++)
|
|
nums[i] = get_ran(1,low[i],high[i]);
|
|
for (i = 0; i < 3; i++)
|
|
nums[i + 7] = get_ran(1,low[i + 7],high[i + 7]);
|
|
notify_out_combat_began(encounter.what_monst,nums);
|
|
print_buf();
|
|
play_sound(23);
|
|
|
|
mainPtr.setActive();
|
|
which_combat_type = 0;
|
|
overall_mode = MODE_COMBAT;
|
|
|
|
// Basically, in outdoor combat, we create kind of a 48x48 town for
|
|
// the combat to take place in
|
|
for (i = 0; i < 48; i++)
|
|
for (j = 0; j < 48; j++) {
|
|
univ.town.fields[i][j] = 0;
|
|
//univ.out.misc_i[i][j] = 0;
|
|
//univ.out.sfx[i][j] = 0;
|
|
}
|
|
univ.town.prep_arena();
|
|
univ.town->in_town_rect = town_rect;
|
|
|
|
create_out_combat_terrain((short) in_which_terrain,num_walls,0);////
|
|
|
|
for (i = 0; i < univ.town->max_monst(); i++) {
|
|
univ.town.monst[i].number = 0;
|
|
univ.town.monst[i].active = 0;
|
|
}
|
|
for (i = 0; i < 7; i++) {
|
|
how_many = nums[i];
|
|
if (encounter.what_monst.monst[i] != 0)
|
|
for (j = 0; j < how_many; j++)
|
|
set_up_monst(0,encounter.what_monst.monst[i]);
|
|
}
|
|
for (i = 0; i < 3; i++) {
|
|
how_many = nums[i + 7];
|
|
if (encounter.what_monst.friendly[i] != 0)
|
|
for (j = 0; j < how_many; j++)
|
|
set_up_monst(1,encounter.what_monst.friendly[i]);
|
|
}
|
|
|
|
// place PCs
|
|
univ.party[0].combat_pos = out_start_loc;
|
|
update_explored(univ.party[0].combat_pos);
|
|
if (get_blockage(combat_terrain[univ.party[0].combat_pos.x][univ.party[0].combat_pos.y]) > 0)
|
|
combat_terrain[univ.party[0].combat_pos.x][univ.party[0].combat_pos.y] = combat_terrain[0][0];
|
|
for (i = 1; i < 6; i++) {
|
|
univ.party[i].combat_pos = univ.party[0].combat_pos;
|
|
univ.party[i].combat_pos.x = univ.party[i].combat_pos.x + hor_vert_place[i].x;
|
|
univ.party[i].combat_pos.y = univ.party[i].combat_pos.y + hor_vert_place[i].y;
|
|
if (get_blockage(combat_terrain[univ.party[i].combat_pos.x][univ.party[i].combat_pos.y]) > 0)
|
|
combat_terrain[univ.party[i].combat_pos.x][univ.party[i].combat_pos.y] = combat_terrain[0][0];
|
|
update_explored(univ.party[i].combat_pos);
|
|
|
|
univ.party[i].status[eStatus::POISONED_WEAPON] = 0;
|
|
univ.party[i].status[eStatus::BLESS_CURSE] = 0;
|
|
univ.party[i].status[eStatus::HASTE_SLOW] = 0;
|
|
univ.party[i].status[eStatus::INVULNERABLE] = 0;
|
|
univ.party[i].status[eStatus::MAGIC_RESISTANCE] = 0;
|
|
}
|
|
|
|
// place monsters, w. friendly monsts landing near PCs
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if (univ.town.monst[i].active > 0) {
|
|
univ.town.monst[i].target = 6;
|
|
|
|
univ.town.monst[i].cur_loc.x = get_ran(1,15,25);
|
|
univ.town.monst[i].cur_loc.y = get_ran(1,14,18);
|
|
if (univ.town.monst[i].attitude == 2)
|
|
univ.town.monst[i].cur_loc.y += 9;
|
|
else if ((univ.town.monst[i].mu > 0) || (univ.town.monst[i].cl > 0))
|
|
univ.town.monst[i].cur_loc.y -= 4;//max(12,univ.town.monst[i].m_loc.y - 4);
|
|
num_tries = 0;
|
|
while (((monst_can_be_there(univ.town.monst[i].cur_loc,i) == false) ||
|
|
(combat_terrain[univ.town.monst[i].cur_loc.x][univ.town.monst[i].cur_loc.y] == 180) ||
|
|
(pc_there(univ.town.monst[i].cur_loc) < 6)) &&
|
|
(num_tries++ < 50)) {
|
|
univ.town.monst[i].cur_loc.x = get_ran(1,15,25);
|
|
univ.town.monst[i].cur_loc.y = get_ran(1,14,18);
|
|
if (univ.town.monst[i].attitude == 2)
|
|
univ.town.monst[i].cur_loc.y += 9;
|
|
else if ((univ.town.monst[i].mu > 0) || (univ.town.monst[i].cl > 0))
|
|
univ.town.monst[i].cur_loc.y -= 4;//max(12,univ.town.monst[i].m_loc.y - 4);
|
|
}
|
|
if (get_blockage(combat_terrain[univ.town.monst[i].cur_loc.x][univ.town.monst[i].cur_loc.y]) > 0)
|
|
combat_terrain[univ.town.monst[i].cur_loc.x][univ.town.monst[i].cur_loc.y] = combat_terrain[0][0];
|
|
}
|
|
|
|
|
|
combat_active_pc = 6;
|
|
spell_caster = 6; missile_firer = 6;
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
univ.town.monst[i].target = 6;
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
univ.party[i].parry = 0;
|
|
univ.party[i].last_attacked = univ.town->max_monst() + 10;
|
|
}
|
|
|
|
for (i = 0; i < NUM_TOWN_ITEMS; i++)
|
|
univ.town.items[i].variety = eItemType::NO_ITEM;
|
|
store_current_pc = current_pc;
|
|
current_pc = 0;
|
|
set_pc_moves();
|
|
pick_next_pc();
|
|
center = univ.party[current_pc].combat_pos;
|
|
draw_buttons(0);
|
|
put_pc_screen();
|
|
set_stat_window(current_pc);
|
|
|
|
adjust_spell_menus();
|
|
|
|
//clear_map();
|
|
give_help(48,49);
|
|
|
|
}
|
|
|
|
bool pc_combat_move(location destination) ////
|
|
{
|
|
short dir,monst_hit,s1,s2,i,monst_exist,switch_pc;
|
|
bool keep_going = true,forced = false,check_f = false;
|
|
location monst_loc,store_loc;
|
|
short spec_num;
|
|
|
|
monst_hit = monst_there(destination);
|
|
|
|
if(monst_hit > univ.town->max_monst() && univ.party[current_pc].status[eStatus::FORCECAGE] > 0) {
|
|
add_string_to_buf("Move: Can't escape.");
|
|
return false;
|
|
}
|
|
|
|
if(monst_hit > univ.town->max_monst())
|
|
keep_going = check_special_terrain(destination,eSpecCtx::COMBAT_MOVE,current_pc,&spec_num,&check_f);
|
|
if (check_f == true)
|
|
forced = true;
|
|
|
|
if(in_scen_debug && ghost_mode) forced = true;
|
|
|
|
if (keep_going == true) {
|
|
|
|
dir = set_direction(univ.party[current_pc].combat_pos, destination);
|
|
|
|
if ((loc_off_act_area(destination) == true) && (which_combat_type == 1)) {
|
|
add_string_to_buf("Move: Can't leave town during combat.");
|
|
print_buf();
|
|
return true;
|
|
}
|
|
else if ((combat_terrain[destination.x][destination.y] == 90) && (which_combat_type == 0)) {
|
|
if (get_ran(1,1,10) < 3) {
|
|
univ.party[current_pc].main_status = eMainStatus::FLED;
|
|
if (combat_active_pc == current_pc)
|
|
combat_active_pc = 6;
|
|
sprintf (create_line, "Moved: Fled. ");
|
|
univ.party[current_pc].ap = 0;
|
|
}
|
|
else {
|
|
take_ap(1);
|
|
sprintf (create_line, "Moved: Couldn't flee. ");
|
|
}
|
|
add_string_to_buf(create_line);
|
|
return true;
|
|
}
|
|
else if(monst_hit <= univ.town->max_monst()) {
|
|
// s2 = 2 here appears to mean "go ahead and attack", while s2 = 1 means "cancel attack".
|
|
// Then s1 % 2 == 1 means the monster is hostile to the party.
|
|
s1 = univ.town.monst[monst_hit].attitude;
|
|
if(s1 % 2 == 1) s2 = 2;
|
|
else {
|
|
std::string result = cChoiceDlog("attack-friendly.xml",{"cancel","attack"}).show();
|
|
if(result == "cancel") s2 = 1;
|
|
else if(result == "attack") s2 = 2;
|
|
}
|
|
if ((s2 == 2) && (s1 % 2 != 1))
|
|
make_town_hostile();
|
|
if (s2 == 2) {
|
|
univ.party[current_pc].last_attacked = monst_hit;
|
|
pc_attack(current_pc,monst_hit);
|
|
return true;
|
|
}
|
|
}
|
|
else if ((switch_pc = pc_there(destination)) < 6) {
|
|
if (univ.party[switch_pc].ap == 0) {
|
|
add_string_to_buf("Move: Can't switch places.");
|
|
add_string_to_buf(" (other PC has no APs) ");
|
|
return false;
|
|
}
|
|
else univ.party[switch_pc].ap--;
|
|
add_string_to_buf("Move: Switch places.");
|
|
store_loc = univ.party[current_pc].combat_pos;
|
|
univ.party[current_pc].combat_pos = destination;
|
|
univ.party[switch_pc].combat_pos = store_loc;
|
|
univ.party[current_pc].direction = dir;
|
|
take_ap(1);
|
|
check_special_terrain(store_loc,eSpecCtx::COMBAT_MOVE,switch_pc,&spec_num,&check_f);
|
|
move_sound(combat_terrain[destination.x][destination.y],univ.party[current_pc].ap);
|
|
return true;
|
|
}
|
|
else if ((forced == true)
|
|
|| ((impassable(combat_terrain[destination.x][destination.y]) == false) && (pc_there(destination) == 6))) {
|
|
|
|
// monsters get back-shots
|
|
for (i = 0; i < univ.town->max_monst(); i++) {
|
|
monst_loc = univ.town.monst[i].cur_loc;
|
|
monst_exist = univ.town.monst[i].active;
|
|
|
|
s1 = current_pc;
|
|
if ((monst_exist > 0) && (monst_adjacent(univ.party[current_pc].combat_pos,i) == true)
|
|
&& (monst_adjacent(destination,i) == false) &&
|
|
(univ.town.monst[i].attitude % 2 == 1) &&
|
|
univ.town.monst[i].status[eStatus::ASLEEP] <= 0 &&
|
|
univ.town.monst[i].status[eStatus::PARALYZED] <= 0) {
|
|
combat_posing_monster = current_working_monster = 100 + i;
|
|
monster_attack_pc(i,current_pc);
|
|
combat_posing_monster = current_working_monster = -1;
|
|
draw_terrain(0);
|
|
}
|
|
if (s1 != current_pc)
|
|
return true;
|
|
}
|
|
|
|
// move if still alive
|
|
if(univ.party[current_pc].main_status == eMainStatus::ALIVE) {
|
|
univ.party[current_pc].dir = set_direction(univ.party[current_pc].combat_pos,destination);
|
|
univ.party[current_pc].combat_pos = destination;
|
|
univ.party[current_pc].direction = dir;
|
|
take_ap(1);
|
|
sprintf ((char *) create_line, "Moved: %s ",d_string[dir]);
|
|
add_string_to_buf((char *) create_line);
|
|
move_sound(combat_terrain[destination.x][destination.y],univ.party[current_pc].ap);
|
|
|
|
}
|
|
else return false;
|
|
return true;
|
|
}
|
|
else {
|
|
sprintf ((char *) create_line, "Blocked: %s ",d_string[dir]);
|
|
add_string_to_buf((char *) create_line);
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void char_parry()
|
|
{
|
|
univ.party[current_pc].parry = (univ.party[current_pc].ap / 4) *
|
|
(2 + stat_adj(current_pc,eSkill::DEXTERITY) + univ.party[current_pc].skills[eSkill::DEFENSE]);
|
|
univ.party[current_pc].ap = 0;
|
|
}
|
|
|
|
void char_stand_ready()
|
|
{
|
|
univ.party[current_pc].parry = 100;
|
|
univ.party[current_pc].ap = 0;
|
|
}
|
|
|
|
void pc_attack(short who_att,short target)////
|
|
{
|
|
short r1,r2,weap1 = 24, weap2 = 24,i,store_hp,skill_item;
|
|
eSkill what_skill1 = eSkill::DEXTERITY, what_skill2 = eSkill::DEXTERITY;
|
|
cCreature *which_m;
|
|
short hit_adj, dam_adj, spec_dam = 0,poison_amt;
|
|
|
|
// slice out bad attacks
|
|
if(univ.party[who_att].main_status != eMainStatus::ALIVE)
|
|
return;
|
|
if(univ.party[who_att].status[eStatus::ASLEEP] > 0 || univ.party[who_att].status[eStatus::PARALYZED] > 0)
|
|
return;
|
|
|
|
univ.party[who_att].last_attacked = target;
|
|
which_m = &univ.town.monst[target];
|
|
|
|
for (i = 0; i < 24; i++)
|
|
if (((univ.party[who_att].items[i].variety == eItemType::ONE_HANDED) || (univ.party[who_att].items[i].variety == eItemType::TWO_HANDED)) &&
|
|
univ.party[who_att].equip[i]) {
|
|
if (weap1 == 24)
|
|
weap1 = i;
|
|
else weap2 = i;
|
|
}
|
|
|
|
hit_adj = (-5 * minmax(-8,8,univ.party[who_att].status[eStatus::BLESS_CURSE])) + 5 * minmax(-8,8,which_m->status[eStatus::BLESS_CURSE])
|
|
- stat_adj(who_att,eSkill::DEXTERITY) * 5 + (get_encumberance(who_att)) * 5;
|
|
|
|
dam_adj = minmax(-8,8,univ.party[who_att].status[eStatus::BLESS_CURSE]) - minmax(-8,8,which_m->status[eStatus::BLESS_CURSE])
|
|
+ stat_adj(who_att,eSkill::STRENGTH);
|
|
|
|
if(which_m->status[eStatus::ASLEEP] > 0 || which_m->status[eStatus::PARALYZED] > 0) {
|
|
hit_adj -= 80;
|
|
dam_adj += 10;
|
|
}
|
|
|
|
|
|
if ((skill_item = pc_has_abil_equip(who_att,eItemAbil::SKILL)) < 24) {
|
|
hit_adj += 5 * (univ.party[who_att].items[skill_item].item_level / 2 + 1);
|
|
dam_adj += univ.party[who_att].items[skill_item].item_level / 2;
|
|
}
|
|
if ((skill_item = pc_has_abil_equip(who_att,eItemAbil::GIANT_STRENGTH)) < 24) {
|
|
dam_adj += univ.party[who_att].items[skill_item].item_level;
|
|
hit_adj += univ.party[who_att].items[skill_item].item_level * 2;
|
|
}
|
|
|
|
void_sanctuary(who_att);
|
|
|
|
store_hp = univ.town.monst[target].health;
|
|
|
|
combat_posing_monster = current_working_monster = who_att;
|
|
|
|
if (weap1 == 24) {
|
|
|
|
sprintf ((char *) create_line, "%s punches. ",(char *) univ.party[who_att].name.c_str());//,hit_adj, dam_adj);
|
|
add_string_to_buf((char *) create_line);
|
|
|
|
r1 = get_ran(1,1,100) + hit_adj - 20;
|
|
r1 += 5 * (univ.party[current_pc].status[eStatus::WEBS] / 3);
|
|
r2 = get_ran(1,1,4) + dam_adj;
|
|
|
|
if (r1 <= hit_chance[univ.party[who_att].skills[what_skill1]]) {
|
|
damage_monst(target, who_att, r2, 0,DAMAGE_WEAPON,4);
|
|
}
|
|
else {
|
|
draw_terrain(2);
|
|
sprintf (create_line, "%s misses. ",(char *) univ.party[who_att].name.c_str());
|
|
add_string_to_buf(create_line);
|
|
play_sound(2);
|
|
}
|
|
}
|
|
|
|
// Don't forget awkward and stat adj.
|
|
if (weap1 < 24) {
|
|
what_skill1 = univ.party[who_att].items[weap1].type;
|
|
|
|
// safety valve
|
|
if (what_skill1 == eSkill::INVALID)
|
|
what_skill1 = eSkill::EDGED_WEAPONS;
|
|
|
|
sprintf (create_line, "%s swings. ",univ.party[who_att].name.c_str());//,hit_adj, dam_adj);
|
|
add_string_to_buf(create_line);
|
|
|
|
r1 = get_ran(1,1,100) - 5 + hit_adj
|
|
- 5 * univ.party[who_att].items[weap1].bonus;
|
|
r1 += 5 * (univ.party[current_pc].status[eStatus::WEBS] / 3);
|
|
|
|
if(weap2 < 24 && !univ.party[who_att].traits[eTrait::AMBIDEXTROUS])
|
|
r1 += 25;
|
|
|
|
// race adj.
|
|
if(univ.party[who_att].race == eRace::SLITH && univ.party[who_att].items[weap1].type == eSkill::POLE_WEAPONS)
|
|
r1 -= 10;
|
|
|
|
r2 = get_ran(1,1,univ.party[who_att].items[weap1].item_level) + dam_adj + 2 + univ.party[who_att].items[weap1].bonus;
|
|
if (univ.party[who_att].items[weap1].ability == eItemAbil::WEAK_WEAPON)
|
|
r2 = (r2 * (10 - univ.party[who_att].items[weap1].ability_strength)) / 10;
|
|
|
|
if (r1 <= hit_chance[univ.party[who_att].skills[what_skill1]]) {
|
|
spec_dam = calc_spec_dam(univ.party[who_att].items[weap1].ability,
|
|
univ.party[who_att].items[weap1].ability_strength,which_m);
|
|
|
|
// assassinate
|
|
r1 = get_ran(1,1,100);
|
|
if ((univ.party[who_att].level >= which_m->level - 1)
|
|
&& univ.party[who_att].skills[eSkill::ASSASSINATION] >= which_m->level / 2
|
|
&& (which_m->spec_skill != 12)) // Can't assassinate splitters
|
|
if(r1 < hit_chance[max(univ.party[who_att].skills[eSkill::ASSASSINATION] - which_m->level,0)]) {
|
|
add_string_to_buf(" You assassinate. ");
|
|
spec_dam += r2;
|
|
}
|
|
|
|
switch (what_skill1) {
|
|
case eSkill::EDGED_WEAPONS:
|
|
if (univ.party[who_att].items[weap1].item_level < 8)
|
|
damage_monst(target, who_att, r2, spec_dam, DAMAGE_WEAPON,1);
|
|
else damage_monst(target, who_att, r2, spec_dam, DAMAGE_WEAPON,2);
|
|
break;
|
|
case eSkill::BASHING_WEAPONS:
|
|
damage_monst(target, who_att, r2, spec_dam, DAMAGE_WEAPON,4);
|
|
break;
|
|
case eSkill::POLE_WEAPONS:
|
|
damage_monst(target, who_att, r2, spec_dam, DAMAGE_WEAPON,3);
|
|
break;
|
|
}
|
|
// poison
|
|
if(univ.party[who_att].status[eStatus::POISONED_WEAPON] > 0 && univ.party[who_att].weap_poisoned == weap1) {
|
|
poison_amt = univ.party[who_att].status[eStatus::POISONED_WEAPON];
|
|
if (pc_has_abil_equip(who_att,eItemAbil::POISON_AUGMENT) < 24)
|
|
poison_amt += 2;
|
|
poison_monst(which_m,poison_amt);
|
|
move_to_zero(univ.party[who_att].status[eStatus::POISONED_WEAPON]);
|
|
}
|
|
if ((univ.party[who_att].items[weap1].ability == eItemAbil::POISONED_WEAPON) && (get_ran(1,0,1) == 1)) {
|
|
add_string_to_buf(" Blade drips venom. ");
|
|
poison_monst(which_m,univ.party[who_att].items[weap1].ability_strength / 2);
|
|
}
|
|
if ((univ.party[who_att].items[weap1].ability == eItemAbil::ACIDIC_WEAPON) && (get_ran(1,0,1) == 1)) {
|
|
add_string_to_buf(" Blade drips acid. ");
|
|
acid_monst(which_m,univ.party[who_att].items[weap1].ability_strength / 2);
|
|
}
|
|
if ((univ.party[who_att].items[weap1].ability == eItemAbil::SOULSUCKER) && (get_ran(1,0,1) == 1)) {
|
|
add_string_to_buf(" Blade drains life. ");
|
|
heal_pc(who_att,univ.party[who_att].items[weap1].ability_strength / 2);
|
|
}
|
|
}
|
|
else {
|
|
draw_terrain(2);
|
|
sprintf ((char *) create_line, " %s misses. ",(char *) univ.party[who_att].name.c_str());
|
|
add_string_to_buf((char *) create_line);
|
|
if (what_skill1 == eSkill::POLE_WEAPONS)
|
|
play_sound(19);
|
|
else play_sound(2);
|
|
}
|
|
}
|
|
if ((weap2 < 24) && (which_m->active > 0)) {
|
|
what_skill2 = univ.party[who_att].items[weap2].type;
|
|
|
|
// safety valve
|
|
if (what_skill1 == eSkill::INVALID)
|
|
what_skill1 = eSkill::EDGED_WEAPONS;
|
|
|
|
|
|
sprintf(create_line, "%s swings. ",univ.party[who_att].name.c_str());//,hit_adj, dam_adj);
|
|
add_string_to_buf(create_line);
|
|
r1 = get_ran(1,1,100) + hit_adj - 5 * univ.party[who_att].items[weap2].bonus;
|
|
|
|
// Ambidextrous?
|
|
if(!univ.party[who_att].traits[eTrait::AMBIDEXTROUS])
|
|
r1 += 25;
|
|
|
|
r1 += 5 * (univ.party[current_pc].status[eStatus::WEBS] / 3);
|
|
r2 = get_ran(1,1,univ.party[who_att].items[weap2].item_level) + dam_adj - 1 + univ.party[who_att].items[weap2].bonus;
|
|
if (univ.party[who_att].items[weap2].ability == eItemAbil::WEAK_WEAPON)
|
|
r2 = (r2 * (10 - univ.party[who_att].items[weap2].ability_strength)) / 10;
|
|
|
|
if (r1 <= hit_chance[univ.party[who_att].skills[what_skill2]]) {
|
|
spec_dam = calc_spec_dam(univ.party[who_att].items[weap2].ability,
|
|
univ.party[who_att].items[weap2].ability_strength,which_m);
|
|
switch (what_skill2) {
|
|
case eSkill::EDGED_WEAPONS:
|
|
if (univ.party[who_att].items[weap1].item_level < 8)
|
|
damage_monst(target, who_att, r2, spec_dam, DAMAGE_WEAPON,1);
|
|
else damage_monst(target, who_att, r2, spec_dam, DAMAGE_WEAPON,2);
|
|
break;
|
|
case eSkill::BASHING_WEAPONS:
|
|
damage_monst(target, who_att, r2, spec_dam, DAMAGE_WEAPON,4);
|
|
break;
|
|
case eSkill::POLE_WEAPONS:
|
|
damage_monst(target, who_att, r2, spec_dam, DAMAGE_WEAPON,3);
|
|
break;
|
|
}
|
|
|
|
if ((univ.party[who_att].items[weap2].ability == eItemAbil::POISONED_WEAPON) && (get_ran(1,0,1) == 1)) {
|
|
add_string_to_buf(" Blade drips venom. ");
|
|
poison_monst(which_m,univ.party[who_att].items[weap2].ability_strength / 2);
|
|
}
|
|
if ((univ.party[who_att].items[weap2].ability == eItemAbil::ACIDIC_WEAPON) && (get_ran(1,0,1) == 1)) {
|
|
add_string_to_buf(" Blade drips acid. ");
|
|
acid_monst(which_m,univ.party[who_att].items[weap2].ability_strength / 2);
|
|
}
|
|
if ((univ.party[who_att].items[weap2].ability == eItemAbil::SOULSUCKER) && (get_ran(1,0,1) == 1)) {
|
|
add_string_to_buf(" Blade drains life. ");
|
|
heal_pc(who_att,univ.party[who_att].items[weap2].ability_strength / 2);
|
|
}
|
|
|
|
}
|
|
else {
|
|
draw_terrain(2);
|
|
sprintf ((char *) create_line, "%s misses. ",(char *) univ.party[who_att].name.c_str());
|
|
add_string_to_buf((char *) create_line);
|
|
if (what_skill2 == eSkill::POLE_WEAPONS)
|
|
play_sound(19);
|
|
else play_sound(2);
|
|
}
|
|
}
|
|
move_to_zero(univ.party[who_att].status[eStatus::POISONED_WEAPON]);
|
|
take_ap(4);
|
|
|
|
if((univ.town.monst[target].status[eStatus::MARTYRS_SHIELD] > 0 || univ.town.monst[target].spec_skill == 22)
|
|
&& (store_hp - univ.town.monst[target].health > 0)) {
|
|
add_string_to_buf(" Shares damage! ");
|
|
damage_pc(who_att, store_hp - univ.town.monst[target].health, DAMAGE_MAGIC,eRace::UNKNOWN,0);
|
|
}
|
|
combat_posing_monster = current_working_monster = -1;
|
|
|
|
}
|
|
|
|
|
|
short calc_spec_dam(eItemAbil abil,short abil_str,cCreature *monst) {
|
|
short store = 0;
|
|
|
|
switch (abil) {
|
|
case eItemAbil::FLAMING_WEAPON:
|
|
case eItemAbil::MISSILE_LIGHTNING:
|
|
store += get_ran(abil_str,1,6);
|
|
break;
|
|
case eItemAbil::DEMON_SLAYER:
|
|
if(monst->m_type == eRace::DEMON)
|
|
store += 8 * abil_str;
|
|
break;
|
|
case eItemAbil::MISSILE_SLAY_DEMON:
|
|
if(monst->m_type == eRace::DEMON)
|
|
store += 25 + 8 * abil_str;
|
|
break;
|
|
case eItemAbil::MISSILE_SLAY_UNDEAD:
|
|
if(monst->m_type == eRace::UNDEAD)
|
|
store += 20 + 6 * abil_str;
|
|
break;
|
|
case eItemAbil::UNDEAD_SLAYER:
|
|
if(monst->m_type == eRace::UNDEAD)
|
|
store += 6 * abil_str;
|
|
break;
|
|
case eItemAbil::LIZARD_SLAYER:
|
|
if(monst->m_type == eRace::REPTILE)
|
|
store += 5 * abil_str;
|
|
break;
|
|
case eItemAbil::GIANT_SLAYER:
|
|
if(monst->m_type == eRace::GIANT)
|
|
store += 8 * abil_str;
|
|
break;
|
|
case eItemAbil::MAGE_SLAYER:
|
|
if(monst->m_type == eRace::MAGE)
|
|
store += 4 * abil_str;
|
|
break;
|
|
case eItemAbil::PRIEST_SLAYER:
|
|
if(monst->m_type == eRace::PRIEST)
|
|
store += 4 * abil_str;
|
|
break;
|
|
case eItemAbil::BUG_SLAYER:
|
|
if(monst->m_type == eRace::BUG)
|
|
store += 7 * abil_str;
|
|
break;
|
|
case eItemAbil::CAUSES_FEAR:
|
|
scare_monst(monst,abil_str * 10);
|
|
break;
|
|
case eItemAbil::MISSILE_ACID:
|
|
acid_monst(monst,abil_str);
|
|
break;
|
|
}
|
|
return store;
|
|
}
|
|
|
|
void place_target(location target)
|
|
{
|
|
short i;
|
|
|
|
if (num_targets_left > 0) {
|
|
if (loc_off_act_area(target) == true) {
|
|
add_string_to_buf(" Space not in town. ");
|
|
return;
|
|
}
|
|
if (can_see_light(univ.party[current_pc].combat_pos,target,sight_obscurity) > 4) {
|
|
add_string_to_buf(" Can't see target. ");
|
|
return;
|
|
}
|
|
if (dist(univ.party[current_pc].combat_pos,target) > (*spell_being_cast).range) {
|
|
add_string_to_buf(" Target out of range.");
|
|
return;
|
|
}
|
|
if(sight_obscurity(target.x,target.y) == 5 && spell_being_cast != eSpell::DISPEL_BARRIER) {
|
|
add_string_to_buf(" Target space obstructed. ");
|
|
return;
|
|
}
|
|
if (univ.town.is_antimagic(target.x,target.y)) {
|
|
add_string_to_buf(" Target in antimagic field.");
|
|
return;
|
|
}
|
|
for (i = 0; i < 8; i++) {
|
|
if (spell_targets[i] == target) {
|
|
add_string_to_buf(" Target removed.");
|
|
num_targets_left++;
|
|
spell_targets[i].x = 120;
|
|
play_sound(-1);
|
|
return;
|
|
}
|
|
}
|
|
for (i = 0; i < 8; i++)
|
|
if (spell_targets[i].x == 120) {
|
|
add_string_to_buf(" Target added.");
|
|
spell_targets[i] = target;
|
|
num_targets_left--;
|
|
play_sound(0);
|
|
i = 8;
|
|
}
|
|
}
|
|
|
|
if (num_targets_left == 0) {
|
|
do_combat_cast(spell_targets[0]);
|
|
overall_mode = MODE_COMBAT;
|
|
}
|
|
}
|
|
|
|
void do_combat_cast(location target)////
|
|
{
|
|
short adjust,r1,r2,targ_num,level,bonus = 1,i,item,store_sound = 0;
|
|
cCreature *cur_monst;
|
|
bool freebie = false,ap_taken = false,cost_taken = false;
|
|
short num_targets = 1,store_m_type = 2;
|
|
eFieldType spray_type_array[15] = {
|
|
FIELD_WEB,FIELD_WEB,FIELD_WEB,
|
|
WALL_FORCE,WALL_FORCE,
|
|
WALL_FIRE,WALL_FIRE,WALL_FIRE,
|
|
FIELD_ANTIMAGIC,FIELD_ANTIMAGIC,
|
|
CLOUD_STINK,CLOUD_STINK,
|
|
WALL_ICE,WALL_ICE,WALL_BLADES,
|
|
};
|
|
m_num_t summon;
|
|
|
|
location ashes_loc;
|
|
|
|
// to wedge in animations, have to kludge like crazy
|
|
short boom_dam[8] = {0,0,0,0,0,0,0,0};
|
|
eDamageType boom_type[8];
|
|
location boom_targ[8];
|
|
|
|
if(spell_freebie) {
|
|
freebie = true;
|
|
level = store_item_spell_level;
|
|
level = minmax(2,20,level);
|
|
}
|
|
else {
|
|
level = 1 + univ.party[current_pc].level / 2;
|
|
bonus = stat_adj(current_pc,eSkill::INTELLIGENCE);
|
|
}
|
|
force_wall_position = 10;
|
|
|
|
void_sanctuary(current_pc);
|
|
if (overall_mode == MODE_SPELL_TARGET) {
|
|
spell_targets[0] = target;
|
|
}
|
|
else {
|
|
num_targets = 8;
|
|
}
|
|
|
|
spell_caster = current_pc;
|
|
|
|
// assign monster summoned, if summoning
|
|
if(spell_being_cast == eSpell::SUMMON_BEAST) {
|
|
summon = get_summon_monster(1);
|
|
|
|
} else if (spell_being_cast == eSpell::SUMMON_WEAK) {
|
|
summon = get_summon_monster(1);
|
|
} else if (spell_being_cast == eSpell::SUMMON) {
|
|
summon = get_summon_monster(2);
|
|
} else if (spell_being_cast == eSpell::SUMMON_MAJOR) {
|
|
summon = get_summon_monster(3);
|
|
}
|
|
combat_posing_monster = current_working_monster = current_pc;
|
|
|
|
for (i = 0; i < num_targets; i++)
|
|
if (spell_targets[i].x != 120) {
|
|
target = spell_targets[i];
|
|
spell_targets[i].x = 120; // nullify target as it is used
|
|
|
|
if(!cost_taken && !freebie && spell_being_cast != eSpell::MINDDUEL && spell_being_cast != eSpell::SIMULACRUM) {
|
|
univ.party[current_pc].cur_sp -= (*spell_being_cast).cost;
|
|
cost_taken = true;
|
|
}
|
|
if(!cost_taken && !freebie && spell_being_cast == eSpell::SIMULACRUM) {
|
|
univ.party[current_pc].cur_sp -= store_sum_monst_cost;
|
|
cost_taken = true;
|
|
}
|
|
|
|
if ((adjust = can_see_light(univ.party[current_pc].combat_pos,target,sight_obscurity)) > 4) {
|
|
add_string_to_buf(" Can't see target. ");
|
|
}
|
|
else if (loc_off_act_area(target) == true) {
|
|
add_string_to_buf(" Space not in town. ");
|
|
}
|
|
else if (dist(univ.party[current_pc].combat_pos,target) > (*spell_being_cast).range)
|
|
add_string_to_buf(" Target out of range.");
|
|
else if(sight_obscurity(target.x,target.y) == 5 && spell_being_cast != eSpell::DISPEL_BARRIER)
|
|
add_string_to_buf(" Target space obstructed. ");
|
|
else if (univ.town.is_antimagic(target.x,target.y))
|
|
add_string_to_buf(" Target in antimagic field.");
|
|
else {
|
|
if (ap_taken == false) {
|
|
if (freebie == false)
|
|
take_ap(5);
|
|
ap_taken = true;
|
|
draw_terrain(2);
|
|
}
|
|
boom_targ[i] = target;
|
|
switch (spell_being_cast) {
|
|
|
|
case eSpell::GOO: case eSpell::WEB: case eSpell::GOO_BOMB:
|
|
place_spell_pattern(current_pat,target,FIELD_WEB,current_pc);
|
|
break;
|
|
case eSpell::CLOUD_FLAME: case eSpell::CONFLAGRATION:
|
|
place_spell_pattern(current_pat,target,WALL_FIRE,current_pc);
|
|
break;
|
|
case eSpell::CLOUD_STINK: case eSpell::FOUL_VAPOR:
|
|
place_spell_pattern(current_pat,target,CLOUD_STINK,current_pc);
|
|
break;
|
|
case eSpell::WALL_FORCE: case eSpell::SHOCKSTORM: case eSpell::FORCEFIELD:
|
|
place_spell_pattern(current_pat,target,WALL_FORCE,current_pc);
|
|
break;
|
|
case eSpell::WALL_ICE: case eSpell::WALL_ICE_BALL:
|
|
place_spell_pattern(current_pat,target,WALL_ICE,current_pc);
|
|
break;
|
|
case eSpell::ANTIMAGIC:
|
|
place_spell_pattern(current_pat,target,FIELD_ANTIMAGIC,current_pc);
|
|
break;
|
|
case eSpell::CLOUD_SLEEP: case eSpell::CLOUD_SLEEP_LARGE:
|
|
place_spell_pattern(current_pat,target,CLOUD_SLEEP,current_pc);
|
|
break;
|
|
case eSpell::QUICKFIRE:
|
|
univ.town.set_quickfire(target.x,target.y,true);
|
|
break;
|
|
case eSpell::SPRAY_FIELDS:
|
|
r1 = get_ran(1,0,14);
|
|
place_spell_pattern(current_pat,target,spray_type_array[r1],current_pc);
|
|
break;
|
|
case eSpell::WALL_BLADES:
|
|
place_spell_pattern(current_pat,target,WALL_BLADES,current_pc);
|
|
break;
|
|
case eSpell::DISPEL_FIELD: case eSpell::DISPEL_SPHERE: case eSpell::DISPEL_SQUARE:
|
|
place_spell_pattern(current_pat,target,FIELD_DISPEL,current_pc);
|
|
break;
|
|
case eSpell::BARRIER_FIRE:
|
|
play_sound(68);
|
|
r1 = get_ran(3,2,7);
|
|
hit_space(target,r1,DAMAGE_FIRE,true,true);
|
|
univ.town.set_fire_barr(target.x,target.y,true);
|
|
if (univ.town.is_fire_barr(target.x,target.y))
|
|
add_string_to_buf(" You create the barrier. ");
|
|
else add_string_to_buf(" Failed.");
|
|
break;
|
|
case eSpell::BARRIER_FORCE:
|
|
play_sound(68);
|
|
r1 = get_ran(7,2,7);
|
|
hit_space(target,r1,DAMAGE_FIRE,true,true);
|
|
univ.town.set_force_barr(target.x,target.y,true);
|
|
if (univ.town.is_force_barr(target.x,target.y))
|
|
add_string_to_buf(" You create the barrier. ");
|
|
else add_string_to_buf(" Failed.");
|
|
break;
|
|
|
|
default: // spells which involve animations
|
|
start_missile_anim();
|
|
switch (spell_being_cast) {
|
|
|
|
case eSpell::DIVINE_THUD:
|
|
add_missile(target,9,1,0,0);
|
|
store_sound = 11;
|
|
r1 = min(18,(level * 7) / 10 + 2 * bonus);
|
|
place_spell_pattern(rad2,target,DAMAGE_MAGIC,r1,current_pc);
|
|
ashes_loc = target;
|
|
break;
|
|
|
|
case eSpell::SPARK: case eSpell::ICE_BOLT:
|
|
r1 = (spell_being_cast == eSpell::SPARK) ? get_ran(2,1,4) : get_ran(min(20,level + bonus),1,4);
|
|
add_missile(target,6,1,0,0);
|
|
do_missile_anim(100,univ.party[current_pc].combat_pos,11);
|
|
hit_space(target,r1,(spell_being_cast == eSpell::SPARK) ? DAMAGE_MAGIC : DAMAGE_COLD,1,0);
|
|
break;
|
|
case eSpell::ARROWS_FLAME:
|
|
add_missile(target,4,1,0,0);
|
|
r1 = get_ran(2,1,4);
|
|
boom_type[i] = DAMAGE_FIRE;
|
|
boom_dam[i] = r1;
|
|
//hit_space(target,r1,1,1,0);
|
|
break;
|
|
case eSpell::SMITE:
|
|
add_missile(target,6,1,0,0);
|
|
r1 = get_ran(2,1,5);
|
|
boom_type[i] = DAMAGE_COLD;
|
|
boom_dam[i] = r1;
|
|
//hit_space(target,r1,5,1,0);
|
|
break;
|
|
case eSpell::WOUND:
|
|
r1 = get_ran(min(7,2 + bonus + level / 2),1,4);
|
|
add_missile(target,14,1,0,0);
|
|
do_missile_anim(100,univ.party[current_pc].combat_pos,24);
|
|
hit_space(target,r1,DAMAGE_UNBLOCKABLE,1,0);
|
|
break;
|
|
case eSpell::FLAME:
|
|
r1 = get_ran(min(10,1 + level / 3 + bonus),1,6);
|
|
add_missile(target,2,1,0,0);
|
|
do_missile_anim(100,univ.party[current_pc].combat_pos,11);
|
|
hit_space(target,r1,DAMAGE_FIRE,1,0);
|
|
break;
|
|
case eSpell::FIREBALL: case eSpell::FLAMESTRIKE:
|
|
r1 = min(9,1 + (level * 2) / 3 + bonus) + 1;
|
|
add_missile(target,2,1,0,0);
|
|
store_sound = 11;
|
|
//do_missile_anim(100,univ.party[current_pc].combat_pos,11);
|
|
if (spell_being_cast == eSpell::FLAMESTRIKE)
|
|
r1 = (r1 * 14) / 10;
|
|
else if (r1 > 10) r1 = (r1 * 8) / 10;
|
|
if (r1 <= 0) r1 = 1;
|
|
place_spell_pattern(square,target,DAMAGE_FIRE,r1,current_pc);
|
|
ashes_loc = target;
|
|
break;
|
|
case eSpell::FIRESTORM:
|
|
add_missile(target,2,1,0,0);
|
|
store_sound = 11;
|
|
//do_missile_anim(100,univ.party[current_pc].combat_pos,11);
|
|
r1 = min(12,1 + (level * 2) / 3 + bonus) + 2;
|
|
if (r1 > 20)
|
|
r1 = (r1 * 8) / 10;
|
|
place_spell_pattern(rad2,target,DAMAGE_FIRE,r1,current_pc);
|
|
ashes_loc = target;
|
|
break;
|
|
case eSpell::KILL:
|
|
add_missile(target,9,1,0,0);
|
|
do_missile_anim(100,univ.party[current_pc].combat_pos,11);
|
|
r1 = get_ran(3,0,10) + univ.party[current_pc].level * 2;
|
|
hit_space(target,40 + r1,DAMAGE_MAGIC,1,0);
|
|
break;
|
|
case eSpell::ARROWS_DEATH:
|
|
add_missile(target,9,1,0,0);
|
|
store_sound = 11;
|
|
r1 = get_ran(3,0,10) + univ.party[current_pc].level + 3 * bonus;
|
|
boom_type[i] = DAMAGE_MAGIC;
|
|
boom_dam[i] = r1;
|
|
//hit_space(target,40 + r1,3,1,0);
|
|
break;
|
|
// summoning spells
|
|
case eSpell::SIMULACRUM: case eSpell::SUMMON_BEAST: case eSpell::SUMMON_RAT:
|
|
case eSpell::SUMMON_WEAK: case eSpell::SUMMON: case eSpell::SUMMON_MAJOR:
|
|
case eSpell::DEMON: case eSpell::SUMMON_SPIRIT: case eSpell::STICKS_TO_SNAKES:
|
|
case eSpell::SUMMON_HOST: case eSpell::SUMMON_GUARDIAN:
|
|
add_missile(target,8,1,0,0);
|
|
do_missile_anim(50,univ.party[current_pc].combat_pos,61);
|
|
switch (spell_being_cast) {
|
|
case eSpell::SIMULACRUM:
|
|
r2 = get_ran(3,1,4) + stat_adj(current_pc,eSkill::INTELLIGENCE);
|
|
if (summon_monster(store_sum_monst,target,r2,2) == false)
|
|
add_string_to_buf(" Summon failed.");
|
|
break;
|
|
case eSpell::SUMMON_BEAST:
|
|
r2 = get_ran(3,1,4) + stat_adj(current_pc,eSkill::INTELLIGENCE);
|
|
if ((summon == 0) || (!summon_monster(summon,target,r2,2)))
|
|
add_string_to_buf(" Summon failed.");
|
|
break;
|
|
case eSpell::SUMMON_WEAK:
|
|
r2 = get_ran(4,1,4) + stat_adj(current_pc,eSkill::INTELLIGENCE);
|
|
if ((summon == 0) || (!summon_monster(summon,target,r2,2)))
|
|
add_string_to_buf(" Summon failed.");
|
|
break;
|
|
case eSpell::SUMMON:
|
|
r2 = get_ran(5,1,4) + stat_adj(current_pc,eSkill::INTELLIGENCE);
|
|
if ((summon == 0) || (!summon_monster(summon,target,r2,2)))
|
|
add_string_to_buf(" Summon failed.");
|
|
break;
|
|
case eSpell::SUMMON_MAJOR:
|
|
r2 = get_ran(7,1,4) + stat_adj(current_pc,eSkill::INTELLIGENCE);
|
|
if ((summon == 0) || (!summon_monster(summon,target,r2,2)))
|
|
add_string_to_buf(" Summon failed.");
|
|
break;
|
|
case eSpell::DEMON:
|
|
r2 = get_ran(5,1,4) + stat_adj(current_pc,eSkill::INTELLIGENCE);
|
|
if (!summon_monster(85,target,r2,2))
|
|
add_string_to_buf(" Summon failed.");
|
|
break;
|
|
case eSpell::SUMMON_RAT:
|
|
r1 = get_ran(3,1,4) + stat_adj(current_pc,eSkill::INTELLIGENCE);
|
|
if (!summon_monster(80,target,r1,2))
|
|
add_string_to_buf(" Summon failed.");
|
|
break;
|
|
|
|
case eSpell::SUMMON_SPIRIT:
|
|
r2 = get_ran(2,1,5) + stat_adj(current_pc,eSkill::INTELLIGENCE);
|
|
if (summon_monster(125,target,r2,2) == false)
|
|
add_string_to_buf(" Summon failed.");
|
|
break;
|
|
case eSpell::STICKS_TO_SNAKES:
|
|
r1 = get_ran(1,0,7);
|
|
r2 = get_ran(2,1,5) + stat_adj(current_pc,eSkill::INTELLIGENCE);
|
|
if (summon_monster((r1 == 1) ? 100 : 99,target,r2,2) == false)
|
|
add_string_to_buf(" Summon failed.");
|
|
break;
|
|
case eSpell::SUMMON_HOST: // host
|
|
r2 = get_ran(2,1,4) + stat_adj(current_pc,eSkill::INTELLIGENCE);
|
|
if (summon_monster((i == 0) ? 126 : 125,target,r2,2) == false)
|
|
add_string_to_buf(" Summon failed.");
|
|
break;
|
|
case eSpell::SUMMON_GUARDIAN: // guardian
|
|
r2 = get_ran(6,1,4) + stat_adj(current_pc,eSkill::INTELLIGENCE);
|
|
if (summon_monster(122,target,r2,2) == false)
|
|
add_string_to_buf(" Summon failed.");
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
targ_num = monst_there(target);
|
|
if (targ_num > univ.town->max_monst())
|
|
add_string_to_buf(" Nobody there ");
|
|
else {
|
|
cur_monst = &univ.town.monst[targ_num];
|
|
cCreature* cur_monst = &univ.town.monst[targ_num];
|
|
if(cur_monst->attitude % 2 != 1 && spell_being_cast != eSpell::SCRY_MONSTER && spell_being_cast != eSpell::CAPTURE_SOUL)
|
|
make_town_hostile();
|
|
switch (spell_being_cast) {
|
|
case eSpell::ACID_SPRAY:
|
|
store_m_type = 0;
|
|
acid_monst(cur_monst,level);
|
|
store_sound = 24;
|
|
break;
|
|
case eSpell::PARALYZE_BEAM:
|
|
store_m_type = 9;
|
|
charm_monst(cur_monst,0,eStatus::PARALYZED,500);
|
|
store_sound = 24;
|
|
break;
|
|
|
|
case eSpell::SCRY_MONSTER:
|
|
store_m_type = -1;
|
|
play_sound(52);
|
|
univ.party.m_noted[cur_monst->number] = true;
|
|
adjust_monst_menu();
|
|
display_monst(0,cur_monst,0);
|
|
store_sound = 25;
|
|
break;
|
|
case eSpell::CAPTURE_SOUL:
|
|
store_m_type = 15;
|
|
record_monst(cur_monst);
|
|
store_sound = 25;
|
|
break;
|
|
|
|
case eSpell::MINDDUEL:
|
|
store_m_type = -1;
|
|
if ((cur_monst->mu == 0) && (cur_monst->cl == 0))
|
|
add_string_to_buf(" Can't duel: no magic.");
|
|
else {
|
|
item = pc_has_abil(current_pc,eItemAbil::SMOKY_CRYSTAL);
|
|
if (item >= 24)
|
|
add_string_to_buf(" You need a smoky crystal. ");
|
|
else {
|
|
remove_charge(current_pc,item);
|
|
do_mindduel(current_pc,cur_monst);
|
|
}
|
|
}
|
|
store_sound = 24;
|
|
break;
|
|
|
|
case eSpell::CHARM_FOE:
|
|
store_m_type = 14;
|
|
charm_monst(cur_monst,-1 * (bonus + univ.party[current_pc].level / 8),eStatus::CHARM,0);
|
|
store_sound = 24;
|
|
break;
|
|
|
|
case eSpell::DISEASE:
|
|
store_m_type = 0;
|
|
r1 = get_ran(1,0,1);
|
|
disease_monst(cur_monst,2 + r1 + bonus);
|
|
store_sound = 24;
|
|
break;
|
|
|
|
case eSpell::STRENGTHEN_TARGET:
|
|
store_m_type = 14;
|
|
cur_monst->health += 20;
|
|
store_sound = 55;
|
|
break;
|
|
|
|
case eSpell::DUMBFOUND:
|
|
store_m_type = 14;
|
|
dumbfound_monst(cur_monst,1 + bonus / 3);
|
|
store_sound = 53;
|
|
break;
|
|
|
|
case eSpell::SCARE:
|
|
store_m_type = 11;
|
|
scare_monst(cur_monst,get_ran(2 + bonus,1,6));
|
|
store_sound = 54;
|
|
break;
|
|
case eSpell::FEAR:
|
|
store_m_type = 11;
|
|
scare_monst(cur_monst,get_ran(min(20,univ.party[current_pc].level / 2 + bonus),1,8));
|
|
store_sound = 54;
|
|
break;
|
|
|
|
case eSpell::SLOW:
|
|
store_m_type = 11;
|
|
r1 = get_ran(1,0,1);
|
|
slow_monst(cur_monst,2 + r1 + bonus);
|
|
store_sound = 25;
|
|
break;
|
|
|
|
case eSpell::POISON_MINOR: case eSpell::ARROWS_VENOM:
|
|
store_m_type = (spell_being_cast == eSpell::ARROWS_VENOM) ? 4 : 11;
|
|
poison_monst(cur_monst,2 + bonus / 2);
|
|
store_sound = 55;
|
|
break;
|
|
case eSpell::PARALYZE:
|
|
store_m_type = 9;
|
|
charm_monst(cur_monst,-10,eStatus::PARALYZED,1000);
|
|
store_sound = 25;
|
|
break;
|
|
case eSpell::POISON:
|
|
store_m_type = 11;
|
|
poison_monst(cur_monst,4 + bonus / 2);
|
|
store_sound = 55;
|
|
break;
|
|
case eSpell::POISON_MAJOR:
|
|
store_m_type = 11;
|
|
poison_monst(cur_monst,8 + bonus / 2);
|
|
store_sound = 55;
|
|
break;
|
|
|
|
case eSpell::STUMBLE:
|
|
store_m_type = 8;
|
|
curse_monst(cur_monst,4 + bonus);
|
|
store_sound = 24;
|
|
break;
|
|
|
|
case eSpell::CURSE:
|
|
store_m_type = 8;
|
|
curse_monst(cur_monst,2 + bonus);
|
|
store_sound = 24;
|
|
break;
|
|
|
|
case eSpell::HOLY_SCOURGE:
|
|
store_m_type = 8;
|
|
curse_monst(cur_monst,2 + univ.party[current_pc].level / 2);
|
|
store_sound = 24;
|
|
break;
|
|
|
|
case eSpell::TURN_UNDEAD: case eSpell::DISPEL_UNDEAD:
|
|
if (cur_monst->m_type != eRace::UNDEAD) {
|
|
add_string_to_buf(" Not undead. ");
|
|
store_m_type = -1;
|
|
break;
|
|
}
|
|
store_m_type = 8;
|
|
r1 = get_ran(1,0,90);
|
|
if (r1 > hit_chance[minmax(0,19,bonus * 2 + level * 4 - (cur_monst->level / 2) + 3)])
|
|
add_string_to_buf(" Monster resisted. ");
|
|
else {
|
|
r1 = get_ran((spell_being_cast == eSpell::TURN_UNDEAD) ? 2 : 6, 1, 14);
|
|
|
|
hit_space(cur_monst->cur_loc,r1,DAMAGE_UNBLOCKABLE,0,current_pc);
|
|
}
|
|
store_sound = 24;
|
|
break;
|
|
|
|
case eSpell::RAVAGE_SPIRIT:
|
|
if (cur_monst->m_type != eRace::DEMON) {
|
|
add_string_to_buf(" Not a demon. ");
|
|
store_m_type = -1;
|
|
break;
|
|
}
|
|
r1 = get_ran(1,1,100);
|
|
if (r1 > hit_chance[minmax(0,19,level * 4 - cur_monst->level + 10)])
|
|
add_string_to_buf(" Demon resisted. ");
|
|
else {
|
|
r1 = get_ran(8 + bonus * 2, 1, 11);
|
|
//if (PSD[4][0] == 3) // anama
|
|
// r1 += 25;
|
|
//play_sound(53);
|
|
hit_space(cur_monst->cur_loc,r1,DAMAGE_UNBLOCKABLE,0,current_pc);
|
|
}
|
|
store_sound = 24;
|
|
break;
|
|
}
|
|
if (store_m_type >= 0)
|
|
add_missile(target,store_m_type,1,
|
|
14 * (cur_monst->x_width - 1),18 * (cur_monst->y_width - 1));
|
|
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
do_missile_anim((num_targets > 1) ? 35 : 60,univ.party[current_pc].combat_pos,store_sound);
|
|
|
|
// process mass damage
|
|
for (i = 0; i < 8; i++)
|
|
if (boom_dam[i] > 0)
|
|
hit_space(boom_targ[i],boom_dam[i],boom_type[i],1,0);
|
|
|
|
if (ashes_loc.x > 0)
|
|
univ.town.set_ash(ashes_loc.x,ashes_loc.y,true);
|
|
|
|
do_explosion_anim(5,0);
|
|
|
|
end_missile_anim();
|
|
|
|
handle_marked_damage();
|
|
combat_posing_monster = current_working_monster = -1;
|
|
|
|
print_buf();
|
|
}
|
|
|
|
void handle_marked_damage()
|
|
{
|
|
short i;
|
|
|
|
for (i = 0; i < 6; i++)
|
|
if (univ.party[i].marked_damage > 0)
|
|
{
|
|
// TODO: Perhaps there should be a way of determining the correct race here?
|
|
damage_pc(i,univ.party[i].marked_damage,DAMAGE_MARKED,eRace::UNKNOWN,0);
|
|
univ.party[i].marked_damage = 0;
|
|
}
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if (monst_marked_damage[i] > 0)
|
|
{
|
|
damage_monst(i, current_pc, monst_marked_damage[i], 0, DAMAGE_MARKED,0); // was 9 rather than 10; probably a mistake
|
|
|
|
monst_marked_damage[i] = 0;
|
|
}
|
|
}
|
|
|
|
void load_missile() ////
|
|
{
|
|
short i,bow = 24,arrow = 24,thrown = 24,crossbow = 24,bolts = 24,no_ammo = 24;
|
|
|
|
for (i = 0; i < 24; i++) {
|
|
if ((univ.party[current_pc].equip[i] == true) &&
|
|
(univ.party[current_pc].items[i].variety == eItemType::THROWN_MISSILE))
|
|
thrown = i;
|
|
if ((univ.party[current_pc].equip[i] == true) &&
|
|
(univ.party[current_pc].items[i].variety == eItemType::BOW))
|
|
bow = i;
|
|
if ((univ.party[current_pc].equip[i] == true) &&
|
|
(univ.party[current_pc].items[i].variety == eItemType::ARROW))
|
|
arrow = i;
|
|
if ((univ.party[current_pc].equip[i] == true) &&
|
|
(univ.party[current_pc].items[i].variety == eItemType::CROSSBOW))
|
|
crossbow = i;
|
|
if ((univ.party[current_pc].equip[i] == true) &&
|
|
(univ.party[current_pc].items[i].variety == eItemType::BOLTS))
|
|
bolts = i;
|
|
if ((univ.party[current_pc].equip[i] == true) &&
|
|
(univ.party[current_pc].items[i].variety == eItemType::MISSILE_NO_AMMO))
|
|
no_ammo = i;
|
|
}
|
|
|
|
if (thrown < 24) {
|
|
ammo_inv_slot = thrown;
|
|
add_string_to_buf("Throw: Select a target. ");
|
|
add_string_to_buf(" (Hit 's' to cancel.)");
|
|
overall_mode = MODE_THROWING;
|
|
current_spell_range = 8;
|
|
current_pat = single;
|
|
}
|
|
else if (((bolts < 24) && (bow < 24)) || ((arrow < 24) && (crossbow < 24))) {
|
|
add_string_to_buf("Fire: Wrong ammunition. ");
|
|
}
|
|
else if ((arrow == 24) && (bow < 24)) {
|
|
add_string_to_buf("Fire: Equip some arrows. ");
|
|
}
|
|
else if(crossbow == 24 && bolts < 24) {
|
|
add_string_to_buf("Fire: Equip some bolts. ");
|
|
}
|
|
else if ((arrow < 24) && (bow < 24)) {
|
|
missile_inv_slot = bow;
|
|
ammo_inv_slot = arrow;
|
|
overall_mode = MODE_FIRING;
|
|
add_string_to_buf("Fire: Select a target. ");
|
|
add_string_to_buf(" (Hit 's' to cancel.)");
|
|
current_spell_range = 12;
|
|
if(univ.party[current_pc].items[arrow].ability == eItemAbil::MISSILE_EXPLODING)
|
|
current_pat = rad2;
|
|
else
|
|
current_pat = single;
|
|
}
|
|
else if ((bolts < 24) && (crossbow < 24)) {
|
|
missile_inv_slot = crossbow;
|
|
ammo_inv_slot = bolts;
|
|
overall_mode = MODE_FIRING;
|
|
add_string_to_buf("Fire: Select a target. ");
|
|
add_string_to_buf(" (Hit 's' to cancel.)");
|
|
current_spell_range = 12;
|
|
current_pat = single;
|
|
}
|
|
else if (no_ammo < 24) {
|
|
missile_inv_slot = no_ammo;
|
|
ammo_inv_slot = no_ammo;
|
|
overall_mode = MODE_FIRING;
|
|
add_string_to_buf("Fire: Select a target. ");
|
|
add_string_to_buf(" (Hit 's' to cancel.)");
|
|
current_spell_range = 12;
|
|
current_pat = single;
|
|
}
|
|
else add_string_to_buf("Fire: Equip a missile. ");
|
|
}
|
|
|
|
void fire_missile(location target) {
|
|
short r1, r2, skill, dam, dam_bonus, hit_bonus, range, targ_monst, spec_dam = 0,poison_amt = 0;
|
|
short skill_item,m_type = 0;
|
|
cCreature *cur_monst;
|
|
bool exploding = false;
|
|
missile_firer = current_pc;
|
|
|
|
skill = overall_mode == MODE_FIRING ? univ.party[missile_firer].skills[eSkill::ARCHERY] : univ.party[missile_firer].skills[eSkill::THROWN_MISSILES];
|
|
range = (overall_mode == MODE_FIRING) ? 12 : 8;
|
|
dam = univ.party[missile_firer].items[ammo_inv_slot].item_level;
|
|
dam_bonus = univ.party[missile_firer].items[ammo_inv_slot].bonus + minmax(-8,8,univ.party[missile_firer].status[eStatus::BLESS_CURSE]);
|
|
hit_bonus = (overall_mode == MODE_FIRING) ? univ.party[missile_firer].items[missile_inv_slot].bonus : 0;
|
|
hit_bonus += stat_adj(missile_firer,eSkill::DEXTERITY) - can_see_light(univ.party[missile_firer].combat_pos,target,sight_obscurity)
|
|
+ minmax(-8,8,univ.party[missile_firer].status[eStatus::BLESS_CURSE]);
|
|
if ((skill_item = pc_has_abil_equip(missile_firer,eItemAbil::ACCURACY)) < 24) {
|
|
hit_bonus += univ.party[missile_firer].items[skill_item].ability_strength / 2;
|
|
dam_bonus += univ.party[missile_firer].items[skill_item].ability_strength / 2;
|
|
}
|
|
|
|
// race adj.
|
|
// TODO: Should this apply to sliths as well? The bladbase suggests otherwise, but it has been changed from the original; maybe the sliths were originally considered to be reptiles.
|
|
if(univ.party[missile_firer].race == eRace::REPTILE)
|
|
hit_bonus += 2;
|
|
|
|
if (univ.party[missile_firer].items[ammo_inv_slot].ability == eItemAbil::MISSILE_EXPLODING)
|
|
exploding = true;
|
|
|
|
if (dist(univ.party[missile_firer].combat_pos,target) > range)
|
|
add_string_to_buf(" Out of range.");
|
|
else if (can_see_light(univ.party[missile_firer].combat_pos,target,sight_obscurity) >= 5)
|
|
add_string_to_buf(" Can't see target. ");
|
|
else {
|
|
// First, some missiles do special things
|
|
if (exploding) {
|
|
take_ap((overall_mode == MODE_FIRING) ? 3 : 2);
|
|
void_sanctuary(current_pc); // TODO: Is this right?
|
|
missile_firer = current_pc;
|
|
add_string_to_buf(" The arrow explodes! ");
|
|
if(PSD[SDF_GAME_SPEED] == 0)
|
|
pause(dist(univ.party[current_pc].combat_pos,target));
|
|
else
|
|
pause(dist(univ.party[current_pc].combat_pos,target)*5);
|
|
run_a_missile(univ.party[missile_firer].combat_pos,target,2,1,5,0,0,100);
|
|
start_missile_anim();
|
|
place_spell_pattern(rad2,target, DAMAGE_FIRE,univ.party[missile_firer].items[ammo_inv_slot].ability_strength * 2,missile_firer);
|
|
do_explosion_anim(5,0);
|
|
end_missile_anim();
|
|
handle_marked_damage();
|
|
} else {
|
|
combat_posing_monster = current_working_monster = missile_firer;
|
|
draw_terrain(2);
|
|
void_sanctuary(missile_firer);
|
|
//play_sound((overall_mode == MODE_FIRING) ? 12 : 14);
|
|
take_ap((overall_mode == MODE_FIRING) ? 3 : 2);
|
|
missile_firer = missile_firer;
|
|
r1 = get_ran(1,1,100) - 5 * hit_bonus - 10;
|
|
r1 += 5 * (univ.party[missile_firer].status[eStatus::WEBS] / 3);
|
|
r2 = get_ran(1,1,dam) + dam_bonus;
|
|
sprintf ((char *) create_line, "%s fires.",(char *) univ.party[missile_firer].name.c_str()); // debug
|
|
add_string_to_buf((char *) create_line);
|
|
|
|
if (overall_mode == MODE_THROWING) {
|
|
switch (univ.party[missile_firer].items[ammo_inv_slot].item_level) {
|
|
case 7:
|
|
m_type = 10;
|
|
break;
|
|
case 4:
|
|
m_type = 1;
|
|
break;
|
|
case 8:
|
|
m_type = 5;
|
|
break;
|
|
case 9:
|
|
m_type = 7;
|
|
break;
|
|
default:
|
|
m_type = 10;
|
|
break;
|
|
}
|
|
} else if (overall_mode == MODE_FIRING || overall_mode == MODE_FANCY_TARGET)
|
|
m_type = univ.party[missile_firer].items[ammo_inv_slot].magic ? 4 : 3;
|
|
run_a_missile(univ.party[missile_firer].combat_pos,target,m_type,1,(overall_mode == MODE_FIRING) ? 12 : 14,
|
|
0,0,100);
|
|
|
|
if (r1 > hit_chance[skill])
|
|
add_string_to_buf(" Missed.");
|
|
else if ((targ_monst = monst_there(target)) < univ.town->max_monst()) {
|
|
cur_monst = &univ.town.monst[targ_monst];
|
|
spec_dam = calc_spec_dam(univ.party[missile_firer].items[ammo_inv_slot].ability,
|
|
univ.party[missile_firer].items[ammo_inv_slot].ability_strength,cur_monst);
|
|
if(univ.party[missile_firer].items[ammo_inv_slot].ability == eItemAbil::MISSILE_HEAL_TARGET) {
|
|
ASB(" There is a flash of light.");
|
|
cur_monst->health += r2;
|
|
}
|
|
else damage_monst(targ_monst, missile_firer, r2, spec_dam, DAMAGE_WEAPON,13);
|
|
|
|
//if (univ.party[missile_firer].items[ammo_inv_slot].ability == 33)
|
|
// hit_space(cur_monst->m_loc,get_ran(3,1,6),1,1,1);
|
|
|
|
// poison
|
|
if(univ.party[missile_firer].status[eStatus::POISONED_WEAPON] > 0 && univ.party[missile_firer].weap_poisoned == ammo_inv_slot) {
|
|
poison_amt = univ.party[missile_firer].status[eStatus::POISONED_WEAPON];
|
|
if (pc_has_abil_equip(missile_firer,eItemAbil::POISON_AUGMENT) < 24)
|
|
poison_amt++;
|
|
poison_monst(cur_monst,poison_amt);
|
|
}
|
|
}
|
|
// else if((targ_monst = pc_there(target)) < 6 && univ.party[current_pc].items[ammo_inv_slot].ability == 176){
|
|
// ASB(" There is a flash of light.");
|
|
// heal_pc(targ_monst,r2);
|
|
// }
|
|
else hit_space(target,r2,DAMAGE_WEAPON,1,0);
|
|
|
|
}
|
|
|
|
if (univ.party[missile_firer].items[ammo_inv_slot].variety != eItemType::MISSILE_NO_AMMO) {
|
|
if(univ.party[missile_firer].items[ammo_inv_slot].ability != eItemAbil::MISSILE_RETURNING)
|
|
univ.party[missile_firer].items[ammo_inv_slot].charges--;
|
|
else univ.party[missile_firer].items[ammo_inv_slot].charges = 1;
|
|
if(pc_has_abil_equip(missile_firer,eItemAbil::DRAIN_MISSILES) < 24
|
|
&& univ.party[missile_firer].items[ammo_inv_slot].ability != eItemAbil::MISSILE_RETURNING)
|
|
univ.party[missile_firer].items[ammo_inv_slot].charges--;
|
|
if (univ.party[missile_firer].items[ammo_inv_slot].charges <= 0)
|
|
take_item(missile_firer,ammo_inv_slot);
|
|
}
|
|
}
|
|
|
|
if(!exploding){
|
|
combat_posing_monster = current_working_monster = -1;
|
|
move_to_zero(univ.party[missile_firer].status[eStatus::POISONED_WEAPON]);
|
|
}
|
|
print_buf();
|
|
}
|
|
|
|
// Select next active PC and, if necessary, run monsters
|
|
// if monsters go or PC switches (i.e. if need redraw above), return true
|
|
bool combat_next_step()
|
|
{
|
|
bool to_return = false;
|
|
short store_pc; // will print current pc name is active pc changes
|
|
|
|
store_pc = current_pc;
|
|
while (pick_next_pc() == true) {
|
|
combat_run_monst();
|
|
set_pc_moves();
|
|
if((combat_active_pc < 6) && (univ.party[combat_active_pc].ap == 0)){
|
|
combat_active_pc = 6;
|
|
ASB("The active character is unable to act!");
|
|
ASB("The whole party is now active.");
|
|
}
|
|
to_return = true;
|
|
// Safety valve
|
|
if (party_toast() == true)
|
|
return true;
|
|
}
|
|
pick_next_pc();
|
|
if (current_pc != store_pc)
|
|
to_return = true;
|
|
center = univ.party[current_pc].combat_pos;
|
|
//if (ensure_redraw == true)
|
|
// draw_terrain(0);
|
|
|
|
adjust_spell_menus();
|
|
|
|
// In case running monsters affected active PC...
|
|
/* if (univ.party[current_pc].status[3] < 0)
|
|
this_pc_hasted = false;
|
|
if ((univ.party[current_pc].main_status != 1) ||
|
|
((univ.party[current_pc].status[3] < 0) && (univ.party.age % 2 == 0)))
|
|
pick_next_pc();
|
|
center = univ.party[current_pc].combat_pos; */
|
|
|
|
if ((combat_active_pc == 6) && (current_pc != store_pc)) {
|
|
sprintf(create_line, "Active: %s (#%d, %d ap.) ",
|
|
univ.party[current_pc].name.c_str(),current_pc + 1,univ.party[current_pc].ap);
|
|
add_string_to_buf(create_line);
|
|
print_buf();
|
|
}
|
|
if ((current_pc != store_pc) || (to_return == true)) {
|
|
put_pc_screen();
|
|
set_stat_window(current_pc);
|
|
}
|
|
return to_return;
|
|
}
|
|
|
|
// Find next active PC, return true is monsters need running, and run monsters is slow spells
|
|
// active
|
|
bool pick_next_pc()
|
|
{
|
|
bool store = false;
|
|
|
|
if (current_pc == 6)
|
|
current_pc = 0;
|
|
|
|
// If current pc isn't active, fry its moves
|
|
if ((combat_active_pc < 6) && (combat_active_pc != current_pc))
|
|
univ.party[current_pc].ap = 0;
|
|
|
|
// Find next PC with moves
|
|
while ((univ.party[current_pc].ap <= 0) && (current_pc < 6)) {
|
|
current_pc++;
|
|
if ((combat_active_pc < 6) && (combat_active_pc != current_pc))
|
|
univ.party[current_pc].ap = 0;
|
|
}
|
|
|
|
// If run out of PC's, return to start and try again
|
|
if (current_pc == 6) {
|
|
current_pc = 0;
|
|
while ((univ.party[current_pc].ap <= 0) && (current_pc < 6)) {
|
|
current_pc++;
|
|
if ((combat_active_pc < 6) && (combat_active_pc != current_pc))
|
|
univ.party[current_pc].ap = 0;
|
|
}
|
|
if (current_pc == 6) {
|
|
store = true;
|
|
current_pc = 0;
|
|
}
|
|
}
|
|
|
|
return store;
|
|
}
|
|
|
|
|
|
void combat_run_monst()
|
|
{
|
|
short i,item,item_level;
|
|
bool update_stat = false;
|
|
|
|
|
|
monsters_going = true;
|
|
do_monster_turn();
|
|
monsters_going = false;
|
|
|
|
process_fields();
|
|
move_to_zero(univ.party.light_level);
|
|
if ((which_combat_type == 1) && (univ.town->lighting_type == 2))
|
|
univ.party.light_level = max (0,univ.party.light_level - 9);
|
|
if (univ.town->lighting_type == 3)
|
|
univ.party.light_level = 0;
|
|
|
|
move_to_zero(PSD[SDF_PARTY_DETECT_LIFE]);
|
|
move_to_zero(PSD[SDF_PARTY_FIREWALK]);
|
|
|
|
// decrease monster present counter
|
|
move_to_zero(PSD[SDF_HOSTILES_PRESENT]);
|
|
|
|
dump_gold(1);
|
|
|
|
univ.party.age++;
|
|
if (univ.party.age % 4 == 0)
|
|
for (i = 0; i < 6; i++) {
|
|
if(univ.party[i].status[eStatus::BLESS_CURSE] != 0 || univ.party[i].status[eStatus::HASTE_SLOW] != 0)
|
|
update_stat = true;
|
|
move_to_zero(univ.party[i].status[eStatus::BLESS_CURSE]);
|
|
move_to_zero(univ.party[i].status[eStatus::HASTE_SLOW]);
|
|
move_to_zero(PSD[SDF_PARTY_STEALTHY]);
|
|
if ((item = pc_has_abil_equip(i,eItemAbil::REGENERATE)) < 24) {
|
|
update_stat = true;
|
|
heal_pc(i,get_ran(1,0,univ.party[i].items[item].item_level + 1));
|
|
}
|
|
}
|
|
for (i = 0; i < 6; i++)
|
|
if(univ.party[i].main_status == eMainStatus::ALIVE) {
|
|
if(univ.party[i].status[eStatus::INVULNERABLE] != 0 || univ.party[i].status[eStatus::MAGIC_RESISTANCE] != 0
|
|
|| univ.party[i].status[eStatus::INVISIBLE] != 0 || univ.party[i].status[eStatus::MARTYRS_SHIELD] != 0
|
|
|| univ.party[i].status[eStatus::ASLEEP] != 0 || univ.party[i].status[eStatus::PARALYZED] != 0)
|
|
update_stat = true;
|
|
|
|
move_to_zero(univ.party[i].status[eStatus::INVULNERABLE]);
|
|
move_to_zero(univ.party[i].status[eStatus::MAGIC_RESISTANCE]);
|
|
move_to_zero(univ.party[i].status[eStatus::INVISIBLE]);
|
|
move_to_zero(univ.party[i].status[eStatus::MARTYRS_SHIELD]);
|
|
move_to_zero(univ.party[i].status[eStatus::ASLEEP]);
|
|
move_to_zero(univ.party[i].status[eStatus::PARALYZED]);
|
|
|
|
// Do special items
|
|
if (((item_level = get_prot_level(i,eItemAbil::OCCASIONAL_HASTE)) > 0)
|
|
&& (get_ran(1,0,10) == 5)) {
|
|
update_stat = true;
|
|
univ.party[i].status[eStatus::HASTE_SLOW] += item_level / 2;
|
|
add_string_to_buf("An item hastes you!");
|
|
}
|
|
if ((item_level = get_prot_level(i,eItemAbil::OCCASIONAL_BLESS)) > 0) {
|
|
if (get_ran(1,0,10) == 5) {
|
|
update_stat = true;
|
|
univ.party[i].status[eStatus::BLESS_CURSE] += item_level / 2;
|
|
add_string_to_buf("An item blesses you!");
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
special_increase_age();
|
|
push_things();
|
|
|
|
if (univ.party.age % 2 == 0)
|
|
do_poison();
|
|
if (univ.party.age % 3 == 0)
|
|
handle_disease();
|
|
handle_acid();
|
|
|
|
if (update_stat == true)
|
|
put_pc_screen();
|
|
|
|
}
|
|
|
|
void do_monster_turn()
|
|
{
|
|
bool acted_yet, had_monst = false,printed_poison = false,printed_disease = false,printed_acid = false;
|
|
bool redraw_not_yet_done = true;
|
|
bool special_called = false;
|
|
location targ_space,move_targ,l;
|
|
short i,j,k,num_monst, target,r1,move_target;
|
|
cCreature *cur_monst;
|
|
bool pc_adj[6];
|
|
short abil_range[40] = {
|
|
0,6,8,8,10, 10,10,8,6,8, 6,0,0,0,6, 0,0,0,0,4, 10,0,0,6,0,
|
|
0,0,0,0,0, 0,0,8,6,9, 0,0,0,0,0};
|
|
short abil_odds[40] = {
|
|
0,5,7,6,6, 5,5,6,6,6, 6,0,0,0,4, 0,0,0,0,4, 8,0,0,7,0,
|
|
0,0,0,0,0, 0,0,7,5,6, 0,0,0,0,0};
|
|
|
|
monsters_going = true; // This affects how graphics are drawn.
|
|
|
|
num_monst = univ.town->max_monst();
|
|
if (overall_mode < MODE_COMBAT)
|
|
which_combat_type = 1;
|
|
|
|
for (i = 0; i < num_monst; i++) { // Give monsters ap's, check activity
|
|
|
|
cur_monst = &univ.town.monst[i];
|
|
|
|
// See if hostile monster notices party, during combat
|
|
if ((cur_monst->active == 1) && (cur_monst->attitude % 2 == 1) && (overall_mode == MODE_COMBAT)) {
|
|
r1 = get_ran(1,1,100); // Check if see PCs first
|
|
r1 += (PSD[SDF_PARTY_STEALTHY] > 0) ? 45 : 0;
|
|
r1 += can_see_light(cur_monst->cur_loc,closest_pc_loc(cur_monst->cur_loc),sight_obscurity) * 10;
|
|
if (r1 < 50)
|
|
cur_monst->active = 2;
|
|
|
|
for (j = 0; j < univ.town->max_monst(); j++)
|
|
if (monst_near(j,cur_monst->cur_loc,5,1) == true) {
|
|
cur_monst->active = 2;
|
|
}
|
|
}
|
|
if ((cur_monst->active == 1) && (cur_monst->attitude % 2 == 1)) {
|
|
// Now it looks for PC-friendly monsters
|
|
// dist check is for efficiency
|
|
for (j = 0; j < univ.town->max_monst(); j++)
|
|
if ((univ.town.monst[j].active > 0) &&
|
|
(univ.town.monst[j].attitude % 2 != 1) &&
|
|
(dist(cur_monst->cur_loc,univ.town.monst[j].cur_loc) <= 6) &&
|
|
(can_see_light(cur_monst->cur_loc,univ.town.monst[j].cur_loc,sight_obscurity) < 5))
|
|
cur_monst->active = 2;
|
|
}
|
|
|
|
// See if friendly, fighting monster see hostile monster. If so, make mobile
|
|
// dist check is for efficiency
|
|
if ((cur_monst->active == 1) && (cur_monst->attitude == 2)) {
|
|
for (j = 0; j < univ.town->max_monst(); j++)
|
|
if ((univ.town.monst[j].active > 0) && (univ.town.monst[j].attitude % 2 == 1) &&
|
|
(dist(cur_monst->cur_loc,univ.town.monst[j].cur_loc) <= 6)
|
|
&& (can_see_light(cur_monst->cur_loc,univ.town.monst[j].cur_loc,sight_obscurity) < 5)) {
|
|
cur_monst->active = 2;
|
|
cur_monst->mobility = 1;
|
|
}
|
|
}
|
|
// End of seeing if monsters see others
|
|
|
|
cur_monst->ap = 0;
|
|
if (cur_monst->active == 2) { // Begin action loop for angry, active monsters
|
|
// First note that hostile monsters are around.
|
|
if (cur_monst->attitude % 2 == 1)
|
|
PSD[SDF_HOSTILES_PRESENT] = 30;
|
|
|
|
// Give monster its action points
|
|
cur_monst->ap = cur_monst->speed;
|
|
if (is_town())
|
|
cur_monst->ap = max(1,cur_monst->ap / 3);
|
|
if (univ.party.age % 2 == 0)
|
|
if(cur_monst->status[eStatus::HASTE_SLOW] < 0)
|
|
cur_monst->ap = 0;
|
|
if (cur_monst->ap > 0) { // adjust for webs
|
|
cur_monst->ap = max(0,cur_monst->ap - cur_monst->status[eStatus::WEBS] / 2);
|
|
if (cur_monst->ap == 0)
|
|
cur_monst->status[eStatus::WEBS] = max(0,cur_monst->status[eStatus::WEBS] - 2);
|
|
}
|
|
if(cur_monst->status[eStatus::HASTE_SLOW] > 0)
|
|
cur_monst->ap *= 2;
|
|
}
|
|
if(cur_monst->status[eStatus::ASLEEP] > 0 || cur_monst->status[eStatus::PARALYZED] > 0)
|
|
cur_monst->ap = 0;
|
|
if (in_scen_debug == true)
|
|
cur_monst->ap = 0;
|
|
center_on_monst = false;
|
|
|
|
// Now take care of summoned monsters
|
|
if (cur_monst->active > 0) {
|
|
if ((cur_monst->summoned % 100) == 1) {
|
|
cur_monst->active = 0;
|
|
cur_monst->ap = 0;
|
|
monst_spell_note(cur_monst->number,17);
|
|
}
|
|
move_to_zero(cur_monst->summoned);
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0; i < num_monst; i++) { // Begin main monster loop, do monster actions
|
|
// If party dead, no point
|
|
if (party_toast() == true)
|
|
return;
|
|
|
|
futzing = 0; // assume monster is fresh
|
|
|
|
cur_monst = &univ.town.monst[i];
|
|
|
|
|
|
for (j = 0; j < 6; j++)
|
|
if(univ.party[j].main_status == eMainStatus::ALIVE && monst_adjacent(univ.party[j].combat_pos,i))
|
|
pc_adj[j] = true;
|
|
else pc_adj[j] = false;
|
|
|
|
|
|
|
|
while ((cur_monst->ap > 0) && (cur_monst->active > 0)) { // Spend each action point
|
|
|
|
if (is_combat()) { // Pick target. If in town, target already picked
|
|
// in do_monsters
|
|
target = monst_pick_target(i);
|
|
target = switch_target_to_adjacent(i,target);
|
|
if (target < 6)
|
|
targ_space = univ.party[target].combat_pos;
|
|
else if (target != 6)
|
|
targ_space = univ.town.monst[target - 100].cur_loc;
|
|
univ.town.monst[i].target = target;
|
|
}
|
|
else {
|
|
if (univ.town.monst[i].target < 6)
|
|
targ_space = univ.town.p_loc;
|
|
else if (univ.town.monst[i].target != 6)
|
|
targ_space = univ.town.monst[univ.town.monst[i].target - 100].cur_loc;
|
|
}
|
|
|
|
// sprintf((char *)create_line," %d targets %d.",i,target);
|
|
// add_string_to_buf((char *) create_line);
|
|
|
|
if ((univ.town.monst[i].target < 0) || ((univ.town.monst[i].target > 5) &&
|
|
(univ.town.monst[i].target < 100)))
|
|
univ.town.monst[i].target = 6;
|
|
target = univ.town.monst[i].target;
|
|
|
|
// Now if in town and monster about to attack, do a redraw, so we see monster
|
|
// in right place
|
|
if ((target != 6) && (is_town() == true) && (redraw_not_yet_done == true)
|
|
&& (party_can_see_monst(i) == true)) {
|
|
draw_terrain(0);
|
|
redraw_not_yet_done = false;
|
|
}
|
|
|
|
// Draw w. monster in center, if can see
|
|
if ((cur_monst->ap > 0) && (is_combat() == true)
|
|
// First make sure it has a target and is close, if not, don't bother
|
|
&& (cur_monst->attitude > 0) && (cur_monst->picture_num > 0)
|
|
&& ((target != 6) || (cur_monst->attitude % 2 == 1))
|
|
&& (party_can_see_monst(i) == true) ) {
|
|
center_on_monst = true;
|
|
center = cur_monst->cur_loc;
|
|
draw_terrain(0);
|
|
pause((PSD[SDF_GAME_SPEED] == 3) ? 9 : PSD[SDF_GAME_SPEED]);
|
|
}
|
|
|
|
combat_posing_monster = current_working_monster = 100 + i;
|
|
|
|
acted_yet = false;
|
|
|
|
|
|
// Now the monster, if evil, looks at the situation and maybe picks a tactic.
|
|
// This only happens when there is >1 a.p. left, and tends to involve
|
|
// running to a nice position.
|
|
current_monst_tactic = 0;
|
|
if ((target != 6) && (cur_monst->ap > 1) && (futzing == 0)) {
|
|
l = closest_pc_loc(cur_monst->cur_loc);
|
|
if (((cur_monst->mu > 0) || (cur_monst->cl > 0)) &&
|
|
(dist(cur_monst->cur_loc,l) < 5) && (monst_adjacent(l,i) == false))
|
|
current_monst_tactic = 1; // this means flee
|
|
|
|
|
|
if ( (((cur_monst->spec_skill > 0) && (cur_monst->spec_skill < 4))
|
|
|| (cur_monst->spec_skill == 20)) && // Archer?
|
|
(dist(cur_monst->cur_loc,targ_space) < 6) &&
|
|
(monst_adjacent(targ_space,i) == false))
|
|
current_monst_tactic = 1; // this means flee
|
|
}
|
|
|
|
|
|
// flee
|
|
if ((univ.town.monst[i].target != 6) && (((cur_monst->morale <= 0)
|
|
&& cur_monst->spec_skill != 13 && cur_monst->m_type != eRace::UNDEAD)
|
|
|| (current_monst_tactic == 1))) {
|
|
if (cur_monst->morale < 0)
|
|
cur_monst->morale++;
|
|
if (cur_monst->health > 50)
|
|
cur_monst->morale++;
|
|
r1 = get_ran(1,1,6);
|
|
if (r1 == 3)
|
|
cur_monst->morale++;
|
|
short targ = univ.town.monst[i].target;
|
|
if(targ < 6 && univ.party[targ].main_status == eMainStatus::ALIVE && cur_monst->mobility == 1) {
|
|
acted_yet = flee_party (i,cur_monst->cur_loc,targ_space);
|
|
if (acted_yet == true) take_m_ap(1,cur_monst);
|
|
}
|
|
}
|
|
if ((target != 6) && (cur_monst->attitude > 0)
|
|
&& (monst_can_see(i,targ_space) == true)
|
|
&& (can_see_monst(targ_space,i) == true)) { // Begin spec. attacks
|
|
|
|
// sprintf((char *)create_line,"%d: %d %d %d",i,cur_monst->breath,cur_monst->mu,cur_monst->cl);
|
|
// add_string_to_buf((char *)create_line);
|
|
|
|
// Breathe (fire)
|
|
if ( (cur_monst->breath > 0)
|
|
&& (get_ran(1,1,8) < 4) && (acted_yet == false)) {
|
|
//print_nums(cur_monst->breath,cur_monst->breath_type,dist(cur_monst->m_loc,targ_space) );
|
|
if ((target != 6)
|
|
&& (dist(cur_monst->cur_loc,targ_space) <= 8)) {
|
|
acted_yet = monst_breathe(cur_monst,targ_space,cur_monst->breath_type);
|
|
had_monst = true;
|
|
acted_yet = true;
|
|
take_m_ap(4,cur_monst);
|
|
}
|
|
}
|
|
// Mage spell
|
|
if ((cur_monst->mu > 0) && (get_ran(1,1,10) < ((cur_monst->cl > 0) ? 6 : 9) )
|
|
&& (acted_yet == false)) {
|
|
if (((monst_adjacent(targ_space,i) == false) || (get_ran(1,0,2) < 2) || (cur_monst->number >= 160)
|
|
|| (cur_monst->level > 9))
|
|
&& (dist(cur_monst->cur_loc,targ_space) <= 10)) {
|
|
acted_yet = monst_cast_mage(cur_monst,target);
|
|
had_monst = true;
|
|
acted_yet = true;
|
|
take_m_ap(5,cur_monst);
|
|
}
|
|
}
|
|
// Priest spell
|
|
if ((cur_monst->cl > 0) && (get_ran(1,1,8) < 7) && (acted_yet == false)) {
|
|
if (((monst_adjacent(targ_space,i) == false) || (get_ran(1,0,2) < 2) || (cur_monst->level > 9))
|
|
&& (dist(cur_monst->cur_loc,targ_space) <= 10)) {
|
|
acted_yet = monst_cast_priest(cur_monst,target);
|
|
had_monst = true;
|
|
acted_yet = true;
|
|
take_m_ap(4,cur_monst);
|
|
}
|
|
}
|
|
|
|
// Missile
|
|
if ((abil_range[cur_monst->spec_skill] > 0) // breathing gas short range
|
|
&& (get_ran(1,1,8) < abil_odds[cur_monst->spec_skill]) && (acted_yet == false)) {
|
|
// Don't fire when adjacent, unless non-gaze magical attack
|
|
if (((monst_adjacent(targ_space,i) == false) ||
|
|
((cur_monst->spec_skill > 7) && (cur_monst->spec_skill != 20)
|
|
&& (cur_monst->spec_skill != 33)))
|
|
// missile range
|
|
&& (dist(cur_monst->cur_loc,targ_space) <= abil_range[cur_monst->spec_skill])) {
|
|
print_monst_name(cur_monst->number);
|
|
monst_fire_missile(i/*,cur_monst->skill*/,cur_monst->status[eStatus::BLESS_CURSE],
|
|
cur_monst->spec_skill,cur_monst->cur_loc,target);
|
|
|
|
// Vapors don't count as action
|
|
if ((cur_monst->spec_skill == 11) || (cur_monst->spec_skill == 7) ||
|
|
(cur_monst->spec_skill == 20))
|
|
take_m_ap(2,cur_monst);
|
|
else if (cur_monst->spec_skill == 10)
|
|
take_m_ap(1,cur_monst);
|
|
else take_m_ap(3,cur_monst);
|
|
had_monst = true;
|
|
acted_yet = true;
|
|
}
|
|
}
|
|
} // Special attacks
|
|
|
|
// Attack pc
|
|
if(univ.town.monst[i].target < 6 && univ.party[univ.town.monst[i].target].main_status == eMainStatus::ALIVE
|
|
&& (monst_adjacent(targ_space,i) == true) && (cur_monst->attitude % 2 == 1)
|
|
&& (acted_yet == false)) {
|
|
monster_attack_pc(i,univ.town.monst[i].target);
|
|
take_m_ap(4,cur_monst);
|
|
acted_yet = true;
|
|
had_monst = true;
|
|
}
|
|
// Attack monst
|
|
if ((univ.town.monst[i].target >= 100) && (univ.town.monst[univ.town.monst[i].target - 100].active > 0)
|
|
&& (monst_adjacent(targ_space,i) == true) && (cur_monst->attitude > 0)
|
|
&& (acted_yet == false)) {
|
|
monster_attack_monster(i,univ.town.monst[i].target - 100);
|
|
take_m_ap(4,cur_monst);
|
|
acted_yet = true;
|
|
had_monst = true;
|
|
}
|
|
|
|
if (acted_yet == true) {
|
|
print_buf();
|
|
if (j == 0)
|
|
pause(8);
|
|
flushingInput = true;
|
|
}
|
|
|
|
if (overall_mode == MODE_COMBAT) {
|
|
if ((acted_yet == false) && (cur_monst->mobility == 1)) { // move monst
|
|
move_target = (univ.town.monst[i].target != 6) ? univ.town.monst[i].target : closest_pc(cur_monst->cur_loc);
|
|
if (monst_hate_spot(i,&move_targ) == true) // First, maybe move out of dangerous space
|
|
seek_party (i,cur_monst->cur_loc,move_targ);
|
|
else { // spot is OK, so go nuts
|
|
if ((cur_monst->attitude % 2 == 1) && (move_target < 6)) // Monsters seeking party do so
|
|
if(univ.party[move_target].main_status == eMainStatus::ALIVE) {
|
|
seek_party (i,cur_monst->cur_loc,univ.party[move_target].combat_pos);
|
|
for (k = 0; k < 6; k++)
|
|
if(univ.party[k].parry > 99 && monst_adjacent(univ.party[k].combat_pos,i)
|
|
&& (cur_monst->active > 0)) {
|
|
univ.party[k].parry = 0;
|
|
pc_attack(k,i);
|
|
}
|
|
}
|
|
|
|
if (move_target >= 100) // Monsters seeking monsters do so
|
|
if (univ.town.monst[move_target - 100].active > 0) {
|
|
seek_party (i,cur_monst->cur_loc,univ.town.monst[move_target - 100].cur_loc);
|
|
for (k = 0; k < 6; k++)
|
|
if(univ.party[k].parry > 99 && monst_adjacent(univ.party[k].combat_pos,i)
|
|
&& (cur_monst->active > 0) && (cur_monst->attitude % 2 == 1)) {
|
|
univ.party[k].parry = 0;
|
|
pc_attack(k,i);
|
|
}
|
|
}
|
|
|
|
if (cur_monst->attitude == 0) {
|
|
acted_yet = rand_move (i);
|
|
futzing++;
|
|
}
|
|
}
|
|
take_m_ap(1,cur_monst);
|
|
}
|
|
if ((acted_yet == false) && (cur_monst->mobility == 0)) { // drain action points
|
|
take_m_ap(1,cur_monst);
|
|
futzing++;
|
|
}
|
|
}
|
|
else if (acted_yet == false) {
|
|
take_m_ap(1,cur_monst);
|
|
futzing++;
|
|
}
|
|
|
|
// pcs attack any fleeing monsters
|
|
if ((overall_mode >= MODE_COMBAT) && (overall_mode < MODE_TALKING))
|
|
for (k = 0; k < 6; k++)
|
|
if(univ.party[k].main_status == eMainStatus::ALIVE && !monst_adjacent(univ.party[k].combat_pos,i)
|
|
&& (pc_adj[k] == true) && (cur_monst->attitude % 2 == 1) && (cur_monst->active > 0) &&
|
|
univ.party[k].status[eStatus::INVISIBLE] == 0) {
|
|
combat_posing_monster = current_working_monster = k;
|
|
pc_attack(k,i);
|
|
combat_posing_monster = current_working_monster = 100 + i;
|
|
pc_adj[k] = false;
|
|
}
|
|
|
|
// Place fields for monsters that create them. Only done when monst sees foe
|
|
if ((target != 6) && (can_see_light(cur_monst->cur_loc,targ_space,sight_obscurity) < 5)) { ////
|
|
if ((cur_monst->radiate_1 == 1) && (get_ran(1,1,100) < cur_monst->radiate_2))
|
|
place_spell_pattern(square,cur_monst->cur_loc,WALL_FIRE,7);
|
|
if ((cur_monst->radiate_1 == 2) && (get_ran(1,1,100) < cur_monst->radiate_2))
|
|
place_spell_pattern(square,cur_monst->cur_loc,WALL_ICE,7);
|
|
if ((cur_monst->radiate_1 == 3) && (get_ran(1,1,100) < cur_monst->radiate_2))
|
|
place_spell_pattern(square,cur_monst->cur_loc,WALL_FORCE,7);
|
|
if ((cur_monst->radiate_1 == 4) && (get_ran(1,1,100) < cur_monst->radiate_2))
|
|
place_spell_pattern(square,cur_monst->cur_loc,FIELD_ANTIMAGIC,7);
|
|
if ((cur_monst->radiate_1 == 5) && (get_ran(1,1,100) < cur_monst->radiate_2))
|
|
place_spell_pattern(square,cur_monst->cur_loc,CLOUD_SLEEP,7);
|
|
if ((cur_monst->radiate_1 == 6) && (get_ran(1,1,100) < cur_monst->radiate_2))
|
|
place_spell_pattern(square,cur_monst->cur_loc,CLOUD_STINK,7);
|
|
if ((cur_monst->radiate_1 == 10) && (get_ran(1,1,100) < 5)){
|
|
if (summon_monster(cur_monst->radiate_2,
|
|
cur_monst->cur_loc,130,cur_monst->attitude) == true)
|
|
{monst_spell_note(cur_monst->number,33); play_sound(61);}
|
|
}
|
|
if ((cur_monst->radiate_1 == 11) && (get_ran(1,1,100) < 20)){
|
|
if (summon_monster(cur_monst->radiate_2,
|
|
cur_monst->cur_loc,130,cur_monst->attitude) == true)
|
|
{monst_spell_note(cur_monst->number,33); play_sound(61);}
|
|
}
|
|
if ((cur_monst->radiate_1 == 12) && (get_ran(1,1,100) < 50)){
|
|
if (summon_monster(cur_monst->radiate_2,
|
|
cur_monst->cur_loc,130,cur_monst->attitude) == true)
|
|
{monst_spell_note(cur_monst->number,33); play_sound(61);}
|
|
}
|
|
if ((cur_monst->radiate_1 == 14) && !special_called && party_can_see_monst(i)) {
|
|
short s1, s2, s3;
|
|
special_called = true;
|
|
take_m_ap(1,cur_monst);
|
|
run_special(eSpecCtx::MONST_SPEC_ABIL,0,cur_monst->radiate_2,cur_monst->cur_loc,&s1,&s2,&s3);
|
|
}
|
|
}
|
|
|
|
combat_posing_monster = current_working_monster = -1;
|
|
// Redraw monster after it goes
|
|
if ((cur_monst->attitude > 0) && (cur_monst->active > 0) && (cur_monst->ap == 0)
|
|
&& (is_combat()) && (cur_monst->picture_num > 0) && (party_can_see_monst(i) == true)) {
|
|
center = cur_monst->cur_loc;
|
|
draw_terrain(0);
|
|
|
|
}
|
|
|
|
// If monster dead, take away actions
|
|
if (cur_monst->active == 0)
|
|
cur_monst->ap = 0;
|
|
|
|
//if ((futzing == 1) && (get_ran(1,0,1) == 0)) // If monster's just pissing around, give up
|
|
// cur_monst->ap = 0;
|
|
if (futzing > 1) // If monster's just pissing around, give up
|
|
cur_monst->ap = 0;
|
|
} // End of monster action loop
|
|
|
|
|
|
}
|
|
|
|
for (i = 0; i < num_monst; i++) { // Begin monster time stuff loop
|
|
// If party dead, no point
|
|
if (party_toast() == true)
|
|
return;
|
|
|
|
cur_monst = &univ.town.monst[i];
|
|
|
|
if ((cur_monst->active < 0) || (cur_monst->active > 2))
|
|
cur_monst->active = 0; // clean up
|
|
if (cur_monst->active != 0) { // Take care of monster effects
|
|
if(cur_monst->status[eStatus::ACID] > 0) { // Acid
|
|
if (printed_acid == false) {
|
|
add_string_to_buf("Acid: ");
|
|
printed_acid = true;
|
|
}
|
|
r1 = get_ran(cur_monst->status[eStatus::ACID],1,6);
|
|
damage_monst(i, 6,r1, 0, DAMAGE_MAGIC,0);
|
|
cur_monst->status[eStatus::ACID]--;
|
|
}
|
|
|
|
if (cur_monst->status[eStatus::ASLEEP] == 1)
|
|
monst_spell_note(cur_monst->number,29);
|
|
move_to_zero(cur_monst->status[eStatus::ASLEEP]);
|
|
move_to_zero(cur_monst->status[eStatus::PARALYZED]);
|
|
|
|
if (univ.party.age % 2 == 0) {
|
|
move_to_zero(cur_monst->status[eStatus::BLESS_CURSE]);
|
|
move_to_zero(cur_monst->status[eStatus::HASTE_SLOW]);
|
|
move_to_zero(cur_monst->status[eStatus::WEBS]);
|
|
|
|
if(cur_monst->status[eStatus::POISON] > 0) { // Poison
|
|
if (printed_poison == false) {
|
|
add_string_to_buf("Poisoned monsters: ");
|
|
printed_poison = true;
|
|
}
|
|
r1 = get_ran(cur_monst->status[eStatus::POISON],1,6);
|
|
damage_monst(i, 6, r1, 0, DAMAGE_POISON,0);
|
|
cur_monst->status[eStatus::POISON]--;
|
|
}
|
|
if(cur_monst->status[eStatus::DISEASE] > 0) { // Disease
|
|
if (printed_disease == false) {
|
|
add_string_to_buf("Diseased monsters: ");
|
|
printed_disease = true;
|
|
}
|
|
k = get_ran(1,1,5);
|
|
switch (k) {
|
|
case 1: case 2: poison_monst(cur_monst, 2);break;
|
|
case 3: slow_monst(cur_monst,2); break;
|
|
case 4: curse_monst(cur_monst,2); break;
|
|
case 5: scare_monst(cur_monst,10); break;
|
|
}
|
|
if (get_ran(1,1,6) < 4)
|
|
cur_monst->status[eStatus::DISEASE]--;
|
|
}
|
|
|
|
}
|
|
|
|
if (univ.party.age % 4 == 0) {
|
|
if (cur_monst->mp < cur_monst->max_mp)
|
|
cur_monst->mp += 2;
|
|
move_to_zero(cur_monst->status[eStatus::DUMB]);
|
|
}
|
|
} // end take care of monsters
|
|
}
|
|
|
|
// If in town, need to restore center
|
|
if (overall_mode < MODE_COMBAT)
|
|
center = univ.town.p_loc;
|
|
if (had_monst == true)
|
|
put_pc_screen();
|
|
for (i = 0; i < 6; i++)
|
|
univ.party[i].parry = 0;
|
|
|
|
monsters_going = false;
|
|
}
|
|
|
|
void monster_attack_pc(short who_att,short target)
|
|
{
|
|
cCreature *attacker;
|
|
short r1,r2,i,store_hp,sound_type = 0;
|
|
eDamageType dam_type = DAMAGE_WEAPON;
|
|
|
|
|
|
attacker = &univ.town.monst[who_att];
|
|
|
|
// A peaceful monster won't attack
|
|
if (attacker->attitude % 2 != 1)
|
|
return;
|
|
|
|
// Draw attacker frames
|
|
if ((is_combat())
|
|
&& ((center_on_monst == true) || (monsters_going == false))) {
|
|
if (attacker->spec_skill != 11)
|
|
frame_space(attacker->cur_loc,0,attacker->x_width,attacker->y_width);
|
|
frame_space(univ.party[target].combat_pos,1,1,1);
|
|
}
|
|
|
|
|
|
|
|
if ((attacker->a[0] != 0) || (attacker->a[2] != 0))
|
|
print_monst_attacks(attacker->number,target);
|
|
|
|
// Check sanctuary
|
|
if(univ.party[target].status[eStatus::INVISIBLE] > 0) {
|
|
r1 = get_ran(1,1,100);
|
|
if (r1 > hit_chance[attacker->level / 2]) {
|
|
add_string_to_buf(" Can't find target! ");
|
|
}
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
if(attacker->a[i] > 0 && univ.party[target].main_status == eMainStatus::ALIVE) {
|
|
// sprintf ((char *) create_line, " Attacks %s.",(char *) univ.party[target].name);
|
|
// add_string_to_buf((char *) create_line);
|
|
|
|
// Attack roll
|
|
r1 = get_ran(1,1,100) - 5 * min(8,attacker->status[eStatus::BLESS_CURSE]) + 5 * univ.party[target].status[eStatus::BLESS_CURSE]
|
|
+ 5 * stat_adj(target,eSkill::DEXTERITY) - 15;
|
|
r1 += 5 * (attacker->status[eStatus::WEBS] / 3);
|
|
if(univ.party[target].parry < 100)
|
|
r1 += 5 * univ.party[target].parry;
|
|
|
|
// Damage roll
|
|
r2 = get_ran(attacker->a[i] / 100 + 1,1,attacker->a[i] % 100)
|
|
+ min(8,attacker->status[eStatus::BLESS_CURSE]) - univ.party[target].status[eStatus::BLESS_CURSE] + 1;
|
|
if (univ.difficulty_adjust() > 2)
|
|
r2 = r2 * 2;
|
|
if (univ.difficulty_adjust() == 2)
|
|
r2 = (r2 * 3) / 2;
|
|
|
|
if ((univ.party[target].status[eStatus::ASLEEP] > 0) || (univ.party[target].status[eStatus::PARALYZED] > 0)) {
|
|
r1 -= 80;
|
|
r2 = r2 * 2;
|
|
}
|
|
|
|
draw_terrain(2);
|
|
// Check if hit, and do effects
|
|
if (r1 <= hit_chance[(attacker->skill + 4) / 2]) {
|
|
if(attacker->m_type == eRace::UNDEAD)
|
|
dam_type = DAMAGE_UNDEAD;
|
|
if(attacker->m_type == eRace::DEMON)
|
|
dam_type = DAMAGE_DEMON;
|
|
|
|
store_hp = univ.party[target].cur_health;
|
|
sound_type = get_monst_sound(attacker,i);
|
|
dam_type += DAMAGE_MARKED;
|
|
if (damage_pc(target,r2,dam_type,
|
|
attacker->m_type,sound_type) &&
|
|
(store_hp - univ.party[target].cur_health > 0)) {
|
|
damaged_message(store_hp - univ.party[target].cur_health,
|
|
attacker->a[i].type);
|
|
|
|
if(univ.party[target].status[eStatus::MARTYRS_SHIELD] > 0) {
|
|
add_string_to_buf(" Shares damage! ");
|
|
damage_monst(who_att, 6, store_hp - univ.party[target].cur_health, 0, DAMAGE_MAGIC,0);
|
|
}
|
|
|
|
if ((attacker->poison > 0) && (i == 0)) {
|
|
poison_pc(target,attacker->poison);
|
|
}
|
|
|
|
// Gremlin
|
|
if ((attacker->spec_skill == 21) && (get_ran(1,0,2) < 2)) {
|
|
add_string_to_buf(" Steals food! ");
|
|
print_buf();
|
|
play_sound(26);
|
|
univ.party.food = (long) max(0, (short) (univ.party.food) - get_ran(1,0,10) - 10);
|
|
put_pc_screen();
|
|
}
|
|
|
|
// Disease
|
|
if (((attacker->spec_skill == 25))
|
|
&& (get_ran(1,0,2) < 2)) {
|
|
add_string_to_buf(" Causes disease! ");
|
|
print_buf();
|
|
disease_pc(target,6);
|
|
}
|
|
|
|
// Petrification touch
|
|
if ((attacker->spec_skill == 30)
|
|
&& (pc_has_abil_equip(target,eItemAbil::PROTECT_FROM_PETRIFY) == 24)
|
|
&& (get_ran(1,0,20) + univ.party[target].level / 4 + univ.party[target].status[eStatus::BLESS_CURSE]) <= 14)
|
|
{
|
|
add_string_to_buf(" Petrifying touch!");
|
|
print_buf();
|
|
kill_pc(target,eMainStatus::STONE); // petrified, duh!
|
|
}
|
|
#if 0 // TODO: This is *i's version of the petrification touch ability.
|
|
// It seems better in some ways, like printing a message when you resist,
|
|
// but its calculation is very different, so I'm not sure what to with it.
|
|
// Note, his version has also been incorporated into monster_attack_monster.
|
|
|
|
// Petrify target
|
|
if (attacker->spec_skill == 30) {
|
|
add_string_to_buf(" Petrification touch! ");
|
|
r1 = max(0,(get_ran(1,0,100) - univ.party[target].level + 0.5*attacker->level));
|
|
// Equip petrify protection?
|
|
if (pc_has_abil_equip(target,eItemAbil:PROTECT_FROM_PETRIFY) < 24)
|
|
r1 = 0;
|
|
// Check if petrified.
|
|
if (r1 > 60) {
|
|
kill_pc(target,eMainStatus::STONE);
|
|
add_string_to_buf(" Turned to stone! ");
|
|
play_sound(43);
|
|
}
|
|
else {
|
|
add_string_to_buf(" Resists! ");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Undead xp drain
|
|
if (((attacker->spec_skill == 16) || (attacker->spec_skill == 17))
|
|
&& (pc_has_abil_equip(target,eItemAbil::LIFE_SAVING) == 24)) {
|
|
// TODO: Uh, wait a second. These two abilities have an identical effect? That doesn't seem right!
|
|
add_string_to_buf(" Drains life! ");
|
|
drain_pc(target,(attacker->level * 3) / 2);
|
|
put_pc_screen();
|
|
}
|
|
|
|
// Undead slow
|
|
if ((attacker->spec_skill == 18) && (get_ran(1,0,8) < 6) && (pc_has_abil_equip(target,eItemAbil::LIFE_SAVING) == 24)) {
|
|
add_string_to_buf(" Stuns! ");
|
|
slow_pc(target,2);
|
|
put_pc_screen();
|
|
}
|
|
// Dumbfound target
|
|
if (attacker->spec_skill == 24) {
|
|
add_string_to_buf(" Dumbfounds! ");
|
|
dumbfound_pc(target,2);
|
|
put_pc_screen();
|
|
}
|
|
|
|
// Web target
|
|
if (attacker->spec_skill == 27) {
|
|
add_string_to_buf(" Webs! ");
|
|
web_pc(target,5);
|
|
put_pc_screen();
|
|
}
|
|
// Sleep target
|
|
if (attacker->spec_skill == 28) {
|
|
add_string_to_buf(" Sleeps! ");
|
|
sleep_pc(target,6,eStatus::ASLEEP,-15);
|
|
put_pc_screen();
|
|
}
|
|
// Paralyze target
|
|
if (attacker->spec_skill == 29) {
|
|
add_string_to_buf(" Paralysis touch! ");
|
|
sleep_pc(target,500,eStatus::PARALYZED,-5);
|
|
put_pc_screen();
|
|
}
|
|
// Acid touch
|
|
if (attacker->spec_skill == 31) {
|
|
add_string_to_buf(" Acid touch! ");
|
|
acid_pc(target,(attacker->level > 20) ? 4 : 2);
|
|
}
|
|
|
|
// Freezing touch
|
|
if (((attacker->spec_skill == 15) || (attacker->spec_skill == 17))
|
|
&& (get_ran(1,0,8) < 6) && (pc_has_abil_equip(target,eItemAbil::LIFE_SAVING) == 24)) {
|
|
add_string_to_buf(" Freezing touch!");
|
|
r1 = get_ran(3,1,10);
|
|
damage_pc(target,r1,DAMAGE_COLD,eRace::UNKNOWN,0);
|
|
}
|
|
// Killing touch
|
|
if (attacker->spec_skill == 35)
|
|
{
|
|
add_string_to_buf(" Killing touch!");
|
|
r1 = get_ran(20,1,10);
|
|
damage_pc(target,r1,DAMAGE_UNBLOCKABLE,eRace::UNKNOWN,0);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
sprintf ((char *) create_line, " Misses.");
|
|
add_string_to_buf((char *) create_line);
|
|
play_sound(2);
|
|
}
|
|
combat_posing_monster = -1;
|
|
draw_terrain(2);
|
|
combat_posing_monster = 100 + who_att;
|
|
|
|
|
|
}
|
|
if(univ.party[target].main_status != eMainStatus::ALIVE)
|
|
i = 3;
|
|
}
|
|
|
|
}
|
|
|
|
void monster_attack_monster(short who_att,short attackee)
|
|
{
|
|
cCreature *attacker,*target;
|
|
short r1,r2,i,store_hp,sound_type = 0;
|
|
eDamageType dam_type = DAMAGE_WEAPON;
|
|
|
|
attacker = &univ.town.monst[who_att];
|
|
target = &univ.town.monst[attackee];
|
|
|
|
// Draw attacker frames
|
|
if ((is_combat())
|
|
&& ((center_on_monst == true) || (monsters_going == false))) {
|
|
if (attacker->spec_skill != 11)
|
|
frame_space(attacker->cur_loc,0,attacker->x_width,attacker->y_width);
|
|
frame_space(target->cur_loc,1,1,1);
|
|
}
|
|
|
|
|
|
if ((attacker->a[1] != 0) || (attacker->a[0] != 0))
|
|
print_monst_attacks(attacker->number,100 + attackee);
|
|
for (i = 0; i < 3; i++) {
|
|
if ((attacker->a[i] > 0) && (target->active != 0)) {
|
|
// sprintf ((char *) create_line, " Attacks %s.",(char *) univ.party[target].name);
|
|
// add_string_to_buf((char *) create_line);
|
|
|
|
// if friendly to party, make able to attack
|
|
if (target->attitude == 0)
|
|
target->attitude = 2;
|
|
|
|
// Attack roll
|
|
r1 = get_ran(1,1,100) - 5 * min(10,attacker->status[eStatus::BLESS_CURSE])
|
|
+ 5 * target->status[eStatus::BLESS_CURSE] - 15;
|
|
r1 += 5 * (attacker->status[eStatus::WEBS] / 3);
|
|
|
|
// Damage roll
|
|
r2 = get_ran(attacker->a[i] / 100 + 1,1,attacker->a[i] % 100)
|
|
+ min(10,attacker->status[eStatus::BLESS_CURSE]) - target->status[eStatus::BLESS_CURSE] + 2;
|
|
|
|
if(target->status[eStatus::ASLEEP] > 0 || target->status[eStatus::PARALYZED] > 0) {
|
|
r1 -= 80;
|
|
r2 = r2 * 2;
|
|
}
|
|
|
|
draw_terrain(2);
|
|
// Check if hit, and do effects
|
|
if (r1 <= hit_chance[(attacker->skill + 4) / 2]) {
|
|
if(attacker->m_type == eRace::DEMON)
|
|
dam_type = DAMAGE_DEMON;
|
|
if(attacker->m_type == eRace::UNDEAD)
|
|
dam_type = DAMAGE_UNDEAD;
|
|
store_hp = target->health;
|
|
|
|
sound_type = get_monst_sound(attacker,i);
|
|
dam_type += DAMAGE_MARKED;
|
|
if (damage_monst(attackee,7,r2,0,dam_type,sound_type) == true) {
|
|
damaged_message(store_hp - target->health,
|
|
attacker->a[i].type);
|
|
|
|
if ((attacker->poison > 0) && (i == 0)) {
|
|
poison_monst(target,attacker->poison);
|
|
}
|
|
|
|
// Undead slow
|
|
if ((attacker->spec_skill == 18) && (get_ran(1,0,8) < 6)) {
|
|
add_string_to_buf(" Stuns! ");
|
|
slow_monst(target,2);
|
|
}
|
|
|
|
// Web target
|
|
if (attacker->spec_skill == 27) {
|
|
add_string_to_buf(" Webs! ");
|
|
web_monst(target,4);
|
|
}
|
|
// Sleep target
|
|
if (attacker->spec_skill == 28) {
|
|
add_string_to_buf(" Sleeps! ");
|
|
charm_monst(target,-15,eStatus::ASLEEP,6);
|
|
}
|
|
// Dumbfound target
|
|
if (attacker->spec_skill == 24) {
|
|
add_string_to_buf(" Dumbfounds! ");
|
|
dumbfound_monst(target,2);
|
|
}
|
|
// Disease target
|
|
if (((attacker->spec_skill == 25))
|
|
&& (get_ran(1,0,2) < 2)) {
|
|
add_string_to_buf(" Causes disease! ");
|
|
print_buf();
|
|
disease_monst(target,6);
|
|
}
|
|
// Paralyze target
|
|
if (attacker->spec_skill == 29) {
|
|
add_string_to_buf(" Paralysis touch! ");
|
|
charm_monst(target,-5,eStatus::PARALYZED,500);
|
|
}
|
|
// Petrify target
|
|
if (attacker->spec_skill == 30) {
|
|
add_string_to_buf(" Petrification touch! ");
|
|
r1 = max(0,(get_ran(1,0,100) - target->level + 0.5*attacker->level));
|
|
// Check if petrified.
|
|
if ((r1 < 60) || (target->immunities & 2)) {
|
|
add_string_to_buf(" Resists! ");
|
|
}
|
|
else {
|
|
kill_monst(target,7);
|
|
add_string_to_buf(" Turned to stone! ");
|
|
play_sound(43);
|
|
|
|
}
|
|
}
|
|
|
|
// Acid touch
|
|
if (attacker->spec_skill == 31) {
|
|
add_string_to_buf(" Acid touch! ");
|
|
acid_monst(target,3);
|
|
}
|
|
|
|
// Freezing touch
|
|
if (((attacker->spec_skill == 15) || (attacker->spec_skill == 17))
|
|
&& (get_ran(1,0,8) < 6)) {
|
|
add_string_to_buf(" Freezing touch!");
|
|
r1 = get_ran(3,1,10);
|
|
damage_monst(attackee,7,r1,0,DAMAGE_COLD,0);
|
|
}
|
|
|
|
// Death touch
|
|
if ((attacker->spec_skill == 35)
|
|
&& (get_ran(1,0,8) < 6)) {
|
|
add_string_to_buf(" Killing touch!");
|
|
r1 = get_ran(20,1,10);
|
|
damage_monst(attackee,7,r1,0,DAMAGE_UNBLOCKABLE,0);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
sprintf ((char *) create_line, " Misses.");
|
|
add_string_to_buf((char *) create_line);
|
|
play_sound(2);
|
|
}
|
|
combat_posing_monster = -1;
|
|
draw_terrain(2);
|
|
combat_posing_monster = 100 + who_att;
|
|
}
|
|
if (target->active == 0)
|
|
i = 3;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void monst_fire_missile(short m_num,short bless,short level,location source,short target)
|
|
//short target; // 100 + - monster is target
|
|
{
|
|
cCreature *m_target;
|
|
short r1,r2,dam[40] = {
|
|
0,1,2,3,4, 6,8,7,0,0, 0,0,0,0,0, 0,0,0,0,0,
|
|
8,0,0,0,0, 0,0,0,0,0, 0,0,0,0,6, 0,0,0,0,0},i,j;
|
|
location targ_space;
|
|
|
|
if (target == 6)
|
|
return;
|
|
if (target >= 100) {
|
|
targ_space = univ.town.monst[target - 100].cur_loc;
|
|
if (univ.town.monst[target - 100].active == 0)
|
|
return;
|
|
}
|
|
else {
|
|
targ_space = (is_combat()) ? univ.party[target].combat_pos : univ.town.p_loc;
|
|
if(univ.party[target].main_status != eMainStatus::ALIVE)
|
|
return;
|
|
}
|
|
|
|
if (target >= 100)
|
|
m_target = &univ.town.monst[target - 100];
|
|
if (((overall_mode >= MODE_COMBAT) && (overall_mode <= MODE_TALKING)) && (center_on_monst == true)) {
|
|
frame_space(source,0,univ.town.monst[m_num].x_width,univ.town.monst[m_num].y_width);
|
|
if (target >= 100)
|
|
frame_space(targ_space,1,m_target->x_width,m_target->y_width);
|
|
else frame_space(targ_space,1,1,1);
|
|
}
|
|
|
|
draw_terrain(2);
|
|
if (level == 32) { // sleep cloud
|
|
ASB("Creature breathes.");
|
|
run_a_missile(source,targ_space,0,0,44,
|
|
0,0,100);
|
|
place_spell_pattern(rad2,targ_space,CLOUD_SLEEP,7);
|
|
}
|
|
else if (level == 14) { // vapors
|
|
//play_sound(44);
|
|
if (target < 100) { // on PC
|
|
sprintf (create_line, " Breathes on %s. ",univ.party[target].name.c_str());
|
|
add_string_to_buf(create_line);
|
|
}
|
|
else { // on monst
|
|
add_string_to_buf(" Breathes vapors.");
|
|
}
|
|
run_a_missile(source,targ_space,12,0,44,
|
|
0,0,100);
|
|
scloud_space(targ_space.x,targ_space.y);
|
|
}
|
|
else if (level == 19) { // webs
|
|
//play_sound(14);
|
|
if (target < 100) { // on PC
|
|
sprintf (create_line, " Throws web at %s. ",univ.party[target].name.c_str());
|
|
add_string_to_buf(create_line);
|
|
}
|
|
else { // on monst
|
|
add_string_to_buf(" Throws web.");
|
|
}
|
|
run_a_missile(source,targ_space,8,0,14,
|
|
0,0,100);
|
|
web_space(targ_space.x,targ_space.y);
|
|
}
|
|
else if (level == 23) { // paral
|
|
play_sound(51);
|
|
if (target < 100) { // on PC
|
|
sprintf (create_line, " Fires ray at %s. ",univ.party[target].name.c_str());
|
|
add_string_to_buf((char *) create_line);
|
|
sleep_pc(target,100,eStatus::PARALYZED,0);
|
|
}
|
|
else { // on monst
|
|
add_string_to_buf(" Shoots a ray.");
|
|
charm_monst(m_target,0,eStatus::PARALYZED,100);
|
|
}
|
|
//run_a_missile(source,targ_space,8,0,14,
|
|
// 0,0,100);
|
|
//web_space(targ_space.x,targ_space.y);
|
|
}
|
|
else if (level == 8) { // petrify
|
|
//play_sound(43);
|
|
run_a_missile(source,targ_space,14,0,43,0,0,100);
|
|
if (target < 100) { // on PC
|
|
sprintf (create_line, " Gazes at %s. ",univ.party[target].name.c_str());
|
|
add_string_to_buf(create_line);
|
|
r1 = get_ran(1,0,20) + univ.party[target].level / 4 + univ.party[target].status[eStatus::BLESS_CURSE];
|
|
if (pc_has_abil_equip(target,eItemAbil::PROTECT_FROM_PETRIFY) < 24)
|
|
r1 = 20;
|
|
if (r1 > 14) {
|
|
sprintf (create_line, " %s resists. ",univ.party[target].name.c_str());
|
|
add_string_to_buf(create_line);
|
|
}
|
|
else {
|
|
sprintf (create_line, " %s is turned to stone. ",univ.party[target].name.c_str());
|
|
add_string_to_buf(create_line);
|
|
kill_pc(target,eMainStatus::STONE);
|
|
}
|
|
}
|
|
else {
|
|
monst_spell_note(m_target->number,9);
|
|
r1 = get_ran(1,0,20) + m_target->level / 4 + m_target->status[eStatus::BLESS_CURSE];
|
|
if ((r1 > 14) || (m_target->immunities & 2))
|
|
monst_spell_note(m_target->number,10);
|
|
else {
|
|
monst_spell_note(m_target->number,8);
|
|
kill_monst(m_target,7);
|
|
}
|
|
}
|
|
}
|
|
else if (level == 9) { /// Drain sp
|
|
if (target < 100) { // pc
|
|
// modify target is target has no sp
|
|
if (univ.party[target].cur_sp < 4) {
|
|
for (i = 0; i < 8; i++) {
|
|
j = get_ran(1,0,5);
|
|
if(univ.party[j].main_status == eMainStatus::ALIVE && univ.party[j].cur_sp > 4 &&
|
|
(can_see_light(source,univ.party[j].combat_pos,sight_obscurity) < 5) && (dist(source,univ.party[j].combat_pos) <= 8)) {
|
|
target = j;
|
|
i = 8;
|
|
targ_space = univ.party[target].combat_pos;
|
|
}
|
|
}
|
|
|
|
}
|
|
run_a_missile(source,targ_space,8,0,43,0,0,100);
|
|
sprintf (create_line, " Drains %s. ",univ.party[target].name.c_str());
|
|
add_string_to_buf(create_line);
|
|
univ.party[target].cur_sp = univ.party[target].cur_sp / 2;
|
|
}
|
|
else { // on monst
|
|
run_a_missile(source,targ_space,8,0,43,0,0,100);
|
|
monst_spell_note(m_target->number,11);
|
|
if (m_target->mp >= 4)
|
|
m_target->mp = m_target->mp / 2;
|
|
else m_target->skill = 1;
|
|
}
|
|
}
|
|
else if (level == 10) { // heat ray
|
|
run_a_missile(source,targ_space,13,0,51,
|
|
0,0,100);
|
|
r1 = get_ran(7,1,6);
|
|
start_missile_anim();
|
|
if (target < 100) { // pc
|
|
sprintf (create_line, " Hits %s with heat ray.",univ.party[target].name.c_str());
|
|
add_string_to_buf(create_line);
|
|
damage_pc(target,r1,DAMAGE_FIRE,eRace::UNKNOWN,0);
|
|
}
|
|
else { // on monst
|
|
add_string_to_buf(" Fires heat ray.");
|
|
damage_monst(target - 100,7,r1,0,DAMAGE_FIRE,0);
|
|
}
|
|
do_explosion_anim(5,0);
|
|
end_missile_anim();
|
|
handle_marked_damage();
|
|
}
|
|
else if (level == 33) { // acid spit
|
|
run_a_missile(source,targ_space,0,1,64,
|
|
0,0,100);
|
|
//play_sound(64);
|
|
if (target < 100) { // pc
|
|
sprintf (create_line, " Spits acid on %s.",univ.party[target].name.c_str());
|
|
add_string_to_buf(create_line);
|
|
acid_pc(target,6);
|
|
}
|
|
else { // on monst
|
|
add_string_to_buf(" Spits acid.");
|
|
acid_monst(m_target,6);
|
|
}
|
|
}
|
|
else if (target < 100) { // missile on PC
|
|
|
|
switch (level) {
|
|
case 1: case 2: case 20:
|
|
run_a_missile(source,targ_space,3,1,12,0,0,100);
|
|
sprintf (create_line, " Shoots at %s.",univ.party[target].name.c_str());
|
|
break;
|
|
case 3:
|
|
run_a_missile(source,targ_space,5,1,14,0,0,100);
|
|
sprintf (create_line, " Throws spear at %s.",univ.party[target].name.c_str());
|
|
break;
|
|
case 7:
|
|
run_a_missile(source,targ_space,7,1,14,0,0,100);
|
|
sprintf (create_line, " Throws razordisk at %s.",univ.party[target].name.c_str());
|
|
break;
|
|
case 34:
|
|
run_a_missile(source,targ_space,5,1,14,0,0,100);
|
|
sprintf (create_line, " Fires spines at %s.",univ.party[target].name.c_str());
|
|
break;
|
|
default:
|
|
run_a_missile(source,targ_space,12,1,14,0,0,100);
|
|
sprintf (create_line, " Throws rock at %s.",univ.party[target].name.c_str());
|
|
break;
|
|
}
|
|
|
|
add_string_to_buf(create_line);
|
|
|
|
// Check sanctuary
|
|
if(univ.party[target].status[eStatus::INVISIBLE] > 0) {
|
|
r1 = get_ran(1,1,100);
|
|
if (r1 > hit_chance[level]) {
|
|
add_string_to_buf(" Can't find target! ");
|
|
}
|
|
return;
|
|
}
|
|
|
|
r1 = get_ran(1,1,100) - 5 * min(10,bless) + 5 * univ.party[target].status[eStatus::BLESS_CURSE]
|
|
- 5 * (can_see_light(source, univ.party[target].combat_pos,sight_obscurity));
|
|
if(univ.party[target].parry < 100)
|
|
r1 += 5 * univ.party[target].parry;
|
|
r2 = get_ran(dam[level],1,7) + min(10,bless);
|
|
|
|
if (r1 <= hit_chance[dam[level] * 2]) {
|
|
// sprintf ((char *) create_line, " Hits %s.",(char *) univ.party[target].name);
|
|
// add_string_to_buf((char *) create_line);
|
|
|
|
if(damage_pc(target,r2,DAMAGE_WEAPON,eRace::UNKNOWN,13)) {
|
|
// TODO: Uh, is something supposed to happen here!?
|
|
}
|
|
}
|
|
else {
|
|
sprintf (create_line, " Misses %s.",univ.party[target].name.c_str());
|
|
add_string_to_buf(create_line);
|
|
}
|
|
|
|
}
|
|
else { // missile on monst
|
|
switch (level) {
|
|
case 1: case 2: case 20:
|
|
run_a_missile(source,targ_space,3,1,12,0,0,100);
|
|
monst_spell_note(m_target->number,12);
|
|
break;
|
|
case 3:
|
|
run_a_missile(source,targ_space,5,1,14,0,0,100);
|
|
monst_spell_note(m_target->number,13);
|
|
break;
|
|
case 7:
|
|
run_a_missile(source,targ_space,7,1,14,0,0,100);
|
|
monst_spell_note(m_target->number,15);
|
|
break;
|
|
case 34:
|
|
run_a_missile(source,targ_space,5,1,14,0,0,100);
|
|
monst_spell_note(m_target->number,32);
|
|
break;
|
|
default:
|
|
run_a_missile(source,targ_space,12,1,14,0,0,100);
|
|
monst_spell_note(m_target->number,14);
|
|
break;
|
|
}
|
|
r1 = get_ran(1,1,100) - 5 * min(10,bless) + 5 * m_target->status[eStatus::BLESS_CURSE]
|
|
- 5 * (can_see_light(source, m_target->cur_loc,sight_obscurity));
|
|
r2 = get_ran(dam[level],1,7) + min(10,bless);
|
|
|
|
if (r1 <= hit_chance[dam[level] * 2]) {
|
|
// monst_spell_note(m_target->number,16);
|
|
|
|
damage_monst(target - 100,7,r2,0,DAMAGE_WEAPON,13);
|
|
}
|
|
else {
|
|
monst_spell_note(m_target->number,18);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool monst_breathe(cCreature *caster,location targ_space,short dam_type)////
|
|
//dam_type; // 0 - fire 1 - cold 2 - magic
|
|
{
|
|
short level,missile_t[4] = {13,6,8,8};
|
|
eDamageType type[4] = {DAMAGE_FIRE, DAMAGE_COLD, DAMAGE_MAGIC, DAMAGE_UNBLOCKABLE};
|
|
location l;
|
|
|
|
draw_terrain(2);
|
|
if ((is_combat()) && (center_on_monst == true)) {
|
|
frame_space(caster->cur_loc,0,caster->x_width,caster->y_width);
|
|
}
|
|
//if (dam_type < 2)
|
|
l = caster->cur_loc;
|
|
if ((caster->direction < 4) &&
|
|
(caster->x_width > 1))
|
|
l.x++;
|
|
|
|
dam_type = caster->breath_type;
|
|
run_a_missile(l,targ_space,missile_t[dam_type],0,44,0,0,100);
|
|
// play_sound(44);
|
|
//else play_sound(64);
|
|
level = caster->breath;
|
|
//if (level > 10)
|
|
// play_sound(5);
|
|
|
|
monst_breathe_note(caster->number);
|
|
level = get_ran(caster->breath,1,8);
|
|
if (overall_mode < MODE_COMBAT)
|
|
level = level / 3;
|
|
start_missile_anim();
|
|
hit_space(targ_space,level,type[dam_type],1,1);
|
|
do_explosion_anim(5,0);
|
|
end_missile_anim();
|
|
handle_marked_damage();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool monst_cast_mage(cCreature *caster,short targ)////
|
|
{
|
|
short r1,j,spell,i,level,target_levels,friend_levels_near,x;
|
|
bool acted = false;
|
|
location target,vict_loc,ashes_loc,l;
|
|
cCreature *affected;
|
|
short caster_array[7][18] = {
|
|
{1,1,1,2,2, 2,1,3,4,4, 1,1,1,2,2, 2,3,4},
|
|
{5,5,5,6,7, 8,9,10,11,11, 2,2,2,5,7, 10,10,5},
|
|
{5,5,2,9,11, 12,12,12,14,13, 13,12,12,2,2, 2,2,2},
|
|
{15,15,16,17,17, 5,12,12,13,13, 17,17,16,17,16, 2,2,2},
|
|
{15,18,19,19,20, 20,21,21,16,17, 18,18,18,18,19, 19,19,20},
|
|
{23,23,22,22,21, 21,20,24,19,18, 18,18,18,18,18, 23,23,19},
|
|
{23,23,24,25,26, 27,19,22,19,18, 18,18,18,18,26, 24,24,23}};
|
|
short emer_spells[7][4] = {
|
|
{2,0,0,5},
|
|
{2,10,11,7},
|
|
{2,13,12,13},
|
|
{2,13,12,13},
|
|
{18,20,19,18},
|
|
{18,24,19,24},
|
|
{18,26,19,27}};
|
|
|
|
|
|
if (univ.town.is_antimagic(caster->cur_loc.x,caster->cur_loc.y)) {
|
|
return false;
|
|
}
|
|
// is target dead?
|
|
if(targ < 6 && univ.party[targ].main_status != eMainStatus::ALIVE)
|
|
return false;
|
|
if ((targ >= 100) && (univ.town.monst[targ - 100].active == 0))
|
|
return false;
|
|
|
|
level = max(1,caster->mu - caster->status[eStatus::DUMB]) - 1;
|
|
|
|
target = find_fireball_loc(caster->cur_loc,1,(caster->attitude % 2 == 1) ? 0 : 1,&target_levels);
|
|
friend_levels_near = (caster->attitude % 2 != 1) ? count_levels(caster->cur_loc,3) : -1 * count_levels(caster->cur_loc,3);
|
|
|
|
if ((caster->health * 4 < caster->m_health) && (get_ran(1,0,10) < 9))
|
|
spell = emer_spells[level][3];
|
|
else if(((caster->status[eStatus::HASTE_SLOW] < 0 && get_ran(1,0,10) < 7) ||
|
|
(caster->status[eStatus::HASTE_SLOW] == 0 && get_ran(1,0,10) < 5)) && emer_spells[level][0] != 0)
|
|
spell = emer_spells[level][0];
|
|
else if ((friend_levels_near <= -10) && (get_ran(1,0,10) < 7) && (emer_spells[level][1] != 0))
|
|
spell = emer_spells[level][1];
|
|
else if ((target_levels > 50) && (get_ran(1,0,10) < 7) && (emer_spells[level][2] != 0))
|
|
spell = emer_spells[level][2];
|
|
else {
|
|
r1 = get_ran(1,0,17);
|
|
spell = caster_array[level][r1];
|
|
}
|
|
|
|
// Hastes happen often now, but don't cast them redundantly
|
|
if(caster->status[eStatus::HASTE_SLOW] > 0 && (spell == 2 || spell == 18))
|
|
spell = emer_spells[level][3];
|
|
|
|
|
|
// Anything prventing spell?
|
|
if ((target.x > 64) && (monst_mage_area_effect[spell - 1] > 0)) {
|
|
r1 = get_ran(1,0,9);
|
|
spell = caster_array[level][r1];
|
|
if ((target.x > 64) && (monst_mage_area_effect[spell - 1] > 0))
|
|
return false;
|
|
}
|
|
if (monst_mage_area_effect[spell - 1] > 0) {
|
|
targ = 6;
|
|
}
|
|
|
|
if (targ < 6) {
|
|
vict_loc = (is_combat()) ? univ.party[targ].combat_pos : univ.town.p_loc;
|
|
if (is_town())
|
|
vict_loc = target = univ.town.p_loc;
|
|
}
|
|
if (targ >= 100)
|
|
vict_loc = univ.town.monst[targ - 100].cur_loc;
|
|
if ((targ == 6) && (univ.town.is_antimagic(target.x,target.y)))
|
|
return false;
|
|
|
|
// check antimagic
|
|
if (is_combat())
|
|
if ((targ < 6) && (univ.town.is_antimagic(univ.party[targ].combat_pos.x,univ.party[targ].combat_pos.y)))
|
|
return false;
|
|
if (is_town())
|
|
if ((targ < 6) && (univ.town.is_antimagic(univ.town.p_loc.x,univ.town.p_loc.y)))
|
|
return false;
|
|
if ((targ >= 100) && (univ.town.is_antimagic(univ.town.monst[targ - 100].cur_loc.x,
|
|
univ.town.monst[targ - 100].cur_loc.y)))
|
|
return false;
|
|
|
|
|
|
// How about shockwave? Good idea?
|
|
if ((spell == 27) && (caster->attitude % 2 != 1))
|
|
spell = 26;
|
|
if ((spell == 27) && (caster->attitude % 2 == 1) && (count_levels(caster->cur_loc,10) < 45))
|
|
spell = 26;
|
|
|
|
// sprintf((char *)create_line,"m att %d trg %d trg2 x%dy%d spl %d mp %d tl:%d ",caster->attitude,targ,
|
|
// (short)target.x,(short)target.y,spell,caster->mp,target_levels);
|
|
// add_string_to_buf((char *) create_line);
|
|
|
|
l = caster->cur_loc;
|
|
if ((caster->direction < 4) && (caster->x_width > 1))
|
|
l.x++;
|
|
|
|
if (caster->mp >= monst_mage_cost[spell - 1]) {
|
|
monst_cast_spell_note(caster->number,spell,0);
|
|
acted = true;
|
|
caster->mp -= monst_mage_cost[spell - 1];
|
|
|
|
draw_terrain(2);
|
|
switch (spell) {
|
|
case 1: // spark
|
|
run_a_missile(l,vict_loc,6,1,11,0,0,80);
|
|
r1 = get_ran(2,1,4);
|
|
damage_target(targ,r1,DAMAGE_FIRE);
|
|
break;
|
|
case 2: // minor haste
|
|
play_sound(25);
|
|
caster->status[eStatus::HASTE_SLOW] += 2;
|
|
break;
|
|
case 3: // strength
|
|
play_sound(25);
|
|
caster->status[eStatus::BLESS_CURSE] += 3;
|
|
break;
|
|
case 4: // flame cloud
|
|
run_a_missile(l,vict_loc,2,1,11,0,0,80);
|
|
place_spell_pattern(single,vict_loc,WALL_FIRE,7);
|
|
break;
|
|
case 5: // flame
|
|
run_a_missile(l,vict_loc,2,1,11,0,0,80);
|
|
start_missile_anim();
|
|
r1 = get_ran(caster->level,1,4);
|
|
damage_target(targ,r1,DAMAGE_FIRE);
|
|
break;
|
|
case 6: // minor poison
|
|
run_a_missile(l,vict_loc,11,0,25,0,0,80);
|
|
if (targ < 6)
|
|
poison_pc(targ,2 + get_ran(1,0,1));
|
|
else poison_monst(&univ.town.monst[targ - 100],2 + get_ran(1,0,1));
|
|
break;
|
|
case 7: // slow
|
|
run_a_missile(l,vict_loc,15,0,25,0,0,80);
|
|
if (targ < 6)
|
|
slow_pc(targ,2 + caster->level / 2);
|
|
else slow_monst(&univ.town.monst[targ - 100],2 + caster->level / 2);
|
|
break;
|
|
case 8: // dumbfound
|
|
run_a_missile(l,vict_loc,14,0,25,0,0,80);
|
|
if (targ < 6)
|
|
dumbfound_pc(targ,2);
|
|
else dumbfound_monst(&univ.town.monst[targ - 100],2);
|
|
break;
|
|
case 9: // scloud
|
|
run_a_missile(l,target,0,0,25,0,0,80);
|
|
place_spell_pattern(square,target,CLOUD_STINK,7);
|
|
break;
|
|
case 10: // summon beast
|
|
r1 = get_summon_monster(1);
|
|
if (r1 == 0)
|
|
break;
|
|
x = get_ran(3,1,4);
|
|
//Delay(12,&dummy); // gives sound time to end
|
|
play_sound(25);
|
|
play_sound(-61);
|
|
summon_monster(r1,caster->cur_loc,
|
|
((caster->attitude % 2 != 1) ? 0 : 100) + x,caster->attitude);
|
|
break;
|
|
case 11: // conflagration
|
|
run_a_missile(l,target,13,1,25,0,0,80);
|
|
place_spell_pattern(rad2,target,WALL_FIRE,7);
|
|
break;
|
|
case 12: // fireball
|
|
r1 = 1 + (caster->level * 3) / 4;
|
|
if (r1 > 29) r1 = 29;
|
|
run_a_missile(l,target,2,1,11,0,0,80);
|
|
start_missile_anim();
|
|
place_spell_pattern(square,target,DAMAGE_FIRE,r1,7);
|
|
ashes_loc = target;
|
|
break;
|
|
case 13: case 20: case 26:// summon
|
|
play_sound(25);
|
|
if (spell == 13) {
|
|
r1 = get_summon_monster(1);
|
|
if (r1 == 0)
|
|
break;
|
|
j = get_ran(2,1,3) + 1;
|
|
}
|
|
if (spell == 20) {
|
|
r1 = get_summon_monster(2);
|
|
if (r1 == 0)
|
|
break;
|
|
j = get_ran(2,1,2) + 1;
|
|
}
|
|
if (spell == 26) {
|
|
r1 = get_summon_monster(3);
|
|
if (r1 == 0)
|
|
break;
|
|
j = get_ran(1,2,3);
|
|
}
|
|
mainPtr.display(); // TODO: Needed?
|
|
sf::sleep(time_in_ticks(12)); // gives sound time to end
|
|
x = get_ran(4,1,4);
|
|
for (i = 0; i < j; i++){
|
|
play_sound(-61);
|
|
if (summon_monster(r1,caster->cur_loc,
|
|
((caster->attitude % 2 != 1) ? 0 : 100) + x,caster->attitude) == false) {
|
|
add_string_to_buf(" Summon failed."); i = j;}
|
|
}
|
|
break;
|
|
case 14: // web
|
|
play_sound(25);
|
|
place_spell_pattern(rad2,target,FIELD_WEB,7);
|
|
break;
|
|
case 15: // poison
|
|
run_a_missile(l,vict_loc,11,0,25,0,0,80);
|
|
x = get_ran(1,0,3);
|
|
if (targ < 6)
|
|
poison_pc(targ,4 + x);
|
|
else poison_monst(&univ.town.monst[targ - 100],4 + x);
|
|
break;
|
|
case 16: // ice bolt
|
|
run_a_missile(l,vict_loc,6,1,11,0,0,80);
|
|
r1 = get_ran(5 + (caster->level / 5),1,8);
|
|
start_missile_anim();
|
|
damage_target(targ,r1,DAMAGE_COLD);
|
|
break;
|
|
case 17: // slow gp
|
|
play_sound(25);
|
|
if (caster->attitude % 2 == 1)
|
|
for (i = 0; i < 6; i++)
|
|
if (pc_near(i,caster->cur_loc,8))
|
|
slow_pc(i,2 + caster->level / 4);
|
|
for (i = 0; i < univ.town->max_monst(); i++) {
|
|
if ((univ.town.monst[i].active != 0) &&
|
|
(((univ.town.monst[i].attitude % 2 == 1) && (caster->attitude % 2 != 1)) ||
|
|
((univ.town.monst[i].attitude % 2 != 1) && (caster->attitude % 2 == 1)) ||
|
|
((univ.town.monst[i].attitude % 2 == 1) && (caster->attitude != univ.town.monst[i].attitude)))
|
|
&& (dist(caster->cur_loc,univ.town.monst[i].cur_loc) <= 7))
|
|
slow_monst(&univ.town.monst[i],2 + caster->level / 4);
|
|
}
|
|
break;
|
|
case 18: // major haste
|
|
play_sound(25);
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if ((monst_near(i,caster->cur_loc,8,0)) &&
|
|
(caster->attitude == univ.town.monst[i].attitude)) {
|
|
affected = &univ.town.monst[i];
|
|
affected->status[eStatus::HASTE_SLOW] += 3;
|
|
}
|
|
play_sound(4);
|
|
break;
|
|
case 19: // firestorm
|
|
run_a_missile(l,target,2,1,11,0,0,80);
|
|
r1 = 1 + (caster->level * 3) / 4 + 3;
|
|
if (r1 > 29) r1 = 29;
|
|
start_missile_anim();
|
|
place_spell_pattern(rad2,target,DAMAGE_FIRE,r1,7);
|
|
ashes_loc = target;
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 21: // shockstorm
|
|
run_a_missile(l,target,6,1,11,0,0,80);
|
|
place_spell_pattern(rad2,target,WALL_FORCE,7);
|
|
break;
|
|
case 22: // m. poison
|
|
run_a_missile(l,vict_loc,11,1,11,0,0,80);
|
|
x = get_ran(1,1,2);
|
|
if (targ < 6)
|
|
poison_pc(targ,6 + x);
|
|
else poison_monst(&univ.town.monst[targ - 100],6 + x);
|
|
break;
|
|
case 23: // kill!!!
|
|
run_a_missile(l,vict_loc,9,1,11,0,0,80);
|
|
r1 = 35 + get_ran(3,1,10);
|
|
start_missile_anim();
|
|
damage_target(targ,r1,DAMAGE_MAGIC);
|
|
break;
|
|
case 24: // daemon
|
|
x = get_ran(3,1,4);
|
|
play_sound(25);
|
|
play_sound(-61);
|
|
mainPtr.display(); // TODO: Needed?
|
|
sf::sleep(time_in_ticks(12)); // gives sound time to end
|
|
summon_monster(85,caster->cur_loc,
|
|
((caster->attitude % 2 != 1) ? 0 : 100) + x,caster->attitude);
|
|
break;
|
|
case 25: // major bless
|
|
play_sound(25);
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if ((monst_near(i,caster->cur_loc,8,0)) &&
|
|
(caster->attitude == univ.town.monst[i].attitude)) {
|
|
affected = &univ.town.monst[i];
|
|
affected->health += get_ran(2,1,10);
|
|
r1 = get_ran(3,1,4);
|
|
affected->status[eStatus::BLESS_CURSE] = min(8,affected->status[eStatus::BLESS_CURSE] + r1);
|
|
affected->status[eStatus::WEBS] = 0;
|
|
if (affected->status[eStatus::HASTE_SLOW] < 0)
|
|
affected->status[eStatus::HASTE_SLOW] = 0;
|
|
affected->morale += get_ran(3,1,10);
|
|
}
|
|
play_sound(4);
|
|
break;
|
|
case 27: // shockwave
|
|
do_shockwave(caster->cur_loc);
|
|
break;
|
|
}
|
|
}
|
|
else caster->mp++;
|
|
|
|
if (ashes_loc.x > 0)
|
|
univ.town.set_ash(ashes_loc.x,ashes_loc.y,true);
|
|
do_explosion_anim(5,0);
|
|
end_missile_anim();
|
|
handle_marked_damage();
|
|
|
|
return acted;
|
|
}
|
|
|
|
bool monst_cast_priest(cCreature *caster,short targ)
|
|
{
|
|
short r1,r2,spell,i,x,level,target_levels,friend_levels_near;
|
|
bool acted = false;
|
|
location target,vict_loc,l;
|
|
cCreature *affected;
|
|
short caster_array[7][10] = {
|
|
{1,1,1,1,3,3,3,4,4,4},
|
|
{5,5,6,6,7,7,8,8,8,9},
|
|
{9,6,6,8,11,12,12,5,5,12},
|
|
{12,12,13,13,14,9,9,14,14,15},
|
|
{19,18,13,19,15,18,18,19,16,18},
|
|
{22,18,16,19,18,18,21,22,23,23},
|
|
{26,26,25,24,26,22,24,22,26,25}};
|
|
short emer_spells[7][4] = {
|
|
{0,1,0,2},
|
|
{0,8,0,2},
|
|
{0,8,0,10},
|
|
{0,14,0,10},
|
|
{0,19,18,17},
|
|
{0,19,18,20},
|
|
{25,25,26,24}};
|
|
location ashes_loc;
|
|
|
|
|
|
if(targ < 6 && univ.party[targ].main_status != eMainStatus::ALIVE)
|
|
return false;
|
|
if ((targ >= 100) && (univ.town.monst[targ - 100].active == 0))
|
|
return false;
|
|
if (univ.town.is_antimagic(caster->cur_loc.x,caster->cur_loc.y)) {
|
|
return false;
|
|
}
|
|
level = max(1,caster->cl - caster->status[eStatus::DUMB]) - 1;
|
|
|
|
target = find_fireball_loc(caster->cur_loc,1,(caster->attitude % 2 == 1) ? 0 : 1,&target_levels);
|
|
friend_levels_near = (caster->attitude % 2 != 1) ? count_levels(caster->cur_loc,3) : -1 * count_levels(caster->cur_loc,3);
|
|
|
|
if ((caster->health * 4 < caster->m_health) && (get_ran(1,0,10) < 9))
|
|
spell = emer_spells[level][3];
|
|
else if(caster->status[eStatus::HASTE_SLOW] < 0 && get_ran(1,0,10) < 7 && emer_spells[level][0] != 0)
|
|
spell = emer_spells[level][0];
|
|
else if ((friend_levels_near <= -10) && (get_ran(1,0,10) < 7) && (emer_spells[level][1] != 0))
|
|
spell = emer_spells[level][1];
|
|
else if ((target_levels > 50 < 0) && (get_ran(1,0,10) < 7) && (emer_spells[level][2] != 0))
|
|
spell = emer_spells[level][2];
|
|
else {
|
|
r1 = get_ran(1,0,9);
|
|
spell = caster_array[level][r1];
|
|
}
|
|
|
|
|
|
|
|
// Anything preventing spell?
|
|
if ((target.x > 64) && (monst_priest_area_effect[spell - 1] > 0)) {
|
|
r1 = get_ran(1,0,9);
|
|
spell = caster_array[level][r1];
|
|
if ((target.x > 64) && (monst_priest_area_effect[spell - 1] > 0))
|
|
return false;
|
|
}
|
|
if (monst_priest_area_effect[spell - 1] > 0)
|
|
targ = 6;
|
|
if (targ < 6)
|
|
vict_loc = (is_town()) ? univ.town.p_loc : univ.party[targ].combat_pos;
|
|
if (targ >= 100)
|
|
vict_loc = univ.town.monst[targ - 100].cur_loc;
|
|
if ((targ == 6) && (univ.town.is_antimagic(target.x,target.y)))
|
|
return false;
|
|
if ((targ < 6) && (univ.town.is_antimagic(univ.party[targ].combat_pos.x,univ.party[targ].combat_pos.y)))
|
|
return false;
|
|
if ((targ >= 100) && (univ.town.is_antimagic(univ.town.monst[targ - 100].cur_loc.x,
|
|
univ.town.monst[targ - 100].cur_loc.y)))
|
|
return false;
|
|
|
|
|
|
// sprintf((char *)create_line,"p att %d trg %d trg2 x%dy%d spl %d mp %d",caster->attitude,targ,
|
|
// (short)target.x,(short)target.y,spell,caster->mp);
|
|
// add_string_to_buf((char *) create_line);
|
|
|
|
// snuff heals if unwounded
|
|
if ((caster->health == caster->m_health) &&
|
|
((spell == 17) || (spell == 20)))
|
|
spell--;
|
|
|
|
l = caster->cur_loc;
|
|
if ((caster->direction < 4) && (caster->x_width > 1))
|
|
l.x++;
|
|
|
|
if (caster->mp >= monst_priest_cost[spell - 1]) {
|
|
monst_cast_spell_note(caster->number,spell,1);
|
|
acted = true;
|
|
caster->mp -= monst_priest_cost[spell - 1];
|
|
draw_terrain(2);
|
|
switch (spell) {
|
|
case 3: // wrack
|
|
run_a_missile(l,vict_loc,8,0,24,0,0,80);
|
|
r1 = get_ran(2,1,4);
|
|
start_missile_anim();
|
|
damage_target(targ,r1,DAMAGE_UNBLOCKABLE);
|
|
break;
|
|
case 4: // stumble
|
|
play_sound(24);
|
|
place_spell_pattern(single,vict_loc,FIELD_WEB,7);
|
|
break;
|
|
case 1: case 5: // Blesses
|
|
play_sound(24);
|
|
caster->status[eStatus::BLESS_CURSE] = min(8,caster->status[eStatus::BLESS_CURSE] + ((spell == 1) ? 3 : 5));
|
|
play_sound(4);
|
|
break;
|
|
case 6: // curse
|
|
run_a_missile(l,vict_loc,8,0,24,0,0,80);
|
|
x = get_ran(1,0,1);
|
|
if (targ < 6)
|
|
curse_pc(targ,2 + x);
|
|
else curse_monst(&univ.town.monst[targ - 100],2 + x);
|
|
break;
|
|
case 7: // wound
|
|
run_a_missile(l,vict_loc,8,0,24,0,0,80);
|
|
r1 = get_ran(2,1,6) + 2;
|
|
start_missile_anim();
|
|
damage_target(targ,r1,DAMAGE_MAGIC);
|
|
break;
|
|
case 8: case 22: // summon spirit,summon guardian
|
|
play_sound(24);
|
|
play_sound(-61);
|
|
|
|
x = get_ran(3,1,4);
|
|
summon_monster(((spell == 8) ? 125 : 122),caster->cur_loc,
|
|
((caster->attitude % 2 != 1) ? 0 : 100) + x,caster->attitude);
|
|
break;
|
|
case 9: // disease
|
|
run_a_missile(l,vict_loc,11,0,24,0,0,80);
|
|
x = get_ran(1,0,2);
|
|
if (targ < 6)
|
|
disease_pc(targ,2 + x);
|
|
else disease_monst(&univ.town.monst[targ - 100],2 + x);
|
|
break;
|
|
case 11: // holy scourge
|
|
run_a_missile(l,vict_loc,15,0,24,0,0,80);
|
|
if (targ < 6) {
|
|
r1 = get_ran(1,0,2);
|
|
slow_pc(targ,2 + r1);
|
|
r1 = get_ran(1,0,2);
|
|
curse_pc(targ,3 + r1);
|
|
}
|
|
else {
|
|
r1 = get_ran(1,0,2);
|
|
slow_monst(&univ.town.monst[targ - 100],r1);
|
|
r1 = get_ran(1,0,2);
|
|
curse_monst(&univ.town.monst[targ - 100],r1);
|
|
}
|
|
break;
|
|
case 12: // smite
|
|
run_a_missile(l,vict_loc,6,0,24,0,0,80);
|
|
r1 = get_ran(4,1,6) + 2;
|
|
start_missile_anim();
|
|
damage_target(targ,r1,DAMAGE_COLD);
|
|
break;
|
|
case 14: // sticks to snakes
|
|
play_sound(24);
|
|
r1 = get_ran(1,1,4) + 2;
|
|
for (i = 0; i < r1; i++) {
|
|
play_sound(-61);
|
|
r2 = get_ran(1,0,7);
|
|
x = get_ran(3,1,4);
|
|
summon_monster((r2 == 1) ? 100 : 99,caster->cur_loc,
|
|
((caster->attitude % 2 != 1) ? 0 : 100) + x,caster->attitude);
|
|
}
|
|
break;
|
|
case 15: // martyr's shield
|
|
play_sound(24);
|
|
caster->status[eStatus::MARTYRS_SHIELD] = min(10,caster->status[eStatus::MARTYRS_SHIELD] + 5);
|
|
break;
|
|
case 19: // summon host
|
|
play_sound(24);
|
|
x = get_ran(3,1,4) + 1;
|
|
play_sound(-61);
|
|
summon_monster(126,caster->cur_loc,
|
|
((caster->attitude % 2 != 1) ? 0 : 100) + x,caster->attitude);
|
|
for (i = 0; i < 4; i++) {
|
|
play_sound(-61);
|
|
if (summon_monster(125,caster->cur_loc,
|
|
((caster->attitude % 2 != 1) ? 0 : 100) + x,caster->attitude) == false)
|
|
i = 4;
|
|
}
|
|
break;
|
|
|
|
case 13: case 23: // holy scourge,curse all,pestilence
|
|
play_sound(24);
|
|
r1 = get_ran(2,0,2);
|
|
r2 = get_ran(1,0,2);
|
|
if (caster->attitude % 2 == 1)
|
|
for (i = 0; i < 6; i++)
|
|
if (pc_near(i,caster->cur_loc,8)) {
|
|
if (spell == 13)
|
|
curse_pc(i,2 + r1);
|
|
if (spell == 23)
|
|
disease_pc(i,2 + r2);
|
|
}
|
|
for (i = 0; i < univ.town->max_monst(); i++) {
|
|
if ((univ.town.monst[i].active != 0) &&
|
|
(((univ.town.monst[i].attitude % 2 == 1) && (caster->attitude % 2 != 1)) ||
|
|
((univ.town.monst[i].attitude % 2 != 1) && (caster->attitude % 2 == 1)) ||
|
|
((univ.town.monst[i].attitude % 2 == 1) && (caster->attitude != univ.town.monst[i].attitude)))
|
|
&& (dist(caster->cur_loc,univ.town.monst[i].cur_loc) <= 7)) {
|
|
if (spell == 13)
|
|
curse_monst(&univ.town.monst[i],2 + r1);
|
|
if (spell == 23)
|
|
disease_monst(&univ.town.monst[i],2 + r2);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 2: case 10: case 17: case 20: // heals
|
|
play_sound(24);
|
|
switch(spell) {
|
|
case 2: r1 = get_ran(2,1,4) + 2; break;
|
|
case 10: r1 = get_ran(3,1,6); break;
|
|
case 17: r1 = get_ran(5,1,6) + 3; break;
|
|
case 20: r1 = 50; break;
|
|
}
|
|
caster->health = min(caster->health + r1, caster->m_health);
|
|
break;
|
|
case 16: case 24:// bless all,revive all
|
|
play_sound(24);
|
|
// TODO: What's r2 for here? Should it be used for Revive All?
|
|
r1 = get_ran(2,1,4); r2 = get_ran(3,1,6);
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if ((monst_near(i,caster->cur_loc,8,0)) &&
|
|
(caster->attitude == univ.town.monst[i].attitude)) {
|
|
affected = &univ.town.monst[i];
|
|
if (spell == 16)
|
|
affected->status[eStatus::BLESS_CURSE] = min(8,affected->status[eStatus::BLESS_CURSE] + r1);
|
|
if (spell == 24)
|
|
affected->health += r1;
|
|
}
|
|
play_sound(4);
|
|
break;
|
|
case 18: // Flamestrike
|
|
run_a_missile(l,target,2,0,11,0,0,80);
|
|
r1 = 2 + caster->level / 2 + 2;
|
|
start_missile_anim();
|
|
place_spell_pattern(square,target,DAMAGE_FIRE,r1,7);
|
|
ashes_loc = target;
|
|
break;
|
|
|
|
|
|
case 21: // holy ravaging
|
|
run_a_missile(l,vict_loc,14,0,53,0,0,80);
|
|
r1 = get_ran(4,1,8);
|
|
r2 = get_ran(1,0,2);
|
|
damage_target(targ,r1,DAMAGE_MAGIC);
|
|
if (targ < 6) {
|
|
slow_pc(targ,6);
|
|
poison_pc(targ,5 + r2);
|
|
}
|
|
else {
|
|
slow_monst(&univ.town.monst[targ - 100],6);
|
|
poison_monst(&univ.town.monst[targ - 100],5 + r2);
|
|
}
|
|
break;
|
|
case 25: // avatar
|
|
play_sound(24);
|
|
monst_spell_note(caster->number,26);
|
|
caster->health = caster->m_health;
|
|
caster->status[eStatus::MARTYRS_SHIELD] = 8;
|
|
caster->status[eStatus::POISON] = 0;
|
|
caster->status[eStatus::HASTE_SLOW] = 8;
|
|
caster->status[eStatus::WEBS] = 0;
|
|
caster->status[eStatus::DISEASE] = 0;
|
|
caster->status[eStatus::DUMB] = 0;
|
|
caster->status[eStatus::MARTYRS_SHIELD] = 8;
|
|
break;
|
|
case 26: // divine thud
|
|
run_a_missile(l,target,9,0,11,0,0,80);
|
|
r1 = (caster->level * 3) / 4 + 5;
|
|
if (r1 > 29) r1 = 29;
|
|
start_missile_anim();
|
|
place_spell_pattern(rad2,target,DAMAGE_MAGIC,r1,7 );
|
|
ashes_loc = target;
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
else caster->mp++;
|
|
if (ashes_loc.x > 0)
|
|
univ.town.set_ash(ashes_loc.x,ashes_loc.y,true);
|
|
do_explosion_anim(5,0);
|
|
end_missile_anim();
|
|
handle_marked_damage();
|
|
|
|
return acted;
|
|
}
|
|
|
|
void damage_target(short target,short dam,eDamageType type)
|
|
{
|
|
if (target == 6) return;
|
|
if (target < 6)
|
|
damage_pc(target,dam,type,eRace::UNKNOWN,0);
|
|
else damage_monst(target - 100, 7, dam, 0, type,0);
|
|
}
|
|
|
|
|
|
// target = find_fireball_loc(caster->m_loc,1,(caster->attitude == 1) ? 0 : 1,&target_levels);
|
|
|
|
location find_fireball_loc(location where,short radius,short mode,short *m)
|
|
//short mode; // 0 - hostile casting 1 - friendly casting
|
|
{
|
|
location check_loc,cast_loc(120,0);
|
|
short cur_lev,level_max = 10;
|
|
|
|
for (check_loc.x = 1; check_loc.x < univ.town->max_dim() - 1; check_loc.x ++)
|
|
for (check_loc.y = 1; check_loc.y < univ.town->max_dim() - 1; check_loc.y ++)
|
|
if(dist(where,check_loc) <= 8 && can_see(where,check_loc,sight_obscurity) < 5 && sight_obscurity(check_loc.x,check_loc.y) < 5) {
|
|
{
|
|
cur_lev = count_levels(check_loc,radius);
|
|
if (mode == 1)
|
|
cur_lev = cur_lev * -1;
|
|
if ( ((cur_lev > level_max) || ((cur_lev == level_max) && (get_ran(1,0,1) == 0)))
|
|
&& (dist(where,check_loc) > radius)) {
|
|
level_max = cur_lev;
|
|
cast_loc = check_loc;
|
|
}
|
|
}
|
|
}
|
|
*m = level_max;
|
|
|
|
return cast_loc;
|
|
}
|
|
|
|
location closest_pc_loc(location where)
|
|
{
|
|
short i;
|
|
location pc_where(120,120);
|
|
|
|
for (i = 0; i < 6; i++)
|
|
if(univ.party[i].main_status == eMainStatus::ALIVE)
|
|
if ((dist(where,univ.party[i].combat_pos)) < (dist(where,pc_where)))
|
|
pc_where = univ.party[i].combat_pos;
|
|
return pc_where;
|
|
}
|
|
|
|
short count_levels(location where,short radius)
|
|
{
|
|
short i,store = 0;
|
|
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if (monst_near(i,where,radius,0) == true) {
|
|
if (univ.town.monst[i].attitude % 2 == 1)
|
|
store = store - univ.town.monst[i].level;
|
|
else store = store + univ.town.monst[i].level;
|
|
}
|
|
if (is_combat()) {
|
|
for (i = 0; i < 6; i++)
|
|
if (pc_near(i,where,radius) == true)
|
|
store = store + 10;
|
|
}
|
|
if (is_town())
|
|
if(vdist(where,univ.town.p_loc) <= radius && can_see(where,univ.town.p_loc,sight_obscurity) < 5)
|
|
store += 20;
|
|
|
|
return store;
|
|
}
|
|
|
|
bool pc_near(short pc_num,location where,short radius)
|
|
{
|
|
// Assuming not looking
|
|
if (overall_mode >= MODE_COMBAT) {
|
|
if(univ.party[pc_num].main_status == eMainStatus::ALIVE && vdist(univ.party[pc_num].combat_pos,where) <= radius)
|
|
return true;
|
|
else return false;
|
|
}
|
|
if(univ.party[pc_num].main_status == eMainStatus::ALIVE && vdist(univ.town.p_loc,where) <= radius)
|
|
return true;
|
|
else return false;
|
|
}
|
|
|
|
/*short pc_there(where)
|
|
location where;
|
|
{
|
|
short i;
|
|
|
|
for (i = 0; i < 6; i++)
|
|
if ((univ.party[i].main_status == 1) && (univ.party[i].combat_pos.x == where.x) && (univ.party[i].combat_pos.y == where.y))
|
|
return i;
|
|
return 6;
|
|
} */
|
|
|
|
bool monst_near(short m_num,location where,short radius,short active)
|
|
//short active; // 0 - any monst 1 - monster need be active
|
|
{
|
|
if ((univ.town.monst[m_num].active != 0) && (vdist(univ.town.monst[m_num].cur_loc,where) <= radius)
|
|
&& ((active == 0) || (univ.town.monst[m_num].active == 2)) )
|
|
return true;
|
|
else return false;
|
|
}
|
|
|
|
void fireball_space(location loc,short dam)
|
|
{
|
|
place_spell_pattern(square,loc,DAMAGE_FIRE,dam,7);
|
|
}
|
|
|
|
static void place_spell_pattern(effect_pat_type pat,location center,unsigned short type,short who_hit)
|
|
//type; // 0 - take codes in pattern, OW make all nonzero this type
|
|
// Types 0 - Null 1 - web 2 - fire barrier 3 - force barrier 4 - force wall 5 - fire wall
|
|
// 6 - anti-magic field 7 - stink cloud 8 - ice wall 9 - blade wall 10 - quickfire
|
|
// 11 - dispel 12 - sleep field
|
|
// 50 + i - 80 : id6 fire damage 90 + i - 120 : id6 cold damage 130 + i - 160 : id6 magic dam.
|
|
// if prep for anim is true, supporess look checks and go fast
|
|
{
|
|
short i,j,r1,k = 0;
|
|
unsigned short effect;
|
|
location spot_hit;
|
|
location s_loc;
|
|
rectangle active;
|
|
cCreature *which_m;
|
|
bool monster_hit = false;
|
|
|
|
|
|
if(type != FIELD_NONE)
|
|
modify_pattern(&pat,type);
|
|
|
|
|
|
|
|
active = univ.town->in_town_rect;
|
|
// eliminate barriers that can't be seen
|
|
for (i = minmax(active.left + 1,active.right - 1,center.x - 4);
|
|
i <= minmax(active.left + 1,active.right - 1,center.x + 4); i++)
|
|
for (j = minmax(active.top + 1,active.bottom - 1,center.y - 4);
|
|
j <= minmax(active.top + 1,active.bottom - 1,center.y + 4); j++) {
|
|
s_loc.x = i; s_loc.y = j;
|
|
if (can_see_light(center,s_loc,sight_obscurity) == 5)
|
|
pat.pattern[i - center.x + 4][j - center.y + 4] = 0;
|
|
}
|
|
|
|
|
|
// First actually make barriers, then draw them, then inflict damaging effects.
|
|
for (i = minmax(0,univ.town->max_dim() - 1,center.x - 4); i <= minmax(0,univ.town->max_dim() - 1,center.x + 4); i++)
|
|
for (j = minmax(0,univ.town->max_dim() - 1,center.y - 4); j <= minmax(0,univ.town->max_dim() - 1,center.y + 4); j++)
|
|
if(sight_obscurity(i,j) < 5) {
|
|
effect = pat.pattern[i - center.x + 4][j - center.y + 4];
|
|
switch (effect) {
|
|
case FIELD_WEB:
|
|
web_space(i,j);
|
|
break;
|
|
case BARRIER_FIRE:
|
|
univ.town.set_fire_barr(i,j,true);
|
|
break;
|
|
case BARRIER_FORCE:
|
|
univ.town.set_force_barr(i,j,true);
|
|
break;
|
|
case WALL_FORCE:
|
|
univ.town.set_force_wall(i, j, true);
|
|
break;
|
|
case WALL_FIRE:
|
|
univ.town.set_fire_wall(i,j,true);
|
|
break;
|
|
case FIELD_ANTIMAGIC:
|
|
univ.town.set_antimagic(i,j,true);
|
|
break;
|
|
case CLOUD_STINK:
|
|
scloud_space(i,j);
|
|
break;
|
|
case WALL_ICE:
|
|
univ.town.set_ice_wall(i,j,true);
|
|
break;
|
|
case WALL_BLADES:
|
|
univ.town.set_blade_wall(i,j,true);
|
|
break;
|
|
case FIELD_QUICKFIRE:
|
|
univ.town.set_quickfire(i,j,true);
|
|
break;
|
|
case FIELD_DISPEL:
|
|
dispel_fields(i,j,0);
|
|
break;
|
|
case CLOUD_SLEEP:
|
|
sleep_cloud_space(i,j);
|
|
break;
|
|
case FIELD_SMASH:
|
|
crumble_wall(loc(i,j));
|
|
break;
|
|
case OBJECT_CRATE:
|
|
univ.town.set_crate(i,j,true);
|
|
break;
|
|
case OBJECT_BARREL:
|
|
univ.town.set_barrel(i,j,true);
|
|
break;
|
|
case OBJECT_BLOCK:
|
|
univ.town.set_block(i,j,true);
|
|
break;
|
|
case BARRIER_CAGE:
|
|
univ.town.set_force_cage(i, j, true);
|
|
break;
|
|
case SFX_SMALL_BLOOD:
|
|
univ.town.set_sm_blood(i,j,true);
|
|
break;
|
|
case SFX_MEDIUM_BLOOD:
|
|
univ.town.set_med_blood(i,j,true);
|
|
break;
|
|
case SFX_LARGE_BLOOD:
|
|
univ.town.set_lg_blood(i,j,true);
|
|
break;
|
|
case SFX_SMALL_SLIME:
|
|
univ.town.set_sm_slime(i,j,true);
|
|
break;
|
|
case SFX_LARGE_SLIME:
|
|
univ.town.set_lg_slime(i,j,true);
|
|
break;
|
|
case SFX_ASH:
|
|
univ.town.set_ash(i,j,true);
|
|
break;
|
|
case SFX_BONES:
|
|
univ.town.set_bones(i,j,true);
|
|
break;
|
|
case SFX_RUBBLE:
|
|
univ.town.set_rubble(i,j,true);
|
|
break;
|
|
}
|
|
}
|
|
draw_terrain(0);
|
|
if (is_town()) // now make things move faster if in town
|
|
fast_bang = 2;
|
|
|
|
// Damage to pcs
|
|
for (k = 0; k < 6; k++)
|
|
for (i = minmax(0,univ.town->max_dim() - 1,center.x - 4); i <= minmax(0,univ.town->max_dim() - 1,center.x + 4); i++)
|
|
for (j = minmax(0,univ.town->max_dim() - 1,center.y - 4); j <= minmax(0,univ.town->max_dim() - 1,center.y + 4); j++) {
|
|
spot_hit.x = i;
|
|
spot_hit.y = j;
|
|
if(sight_obscurity(i,j) < 5 && univ.party[k].main_status == eMainStatus::ALIVE
|
|
&& (((is_combat()) && (univ.party[k].combat_pos == spot_hit)) ||
|
|
((is_town()) && (univ.town.p_loc == spot_hit)))) {
|
|
effect = pat.pattern[i - center.x + 4][j - center.y + 4];
|
|
switch (effect) {
|
|
case WALL_FORCE:
|
|
r1 = get_ran(2,1,6);
|
|
damage_pc(k,r1,DAMAGE_MAGIC,eRace::UNKNOWN,0);
|
|
break;
|
|
case WALL_FIRE:
|
|
r1 = get_ran(1,1,6) + 1;
|
|
damage_pc(k,r1,DAMAGE_FIRE,eRace::UNKNOWN,0);
|
|
break;
|
|
case WALL_ICE:
|
|
r1 = get_ran(2,1,6);
|
|
damage_pc(k,r1,DAMAGE_COLD,eRace::UNKNOWN,0);
|
|
break;
|
|
case WALL_BLADES:
|
|
r1 = get_ran(4,1,8);
|
|
damage_pc(k,r1,DAMAGE_WEAPON,eRace::UNKNOWN,0);
|
|
break;
|
|
case OBJECT_BLOCK:
|
|
r1 = get_ran(6,1,8);
|
|
damage_pc(k,r1,DAMAGE_WEAPON,eRace::UNKNOWN,0);
|
|
break;
|
|
case BARRIER_CAGE:
|
|
univ.party[k].status[eStatus::FORCECAGE] = 8;
|
|
break;
|
|
default:
|
|
eDamageType type = DAMAGE_MARKED;
|
|
unsigned short dice;
|
|
if(effect > 50 && effect <= 80) {
|
|
type = DAMAGE_FIRE;
|
|
dice = effect - 50;
|
|
} else if(effect > 90 && effect <= 120) {
|
|
type = DAMAGE_COLD;
|
|
dice = effect - 90;
|
|
} else if(effect > 130 && effect <= 160) {
|
|
type = DAMAGE_MAGIC;
|
|
dice = effect - 130;
|
|
// The rest of these are new, currently unused.
|
|
} else if(effect > 170 && effect <= 200) {
|
|
type = DAMAGE_WEAPON;
|
|
dice = effect - 170;
|
|
} else if(effect > 210 && effect <= 240) {
|
|
type = DAMAGE_POISON;
|
|
dice = effect - 210;
|
|
} else if(effect > 250 && effect <= 280) {
|
|
type = DAMAGE_UNBLOCKABLE;
|
|
dice = effect - 250;
|
|
} else if(effect > 290 && effect <= 320) {
|
|
type = DAMAGE_UNDEAD;
|
|
dice = effect - 290;
|
|
} else if(effect > 330 && effect <= 360) {
|
|
type = DAMAGE_DEMON;
|
|
dice = effect - 330;
|
|
}
|
|
if(type == DAMAGE_MARKED) break;
|
|
r1 = get_ran(dice,1,6);
|
|
damage_pc(k,r1,type,eRace::UNKNOWN,0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
fast_bang = 0;
|
|
|
|
// Damage to monsters
|
|
for (k = 0; k < univ.town->max_monst(); k++)
|
|
if ((univ.town.monst[k].active > 0) && (dist(center,univ.town.monst[k].cur_loc) <= 5)) {
|
|
monster_hit = false;
|
|
// First actually make barriers, then draw them, then inflict damaging effects.
|
|
for (i = minmax(0,univ.town->max_dim() - 1,center.x - 4); i <= minmax(0,univ.town->max_dim() - 1,center.x + 4); i++)
|
|
for (j = minmax(0,univ.town->max_dim() - 1,center.y - 4); j <= minmax(0,univ.town->max_dim() - 1,center.y + 4); j++) {
|
|
spot_hit.x = i;
|
|
spot_hit.y = j;
|
|
|
|
if(!monster_hit && sight_obscurity(i,j) < 5 && monst_on_space(spot_hit,k) > 0) {
|
|
|
|
if (pat.pattern[i - center.x + 4][j - center.y + 4] > 0)
|
|
monster_hit = true;
|
|
effect = pat.pattern[i - center.x + 4][j - center.y + 4];
|
|
switch (effect) {
|
|
case FIELD_WEB:
|
|
which_m = &univ.town.monst[k];
|
|
web_monst(which_m,3);
|
|
break;
|
|
case WALL_FORCE:
|
|
r1 = get_ran(3,1,6);
|
|
damage_monst(k, who_hit, r1,0, DAMAGE_MAGIC,0);
|
|
break;
|
|
case WALL_FIRE:
|
|
r1 = get_ran(2,1,6);
|
|
which_m = &univ.town.monst[k];
|
|
if (which_m->spec_skill == 22)
|
|
break;
|
|
damage_monst(k, who_hit, r1,0, DAMAGE_FIRE,0);
|
|
break;
|
|
case CLOUD_STINK:
|
|
which_m = &univ.town.monst[k];
|
|
curse_monst(which_m,get_ran(1,1,2));
|
|
break;
|
|
case WALL_ICE:
|
|
which_m = &univ.town.monst[k];
|
|
r1 = get_ran(3,1,6);
|
|
if (which_m->spec_skill == 23)
|
|
break;
|
|
damage_monst(k, who_hit, r1,0, DAMAGE_COLD,0);
|
|
break;
|
|
case WALL_BLADES:
|
|
r1 = get_ran(6,1,8);
|
|
damage_monst(k, who_hit, r1,0, DAMAGE_WEAPON,0);
|
|
break;
|
|
case CLOUD_SLEEP:
|
|
which_m = &univ.town.monst[k];
|
|
charm_monst(which_m,0,eStatus::ASLEEP,3);
|
|
break;
|
|
case OBJECT_BLOCK:
|
|
r1 = get_ran(6,1,8);
|
|
damage_monst(k,who_hit,r1,0,DAMAGE_WEAPON,0);
|
|
break;
|
|
case BARRIER_CAGE:
|
|
univ.town.monst[k].status[eStatus::FORCECAGE] = 8;
|
|
break;
|
|
default:
|
|
eDamageType type = DAMAGE_MARKED;
|
|
unsigned short dice;
|
|
if(effect > 50 && effect <= 80) {
|
|
type = DAMAGE_FIRE;
|
|
dice = effect - 50;
|
|
} else if(effect > 90 && effect <= 120) {
|
|
type = DAMAGE_COLD;
|
|
dice = effect - 90;
|
|
} else if(effect > 130 && effect <= 160) {
|
|
type = DAMAGE_MAGIC;
|
|
dice = effect - 130;
|
|
// The rest of these are new, currently unused.
|
|
} else if(effect > 170 && effect <= 200) {
|
|
type = DAMAGE_WEAPON;
|
|
dice = effect - 170;
|
|
} else if(effect > 210 && effect <= 240) {
|
|
type = DAMAGE_POISON;
|
|
dice = effect - 210;
|
|
} else if(effect > 250 && effect <= 280) {
|
|
type = DAMAGE_UNBLOCKABLE;
|
|
dice = effect - 250;
|
|
} else if(effect > 290 && effect <= 320) {
|
|
type = DAMAGE_UNDEAD;
|
|
dice = effect - 290;
|
|
} else if(effect > 330 && effect <= 360) {
|
|
type = DAMAGE_DEMON;
|
|
dice = effect - 330;
|
|
}
|
|
if(type == DAMAGE_MARKED) break;
|
|
r1 = get_ran(dice,1,6);
|
|
damage_monst(k,who_hit,r1,0,type,0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void handle_item_spell(location loc,short num)
|
|
{
|
|
// TODO: This function is currently unused
|
|
switch (num) {
|
|
case 82: // Pyhrrus
|
|
place_spell_pattern(rad2,loc,WALL_BLADES,6);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
void place_spell_pattern(effect_pat_type pat,location center,short who_hit) {
|
|
place_spell_pattern(pat, center, FIELD_NONE, who_hit);
|
|
}
|
|
|
|
void place_spell_pattern(effect_pat_type pat,location center,eFieldType type,short who_hit) {
|
|
unsigned short code = type;
|
|
place_spell_pattern(pat, center, code, who_hit);
|
|
}
|
|
|
|
// Copied from place_spell_pattern comment above:
|
|
// 50 + i - 80 : id6 fire damage 90 + i - 120 : id6 cold damage 130 + i - 160 : id6 magic dam.
|
|
void place_spell_pattern(effect_pat_type pat,location center,eDamageType type,short dice,short who_hit) {
|
|
unsigned short code;
|
|
dice = minmax(1, 30, dice);
|
|
switch(type) {
|
|
case DAMAGE_FIRE: code = 50; break;
|
|
case DAMAGE_COLD: code = 90; break;
|
|
case DAMAGE_MAGIC: code = 130; break;
|
|
// TODO: These are new; nothing actually uses them, but maybe eventually!
|
|
case DAMAGE_WEAPON: code = 170; break;
|
|
case DAMAGE_POISON: code = 210; break;
|
|
case DAMAGE_UNBLOCKABLE: code = 250; break;
|
|
case DAMAGE_UNDEAD: code = 290; break;
|
|
case DAMAGE_DEMON: code = 330; break;
|
|
}
|
|
place_spell_pattern(pat, center, code + dice, who_hit);
|
|
}
|
|
|
|
void modify_pattern(effect_pat_type *pat,unsigned short type)
|
|
{
|
|
short i,j;
|
|
|
|
for (i = 0; i < 9; i++)
|
|
for (j = 0; j < 9; j++)
|
|
if (pat->pattern[i][j] > 0)
|
|
pat->pattern[i][j] = type;
|
|
}
|
|
|
|
void do_shockwave(location target)
|
|
{
|
|
short i;
|
|
|
|
start_missile_anim();
|
|
for (i = 0; i < 6; i++)
|
|
if ((dist(target,univ.party[i].combat_pos) > 0) && (dist(target,univ.party[i].combat_pos) < 11)
|
|
&& univ.party[i].main_status == eMainStatus::ALIVE)
|
|
damage_pc(i, get_ran(2 + dist(target,univ.party[i].combat_pos) / 2, 1, 6), DAMAGE_UNBLOCKABLE,eRace::UNKNOWN,0);
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if ((univ.town.monst[i].active != 0) && (dist(target,univ.town.monst[i].cur_loc) > 0)
|
|
&& (dist(target,univ.town.monst[i].cur_loc) < 11)
|
|
&& (can_see_light(target,univ.town.monst[i].cur_loc,sight_obscurity) < 5))
|
|
damage_monst(i, current_pc, get_ran(2 + dist(target,univ.town.monst[i].cur_loc) / 2 , 1, 6), 0, DAMAGE_UNBLOCKABLE,0);
|
|
do_explosion_anim(5,0);
|
|
end_missile_anim();
|
|
handle_marked_damage();
|
|
}
|
|
|
|
void radius_damage(location target,short radius, short dam, eDamageType type)////
|
|
{
|
|
short i;
|
|
|
|
if (is_town()) {
|
|
for (i = 0; i < 6; i++)
|
|
if ((dist(target,univ.town.p_loc) > 0) && (dist(target,univ.town.p_loc) <= radius)
|
|
&& univ.party[i].main_status == eMainStatus::ALIVE)
|
|
damage_pc(i, dam, type,eRace::UNKNOWN,0);
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if ((univ.town.monst[i].active != 0) && (dist(target,univ.town.monst[i].cur_loc) > 0)
|
|
&& (dist(target,univ.town.monst[i].cur_loc) <= radius)
|
|
&& (can_see_light(target,univ.town.monst[i].cur_loc,sight_obscurity) < 5))
|
|
damage_monst(i, current_pc, dam, 0, type,0);
|
|
return;
|
|
}
|
|
|
|
start_missile_anim();
|
|
for (i = 0; i < 6; i++)
|
|
if ((dist(target,univ.party[i].combat_pos) > 0) && (dist(target,univ.party[i].combat_pos) <= radius)
|
|
&& univ.party[i].main_status == eMainStatus::ALIVE)
|
|
damage_pc(i, dam, type,eRace::UNKNOWN,0);
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if ((univ.town.monst[i].active != 0) && (dist(target,univ.town.monst[i].cur_loc) > 0)
|
|
&& (dist(target,univ.town.monst[i].cur_loc) <= radius)
|
|
&& (can_see_light(target,univ.town.monst[i].cur_loc,sight_obscurity) < 5))
|
|
damage_monst(i, current_pc, dam, 0, type,0);
|
|
do_explosion_anim(5,0);
|
|
end_missile_anim();
|
|
handle_marked_damage();
|
|
}
|
|
// Slightly kludgy way to only damage PCs in space)
|
|
void hit_pcs_in_space(location target,short dam,eDamageType type,short report,short hit_all)
|
|
{
|
|
//short store_active[T_M],i;
|
|
|
|
//for (i = 0; i < T_M; i++) {
|
|
// store_active[i] = univ.town.monst[i].active;
|
|
// univ.town.monst[i].active = 0;
|
|
//}
|
|
hit_space(target, dam,type, report, 10 + hit_all);
|
|
//for (i = 0; i < T_M; i++)
|
|
// univ.town.monst[i].active = store_active[i];
|
|
}
|
|
|
|
void hit_space(location target,short dam,eDamageType type,short report,short hit_all)
|
|
//type; // 0 - weapon 1 - fire 2 - poison 3 - general magic 4 - unblockable 5 - cold
|
|
// 6 - demon 7 - undead
|
|
//short report; // 0 - no 1 - print result
|
|
//hit_all; // 0 - nail top thing 1 - hit all in space + 10 ... no monsters
|
|
{
|
|
short i;
|
|
bool stop_hitting = false,hit_monsters = true;
|
|
|
|
// sprintf ((char *) create_line, " %d %d. ",target.x,target.y);
|
|
// add_string_to_buf((char *) create_line);
|
|
if ((target.x < 0) || (target.x > 63) || (target.y < 0) || (target.y > 63))
|
|
return;
|
|
|
|
if (hit_all >= 10) {
|
|
hit_monsters = false;
|
|
hit_all -= 10;
|
|
}
|
|
|
|
if ((univ.town.is_antimagic(target.x,target.y)) && ((type == 1) || (type == 3) || (type == 5))) {
|
|
return;
|
|
}
|
|
|
|
if (dam <= 0) {
|
|
add_string_to_buf(" No damage.");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if ((hit_monsters == true) && (univ.town.monst[i].active != 0) && (stop_hitting == false))
|
|
if (monst_on_space(target,i)) {
|
|
if (processing_fields == true)
|
|
damage_monst(i, 6, dam, 0, type,0);
|
|
else damage_monst(i, (monsters_going == true) ? 7 : current_pc, dam, 0, type,0);
|
|
stop_hitting = (hit_all == 1) ? false : true;
|
|
}
|
|
|
|
if (overall_mode >= MODE_COMBAT)
|
|
for (i = 0; i < 6; i++)
|
|
if(univ.party[i].main_status == eMainStatus::ALIVE && !stop_hitting)
|
|
if (univ.party[i].combat_pos == target) {
|
|
damage_pc(i,dam,type,eRace::UNKNOWN,0);
|
|
stop_hitting = (hit_all == 1) ? false : true;
|
|
}
|
|
if (overall_mode < MODE_COMBAT)
|
|
if (target == univ.town.p_loc) {
|
|
fast_bang = 1;
|
|
hit_party(dam,type);
|
|
fast_bang = 0;
|
|
stop_hitting = (hit_all == 1) ? false : true;
|
|
}
|
|
|
|
if ((report == 1) && (hit_all == 0) && (stop_hitting == false))
|
|
add_string_to_buf(" Missed.");
|
|
|
|
}
|
|
|
|
|
|
|
|
void do_poison()
|
|
{
|
|
short i,r1 = 0;
|
|
bool some_poison = false;
|
|
|
|
for (i = 0; i < 6; i++)
|
|
if(univ.party[i].main_status == eMainStatus::ALIVE)
|
|
if(univ.party[i].status[eStatus::POISON] > 0)
|
|
some_poison = true;
|
|
if (some_poison == true) {
|
|
add_string_to_buf("Poison: ");
|
|
for (i = 0; i < 6; i++)
|
|
if(univ.party[i].main_status == eMainStatus::ALIVE)
|
|
if(univ.party[i].status[eStatus::POISON] > 0) {
|
|
r1 = get_ran(univ.party[i].status[eStatus::POISON],1,6);
|
|
damage_pc(i,r1,DAMAGE_POISON,eRace::UNKNOWN,0);
|
|
if (get_ran(1,0,8) < 6)
|
|
move_to_zero(univ.party[i].status[eStatus::POISON]);
|
|
if (get_ran(1,0,8) < 6)
|
|
if(univ.party[i].traits[eTrait::GOOD_CONST])
|
|
move_to_zero(univ.party[i].status[eStatus::POISON]);
|
|
// TODO: Shouldn't the above two conditionals be swapped?
|
|
}
|
|
put_pc_screen();
|
|
//if (overall_mode < 10)
|
|
// boom_space(univ.univ.party.p_loc,overall_mode,2,r1);
|
|
}
|
|
}
|
|
|
|
|
|
void handle_disease()
|
|
{
|
|
short i,r1 = 0;
|
|
bool disease = false;
|
|
|
|
for (i = 0; i < 6; i++)
|
|
if(univ.party[i].main_status == eMainStatus::ALIVE)
|
|
if(univ.party[i].status[eStatus::DISEASE] > 0)
|
|
disease = true;
|
|
|
|
if (disease == true) {
|
|
add_string_to_buf("Disease: ");
|
|
for (i = 0; i < 6; i++)
|
|
if(univ.party[i].main_status == eMainStatus::ALIVE)
|
|
if(univ.party[i].status[eStatus::DISEASE] > 0) {
|
|
r1 = get_ran(1,1,10);
|
|
switch (r1) {
|
|
case 1: case 2:
|
|
poison_pc(i,2);
|
|
break;
|
|
case 3: case 4:
|
|
slow_pc(i,2);
|
|
break;
|
|
case 5:
|
|
drain_pc(i,5);
|
|
break;
|
|
case 6: case 7:
|
|
curse_pc(i,3);
|
|
break;
|
|
case 8:
|
|
dumbfound_pc(i,3);
|
|
break;
|
|
case 9: case 10:
|
|
sprintf (create_line, " %s unaffected. ",
|
|
univ.party[i].name.c_str());
|
|
add_string_to_buf(create_line);
|
|
break;
|
|
}
|
|
r1 = get_ran(1,0,7);
|
|
if(univ.party[i].traits[eTrait::GOOD_CONST])
|
|
r1 -= 2;
|
|
if ((get_ran(1,0,7) <= 0) || (pc_has_abil_equip(i,eItemAbil::PROTECT_FROM_DISEASE) < 24))
|
|
move_to_zero(univ.party[i].status[eStatus::DISEASE]);
|
|
}
|
|
put_pc_screen();
|
|
}
|
|
}
|
|
|
|
void handle_acid()
|
|
{
|
|
short i,r1 = 0;
|
|
bool some_acid = false;
|
|
|
|
for (i = 0; i < 6; i++)
|
|
if(univ.party[i].main_status == eMainStatus::ALIVE)
|
|
if(univ.party[i].status[eStatus::ACID] > 0)
|
|
some_acid = true;
|
|
|
|
if (some_acid == true) {
|
|
add_string_to_buf("Acid: ");
|
|
for (i = 0; i < 6; i++)
|
|
if(univ.party[i].main_status == eMainStatus::ALIVE)
|
|
if(univ.party[i].status[eStatus::ACID] > 0) {
|
|
r1 = get_ran(univ.party[i].status[eStatus::ACID],1,6);
|
|
damage_pc(i,r1,DAMAGE_MAGIC,eRace::UNKNOWN,0);
|
|
move_to_zero(univ.party[i].status[eStatus::ACID]);
|
|
}
|
|
if (overall_mode < MODE_COMBAT)
|
|
boom_space(univ.party.p_loc,overall_mode,3,r1,8);
|
|
}
|
|
}
|
|
|
|
bool no_pcs_left()
|
|
{
|
|
short i = 0;
|
|
|
|
while (i < 6) {
|
|
if(univ.party[i].main_status == eMainStatus::ALIVE)
|
|
return false;
|
|
i++;
|
|
}
|
|
return true;
|
|
|
|
}
|
|
|
|
bool hit_end_c_button()
|
|
{
|
|
bool end_ok = true;
|
|
|
|
if (which_combat_type == 0) {
|
|
end_ok = out_monst_all_dead();
|
|
}
|
|
for(int i = 0; i < 6; i++) {
|
|
if(univ.party[i].status[eStatus::FORCECAGE] > 0) {
|
|
add_string_to_buf(" Someone trapped.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (end_ok == true)
|
|
end_combat();
|
|
return end_ok;
|
|
}
|
|
|
|
bool out_monst_all_dead()
|
|
{
|
|
short i;
|
|
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if ((univ.town.monst[i].active > 0) && (univ.town.monst[i].attitude % 2 == 1)) {
|
|
//print_nums(5555,i,univ.town.monst[i].number);
|
|
//print_nums(5555,univ.town.monst[i].m_loc.x,univ.town.monst[i].m_loc.y);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void end_combat()
|
|
{
|
|
short i;
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
if(univ.party[i].main_status == eMainStatus::FLED)
|
|
univ.party[i].main_status = eMainStatus::ALIVE;
|
|
univ.party[i].status[eStatus::POISONED_WEAPON] = 0;
|
|
univ.party[i].status[eStatus::BLESS_CURSE] = 0;
|
|
univ.party[i].status[eStatus::HASTE_SLOW] = 0;
|
|
}
|
|
if (which_combat_type == 0) {
|
|
overall_mode = MODE_OUTDOORS;
|
|
}
|
|
combat_active_pc = 6;
|
|
current_pc = store_current_pc;
|
|
if(univ.party[current_pc].main_status != eMainStatus::ALIVE)
|
|
current_pc = first_active_pc();
|
|
put_item_screen(stat_window,0);
|
|
draw_buttons(0);
|
|
}
|
|
|
|
|
|
bool combat_cast_mage_spell()
|
|
{
|
|
short target,i,store_sp,bonus = 1,r1,store_sound = 0,store_m_type = 0,num_opp = 0;
|
|
eSpell spell_num;
|
|
char c_line[60];
|
|
cCreature *which_m;
|
|
cMonster get_monst;
|
|
|
|
if (univ.town.is_antimagic(univ.party[current_pc].combat_pos.x,univ.party[current_pc].combat_pos.y)) {
|
|
add_string_to_buf(" Not in antimagic field.");
|
|
return false;
|
|
}
|
|
store_sp = univ.party[current_pc].cur_sp;
|
|
if (univ.party[current_pc].cur_sp == 0)
|
|
add_string_to_buf("Cast: No spell points. ");
|
|
else if (univ.party[current_pc].skills[eSkill::MAGE_SPELLS] == 0)
|
|
add_string_to_buf("Cast: No mage skill. ");
|
|
else if (get_encumberance(current_pc) > 1) {
|
|
add_string_to_buf("Cast: Too encumbered. ");
|
|
take_ap(6);
|
|
give_help(40,0);
|
|
return true;
|
|
}
|
|
else {
|
|
|
|
|
|
if (spell_forced == false)
|
|
spell_num = pick_spell(current_pc,eSkill::MAGE_SPELLS);
|
|
else {
|
|
if(!repeat_cast_ok(eSkill::MAGE_SPELLS))
|
|
return false;
|
|
spell_num = univ.party[current_pc].last_cast[eSkill::MAGE_SPELLS];
|
|
}
|
|
|
|
if (spell_num == eSpell::SIMULACRUM) {
|
|
store_sum_monst = pick_trapped_monst();
|
|
if (store_sum_monst == 0)
|
|
return false;
|
|
get_monst = scenario.scen_monsters[store_sum_monst];
|
|
if (store_sp < get_monst.level) {
|
|
add_string_to_buf("Cast: Not enough spell points. ");
|
|
return false;
|
|
}
|
|
store_sum_monst_cost = get_monst.level;
|
|
}
|
|
|
|
bonus = stat_adj(current_pc,eSkill::INTELLIGENCE);
|
|
combat_posing_monster = current_working_monster = current_pc;
|
|
if(spell_num == eSpell::NONE) return false;
|
|
print_spell_cast(spell_num,eSkill::MAGE_SPELLS);
|
|
if ((*spell_num).refer == REFER_YES) {
|
|
take_ap(6);
|
|
draw_terrain(2);
|
|
do_mage_spell(current_pc,spell_num);
|
|
combat_posing_monster = current_working_monster = -1;
|
|
}
|
|
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 {
|
|
start_missile_anim();
|
|
take_ap(6);
|
|
draw_terrain(2);
|
|
switch (spell_num) {
|
|
case eSpell::SHOCKWAVE:
|
|
univ.party[current_pc].cur_sp -= (*spell_num).cost;
|
|
add_string_to_buf(" The ground shakes. ");
|
|
do_shockwave(univ.party[current_pc].combat_pos);
|
|
break;
|
|
|
|
case eSpell::HASTE_MINOR: case eSpell::HASTE: case eSpell::STRENGTH: case eSpell::ENVENOM: case eSpell::RESIST_MAGIC:
|
|
// target = select_pc(11,0);
|
|
target = store_spell_target;
|
|
if (target < 6) {
|
|
univ.party[current_pc].cur_sp -= (*spell_num).cost;
|
|
play_sound(4);
|
|
switch (spell_num) {
|
|
case eSpell::ENVENOM:
|
|
sprintf (c_line, " %s receives venom. ",
|
|
univ.party[target].name.c_str());
|
|
poison_weapon(target,3 + bonus,1);
|
|
store_m_type = 11;
|
|
break;
|
|
|
|
case eSpell::STRENGTH:
|
|
sprintf (c_line, " %s stronger. ",
|
|
univ.party[target].name.c_str());
|
|
univ.party[target].status[eStatus::BLESS_CURSE] = univ.party[target].status[eStatus::BLESS_CURSE] + 3;
|
|
store_m_type = 8;
|
|
break;
|
|
case eSpell::RESIST_MAGIC:
|
|
sprintf (c_line, " %s resistant. ",
|
|
univ.party[target].name.c_str());
|
|
univ.party[target].status[eStatus::MAGIC_RESISTANCE] = univ.party[target].status[eStatus::MAGIC_RESISTANCE] + 5 + bonus;
|
|
store_m_type = 15;
|
|
break;
|
|
|
|
default:
|
|
i = (spell_num == eSpell::HASTE_MINOR) ? 2 : max(2,univ.party[current_pc].level / 2 + bonus);
|
|
univ.party[target].status[eStatus::HASTE_SLOW] = min(8, univ.party[target].status[eStatus::HASTE_SLOW] + i);
|
|
sprintf (c_line, " %s hasted. ",
|
|
univ.party[target].name.c_str());
|
|
store_m_type = 8;
|
|
break;
|
|
}
|
|
add_string_to_buf(c_line);
|
|
add_missile(univ.party[target].combat_pos,store_m_type,0,0,0);
|
|
}
|
|
break;
|
|
|
|
case eSpell::HASTE_MAJOR: case eSpell::BLESS_MAJOR:
|
|
store_sound = 25;
|
|
univ.party[current_pc].cur_sp -= (*spell_num).cost;
|
|
|
|
|
|
for (i = 0; i < 6; i++)
|
|
if(univ.party[i].main_status == eMainStatus::ALIVE) {
|
|
univ.party[i].status[eStatus::HASTE_SLOW] = min(8, univ.party[i].status[eStatus::HASTE_SLOW] + ((spell_num == eSpell::HASTE_MAJOR) ? 1 + univ.party[current_pc].level / 8 + bonus : 3 + bonus));
|
|
if (spell_num == eSpell::BLESS_MAJOR) {
|
|
poison_weapon(i,2,1);
|
|
univ.party[i].status[eStatus::BLESS_CURSE] += 4;
|
|
add_missile(univ.party[i].combat_pos,14,0,0,0);
|
|
}
|
|
else add_missile(univ.party[i].combat_pos,8,0,0,0);
|
|
}
|
|
//play_sound(4);
|
|
if (spell_num == eSpell::HASTE_MAJOR)
|
|
sprintf (c_line, " Party hasted. ");
|
|
else
|
|
sprintf (c_line, " Party blessed! ");
|
|
add_string_to_buf(c_line);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case eSpell::SLOW_GROUP: case eSpell::FEAR_GROUP: case eSpell::PARALYSIS_MASS: // affect monsters in area spells
|
|
univ.party[current_pc].cur_sp -= (*spell_num).cost;
|
|
store_sound = 25;
|
|
if (spell_num == eSpell::FEAR_GROUP)
|
|
store_sound = 54;
|
|
switch (spell_num) {
|
|
case eSpell::SLOW_GROUP: sprintf (c_line, " Enemy slowed: "); break;
|
|
case eSpell::RAVAGE_ENEMIES: sprintf (c_line, " Enemy ravaged: ");break;
|
|
case eSpell::FEAR_GROUP: sprintf (c_line, " Enemy scared: ");break;
|
|
case eSpell::PARALYSIS_MASS: sprintf (c_line, " Enemy paralyzed: ");break;
|
|
}
|
|
add_string_to_buf(c_line);
|
|
for (i = 0; i < univ.town->max_monst(); i++) {
|
|
if ((univ.town.monst[i].active != 0) && (univ.town.monst[i].attitude % 2 == 1)
|
|
&& (dist(univ.party[current_pc].combat_pos,univ.town.monst[i].cur_loc) <= (*spell_num).range)
|
|
&& (can_see_light(univ.party[current_pc].combat_pos,univ.town.monst[i].cur_loc,sight_obscurity) < 5)) {
|
|
which_m = &univ.town.monst[i];
|
|
switch (spell_num) {
|
|
case eSpell::FEAR_GROUP:
|
|
r1 = get_ran(univ.party[current_pc].level / 3,1,8);
|
|
scare_monst(which_m,r1);
|
|
store_m_type = 10;
|
|
break;
|
|
case eSpell::SLOW_GROUP: case eSpell::RAVAGE_ENEMIES:
|
|
slow_monst(which_m,5 + bonus);
|
|
if (spell_num == eSpell::RAVAGE_ENEMIES)
|
|
curse_monst(which_m,3 + bonus);
|
|
store_m_type = 8;
|
|
break;
|
|
case eSpell::PARALYSIS_MASS:
|
|
charm_monst(which_m,15,eStatus::PARALYZED,1000);
|
|
store_m_type = 15;
|
|
break;
|
|
}
|
|
num_opp++;
|
|
add_missile(univ.town.monst[i].cur_loc,store_m_type,0,
|
|
14 * (which_m->x_width - 1),18 * (which_m->y_width - 1));
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
if (num_opp < 10)
|
|
do_missile_anim((num_opp < 5) ? 50 : 25,univ.party[current_pc].combat_pos,store_sound);
|
|
else play_sound(store_sound);
|
|
end_missile_anim();
|
|
put_pc_screen();
|
|
}
|
|
combat_posing_monster = current_working_monster = -1;
|
|
// Did anything actually get cast?
|
|
if (store_sp == univ.party[current_pc].cur_sp)
|
|
return false;
|
|
else return true;
|
|
}
|
|
|
|
|
|
bool combat_cast_priest_spell()
|
|
{
|
|
short target,i,store_sp,bonus,store_sound = 0,store_m_type = 0,num_opp = 0;
|
|
eSpell spell_num;
|
|
char c_line[60];
|
|
cCreature *which_m;
|
|
effect_pat_type protect_pat = {{
|
|
{0,4,4,4,4,4,4,4,0},
|
|
{4,8,8,8,8,8,8,8,4},
|
|
{4,8,9,9,9,9,9,8,4},
|
|
{4,8,9,6,6,6,9,8,4},
|
|
{4,8,9,6,6,6,9,8,4},
|
|
{4,8,9,6,6,6,9,8,4},
|
|
{4,8,9,9,9,9,9,8,4},
|
|
{4,8,8,8,8,8,8,8,4},
|
|
{0,4,4,4,4,4,4,4,0}}};
|
|
|
|
if (univ.town.is_antimagic(univ.party[current_pc].combat_pos.x,univ.party[current_pc].combat_pos.y)) {
|
|
add_string_to_buf(" Not in antimagic field.");
|
|
return false;
|
|
}
|
|
if (spell_forced == false)
|
|
spell_num = pick_spell(current_pc,eSkill::PRIEST_SPELLS);
|
|
else {
|
|
if(!repeat_cast_ok(eSkill::PRIEST_SPELLS))
|
|
return false;
|
|
spell_num = univ.party[current_pc].last_cast[eSkill::PRIEST_SPELLS];
|
|
}
|
|
|
|
store_sp = univ.party[current_pc].cur_sp;
|
|
if(univ.party[current_pc].cur_sp == 0) {
|
|
add_string_to_buf("Cast: No spell points. ");
|
|
return false;
|
|
} else if(univ.party[current_pc].skills[eSkill::PRIEST_SPELLS] == 0) {
|
|
add_string_to_buf("Cast: No priest skill. ");
|
|
return false;
|
|
}
|
|
|
|
if(spell_num == eSpell::NONE) return false;
|
|
bonus = stat_adj(current_pc,eSkill::INTELLIGENCE);
|
|
|
|
combat_posing_monster = current_working_monster = current_pc;
|
|
|
|
if (univ.party[current_pc].cur_sp == 0)
|
|
add_string_to_buf("Cast: No spell points. ");
|
|
else 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(current_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 {
|
|
start_missile_anim();
|
|
take_ap(5);
|
|
draw_terrain(2);
|
|
switch (spell_num) {
|
|
case eSpell::BLESS_MINOR: case eSpell::BLESS:
|
|
// target = select_pc(11,0);
|
|
target = store_spell_target;
|
|
if (target < 6) {
|
|
store_sound = 4;
|
|
univ.party[current_pc].cur_sp -= (*spell_num).cost;
|
|
univ.party[target].status[eStatus::BLESS_CURSE] += (spell_num == eSpell::BLESS_MINOR) ? 2 :
|
|
max(2,(univ.party[current_pc].level * 3) / 4 + 1 + bonus);
|
|
sprintf ((char *) c_line, " %s blessed. ",
|
|
(char *) univ.party[target].name.c_str());
|
|
add_string_to_buf((char *) c_line);
|
|
add_missile(univ.party[target].combat_pos,8,0,0,0);
|
|
}
|
|
break;
|
|
|
|
case eSpell::BLESS_PARTY:
|
|
univ.party[current_pc].cur_sp -= (*spell_num).cost;
|
|
for (i = 0; i < 6; i++)
|
|
if(univ.party[i].main_status == eMainStatus::ALIVE) {
|
|
univ.party[i].status[eStatus::BLESS_CURSE] += univ.party[current_pc].level / 3;
|
|
add_missile(univ.party[i].combat_pos,8,0,0,0);
|
|
}
|
|
sprintf ((char *) c_line, " Party blessed. ");
|
|
add_string_to_buf((char *) c_line);
|
|
store_sound = 4;
|
|
break;
|
|
|
|
case eSpell::AVATAR:
|
|
univ.party[current_pc].cur_sp -= (*spell_num).cost;
|
|
sprintf ((char *) c_line, " %s is an avatar! ",
|
|
(char *) univ.party[current_pc].name.c_str());
|
|
add_string_to_buf((char *) c_line);
|
|
heal_pc(current_pc,200);
|
|
cure_pc(current_pc,8);
|
|
univ.party[current_pc].status[eStatus::BLESS_CURSE] = 8;
|
|
univ.party[current_pc].status[eStatus::HASTE_SLOW] = 8;
|
|
univ.party[current_pc].status[eStatus::INVULNERABLE] = 3;
|
|
univ.party[current_pc].status[eStatus::MAGIC_RESISTANCE] = 8;
|
|
univ.party[current_pc].status[eStatus::WEBS] = 0;
|
|
univ.party[current_pc].status[eStatus::DISEASE] = 0;
|
|
univ.party[current_pc].status[eStatus::DUMB] = 0;
|
|
univ.party[current_pc].status[eStatus::MARTYRS_SHIELD] = 8;
|
|
break;
|
|
|
|
case eSpell::CURSE_ALL: case eSpell::CHARM_MASS: case eSpell::PESTILENCE:
|
|
univ.party[current_pc].cur_sp -= (*spell_num).cost;
|
|
store_sound = 24;
|
|
for (i = 0; i < univ.town->max_monst(); i++) {
|
|
if ((univ.town.monst[i].active != 0) &&(univ.town.monst[i].attitude % 2 == 1) &&
|
|
(dist(univ.party[current_pc].combat_pos,univ.town.monst[i].cur_loc) <= (*spell_num).range)) {
|
|
which_m = &univ.town.monst[i];
|
|
switch (spell_num) {
|
|
case eSpell::CURSE_ALL:
|
|
curse_monst(which_m,3 + bonus);
|
|
store_m_type = 8;
|
|
break;
|
|
case eSpell::CHARM_MASS:
|
|
charm_monst(which_m,28 - bonus,eStatus::CHARM,0);
|
|
store_m_type = 14;
|
|
break;
|
|
case eSpell::PESTILENCE:
|
|
disease_monst(which_m,3 + bonus);
|
|
store_m_type = 0;
|
|
break;
|
|
}
|
|
num_opp++;
|
|
add_missile(univ.town.monst[i].cur_loc,store_m_type,0,
|
|
14 * (which_m->x_width - 1),18 * (which_m->y_width - 1));
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case eSpell::PROTECTIVE_CIRCLE:
|
|
univ.party[current_pc].cur_sp -= (*spell_num).cost;
|
|
play_sound(24);
|
|
add_string_to_buf(" Protective field created.");
|
|
place_spell_pattern(protect_pat,univ.party[current_pc].combat_pos,6);
|
|
break;
|
|
}
|
|
}
|
|
if (num_opp < 10)
|
|
do_missile_anim((num_opp < 5) ? 50 : 25,univ.party[current_pc].combat_pos,store_sound);
|
|
else play_sound(store_sound);
|
|
end_missile_anim();
|
|
put_pc_screen();
|
|
}
|
|
|
|
combat_posing_monster = current_working_monster = -1;
|
|
// Did anything actually get cast?
|
|
if (store_sp == univ.party[current_pc].cur_sp)
|
|
return false;
|
|
else return true;
|
|
}
|
|
|
|
void start_spell_targeting(eSpell num, bool freebie)
|
|
{
|
|
|
|
// First, remember what spell was cast.
|
|
spell_being_cast = num;
|
|
spell_freebie = freebie;
|
|
|
|
sprintf ((char *) create_line, " Target spell. ");
|
|
add_string_to_buf((char *) create_line);
|
|
if(isMage(num))
|
|
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_pat = single;
|
|
|
|
switch (num) { // Different spells have different messages and diff. target shapes
|
|
case eSpell::CLOUD_SLEEP:
|
|
current_pat = small_square;
|
|
break;
|
|
case eSpell::DISPEL_SQUARE: case eSpell::FIREBALL: case eSpell::CLOUD_STINK:
|
|
case eSpell::FLAMESTRIKE: case eSpell::FORCEFIELD:
|
|
current_pat = square;
|
|
break;
|
|
case eSpell::CONFLAGRATION: case eSpell::FIRESTORM: case eSpell::SHOCKSTORM: case eSpell::WEB:
|
|
case eSpell::ANTIMAGIC: case eSpell::WALL_ICE_BALL: case eSpell::CLOUD_SLEEP_LARGE:
|
|
case eSpell::DIVINE_THUD: case eSpell::DISPEL_SPHERE:
|
|
current_pat = rad2;
|
|
break;
|
|
case eSpell::PESTILENCE: case eSpell::GOO_BOMB: case eSpell::FOUL_VAPOR:
|
|
current_pat = rad3;
|
|
break;
|
|
case eSpell::WALL_FORCE: case eSpell::WALL_ICE: case eSpell::WALL_BLADES:
|
|
add_string_to_buf(" (Hit space to rotate.)");
|
|
force_wall_position = 0;
|
|
current_pat = field[0];
|
|
break;
|
|
}
|
|
}
|
|
|
|
void start_fancy_spell_targeting(eSpell num, bool freebie)
|
|
{
|
|
short i;
|
|
location null_loc(120,0);
|
|
|
|
// First, remember what spell was cast.
|
|
spell_being_cast = num;
|
|
spell_freebie = freebie;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
spell_targets[i] = null_loc;
|
|
sprintf (create_line, " Target spell. ");
|
|
if(isMage(num))
|
|
add_string_to_buf(" (Hit 'm' to cancel.)");
|
|
else add_string_to_buf(" (Hit 'p' to cancel.)");
|
|
add_string_to_buf(" (Hit space to cast.)");
|
|
add_string_to_buf(create_line);
|
|
overall_mode = MODE_FANCY_TARGET;
|
|
current_pat = single;
|
|
current_spell_range = (*num).range;
|
|
|
|
switch (num) { // Assign special targeting shapes and number of targets
|
|
case eSpell::SMITE:
|
|
num_targets_left = minmax(1,8,univ.party[current_pc].level / 4 + stat_adj(current_pc,eSkill::INTELLIGENCE) / 2);
|
|
break;
|
|
case eSpell::STICKS_TO_SNAKES:
|
|
num_targets_left = univ.party[current_pc].level / 5 + stat_adj(current_pc,eSkill::INTELLIGENCE) / 2;
|
|
break;
|
|
case eSpell::SUMMON_HOST:
|
|
num_targets_left = 5;
|
|
break;
|
|
case eSpell::ARROWS_FLAME:
|
|
num_targets_left = univ.party[current_pc].level / 4 + stat_adj(current_pc,eSkill::INTELLIGENCE) / 2;
|
|
break;
|
|
case eSpell::ARROWS_VENOM:
|
|
num_targets_left = univ.party[current_pc].level / 5 + stat_adj(current_pc,eSkill::INTELLIGENCE) / 2;
|
|
break;
|
|
case eSpell::ARROWS_DEATH: case eSpell::PARALYZE:
|
|
num_targets_left = univ.party[current_pc].level / 8 + stat_adj(current_pc,eSkill::INTELLIGENCE) / 3;
|
|
break;
|
|
case eSpell::SPRAY_FIELDS:
|
|
num_targets_left = univ.party[current_pc].level / 5 + stat_adj(current_pc,eSkill::INTELLIGENCE) / 2;
|
|
current_pat = t;
|
|
break;
|
|
case eSpell::SUMMON_WEAK:
|
|
num_targets_left = minmax(1,7,univ.party[current_pc].level / 4 + stat_adj(current_pc,eSkill::INTELLIGENCE) / 2);
|
|
break;
|
|
case eSpell::SUMMON:
|
|
num_targets_left = minmax(1,6,univ.party[current_pc].level / 6 + stat_adj(current_pc,eSkill::INTELLIGENCE) / 2);
|
|
break;
|
|
case eSpell::SUMMON_MAJOR:
|
|
num_targets_left = minmax(1,5,univ.party[current_pc].level / 8 + stat_adj(current_pc,eSkill::INTELLIGENCE) / 2);
|
|
break;
|
|
}
|
|
|
|
num_targets_left = minmax(1,8,num_targets_left);
|
|
}
|
|
|
|
void spell_cast_hit_return()
|
|
{
|
|
|
|
if (force_wall_position < 10) {
|
|
force_wall_position = (force_wall_position + 1) % 8;
|
|
current_pat = field[force_wall_position];
|
|
}
|
|
}
|
|
|
|
static void process_force_cage(location loc, short i) {
|
|
if(i >= 100) {
|
|
short m = i - 100;
|
|
cCreature& which_m = univ.town.monst[m];
|
|
if(which_m.attitude % 2 == 1 && get_ran(1,1,100) < which_m.mu * 10 + which_m.cl * 4 + 5) {
|
|
play_sound(60);
|
|
monst_spell_note(m, 50);
|
|
univ.town.set_force_cage(loc.x,loc.y,false);
|
|
which_m.status[eStatus::FORCECAGE] = 0;
|
|
} else which_m.status[eStatus::FORCECAGE] = 8;
|
|
} else if(i < 0) {
|
|
if(get_ran(1,1,100) < 35)
|
|
univ.town.set_force_cage(loc.x,loc.y,false);
|
|
} else if(i < 6) {
|
|
cPlayer& who = univ.party[i];
|
|
// We want to make sure everyone has a chance of eventually breaking a cage, because it never ends on its own,
|
|
// and because being trapped unconditionally prevents you from ending combat mode.
|
|
short bonus = 5 + who.skills[eSkill::MAGE_LORE];
|
|
if(get_ran(1,1,100) < who.skills[eSkill::MAGE_SPELLS]*10 + who.skills[eSkill::PRIEST_SPELLS]*4 + bonus) {
|
|
play_sound(60);
|
|
add_string_to_buf(" " + who.name + " breaks force cage.");
|
|
univ.town.set_force_cage(loc.x,loc.y,false);
|
|
who.status[eStatus::FORCECAGE] = 0;
|
|
} else who.status[eStatus::FORCECAGE] = 8;
|
|
}
|
|
}
|
|
|
|
void process_fields()
|
|
{
|
|
short i,j,k,r1;
|
|
location loc;
|
|
char qf[64][64];
|
|
rectangle r;
|
|
|
|
if (is_out())
|
|
return;
|
|
|
|
if(univ.town.quickfire_present) {
|
|
r = univ.town->in_town_rect;
|
|
for (i = 0; i < univ.town->max_dim(); i++)
|
|
for (j = 0; j < univ.town->max_dim(); j++)
|
|
qf[i][j] = (univ.town.is_quickfire(i,j)) ? 2 : 0;
|
|
for (k = 0; k < ((is_combat()) ? 4 : 1); k++) {
|
|
for (i = r.left + 1; i < r.right ; i++)
|
|
for (j = r.top + 1; j < r.bottom ; j++)
|
|
if (univ.town.is_quickfire(i,j) > 0) {
|
|
r1 = get_ran(1,1,8);
|
|
if (r1 != 1) {
|
|
qf[i - 1][j] = 1;
|
|
qf[i + 1][j] = 1;
|
|
qf[i][j + 1] = 1;
|
|
qf[i][j - 1] = 1;
|
|
}
|
|
}
|
|
for (i = r.left + 1; i < r.right ; i++)
|
|
for (j = r.top + 1; j < r.bottom ; j++)
|
|
if (qf[i][j] > 0) {
|
|
univ.town.set_quickfire(i,j,true);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if (univ.town.monst[i].active > 0)
|
|
monst_inflict_fields(i);
|
|
|
|
// First fry PCs, then call to handle damage to monsters
|
|
processing_fields = true; // this, in hit_space, makes damage considered to come from whole party
|
|
for (i = 0; i < univ.town->max_dim(); i++)
|
|
for(j = 0; j < univ.town->max_dim(); j++) {
|
|
if (univ.town.is_force_wall(i,j)) {
|
|
r1 = get_ran(3,1,6);
|
|
loc.x = i; loc.y = j;
|
|
hit_pcs_in_space(loc,r1,DAMAGE_MAGIC,1,1);
|
|
r1 = get_ran(1,1,6);
|
|
if (r1 == 2)
|
|
univ.town.set_force_wall(i,j,false);
|
|
}
|
|
if (univ.town.is_fire_wall(i,j)) {
|
|
loc.x = i; loc.y = j;
|
|
r1 = get_ran(2,1,6) + 1;
|
|
hit_pcs_in_space(loc,r1,DAMAGE_FIRE,1,1);
|
|
r1 = get_ran(1,1,4);
|
|
if (r1 == 2)
|
|
univ.town.set_fire_wall(i,j,false);
|
|
}
|
|
if (univ.town.is_antimagic(i,j)) {
|
|
r1 = get_ran(1,1,8);
|
|
if (r1 == 2)
|
|
univ.town.set_antimagic(i,j,false);
|
|
}
|
|
if (univ.town.is_scloud(i,j)) {
|
|
r1 = get_ran(1,1,4);
|
|
if (r1 == 2)
|
|
univ.town.set_scloud(i,j,false);
|
|
else {
|
|
scloud_space(i,j);
|
|
}
|
|
}
|
|
if (univ.town.is_sleep_cloud(i,j)) {
|
|
r1 = get_ran(1,1,4);
|
|
if (r1 == 2)
|
|
univ.town.set_sleep_cloud(i,j,false);
|
|
else {
|
|
sleep_cloud_space(i,j);
|
|
}
|
|
}
|
|
if (univ.town.is_ice_wall(i,j)) {
|
|
loc.x = i; loc.y = j;
|
|
r1 = get_ran(3,1,6);
|
|
hit_pcs_in_space(loc,r1,DAMAGE_COLD,1,1);
|
|
r1 = get_ran(1,1,6);
|
|
if (r1 == 1)
|
|
univ.town.set_ice_wall(i,j,false);
|
|
}
|
|
if (univ.town.is_blade_wall(i,j)) {
|
|
loc.x = i; loc.y = j;
|
|
r1 = get_ran(6,1,8);
|
|
hit_pcs_in_space(loc,r1,DAMAGE_WEAPON,1,1);
|
|
r1 = get_ran(1,1,5);
|
|
if (r1 == 1)
|
|
univ.town.set_blade_wall(i,j,false);
|
|
}
|
|
if(univ.town.is_force_cage(i,j)) {
|
|
loc.x = i; loc.y = j;
|
|
short m = monst_there(loc);
|
|
if(m == 90) {
|
|
short pc = pc_there(loc);
|
|
if(pc == 6) process_force_cage(loc, -1);
|
|
else process_force_cage(loc, pc);
|
|
} else process_force_cage(loc, 100 + m);
|
|
}
|
|
}
|
|
|
|
processing_fields = false;
|
|
monsters_going = true; // this changes who the damage is considered to come from in hit_space
|
|
|
|
if(univ.town.quickfire_present) {
|
|
for (i = 0; i < univ.town->max_dim(); i++)
|
|
for (j = 0; j < univ.town->max_dim(); j++)
|
|
if (univ.town.is_quickfire(i,j)) {
|
|
loc.x = i; loc.y = j;
|
|
r1 = get_ran(2,1,8);
|
|
hit_pcs_in_space(loc,r1,DAMAGE_FIRE,1,1);
|
|
}
|
|
}
|
|
|
|
monsters_going = false;
|
|
}
|
|
|
|
void scloud_space(short m,short n)
|
|
{
|
|
location target;
|
|
//cPopulation::cCreature;
|
|
short i;
|
|
|
|
target.x = (char) m;
|
|
target.y = (char) n;
|
|
|
|
univ.town.set_scloud(m,n,true);
|
|
|
|
if (overall_mode >= MODE_COMBAT)
|
|
for (i = 0; i < 6; i++)
|
|
if(univ.party[i].main_status == eMainStatus::ALIVE)
|
|
if (univ.party[i].combat_pos == target) {
|
|
curse_pc(i,get_ran(1,1,2));
|
|
}
|
|
if (overall_mode < MODE_COMBAT)
|
|
if (target == univ.town.p_loc) {
|
|
for (i = 0; i < 6; i++)
|
|
if(univ.party[i].main_status == eMainStatus::ALIVE)
|
|
curse_pc(i,get_ran(1,1,2));
|
|
}
|
|
}
|
|
|
|
void web_space(short m,short n)
|
|
{
|
|
location target;
|
|
short i;
|
|
|
|
target.x = (char) m;
|
|
target.y = (char) n;
|
|
|
|
univ.town.set_web(m,n,true);
|
|
|
|
if (overall_mode >= MODE_COMBAT)
|
|
for (i = 0; i < 6; i++)
|
|
if(univ.party[i].main_status == eMainStatus::ALIVE)
|
|
if (univ.party[i].combat_pos == target) {
|
|
web_pc(i,3);
|
|
}
|
|
if (overall_mode < MODE_COMBAT)
|
|
if (target == univ.town.p_loc) {
|
|
for (i = 0; i < 6; i++)
|
|
web_pc(i,3);
|
|
}
|
|
}
|
|
void sleep_cloud_space(short m,short n)
|
|
{
|
|
location target;
|
|
short i;
|
|
|
|
target.x = (char) m;
|
|
target.y = (char) n;
|
|
|
|
univ.town.set_sleep_cloud(m,n,true);
|
|
|
|
if (overall_mode >= MODE_COMBAT)
|
|
for (i = 0; i < 6; i++)
|
|
if(univ.party[i].main_status == eMainStatus::ALIVE)
|
|
if (univ.party[i].combat_pos == target) {
|
|
sleep_pc(i,3,eStatus::ASLEEP,0);
|
|
}
|
|
if (overall_mode < MODE_COMBAT)
|
|
if (target == univ.town.p_loc) {
|
|
for (i = 0; i < 6; i++)
|
|
sleep_pc(i,3,eStatus::ASLEEP,0);
|
|
}
|
|
}
|
|
|
|
|
|
void take_m_ap(short num,cCreature *monst)
|
|
{
|
|
monst->ap = max(0,monst->ap - num);
|
|
}
|
|
|
|
void add_new_action(short pc_num)
|
|
{
|
|
if (pc_num < 6)
|
|
univ.party[pc_num].ap++;
|
|
}
|
|
|
|
short get_monst_sound(cCreature *attacker,short which_att) {
|
|
short type,strength;
|
|
|
|
type = attacker->a[which_att].type;
|
|
strength = attacker->a[which_att];
|
|
|
|
switch (type) {
|
|
case 3:
|
|
return 11;
|
|
break;
|
|
case 4:
|
|
return 4;
|
|
break;
|
|
case 1:
|
|
return 9;
|
|
break;
|
|
case 2:
|
|
return 10;
|
|
break;
|
|
|
|
default:
|
|
if(attacker->m_type == eRace::HUMAN) {
|
|
if (strength > 9)
|
|
return 3;
|
|
else return 2;
|
|
}
|
|
if(attacker->m_type == eRace::HUMAN || attacker->m_type == eRace::IMPORTANT || attacker->m_type == eRace::GIANT) {
|
|
return 2;
|
|
}
|
|
if(attacker->m_type == eRace::MAGE)
|
|
return 1;
|
|
if(attacker->m_type == eRace::PRIEST)
|
|
return 4;
|
|
return 0;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|