(PC Editor and Scenario Editor are unaffected by this commit.) Things removed: - All references to Carbon and QuickDraw are gone. - No more Resource Manager; the game no longer relies on old resource files at all - The old dialog framework (from dlogtool.c, functions usually prefixed with cd_) is no longer used. - Files that weren't being compiled - Boost libraries that are now in the C++ library (function, shared_ptr) - Obsolete build settings Replacement dependencies: - Boost Filesystem replaces references to things like FSSpec - SFML replaces all the QuickDraw code and most window management - Cocoa replaces AppleEvent management, menu management, and some window management - I wrote a resource manager interface to mimick the important aspects of the behaviour of the Mac Resource Manager - I had to rewrite some functions that QuickDraw provided natively, such as clipping regions; not all of these are tested Things added: - Every referenced dialog has been converted from a DITL resource into the new XML-based dialog format. - All referenced STR# resources have been extracted into simple text files - Now compiles against Mac OSX 10.7 SDK and libc++ - The first item in the Help menu opens the docs on Sylae's website - It seems all the constants for soundtool vanished somewhere, so I added them back from the original Mac source Other changes: - Updated to XCode 4(?) project format - Added the xcschemes created by XCode 4; I'm not sure how important these are, but they seem like they might be important - Added document on converting dialogs to the XML format. - Make string formatting mismatches into errors instead of warnings - Disable error limit - Graphics sheets that previously used masking now have alpha transparency - Converted all graphics sheets to 8-bit PNG - Trimmed white border from intro image - Converted dialogs in the resource file have their resource name set to the name of the XML file of the converted version - Referenced string resources in the resource file have their resource name set to the name of the text file of the extracted version - Add the black-and-white patterns from the PAT resources to the pixpats.png; at least one of them is used somewhere in the game - Recreated the menu.xib as a Cocoa xib file instead of a Carbon xib file - Disable GNU C++ extensions; maybe this'll make it easier to compile with cl.exe later - Add marks to the enormous handle_action function to make it easier to navigate\ - A build step to validate the XML dialogs using xmllint (doesn't quite work properly yet but does at least catch dialogs that are not well-formed)- Fix a lot of warnings about assigning string constants to non-const char pointers - Fixed the file and application icons (which had somehow become corrupted at some point) - Lots of additional functions in the custom location and rectangle classes, including implicit conversion to and from SFML rects and vectors; also they now store coordinates as int instead of char - A new enum for encounter note types - Much tweaking of the encounter note recording mechanisms - To ease porting, I added a simple function that converts from classic Mac ticks (about 1/60 of a second) to the SFML time type - Python script to convert STR# resources to txt files, replacing newlines with vertical bars - Extracted the Mac font (Dungeon Bold) from the resource file and also added the Windows font (MaidenWord) XML Dialog Framework changes: - Remove the static initialization object in favour of manually calling cDialog::init() - {set,get}Format() no longer used for colour; there's a dedicated {set,get}Colour() instead - draw() methods unprotected in the control classes so that controls can be drawn in the main window - There's no longer a friend relationship between the dialogs and the controls - Fixed buttons duplicating the "depressed" boolean - Buttons now properly offset the label for tiny and push buttons, and for LEDs - Buttons no longer assume that either none or both of "width" and "height" are given in the XML - Add {get,set}BtnType() to cButton - cLedGroup now overrides handleClick(), which has also been made virtual; this was necessary for LEDs within a group to properly hilite while being clicked - Add addChoice() to cLedGroup to insert additional LED choices - Moved the key enums and cKey to a separate file - Add a method to get a control's attached key - Add methods to get and set a control's rect and position - Controls can now directly take a window as a parent rather than a dialog - Add addLabelFor() method to cDialog which adds a static text control as a label for another control - Remove hack for storing a dialog result of arbitrary type in favour of boost::any - Add method to get default text colour for a dialog - Add method to get a dialog's rect - Add method to add the same event handler to multiple controls in a dialog - Add concept of default button to dialogs - Add enum for text field type (currently either number or text) - Implement the text field without use of native controls, including somewhat decent text input and a flashing insertion point - cPict no longer stores references to every sheet; it fetches them as needed from the resource manager - The many draw functions in cPict are no longer static, since they need to access the window containing the pict (before they could only be static due to QuickDraw's global state) - Add setPict() without a type argument to change the graphic without changing the type, which is a common operation - Add a scrollbar control; the specifics aren't implemented yet - Change signature of the record callback for cStrDlog; it will no longer be passed the strings - Publicize the no-button constructor of cChoiceDlog; it'll assume "okay" is the only button - Add operator-> to cPictChoice for accessing the underlying dialog - Add constructor to cPictChoice that takes a starting and ending pic num - Remove err parameter from giveError - Many more keys handled, plus support for catching copy, paste, cut, and select all keyboard shortcuts - Text input fields take priority, overriding any other keyboard shortcuts in the dialog, but they never catch help (F1), escape, or enter - Some changes to the format itself: -> keys go in the "def-key" attribute, but modifiers go in "key-mod" -> "clickable" is no longer a recognized attribute -> "title" is now a recognized text size (18pt, even larger than "large" at 12pt) -> "defbtn" attribute on the root element
1423 lines
43 KiB
C++
1423 lines
43 KiB
C++
|
|
#include <cstdio>
|
|
|
|
//#include "item.h"
|
|
|
|
#include "boe.global.h"
|
|
|
|
#include "classes.h"
|
|
#include "boe.locutils.h"
|
|
#include "boe.fields.h"
|
|
#include "boe.monster.h"
|
|
#include "boe.text.h"
|
|
#include "boe.specials.h"
|
|
#include "boe.items.h"
|
|
#include "soundtool.h"
|
|
#include "boe.graphics.h"
|
|
#include "boe.newgraph.h"
|
|
#include "boe.main.h"
|
|
#include "mathutil.h"
|
|
#include "graphtool.h"
|
|
|
|
//extern current_town_type univ.town;
|
|
//extern party_record_type party;
|
|
extern eGameMode overall_mode;
|
|
//extern cOutdoors univ.out.outdoors[2][2];
|
|
extern ter_num_t combat_terrain[64][64];//,out[96][96];
|
|
extern short which_combat_type;
|
|
//extern big_tr_type t_d;
|
|
//extern short monst_target[60]; // 0-5 target that pc 6 - no target 100 + x - target monster x
|
|
extern short spell_caster, missile_firer,current_monst_tactic;
|
|
extern short hit_chance[21];
|
|
//extern unsigned char univ.out.misc_i[64][64];
|
|
//extern location monster_targs[60];
|
|
|
|
extern location pc_pos[6],center;
|
|
extern short boom_gr[8],futzing;
|
|
extern bool processing_fields,monsters_going;
|
|
//extern town_item_list univ.town;
|
|
extern cScenario scenario;
|
|
extern cUniverse univ;
|
|
|
|
|
|
short charm_odds[20] = {90,90,85,80,78, 75,73,60,40,30, 20,10,4,1,0, 0,0,0,0,0};
|
|
|
|
//cCreature null_start_type; = {0,0,loc(80,80),1,0,0,0,0,0,0,0, 0,-1,-1,-1};
|
|
|
|
////
|
|
//cMonster return_monster_template(m_num_t store)
|
|
//{
|
|
// cMonster monst;
|
|
// short m_num,i;
|
|
//
|
|
// m_num = store;
|
|
// monst = scenario.scen_monsters[store];
|
|
// if (monst.spec_skill == 11)
|
|
// monst.picture_num = 0;
|
|
//
|
|
// monst.m_num = m_num;
|
|
// monst.health = (PSD[SDF_EASY_MODE] == 0) ? monst.health : monst.health / 2;
|
|
//
|
|
// // now adjust for difficulty
|
|
// monst.health = monst.health * difficulty_adjust();
|
|
//
|
|
// monst.m_health = monst.health; // in scenario file, health is stored in health field
|
|
// monst.max_mp = 0;
|
|
// monst.mp = monst.max_mp;
|
|
//
|
|
// monst.ap = 0;
|
|
//
|
|
// if ((monst.mu > 0) || (monst.cl > 0))
|
|
// monst.max_mp = monst.mp = 12 * monst.level;
|
|
//
|
|
// monst.m_morale = 10 * monst.level;
|
|
// if (monst.level >= 20)
|
|
// monst.m_morale += 10 * (monst.level - 20);
|
|
//
|
|
// monst.morale = monst.m_morale;
|
|
// monst.direction = 0;
|
|
// for (i = 0; i < 15; i++)
|
|
// monst.status[i] = 0;
|
|
//
|
|
// return monst;
|
|
//}
|
|
|
|
short out_enc_lev_tot(short which)
|
|
{
|
|
short count = 0,i;
|
|
short num[7] = {22,8,4,4,3,2,1};
|
|
|
|
if (univ.party.out_c[which].what_monst.cant_flee == true)
|
|
return 10000;
|
|
|
|
for (i = 0; i < 7; i++)
|
|
if (univ.party.out_c[which].what_monst.monst[i] != 0)
|
|
count += scenario.scen_monsters[univ.party.out_c[which].what_monst.monst[i]].level * num[i];
|
|
return count;
|
|
}
|
|
|
|
void create_wand_monst()
|
|
{
|
|
short r1,r2,r3,i = 0,num_tries = 0;
|
|
location p_loc;
|
|
|
|
r1 = get_ran(1,0,3);
|
|
if (overall_mode == MODE_OUTDOORS)
|
|
if (!univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].wandering[r1].isNull()) {
|
|
r2 = get_ran(1,0,3);
|
|
while ((point_onscreen(univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].wandering_locs[r2],
|
|
global_to_local(univ.party.p_loc))) && (num_tries++ < 100))
|
|
r2 = get_ran(1,0,3);
|
|
if (!is_blocked(univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].wandering_locs[r2]))
|
|
place_outd_wand_monst(univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].wandering_locs[r2],
|
|
univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].wandering[r1],0);
|
|
}
|
|
else // won't place wandering is more than 50 monsters
|
|
if ((!univ.town->wandering[r1].isNull()) && (univ.town.countMonsters() <= 50)
|
|
&& (univ.party.m_killed[univ.town.num] < univ.town->max_num_monst)) {
|
|
r2 = get_ran(1,0,3);
|
|
while (point_onscreen(univ.town->wandering_locs[r2],univ.town.p_loc) &&
|
|
!loc_off_act_area(univ.town->wandering_locs[r2]) && (num_tries++ < 100))
|
|
r2 = get_ran(1,0,3);
|
|
for (i = 0; i < 4; i++) {
|
|
if (univ.town->wandering[r1].monst[i] != 0) { // place a monster
|
|
p_loc = univ.town->wandering_locs[r2];
|
|
p_loc.x += get_ran(1,0,4) - 2;
|
|
p_loc.y += get_ran(1,0,4) - 2;
|
|
if (!is_blocked(p_loc))
|
|
place_monster(univ.town->wandering[r1].monst[i],p_loc);
|
|
}
|
|
p_loc = univ.town->wandering_locs[r2];
|
|
p_loc.x += get_ran(1,0,4) - 2;
|
|
p_loc.y += get_ran(1,0,4) - 2;
|
|
r3 = get_ran(1,0,3);
|
|
if ((r3 >= 2) && (!is_blocked(p_loc))) // place extra monsters?
|
|
place_monster(univ.town->wandering[r1].monst[3],p_loc);
|
|
}
|
|
}
|
|
}
|
|
|
|
void place_outd_wand_monst(location where,cOutdoors::cWandering group,short forced)
|
|
{
|
|
short i = 0,j = 0;
|
|
location l;
|
|
|
|
|
|
while (i < 10) {
|
|
////
|
|
if ((univ.party.out_c[i].exists == false) || ((i == 9) && (forced > 0))) {
|
|
if ((sd_legit(group.end_spec1,group.end_spec2) == true) && (PSD[group.end_spec1][group.end_spec2] > 0))
|
|
return;
|
|
univ.party.out_c[i].exists = true;
|
|
univ.party.out_c[i].direction = 0;
|
|
univ.party.out_c[i].what_monst = group;
|
|
univ.party.out_c[i].which_sector = univ.party.i_w_c;
|
|
univ.party.out_c[i].m_loc = where;
|
|
if (univ.party.out_c[i].which_sector.x == 1)
|
|
univ.party.out_c[i].m_loc.x += 48;
|
|
if (univ.party.out_c[i].which_sector.y == 1)
|
|
univ.party.out_c[i].m_loc.y += 48;
|
|
l = univ.party.out_c[i].m_loc;
|
|
while ((forced == true) && (is_blocked(l)) && (j < 50)) {
|
|
l = univ.party.out_c[i].m_loc;
|
|
l.x += get_ran(1,0,2) - 1;
|
|
l.y += get_ran(1,0,2) - 1;
|
|
j++;
|
|
}
|
|
univ.party.out_c[i].m_loc = l;
|
|
|
|
i = 50;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
}
|
|
|
|
location get_monst_head(short m_num)
|
|
{
|
|
location l;
|
|
|
|
l = univ.town.monst[m_num].cur_loc;
|
|
if ((univ.town.monst[m_num].direction < 4) &&
|
|
(univ.town.monst[m_num].x_width > 1))
|
|
l.x++;
|
|
|
|
return l;
|
|
}
|
|
|
|
short get_monst_picnum(m_num_t monst)
|
|
{
|
|
return scenario.scen_monsters[monst].picture_num;
|
|
}
|
|
|
|
ePicType get_monst_pictype(m_num_t monst)
|
|
{
|
|
ePicType type = PIC_MONST;
|
|
short n = scenario.scen_monsters[monst].picture_num;
|
|
if (n >= 1000){
|
|
type += PIC_CUSTOM;
|
|
switch(n / 1000){
|
|
case 2:
|
|
type += PIC_WIDE;
|
|
break;
|
|
case 3:
|
|
type += PIC_TALL;
|
|
break;
|
|
case 4:
|
|
type += PIC_WIDE;
|
|
type += PIC_TALL;
|
|
break;
|
|
}
|
|
}else{
|
|
if(m_pic_index[n].x == 2) type += PIC_WIDE;
|
|
if(m_pic_index[n].y == 2) type += PIC_TALL;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
void get_monst_dims(m_num_t monst,short *width, short *height)
|
|
{
|
|
|
|
*width = scenario.scen_monsters[monst].x_width;
|
|
*height = scenario.scen_monsters[monst].y_width;
|
|
}
|
|
|
|
// Used to set up monsters for outdoor wandering encounters.
|
|
void set_up_monst(short mode,m_num_t m_num)
|
|
//mode; // 0 - unfriendly 1 - friendly & fightin'
|
|
{
|
|
short which;
|
|
|
|
for (which = 0; which < univ.town->max_monst(); which++)
|
|
if (univ.town.monst[which].active == 0) {
|
|
univ.town.monst[which].number = m_num;
|
|
univ.town.monst[which] = cCreature();
|
|
univ.town.monst[which].active = 2;
|
|
univ.town.monst[which].summoned = 0;
|
|
univ.town.monst[which].attitude = mode + 1;
|
|
univ.town.monst[which].mobility = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void do_monsters()
|
|
{
|
|
short i,j,r1,target;
|
|
location l1,l2;
|
|
bool acted_yet = false;
|
|
|
|
if (overall_mode == MODE_TOWN)
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if ((univ.town.monst[i].active != 0) && (univ.town.monst[i].status[11] <= 0)
|
|
&& (univ.town.monst[i].status[12] <= 0)) {
|
|
// have to pick targets
|
|
if (univ.town.monst[i].active == 1)
|
|
target = 6;
|
|
else {
|
|
target = monst_pick_target(i); // will return 0 if target party
|
|
target = switch_target_to_adjacent(i,target);
|
|
if (target == 0) {
|
|
if (dist(univ.town.monst[i].cur_loc,univ.town.p_loc) > 8)
|
|
target = 6;
|
|
else target = select_active_pc();
|
|
}
|
|
if ((univ.town.monst[i].attitude % 2 != 1) && (target < 6))
|
|
target = 6;
|
|
}
|
|
univ.town.monst[i].target = target;
|
|
// sprintf((char *)debug," t: %d targets %d.",i,monst_target[i]);
|
|
// add_string_to_buf((char *) debug);
|
|
|
|
if ((univ.town.monst[i].active == 2)
|
|
|| ((univ.town.monst[i].active != 0) && (univ.town.monst[i].attitude % 2 != 1))) {
|
|
acted_yet = false;
|
|
if (((univ.town.monst[i].attitude == 0) || (univ.town.monst[i].target == 6)) && (univ.town.hostile == 0)) {
|
|
if (univ.town.monst[i].mobility == 1) { // OK, it doesn't see the party or
|
|
// isn't nasty, and the town isn't totally hostile.
|
|
if ((univ.town.monst[i].attitude % 2 != 1) || (get_ran(1,0,1) == 0)) {
|
|
acted_yet = rand_move(i);
|
|
}
|
|
else acted_yet = seek_party(i,univ.town.monst[i].cur_loc,univ.town.p_loc);
|
|
}
|
|
}
|
|
if ((univ.town.monst[i].attitude > 0) || (univ.town.hostile == 1)) {
|
|
if ((univ.town.monst[i].mobility == 1) && (univ.town.monst[i].target != 6)) {
|
|
l1 = univ.town.monst[i].cur_loc;
|
|
l2 = (univ.town.monst[i].target <= 6) ? univ.town.p_loc : univ.town.monst[target - 100].cur_loc;
|
|
|
|
if ((univ.town.monst[i].morale < 0) && (univ.town.monst[i].spec_skill != 13)
|
|
&& (univ.town.monst[i].m_type != 8)) {
|
|
acted_yet = flee_party(i,l1,l2);
|
|
if (get_ran(1,0,10) < 6)
|
|
univ.town.monst[i].morale++;
|
|
}
|
|
else if (monst_hate_spot(i,&l2) == true)
|
|
acted_yet = seek_party(i,l1,l2);
|
|
else if (((univ.town.monst[i].mu == 0) && (univ.town.monst[i].mu == 0))
|
|
|| (can_see(l1,l2,0) > 3))
|
|
acted_yet = seek_party(i,l1,l2);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Make hostile monsters active
|
|
if ((univ.town.monst[i].active == 1) && (univ.town.monst[i].attitude % 2 == 1)
|
|
&& (dist(univ.town.monst[i].cur_loc,univ.town.p_loc) <= 8)) {
|
|
r1 = get_ran(1,1,100);
|
|
r1 += (PSD[SDF_PARTY_STEALTHY] > 0) ? 46 : 0;
|
|
r1 += can_see(univ.town.monst[i].cur_loc,univ.town.p_loc,0) * 10;
|
|
if (r1 < 50) {
|
|
univ.town.monst[i].active = 2;
|
|
add_string_to_buf("Monster saw you!");
|
|
// play go active sound
|
|
switch (univ.town.monst[i].m_type) {
|
|
case 0: case 3: case 4: case 5: case 6: case 9:
|
|
play_sound(18); break;
|
|
default: play_sound(46); break;
|
|
}
|
|
}
|
|
for (j = 0; j < univ.town->max_monst(); j++)
|
|
if ((univ.town.monst[j].active == 2)
|
|
&& ((dist(univ.town.monst[i].cur_loc,univ.town.monst[j].cur_loc) <= 5) == true))
|
|
univ.town.monst[i].active = 2;
|
|
}
|
|
|
|
}
|
|
if (overall_mode == MODE_OUTDOORS) {
|
|
for (i = 0; i < 10; i++)
|
|
if (univ.party.out_c[i].exists == true) {
|
|
acted_yet = false;
|
|
l1 = univ.party.out_c[i].m_loc;
|
|
l2 = univ.party.p_loc;
|
|
|
|
r1 = get_ran(1,1,6);
|
|
if (r1 == 3)
|
|
acted_yet = rand_move(i);
|
|
else acted_yet = seek_party(i,l1,l2);
|
|
}
|
|
}
|
|
}
|
|
|
|
////
|
|
bool monst_hate_spot(short which_m,location *good_loc)
|
|
{
|
|
location prospect,loc;
|
|
|
|
loc = univ.town.monst[which_m].cur_loc;
|
|
if (univ.town.is_fire_barr(loc.x,loc.y) || univ.town.is_force_barr(loc.x,loc.y) || univ.town.is_quickfire(loc.x,loc.y)
|
|
|| univ.town.is_blade_wall(loc.x,loc.y) // hate regular fields
|
|
|| (univ.town.is_ice_wall(loc.x,loc.y) && (univ.town.monst[which_m].radiate_1 != 2)
|
|
&& (univ.town.monst[which_m].immunities & 32 == 0)) // hate ice wall?
|
|
|| (univ.town.is_fire_wall(loc.x,loc.y) && (univ.town.monst[which_m].radiate_1 != 1)
|
|
&& (univ.town.monst[which_m].immunities & 8 == 0)) // hate fire wall?
|
|
|| (univ.town.is_scloud(loc.x,loc.y) && (univ.town.monst[which_m].radiate_1 != 6)
|
|
&& (univ.town.monst[which_m].immunities & 3 == 0)) // hate stink cloud?
|
|
|| (univ.town.is_sleep_cloud(loc.x,loc.y) && (univ.town.monst[which_m].radiate_1 != 5)
|
|
&& (univ.town.monst[which_m].immunities & 3 == 0)) // hate sleep cloud?
|
|
|| (univ.town.is_force_wall(loc.x,loc.y) && (univ.town.monst[which_m].radiate_1 != 3)
|
|
&& (univ.town.monst[which_m].immunities & 3 == 0)) // hate shock cloud?
|
|
|| (((univ.town.monst[which_m].mu > 0) || (univ.town.monst[which_m].cl > 0))
|
|
&& univ.town.is_antimagic(loc.x,loc.y))) // hate antimagic
|
|
{
|
|
prospect = find_clear_spot(loc,1);
|
|
if (prospect.x > 0) {
|
|
*good_loc = prospect;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
else return false;
|
|
}
|
|
|
|
short monst_pick_target(short which_m)
|
|
{
|
|
cCreature *cur_monst;
|
|
short targ_pc,targ_m;
|
|
|
|
cur_monst = &univ.town.monst[which_m];
|
|
|
|
// First, any chance target is screwed?
|
|
if (univ.town.monst[which_m].target >= 100) {
|
|
if (((cur_monst->attitude % 2 == 1) &&
|
|
(univ.town.monst[univ.town.monst[which_m].target - 100].attitude == cur_monst->attitude)) ||
|
|
((cur_monst->attitude % 2 == 0) && (univ.town.monst[univ.town.monst[which_m].target - 100].attitude % 2 == 0)))
|
|
univ.town.monst[which_m].target = 6;
|
|
else if (univ.town.monst[univ.town.monst[which_m].target - 100].active == 0)
|
|
univ.town.monst[which_m].target = 6;
|
|
}
|
|
if (univ.town.monst[which_m].target < 6)
|
|
if ((univ.party[univ.town.monst[which_m].target].main_status != 1) || (get_ran(1,0,3) == 1))
|
|
univ.town.monst[which_m].target = 6;
|
|
|
|
if ((is_combat()) && (cur_monst->attitude % 2 == 1)) {
|
|
if (spell_caster < 6)
|
|
if ((get_ran(1,1,5) < 5) && (monst_can_see(which_m,pc_pos[spell_caster]) == true)
|
|
&& (univ.party[spell_caster].main_status == 1))
|
|
return spell_caster;
|
|
if (missile_firer < 6)
|
|
if ((get_ran(1,1,5) < 3) && (monst_can_see(which_m,pc_pos[missile_firer]) == true)
|
|
&& (univ.party[missile_firer].main_status == 1))
|
|
return missile_firer;
|
|
if (univ.town.monst[which_m].target < 6)
|
|
if ((monst_can_see(which_m,pc_pos[univ.town.monst[which_m].target]) == true)
|
|
&& (univ.party[univ.town.monst[which_m].target].main_status == 1))
|
|
return univ.town.monst[which_m].target;
|
|
}
|
|
|
|
// if (monst_target[which_m] >= 100) {
|
|
// if ((can_see(cur_monst->m_loc,univ.town.monst[monst_target[which_m] - 100].m_loc,0) < 4)
|
|
// && (univ.town.monst[monst_target[which_m] - 100].active > 0))
|
|
// return monst_target[which_m];
|
|
// }
|
|
|
|
// Now pick a target pc and a target monst and see which is more attractive
|
|
targ_pc = monst_pick_target_pc(which_m,cur_monst);
|
|
targ_m = monst_pick_target_monst(cur_monst);
|
|
|
|
if ((targ_pc != 6) && (targ_m == 6))
|
|
return targ_pc;
|
|
if ((targ_pc == 6) && (targ_m != 6))
|
|
return targ_m;
|
|
if ((targ_pc == 6) && (targ_m == 6))
|
|
return 6;
|
|
|
|
if (is_town()) {
|
|
if (cur_monst->attitude % 2 == 0) {
|
|
return targ_m;
|
|
}
|
|
if ((targ_m == 6) && (cur_monst->attitude % 2 == 1))
|
|
return 0;
|
|
if (dist(cur_monst->cur_loc,univ.town.monst[targ_m - 100].cur_loc) <
|
|
dist(cur_monst->cur_loc,univ.town.p_loc))
|
|
return targ_m;
|
|
else return 0;
|
|
}
|
|
// Otherwise we're in combat
|
|
if ((dist(cur_monst->cur_loc,univ.town.monst[targ_m - 100].cur_loc) ==
|
|
dist(cur_monst->cur_loc,pc_pos[targ_pc])) && (get_ran(1,0,6) < 3))
|
|
return targ_m;
|
|
else return targ_pc;
|
|
if (dist(cur_monst->cur_loc,univ.town.monst[targ_m - 100].cur_loc) <
|
|
dist(cur_monst->cur_loc,pc_pos[targ_pc]))
|
|
return targ_m;
|
|
else return targ_pc;
|
|
|
|
}
|
|
|
|
short monst_pick_target_monst(cCreature *which_m)
|
|
{
|
|
short min_dist = 1000,i,cur_targ = 6;
|
|
|
|
for (i = 0; i < univ.town->max_monst(); i++) {
|
|
if ((univ.town.monst[i].active > 0) && // alive
|
|
(((which_m->attitude % 2 == 1) && (univ.town.monst[i].attitude % 2 == 0)) ||
|
|
((which_m->attitude % 2 == 0) && (univ.town.monst[i].attitude % 2 == 1)) ||
|
|
((which_m->attitude % 2 == 1) && (univ.town.monst[i].attitude != which_m->attitude))) && // they hate each other
|
|
((dist(which_m->cur_loc,univ.town.monst[i].cur_loc) < min_dist) ||
|
|
((dist(which_m->cur_loc,univ.town.monst[i].cur_loc) == min_dist) && (get_ran(1,0,7) < 4))) &&
|
|
(monst_can_see(i,univ.town.monst[i].cur_loc) == true) ) {
|
|
min_dist = dist(which_m->cur_loc,univ.town.monst[i].cur_loc);
|
|
cur_targ = i + 100;
|
|
|
|
}
|
|
}
|
|
return cur_targ;
|
|
}
|
|
|
|
short monst_pick_target_pc(short m_num,cCreature *which_m)////
|
|
{
|
|
short num_tries = 0,r1,store_targ = 6;
|
|
|
|
if (which_m->attitude % 2 == 0)
|
|
return 6;
|
|
if (is_town())
|
|
return 0;
|
|
|
|
// First pick any visible, nearby PC
|
|
r1 = get_ran(1,0,5);
|
|
while ((num_tries < 6) && ((univ.party[r1].main_status != 1) ||
|
|
(monst_can_see(m_num,pc_pos[r1]) == false))) {
|
|
r1 = get_ran(1,0,5);
|
|
num_tries++;
|
|
}
|
|
if (num_tries < 6)
|
|
store_targ = r1;
|
|
|
|
// Then, see if target can be replaced with someone nice and close
|
|
r1 = get_ran(1,0,5);
|
|
while ((num_tries < 6) && ((univ.party[r1].main_status != 1) ||
|
|
(dist(which_m->cur_loc,pc_pos[r1]) > 4) ||
|
|
(monst_can_see(m_num,pc_pos[r1]) == false))) {
|
|
r1 = get_ran(1,0,5);
|
|
num_tries++;
|
|
}
|
|
|
|
if (num_tries < 6)
|
|
return r1;
|
|
else return store_targ;
|
|
}
|
|
|
|
// returns 6 if no
|
|
short select_active_pc()
|
|
{
|
|
short r1, num_tries = 0;
|
|
|
|
r1 = get_ran(1,0,5);
|
|
while ((univ.party[r1].main_status != 1) && (num_tries++ < 50))
|
|
r1 = get_ran(1,0,5);
|
|
|
|
return r1;
|
|
}
|
|
|
|
short closest_pc(location where)
|
|
{
|
|
short how_close = 200,i,store = 6;
|
|
|
|
for (i = 0; i < 6; i++)
|
|
if ((univ.party[i].main_status == 1) && (dist(where,pc_pos[i]) < how_close)) {
|
|
store = i;
|
|
how_close = dist(where,pc_pos[i]);
|
|
}
|
|
return store;
|
|
}
|
|
|
|
short closest_monst(location where,short mode)
|
|
//mode; // 1 - closest hostile to PCs 2 - closest friendly to PCs
|
|
{
|
|
short how_close = 200,i,store = 6;
|
|
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if ((((univ.town.monst[i].attitude % 2 == 1) && (mode == 1)) ||
|
|
((univ.town.monst[i].attitude % 2 == 0) && (mode == 2)))
|
|
&& (dist(where,univ.town.monst[i].cur_loc) < how_close)) {
|
|
store = i;
|
|
how_close = dist(where,univ.town.monst[i].cur_loc);
|
|
}
|
|
return store;
|
|
}
|
|
|
|
short switch_target_to_adjacent(short which_m,short orig_target)
|
|
{
|
|
location monst_loc;
|
|
short i,num_adj = 0;
|
|
|
|
monst_loc = univ.town.monst[which_m].cur_loc;
|
|
|
|
// First, take care of friendly monsters.
|
|
if (univ.town.monst[which_m].attitude % 2 == 0) {
|
|
if (orig_target >= 100)
|
|
if ((univ.town.monst[orig_target - 100].active > 0) &&
|
|
(monst_adjacent(univ.town.monst[orig_target - 100].cur_loc,which_m) == true))
|
|
return orig_target;
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if ((univ.town.monst[i].active > 0) &&
|
|
(univ.town.monst[i].attitude % 2 == 1) &&
|
|
(monst_adjacent(univ.town.monst[i].cur_loc,which_m) == true))
|
|
return i + 100;
|
|
return orig_target;
|
|
}
|
|
|
|
// If we get here while in town, just need to check if switch to pc
|
|
if ((is_town()) && (monst_adjacent(univ.town.p_loc,which_m) == true))
|
|
return 0;
|
|
if (is_town())
|
|
return orig_target;
|
|
|
|
// If target is already adjacent, we're done here.
|
|
if ((is_combat()) && (orig_target < 6))
|
|
if ((univ.party[orig_target].main_status == 1) && (monst_adjacent(pc_pos[orig_target],which_m) == true))
|
|
return orig_target;
|
|
if (orig_target >= 100)
|
|
if ((univ.town.monst[orig_target - 100].active > 0) &&
|
|
(monst_adjacent(univ.town.monst[orig_target - 100].cur_loc,which_m) == true))
|
|
return orig_target;
|
|
|
|
// Anyone unarmored? Heh heh heh...
|
|
if (is_combat())
|
|
for (i = 0; i < 6; i++)
|
|
if ((univ.party[i].main_status == 1) && (monst_adjacent(pc_pos[i],which_m) == true) &&
|
|
(get_encumberance(i) < 2))
|
|
return i;
|
|
|
|
// Check for a nice, adjacent, friendly monster and maybe attack
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if ((univ.town.monst[i].active > 0) &&
|
|
(univ.town.monst[i].attitude % 2 == 0) &&
|
|
(monst_adjacent(univ.town.monst[i].cur_loc,which_m) == true) &&
|
|
(get_ran(1,0,2) < 2))
|
|
return i + 100;
|
|
|
|
// OK. Now if this monster has PCs adjacent, pick one at randomn and hack. Otherwise,
|
|
// stick with orig. target.
|
|
for (i = 0; i < 6; i++)
|
|
if ((univ.party[i].main_status == 1) && (monst_adjacent(pc_pos[i],which_m) == true))
|
|
num_adj++;
|
|
|
|
if (num_adj == 0)
|
|
return orig_target;
|
|
|
|
i = 0;
|
|
num_adj = get_ran(1,1,num_adj);
|
|
while ((num_adj > 1) || (univ.party[i].main_status != 1) || (monst_adjacent(pc_pos[i],which_m) == false)) {
|
|
if ((univ.party[i].main_status == 1) && (monst_adjacent(pc_pos[i],which_m) == true))
|
|
num_adj--;
|
|
i++;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
bool rand_move(char i)
|
|
{
|
|
bool acted_yet = false;
|
|
short j;
|
|
location store_loc;
|
|
|
|
// first, if outdoor, just roam.
|
|
if (is_out()) {
|
|
store_loc = random_shift(univ.party.out_c[i].m_loc);
|
|
return outdoor_move_monster(i,store_loc);
|
|
}
|
|
|
|
if (univ.town.monst[i].targ_loc == univ.town.monst[i].cur_loc)
|
|
univ.town.monst[i].targ_loc.x = 0;
|
|
|
|
// FIrst, try to move to monst_targs. If it don't work, then we'll shift.
|
|
if (univ.town.monst[i].targ_loc.x > 0)
|
|
acted_yet = seek_party(i,univ.town.monst[i].cur_loc,univ.town.monst[i].targ_loc);
|
|
|
|
if (acted_yet == false) {
|
|
univ.town.monst[i].targ_loc.x = 0;
|
|
for (j = 0; j < 3; j++) {
|
|
store_loc = univ.town.monst[i].cur_loc;
|
|
store_loc.x += get_ran(1,0,24) - 12;
|
|
store_loc.y += get_ran(1,0,24) - 12;
|
|
if ((loc_off_act_area(store_loc) == false) && (can_see(univ.town.monst[i].cur_loc,store_loc,0) < 5)) {
|
|
univ.town.monst[i].targ_loc = store_loc; j = 3;
|
|
}
|
|
}
|
|
|
|
if (univ.town.monst[i].targ_loc.x == 0) {
|
|
// maybe pick a wand loc, else juist pick a loc
|
|
j = get_ran(1,0,3);
|
|
store_loc = univ.town->wandering_locs[j];
|
|
|
|
if ((loc_off_act_area(store_loc) == false) && (get_ran(1,0,1) == 1))
|
|
univ.town.monst[i].targ_loc = store_loc;
|
|
else {
|
|
store_loc = univ.town.monst[i].cur_loc;
|
|
store_loc.x += get_ran(1,0,20) - 10;
|
|
store_loc.y += get_ran(1,0,20) - 10;
|
|
if (loc_off_act_area(store_loc) == false)
|
|
univ.town.monst[i].targ_loc = store_loc;
|
|
}
|
|
}
|
|
if (univ.town.monst[i].targ_loc.x > 0)
|
|
acted_yet = seek_party(i,univ.town.monst[i].cur_loc,univ.town.monst[i].targ_loc);
|
|
}
|
|
|
|
return acted_yet;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool seek_party(short i,location l1,location l2)
|
|
{
|
|
bool acted_yet = false;
|
|
short m,n;
|
|
if ((l1.x > l2.x) && (l1.y > l2.y))
|
|
acted_yet = try_move(i,l1,-1,-1);
|
|
if ((l1.x < l2.x) & (l1.y < l2.y) & (acted_yet == false))
|
|
acted_yet = try_move(i,l1,1,1);
|
|
if ((l1.x > l2.x) & (l1.y < l2.y) & (acted_yet == false))
|
|
acted_yet = try_move(i,l1,-1,1);
|
|
if ((l1.x < l2.x) & (l1.y > l2.y) & (acted_yet == false))
|
|
acted_yet = try_move(i,l1,1,-1);
|
|
if ((l1.x > l2.x) & (acted_yet == false))
|
|
acted_yet = try_move(i,l1,-1,0);
|
|
if ((l1.x < l2.x) & (acted_yet == false))
|
|
acted_yet = try_move(i,l1,1,0);
|
|
if ( (l1.y < l2.y) & (acted_yet == false))
|
|
acted_yet = try_move(i,l1,0,1);
|
|
if ( (l1.y > l2.y) & (acted_yet == false))
|
|
acted_yet = try_move(i,l1,0,-1);
|
|
if (acted_yet == false) {
|
|
futzing++;
|
|
m = get_ran(1,0,2) - 1;
|
|
n = get_ran(1,0,2) - 1;
|
|
acted_yet = try_move(i,l1,m,n);
|
|
}
|
|
return acted_yet;
|
|
}
|
|
|
|
bool flee_party(short i,location l1,location l2)
|
|
{
|
|
bool acted_yet = false;
|
|
|
|
if ((l1.x > l2.x) & (l1.y > l2.y))
|
|
acted_yet = try_move(i,l1,1,1);
|
|
if ((l1.x < l2.x) & (l1.y < l2.y) & (acted_yet == false))
|
|
acted_yet = try_move(i,l1,-1,-1);
|
|
if ((l1.x > l2.x) & (l1.y < l2.y) & (acted_yet == false))
|
|
acted_yet = try_move(i,l1,1,-1);
|
|
if ((l1.x < l2.x) & (l1.y > l2.y) & (acted_yet == false))
|
|
acted_yet = try_move(i,l1,-1,+1);
|
|
if ((l1.x > l2.x) & (acted_yet == false))
|
|
acted_yet = try_move(i,l1,1,0);
|
|
if ((l1.x < l2.x) & (acted_yet == false))
|
|
acted_yet = try_move(i,l1,-1,0);
|
|
if ( (l1.y < l2.y) & (acted_yet == false))
|
|
acted_yet = try_move(i,l1,0,-1);
|
|
if ( (l1.y > l2.y) & (acted_yet == false))
|
|
acted_yet = try_move(i,l1,0,1);
|
|
if (acted_yet == false) {
|
|
futzing++;
|
|
acted_yet = rand_move(i);
|
|
}
|
|
return acted_yet;
|
|
}
|
|
|
|
bool try_move(short i,location start,short x,short y)
|
|
{
|
|
location dest;
|
|
|
|
dest = start;
|
|
dest.x = dest.x + x;
|
|
dest.y = dest.y + y;
|
|
|
|
|
|
if (overall_mode == MODE_TOWN)
|
|
return town_move_monster(i,dest);
|
|
if (overall_mode == MODE_OUTDOORS)
|
|
return outdoor_move_monster(i,dest);
|
|
if (overall_mode == MODE_COMBAT)
|
|
return combat_move_monster((short) i,dest);
|
|
return 0;
|
|
}
|
|
|
|
bool combat_move_monster(short which,location destination)
|
|
{
|
|
|
|
|
|
if (monst_can_be_there(destination,which) == false)
|
|
return false;
|
|
else if (monst_check_special_terrain(destination,2,which) == false)
|
|
return false;
|
|
else {
|
|
univ.town.monst[which].direction =
|
|
set_direction(univ.town.monst[which].cur_loc, destination);
|
|
univ.town.monst[which].cur_loc = destination;
|
|
monst_inflict_fields(which);
|
|
|
|
if (point_onscreen(destination,center) == true) {
|
|
if (is_combat())
|
|
move_sound(combat_terrain[destination.x][destination.y],
|
|
(short) univ.town.monst[which].ap);
|
|
else move_sound(univ.town->terrain(destination.x,destination.y),
|
|
(short) univ.town.monst[which].ap);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Looks at all spaces within 2, looking for a spot which is clear of nastiness and beings
|
|
// returns {0,0} if none found
|
|
// THIS MAKES NO ADJUSTMENTS FOR BIG MONSTERS!!!
|
|
location find_clear_spot(location from_where,short mode)
|
|
//mode; // 0 - normal 1 - prefer adjacent space
|
|
{
|
|
location loc,store_loc;
|
|
short num_tries = 0,r1;
|
|
|
|
while (num_tries < 75) {
|
|
num_tries++;
|
|
loc = from_where;
|
|
r1 = get_ran(1,-2,2);
|
|
loc.x = loc.x + r1;
|
|
r1 = get_ran(1,-2,2);
|
|
loc.y = loc.y + r1;
|
|
if ((loc_off_act_area(loc) == false) && (is_blocked(loc) == false)
|
|
&& (can_see(from_where,loc,1) == 0)
|
|
&& (!(is_combat()) || (pc_there(loc) == 6))
|
|
&& (!(is_town()) || (loc != univ.town.p_loc))
|
|
&& (!(univ.town.misc_i(loc.x,loc.y) & 248)) && // check for crate, barrel, barrier, quickfire
|
|
(!(univ.town.explored(loc.x,loc.y) & 254))) { // check for fields, clouds
|
|
if ((mode == 0) || ((mode == 1) && (adjacent(from_where,loc) == true)))
|
|
return loc;
|
|
else store_loc = loc;
|
|
}
|
|
}
|
|
return store_loc;
|
|
}
|
|
|
|
short pc_there(location where)
|
|
{
|
|
short i;
|
|
|
|
for (i = 0; i < 6; i++)
|
|
if ((where == pc_pos[i]) && (univ.party[i].main_status == 1))
|
|
return i;
|
|
return 6;
|
|
}
|
|
|
|
location random_shift(location start)
|
|
{
|
|
location store;
|
|
|
|
store = start;
|
|
store.x = store.x + get_ran(1,0,2) - 1;
|
|
store.y = store.y + get_ran(1,0,2) - 1;
|
|
|
|
return store;
|
|
}
|
|
|
|
bool outdoor_move_monster(short num,location dest)
|
|
{
|
|
|
|
if ((outd_is_blocked(dest) == false) && (outd_is_special(dest) == false) &&
|
|
(dest != univ.party.p_loc) &&
|
|
((univ.out[dest.x][dest.y] > 21) || (univ.out[dest.x][dest.y] < 5))) {
|
|
univ.party.out_c[num].direction =
|
|
set_direction(univ.party.out_c[num].m_loc, dest);
|
|
univ.party.out_c[num].m_loc = dest;
|
|
return true;
|
|
}
|
|
else return false;
|
|
}
|
|
|
|
bool town_move_monster(short num,location dest)
|
|
{
|
|
if (monst_check_special_terrain(dest,1,num) == false)
|
|
return false;
|
|
|
|
if (monst_can_be_there(dest,num) == true) {
|
|
univ.town.monst[num].direction =
|
|
set_direction(univ.town.monst[num].cur_loc, dest);
|
|
univ.town.monst[num].cur_loc = dest;
|
|
monst_inflict_fields(num);
|
|
return true;
|
|
}
|
|
else return false;
|
|
}
|
|
|
|
bool monster_placid(short m_num)
|
|
{
|
|
if ((univ.town.monst[m_num].attitude == 0) ||
|
|
((univ.town.monst[m_num].attitude == 2) && (PSD[SDF_HOSTILES_PRESENT] == 0)))
|
|
{ return true;}
|
|
else { return false;}
|
|
}
|
|
|
|
// This damages a monster by any fields it's in, and destroys any barrels or crates
|
|
// it's stiing on.
|
|
void monst_inflict_fields(short which_monst)
|
|
{
|
|
short i,j,r1,k;
|
|
location where_check;
|
|
cCreature *which_m;
|
|
|
|
if (univ.town.monst[which_monst].active == 0)
|
|
return;
|
|
|
|
which_m = &univ.town.monst[which_monst];
|
|
for (i = 0; i < univ.town.monst[which_monst].x_width; i++)
|
|
for (j = 0; j < univ.town.monst[which_monst].y_width; j++)
|
|
if (univ.town.monst[which_monst].active > 0) {
|
|
where_check.x = univ.town.monst[which_monst].cur_loc.x + i;
|
|
where_check.y = univ.town.monst[which_monst].cur_loc.y + j;
|
|
if (univ.town.is_quickfire(where_check.x,where_check.y)) {
|
|
r1 = get_ran(2,1,8);
|
|
damage_monst(which_monst,7,r1,0,DAMAGE_FIRE,0);
|
|
break;
|
|
}
|
|
if (univ.town.is_blade_wall(where_check.x,where_check.y)) {
|
|
r1 = get_ran(6,1,8);
|
|
damage_monst(which_monst,7,r1,0,DAMAGE_WEAPON,0);
|
|
break;
|
|
}
|
|
if (univ.town.is_force_wall(where_check.x,where_check.y)) {
|
|
r1 = get_ran(3,1,6);
|
|
damage_monst(which_monst,7,r1,0,DAMAGE_MAGIC,0);
|
|
break;
|
|
}
|
|
if (univ.town.is_sleep_cloud(where_check.x,where_check.y)) {
|
|
charm_monst(which_m,0,11,3);
|
|
break;
|
|
}
|
|
if (univ.town.is_ice_wall(where_check.x,where_check.y)) {
|
|
r1 = get_ran(3,1,6);
|
|
if (univ.town.monst[which_monst].spec_skill != 23)
|
|
damage_monst(which_monst,7,r1,0,DAMAGE_COLD,0);
|
|
break;
|
|
}
|
|
if (univ.town.is_scloud(where_check.x,where_check.y)) {
|
|
r1 = get_ran(1,2,3);
|
|
curse_monst(which_m,r1);
|
|
break;
|
|
}
|
|
if ((univ.town.is_web(where_check.x,where_check.y)) && (which_m->m_type != 12)) {
|
|
monst_spell_note(which_m->number,19);
|
|
r1 = get_ran(1,2,3);
|
|
web_monst(which_m,r1);
|
|
univ.town.set_web(where_check.x,where_check.y,false);
|
|
break;
|
|
}
|
|
if (univ.town.is_fire_wall(where_check.x,where_check.y)) {
|
|
r1 = get_ran(2,1,6);
|
|
if (univ.town.monst[which_monst].spec_skill != 22)
|
|
damage_monst(which_monst,7,r1,0,DAMAGE_FIRE,0);
|
|
break;
|
|
}
|
|
}
|
|
if (univ.town.monst[which_monst].active > 0)
|
|
for (i = 0; i < univ.town.monst[which_monst].x_width; i++)
|
|
for (j = 0; j < univ.town.monst[which_monst].y_width; j++) {
|
|
where_check.x = univ.town.monst[which_monst].cur_loc.x + i;
|
|
where_check.y = univ.town.monst[which_monst].cur_loc.y + j;
|
|
if ((univ.town.is_crate(where_check.x,where_check.y)) ||
|
|
(univ.town.is_barrel(where_check.x,where_check.y)) )
|
|
for (k = 0; k < NUM_TOWN_ITEMS; k++)
|
|
if ((univ.town.items[k].variety > 0) && (univ.town.items[k].contained)
|
|
&& (univ.town.items[k].item_loc == where_check))
|
|
univ.town.items[k].contained = false;
|
|
univ.town.set_crate(where_check.x,where_check.y,false);
|
|
univ.town.set_barrel(where_check.x,where_check.y,false);
|
|
if (univ.town.is_fire_barr(where_check.x,where_check.y)) {
|
|
r1 = get_ran(2,1,10);
|
|
damage_monst(which_monst,7,r1,0,DAMAGE_FIRE,0);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
bool monst_check_special_terrain(location where_check,short mode,short which_monst)
|
|
//mode; // 1 - town 2 - combat
|
|
{
|
|
ter_num_t ter = 0;
|
|
short r1,i,guts = 0;
|
|
bool can_enter = true,mage = false;
|
|
location from_loc,to_loc;
|
|
bool do_look = false; // If becomes true, terrain changed, so need to update what party sees
|
|
cCreature *which_m;
|
|
short ter_abil;
|
|
unsigned short ter_flag;
|
|
|
|
from_loc = univ.town.monst[which_monst].cur_loc;
|
|
switch (mode) {
|
|
case 1:
|
|
ter = univ.town->terrain(where_check.x,where_check.y);
|
|
break;
|
|
case 2:
|
|
ter = combat_terrain[where_check.x][where_check.y];
|
|
break;
|
|
}
|
|
////
|
|
which_m = &univ.town.monst[which_monst];
|
|
ter_abil = scenario.ter_types[ter].special;
|
|
ter_flag = scenario.ter_types[ter].flag3.u;
|
|
|
|
if ((mode > 0) && (ter_abil == TER_SPEC_CONVEYOR)) {
|
|
if (
|
|
((ter_flag == DIR_N) && (where_check.y > from_loc.y)) ||
|
|
((ter_flag == DIR_E) && (where_check.x < from_loc.x)) ||
|
|
((ter_flag == DIR_S) && (where_check.y < from_loc.y)) ||
|
|
((ter_flag == DIR_W) && (where_check.x > from_loc.x)) ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// begin determining guts, which determines how enthused the monst is about entering
|
|
// nasty barriers
|
|
if ((which_m->mu > 0) || (which_m->cl > 0))
|
|
mage = true;
|
|
if (which_m->spec_skill == 13)
|
|
guts = 20;
|
|
else guts = get_ran(1,1,(which_m->level / 2));
|
|
guts += which_m->health / 20;
|
|
if (mage == true)
|
|
guts = guts / 2;
|
|
if (which_m->attitude == 0)
|
|
guts = guts / 2;
|
|
|
|
if ((univ.town.is_antimagic(where_check.x,where_check.y)) && (mage == true))
|
|
return false;
|
|
if ((univ.town.is_fire_wall(where_check.x,where_check.y)) && (which_m->spec_skill != 22)) {
|
|
if (guts < 3) return false;
|
|
}
|
|
if (univ.town.is_force_wall(where_check.x,where_check.y)) {
|
|
if (guts < 4) return false;
|
|
}
|
|
if ((univ.town.is_ice_wall(where_check.x,where_check.y)) && (which_m->spec_skill != 23)) {
|
|
if (guts < 5) return false;
|
|
}
|
|
if (univ.town.is_sleep_cloud(where_check.x,where_check.y)) {
|
|
if (guts < 8) return false;
|
|
}
|
|
if (univ.town.is_blade_wall(where_check.x,where_check.y)) {
|
|
if (guts < 8) return false;
|
|
}
|
|
if (univ.town.is_quickfire(where_check.x,where_check.y)) {
|
|
if (guts < 8) return false;
|
|
}
|
|
if (univ.town.is_scloud(where_check.x,where_check.y)) {
|
|
if (guts < 4) return false;
|
|
}
|
|
if ((univ.town.is_web(where_check.x,where_check.y)) && (which_m->m_type != 12)) {
|
|
if (guts < 3) return false;
|
|
}
|
|
if (univ.town.is_fire_barr(where_check.x,where_check.y)) {
|
|
if ((which_m->attitude % 2 == 1) && (get_ran(1,1,100) < (which_m->mu * 10 + which_m->cl * 4))) {
|
|
play_sound(60);
|
|
add_string_to_buf("Monster breaks barrier.");
|
|
univ.town.set_fire_barr(where_check.x,where_check.y,false);
|
|
}
|
|
else {
|
|
if (guts < 6) return false;
|
|
r1 = get_ran(1,0,10);
|
|
if ((r1 < 8) || (monster_placid(which_monst)))
|
|
can_enter = false;
|
|
}
|
|
}
|
|
if (univ.town.is_force_barr(where_check.x,where_check.y)) { /// Not in big towns
|
|
if ((which_m->attitude % 2 == 1) && (get_ran(1,1,100) < (which_m->mu * 10 + which_m->cl * 4))
|
|
&& (!univ.town->strong_barriers)) {
|
|
play_sound(60);
|
|
add_string_to_buf("Monster breaks barrier.");
|
|
univ.town.set_force_barr(where_check.x,where_check.y,false);
|
|
}
|
|
else can_enter = false;
|
|
}
|
|
if (univ.town.is_crate(where_check.x,where_check.y)) {
|
|
if (monster_placid(which_monst))
|
|
can_enter = false;
|
|
else {
|
|
to_loc = push_loc(from_loc,where_check);
|
|
univ.town.set_crate((short) where_check.x,(short) where_check.y,false);
|
|
if (to_loc.x > 0)
|
|
univ.town.set_crate((short) to_loc.x,(short) to_loc.y, true);
|
|
for (i = 0; i < NUM_TOWN_ITEMS; i++)
|
|
if ((univ.town.items[i].variety > 0) && (univ.town.items[i].item_loc == where_check)
|
|
&& (univ.town.items[i].contained))
|
|
univ.town.items[i].item_loc = to_loc;
|
|
}
|
|
}
|
|
if (univ.town.is_barrel(where_check.x,where_check.y)) {
|
|
if (monster_placid(which_monst))
|
|
can_enter = false;
|
|
else {
|
|
to_loc = push_loc(from_loc,where_check);
|
|
univ.town.set_barrel((short) where_check.x,(short) where_check.y,false);
|
|
if (to_loc.x > 0)
|
|
univ.town.set_barrel((short) to_loc.x,(short) to_loc.y,true);
|
|
for (i = 0; i < NUM_TOWN_ITEMS; i++)
|
|
if ((univ.town.items[i].variety > 0) && (univ.town.items[i].item_loc == where_check)
|
|
&& (univ.town.items[i].contained))
|
|
univ.town.items[i].item_loc = to_loc;
|
|
|
|
}
|
|
}
|
|
if (monster_placid(which_monst) && // monsters don't hop into bed when things are calm
|
|
(scenario.ter_types[ter].special == TER_SPEC_BED))
|
|
can_enter = false;
|
|
if (mode == 1 && univ.town.is_spot(where_check.x, where_check.y))
|
|
can_enter = false;
|
|
if (ter == 90) {
|
|
if ((is_combat()) && (which_combat_type == 0)) {
|
|
univ.town.monst[which_monst].active = 0;
|
|
add_string_to_buf("Monster escaped! ");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
switch (ter_abil) {
|
|
// changing ter
|
|
case TER_SPEC_CHANGE_WHEN_STEP_ON:
|
|
can_enter = false;
|
|
if (!(monster_placid(which_monst))) {
|
|
univ.town->terrain(where_check.x,where_check.y) = scenario.ter_types[ter].flag1.u;
|
|
combat_terrain[where_check.x][where_check.y] = scenario.ter_types[ter].flag1.u;
|
|
do_look = true;
|
|
if (point_onscreen(center,where_check))
|
|
play_sound(scenario.ter_types[ter].flag2.u);
|
|
}
|
|
break;
|
|
|
|
case TER_SPEC_BLOCKED_TO_MONSTERS:
|
|
case TER_SPEC_TOWN_ENTRANCE:
|
|
case TER_SPEC_WATERFALL:
|
|
can_enter = false;
|
|
break;
|
|
|
|
case TER_SPEC_DAMAGING: // TODO: Update this to check other cases
|
|
if (ter_flag == DAMAGE_FIRE && univ.town.monst[which_monst].immunities & 8)
|
|
return true;
|
|
else return false;
|
|
break;
|
|
}
|
|
|
|
// Action may change terrain, so update what's been seen
|
|
if (do_look == true) {
|
|
if (is_town())
|
|
update_explored(univ.town.p_loc);
|
|
if (is_combat())
|
|
for (i = 0; i < 6; i++)
|
|
if (univ.party[i].main_status == 1)
|
|
update_explored(pc_pos[i]);
|
|
}
|
|
|
|
return can_enter;
|
|
}
|
|
|
|
void forced_place_monster(m_num_t which,location where)
|
|
{
|
|
bool free_spot = false;
|
|
short i = 0,r1;
|
|
|
|
while ((free_spot == false) && (i < univ.town->max_monst())) {
|
|
if (univ.town.monst[i].active == 0)
|
|
free_spot = true;
|
|
i++;
|
|
}
|
|
do
|
|
r1 = get_ran(1,0,59);
|
|
while ((univ.town.monst[r1].spec1 != 0) || (univ.town.monst[r1].spec2 != 0));
|
|
if (free_spot == false)
|
|
univ.town.monst[r1].active = 0;
|
|
place_monster(which,where);
|
|
}
|
|
|
|
void magic_adjust(cCreature *which_m,short *how_much)
|
|
{
|
|
if (which_m->spec_skill == 26) {
|
|
*how_much = 0;
|
|
if(32767 - which_m->health > 3)
|
|
which_m->health = 32767;
|
|
else which_m->health += 3;
|
|
}
|
|
if (which_m->immunities & 1)
|
|
*how_much = *how_much / 2;
|
|
if (which_m->immunities & 2)
|
|
*how_much = 0;
|
|
}
|
|
|
|
void poison_monst(cCreature *which_m,short how_much)
|
|
{
|
|
if (which_m->immunities & 64)
|
|
how_much = how_much / 2;
|
|
if (which_m->immunities & 128) {
|
|
monst_spell_note(which_m->number,10);
|
|
return;
|
|
}
|
|
which_m->status[2] = min(8, which_m->status[2] + how_much);
|
|
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 4);
|
|
|
|
}
|
|
void acid_monst(cCreature *which_m,short how_much)
|
|
{
|
|
magic_adjust(which_m,&how_much);
|
|
which_m->status[13] = minmax(-8,8, which_m->status[13] + how_much);
|
|
monst_spell_note(which_m->number,31);
|
|
|
|
}
|
|
|
|
void slow_monst(cCreature *which_m,short how_much)
|
|
{
|
|
magic_adjust(which_m,&how_much);
|
|
which_m->status[3] = minmax(-8,8, which_m->status[3] - how_much);
|
|
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 2);
|
|
|
|
}
|
|
void curse_monst(cCreature *which_m,short how_much)
|
|
{
|
|
magic_adjust(which_m,&how_much);
|
|
which_m->status[1] = minmax(-8,8, which_m->status[1] - how_much);
|
|
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 5);
|
|
|
|
}
|
|
void web_monst(cCreature *which_m,short how_much)
|
|
{
|
|
magic_adjust(which_m,&how_much);
|
|
which_m->status[6] = minmax(-8,8, which_m->status[6] + how_much);
|
|
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 19);
|
|
|
|
}
|
|
void scare_monst(cCreature *which_m,short how_much)
|
|
{
|
|
magic_adjust(which_m,&how_much);
|
|
which_m->morale = which_m->morale - how_much;
|
|
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 1);
|
|
|
|
}
|
|
void disease_monst(cCreature *which_m,short how_much)
|
|
{
|
|
magic_adjust(which_m,&how_much);
|
|
which_m->status[7] = minmax(-8,8, which_m->status[7] + how_much);
|
|
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 25);
|
|
|
|
}
|
|
|
|
void dumbfound_monst(cCreature *which_m,short how_much)
|
|
{
|
|
magic_adjust(which_m,&how_much);
|
|
which_m->status[9] = minmax(-8,8, which_m->status[9] + how_much);
|
|
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 22);
|
|
|
|
}
|
|
|
|
void charm_monst(cCreature *which_m,short penalty,short which_status,short amount)
|
|
// Also used for sleep and paralyze, which_statys is 0 means charm
|
|
{
|
|
short r1;
|
|
|
|
|
|
if ((which_status == 11) &&
|
|
((which_m->m_type == 8) || (which_m->m_type == 10) || (which_m->m_type == 11)))
|
|
return;
|
|
r1 = get_ran(1,1,100);
|
|
if (which_m->immunities & 1)
|
|
r1 = r1 * 2;
|
|
if (which_m->immunities & 2)
|
|
r1 = 200;
|
|
r1 += penalty;
|
|
if (which_status == 11)
|
|
r1 -= 25;
|
|
if (which_status == 12)
|
|
r1 -= 15;
|
|
if ((which_status == 11) && (which_m->spec_skill == 32))
|
|
return;
|
|
|
|
if (r1 > charm_odds[which_m->level / 2]) {
|
|
//one_sound(68);
|
|
monst_spell_note(which_m->number,10);
|
|
}
|
|
else {
|
|
if (which_status == 0) {
|
|
which_m->attitude = 2;
|
|
monst_spell_note(which_m->number,23);
|
|
}
|
|
else {
|
|
which_m->status[which_status] = amount;
|
|
if (which_status == 11)
|
|
monst_spell_note(which_m->number,28);
|
|
if (which_status == 12)
|
|
monst_spell_note(which_m->number,30);
|
|
}
|
|
//one_sound(53);
|
|
}
|
|
}
|
|
void record_monst(cCreature *which_m)
|
|
{
|
|
short r1;
|
|
char str[60];
|
|
|
|
r1 = get_ran(1,1,100);
|
|
r1 = (r1 * 7) / 10;
|
|
|
|
if ((which_m->x_width > 1) || (which_m->y_width > 1)) {
|
|
ASB("Capture Soul: Monster is too big.");
|
|
}
|
|
else if ((r1 > charm_odds[which_m->level / 2]) || (which_m->spec_skill == 12)
|
|
|| (which_m->m_type == 3)) {
|
|
monst_spell_note(which_m->number,10);
|
|
play_sound(68);
|
|
}
|
|
else {
|
|
monst_spell_note(which_m->number,24);
|
|
r1 = get_ran(1,0,3);
|
|
if (univ.party.imprisoned_monst[r1] == 0)
|
|
univ.party.imprisoned_monst[r1] = which_m->number;
|
|
else {
|
|
r1 = get_ran(1,0,3);
|
|
univ.party.imprisoned_monst[r1] = which_m->number;
|
|
}
|
|
ASB("Capture Soul: Success!");
|
|
sprintf((char *)str," Caught in slot %d.",r1 + 1);
|
|
add_string_to_buf((char *)str);
|
|
play_sound(53);
|
|
}
|
|
}
|
|
// returns 90 is no placement, OW returns # of spot
|
|
////
|
|
short place_monster(m_num_t which,location where)
|
|
{
|
|
short i = 0;
|
|
|
|
while ((i < univ.town->max_monst()) && ((univ.town.monst[i].active != 0) ||
|
|
(univ.town.monst[i].spec_enc_code > 0))) {
|
|
i++;
|
|
}
|
|
|
|
if (i < univ.town->max_monst()) {
|
|
univ.town.monst[i].number = which;
|
|
univ.town.monst[i] = cCreature();
|
|
univ.town.monst[i].attitude = scenario.scen_monsters[which].default_attitude;
|
|
if (univ.town.monst[i].attitude % 2 == 0)
|
|
univ.town.monst[i].attitude = 1;
|
|
univ.town.monst[i].mobility = 1;
|
|
univ.town.monst[i].active = 2;
|
|
univ.town.monst[i].cur_loc = where;
|
|
univ.town.monst[i].summoned = 0;
|
|
univ.town.monst[i].target = 6;
|
|
|
|
univ.town.set_crate(where.x,where.y,false);
|
|
univ.town.set_barrel(where.x,where.y,false);
|
|
|
|
return i;
|
|
}
|
|
return 90;
|
|
}
|
|
|
|
// returns true if placement was successful
|
|
bool summon_monster(m_num_t which,location where,short duration,short given_attitude)
|
|
//which; // if in town, this is caster loc., if in combat, this is where to try
|
|
// to put monster
|
|
{
|
|
location loc;
|
|
short which_m,spot;
|
|
|
|
if ((is_town()) || (monsters_going)) {
|
|
// Ooooh ... mondo kludge. Need to find caster's attitude to give it to monst.
|
|
which_m = monst_there(where);
|
|
// if (pc_there(where) < 6)
|
|
// which_att = 2;
|
|
// else if (which_m == 90)
|
|
// which_att = 1;
|
|
// else which_att = univ.town.monst[which_m].attitude;
|
|
loc = find_clear_spot(where,0);
|
|
if (loc.x == 0)
|
|
return false;
|
|
}
|
|
else {
|
|
// pc may be summoning using item, in which case where will be pc's space, so fix
|
|
if (pc_there(where) < 6) {
|
|
where = find_clear_spot(where,0);
|
|
if (where.x == 0)
|
|
return false;
|
|
}
|
|
if ((univ.town.is_barrel(where.x,where.y)) || (univ.town.is_crate(where.x,where.y)))
|
|
return false;
|
|
loc = where;
|
|
}
|
|
|
|
spot = place_monster(which,loc);
|
|
if (spot >= univ.town->max_monst()) {
|
|
if (duration < 100)
|
|
add_string_to_buf(" Too many monsters.");
|
|
//ASB(" Monster fails to summon monster.");
|
|
return false;
|
|
}
|
|
//play_sound(61);
|
|
|
|
// if (duration < 100)
|
|
univ.town.monst[spot].attitude = given_attitude;
|
|
// else univ.town.monst[spot].attitude = which_att;
|
|
|
|
if (which > 0) {//monster here for good
|
|
univ.town.monst[spot].summoned = duration;
|
|
monst_spell_note(which,21);
|
|
}
|
|
else univ.town.monst[spot].summoned = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
void activate_monsters(short code,short attitude)
|
|
{
|
|
short i;
|
|
|
|
if (code == 0)
|
|
return;
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if (univ.town.monst[i].spec_enc_code == code)
|
|
{
|
|
univ.town.monst[i] = univ.town->creatures(i);
|
|
univ.town.monst[i].spec_enc_code = 0;
|
|
univ.town.monst[i].active = 2; // TODO: Can thes be commented out? \/
|
|
//univ.town.monst[i].attitude = univ.town->creatures(i).start_attitude;
|
|
|
|
univ.town.monst[i].summoned = 0;
|
|
//univ.town.monst[i].cur_loc = univ.town->creatures(i).start_loc;
|
|
univ.town.monst[i].target = 6;
|
|
|
|
univ.town.set_crate(univ.town.monst[i].cur_loc.x,univ.town.monst[i].cur_loc.y,false);
|
|
univ.town.set_barrel(univ.town.monst[i].cur_loc.x,univ.town.monst[i].cur_loc.y,false);
|
|
}
|
|
}
|
|
|
|
short get_encumberance(short pc_num)
|
|
{
|
|
short store = 0,i,what_val;
|
|
|
|
for (i = 0; i < 24; i++)
|
|
if (univ.party[pc_num].equip[i] == true) {
|
|
what_val = univ.party[pc_num].items[i].awkward;
|
|
if ((what_val == 1) && (get_ran(1,0,130) < hit_chance[univ.party[pc_num].skills[8]]))
|
|
what_val--;
|
|
if ((what_val > 1) && (get_ran(1,0,70) < hit_chance[univ.party[pc_num].skills[8]]))
|
|
what_val--;
|
|
store += what_val;
|
|
}
|
|
return store;
|
|
}
|
|
|
|
m_num_t get_summon_monster(short summon_class)
|
|
{
|
|
short i,j;
|
|
|
|
for (i = 0; i < 200; i++) {
|
|
j = get_ran(1,0,255);
|
|
if (scenario.scen_monsters[j].summon_type == summon_class) {
|
|
return j;
|
|
}
|
|
}
|
|
ASB(" Summon failed.");
|
|
return 0;
|
|
}
|