2006-03-20 16:43:15 +00:00
/*
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ( " PARALLAX " ) . PARALLAX , IN DISTRIBUTING THE CODE TO
END - USERS , AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN , GRANTS A
ROYALTY - FREE , PERPETUAL LICENSE TO SUCH END - USERS FOR USE BY SUCH END - USERS
IN USING , DISPLAYING , AND CREATING DERIVATIVE WORKS THEREOF , SO LONG AS
SUCH USE , DISPLAY OR CREATION IS FOR NON - COMMERCIAL , ROYALTY OR REVENUE
FREE PURPOSES . IN NO EVENT SHALL THE END - USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE - BEARING PURPOSES . THE END - USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE .
COPYRIGHT 1993 - 1998 PARALLAX SOFTWARE CORPORATION . ALL RIGHTS RESERVED .
*/
2007-12-29 14:18:49 +00:00
2006-03-20 16:43:15 +00:00
/*
*
* Autonomous Individual movement .
*
*/
# ifdef RCS
static char rcsid [ ] = " $Id: ai.c,v 1.1.1.1 2006/03/17 19:41:40 zicodxx Exp $ " ;
# endif
# include <stdio.h>
# include <stdlib.h>
# include "inferno.h"
# include "game.h"
# include "mono.h"
# include "3d.h"
# include "object.h"
# include "render.h"
# include "error.h"
# include "ai.h"
# include "laser.h"
# include "fvi.h"
# include "polyobj.h"
# include "bm.h"
# include "weapon.h"
# include "physics.h"
# include "collide.h"
# include "fuelcen.h"
# include "player.h"
# include "wall.h"
# include "vclip.h"
# include "digi.h"
# include "fireball.h"
# include "morph.h"
# include "effects.h"
# include "timer.h"
# include "sounds.h"
# include "cntrlcen.h"
# include "multibot.h"
# include "multi.h"
# include "network.h"
# include "gameseq.h"
# include "key.h"
# include "powerup.h"
# include "gauges.h"
# include "text.h"
# ifdef EDITOR
# include "editor/editor.h"
# endif
# ifndef NDEBUG
# include "string.h"
# ifdef __MSDOS__
# include <time.h>
# endif
# endif
# include "d_gamecv.h"
//added 05/17/99 Matt Mueller
# include "u_mem.h"
//end addition -MM
void init_boss_segments ( short segptr [ ] , int * num_segs , int size_check ) ;
void ai_multi_send_robot_position ( int objnum , int force ) ;
# define JOHN_CHEATS_SIZE_1 6
# define JOHN_CHEATS_SIZE_2 6
# define JOHN_CHEATS_SIZE_3 6
ubyte john_cheats_1 [ JOHN_CHEATS_SIZE_1 ] = { KEY_P ^ 0x00 ^ 0x34 ,
KEY_O ^ 0x10 ^ 0x34 ,
KEY_B ^ 0x20 ^ 0x34 ,
KEY_O ^ 0x30 ^ 0x34 ,
KEY_Y ^ 0x40 ^ 0x34 ,
KEY_S ^ 0x50 ^ 0x34 } ;
# define PARALLAX 0 // If !0, then special debugging info for Parallax eyes only enabled.
# define MIN_D 0x100
int Flinch_scale = 4 ;
int john_cheats_index_1 ; // POBOYS detonate reactor
int Attack_scale = 24 ;
# define ANIM_RATE (F1_0 / 16)
# define DELTA_ANG_SCALE 16
2006-10-06 14:41:31 +00:00
sbyte Mike_to_matt_xlate [ ] = { AS_REST , AS_REST , AS_ALERT , AS_ALERT , AS_FLINCH , AS_FIRE , AS_RECOIL , AS_REST } ;
2006-03-20 16:43:15 +00:00
int john_cheats_index_2 ; // PORGYS high speed weapon firing
// int No_ai_flag=0;
# define OVERALL_AGITATION_MAX 100
# define MAX_AI_CLOAK_INFO 8 // Must be a power of 2!
typedef struct {
fix last_time ;
vms_vector last_position ;
} ai_cloak_info ;
# define BOSS_CLOAK_DURATION (F1_0*7)
# define BOSS_DEATH_DURATION (F1_0*6)
# define BOSS_DEATH_SOUND_DURATION 0x2ae14 // 2.68 seconds
// Amount of time since the current robot was last processed for things such as movement.
// It is not valid to use FrameTime because robots do not get moved every frame.
//fix AI_proc_time;
int Num_boss_teleport_segs ;
short Boss_teleport_segs [ MAX_BOSS_TELEPORT_SEGS ] ;
# ifndef SHAREWARE
int Num_boss_gate_segs ;
short Boss_gate_segs [ MAX_BOSS_TELEPORT_SEGS ] ;
# endif
int john_cheats_index_3 ; // LUNACY lunacy (insane behavior, rookie firing)
// ---------- John: These variables must be saved as part of gamesave. ----------
int Ai_initialized = 0 ;
int Overall_agitation ;
ai_local Ai_local_info [ MAX_OBJECTS ] ;
point_seg Point_segs [ MAX_POINT_SEGS ] ;
point_seg * Point_segs_free_ptr = Point_segs ;
ai_cloak_info Ai_cloak_info [ MAX_AI_CLOAK_INFO ] ;
fix Boss_cloak_start_time = 0 ;
fix Boss_cloak_end_time = 0 ;
fix Last_teleport_time = 0 ;
fix Boss_teleport_interval = F1_0 * 8 ;
fix Boss_cloak_interval = F1_0 * 10 ; // Time between cloaks
fix Boss_cloak_duration = BOSS_CLOAK_DURATION ;
fix Last_gate_time = 0 ;
fix Gate_interval = F1_0 * 6 ;
fix Boss_dying_start_time ;
int Boss_dying , Boss_dying_sound_playing , Boss_hit_this_frame ;
int Boss_been_hit = 0 ;
// ---------- John: End of variables which must be saved as part of gamesave. ----------
int john_cheats_index_4 ; // PLETCHnnn paint robots
int ai_evaded = 0 ;
# ifndef SHAREWARE
// 0 mech
// 1 green claw
// 2 spider
// 3 josh
// 4 violet
// 5 cloak vulcan
// 6 cloak mech
// 7 brain
// 8 onearm
// 9 plasma
// 10 toaster
// 11 bird
// 12 missile bird
// 13 polyhedron
// 14 baby spider
// 15 mini boss
// 16 super mech
// 17 shareware boss
// 18 cloak-green ; note, gating in this guy benefits player, cloak objects
// 19 vulcan
// 20 toad
// 21 4-claw
// 22 quad-laser
// 23 super boss
// byte Super_boss_gate_list[] = {0, 1, 2, 9, 11, 16, 18, 19, 21, 22, 0, 9, 9, 16, 16, 18, 19, 19, 22, 22};
2006-10-06 14:41:31 +00:00
sbyte Super_boss_gate_list [ ] = { 0 , 1 , 8 , 9 , 10 , 11 , 12 , 15 , 16 , 18 , 19 , 20 , 22 , 0 , 8 , 11 , 19 , 20 , 8 , 20 , 8 } ;
2006-03-20 16:43:15 +00:00
# define MAX_GATE_INDEX ( sizeof(Super_boss_gate_list) / sizeof(Super_boss_gate_list[0]) )
# endif
int Ai_info_enabled = 0 ;
int Robot_firing_enabled = 1 ;
extern int Ugly_robot_cheat , Ugly_robot_texture , Laser_rapid_fire ;
2006-10-06 14:41:31 +00:00
extern sbyte Enable_john_cheat_1 , Enable_john_cheat_2 , Enable_john_cheat_3 , Enable_john_cheat_4 ;
2006-03-20 16:43:15 +00:00
ubyte john_cheats_3 [ 2 * JOHN_CHEATS_SIZE_3 + 1 ] = { KEY_Y ^ 0x67 ,
KEY_E ^ 0x66 ,
KEY_C ^ 0x65 ,
KEY_A ^ 0x64 ,
KEY_N ^ 0x63 ,
KEY_U ^ 0x62 ,
KEY_L ^ 0x61 } ;
# define MAX_AWARENESS_EVENTS 64
typedef struct awareness_event {
short segnum ; // segment the event occurred in
short type ; // type of event, defines behavior
vms_vector pos ; // absolute 3 space location of event
} awareness_event ;
// These globals are set by a call to find_vector_intersection, which is a slow routine,
// so we don't want to call it again (for this object) unless we have to.
vms_vector Hit_pos ;
int Hit_type , Hit_seg ;
fvi_info Hit_data ;
int Num_awareness_events = 0 ;
awareness_event Awareness_events [ MAX_AWARENESS_EVENTS ] ;
vms_vector Believed_player_pos ;
# define AIS_MAX 8
# define AIE_MAX 4
//--unused-- int Processed_this_frame, LastFrameCount;
# ifndef NDEBUG
// Index into this array with ailp->mode
char mode_text [ 8 ] [ 9 ] = {
" STILL " ,
" WANDER " ,
" FOL_PATH " ,
" CHASE_OB " ,
" RUN_FROM " ,
" HIDE " ,
" FOL_PAT2 " ,
" OPENDOR2 "
} ;
// Index into this array with aip->behavior
char behavior_text [ 6 ] [ 9 ] = {
" STILL " ,
" NORMAL " ,
" HIDE " ,
" RUN_FROM " ,
" FOLPATH " ,
" STATION "
} ;
// Index into this array with aip->GOAL_STATE or aip->CURRENT_STATE
char state_text [ 8 ] [ 5 ] = {
" NONE " ,
" REST " ,
" SRCH " ,
" LOCK " ,
" FLIN " ,
" FIRE " ,
" RECO " ,
" ERR_ " ,
} ;
int Ai_animation_test = 0 ;
# endif
// Current state indicates where the robot current is, or has just done.
// Transition table between states for an AI object.
// First dimension is trigger event.
// Second dimension is current state.
// Third dimension is goal state.
// Result is new goal state.
// ERR_ means something impossible has happened.
2006-10-06 14:41:31 +00:00
sbyte Ai_transition_table [ AI_MAX_EVENT ] [ AI_MAX_STATE ] [ AI_MAX_STATE ] = {
2006-03-20 16:43:15 +00:00
{
// Event = AIE_FIRE, a nearby object fired
// none rest srch lock flin fire reco // CURRENT is rows, GOAL is columns
{ AIS_ERR_ , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_FLIN , AIS_FIRE , AIS_RECO } , // none
{ AIS_ERR_ , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_FLIN , AIS_FIRE , AIS_RECO } , // rest
{ AIS_ERR_ , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_FLIN , AIS_FIRE , AIS_RECO } , // search
{ AIS_ERR_ , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_FLIN , AIS_FIRE , AIS_RECO } , // lock
{ AIS_ERR_ , AIS_REST , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_FIRE , AIS_RECO } , // flinch
{ AIS_ERR_ , AIS_FIRE , AIS_FIRE , AIS_FIRE , AIS_FLIN , AIS_FIRE , AIS_RECO } , // fire
{ AIS_ERR_ , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_FLIN , AIS_FIRE , AIS_FIRE } // recoil
} ,
// Event = AIE_HITT, a nearby object was hit (or a wall was hit)
{
{ AIS_ERR_ , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_FLIN , AIS_FIRE , AIS_RECO } ,
{ AIS_ERR_ , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_FLIN , AIS_FIRE , AIS_RECO } ,
{ AIS_ERR_ , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_FLIN , AIS_FIRE , AIS_RECO } ,
{ AIS_ERR_ , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_FLIN , AIS_FIRE , AIS_RECO } ,
{ AIS_ERR_ , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_FLIN , AIS_FLIN } ,
{ AIS_ERR_ , AIS_REST , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_FIRE , AIS_RECO } ,
{ AIS_ERR_ , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_FLIN , AIS_FIRE , AIS_FIRE }
} ,
// Event = AIE_COLL, player collided with robot
{
{ AIS_ERR_ , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_FLIN , AIS_FIRE , AIS_RECO } ,
{ AIS_ERR_ , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_FLIN , AIS_FIRE , AIS_RECO } ,
{ AIS_ERR_ , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_FLIN , AIS_FIRE , AIS_RECO } ,
{ AIS_ERR_ , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_FLIN , AIS_FIRE , AIS_RECO } ,
{ AIS_ERR_ , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_LOCK , AIS_FLIN , AIS_FLIN } ,
{ AIS_ERR_ , AIS_REST , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_FIRE , AIS_RECO } ,
{ AIS_ERR_ , AIS_LOCK , AIS_LOCK , AIS_LOCK , AIS_FLIN , AIS_FIRE , AIS_FIRE }
} ,
// Event = AIE_HURT, player hurt robot (by firing at and hitting it)
// Note, this doesn't necessarily mean the robot JUST got hit, only that that is the most recent thing that happened.
{
{ AIS_ERR_ , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN } ,
{ AIS_ERR_ , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN } ,
{ AIS_ERR_ , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN } ,
{ AIS_ERR_ , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN } ,
{ AIS_ERR_ , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN } ,
{ AIS_ERR_ , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN } ,
{ AIS_ERR_ , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN , AIS_FLIN }
}
} ;
ubyte john_cheats_2 [ 2 * JOHN_CHEATS_SIZE_2 ] = { KEY_P ^ 0x00 ^ 0x43 , 0x66 ,
KEY_O ^ 0x10 ^ 0x43 , 0x11 ,
KEY_R ^ 0x20 ^ 0x43 , 0x8 ,
KEY_G ^ 0x30 ^ 0x43 , 0x2 ,
KEY_Y ^ 0x40 ^ 0x43 , 0x0 ,
KEY_S ^ 0x50 ^ 0x43 } ;
// ---------------------------------------------------------
// On entry, N_robot_types had darn sure better be set.
// Mallocs N_robot_types robot_info structs into global Robot_info.
void init_ai_system ( void )
{
#if 0
int i ;
mprintf ( ( 0 , " Trying to malloc %i bytes for Robot_info. \n " , N_robot_types * sizeof ( * Robot_info ) ) ) ;
Robot_info = ( robot_info * ) malloc ( N_robot_types * sizeof ( * Robot_info ) ) ;
mprintf ( ( 0 , " Robot_info = %i \n " , Robot_info ) ) ;
for ( i = 0 ; i < N_robot_types ; i + + ) {
Robot_info [ i ] . field_of_view = F1_0 / 2 ;
Robot_info [ i ] . firing_wait = F1_0 ;
Robot_info [ i ] . turn_time = F1_0 * 2 ;
Robot_info [ i ] . fire_power = F1_0 ;
Robot_info [ i ] . shield = F1_0 / 2 ;
Robot_info [ i ] . max_speed = F1_0 * 10 ;
Robot_info [ i ] . always_0xabcd = 0xabcd ;
}
# endif
}
void john_cheat_func_1 ( int key )
{
if ( ! Cheats_enabled )
return ;
if ( key = = ( john_cheats_1 [ john_cheats_index_1 ] ^ ( john_cheats_index_1 < < 4 ) ^ 0x34 ) ) {
john_cheats_index_1 + + ;
if ( john_cheats_index_1 = = JOHN_CHEATS_SIZE_1 ) {
do_controlcen_destroyed_stuff ( NULL ) ;
john_cheats_index_1 = 0 ;
digi_play_sample ( SOUND_CHEATER , F1_0 ) ;
}
} else
john_cheats_index_1 = 0 ;
}
// ---------------------------------------------------------------------------------------------------------------------
// Given a behavior, set initial mode.
int ai_behavior_to_mode ( int behavior )
{
switch ( behavior ) {
case AIB_STILL : return AIM_STILL ;
case AIB_NORMAL : return AIM_CHASE_OBJECT ;
case AIB_HIDE : return AIM_HIDE ;
case AIB_RUN_FROM : return AIM_RUN_FROM_OBJECT ;
case AIB_FOLLOW_PATH : return AIM_FOLLOW_PATH ;
case AIB_STATION : return AIM_STILL ;
default : Int3 ( ) ; // Contact Mike: Error, illegal behavior type
}
return AIM_STILL ;
}
// ---------------------------------------------------------------------------------------------------------------------
// Call every time the player starts a new ship.
void ai_init_boss_for_ship ( void )
{
Boss_been_hit = 0 ;
}
// ---------------------------------------------------------------------------------------------------------------------
// initial_mode == -1 means leave mode unchanged.
void init_ai_object ( int objnum , int behavior , int hide_segment )
{
object * objp = & Objects [ objnum ] ;
ai_static * aip = & objp - > ctype . ai_info ;
ai_local * ailp = & Ai_local_info [ objnum ] ;
# ifdef DEST_SAT
if ( ! ( Game_mode & GM_MULTI ) & & Robot_info [ objp - > id ] . boss_flag ) {
mprintf ( ( 0 , " Current_level_num = %i, Last_level = %i \n " , Current_level_num , Last_level ) ) ;
if ( Current_level_num ! = Last_level ) {
mprintf ( ( 0 , " Removing boss, object num = %i \n " , objnum ) ) ;
objp - > id = 0 ;
objp - > flags | = OF_SHOULD_BE_DEAD ;
}
}
# endif
if ( behavior = = 0 ) {
// mprintf((0, "Behavior of 0 for object #%i, bashing to AIB_NORMAL.\n", objnum));
behavior = AIB_NORMAL ;
objp - > ctype . ai_info . behavior = behavior ;
}
// mprintf((0, "Initializing object #%i\n", objnum));
// mode is now set from the Robot dialog, so this should get overwritten.
ailp - > mode = AIM_STILL ;
ailp - > previous_visibility = 0 ;
if ( behavior ! = - 1 ) {
aip - > behavior = behavior ;
ailp - > mode = ai_behavior_to_mode ( aip - > behavior ) ;
} else if ( ! ( ( aip - > behavior > = MIN_BEHAVIOR ) & & ( aip - > behavior < = MAX_BEHAVIOR ) ) ) {
mprintf ( ( 0 , " [obj %i -> normal] " , objnum ) ) ;
aip - > behavior = AIB_NORMAL ;
}
// This is astonishingly stupid! This routine gets called by matcens! KILL KILL KILL!!! Point_segs_free_ptr = Point_segs;
vm_vec_zero ( & objp - > mtype . phys_info . velocity ) ;
// -- ailp->wait_time = F1_0*5;
ailp - > player_awareness_time = 0 ;
ailp - > player_awareness_type = 0 ;
aip - > GOAL_STATE = AIS_SRCH ;
aip - > CURRENT_STATE = AIS_REST ;
ailp - > time_player_seen = GameTime ;
ailp - > next_misc_sound_time = GameTime ;
ailp - > time_player_sound_attacked = GameTime ;
if ( ( behavior = = AIB_HIDE ) | | ( behavior = = AIB_FOLLOW_PATH ) | | ( behavior = = AIB_STATION ) | | ( behavior = = AIB_RUN_FROM ) ) {
aip - > hide_segment = hide_segment ;
ailp - > goal_segment = hide_segment ;
aip - > hide_index = - 1 ; // This means the path has not yet been created.
aip - > cur_path_index = 0 ;
}
aip - > SKIP_AI_COUNT = 0 ;
if ( Robot_info [ objp - > id ] . cloak_type = = RI_CLOAKED_ALWAYS )
aip - > CLOAKED = 1 ;
else
aip - > CLOAKED = 0 ;
objp - > mtype . phys_info . flags | = ( PF_BOUNCE | PF_TURNROLL ) ;
aip - > REMOTE_OWNER = - 1 ;
}
void john_cheat_func_2 ( int key )
{
if ( ! Cheats_enabled )
return ;
if ( key = = ( john_cheats_2 [ 2 * john_cheats_index_2 ] ^ ( john_cheats_index_2 < < 4 ) ^ 0x43 ) ) {
john_cheats_index_2 + + ;
if ( john_cheats_index_2 = = JOHN_CHEATS_SIZE_2 ) {
Laser_rapid_fire = 0xBADA55 ;
do_megawow_powerup ( 200 ) ;
john_cheats_index_2 = 0 ;
digi_play_sample ( SOUND_CHEATER , F1_0 ) ;
}
} else
john_cheats_index_2 = 0 ;
}
// ---------------------------------------------------------------------------------------------------------------------
void init_ai_objects ( void )
{
int i ;
Point_segs_free_ptr = Point_segs ;
for ( i = 0 ; i < MAX_OBJECTS ; i + + ) {
object * objp = & Objects [ i ] ;
if ( objp - > control_type = = CT_AI )
init_ai_object ( i , objp - > ctype . ai_info . behavior , objp - > ctype . ai_info . hide_segment ) ;
}
init_boss_segments ( Boss_teleport_segs , & Num_boss_teleport_segs , 1 ) ;
# ifndef SHAREWARE
init_boss_segments ( Boss_gate_segs , & Num_boss_gate_segs , 0 ) ;
# endif
Boss_dying_sound_playing = 0 ;
Boss_dying = 0 ;
Boss_been_hit = 0 ;
# ifndef SHAREWARE
Gate_interval = F1_0 * 5 - Difficulty_level * F1_0 / 2 ;
# endif
Ai_initialized = 1 ;
}
int Lunacy = 0 ;
int Diff_save = 1 ;
fix Firing_wait_copy [ MAX_ROBOT_TYPES ] ;
2006-10-06 14:41:31 +00:00
sbyte Rapidfire_count_copy [ MAX_ROBOT_TYPES ] ;
2006-03-20 16:43:15 +00:00
void do_lunacy_on ( void )
{
int i ;
if ( ! Lunacy ) {
Lunacy = 1 ;
Diff_save = Difficulty_level ;
Difficulty_level = NDL - 1 ;
for ( i = 0 ; i < MAX_ROBOT_TYPES ; i + + ) {
Firing_wait_copy [ i ] = Robot_info [ i ] . firing_wait [ NDL - 1 ] ;
Rapidfire_count_copy [ i ] = Robot_info [ i ] . rapidfire_count [ NDL - 1 ] ;
Robot_info [ i ] . firing_wait [ NDL - 1 ] = Robot_info [ i ] . firing_wait [ 1 ] ;
Robot_info [ i ] . rapidfire_count [ NDL - 1 ] = Robot_info [ i ] . rapidfire_count [ 1 ] ;
}
}
}
void do_lunacy_off ( void )
{
int i ;
if ( Lunacy ) {
Lunacy = 0 ;
for ( i = 0 ; i < MAX_ROBOT_TYPES ; i + + ) {
Robot_info [ i ] . firing_wait [ NDL - 1 ] = Firing_wait_copy [ i ] ;
Robot_info [ i ] . rapidfire_count [ NDL - 1 ] = Rapidfire_count_copy [ i ] ;
}
Difficulty_level = Diff_save ;
}
}
void john_cheat_func_3 ( int key )
{
if ( ! Cheats_enabled )
return ;
if ( key = = ( john_cheats_3 [ JOHN_CHEATS_SIZE_3 - john_cheats_index_3 ] ^ ( 0x61 + john_cheats_index_3 ) ) ) {
if ( john_cheats_index_3 = = 4 )
john_cheats_index_3 + + ;
john_cheats_index_3 + + ;
if ( john_cheats_index_3 = = JOHN_CHEATS_SIZE_3 + 1 ) {
if ( Lunacy ) {
do_lunacy_off ( ) ;
hud_message ( MSGC_GAME_FEEDBACK , TXT_NO_LUNACY ) ;
} else {
do_lunacy_on ( ) ;
hud_message ( MSGC_GAME_FEEDBACK , TXT_LUNACY ) ;
digi_play_sample ( SOUND_CHEATER , F1_0 ) ;
}
john_cheats_index_3 = 0 ;
}
} else
john_cheats_index_3 = 0 ;
}
// ----------------------------------------------------------------
// Do *dest = *delta unless:
// *delta is pretty small
// and they are of different signs.
void set_rotvel_and_saturate ( fix * dest , fix delta )
{
if ( ( delta ^ * dest ) < 0 ) {
if ( abs ( delta ) < F1_0 / 8 ) {
// mprintf((0, "D"));
* dest = delta / 4 ;
} else
// mprintf((0, "d"));
* dest = delta ;
} else {
// mprintf((0, "!"));
* dest = delta ;
}
}
//--debug-- #ifndef NDEBUG
//--debug-- int Total_turns=0;
//--debug-- int Prevented_turns=0;
//--debug-- #endif
# define AI_TURN_SCALE 1
# define BABY_SPIDER_ID 14
extern void physics_turn_towards_vector ( vms_vector * goal_vector , object * obj , fix rate ) ;
//-------------------------------------------------------------------------------------------
void ai_turn_towards_vector ( vms_vector * goal_vector , object * objp , fix rate )
{
vms_vector new_fvec ;
fix dot ;
if ( ( objp - > id = = BABY_SPIDER_ID ) & & ( objp - > type = = OBJ_ROBOT ) ) {
physics_turn_towards_vector ( goal_vector , objp , rate ) ;
return ;
}
new_fvec = * goal_vector ;
dot = vm_vec_dot ( goal_vector , & objp - > orient . fvec ) ;
if ( dot < ( F1_0 - FrameTime / 2 ) ) {
fix mag ;
fix new_scale = fixdiv ( FrameTime * AI_TURN_SCALE , rate ) ;
vm_vec_scale ( & new_fvec , new_scale ) ;
vm_vec_add2 ( & new_fvec , & objp - > orient . fvec ) ;
mag = vm_vec_normalize_quick ( & new_fvec ) ;
if ( mag < F1_0 / 256 ) {
mprintf ( ( 1 , " Degenerate vector in ai_turn_towards_vector (mag = %7.3f) \n " , f2fl ( mag ) ) ) ;
new_fvec = * goal_vector ; // if degenerate vector, go right to goal
}
}
// // Every 8th time, do a correct matrix create, 7/8 time, do a quick one.
// if (d_rand() < 0x1000)
vm_vector_2_matrix ( & objp - > orient , & new_fvec , NULL , & objp - > orient . rvec ) ;
// else
// vm_vector_2_matrix_norm(&objp->orient, &new_fvec, NULL, &objp->orient.rvec);
//--{
//--vms_vector tvec;
//--fix mag;
//--tvec = objp->orient.fvec;
//--mag = vm_vec_mag(&tvec);
//--mprintf((0, "mags = %7.3f ", f2fl(mag)));
//--
//--tvec = objp->orient.uvec;
//--mag = vm_vec_mag(&tvec);
//--mprintf((0, "%7.3f ", f2fl(mag)));
//--
//--tvec = objp->orient.rvec;
//--mag = vm_vec_mag(&tvec);
//--mprintf((0, "%7.3f\n", f2fl(mag)));
//--}
//--simpler, but buggy: // The cross product of the forward vector with the right vector is the up vector
//--simpler, but buggy: vm_vec_cross(&new_uvec, &new_fvec, &objp->orient.rvec);
//--simpler, but buggy: vm_vec_cross(&new_rvec, &new_uvec, &new_fvec);
//--simpler, but buggy:
//--simpler, but buggy: objp->orient.fvec = new_fvec;
//--simpler, but buggy: objp->orient.rvec = new_rvec;
//--simpler, but buggy: objp->orient.uvec = new_uvec;
}
// --------------------------------------------------------------------------------------------------------------------
void ai_turn_randomly ( vms_vector * vec_to_player , object * obj , fix rate , int previous_visibility )
{
vms_vector curvec ;
// Random turning looks too stupid, so 1/4 of time, cheat.
if ( previous_visibility )
if ( d_rand ( ) > 0x7400 ) {
ai_turn_towards_vector ( vec_to_player , obj , rate ) ;
return ;
}
//--debug-- if (d_rand() > 0x6000)
//--debug-- Prevented_turns++;
curvec = obj - > mtype . phys_info . rotvel ;
curvec . y + = F1_0 / 64 ;
curvec . x + = curvec . y / 6 ;
curvec . y + = curvec . z / 4 ;
curvec . z + = curvec . x / 10 ;
if ( abs ( curvec . x ) > F1_0 / 8 ) curvec . x / = 4 ;
if ( abs ( curvec . y ) > F1_0 / 8 ) curvec . y / = 4 ;
if ( abs ( curvec . z ) > F1_0 / 8 ) curvec . z / = 4 ;
obj - > mtype . phys_info . rotvel = curvec ;
}
// Overall_agitation affects:
// Widens field of view. Field of view is in range 0..1 (specified in bitmaps.tbl as N/360 degrees).
// Overall_agitation/128 subtracted from field of view, making robots see wider.
// Increases distance to which robot will search to create path to player by Overall_agitation/8 segments.
// Decreases wait between fire times by Overall_agitation/64 seconds.
void john_cheat_func_4 ( int key )
{
if ( ! Cheats_enabled )
return ;
switch ( john_cheats_index_4 ) {
case 3 :
if ( key = = KEY_T )
john_cheats_index_4 + + ;
else
john_cheats_index_4 = 0 ;
break ;
case 1 :
if ( key = = KEY_L )
john_cheats_index_4 + + ;
else
john_cheats_index_4 = 0 ;
break ;
case 2 :
if ( key = = KEY_E )
john_cheats_index_4 + + ;
else
john_cheats_index_4 = 0 ;
break ;
case 0 :
if ( key = = KEY_P )
john_cheats_index_4 + + ;
break ;
case 4 :
if ( key = = KEY_C )
john_cheats_index_4 + + ;
else
john_cheats_index_4 = 0 ;
break ;
case 5 :
if ( key = = KEY_H )
john_cheats_index_4 + + ;
else
john_cheats_index_4 = 0 ;
break ;
case 6 :
Ugly_robot_texture = 0 ;
case 7 :
case 8 :
if ( ( key > = KEY_1 ) & & ( key < = KEY_0 ) ) {
john_cheats_index_4 + + ;
Ugly_robot_texture * = 10 ;
if ( key ! = KEY_0 )
Ugly_robot_texture + = key - 1 ;
if ( john_cheats_index_4 = = 9 ) {
if ( Ugly_robot_texture = = 999 ) {
Ugly_robot_cheat = 0 ;
hud_message ( MSGC_GAME_FEEDBACK , TXT_ROBOT_PAINTING_OFF ) ;
} else {
hud_message ( MSGC_GAME_FEEDBACK , TXT_ROBOT_PAINTING_ON , Ugly_robot_texture ) ;
Ugly_robot_cheat = 0xBADA55 ;
}
mprintf ( ( 0 , " Paint value = %i \n " , Ugly_robot_texture ) ) ;
john_cheats_index_4 = 0 ;
}
} else
john_cheats_index_4 = 0 ;
break ;
default :
john_cheats_index_4 = 0 ;
}
}
// --------------------------------------------------------------------------------------------------------------------
// Returns:
// 0 Player is not visible from object, obstruction or something.
// 1 Player is visible, but not in field of view.
// 2 Player is visible and in field of view.
// Note: Uses Believed_player_pos as player's position for cloak effect.
// NOTE: Will destructively modify *pos if *pos is outside the mine.
int player_is_visible_from_object ( object * objp , vms_vector * pos , fix field_of_view , vms_vector * vec_to_player )
{
fix dot ;
fvi_query fq ;
fq . p0 = pos ;
if ( ( pos - > x ! = objp - > pos . x ) | | ( pos - > y ! = objp - > pos . y ) | | ( pos - > z ! = objp - > pos . z ) ) {
int segnum = find_point_seg ( pos , objp - > segnum ) ;
if ( segnum = = - 1 ) {
fq . startseg = objp - > segnum ;
* pos = objp - > pos ;
mprintf ( ( 1 , " Object %i, gun is outside mine, moving towards center. \n " , objp - Objects ) ) ;
move_towards_segment_center ( objp ) ;
} else
fq . startseg = segnum ;
} else
fq . startseg = objp - > segnum ;
fq . p1 = & Believed_player_pos ;
fq . rad = F1_0 / 4 ;
fq . thisobjnum = objp - Objects ;
fq . ignore_obj_list = NULL ;
fq . flags = FQ_TRANSWALL | FQ_CHECK_OBJS ; //what about trans walls???
Hit_type = find_vector_intersection ( & fq , & Hit_data ) ;
Hit_pos = Hit_data . hit_pnt ;
Hit_seg = Hit_data . hit_seg ;
if ( ( Hit_type = = HIT_NONE ) | | ( ( Hit_type = = HIT_OBJECT ) & & ( Hit_data . hit_object = = Players [ Player_num ] . objnum ) ) ) {
dot = vm_vec_dot ( vec_to_player , & objp - > orient . fvec ) ;
// mprintf((0, "Fvec = [%5.2f %5.2f %5.2f], vec_to_player = [%5.2f %5.2f %5.2f], dot = %7.3f\n", f2fl(objp->orient.fvec.x), f2fl(objp->orient.fvec.y), f2fl(objp->orient.fvec.z), f2fl(vec_to_player->x), f2fl(vec_to_player->y), f2fl(vec_to_player->z), f2fl(dot)));
if ( dot > field_of_view - ( Overall_agitation < < 9 ) ) {
// mprintf((0, "I can see you!\n"));
return 2 ;
} else {
// mprintf((0, "Damn, I could see you if I were looking...\n"));
return 1 ;
}
} else {
// mprintf((0, " ** Where are you? **\n"));
return 0 ;
}
}
// ------------------------------------------------------------------------------------------------------------------
// Return 1 if animates, else return 0
int do_silly_animation ( object * objp )
{
int objnum = objp - Objects ;
jointpos * jp_list ;
int robot_type , gun_num , robot_state , num_joint_positions ;
polyobj_info * pobj_info = & objp - > rtype . pobj_info ;
ai_static * aip = & objp - > ctype . ai_info ;
// ai_local *ailp = &Ai_local_info[objnum];
int num_guns , at_goal ;
int attack_type ;
int flinch_attack_scale = 1 ;
robot_type = objp - > id ;
num_guns = Robot_info [ robot_type ] . n_guns ;
attack_type = Robot_info [ robot_type ] . attack_type ;
if ( num_guns = = 0 ) {
// mprintf((0, "Object #%i of type #%i has 0 guns.\n", objp-Objects, robot_type));
return 0 ;
}
// This is a hack. All positions should be based on goal_state, not GOAL_STATE.
robot_state = Mike_to_matt_xlate [ aip - > GOAL_STATE ] ;
// previous_robot_state = Mike_to_matt_xlate[aip->CURRENT_STATE];
if ( attack_type ) // && ((robot_state == AS_FIRE) || (robot_state == AS_RECOIL)))
flinch_attack_scale = Attack_scale ;
else if ( ( robot_state = = AS_FLINCH ) | | ( robot_state = = AS_RECOIL ) )
flinch_attack_scale = Flinch_scale ;
at_goal = 1 ;
for ( gun_num = 0 ; gun_num < = num_guns ; gun_num + + ) {
int joint ;
num_joint_positions = robot_get_anim_state ( & jp_list , robot_type , gun_num , robot_state ) ;
for ( joint = 0 ; joint < num_joint_positions ; joint + + ) {
fix delta_angle , delta_2 ;
int jointnum = jp_list [ joint ] . jointnum ;
vms_angvec * jp = & jp_list [ joint ] . angles ;
vms_angvec * pobjp = & pobj_info - > anim_angles [ jointnum ] ;
if ( jointnum > = Polygon_models [ objp - > rtype . pobj_info . model_num ] . n_models ) {
//Int3(); // Contact Mike: incompatible data, illegal jointnum, problem in pof file?
continue ;
}
if ( jp - > p ! = pobjp - > p ) {
if ( gun_num = = 0 )
at_goal = 0 ;
Ai_local_info [ objnum ] . goal_angles [ jointnum ] . p = jp - > p ;
delta_angle = jp - > p - pobjp - > p ;
if ( delta_angle > = F1_0 / 2 )
delta_2 = - ANIM_RATE ;
else if ( delta_angle > = 0 )
delta_2 = ANIM_RATE ;
else if ( delta_angle > = - F1_0 / 2 )
delta_2 = - ANIM_RATE ;
else
delta_2 = ANIM_RATE ;
if ( flinch_attack_scale ! = 1 )
delta_2 * = flinch_attack_scale ;
Ai_local_info [ objnum ] . delta_angles [ jointnum ] . p = delta_2 / DELTA_ANG_SCALE ; // complete revolutions per second
}
if ( jp - > b ! = pobjp - > b ) {
if ( gun_num = = 0 )
at_goal = 0 ;
Ai_local_info [ objnum ] . goal_angles [ jointnum ] . b = jp - > b ;
delta_angle = jp - > b - pobjp - > b ;
if ( delta_angle > = F1_0 / 2 )
delta_2 = - ANIM_RATE ;
else if ( delta_angle > = 0 )
delta_2 = ANIM_RATE ;
else if ( delta_angle > = - F1_0 / 2 )
delta_2 = - ANIM_RATE ;
else
delta_2 = ANIM_RATE ;
if ( flinch_attack_scale ! = 1 )
delta_2 * = flinch_attack_scale ;
Ai_local_info [ objnum ] . delta_angles [ jointnum ] . b = delta_2 / DELTA_ANG_SCALE ; // complete revolutions per second
}
if ( jp - > h ! = pobjp - > h ) {
if ( gun_num = = 0 )
at_goal = 0 ;
Ai_local_info [ objnum ] . goal_angles [ jointnum ] . h = jp - > h ;
delta_angle = jp - > h - pobjp - > h ;
if ( delta_angle > = F1_0 / 2 )
delta_2 = - ANIM_RATE ;
else if ( delta_angle > = 0 )
delta_2 = ANIM_RATE ;
else if ( delta_angle > = - F1_0 / 2 )
delta_2 = - ANIM_RATE ;
else
delta_2 = ANIM_RATE ;
if ( flinch_attack_scale ! = 1 )
delta_2 * = flinch_attack_scale ;
Ai_local_info [ objnum ] . delta_angles [ jointnum ] . h = delta_2 / DELTA_ANG_SCALE ; // complete revolutions per second
}
}
if ( at_goal ) {
//ai_static *aip = &objp->ctype.ai_info;
ai_local * ailp = & Ai_local_info [ objp - Objects ] ;
ailp - > achieved_state [ gun_num ] = ailp - > goal_state [ gun_num ] ;
if ( ailp - > achieved_state [ gun_num ] = = AIS_RECO )
ailp - > goal_state [ gun_num ] = AIS_FIRE ;
if ( ailp - > achieved_state [ gun_num ] = = AIS_FLIN )
ailp - > goal_state [ gun_num ] = AIS_LOCK ;
}
}
if ( at_goal = = 1 ) //num_guns)
aip - > CURRENT_STATE = aip - > GOAL_STATE ;
return 1 ;
}
// ------------------------------------------------------------------------------------------
// Move all sub-objects in an object towards their goals.
// Current orientation of object is at: pobj_info.anim_angles
// Goal orientation of object is at: ai_info.goal_angles
// Delta orientation of object is at: ai_info.delta_angles
void ai_frame_animation ( object * objp )
{
int objnum = objp - Objects ;
int joint ;
int num_joints ;
num_joints = Polygon_models [ objp - > rtype . pobj_info . model_num ] . n_models ;
for ( joint = 1 ; joint < num_joints ; joint + + ) {
fix delta_to_goal ;
fix scaled_delta_angle ;
vms_angvec * curangp = & objp - > rtype . pobj_info . anim_angles [ joint ] ;
vms_angvec * goalangp = & Ai_local_info [ objnum ] . goal_angles [ joint ] ;
vms_angvec * deltaangp = & Ai_local_info [ objnum ] . delta_angles [ joint ] ;
# ifndef NDEBUG
if ( Ai_animation_test ) {
printf ( " %i: [%7.3f %7.3f %7.3f] [%7.3f %7.3f %7.3f] \n " , joint , f2fl ( curangp - > p ) , f2fl ( curangp - > b ) , f2fl ( curangp - > h ) , f2fl ( goalangp - > p ) , f2fl ( goalangp - > b ) , f2fl ( goalangp - > h ) ) ;
}
# endif
delta_to_goal = goalangp - > p - curangp - > p ;
if ( delta_to_goal > 32767 )
delta_to_goal = delta_to_goal - 65536 ;
else if ( delta_to_goal < - 32767 )
delta_to_goal = 65536 + delta_to_goal ;
if ( delta_to_goal ) {
scaled_delta_angle = fixmul ( deltaangp - > p , FrameTime ) * DELTA_ANG_SCALE ;
curangp - > p + = scaled_delta_angle ;
if ( abs ( delta_to_goal ) < abs ( scaled_delta_angle ) )
curangp - > p = goalangp - > p ;
}
delta_to_goal = goalangp - > b - curangp - > b ;
if ( delta_to_goal > 32767 )
delta_to_goal = delta_to_goal - 65536 ;
else if ( delta_to_goal < - 32767 )
delta_to_goal = 65536 + delta_to_goal ;
if ( delta_to_goal ) {
scaled_delta_angle = fixmul ( deltaangp - > b , FrameTime ) * DELTA_ANG_SCALE ;
curangp - > b + = scaled_delta_angle ;
if ( abs ( delta_to_goal ) < abs ( scaled_delta_angle ) )
curangp - > b = goalangp - > b ;
}
delta_to_goal = goalangp - > h - curangp - > h ;
if ( delta_to_goal > 32767 )
delta_to_goal = delta_to_goal - 65536 ;
else if ( delta_to_goal < - 32767 )
delta_to_goal = 65536 + delta_to_goal ;
if ( delta_to_goal ) {
scaled_delta_angle = fixmul ( deltaangp - > h , FrameTime ) * DELTA_ANG_SCALE ;
curangp - > h + = scaled_delta_angle ;
if ( abs ( delta_to_goal ) < abs ( scaled_delta_angle ) )
curangp - > h = goalangp - > h ;
}
}
}
// ----------------------------------------------------------------------------------
void set_next_fire_time ( ai_local * ailp , robot_info * robptr )
{
ailp - > rapidfire_count + + ;
if ( ailp - > rapidfire_count < robptr - > rapidfire_count [ Difficulty_level ] ) {
ailp - > next_fire = min ( F1_0 / 8 , robptr - > firing_wait [ Difficulty_level ] / 2 ) ;
} else {
ailp - > rapidfire_count = 0 ;
ailp - > next_fire = robptr - > firing_wait [ Difficulty_level ] ;
}
}
// ----------------------------------------------------------------------------------
// When some robots collide with the player, they attack.
// If player is cloaked, then robot probably didn't actually collide, deal with that here.
void do_ai_robot_hit_attack ( object * robot , object * player , vms_vector * collision_point )
{
ai_local * ailp = & Ai_local_info [ robot - Objects ] ;
robot_info * robptr = & Robot_info [ robot - > id ] ;
//#ifndef NDEBUG
if ( ! Robot_firing_enabled )
return ;
//#endif
// If player is dead, stop firing.
if ( Objects [ Players [ Player_num ] . objnum ] . type = = OBJ_GHOST )
return ;
if ( robptr - > attack_type = = 1 ) {
if ( ailp - > next_fire < = 0 ) {
if ( ! ( Players [ Player_num ] . flags & PLAYER_FLAGS_CLOAKED ) )
if ( vm_vec_dist_quick ( & ConsoleObject - > pos , & robot - > pos ) < robot - > size + ConsoleObject - > size + F1_0 * 2 )
collide_player_and_nasty_robot ( player , robot , collision_point ) ;
robot - > ctype . ai_info . GOAL_STATE = AIS_RECO ;
set_next_fire_time ( ailp , robptr ) ;
}
}
}
extern int Player_exploded ;
// --------------------------------------------------------------------------------------------------------------------
// Note: Parameter vec_to_player is only passed now because guns which aren't on the forward vector from the
// center of the robot will not fire right at the player. We need to aim the guns at the player. Barring that, we cheat.
// When this routine is complete, the parameter vec_to_player should not be necessary.
void ai_fire_laser_at_player ( object * obj , vms_vector * fire_point )
{
int objnum = obj - Objects ;
ai_local * ailp = & Ai_local_info [ objnum ] ;
robot_info * robptr = & Robot_info [ obj - > id ] ;
vms_vector fire_vec ;
vms_vector bpp_diff ;
if ( ! Robot_firing_enabled )
return ;
# ifndef NDEBUG
// We should never be coming here for the green guy, as he has no laser!
if ( robptr - > attack_type = = 1 )
Int3 ( ) ; // Contact Mike: This is impossible.
# endif
if ( obj - > control_type = = CT_MORPH )
return ;
// If player is exploded, stop firing.
if ( Player_exploded )
return ;
// If player is cloaked, maybe don't fire based on how long cloaked and randomness.
if ( Players [ Player_num ] . flags & PLAYER_FLAGS_CLOAKED ) {
fix cloak_time = Ai_cloak_info [ objnum % MAX_AI_CLOAK_INFO ] . last_time ;
if ( GameTime - cloak_time > CLOAK_TIME_MAX / 4 )
if ( d_rand ( ) > fixdiv ( GameTime - cloak_time , CLOAK_TIME_MAX ) / 2 ) {
set_next_fire_time ( ailp , robptr ) ;
return ;
}
}
//-- // Find segment containing laser fire position. If the robot is straddling a segment, the position from
//-- // which it fires may be in a different segment, which is bad news for find_vector_intersection. So, cast
//-- // a ray from the object center (whose segment we know) to the laser position. Then, in the call to Laser_create_new
//-- // use the data returned from this call to find_vector_intersection.
//-- // Note that while find_vector_intersection is pretty slow, it is not terribly slow if the destination point is
//-- // in the same segment as the source point.
//--
//-- fq.p0 = &obj->pos;
//-- fq.startseg = obj->segnum;
//-- fq.p1 = fire_point;
//-- fq.rad = 0;
//-- fq.thisobjnum = obj-Objects;
//-- fq.ignore_obj_list = NULL;
//-- fq.flags = FQ_TRANSWALL | FQ_CHECK_OBJS; //what about trans walls???
//--
//-- fate = find_vector_intersection(&fq, &hit_data);
//-- if (fate != HIT_NONE)
//-- return;
// Set position to fire at based on difficulty level.
bpp_diff . x = Believed_player_pos . x + ( d_rand ( ) - 16384 ) * ( NDL - Difficulty_level - 1 ) * 4 ;
bpp_diff . y = Believed_player_pos . y + ( d_rand ( ) - 16384 ) * ( NDL - Difficulty_level - 1 ) * 4 ;
bpp_diff . z = Believed_player_pos . z + ( d_rand ( ) - 16384 ) * ( NDL - Difficulty_level - 1 ) * 4 ;
// Half the time fire at the player, half the time lead the player.
if ( d_rand ( ) > 16384 ) {
vm_vec_normalized_dir_quick ( & fire_vec , & bpp_diff , fire_point ) ;
} else {
vms_vector player_direction_vector ;
vm_vec_sub ( & player_direction_vector , & bpp_diff , & bpp_diff ) ;
// If player is not moving, fire right at him!
// Note: If the robot fires in the direction of its forward vector, this is bad because the weapon does not
// come out from the center of the robot; it comes out from the side. So it is common for the weapon to miss
// its target. Ideally, we want to point the guns at the player. For now, just fire right at the player.
if ( ( abs ( player_direction_vector . x < 0x10000 ) ) & & ( abs ( player_direction_vector . y < 0x10000 ) ) & & ( abs ( player_direction_vector . z < 0x10000 ) ) ) {
vm_vec_normalized_dir_quick ( & fire_vec , & bpp_diff , fire_point ) ;
// Player is moving. Determine where the player will be at the end of the next frame if he doesn't change his
// behavior. Fire at exactly that point. This isn't exactly what you want because it will probably take the laser
// a different amount of time to get there, since it will probably be a different distance from the player.
// So, that's why we write games, instead of guiding missiles...
} else {
vm_vec_sub ( & fire_vec , & bpp_diff , fire_point ) ;
vm_vec_scale ( & fire_vec , fixmul ( Weapon_info [ Robot_info [ obj - > id ] . weapon_type ] . speed [ Difficulty_level ] , FrameTime ) ) ;
vm_vec_add2 ( & fire_vec , & player_direction_vector ) ;
vm_vec_normalize_quick ( & fire_vec ) ;
}
}
//#ifndef NDEBUG
// if (robptr->boss_flag)
// mprintf((0, "Boss (%i) fires!\n", obj-Objects));
//#endif
Laser_create_new_easy ( & fire_vec , fire_point , obj - Objects , robptr - > weapon_type , 1 ) ;
# ifndef SHAREWARE
# ifdef NETWORK
if ( Game_mode & GM_MULTI )
{
ai_multi_send_robot_position ( objnum , - 1 ) ;
multi_send_robot_fire ( objnum , obj - > ctype . ai_info . CURRENT_GUN , & fire_vec ) ;
}
# endif
# endif
create_awareness_event ( obj , PA_NEARBY_ROBOT_FIRED ) ;
set_next_fire_time ( ailp , robptr ) ;
// If the boss fired, allow him to teleport very soon (right after firing, cool!), pending other factors.
if ( robptr - > boss_flag )
Last_teleport_time - = Boss_teleport_interval / 2 ;
}
// --------------------------------------------------------------------------------------------------------------------
// vec_goal must be normalized, or close to it.
void move_towards_vector ( object * objp , vms_vector * vec_goal )
{
physics_info * pptr = & objp - > mtype . phys_info ;
fix speed , dot , max_speed ;
robot_info * robptr = & Robot_info [ objp - > id ] ;
vms_vector vel ;
// Trying to move towards player. If forward vector much different than velocity vector,
// bash velocity vector twice as much towards player as usual.
vel = pptr - > velocity ;
vm_vec_normalize_quick ( & vel ) ;
dot = vm_vec_dot ( & vel , & objp - > orient . fvec ) ;
if ( dot < 3 * F1_0 / 4 ) {
// This funny code is supposed to slow down the robot and move his velocity towards his direction
// more quickly than the general code
//-! mprintf((0, "Th "));
pptr - > velocity . x = pptr - > velocity . x / 2 + fixmul ( vec_goal - > x , FrameTime * 32 ) ;
pptr - > velocity . y = pptr - > velocity . y / 2 + fixmul ( vec_goal - > y , FrameTime * 32 ) ;
pptr - > velocity . z = pptr - > velocity . z / 2 + fixmul ( vec_goal - > z , FrameTime * 32 ) ;
} else {
//-! mprintf((0, "Tn "));
pptr - > velocity . x + = fixmul ( vec_goal - > x , FrameTime * 64 ) * ( Difficulty_level + 5 ) / 4 ;
pptr - > velocity . y + = fixmul ( vec_goal - > y , FrameTime * 64 ) * ( Difficulty_level + 5 ) / 4 ;
pptr - > velocity . z + = fixmul ( vec_goal - > z , FrameTime * 64 ) * ( Difficulty_level + 5 ) / 4 ;
}
speed = vm_vec_mag_quick ( & pptr - > velocity ) ;
max_speed = robptr - > max_speed [ Difficulty_level ] ;
// Green guy attacks twice as fast as he moves away.
if ( robptr - > attack_type = = 1 )
max_speed * = 2 ;
if ( speed > max_speed ) {
pptr - > velocity . x = ( pptr - > velocity . x * 3 ) / 4 ;
pptr - > velocity . y = ( pptr - > velocity . y * 3 ) / 4 ;
pptr - > velocity . z = ( pptr - > velocity . z * 3 ) / 4 ;
}
}
// --------------------------------------------------------------------------------------------------------------------
void move_towards_player ( object * objp , vms_vector * vec_to_player )
// vec_to_player must be normalized, or close to it.
{
move_towards_vector ( objp , vec_to_player ) ;
}
// --------------------------------------------------------------------------------------------------------------------
// I am ashamed of this: fast_flag == -1 means normal slide about. fast_flag = 0 means no evasion.
void move_around_player ( object * objp , vms_vector * vec_to_player , int fast_flag )
{
physics_info * pptr = & objp - > mtype . phys_info ;
fix speed ;
robot_info * robptr = & Robot_info [ objp - > id ] ;
int objnum = objp - Objects ;
int dir ;
int dir_change ;
fix ft ;
vms_vector evade_vector ;
int count = 0 ;
if ( fast_flag = = 0 )
return ;
dir_change = 48 ;
ft = FrameTime ;
if ( ft < F1_0 / 32 ) {
dir_change * = 8 ;
count + = 3 ;
} else
while ( ft < F1_0 / 4 ) {
dir_change * = 2 ;
ft * = 2 ;
count + + ;
}
dir = ( FrameCount + ( count + 1 ) * ( objnum * 8 + objnum * 4 + objnum ) ) & dir_change ;
dir > > = ( 4 + count ) ;
Assert ( ( dir > = 0 ) & & ( dir < = 3 ) ) ;
switch ( dir ) {
case 0 :
evade_vector . x = fixmul ( vec_to_player - > z , FrameTime * 32 ) ;
evade_vector . y = fixmul ( vec_to_player - > y , FrameTime * 32 ) ;
evade_vector . z = fixmul ( - vec_to_player - > x , FrameTime * 32 ) ;
break ;
case 1 :
evade_vector . x = fixmul ( - vec_to_player - > z , FrameTime * 32 ) ;
evade_vector . y = fixmul ( vec_to_player - > y , FrameTime * 32 ) ;
evade_vector . z = fixmul ( vec_to_player - > x , FrameTime * 32 ) ;
break ;
case 2 :
evade_vector . x = fixmul ( - vec_to_player - > y , FrameTime * 32 ) ;
evade_vector . y = fixmul ( vec_to_player - > x , FrameTime * 32 ) ;
evade_vector . z = fixmul ( vec_to_player - > z , FrameTime * 32 ) ;
break ;
case 3 :
evade_vector . x = fixmul ( vec_to_player - > y , FrameTime * 32 ) ;
evade_vector . y = fixmul ( - vec_to_player - > x , FrameTime * 32 ) ;
evade_vector . z = fixmul ( vec_to_player - > z , FrameTime * 32 ) ;
break ;
}
// Note: -1 means normal circling about the player. > 0 means fast evasion.
if ( fast_flag > 0 ) {
fix dot ;
// Only take evasive action if looking at player.
// Evasion speed is scaled by percentage of shields left so wounded robots evade less effectively.
dot = vm_vec_dot ( vec_to_player , & objp - > orient . fvec ) ;
if ( ( dot > robptr - > field_of_view [ Difficulty_level ] ) & & ! ( ConsoleObject - > flags & PLAYER_FLAGS_CLOAKED ) ) {
fix damage_scale ;
damage_scale = fixdiv ( objp - > shields , robptr - > strength ) ;
if ( damage_scale > F1_0 )
damage_scale = F1_0 ; // Just in case...
else if ( damage_scale < 0 )
damage_scale = 0 ; // Just in case...
vm_vec_scale ( & evade_vector , i2f ( fast_flag ) + damage_scale ) ;
}
}
pptr - > velocity . x + = evade_vector . x ;
pptr - > velocity . y + = evade_vector . y ;
pptr - > velocity . z + = evade_vector . z ;
speed = vm_vec_mag_quick ( & pptr - > velocity ) ;
if ( speed > robptr - > max_speed [ Difficulty_level ] ) {
pptr - > velocity . x = ( pptr - > velocity . x * 3 ) / 4 ;
pptr - > velocity . y = ( pptr - > velocity . y * 3 ) / 4 ;
pptr - > velocity . z = ( pptr - > velocity . z * 3 ) / 4 ;
}
}
// --------------------------------------------------------------------------------------------------------------------
void move_away_from_player ( object * objp , vms_vector * vec_to_player , int attack_type )
{
fix speed ;
physics_info * pptr = & objp - > mtype . phys_info ;
robot_info * robptr = & Robot_info [ objp - > id ] ;
int objref ;
pptr - > velocity . x - = fixmul ( vec_to_player - > x , FrameTime * 16 ) ;
pptr - > velocity . y - = fixmul ( vec_to_player - > y , FrameTime * 16 ) ;
pptr - > velocity . z - = fixmul ( vec_to_player - > z , FrameTime * 16 ) ;
if ( attack_type ) {
// Get value in 0..3 to choose evasion direction.
objref = ( ( objp - Objects ) ^ ( ( FrameCount + 3 * ( objp - Objects ) ) > > 5 ) ) & 3 ;
switch ( objref ) {
case 0 : vm_vec_scale_add2 ( & pptr - > velocity , & objp - > orient . uvec , FrameTime < < 5 ) ; break ;
case 1 : vm_vec_scale_add2 ( & pptr - > velocity , & objp - > orient . uvec , - FrameTime < < 5 ) ; break ;
case 2 : vm_vec_scale_add2 ( & pptr - > velocity , & objp - > orient . rvec , FrameTime < < 5 ) ; break ;
case 3 : vm_vec_scale_add2 ( & pptr - > velocity , & objp - > orient . rvec , - FrameTime < < 5 ) ; break ;
default : Int3 ( ) ; // Impossible, bogus value on objref, must be in 0..3
}
}
speed = vm_vec_mag_quick ( & pptr - > velocity ) ;
if ( speed > robptr - > max_speed [ Difficulty_level ] ) {
pptr - > velocity . x = ( pptr - > velocity . x * 3 ) / 4 ;
pptr - > velocity . y = ( pptr - > velocity . y * 3 ) / 4 ;
pptr - > velocity . z = ( pptr - > velocity . z * 3 ) / 4 ;
}
//--old-- fix speed, dot;
//--old-- physics_info *pptr = &objp->mtype.phys_info;
//--old-- robot_info *robptr = &Robot_info[objp->id];
//--old--
//--old-- // Trying to move away from player. If forward vector much different than velocity vector,
//--old-- // bash velocity vector twice as much away from player as usual.
//--old-- dot = vm_vec_dot(&pptr->velocity, &objp->orient.fvec);
//--old-- if (dot > -3*F1_0/4) {
//--old-- // This funny code is supposed to slow down the robot and move his velocity towards his direction
//--old-- // more quickly than the general code
//--old-- pptr->velocity.x = pptr->velocity.x/2 - fixmul(vec_to_player->x, FrameTime*16);
//--old-- pptr->velocity.y = pptr->velocity.y/2 - fixmul(vec_to_player->y, FrameTime*16);
//--old-- pptr->velocity.z = pptr->velocity.z/2 - fixmul(vec_to_player->z, FrameTime*16);
//--old-- } else {
//--old-- pptr->velocity.x -= fixmul(vec_to_player->x, FrameTime*16);
//--old-- pptr->velocity.y -= fixmul(vec_to_player->y, FrameTime*16);
//--old-- pptr->velocity.z -= fixmul(vec_to_player->z, FrameTime*16);
//--old-- }
//--old--
//--old-- speed = vm_vec_mag_quick(&pptr->velocity);
//--old--
//--old-- if (speed > robptr->max_speed[Difficulty_level]) {
//--old-- pptr->velocity.x = (pptr->velocity.x*3)/4;
//--old-- pptr->velocity.y = (pptr->velocity.y*3)/4;
//--old-- pptr->velocity.z = (pptr->velocity.z*3)/4;
//--old-- }
}
// --------------------------------------------------------------------------------------------------------------------
// Move towards, away_from or around player.
// Also deals with evasion.
// If the flag evade_only is set, then only allowed to evade, not allowed to move otherwise (must have mode == AIM_STILL).
void ai_move_relative_to_player ( object * objp , ai_local * ailp , fix dist_to_player , vms_vector * vec_to_player , fix circle_distance , int evade_only )
{
object * dobjp ;
robot_info * robptr = & Robot_info [ objp - > id ] ;
// See if should take avoidance.
// New way, green guys don't evade: if ((robptr->attack_type == 0) && (objp->ctype.ai_info.danger_laser_num != -1)) {
if ( objp - > ctype . ai_info . danger_laser_num ! = - 1 ) {
dobjp = & Objects [ objp - > ctype . ai_info . danger_laser_num ] ;
if ( ( dobjp - > type = = OBJ_WEAPON ) & & ( dobjp - > signature = = objp - > ctype . ai_info . danger_laser_signature ) ) {
fix dot , dist_to_laser , field_of_view ;
vms_vector vec_to_laser , laser_fvec ;
field_of_view = Robot_info [ objp - > id ] . field_of_view [ Difficulty_level ] ;
vm_vec_sub ( & vec_to_laser , & dobjp - > pos , & objp - > pos ) ;
dist_to_laser = vm_vec_normalize_quick ( & vec_to_laser ) ;
dot = vm_vec_dot ( & vec_to_laser , & objp - > orient . fvec ) ;
if ( dot > field_of_view ) {
fix laser_robot_dot ;
vms_vector laser_vec_to_robot ;
// The laser is seen by the robot, see if it might hit the robot.
// Get the laser's direction. If it's a polyobj, it can be gotten cheaply from the orientation matrix.
if ( dobjp - > render_type = = RT_POLYOBJ )
laser_fvec = dobjp - > orient . fvec ;
else { // Not a polyobj, get velocity and normalize.
laser_fvec = dobjp - > mtype . phys_info . velocity ; //dobjp->orient.fvec;
vm_vec_normalize_quick ( & laser_fvec ) ;
}
vm_vec_sub ( & laser_vec_to_robot , & objp - > pos , & dobjp - > pos ) ;
vm_vec_normalize_quick ( & laser_vec_to_robot ) ;
laser_robot_dot = vm_vec_dot ( & laser_fvec , & laser_vec_to_robot ) ;
if ( ( laser_robot_dot > F1_0 * 7 / 8 ) & & ( dist_to_laser < F1_0 * 80 ) ) {
int evade_speed ;
ai_evaded = 1 ;
evade_speed = Robot_info [ objp - > id ] . evade_speed [ Difficulty_level ] ;
move_around_player ( objp , vec_to_player , evade_speed ) ;
}
}
return ;
}
}
// If only allowed to do evade code, then done.
// Hmm, perhaps brilliant insight. If want claw-type guys to keep coming, don't return here after evasion.
if ( ( ! robptr - > attack_type ) & & evade_only )
return ;
// If we fall out of above, then no object to be avoided.
objp - > ctype . ai_info . danger_laser_num = - 1 ;
// Green guy selects move around/towards/away based on firing time, not distance.
if ( robptr - > attack_type = = 1 ) {
if ( ( ( ailp - > next_fire > robptr - > firing_wait [ Difficulty_level ] / 4 ) & & ( dist_to_player < F1_0 * 30 ) ) | | Player_is_dead ) {
// 1/4 of time, move around player, 3/4 of time, move away from player
if ( d_rand ( ) < 8192 ) {
move_around_player ( objp , vec_to_player , - 1 ) ;
} else {
move_away_from_player ( objp , vec_to_player , 1 ) ;
}
} else {
move_towards_player ( objp , vec_to_player ) ;
}
} else {
if ( dist_to_player < circle_distance )
move_away_from_player ( objp , vec_to_player , 0 ) ;
else if ( dist_to_player < circle_distance * 2 )
move_around_player ( objp , vec_to_player , - 1 ) ;
else
move_towards_player ( objp , vec_to_player ) ;
}
}
// --------------------------------------------------------------------------------------------------------------------
// Compute a somewhat random, normalized vector.
void make_random_vector ( vms_vector * vec )
{
vec - > x = ( d_rand ( ) - 16384 ) | 1 ; // make sure we don't create null vector
vec - > y = d_rand ( ) - 16384 ;
vec - > z = d_rand ( ) - 16384 ;
vm_vec_normalize_quick ( vec ) ;
}
# ifndef NDEBUG
void mprintf_animation_info ( object * objp )
{
ai_static * aip = & objp - > ctype . ai_info ;
ai_local * ailp = & Ai_local_info [ objp - Objects ] ;
if ( ! Ai_info_enabled )
return ;
mprintf ( ( 0 , " Goal = " ) ) ;
switch ( aip - > GOAL_STATE ) {
case AIS_NONE : mprintf ( ( 0 , " NONE " ) ) ; break ;
case AIS_REST : mprintf ( ( 0 , " REST " ) ) ; break ;
case AIS_SRCH : mprintf ( ( 0 , " SRCH " ) ) ; break ;
case AIS_LOCK : mprintf ( ( 0 , " LOCK " ) ) ; break ;
case AIS_FLIN : mprintf ( ( 0 , " FLIN " ) ) ; break ;
case AIS_FIRE : mprintf ( ( 0 , " FIRE " ) ) ; break ;
case AIS_RECO : mprintf ( ( 0 , " RECO " ) ) ; break ;
case AIS_ERR_ : mprintf ( ( 0 , " ERR_ " ) ) ; break ;
}
mprintf ( ( 0 , " Cur = " ) ) ;
switch ( aip - > CURRENT_STATE ) {
case AIS_NONE : mprintf ( ( 0 , " NONE " ) ) ; break ;
case AIS_REST : mprintf ( ( 0 , " REST " ) ) ; break ;
case AIS_SRCH : mprintf ( ( 0 , " SRCH " ) ) ; break ;
case AIS_LOCK : mprintf ( ( 0 , " LOCK " ) ) ; break ;
case AIS_FLIN : mprintf ( ( 0 , " FLIN " ) ) ; break ;
case AIS_FIRE : mprintf ( ( 0 , " FIRE " ) ) ; break ;
case AIS_RECO : mprintf ( ( 0 , " RECO " ) ) ; break ;
case AIS_ERR_ : mprintf ( ( 0 , " ERR_ " ) ) ; break ;
}
mprintf ( ( 0 , " Aware = " ) ) ;
switch ( ailp - > player_awareness_type ) {
case AIE_FIRE : mprintf ( ( 0 , " FIRE " ) ) ; break ;
case AIE_HITT : mprintf ( ( 0 , " HITT " ) ) ; break ;
case AIE_COLL : mprintf ( ( 0 , " COLL " ) ) ; break ;
case AIE_HURT : mprintf ( ( 0 , " HURT " ) ) ; break ;
}
mprintf ( ( 0 , " Next fire = %6.3f, Time = %6.3f \n " , f2fl ( ailp - > next_fire ) , f2fl ( ailp - > player_awareness_time ) ) ) ;
}
# endif
// -------------------------------------------------------------------------------------------------------------------
int Break_on_object = - 1 ;
void do_firing_stuff ( object * obj , int player_visibility , vms_vector * vec_to_player )
{
//mprintf((0, "!"));
if ( player_visibility > = 1 ) {
// Now, if in robot's field of view, lock onto player
fix dot = vm_vec_dot ( & obj - > orient . fvec , vec_to_player ) ;
//mprintf((0, "dot = %8x ", dot));
if ( ( dot > = 7 * F1_0 / 8 ) | | ( Players [ Player_num ] . flags & PLAYER_FLAGS_CLOAKED ) ) {
ai_static * aip = & obj - > ctype . ai_info ;
ai_local * ailp = & Ai_local_info [ obj - Objects ] ;
switch ( aip - > GOAL_STATE ) {
case AIS_NONE :
case AIS_REST :
case AIS_SRCH :
case AIS_LOCK :
aip - > GOAL_STATE = AIS_FIRE ;
if ( ailp - > player_awareness_type < = PA_NEARBY_ROBOT_FIRED ) {
ailp - > player_awareness_type = PA_NEARBY_ROBOT_FIRED ;
ailp - > player_awareness_time = PLAYER_AWARENESS_INITIAL_TIME ;
}
break ;
}
} else if ( dot > = F1_0 / 2 ) {
ai_static * aip = & obj - > ctype . ai_info ;
switch ( aip - > GOAL_STATE ) {
case AIS_NONE :
case AIS_REST :
case AIS_SRCH :
aip - > GOAL_STATE = AIS_LOCK ;
break ;
}
}
}
}
// --------------------------------------------------------------------------------------------------------------------
// If a hiding robot gets bumped or hit, he decides to find another hiding place.
void do_ai_robot_hit ( object * objp , int type )
{
if ( objp - > control_type = = CT_AI ) {
if ( ( type = = PA_WEAPON_ROBOT_COLLISION ) | | ( type = = PA_PLAYER_COLLISION ) )
switch ( objp - > ctype . ai_info . behavior ) {
case AIM_HIDE :
objp - > ctype . ai_info . SUBMODE = AISM_GOHIDE ;
break ;
case AIM_STILL :
Ai_local_info [ objp - Objects ] . mode = AIM_CHASE_OBJECT ;
break ;
}
}
}
# ifndef NDEBUG
int Do_ai_flag = 1 ;
int Cvv_test = 0 ;
int Cvv_last_time [ MAX_OBJECTS ] ;
int Gun_point_hack = 0 ;
# endif
# define CHASE_TIME_LENGTH (F1_0*8)
# define DEFAULT_ROBOT_SOUND_VOLUME F1_0
int Robot_sound_volume = DEFAULT_ROBOT_SOUND_VOLUME ;
// --------------------------------------------------------------------------------------------------------------------
// Note: This function could be optimized. Surely player_is_visible_from_object would benefit from the
// information of a normalized vec_to_player.
// Return player visibility:
// 0 not visible
// 1 visible, but robot not looking at player (ie, on an unobstructed vector)
// 2 visible and in robot's field of view
// -1 player is cloaked
// If the player is cloaked, set vec_to_player based on time player cloaked and last uncloaked position.
// Updates ailp->previous_visibility if player is not cloaked, in which case the previous visibility is left unchanged
// and is copied to player_visibility
void compute_vis_and_vec ( object * objp , vms_vector * pos , ai_local * ailp , vms_vector * vec_to_player , int * player_visibility , robot_info * robptr , int * flag )
{
if ( ! * flag ) {
if ( Players [ Player_num ] . flags & PLAYER_FLAGS_CLOAKED ) {
fix delta_time , dist ;
int cloak_index = ( objp - Objects ) % MAX_AI_CLOAK_INFO ;
delta_time = GameTime - Ai_cloak_info [ cloak_index ] . last_time ;
if ( delta_time > F1_0 * 2 ) {
vms_vector randvec ;
Ai_cloak_info [ cloak_index ] . last_time = GameTime ;
make_random_vector ( & randvec ) ;
vm_vec_scale_add2 ( & Ai_cloak_info [ cloak_index ] . last_position , & randvec , 8 * delta_time ) ;
}
dist = vm_vec_normalized_dir_quick ( vec_to_player , & Ai_cloak_info [ cloak_index ] . last_position , pos ) ;
* player_visibility = player_is_visible_from_object ( objp , pos , robptr - > field_of_view [ Difficulty_level ] , vec_to_player ) ;
// *player_visibility = 2;
if ( ( ailp - > next_misc_sound_time < GameTime ) & & ( ailp - > next_fire < F1_0 ) & & ( dist < F1_0 * 20 ) ) {
mprintf ( ( 0 , " ANGRY! " ) ) ;
ailp - > next_misc_sound_time = GameTime + ( d_rand ( ) + F1_0 ) * ( 7 - Difficulty_level ) / 1 ;
digi_link_sound_to_pos ( robptr - > see_sound , objp - > segnum , 0 , pos , 0 , Robot_sound_volume ) ;
}
} else {
// Compute expensive stuff -- vec_to_player and player_visibility
vm_vec_normalized_dir_quick ( vec_to_player , & Believed_player_pos , pos ) ;
if ( ( vec_to_player - > x = = 0 ) & & ( vec_to_player - > y = = 0 ) & & ( vec_to_player - > z = = 0 ) ) {
mprintf ( ( 0 , " Warning: Player and robot at exactly the same location. \n " ) ) ;
vec_to_player - > x = F1_0 ;
}
* player_visibility = player_is_visible_from_object ( objp , pos , robptr - > field_of_view [ Difficulty_level ] , vec_to_player ) ;
// This horrible code added by MK in desperation on 12/13/94 to make robots wake up as soon as they
// see you without killing frame rate.
{
ai_static * aip = & objp - > ctype . ai_info ;
if ( ( * player_visibility = = 2 ) & & ( ailp - > previous_visibility ! = 2 ) )
if ( ( aip - > GOAL_STATE = = AIS_REST ) | | ( aip - > CURRENT_STATE = = AIS_REST ) ) {
aip - > GOAL_STATE = AIS_FIRE ;
aip - > CURRENT_STATE = AIS_FIRE ;
}
}
if ( ! Player_exploded & & ( ailp - > previous_visibility ! = * player_visibility ) & & ( * player_visibility = = 2 ) ) {
if ( ailp - > previous_visibility = = 0 ) {
if ( ailp - > time_player_seen + F1_0 / 2 < GameTime ) {
// mprintf((0, "SEE! "));
digi_link_sound_to_pos ( robptr - > see_sound , objp - > segnum , 0 , pos , 0 , Robot_sound_volume ) ;
ailp - > time_player_sound_attacked = GameTime ;
ailp - > next_misc_sound_time = GameTime + F1_0 + d_rand ( ) * 4 ;
}
} else if ( ailp - > time_player_sound_attacked + F1_0 / 4 < GameTime ) {
// mprintf((0, "ANGRY! "));
digi_link_sound_to_pos ( robptr - > attack_sound , objp - > segnum , 0 , pos , 0 , Robot_sound_volume ) ;
ailp - > time_player_sound_attacked = GameTime ;
}
}
if ( ( * player_visibility = = 2 ) & & ( ailp - > next_misc_sound_time < GameTime ) ) {
// mprintf((0, "ATTACK! "));
ailp - > next_misc_sound_time = GameTime + ( d_rand ( ) + F1_0 ) * ( 7 - Difficulty_level ) / 2 ;
digi_link_sound_to_pos ( robptr - > attack_sound , objp - > segnum , 0 , pos , 0 , Robot_sound_volume ) ;
}
ailp - > previous_visibility = * player_visibility ;
}
* flag = 1 ;
if ( * player_visibility ) {
ailp - > time_player_seen = GameTime ;
}
}
}
// --------------------------------------------------------------------------------------------------------------------
// Move the object objp to a spot in which it doesn't intersect a wall.
// It might mean moving it outside its current segment.
void move_object_to_legal_spot ( object * objp )
{
vms_vector original_pos = objp - > pos ;
int i ;
segment * segp = & Segments [ objp - > segnum ] ;
for ( i = 0 ; i < MAX_SIDES_PER_SEGMENT ; i + + ) {
if ( WALL_IS_DOORWAY ( segp , i ) & WID_FLY_FLAG ) {
vms_vector segment_center , goal_dir ;
fix dist_to_center ;
compute_segment_center ( & segment_center , & Segments [ segp - > children [ i ] ] ) ;
vm_vec_sub ( & goal_dir , & segment_center , & objp - > pos ) ;
dist_to_center = vm_vec_normalize_quick ( & goal_dir ) ;
vm_vec_scale ( & goal_dir , objp - > size ) ;
vm_vec_add2 ( & objp - > pos , & goal_dir ) ;
if ( ! object_intersects_wall ( objp ) ) {
int new_segnum = find_point_seg ( & objp - > pos , objp - > segnum ) ;
if ( new_segnum ! = - 1 ) {
obj_relink ( objp - Objects , new_segnum ) ;
return ;
}
} else
objp - > pos = original_pos ;
}
}
// Int3(); // Darn you John, you done it again! (But contact Mike)
mprintf ( ( 0 , " Note: Killing robot #%i because he's badly stuck outside the mine. \n " , objp - Objects ) ) ;
apply_damage_to_robot ( objp , objp - > shields * 2 , objp - Objects ) ;
}
// --------------------------------------------------------------------------------------------------------------------
// Move object one object radii from current position towards segment center.
// If segment center is nearer than 2 radii, move it to center.
void move_towards_segment_center ( object * objp )
{
int segnum = objp - > segnum ;
fix dist_to_center ;
vms_vector segment_center , goal_dir ;
compute_segment_center ( & segment_center , & Segments [ segnum ] ) ;
vm_vec_sub ( & goal_dir , & segment_center , & objp - > pos ) ;
dist_to_center = vm_vec_normalize_quick ( & goal_dir ) ;
if ( dist_to_center < objp - > size ) {
// Center is nearer than the distance we want to move, so move to center.
objp - > pos = segment_center ;
mprintf ( ( 0 , " Object #%i moved to center of segment #%i (%7.3f %7.3f %7.3f) \n " , objp - Objects , objp - > segnum , f2fl ( objp - > pos . x ) , f2fl ( objp - > pos . y ) , f2fl ( objp - > pos . z ) ) ) ;
if ( object_intersects_wall ( objp ) ) {
mprintf ( ( 0 , " Object #%i still illegal, trying trickier move. \n " ) ) ;
move_object_to_legal_spot ( objp ) ;
}
} else {
int new_segnum ;
// Move one radii towards center.
vm_vec_scale ( & goal_dir , objp - > size ) ;
vm_vec_add2 ( & objp - > pos , & goal_dir ) ;
new_segnum = find_point_seg ( & objp - > pos , objp - > segnum ) ;
if ( new_segnum = = - 1 ) {
objp - > pos = segment_center ;
move_object_to_legal_spot ( objp ) ;
}
mprintf ( ( 0 , " Obj %i moved twrds seg %i (%6.2f %6.2f %6.2f), dists: [%6.2f %6.2f] \n " , objp - Objects , objp - > segnum , f2fl ( objp - > pos . x ) , f2fl ( objp - > pos . y ) , f2fl ( objp - > pos . z ) , f2fl ( vm_vec_dist_quick ( & objp - > pos , & segment_center ) ) , f2fl ( vm_vec_dist_quick ( & objp - > pos , & segment_center ) ) ) ) ;
}
}
// -----------------------------------------------------------------------------------------------------------
// Return true if door can be flown through by a suitable type robot.
// Only brains and avoid robots can open doors.
int ai_door_is_openable ( object * objp , segment * segp , int sidenum )
{
int wall_num ;
// The mighty console object can open all doors (for purposes of determining paths).
if ( objp = = ConsoleObject ) {
int wall_num = segp - > sides [ sidenum ] . wall_num ;
if ( Walls [ wall_num ] . type = = WALL_DOOR )
return 1 ;
}
if ( ( objp - > id = = ROBOT_BRAIN ) | | ( objp - > ctype . ai_info . behavior = = AIB_RUN_FROM ) ) {
wall_num = segp - > sides [ sidenum ] . wall_num ;
if ( wall_num ! = - 1 )
if ( ( Walls [ wall_num ] . type = = WALL_DOOR ) & & ( Walls [ wall_num ] . keys = = KEY_NONE ) & & ! ( Walls [ wall_num ] . flags & WALL_DOOR_LOCKED ) )
return 1 ;
}
return 0 ;
}
//--// -----------------------------------------------------------------------------------------------------------
//--// Return true if object *objp is allowed to open door at wall_num
//--int door_openable_by_robot(object *objp, int wall_num)
//--{
//-- if (objp->id == ROBOT_BRAIN)
//-- if (Walls[wall_num].keys == KEY_NONE)
//-- return 1;
//--
//-- return 0;
//--}
// -----------------------------------------------------------------------------------------------------------
// Return side of openable door in segment, if any. If none, return -1.
int openable_doors_in_segment ( object * objp )
{
int i ;
int segnum = objp - > segnum ;
for ( i = 0 ; i < MAX_SIDES_PER_SEGMENT ; i + + ) {
if ( Segments [ segnum ] . sides [ i ] . wall_num ! = - 1 ) {
int wall_num = Segments [ segnum ] . sides [ i ] . wall_num ;
if ( ( Walls [ wall_num ] . type = = WALL_DOOR ) & & ( Walls [ wall_num ] . keys = = KEY_NONE ) & & ( Walls [ wall_num ] . state = = WALL_DOOR_CLOSED ) & & ! ( Walls [ wall_num ] . flags & WALL_DOOR_LOCKED ) )
return i ;
}
}
return - 1 ;
}
//--unused-- // -----------------------------------------------------------------------------------------------------------
//--unused-- // For all doors this guy can open in his segment, open them.
//--unused-- void ai_open_doors_in_segment(object *objp)
//--unused-- {
//--unused-- int i;
//--unused-- int segnum = objp->segnum;
//--unused--
//--unused-- for (i=0; i<MAX_SIDES_PER_SEGMENT; i++)
//--unused-- if (Segments[segnum].sides[i].wall_num != -1) {
//--unused-- int wall_num = Segments[segnum].sides[i].wall_num;
//--unused-- if ((Walls[wall_num].type == WALL_DOOR) && (Walls[wall_num].keys == KEY_NONE) && (Walls[wall_num].state == WALL_DOOR_CLOSED))
//--unused-- if (door_openable_by_robot(objp, wall_num)) {
//--unused-- mprintf((0, "Trying to open door at segment %i, side %i\n", segnum, i));
//--unused-- wall_open_door(&Segments[segnum], i);
//--unused-- }
//--unused-- }
//--unused-- }
// --------------------------------------------------------------------------------------------------------------------
// Return true if a special object (player or control center) is in this segment.
int special_object_in_seg ( int segnum )
{
int objnum ;
objnum = Segments [ segnum ] . objects ;
while ( objnum ! = - 1 ) {
if ( ( Objects [ objnum ] . type = = OBJ_PLAYER ) | | ( Objects [ objnum ] . type = = OBJ_CNTRLCEN ) ) {
mprintf ( ( 0 , " Special object of type %i in segment %i \n " , Objects [ objnum ] . type , segnum ) ) ;
return 1 ;
} else
objnum = Objects [ objnum ] . next ;
}
return 0 ;
}
// --------------------------------------------------------------------------------------------------------------------
// Randomly select a segment attached to *segp, reachable by flying.
int get_random_child ( int segnum )
{
int sidenum ;
segment * segp = & Segments [ segnum ] ;
sidenum = ( d_rand ( ) * 6 ) > > 15 ;
while ( ! ( WALL_IS_DOORWAY ( segp , sidenum ) & WID_FLY_FLAG ) )
sidenum = ( d_rand ( ) * 6 ) > > 15 ;
segnum = segp - > children [ sidenum ] ;
return segnum ;
}
// --------------------------------------------------------------------------------------------------------------------
// Return true if placing an object of size size at pos *pos intersects a (player or robot or control center) in segment *segp.
int check_object_object_intersection ( vms_vector * pos , fix size , segment * segp )
{
int curobjnum ;
// If this would intersect with another object (only check those in this segment), then try to move.
curobjnum = segp - > objects ;
while ( curobjnum ! = - 1 ) {
object * curobjp = & Objects [ curobjnum ] ;
if ( ( curobjp - > type = = OBJ_PLAYER ) | | ( curobjp - > type = = OBJ_ROBOT ) | | ( curobjp - > type = = OBJ_CNTRLCEN ) ) {
if ( vm_vec_dist_quick ( pos , & curobjp - > pos ) < size + curobjp - > size )
return 1 ;
}
curobjnum = curobjp - > next ;
}
return 0 ;
}
# ifndef SHAREWARE
// --------------------------------------------------------------------------------------------------------------------
// Return true if object created, else return false.
int create_gated_robot ( int segnum , int object_id )
{
int objnum ;
object * objp ;
segment * segp = & Segments [ segnum ] ;
vms_vector object_pos ;
robot_info * robptr = & Robot_info [ object_id ] ;
int i , count = 0 ;
fix objsize = Polygon_models [ robptr - > model_num ] . rad ;
int default_behavior ;
for ( i = 0 ; i < = Highest_object_index ; i + + )
if ( Objects [ i ] . type = = OBJ_ROBOT )
if ( Objects [ i ] . matcen_creator = = BOSS_GATE_MATCEN_NUM )
count + + ;
if ( count > 2 * Difficulty_level + 3 ) {
// mprintf((0, "Cannot gate in a robot until you kill one.\n"));
Last_gate_time = GameTime - 3 * Gate_interval / 4 ;
return 0 ;
}
compute_segment_center ( & object_pos , segp ) ;
pick_random_point_in_seg ( & object_pos , segp - Segments ) ;
// See if legal to place object here. If not, move about in segment and try again.
if ( check_object_object_intersection ( & object_pos , objsize , segp ) ) {
// mprintf((0, "Can't get in because object collides with something.\n"));
Last_gate_time = GameTime - 3 * Gate_interval / 4 ;
return 0 ;
}
objnum = obj_create ( OBJ_ROBOT , object_id , segnum , & object_pos , & vmd_identity_matrix , objsize , CT_AI , MT_PHYSICS , RT_POLYOBJ ) ;
if ( objnum < 0 ) {
// mprintf((1, "Can't get object to gate in robot. Not gating in.\n"));
Last_gate_time = GameTime - 3 * Gate_interval / 4 ;
return 0 ;
}
mprintf ( ( 0 , " Gating in object %i in segment %i \n " , objnum , segp - Segments ) ) ;
# ifdef NETWORK
Net_create_objnums [ 0 ] = objnum ; // A convenient global to get objnum back to caller for multiplayer
# endif
objp = & Objects [ objnum ] ;
//Set polygon-object-specific data
objp - > rtype . pobj_info . model_num = robptr - > model_num ;
objp - > rtype . pobj_info . subobj_flags = 0 ;
//set Physics info
objp - > mtype . phys_info . mass = robptr - > mass ;
objp - > mtype . phys_info . drag = robptr - > drag ;
objp - > mtype . phys_info . flags | = ( PF_LEVELLING ) ;
objp - > shields = robptr - > strength ;
objp - > matcen_creator = BOSS_GATE_MATCEN_NUM ; // flag this robot as having been created by the boss.
default_behavior = AIB_NORMAL ;
if ( object_id = = 10 ) // This is a toaster guy!
default_behavior = AIB_RUN_FROM ;
init_ai_object ( objp - Objects , default_behavior , - 1 ) ; // Note, -1 = segment this robot goes to to hide, should probably be something useful
object_create_explosion ( segnum , & object_pos , i2f ( 10 ) , VCLIP_MORPHING_ROBOT ) ;
digi_link_sound_to_pos ( Vclip [ VCLIP_MORPHING_ROBOT ] . sound_num , segnum , 0 , & object_pos , 0 , F1_0 ) ;
morph_start ( objp ) ;
Last_gate_time = GameTime ;
Players [ Player_num ] . num_robots_level + + ;
Players [ Player_num ] . num_robots_total + + ;
return 1 ;
}
// --------------------------------------------------------------------------------------------------------------------
// Make object objp gate in a robot.
// The process of him bringing in a robot takes one second.
// Then a robot appears somewhere near the player.
// Return true if robot successfully created, else return false
int gate_in_robot ( int type , int segnum )
{
if ( segnum < 0 )
segnum = Boss_gate_segs [ ( d_rand ( ) * Num_boss_gate_segs ) > > 15 ] ;
Assert ( ( segnum > = 0 ) & & ( segnum < = Highest_segment_index ) ) ;
return create_gated_robot ( segnum , type ) ;
}
# endif
//// --------------------------------------------------------------------------------------------------------------------
//// Return true if some distance function of vertices implies segment is too small for object.
//int segment_too_small_for_object(object *objp, segment *segp)
//{
// int i;
// fix threshold_distance;
//
// threshold_distance = objp->size*2;
//
// for (i=1; i<MAX_VERTICES_PER_SEGMENT; i++)
// if (vm_vec_dist_quick(&Vertices[segp->verts[i]], &Vertices[segp->verts[i-1]]) < threshold_distance) {
//#ifndef NDEBUG
// fix dist = vm_vec_dist_quick(&Vertices[segp->verts[i]], &Vertices[segp->verts[i-1]]);
//#endif
// mprintf((0, "Seg %i too small for obj %i (sz=%7.3f), verts %i, %i only %7.3f apart\n", segp-Segments, objp-Objects, f2fl(objp->size), segp->verts[i], segp->verts[i-1], f2fl(dist)));
// return 1;
// }
//
// return 0;
//}
//--unused-- int Shown_all_segments=0;
// --------------------------------------------------------------------------------------------------------------------
int boss_fits_in_seg ( object * boss_objp , int segnum )
{
vms_vector segcenter ;
int boss_objnum = boss_objp - Objects ;
int posnum ;
compute_segment_center ( & segcenter , & Segments [ segnum ] ) ;
for ( posnum = 0 ; posnum < 9 ; posnum + + ) {
if ( posnum > 0 ) {
vms_vector vertex_pos ;
Assert ( ( posnum - 1 > = 0 ) & & ( posnum - 1 < 8 ) ) ;
vertex_pos = Vertices [ Segments [ segnum ] . verts [ posnum - 1 ] ] ;
vm_vec_avg ( & boss_objp - > pos , & vertex_pos , & segcenter ) ;
} else
boss_objp - > pos = segcenter ;
obj_relink ( boss_objnum , segnum ) ;
if ( ! object_intersects_wall ( boss_objp ) )
return 1 ;
}
return 0 ;
}
# define QUEUE_SIZE 256
// --------------------------------------------------------------------------------------------------------------------
// Create list of segments boss is allowed to teleport to at segptr.
// Set *num_segs.
// Boss is allowed to teleport to segments he fits in (calls object_intersects_wall) and
// he can reach from his initial position (calls find_connected_distance).
// If size_check is set, then only add segment if boss can fit in it, else any segment is legal.
void init_boss_segments ( short segptr [ ] , int * num_segs , int size_check )
{
int boss_objnum = - 1 ;
int i ;
* num_segs = 0 ;
# ifdef EDITOR
N_selected_segs = 0 ;
# endif
// See if there is a boss. If not, quick out.
for ( i = 0 ; i < = Highest_object_index ; i + + )
if ( ( Objects [ i ] . type = = OBJ_ROBOT ) & & ( Robot_info [ Objects [ i ] . id ] . boss_flag ) ) {
Assert ( boss_objnum = = - 1 ) ; // There are two bosses in this mine! i and boss_objnum!
boss_objnum = i ;
}
if ( boss_objnum ! = - 1 ) {
int original_boss_seg ;
vms_vector original_boss_pos ;
object * boss_objp = & Objects [ boss_objnum ] ;
int head , tail ;
int seg_queue [ QUEUE_SIZE ] ;
//ALREADY IN RENDER.H byte visited[MAX_SEGMENTS];
fix boss_size_save ;
boss_size_save = boss_objp - > size ;
boss_objp - > size = fixmul ( ( F1_0 / 4 ) * 3 , boss_objp - > size ) ;
original_boss_seg = boss_objp - > segnum ;
original_boss_pos = boss_objp - > pos ;
head = 0 ;
tail = 0 ;
seg_queue [ head + + ] = original_boss_seg ;
segptr [ ( * num_segs ) + + ] = original_boss_seg ;
# ifdef EDITOR
Selected_segs [ N_selected_segs + + ] = original_boss_seg ;
# endif
for ( i = 0 ; i < = Highest_segment_index ; i + + )
visited [ i ] = 0 ;
while ( tail ! = head ) {
int sidenum ;
segment * segp = & Segments [ seg_queue [ tail + + ] ] ;
tail & = QUEUE_SIZE - 1 ;
for ( sidenum = 0 ; sidenum < MAX_SIDES_PER_SEGMENT ; sidenum + + ) {
if ( WALL_IS_DOORWAY ( segp , sidenum ) & WID_FLY_FLAG ) {
if ( visited [ segp - > children [ sidenum ] ] = = 0 ) {
seg_queue [ head + + ] = segp - > children [ sidenum ] ;
visited [ segp - > children [ sidenum ] ] = 1 ;
head & = QUEUE_SIZE - 1 ;
if ( head > tail ) {
if ( head = = tail + QUEUE_SIZE - 1 )
Int3 ( ) ; // queue overflow. Make it bigger!
} else
if ( head + QUEUE_SIZE = = tail + QUEUE_SIZE - 1 )
Int3 ( ) ; // queue overflow. Make it bigger!
if ( ( ! size_check ) | | boss_fits_in_seg ( boss_objp , segp - > children [ sidenum ] ) ) {
segptr [ ( * num_segs ) + + ] = segp - > children [ sidenum ] ;
# ifdef EDITOR
Selected_segs [ N_selected_segs + + ] = segp - > children [ sidenum ] ;
# endif
if ( * num_segs > = MAX_BOSS_TELEPORT_SEGS ) {
mprintf ( ( 1 , " Warning: Too many boss teleport segments. Found %i after searching %i/%i segments. \n " , MAX_BOSS_TELEPORT_SEGS , segp - > children [ sidenum ] , Highest_segment_index + 1 ) ) ;
tail = head ;
}
}
}
}
}
}
boss_objp - > size = boss_size_save ;
boss_objp - > pos = original_boss_pos ;
obj_relink ( boss_objnum , original_boss_seg ) ;
}
}
// --------------------------------------------------------------------------------------------------------------------
void teleport_boss ( object * objp )
{
int rand_segnum ;
vms_vector boss_dir ;
int rand_seg ;
Assert ( Num_boss_teleport_segs > 0 ) ;
// Pick a random segment from the list of boss-teleportable-to segments.
rand_seg = ( d_rand ( ) * Num_boss_teleport_segs ) > > 15 ;
rand_segnum = Boss_teleport_segs [ rand_seg ] ;
Assert ( ( rand_segnum > = 0 ) & & ( rand_segnum < = Highest_segment_index ) ) ;
# ifndef SHAREWARE
# ifdef NETWORK
if ( Game_mode & GM_MULTI )
multi_send_boss_actions ( objp - Objects , 1 , rand_seg , 0 ) ;
# endif
# endif
compute_segment_center ( & objp - > pos , & Segments [ rand_segnum ] ) ;
obj_relink ( objp - Objects , rand_segnum ) ;
Last_teleport_time = GameTime ;
// make boss point right at player
vm_vec_sub ( & boss_dir , & Objects [ Players [ Player_num ] . objnum ] . pos , & objp - > pos ) ;
vm_vector_2_matrix ( & objp - > orient , & boss_dir , NULL , NULL ) ;
digi_link_sound_to_pos ( Vclip [ VCLIP_MORPHING_ROBOT ] . sound_num , rand_segnum , 0 , & objp - > pos , 0 , F1_0 ) ;
digi_kill_sound_linked_to_object ( objp - Objects ) ;
digi_link_sound_to_object2 ( SOUND_BOSS_SHARE_SEE , objp - Objects , 1 , F1_0 , F1_0 * 512 ) ; // F1_0*512 means play twice as loud
# ifndef NDEBUG
mprintf ( ( 0 , " Boss teleported to segment %i \n " , rand_segnum ) ) ;
# endif
// After a teleport, boss can fire right away.
Ai_local_info [ objp - Objects ] . next_fire = 0 ;
}
// ----------------------------------------------------------------------
void start_boss_death_sequence ( object * objp )
{
if ( Robot_info [ objp - > id ] . boss_flag ) {
Boss_dying = 1 ;
Boss_dying_start_time = GameTime ;
}
}
// ----------------------------------------------------------------------
void do_boss_dying_frame ( object * objp )
{
fix boss_roll_val , temp ;
boss_roll_val = fixdiv ( GameTime - Boss_dying_start_time , BOSS_DEATH_DURATION ) ;
fix_sincos ( fixmul ( boss_roll_val , boss_roll_val ) , & temp , & objp - > mtype . phys_info . rotvel . x ) ;
fix_sincos ( boss_roll_val , & temp , & objp - > mtype . phys_info . rotvel . y ) ;
fix_sincos ( boss_roll_val - F1_0 / 8 , & temp , & objp - > mtype . phys_info . rotvel . z ) ;
objp - > mtype . phys_info . rotvel . x = ( GameTime - Boss_dying_start_time ) / 9 ;
objp - > mtype . phys_info . rotvel . y = ( GameTime - Boss_dying_start_time ) / 5 ;
objp - > mtype . phys_info . rotvel . z = ( GameTime - Boss_dying_start_time ) / 7 ;
if ( Boss_dying_start_time + BOSS_DEATH_DURATION - BOSS_DEATH_SOUND_DURATION < GameTime ) {
if ( ! Boss_dying_sound_playing ) {
mprintf ( ( 0 , " Starting boss death sound! \n " ) ) ;
Boss_dying_sound_playing = 1 ;
digi_link_sound_to_object2 ( SOUND_BOSS_SHARE_DIE , objp - Objects , 0 , F1_0 * 4 , F1_0 * 1024 ) ; // F1_0*512 means play twice as loud
} else if ( d_rand ( ) < FrameTime * 16 )
create_small_fireball_on_object ( objp , ( F1_0 + d_rand ( ) ) * 8 , 0 ) ;
} else if ( d_rand ( ) < FrameTime * 8 )
create_small_fireball_on_object ( objp , ( F1_0 / 2 + d_rand ( ) ) * 8 , 1 ) ;
if ( Boss_dying_start_time + BOSS_DEATH_DURATION < GameTime ) {
do_controlcen_destroyed_stuff ( NULL ) ;
explode_object ( objp , F1_0 / 4 ) ;
digi_link_sound_to_object2 ( SOUND_BADASS_EXPLOSION , objp - Objects , 0 , F2_0 , F1_0 * 512 ) ;
}
}
# ifndef SHAREWARE
# ifdef NETWORK
// --------------------------------------------------------------------------------------------------------------------
// Called for an AI object if it is fairly aware of the player.
// awareness_level is in 0..100. Larger numbers indicate greater awareness (eg, 99 if firing at player).
// In a given frame, might not get called for an object, or might be called more than once.
// The fact that this routine is not called for a given object does not mean that object is not interested in the player.
// Objects are moved by physics, so they can move even if not interested in a player. However, if their velocity or
// orientation is changing, this routine will be called.
// Return value:
// 0 this player IS NOT allowed to move this robot.
// 1 this player IS allowed to move this robot.
int ai_multiplayer_awareness ( object * objp , int awareness_level )
{
int rval = 1 ;
# ifndef SHAREWARE
if ( Game_mode & GM_MULTI ) {
if ( awareness_level = = 0 )
return 0 ;
rval = multi_can_move_robot ( objp - Objects , awareness_level ) ;
}
# endif
return rval ;
}
# else
# define ai_multiplayer_awareness(a, b) 1
# endif
# else
# define ai_multiplayer_awareness(a, b) 1
# endif
# ifndef NDEBUG
fix Prev_boss_shields = - 1 ;
# endif
// --------------------------------------------------------------------------------------------------------------------
// Do special stuff for a boss.
void do_boss_stuff ( object * objp )
{
// New code, fixes stupid bug which meant boss never gated in robots if > 32767 seconds played.
if ( Last_teleport_time > GameTime )
Last_teleport_time = GameTime ;
if ( Last_gate_time > GameTime )
Last_gate_time = GameTime ;
# ifndef NDEBUG
if ( objp - > shields ! = Prev_boss_shields ) {
mprintf ( ( 0 , " Boss shields = %7.3f, object %i \n " , f2fl ( objp - > shields ) , objp - Objects ) ) ;
Prev_boss_shields = objp - > shields ;
}
# endif
if ( ! Boss_dying ) {
if ( objp - > ctype . ai_info . CLOAKED = = 1 ) {
if ( ( GameTime - Boss_cloak_start_time > BOSS_CLOAK_DURATION / 3 ) & & ( Boss_cloak_end_time - GameTime > BOSS_CLOAK_DURATION / 3 ) & & ( GameTime - Last_teleport_time > Boss_teleport_interval ) ) {
if ( ai_multiplayer_awareness ( objp , 98 ) )
teleport_boss ( objp ) ;
} else if ( Boss_hit_this_frame ) {
Boss_hit_this_frame = 0 ;
Last_teleport_time - = Boss_teleport_interval / 4 ;
}
if ( GameTime > Boss_cloak_end_time )
objp - > ctype . ai_info . CLOAKED = 0 ;
} else {
if ( ( GameTime - Boss_cloak_end_time > Boss_cloak_interval ) | | Boss_hit_this_frame ) {
if ( ai_multiplayer_awareness ( objp , 95 ) )
{
Boss_hit_this_frame = 0 ;
Boss_cloak_start_time = GameTime ;
Boss_cloak_end_time = GameTime + Boss_cloak_duration ;
objp - > ctype . ai_info . CLOAKED = 1 ;
# ifndef SHAREWARE
# ifdef NETWORK
if ( Game_mode & GM_MULTI )
multi_send_boss_actions ( objp - Objects , 2 , 0 , 0 ) ;
# endif
# endif
}
}
}
} else
do_boss_dying_frame ( objp ) ;
}
# define BOSS_TO_PLAYER_GATE_DISTANCE (F1_0*150)
# ifndef SHAREWARE
// --------------------------------------------------------------------------------------------------------------------
// Do special stuff for a boss.
void do_super_boss_stuff ( object * objp , fix dist_to_player , int player_visibility )
{
# ifdef NETWORK
static int eclip_state = 0 ;
# endif
do_boss_stuff ( objp ) ;
// Only master player can cause gating to occur.
# ifdef NETWORK
if ( ( Game_mode & GM_MULTI ) & & ! network_i_am_master ( ) )
return ;
# endif
if ( ( dist_to_player < BOSS_TO_PLAYER_GATE_DISTANCE ) | | player_visibility | | ( Game_mode & GM_MULTI ) ) {
if ( GameTime - Last_gate_time > Gate_interval / 2 ) {
restart_effect ( BOSS_ECLIP_NUM ) ;
# ifndef SHAREWARE
# ifdef NETWORK
if ( eclip_state = = 0 ) {
multi_send_boss_actions ( objp - Objects , 4 , 0 , 0 ) ;
eclip_state = 1 ;
}
# endif
# endif
}
else {
stop_effect ( BOSS_ECLIP_NUM ) ;
# ifndef SHAREWARE
# ifdef NETWORK
if ( eclip_state = = 1 ) {
multi_send_boss_actions ( objp - Objects , 5 , 0 , 0 ) ;
eclip_state = 0 ;
}
# endif
# endif
}
if ( GameTime - Last_gate_time > Gate_interval )
if ( ai_multiplayer_awareness ( objp , 99 ) ) {
int rtval ;
int randtype = ( d_rand ( ) * MAX_GATE_INDEX ) > > 15 ;
Assert ( randtype < MAX_GATE_INDEX ) ;
randtype = Super_boss_gate_list [ randtype ] ;
Assert ( randtype < N_robot_types ) ;
rtval = gate_in_robot ( randtype , - 1 ) ;
# ifndef SHAREWARE
# ifdef NETWORK
if ( rtval & & ( Game_mode & GM_MULTI ) )
{
multi_send_boss_actions ( objp - Objects , 3 , randtype , Net_create_objnums [ 0 ] ) ;
map_objnum_local_to_local ( Net_create_objnums [ 0 ] ) ;
}
# endif
# endif
}
}
}
# endif
//int multi_can_move_robot(object *objp, int awareness_level)
//{
// return 0;
//}
# ifndef SHAREWARE
void ai_multi_send_robot_position ( int objnum , int force )
{
# ifndef SHAREWARE
# ifdef NETWORK
if ( Game_mode & GM_MULTI )
{
if ( force ! = - 1 )
multi_send_robot_position ( objnum , 1 ) ;
else
multi_send_robot_position ( objnum , 0 ) ;
}
# endif
# endif
return ;
}
# else
# define ai_multi_send_robot_position(a, b)
# endif
// --------------------------------------------------------------------------------------------------------------------
// Returns true if this object should be allowed to fire at the player.
int maybe_ai_do_actual_firing_stuff ( object * obj , ai_static * aip )
{
if ( Game_mode & GM_MULTI )
if ( ( aip - > GOAL_STATE ! = AIS_FLIN ) & & ( obj - > id ! = ROBOT_BRAIN ) )
if ( aip - > CURRENT_STATE = = AIS_FIRE )
return 1 ;
return 0 ;
}
// --------------------------------------------------------------------------------------------------------------------
void ai_do_actual_firing_stuff ( object * obj , ai_static * aip , ai_local * ailp , robot_info * robptr , vms_vector * vec_to_player , fix dist_to_player , vms_vector * gun_point , int player_visibility , int object_animates )
{
fix dot ;
if ( player_visibility = = 2 ) {
// Changed by mk, 01/04/94, onearm would take about 9 seconds until he can fire at you.
// if (((!object_animates) || (ailp->achieved_state[aip->CURRENT_GUN] == AIS_FIRE)) && (ailp->next_fire <= 0)) {
if ( ! object_animates | | ( ailp - > next_fire < = 0 ) ) {
dot = vm_vec_dot ( & obj - > orient . fvec , vec_to_player ) ;
if ( dot > = 7 * F1_0 / 8 ) {
if ( aip - > CURRENT_GUN < Robot_info [ obj - > id ] . n_guns ) {
if ( robptr - > attack_type = = 1 ) {
if ( ! Player_exploded & & ( dist_to_player < obj - > size + ConsoleObject - > size + F1_0 * 2 ) ) { // robptr->circle_distance[Difficulty_level] + ConsoleObject->size) {
if ( ! ai_multiplayer_awareness ( obj , ROBOT_FIRE_AGITATION - 2 ) )
return ;
do_ai_robot_hit_attack ( obj , ConsoleObject , & obj - > pos ) ;
} else {
// mprintf((0, "Green won't fire: Too far: dist = %7.3f, threshold = %7.3f\n", f2fl(dist_to_player), f2fl(obj->size + ConsoleObject->size + F1_0*2)));
return ;
}
} else {
if ( ( gun_point - > x = = 0 ) & & ( gun_point - > y = = 0 ) & & ( gun_point - > z = = 0 ) ) {
; //mprintf((0, "Would like to fire gun, but gun not selected.\n"));
} else {
if ( ! ai_multiplayer_awareness ( obj , ROBOT_FIRE_AGITATION ) )
return ;
ai_fire_laser_at_player ( obj , gun_point ) ;
}
}
// Wants to fire, so should go into chase mode, probably.
if ( ( aip - > behavior ! = AIB_RUN_FROM ) & & ( aip - > behavior ! = AIB_STILL ) & & ( aip - > behavior ! = AIB_FOLLOW_PATH ) & & ( ( ailp - > mode = = AIM_FOLLOW_PATH ) | | ( ailp - > mode = = AIM_STILL ) ) )
ailp - > mode = AIM_CHASE_OBJECT ;
}
aip - > GOAL_STATE = AIS_RECO ;
ailp - > goal_state [ aip - > CURRENT_GUN ] = AIS_RECO ;
// Switch to next gun for next fire.
aip - > CURRENT_GUN + + ;
if ( aip - > CURRENT_GUN > = Robot_info [ obj - > id ] . n_guns )
aip - > CURRENT_GUN = 0 ;
}
}
} else if ( Weapon_info [ Robot_info [ obj - > id ] . weapon_type ] . homing_flag = = 1 ) {
// Robots which fire homing weapons might fire even if they don't have a bead on the player.
if ( ( ( ! object_animates ) | | ( ailp - > achieved_state [ aip - > CURRENT_GUN ] = = AIS_FIRE ) ) & & ( ailp - > next_fire < = 0 ) & & ( vm_vec_dist_quick ( & Hit_pos , & obj - > pos ) > F1_0 * 40 ) ) {
if ( ! ai_multiplayer_awareness ( obj , ROBOT_FIRE_AGITATION ) )
return ;
ai_fire_laser_at_player ( obj , gun_point ) ;
aip - > GOAL_STATE = AIS_RECO ;
ailp - > goal_state [ aip - > CURRENT_GUN ] = AIS_RECO ;
// Switch to next gun for next fire.
aip - > CURRENT_GUN + + ;
if ( aip - > CURRENT_GUN > = Robot_info [ obj - > id ] . n_guns )
aip - > CURRENT_GUN = 0 ;
} else {
// Switch to next gun for next fire.
aip - > CURRENT_GUN + + ;
if ( aip - > CURRENT_GUN > = Robot_info [ obj - > id ] . n_guns )
aip - > CURRENT_GUN = 0 ;
}
}
}
// --------------------------------------------------------------------------------------------------------------------
void do_ai_frame ( object * obj )
{
int objnum = obj - Objects ;
ai_static * aip = & obj - > ctype . ai_info ;
ai_local * ailp = & Ai_local_info [ objnum ] ;
fix dist_to_player ;
vms_vector vec_to_player ;
fix dot ;
robot_info * robptr ;
int player_visibility = - 1 ;
int obj_ref ;
int object_animates ;
int new_goal_state ;
int visibility_and_vec_computed = 0 ;
int previous_visibility ;
vms_vector gun_point ;
vms_vector vis_vec_pos ;
if ( aip - > SKIP_AI_COUNT ) {
aip - > SKIP_AI_COUNT - - ;
return ;
}
// Kind of a hack. If a robot is flinching, but it is time for it to fire, unflinch it.
// Else, you can turn a big nasty robot into a wimp by firing flares at it.
// This also allows the player to see the cool flinch effect for mechs without unbalancing the game.
if ( ( aip - > GOAL_STATE = = AIS_FLIN ) & & ( ailp - > next_fire < 0 ) ) {
aip - > GOAL_STATE = AIS_FIRE ;
}
# ifndef NDEBUG
if ( ( aip - > behavior = = AIB_RUN_FROM ) & & ( ailp - > mode ! = AIM_RUN_FROM_OBJECT ) )
Int3 ( ) ; // This is peculiar. Behavior is run from, but mode is not. Contact Mike.
mprintf_animation_info ( ( obj ) ) ;
if ( Ai_animation_test ) {
if ( aip - > GOAL_STATE = = aip - > CURRENT_STATE ) {
aip - > GOAL_STATE + + ;
if ( aip - > GOAL_STATE > AIS_RECO )
aip - > GOAL_STATE = AIS_REST ;
}
mprintf ( ( 0 , " Frame %4i, current = %i, goal = %i \n " , FrameCount , aip - > CURRENT_STATE , aip - > GOAL_STATE ) ) ;
object_animates = do_silly_animation ( obj ) ;
if ( object_animates )
ai_frame_animation ( obj ) ;
return ;
}
if ( ! Do_ai_flag )
return ;
if ( Break_on_object ! = - 1 )
if ( ( obj - Objects ) = = Break_on_object )
Int3 ( ) ; // Contact Mike: This is a debug break
# endif
Believed_player_pos = Ai_cloak_info [ objnum & ( MAX_AI_CLOAK_INFO - 1 ) ] . last_position ;
// mprintf((0, "Object %i: behavior = %02x, mode = %i, awareness = %i, time = %7.3f\n", obj-Objects, aip->behavior, ailp->mode, ailp->player_awareness_type, f2fl(ailp->player_awareness_time)));
// mprintf((0, "Object %i: behavior = %02x, mode = %i, awareness = %i, cur=%i, goal=%i\n", obj-Objects, aip->behavior, ailp->mode, ailp->player_awareness_type, aip->CURRENT_STATE, aip->GOAL_STATE));
// Assert((aip->behavior >= MIN_BEHAVIOR) && (aip->behavior <= MAX_BEHAVIOR));
if ( ! ( ( aip - > behavior > = MIN_BEHAVIOR ) & & ( aip - > behavior < = MAX_BEHAVIOR ) ) ) {
// mprintf((0, "Object %i behavior is %i, setting to AIB_NORMAL, fix in editor!\n", objnum, aip->behavior));
aip - > behavior = AIB_NORMAL ;
}
Assert ( obj - > segnum ! = - 1 ) ;
Assert ( obj - > id < N_robot_types ) ;
robptr = & Robot_info [ obj - > id ] ;
Assert ( robptr - > always_0xabcd = = 0xabcd ) ;
obj_ref = objnum ^ FrameCount ;
// -- if (ailp->wait_time > -F1_0*8)
// -- ailp->wait_time -= FrameTime;
if ( ailp - > next_fire > - F1_0 * 8 )
ailp - > next_fire - = FrameTime ;
if ( ailp - > time_since_processed < F1_0 * 256 )
ailp - > time_since_processed + = FrameTime ;
previous_visibility = ailp - > previous_visibility ; // Must get this before we toast the master copy!
// Deal with cloaking for robots which are cloaked except just before firing.
if ( robptr - > cloak_type = = RI_CLOAKED_EXCEPT_FIRING )
{
if ( ailp - > next_fire < F1_0 / 2 )
aip - > CLOAKED = 1 ;
else
aip - > CLOAKED = 0 ;
}
if ( ! ( Players [ Player_num ] . flags & PLAYER_FLAGS_CLOAKED ) )
Believed_player_pos = ConsoleObject - > pos ;
dist_to_player = vm_vec_dist_quick ( & Believed_player_pos , & obj - > pos ) ;
//--!-- mprintf((0, "%2i: %s, [vel = %5.1f %5.1f %5.1f] spd = %4.1f dtp=%5.1f ", objnum, mode_text[ailp->mode], f2fl(obj->mtype.phys_info.velocity.x), f2fl(obj->mtype.phys_info.velocity.y), f2fl(obj->mtype.phys_info.velocity.z), f2fl(vm_vec_mag(&obj->mtype.phys_info.velocity)), f2fl(dist_to_player)));
//--!-- if (ailp->mode == AIM_FOLLOW_PATH) {
//--!-- mprintf((0, "gseg = %i\n", Point_segs[aip->hide_index+aip->cur_path_index].segnum));
//--!-- } else
//--!-- mprintf((0, "\n"));
// If this robot can fire, compute visibility from gun position.
// Don't want to compute visibility twice, as it is expensive. (So is call to calc_gun_point).
if ( ( ailp - > next_fire < = 0 ) & & ( dist_to_player < F1_0 * 200 ) & & ( robptr - > n_guns ) & & ! ( robptr - > attack_type ) ) {
calc_gun_point ( & gun_point , obj , aip - > CURRENT_GUN ) ;
vis_vec_pos = gun_point ;
// mprintf((0, "Visibility = %i, computed from gun #%i\n", player_visibility, aip->CURRENT_GUN));
} else {
vis_vec_pos = obj - > pos ;
vm_vec_zero ( & gun_point ) ;
// mprintf((0, "Visibility = %i, computed from center.\n", player_visibility));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Occasionally make non-still robots make a path to the player. Based on agitation and distance from player.
if ( ( aip - > behavior ! = AIB_RUN_FROM ) & & ( aip - > behavior ! = AIB_STILL ) & & ! ( Game_mode & GM_MULTI ) )
if ( Overall_agitation > 70 ) {
if ( ( dist_to_player < F1_0 * 200 ) & & ( d_rand ( ) < FrameTime / 4 ) ) {
if ( d_rand ( ) * ( Overall_agitation - 40 ) > F1_0 * 5 ) {
// -- mprintf((0, "(1) Object #%i going from still to path in frame %i.\n", objnum, FrameCount));
create_path_to_player ( obj , 4 + Overall_agitation / 8 + Difficulty_level , 1 ) ;
// -- show_path_and_other(obj);
return ;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// If retry count not 0, then add it into consecutive_retries.
// If it is 0, cut down consecutive_retries.
// This is largely a hack to speed up physics and deal with stupid AI. This is low level
// communication between systems of a sort that should not be done.
if ( ( ailp - > retry_count ) & & ! ( Game_mode & GM_MULTI ) ) {
ailp - > consecutive_retries + = ailp - > retry_count ;
ailp - > retry_count = 0 ;
if ( ailp - > consecutive_retries > 3 ) {
switch ( ailp - > mode ) {
case AIM_CHASE_OBJECT :
// -- mprintf((0, "(2) Object #%i, retries while chasing, creating path to player in frame %i\n", objnum, FrameCount));
create_path_to_player ( obj , 4 + Overall_agitation / 8 + Difficulty_level , 1 ) ;
break ;
case AIM_STILL :
if ( ! ( ( aip - > behavior = = AIB_STILL ) | | ( aip - > behavior = = AIB_STATION ) ) ) // Behavior is still, so don't follow path.
attempt_to_resume_path ( obj ) ;
break ;
case AIM_FOLLOW_PATH :
// mprintf((0, "Object %i following path got %i retries in frame %i\n", obj-Objects, ailp->consecutive_retries, FrameCount));
if ( Game_mode & GM_MULTI )
ailp - > mode = AIM_STILL ;
else
attempt_to_resume_path ( obj ) ;
break ;
case AIM_RUN_FROM_OBJECT :
move_towards_segment_center ( obj ) ;
obj - > mtype . phys_info . velocity . x = 0 ;
obj - > mtype . phys_info . velocity . y = 0 ;
obj - > mtype . phys_info . velocity . z = 0 ;
create_n_segment_path ( obj , 5 , - 1 ) ;
ailp - > mode = AIM_RUN_FROM_OBJECT ;
break ;
case AIM_HIDE :
move_towards_segment_center ( obj ) ;
obj - > mtype . phys_info . velocity . x = 0 ;
obj - > mtype . phys_info . velocity . y = 0 ;
obj - > mtype . phys_info . velocity . z = 0 ;
// -- mprintf((0, "Hiding, yet creating path to player.\n"));
if ( Overall_agitation > ( 50 - Difficulty_level * 4 ) )
create_path_to_player ( obj , 4 + Overall_agitation / 8 , 1 ) ;
else {
create_n_segment_path ( obj , 5 , - 1 ) ;
}
break ;
case AIM_OPEN_DOOR :
create_n_segment_path_to_door ( obj , 5 , - 1 ) ;
break ;
# ifndef NDEBUG
case AIM_FOLLOW_PATH_2 :
Int3 ( ) ; // Should never happen!
break ;
# endif
}
ailp - > consecutive_retries = 0 ;
}
} else
ailp - > consecutive_retries / = 2 ;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// If in materialization center, exit
if ( ! ( Game_mode & GM_MULTI ) & & ( Segments [ obj - > segnum ] . special = = SEGMENT_IS_ROBOTMAKER ) ) {
ai_follow_path ( obj , 1 ) ; // 1 = player is visible, which might be a lie, but it works.
return ;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Decrease player awareness due to the passage of time.
//-----old, faster way from about 11/06/94----- if (ailp->player_awareness_type) {
//-----old, faster way from about 11/06/94----- if (ailp->player_awareness_time > 0) {
//-----old, faster way from about 11/06/94----- ailp->player_awareness_time -= FrameTime;
//-----old, faster way from about 11/06/94----- if (ailp->player_awareness_time <= 0) {
//-----old, faster way from about 11/06/94----- ailp->player_awareness_time = F1_0*2;
//-----old, faster way from about 11/06/94----- ailp->player_awareness_type--;
//-----old, faster way from about 11/06/94----- if (ailp->player_awareness_type < 0) {
//-----old, faster way from about 11/06/94----- aip->GOAL_STATE = AIS_REST;
//-----old, faster way from about 11/06/94----- ailp->player_awareness_type = 0;
//-----old, faster way from about 11/06/94----- }
//-----old, faster way from about 11/06/94-----
//-----old, faster way from about 11/06/94----- }
//-----old, faster way from about 11/06/94----- } else {
//-----old, faster way from about 11/06/94----- ailp->player_awareness_type = 0;
//-----old, faster way from about 11/06/94----- aip->GOAL_STATE = AIS_REST;
//-----old, faster way from about 11/06/94----- }
//-----old, faster way from about 11/06/94----- } else
//-----old, faster way from about 11/06/94----- aip->GOAL_STATE = AIS_REST;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Decrease player awareness due to the passage of time.
if ( ailp - > player_awareness_type ) {
if ( ailp - > player_awareness_time > 0 ) {
ailp - > player_awareness_time - = FrameTime ;
if ( ailp - > player_awareness_time < = 0 ) {
ailp - > player_awareness_time = F1_0 * 2 ; //new: 11/05/94
ailp - > player_awareness_type - - ; //new: 11/05/94
}
} else {
ailp - > player_awareness_type - - ;
ailp - > player_awareness_time = F1_0 * 2 ;
// aip->GOAL_STATE = AIS_REST;
}
} else
aip - > GOAL_STATE = AIS_REST ; //new: 12/13/94
if ( Player_is_dead & & ( ailp - > player_awareness_type = = 0 ) )
if ( ( dist_to_player < F1_0 * 200 ) & & ( d_rand ( ) < FrameTime / 8 ) ) {
if ( ( aip - > behavior ! = AIB_STILL ) & & ( aip - > behavior ! = AIB_RUN_FROM ) ) {
if ( ! ai_multiplayer_awareness ( obj , 30 ) )
return ;
ai_multi_send_robot_position ( objnum , - 1 ) ;
if ( ! ( ( ailp - > mode = = AIM_FOLLOW_PATH ) & & ( aip - > cur_path_index < aip - > path_length - 1 ) ) )
{
if ( dist_to_player < F1_0 * 30 )
create_n_segment_path ( obj , 5 , 1 ) ;
else
create_path_to_player ( obj , 20 , 1 ) ;
}
}
}
// Make sure that if this guy got hit or bumped, then he's chasing player.
if ( ( ailp - > player_awareness_type = = PA_WEAPON_ROBOT_COLLISION ) | | ( ailp - > player_awareness_type > = PA_PLAYER_COLLISION ) ) {
if ( ( aip - > behavior ! = AIB_STILL ) & & ( aip - > behavior ! = AIB_FOLLOW_PATH ) & & ( aip - > behavior ! = AIB_RUN_FROM ) & & ( obj - > id ! = ROBOT_BRAIN ) )
ailp - > mode = AIM_CHASE_OBJECT ;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if ( ( aip - > GOAL_STATE = = AIS_FLIN ) & & ( aip - > CURRENT_STATE = = AIS_FLIN ) )
aip - > GOAL_STATE = AIS_LOCK ;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Note: Should only do these two function calls for objects which animate
if ( ( dist_to_player < F1_0 * 100 ) ) { // && !(Game_mode & GM_MULTI)) {
object_animates = do_silly_animation ( obj ) ;
if ( object_animates )
ai_frame_animation ( obj ) ;
//mprintf((0, "Object %i: goal=%i, current=%i\n", obj-Objects, obj->ctype.ai_info.GOAL_STATE, obj->ctype.ai_info.CURRENT_STATE));
} else {
// If Object is supposed to animate, but we don't let it animate due to distance, then
// we must change its state, else it will never update.
aip - > CURRENT_STATE = aip - > GOAL_STATE ;
object_animates = 0 ; // If we're not doing the animation, then should pretend it doesn't animate.
}
//Processed_this_frame++;
//if (FrameCount != LastFrameCount) {
// LastFrameCount = FrameCount;
// mprintf((0, "Processed in frame %i = %i robots\n", FrameCount-1, Processed_this_frame));
// Processed_this_frame = 0;
//}
switch ( Robot_info [ obj - > id ] . boss_flag ) {
case 0 :
break ;
case 1 :
if ( aip - > GOAL_STATE = = AIS_FLIN )
aip - > GOAL_STATE = AIS_FIRE ;
if ( aip - > CURRENT_STATE = = AIS_FLIN )
aip - > CURRENT_STATE = AIS_FIRE ;
dist_to_player / = 4 ;
do_boss_stuff ( obj ) ;
dist_to_player * = 4 ;
break ;
# ifndef SHAREWARE
case 2 :
if ( aip - > GOAL_STATE = = AIS_FLIN )
aip - > GOAL_STATE = AIS_FIRE ;
if ( aip - > CURRENT_STATE = = AIS_FLIN )
aip - > CURRENT_STATE = AIS_FIRE ;
compute_vis_and_vec ( obj , & vis_vec_pos , ailp , & vec_to_player , & player_visibility , robptr , & visibility_and_vec_computed ) ;
{ int pv = player_visibility ;
fix dtp = dist_to_player / 4 ;
// If player cloaked, visibility is screwed up and superboss will gate in robots when not supposed to.
if ( Players [ Player_num ] . flags & PLAYER_FLAGS_CLOAKED ) {
pv = 0 ;
dtp = vm_vec_dist_quick ( & ConsoleObject - > pos , & obj - > pos ) / 4 ;
}
do_super_boss_stuff ( obj , dtp , pv ) ;
}
break ;
# endif
default :
Int3 ( ) ; // Bogus boss flag value.
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Time-slice, don't process all the time, purely an efficiency hack.
// Guys whose behavior is station and are not at their hide segment get processed anyway.
if ( ailp - > player_awareness_type < PA_WEAPON_ROBOT_COLLISION - 1 ) { // If robot got hit, he gets to attack player always!
# ifndef NDEBUG
if ( Break_on_object ! = objnum ) { // don't time slice if we're interested in this object.
# endif
if ( ( dist_to_player > F1_0 * 250 ) & & ( ailp - > time_since_processed < = F1_0 * 2 ) )
return ;
else if ( ! ( ( aip - > behavior = = AIB_STATION ) & & ( ailp - > mode = = AIM_FOLLOW_PATH ) & & ( aip - > hide_segment ! = obj - > segnum ) ) ) {
if ( ( dist_to_player > F1_0 * 150 ) & & ( ailp - > time_since_processed < = F1_0 ) )
return ;
else if ( ( dist_to_player > F1_0 * 100 ) & & ( ailp - > time_since_processed < = F1_0 / 2 ) )
return ;
}
# ifndef NDEBUG
}
# endif
}
// -- if ((aip->behavior == AIB_STATION) && (ailp->mode == AIM_FOLLOW_PATH) && (aip->hide_segment != obj->segnum))
// -- mprintf((0, "[%i] ", obj-Objects));
// Reset time since processed, but skew objects so not everything processed synchronously, else
// we get fast frames with the occasional very slow frame.
// AI_proc_time = ailp->time_since_processed;
ailp - > time_since_processed = - ( ( objnum & 0x03 ) * FrameTime ) / 2 ;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Perform special ability
switch ( obj - > id ) {
case ROBOT_BRAIN :
// Robots function nicely if behavior is Station. This means they won't move until they
// can see the player, at which time they will start wandering about opening doors.
if ( ConsoleObject - > segnum = = obj - > segnum ) {
if ( ! ai_multiplayer_awareness ( obj , 97 ) )
return ;
compute_vis_and_vec ( obj , & vis_vec_pos , ailp , & vec_to_player , & player_visibility , robptr , & visibility_and_vec_computed ) ;
move_away_from_player ( obj , & vec_to_player , 0 ) ;
ai_multi_send_robot_position ( objnum , - 1 ) ;
} else if ( ailp - > mode ! = AIM_STILL ) {
int r ;
r = openable_doors_in_segment ( obj ) ;
if ( r ! = - 1 ) {
ailp - > mode = AIM_OPEN_DOOR ;
aip - > GOALSIDE = r ;
} else if ( ailp - > mode ! = AIM_FOLLOW_PATH ) {
if ( ! ai_multiplayer_awareness ( obj , 50 ) )
return ;
create_n_segment_path_to_door ( obj , 8 + Difficulty_level , - 1 ) ; // third parameter is avoid_seg, -1 means avoid nothing.
ai_multi_send_robot_position ( objnum , - 1 ) ;
}
} else {
compute_vis_and_vec ( obj , & vis_vec_pos , ailp , & vec_to_player , & player_visibility , robptr , & visibility_and_vec_computed ) ;
if ( player_visibility ) {
if ( ! ai_multiplayer_awareness ( obj , 50 ) )
return ;
create_n_segment_path_to_door ( obj , 8 + Difficulty_level , - 1 ) ; // third parameter is avoid_seg, -1 means avoid nothing.
ai_multi_send_robot_position ( objnum , - 1 ) ;
}
}
break ;
default :
break ;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
switch ( ailp - > mode ) {
case AIM_CHASE_OBJECT : { // chasing player, sort of, chase if far, back off if close, circle in between
fix circle_distance ;
circle_distance = robptr - > circle_distance [ Difficulty_level ] + ConsoleObject - > size ;
// Green guy doesn't get his circle distance boosted, else he might never attack.
if ( robptr - > attack_type ! = 1 )
circle_distance + = ( objnum & 0xf ) * F1_0 / 2 ;
compute_vis_and_vec ( obj , & vis_vec_pos , ailp , & vec_to_player , & player_visibility , robptr , & visibility_and_vec_computed ) ;
// @mk, 12/27/94, structure here was strange. Would do both clauses of what are now this if/then/else. Used to be if/then, if/then.
if ( ( player_visibility < 2 ) & & ( previous_visibility = = 2 ) ) { // this is redundant: mk, 01/15/95: && (ailp->mode == AIM_CHASE_OBJECT)) {
// mprintf((0, "I used to be able to see the player!\n"));
if ( ! ai_multiplayer_awareness ( obj , 53 ) ) {
if ( maybe_ai_do_actual_firing_stuff ( obj , aip ) )
ai_do_actual_firing_stuff ( obj , aip , ailp , robptr , & vec_to_player , dist_to_player , & gun_point , player_visibility , object_animates ) ;
return ;
}
// -- mprintf((0, "(3) Object #%i going from chase to player path in frame %i.\n", objnum, FrameCount));
create_path_to_player ( obj , 8 , 1 ) ;
// -- show_path_and_other(obj);
ai_multi_send_robot_position ( objnum , - 1 ) ;
} else if ( ( player_visibility = = 0 ) & & ( dist_to_player > F1_0 * 80 ) & & ( ! ( Game_mode & GM_MULTI ) ) ) {
// If pretty far from the player, player cannot be seen (obstructed) and in chase mode, switch to follow path mode.
// This has one desirable benefit of avoiding physics retries.
if ( aip - > behavior = = AIB_STATION ) {
ailp - > goal_segment = aip - > hide_segment ;
// -- mprintf((0, "(1) Object #%i going from chase to STATION in frame %i.\n", objnum, FrameCount));
create_path_to_station ( obj , 15 ) ;
// -- show_path_and_other(obj);
} else
create_n_segment_path ( obj , 5 , - 1 ) ;
break ;
}
if ( ( aip - > CURRENT_STATE = = AIS_REST ) & & ( aip - > GOAL_STATE = = AIS_REST ) ) {
if ( player_visibility ) {
if ( d_rand ( ) < FrameTime * player_visibility ) {
if ( dist_to_player / 256 < d_rand ( ) * player_visibility ) {
// mprintf((0, "Object %i searching for player.\n", obj-Objects));
aip - > GOAL_STATE = AIS_SRCH ;
aip - > CURRENT_STATE = AIS_SRCH ;
}
}
}
}
if ( GameTime - ailp - > time_player_seen > CHASE_TIME_LENGTH ) {
if ( Game_mode & GM_MULTI )
if ( ! player_visibility & & ( dist_to_player > F1_0 * 70 ) ) {
ailp - > mode = AIM_STILL ;
return ;
}
if ( ! ai_multiplayer_awareness ( obj , 64 ) ) {
if ( maybe_ai_do_actual_firing_stuff ( obj , aip ) )
ai_do_actual_firing_stuff ( obj , aip , ailp , robptr , & vec_to_player , dist_to_player , & gun_point , player_visibility , object_animates ) ;
return ;
}
// -- mprintf((0, "(4) Object #%i going from chase to player path in frame %i.\n", objnum, FrameCount));
create_path_to_player ( obj , 10 , 1 ) ;
// -- show_path_and_other(obj);
ai_multi_send_robot_position ( objnum , - 1 ) ;
} else if ( ( aip - > CURRENT_STATE ! = AIS_REST ) & & ( aip - > GOAL_STATE ! = AIS_REST ) ) {
if ( ! ai_multiplayer_awareness ( obj , 70 ) ) {
if ( maybe_ai_do_actual_firing_stuff ( obj , aip ) )
ai_do_actual_firing_stuff ( obj , aip , ailp , robptr , & vec_to_player , dist_to_player , & gun_point , player_visibility , object_animates ) ;
return ;
}
ai_move_relative_to_player ( obj , ailp , dist_to_player , & vec_to_player , circle_distance , 0 ) ;
if ( ( obj_ref & 1 ) & & ( ( aip - > GOAL_STATE = = AIS_SRCH ) | | ( aip - > GOAL_STATE = = AIS_LOCK ) ) ) {
if ( player_visibility ) // == 2)
ai_turn_towards_vector ( & vec_to_player , obj , robptr - > turn_time [ Difficulty_level ] ) ;
else
ai_turn_randomly ( & vec_to_player , obj , robptr - > turn_time [ Difficulty_level ] , previous_visibility ) ;
}
if ( ai_evaded ) {
ai_multi_send_robot_position ( objnum , 1 ) ;
ai_evaded = 0 ;
}
else
ai_multi_send_robot_position ( objnum , - 1 ) ;
do_firing_stuff ( obj , player_visibility , & vec_to_player ) ;
}
break ;
}
case AIM_RUN_FROM_OBJECT :
compute_vis_and_vec ( obj , & vis_vec_pos , ailp , & vec_to_player , & player_visibility , robptr , & visibility_and_vec_computed ) ;
if ( player_visibility ) {
if ( ailp - > player_awareness_type = = 0 )
ailp - > player_awareness_type = PA_WEAPON_ROBOT_COLLISION ;
}
// If in multiplayer, only do if player visible. If not multiplayer, do always.
if ( ! ( Game_mode & GM_MULTI ) | | player_visibility )
if ( ai_multiplayer_awareness ( obj , 75 ) ) {
ai_follow_path ( obj , player_visibility ) ;
ai_multi_send_robot_position ( objnum , - 1 ) ;
}
if ( aip - > GOAL_STATE ! = AIS_FLIN )
aip - > GOAL_STATE = AIS_LOCK ;
else if ( aip - > CURRENT_STATE = = AIS_FLIN )
aip - > GOAL_STATE = AIS_LOCK ;
// Bad to let run_from robot fire at player because it will cause a war in which it turns towards the
// player to fire and then towards its goal to move.
// do_firing_stuff(obj, player_visibility, &vec_to_player);
// Instead, do this:
// (Note, only drop if player is visible. This prevents the bombs from being a giveaway, and
// also ensures that the robot is moving while it is dropping. Also means fewer will be dropped.)
if ( ( ailp - > next_fire < = 0 ) & & ( player_visibility ) ) {
vms_vector fire_vec , fire_pos ;
if ( ! ai_multiplayer_awareness ( obj , 75 ) )
return ;
fire_vec = obj - > orient . fvec ;
vm_vec_negate ( & fire_vec ) ;
vm_vec_add ( & fire_pos , & obj - > pos , & fire_vec ) ;
Laser_create_new_easy ( & fire_vec , & fire_pos , obj - Objects , PROXIMITY_ID , 1 ) ;
ailp - > next_fire = F1_0 * 5 ; // Drop a proximity bomb every 5 seconds.
# ifdef NETWORK
# ifndef SHAREWARE
if ( Game_mode & GM_MULTI )
{
ai_multi_send_robot_position ( obj - Objects , - 1 ) ;
multi_send_robot_fire ( obj - Objects , - 1 , & fire_vec ) ;
}
# endif
# endif
}
break ;
case AIM_FOLLOW_PATH : {
int anger_level = 65 ;
if ( aip - > behavior = = AIB_STATION )
if ( Point_segs [ aip - > hide_index + aip - > path_length - 1 ] . segnum = = aip - > hide_segment ) {
anger_level = 64 ;
// mprintf((0, "Object %i, station, lowering anger to 64.\n"));
}
compute_vis_and_vec ( obj , & vis_vec_pos , ailp , & vec_to_player , & player_visibility , robptr , & visibility_and_vec_computed ) ;
if ( ! ai_multiplayer_awareness ( obj , anger_level ) ) {
if ( maybe_ai_do_actual_firing_stuff ( obj , aip ) ) {
compute_vis_and_vec ( obj , & vis_vec_pos , ailp , & vec_to_player , & player_visibility , robptr , & visibility_and_vec_computed ) ;
ai_do_actual_firing_stuff ( obj , aip , ailp , robptr , & vec_to_player , dist_to_player , & gun_point , player_visibility , object_animates ) ;
}
return ;
}
ai_follow_path ( obj , player_visibility ) ;
if ( aip - > GOAL_STATE ! = AIS_FLIN )
aip - > GOAL_STATE = AIS_LOCK ;
else if ( aip - > CURRENT_STATE = = AIS_FLIN )
aip - > GOAL_STATE = AIS_LOCK ;
if ( ( aip - > behavior ! = AIB_FOLLOW_PATH ) & & ( aip - > behavior ! = AIB_RUN_FROM ) )
do_firing_stuff ( obj , player_visibility , & vec_to_player ) ;
if ( ( player_visibility = = 2 ) & & ( aip - > behavior ! = AIB_FOLLOW_PATH ) & & ( aip - > behavior ! = AIB_RUN_FROM ) & & ( obj - > id ! = ROBOT_BRAIN ) ) {
if ( robptr - > attack_type = = 0 )
ailp - > mode = AIM_CHASE_OBJECT ;
} else if ( ( player_visibility = = 0 ) & & ( aip - > behavior = = AIB_NORMAL ) & & ( ailp - > mode = = AIM_FOLLOW_PATH ) & & ( aip - > behavior ! = AIB_RUN_FROM ) ) {
ailp - > mode = AIM_STILL ;
aip - > hide_index = - 1 ;
aip - > path_length = 0 ;
}
ai_multi_send_robot_position ( objnum , - 1 ) ;
break ;
}
case AIM_HIDE :
if ( ! ai_multiplayer_awareness ( obj , 71 ) ) {
if ( maybe_ai_do_actual_firing_stuff ( obj , aip ) ) {
compute_vis_and_vec ( obj , & vis_vec_pos , ailp , & vec_to_player , & player_visibility , robptr , & visibility_and_vec_computed ) ;
ai_do_actual_firing_stuff ( obj , aip , ailp , robptr , & vec_to_player , dist_to_player , & gun_point , player_visibility , object_animates ) ;
}
return ;
}
compute_vis_and_vec ( obj , & vis_vec_pos , ailp , & vec_to_player , & player_visibility , robptr , & visibility_and_vec_computed ) ;
ai_follow_path ( obj , player_visibility ) ;
if ( aip - > GOAL_STATE ! = AIS_FLIN )
aip - > GOAL_STATE = AIS_LOCK ;
else if ( aip - > CURRENT_STATE = = AIS_FLIN )
aip - > GOAL_STATE = AIS_LOCK ;
ai_multi_send_robot_position ( objnum , - 1 ) ;
break ;
case AIM_STILL :
if ( ( dist_to_player < F1_0 * 120 + Difficulty_level * F1_0 * 20 ) | | ( ailp - > player_awareness_type > = PA_WEAPON_ROBOT_COLLISION - 1 ) ) {
compute_vis_and_vec ( obj , & vis_vec_pos , ailp , & vec_to_player , & player_visibility , robptr , & visibility_and_vec_computed ) ;
// turn towards vector if visible this time or last time, or rand
// new!
if ( ( player_visibility ) | | ( previous_visibility ) | | ( ( d_rand ( ) > 0x4000 ) & & ! ( Game_mode & GM_MULTI ) ) ) {
if ( ! ai_multiplayer_awareness ( obj , 71 ) ) {
if ( maybe_ai_do_actual_firing_stuff ( obj , aip ) )
ai_do_actual_firing_stuff ( obj , aip , ailp , robptr , & vec_to_player , dist_to_player , & gun_point , player_visibility , object_animates ) ;
return ;
}
ai_turn_towards_vector ( & vec_to_player , obj , robptr - > turn_time [ Difficulty_level ] ) ;
ai_multi_send_robot_position ( objnum , - 1 ) ;
}
do_firing_stuff ( obj , player_visibility , & vec_to_player ) ;
// This is debugging code! Remove it! It's to make the green guy attack without doing other kinds of movement.
if ( player_visibility ) { // Change, MK, 01/03/94 for Multiplayer reasons. If robots can't see you (even with eyes on back of head), then don't do evasion.
if ( robptr - > attack_type = = 1 ) {
aip - > behavior = AIB_NORMAL ;
if ( ! ai_multiplayer_awareness ( obj , 80 ) ) {
if ( maybe_ai_do_actual_firing_stuff ( obj , aip ) )
ai_do_actual_firing_stuff ( obj , aip , ailp , robptr , & vec_to_player , dist_to_player , & gun_point , player_visibility , object_animates ) ;
return ;
}
ai_move_relative_to_player ( obj , ailp , dist_to_player , & vec_to_player , 0 , 0 ) ;
if ( ai_evaded ) {
ai_multi_send_robot_position ( objnum , 1 ) ;
ai_evaded = 0 ;
}
else
ai_multi_send_robot_position ( objnum , - 1 ) ;
} else {
// Robots in hover mode are allowed to evade at half normal speed.
if ( ! ai_multiplayer_awareness ( obj , 81 ) ) {
if ( maybe_ai_do_actual_firing_stuff ( obj , aip ) )
ai_do_actual_firing_stuff ( obj , aip , ailp , robptr , & vec_to_player , dist_to_player , & gun_point , player_visibility , object_animates ) ;
return ;
}
ai_move_relative_to_player ( obj , ailp , dist_to_player , & vec_to_player , 0 , 1 ) ;
if ( ai_evaded ) {
ai_multi_send_robot_position ( objnum , - 1 ) ;
ai_evaded = 0 ;
}
else
ai_multi_send_robot_position ( objnum , - 1 ) ;
}
} else if ( ( obj - > segnum ! = aip - > hide_segment ) & & ( dist_to_player > F1_0 * 80 ) & & ( ! ( Game_mode & GM_MULTI ) ) ) {
// If pretty far from the player, player cannot be seen (obstructed) and in chase mode, switch to follow path mode.
// This has one desirable benefit of avoiding physics retries.
if ( aip - > behavior = = AIB_STATION ) {
ailp - > goal_segment = aip - > hide_segment ;
// -- mprintf((0, "(2) Object #%i going from STILL to STATION in frame %i.\n", objnum, FrameCount));
create_path_to_station ( obj , 15 ) ;
// -- show_path_and_other(obj);
}
break ;
}
}
break ;
case AIM_OPEN_DOOR : { // trying to open a door.
vms_vector center_point , goal_vector ;
Assert ( obj - > id = = ROBOT_BRAIN ) ; // Make sure this guy is allowed to be in this mode.
if ( ! ai_multiplayer_awareness ( obj , 62 ) )
return ;
compute_center_point_on_side ( & center_point , & Segments [ obj - > segnum ] , aip - > GOALSIDE ) ;
vm_vec_sub ( & goal_vector , & center_point , & obj - > pos ) ;
vm_vec_normalize_quick ( & goal_vector ) ;
ai_turn_towards_vector ( & goal_vector , obj , robptr - > turn_time [ Difficulty_level ] ) ;
move_towards_vector ( obj , & goal_vector ) ;
ai_multi_send_robot_position ( objnum , - 1 ) ;
break ;
}
default :
mprintf ( ( 0 , " Unknown mode = %i in robot %i, behavior = %i \n " , ailp - > mode , obj - Objects , aip - > behavior ) ) ;
ailp - > mode = AIM_CHASE_OBJECT ;
break ;
} // end: switch (ailp->mode) {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// If the robot can see you, increase his awareness of you.
// This prevents the problem of a robot looking right at you but doing nothing.
// Assert(player_visibility != -1); // Means it didn't get initialized!
compute_vis_and_vec ( obj , & vis_vec_pos , ailp , & vec_to_player , & player_visibility , robptr , & visibility_and_vec_computed ) ;
if ( player_visibility = = 2 )
if ( ailp - > player_awareness_type = = 0 )
ailp - > player_awareness_type = PA_PLAYER_COLLISION ;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if ( ! object_animates ) {
aip - > CURRENT_STATE = aip - > GOAL_STATE ;
// mprintf((0, "Setting current to goal (%i) because object doesn't animate.\n", aip->GOAL_STATE));
}
Assert ( ailp - > player_awareness_type < = AIE_MAX ) ;
Assert ( aip - > CURRENT_STATE < AIS_MAX ) ;
Assert ( aip - > GOAL_STATE < AIS_MAX ) ;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if ( ailp - > player_awareness_type ) {
new_goal_state = Ai_transition_table [ ailp - > player_awareness_type - 1 ] [ aip - > CURRENT_STATE ] [ aip - > GOAL_STATE ] ;
if ( ailp - > player_awareness_type = = PA_WEAPON_ROBOT_COLLISION ) {
// Decrease awareness, else this robot will flinch every frame.
ailp - > player_awareness_type - - ;
ailp - > player_awareness_time = F1_0 * 3 ;
}
if ( new_goal_state = = AIS_ERR_ )
new_goal_state = AIS_REST ;
if ( aip - > CURRENT_STATE = = AIS_NONE )
aip - > CURRENT_STATE = AIS_REST ;
aip - > GOAL_STATE = new_goal_state ;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// If new state = fire, then set all gun states to fire.
if ( ( aip - > GOAL_STATE = = AIS_FIRE ) ) {
int i , num_guns ;
num_guns = Robot_info [ obj - > id ] . n_guns ;
for ( i = 0 ; i < num_guns ; i + + )
ailp - > goal_state [ i ] = AIS_FIRE ;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Hack by mk on 01/04/94, if a guy hasn't animated to the firing state, but his next_fire says ok to fire, bash him there
if ( ( ailp - > next_fire < 0 ) & & ( aip - > GOAL_STATE = = AIS_FIRE ) )
aip - > CURRENT_STATE = AIS_FIRE ;
if ( ( aip - > GOAL_STATE ! = AIS_FLIN ) & & ( obj - > id ! = ROBOT_BRAIN ) ) {
switch ( aip - > CURRENT_STATE ) {
case AIS_NONE :
compute_vis_and_vec ( obj , & vis_vec_pos , ailp , & vec_to_player , & player_visibility , robptr , & visibility_and_vec_computed ) ;
dot = vm_vec_dot ( & obj - > orient . fvec , & vec_to_player ) ;
if ( dot > = F1_0 / 2 )
if ( aip - > GOAL_STATE = = AIS_REST )
aip - > GOAL_STATE = AIS_SRCH ;
//mprintf((0, "State = none, goal = %i.\n", aip->GOAL_STATE));
break ;
case AIS_REST :
if ( aip - > GOAL_STATE = = AIS_REST ) {
compute_vis_and_vec ( obj , & vis_vec_pos , ailp , & vec_to_player , & player_visibility , robptr , & visibility_and_vec_computed ) ;
if ( ( ailp - > next_fire < = 0 ) & & ( player_visibility ) ) {
// mprintf((0, "Setting goal state to fire from rest.\n"));
aip - > GOAL_STATE = AIS_FIRE ;
}
//mprintf((0, "State = rest, goal = %i.\n", aip->GOAL_STATE));
}
break ;
case AIS_SRCH :
if ( ! ai_multiplayer_awareness ( obj , 60 ) )
return ;
compute_vis_and_vec ( obj , & vis_vec_pos , ailp , & vec_to_player , & player_visibility , robptr , & visibility_and_vec_computed ) ;
if ( player_visibility ) {
ai_turn_towards_vector ( & vec_to_player , obj , robptr - > turn_time [ Difficulty_level ] ) ;
ai_multi_send_robot_position ( objnum , - 1 ) ;
} else if ( ! ( Game_mode & GM_MULTI ) )
ai_turn_randomly ( & vec_to_player , obj , robptr - > turn_time [ Difficulty_level ] , previous_visibility ) ;
break ;
case AIS_LOCK :
compute_vis_and_vec ( obj , & vis_vec_pos , ailp , & vec_to_player , & player_visibility , robptr , & visibility_and_vec_computed ) ;
if ( ! ( Game_mode & GM_MULTI ) | | ( player_visibility ) ) {
if ( ! ai_multiplayer_awareness ( obj , 68 ) )
return ;
if ( player_visibility ) {
ai_turn_towards_vector ( & vec_to_player , obj , robptr - > turn_time [ Difficulty_level ] ) ;
ai_multi_send_robot_position ( objnum , - 1 ) ;
} else if ( ! ( Game_mode & GM_MULTI ) )
ai_turn_randomly ( & vec_to_player , obj , robptr - > turn_time [ Difficulty_level ] , previous_visibility ) ;
}
break ;
case AIS_FIRE :
compute_vis_and_vec ( obj , & vis_vec_pos , ailp , & vec_to_player , & player_visibility , robptr , & visibility_and_vec_computed ) ;
if ( player_visibility ) {
if ( ! ai_multiplayer_awareness ( obj , ( ROBOT_FIRE_AGITATION - 1 ) ) )
{
if ( Game_mode & GM_MULTI ) {
ai_do_actual_firing_stuff ( obj , aip , ailp , robptr , & vec_to_player , dist_to_player , & gun_point , player_visibility , object_animates ) ;
return ;
}
}
ai_turn_towards_vector ( & vec_to_player , obj , robptr - > turn_time [ Difficulty_level ] ) ;
ai_multi_send_robot_position ( objnum , - 1 ) ;
} else if ( ! ( Game_mode & GM_MULTI ) ) {
ai_turn_randomly ( & vec_to_player , obj , robptr - > turn_time [ Difficulty_level ] , previous_visibility ) ;
}
// Fire at player, if appropriate.
ai_do_actual_firing_stuff ( obj , aip , ailp , robptr , & vec_to_player , dist_to_player , & gun_point , player_visibility , object_animates ) ;
break ;
case AIS_RECO :
if ( ! ( obj_ref & 3 ) ) {
compute_vis_and_vec ( obj , & vis_vec_pos , ailp , & vec_to_player , & player_visibility , robptr , & visibility_and_vec_computed ) ;
if ( player_visibility ) {
if ( ! ai_multiplayer_awareness ( obj , 69 ) )
return ;
ai_turn_towards_vector ( & vec_to_player , obj , robptr - > turn_time [ Difficulty_level ] ) ;
ai_multi_send_robot_position ( objnum , - 1 ) ;
} else if ( ! ( Game_mode & GM_MULTI ) ) {
ai_turn_randomly ( & vec_to_player , obj , robptr - > turn_time [ Difficulty_level ] , previous_visibility ) ;
}
}
break ;
case AIS_FLIN :
// mprintf((0, "State = flinch, goal = %i.\n", aip->GOAL_STATE));
break ;
default :
mprintf ( ( 1 , " Unknown mode for AI object #%i \n " , objnum ) ) ;
aip - > GOAL_STATE = AIS_REST ;
aip - > CURRENT_STATE = AIS_REST ;
break ;
}
} // end of: if (aip->GOAL_STATE != AIS_FLIN) {
// Switch to next gun for next fire.
if ( player_visibility = = 0 ) {
aip - > CURRENT_GUN + + ;
if ( aip - > CURRENT_GUN > = Robot_info [ obj - > id ] . n_guns )
aip - > CURRENT_GUN = 0 ;
}
}
//--mk, 121094 -- // ----------------------------------------------------------------------------------
//--mk, 121094 -- void spin_robot(object *robot, vms_vector *collision_point)
//--mk, 121094 -- {
//--mk, 121094 -- if (collision_point->x != 3) {
//--mk, 121094 -- robot->phys_info.rotvel.x = 0x1235;
//--mk, 121094 -- robot->phys_info.rotvel.y = 0x2336;
//--mk, 121094 -- robot->phys_info.rotvel.z = 0x3737;
//--mk, 121094 -- }
//--mk, 121094 --
//--mk, 121094 -- }
// -----------------------------------------------------------------------------------
void ai_do_cloak_stuff ( void )
{
int i ;
for ( i = 0 ; i < MAX_AI_CLOAK_INFO ; i + + ) {
Ai_cloak_info [ i ] . last_position = ConsoleObject - > pos ;
Ai_cloak_info [ i ] . last_time = GameTime ;
}
// Make work for control centers.
Believed_player_pos = Ai_cloak_info [ 0 ] . last_position ;
}
// -----------------------------------------------------------------------------------
// Returns false if awareness is considered too puny to add, else returns true.
int add_awareness_event ( object * objp , int type )
{
// If player cloaked and hit a robot, then increase awareness
if ( ( type = = PA_WEAPON_ROBOT_COLLISION ) | | ( type = = PA_WEAPON_WALL_COLLISION ) | | ( type = = PA_PLAYER_COLLISION ) )
ai_do_cloak_stuff ( ) ;
if ( Num_awareness_events < MAX_AWARENESS_EVENTS ) {
if ( ( type = = PA_WEAPON_WALL_COLLISION ) | | ( type = = PA_WEAPON_ROBOT_COLLISION ) )
if ( objp - > id = = VULCAN_ID )
if ( d_rand ( ) > 3276 )
return 0 ; // For vulcan cannon, only about 1/10 actually cause awareness
Awareness_events [ Num_awareness_events ] . segnum = objp - > segnum ;
Awareness_events [ Num_awareness_events ] . pos = objp - > pos ;
Awareness_events [ Num_awareness_events ] . type = type ;
Num_awareness_events + + ;
} else
Assert ( 0 ) ; // Hey -- Overflowed Awareness_events, make more or something
// This just gets ignored, so you can just continue.
return 1 ;
}
// ----------------------------------------------------------------------------------
// Robots will become aware of the player based on something that occurred.
// The object (probably player or weapon) which created the awareness is objp.
void create_awareness_event ( object * objp , int type )
{
if ( add_awareness_event ( objp , type ) ) {
if ( ( ( d_rand ( ) * ( type + 4 ) ) > > 15 ) > 4 )
Overall_agitation + + ;
if ( Overall_agitation > OVERALL_AGITATION_MAX )
Overall_agitation = OVERALL_AGITATION_MAX ;
}
}
2006-10-06 14:41:31 +00:00
sbyte New_awareness [ MAX_SEGMENTS ] ;
2006-03-20 16:43:15 +00:00
// ----------------------------------------------------------------------------------
void pae_aux ( int segnum , int type , int level )
{
int j ;
if ( New_awareness [ segnum ] < type )
New_awareness [ segnum ] = type ;
// Process children.
if ( level < = 4 )
for ( j = 0 ; j < MAX_SIDES_PER_SEGMENT ; j + + )
if ( IS_CHILD ( Segments [ segnum ] . children [ j ] ) )
{
if ( type = = 4 )
pae_aux ( Segments [ segnum ] . children [ j ] , type - 1 , level + 1 ) ;
else
pae_aux ( Segments [ segnum ] . children [ j ] , type , level + 1 ) ;
}
}
// ----------------------------------------------------------------------------------
void process_awareness_events ( void )
{
int i ;
for ( i = 0 ; i < = Highest_segment_index ; i + + )
New_awareness [ i ] = 0 ;
for ( i = 0 ; i < Num_awareness_events ; i + + )
pae_aux ( Awareness_events [ i ] . segnum , Awareness_events [ i ] . type , 1 ) ;
Num_awareness_events = 0 ;
}
// ----------------------------------------------------------------------------------
void set_player_awareness_all ( void )
{
int i ;
process_awareness_events ( ) ;
for ( i = 0 ; i < = Highest_object_index ; i + + )
if ( Objects [ i ] . control_type = = CT_AI )
if ( New_awareness [ Objects [ i ] . segnum ] > Ai_local_info [ i ] . player_awareness_type ) {
Ai_local_info [ i ] . player_awareness_type = New_awareness [ Objects [ i ] . segnum ] ;
Ai_local_info [ i ] . player_awareness_time = PLAYER_AWARENESS_INITIAL_TIME ;
}
}
# ifndef NDEBUG
int Ai_dump_enable = 0 ;
FILE * Ai_dump_file = NULL ;
char Ai_error_message [ 128 ] = " " ;
// ----------------------------------------------------------------------------------
void dump_ai_objects_all ( )
{
# if PARALLAX
int objnum ;
int total = 0 ;
time_t time_of_day ;
time_of_day = time ( NULL ) ;
if ( ! Ai_dump_enable )
return ;
if ( Ai_dump_file = = NULL )
Ai_dump_file = fopen ( " ai.out " , " a+t " ) ;
fprintf ( Ai_dump_file , " \n num: seg distance __mode__ behav. [velx vely velz] (Frame = %i) \n " , FrameCount ) ;
fprintf ( Ai_dump_file , " Date & Time = %s \n " , ctime ( & time_of_day ) ) ;
if ( Ai_error_message [ 0 ] )
fprintf ( Ai_dump_file , " Error message: %s \n " , Ai_error_message ) ;
for ( objnum = 0 ; objnum < = Highest_object_index ; objnum + + ) {
object * objp = & Objects [ objnum ] ;
ai_static * aip = & objp - > ctype . ai_info ;
ai_local * ailp = & Ai_local_info [ objnum ] ;
fix dist_to_player ;
dist_to_player = vm_vec_dist ( & objp - > pos , & ConsoleObject - > pos ) ;
if ( objp - > control_type = = CT_AI ) {
fprintf ( Ai_dump_file , " %3i: %3i %8.3f %8s %8s [%3i %4i] \n " ,
objnum , objp - > segnum , f2fl ( dist_to_player ) , mode_text [ ailp - > mode ] , behavior_text [ aip - > behavior - 0x80 ] , aip - > hide_index , aip - > path_length ) ;
if ( aip - > path_length )
total + = aip - > path_length ;
}
}
fprintf ( Ai_dump_file , " Total path length = %4i \n " , total ) ;
# endif
}
// ----------------------------------------------------------------------------------
void force_dump_ai_objects_all ( char * msg )
{
int tsave ;
tsave = Ai_dump_enable ;
Ai_dump_enable = 1 ;
sprintf ( Ai_error_message , " %s \n " , msg ) ;
dump_ai_objects_all ( ) ;
Ai_error_message [ 0 ] = 0 ;
Ai_dump_enable = tsave ;
}
// ----------------------------------------------------------------------------------
void turn_off_ai_dump ( void )
{
if ( Ai_dump_file ! = NULL )
fclose ( Ai_dump_file ) ;
Ai_dump_file = NULL ;
}
# endif
// ----------------------------------------------------------------------------------
// Do things which need to get done for all AI objects each frame.
// This includes:
// Setting player_awareness (a fix, time in seconds which object is aware of player)
void do_ai_frame_all ( void )
{
# ifndef NDEBUG
dump_ai_objects_all ( ) ;
# endif
set_player_awareness_all ( ) ;
// if ((FrameCount & 0x07) == 0)
// mprintf((0, "[%i] ", Overall_agitation));
}
//--unused-- // ----------------------------------------------------------------------------------
//--unused-- // Reset various state information for a new life.
//--unused-- // Probably not going to be called for a new game because for that an entire object
//--unused-- // gets reloaded.
//--unused-- void reset_ai_states(object *objp)
//--unused-- {
//--unused-- int i;
//--unused-- //ai_static *aip = &objp->ctype.ai_info;
//--unused-- ai_local *ailp = &Ai_local_info[objp-Objects];
//--unused--
//--unused-- ailp->wait_time = 0;
//--unused-- ailp->next_fire = 0;
//--unused-- ailp->player_awareness_type = 0;
//--unused-- for (i=0; i<MAX_SUBMODELS; i++) {
//--unused-- ailp->goal_angles[i].p = 0; ailp->goal_angles[i].b = 0; ailp->goal_angles[i].h = 0;
//--unused-- ailp->delta_angles[i].p = 0; ailp->delta_angles[i].b = 0; ailp->delta_angles[i].h = 0;
//--unused-- ailp->goal_state[i] = 0;
//--unused-- ailp->achieved_state[i] = 0;
//--unused-- }
//--unused-- }
// Initializations to be performed for all robots for a new level.
void init_robots_for_level ( void )
{
Overall_agitation = 0 ;
}
2008-01-23 17:25:09 +00:00
// int ai_save_state( FILE * fp )
// {
// char buffer[1024]; // That should be enough for us...
// char *ptr=buffer;
// int i;
//
// d_export_int(buffer, Ai_initialized);
// d_export_int(buffer+SIZEOF_INT, Overall_agitation);
// fwrite(buffer, SIZEOF_INT, 2, fp);
//
// for (i=0; i<MAX_OBJECTS; i++)
// {
// d_export_ai_local(buffer, &Ai_local_info[i]);
// fwrite(buffer, SIZEOF_AI_LOCAL, 1, fp);
// }
// for (i=0; i<MAX_POINT_SEGS; i++)
// {
// d_export_point_seg(buffer, &Point_segs[i]);
// fwrite(buffer, SIZEOF_POINT_SEG, 1, fp);
// }
// for (i=0; i<MAX_AI_CLOAK_INFO; i++)
// {
// d_export_fix(buffer,Ai_cloak_info[i].last_time);
// d_export_vms_vector(buffer + SIZEOF_FIX, &Ai_cloak_info[i].last_position);
// fwrite(buffer, SIZEOF_FIX + SIZEOF_VMS_VECTOR, 1, fp);
// }
// d_export_fix(ptr, Boss_cloak_start_time); ptr+=SIZEOF_FIX;
// d_export_fix(ptr, Boss_cloak_end_time); ptr+=SIZEOF_FIX;
// d_export_fix(ptr, Last_teleport_time); ptr+=SIZEOF_FIX;
// d_export_fix(ptr, Boss_teleport_interval); ptr+=SIZEOF_FIX;
// d_export_fix(ptr, Boss_cloak_interval); ptr+=SIZEOF_FIX;
// d_export_fix(ptr, Boss_cloak_duration); ptr+=SIZEOF_FIX;
// d_export_fix(ptr, Last_gate_time); ptr+=SIZEOF_FIX;
// d_export_fix(ptr, Gate_interval); ptr+=SIZEOF_FIX;
// d_export_fix(ptr, Boss_dying_start_time); ptr+=SIZEOF_FIX;
// d_export_int(ptr, Boss_dying); ptr+=SIZEOF_INT;
// d_export_int(ptr, Boss_dying_sound_playing); ptr+=SIZEOF_INT;
// d_export_int(ptr, Boss_hit_this_frame); ptr+=SIZEOF_INT;
// d_export_int(ptr, Boss_been_hit);
// fwrite(buffer, (SIZEOF_FIX * 9) + (SIZEOF_INT * 4), 1, fp);
//
// return 1;
// }
int ai_save_state ( PHYSFS_file * fp )
{
PHYSFS_write ( fp , & Ai_initialized , sizeof ( int ) , 1 ) ;
PHYSFS_write ( fp , & Overall_agitation , sizeof ( int ) , 1 ) ;
PHYSFS_write ( fp , Ai_local_info , sizeof ( ai_local ) * MAX_OBJECTS , 1 ) ;
PHYSFS_write ( fp , Point_segs , sizeof ( point_seg ) * MAX_POINT_SEGS , 1 ) ;
PHYSFS_write ( fp , Ai_cloak_info , sizeof ( ai_cloak_info ) * MAX_AI_CLOAK_INFO , 1 ) ;
PHYSFS_write ( fp , & Boss_cloak_start_time , sizeof ( fix ) , 1 ) ;
PHYSFS_write ( fp , & Boss_cloak_end_time , sizeof ( fix ) , 1 ) ;
PHYSFS_write ( fp , & Last_teleport_time , sizeof ( fix ) , 1 ) ;
PHYSFS_write ( fp , & Boss_teleport_interval , sizeof ( fix ) , 1 ) ;
PHYSFS_write ( fp , & Boss_cloak_interval , sizeof ( fix ) , 1 ) ;
PHYSFS_write ( fp , & Boss_cloak_duration , sizeof ( fix ) , 1 ) ;
PHYSFS_write ( fp , & Last_gate_time , sizeof ( fix ) , 1 ) ;
PHYSFS_write ( fp , & Gate_interval , sizeof ( fix ) , 1 ) ;
PHYSFS_write ( fp , & Boss_dying_start_time , sizeof ( fix ) , 1 ) ;
PHYSFS_write ( fp , & Boss_dying , sizeof ( int ) , 1 ) ;
PHYSFS_write ( fp , & Boss_dying_sound_playing , sizeof ( int ) , 1 ) ;
PHYSFS_write ( fp , & Boss_hit_this_frame , sizeof ( int ) , 1 ) ;
PHYSFS_write ( fp , & Boss_been_hit , sizeof ( int ) , 1 ) ;
return 1 ;
}
// int ai_restore_state( FILE * fp )
// {
// // Enough storage space to last us.
// char buffer[1024];
// char *ptr=buffer;
// int i;
//
// fread(buffer,SIZEOF_INT,2,fp);
// Ai_initialized = d_import_int(buffer);
// Overall_agitation = d_import_int(buffer+SIZEOF_INT);
//
// for (i=0; i<MAX_OBJECTS; i++)
// {
// fread(buffer, SIZEOF_AI_LOCAL, 1, fp);
// d_import_ai_local(&Ai_local_info[i], buffer);
// }
//
// for (i=0; i<MAX_POINT_SEGS; i++)
// {
// fread(buffer, SIZEOF_POINT_SEG, 1, fp);
// d_import_point_seg(&Point_segs[i], buffer);
// }
// for (i=0; i<MAX_AI_CLOAK_INFO; i++)
// {
// // This struct is local to ai.c, so we handle it internally here.
// // It is only ever read here, and only ever stored above.
// fread(buffer, SIZEOF_FIX + SIZEOF_VMS_VECTOR, 1, fp);
// Ai_cloak_info[i].last_time = d_import_fix(buffer);
// d_import_vms_vector(&Ai_cloak_info[i].last_position, buffer + SIZEOF_FIX);
// }
//
// fread(buffer,SIZEOF_FIX,9,fp);
// Boss_cloak_start_time = d_import_fix(ptr); ptr += SIZEOF_FIX;
// Boss_cloak_end_time = d_import_fix(ptr); ptr += SIZEOF_FIX;
// Last_teleport_time = d_import_fix(ptr); ptr += SIZEOF_FIX;
// Boss_teleport_interval = d_import_fix(ptr); ptr += SIZEOF_FIX;
// Boss_cloak_interval = d_import_fix(ptr); ptr += SIZEOF_FIX;
// Boss_cloak_duration = d_import_fix(ptr); ptr += SIZEOF_FIX;
// Last_gate_time = d_import_fix(ptr); ptr += SIZEOF_FIX;
// Gate_interval = d_import_fix(ptr); ptr += SIZEOF_FIX;
// Boss_dying_start_time = d_import_fix(ptr); ptr = buffer;
//
// fread(buffer,SIZEOF_INT,4,fp);
// Boss_dying = d_import_int(ptr); ptr+=SIZEOF_INT;
// Boss_dying_sound_playing = d_import_int(ptr); ptr+=SIZEOF_INT;
// Boss_hit_this_frame = d_import_int(ptr); ptr+=SIZEOF_INT;
// Boss_been_hit = d_import_int(ptr);
//
// return 1;
// }
int ai_restore_state ( PHYSFS_file * fp , int version )
{
PHYSFS_read ( fp , & Ai_initialized , sizeof ( int ) , 1 ) ;
PHYSFS_read ( fp , & Overall_agitation , sizeof ( int ) , 1 ) ;
PHYSFS_read ( fp , Ai_local_info , sizeof ( ai_local ) * MAX_OBJECTS , 1 ) ;
PHYSFS_read ( fp , Point_segs , sizeof ( point_seg ) * MAX_POINT_SEGS , 1 ) ;
PHYSFS_read ( fp , Ai_cloak_info , sizeof ( ai_cloak_info ) * MAX_AI_CLOAK_INFO , 1 ) ;
PHYSFS_read ( fp , & Boss_cloak_start_time , sizeof ( fix ) , 1 ) ;
PHYSFS_read ( fp , & Boss_cloak_end_time , sizeof ( fix ) , 1 ) ;
PHYSFS_read ( fp , & Last_teleport_time , sizeof ( fix ) , 1 ) ;
PHYSFS_read ( fp , & Boss_teleport_interval , sizeof ( fix ) , 1 ) ;
PHYSFS_read ( fp , & Boss_cloak_interval , sizeof ( fix ) , 1 ) ;
PHYSFS_read ( fp , & Boss_cloak_duration , sizeof ( fix ) , 1 ) ;
PHYSFS_read ( fp , & Last_gate_time , sizeof ( fix ) , 1 ) ;
PHYSFS_read ( fp , & Gate_interval , sizeof ( fix ) , 1 ) ;
PHYSFS_read ( fp , & Boss_dying_start_time , sizeof ( fix ) , 1 ) ;
PHYSFS_read ( fp , & Boss_dying , sizeof ( int ) , 1 ) ;
PHYSFS_read ( fp , & Boss_dying_sound_playing , sizeof ( int ) , 1 ) ;
PHYSFS_read ( fp , & Boss_hit_this_frame , sizeof ( int ) , 1 ) ;
PHYSFS_read ( fp , & Boss_been_hit , sizeof ( int ) , 1 ) ;
return 1 ;
2006-03-20 16:43:15 +00:00
}
// -- void show_path_and_other(object *objp )
// -- {
// -- int i;
// -- ai_static *aip = &objp->ctype.ai_info;
// -- for (i=0; i<aip->path_length; i++)
// -- mprintf((0, "%2i ", Point_segs[aip->hide_index+i].segnum));
// -- mprintf((0, "[pl: %i cur: st: %i]\n", ConsoleObject->segnum, objp->segnum, aip->hide_segment));
// -- }