/*********************************** Sandstorm AI.C ************************************/ /**** INCLUDES ****/ #include #include #include "main.h" #include "graphics.h" #include "pad.h" #include "control.h" #include "debug.h" #include "sprite.h" #include "collide.h" #include "fire.h" #include "ai.h" #include "cdread.h" #include "sound.h" /**** GLOBALS ****/ #define CHANGE_TIME (100) //amount of time per state under normal conditions #define CPU_SPEED (5) //speed of CPU while moving #define AREA_BUFF (100) //buffer space around car objects #define Z_OFFSET (100) //buffer space around car objects in z direction #define X_OFFSET (100) /**** DEFINES ****/ #define HOMING_TIME (200) #define FIRE_H_TIME (100) #define HOMING_H_SPEED (10) #define FIRE_H_SPEED (15) #define CPU_FIRE_GAP (150) //gap between firing for CPU int state1, state2; int x_dir, z_dir, x_dir2, z_dir2; //values for state variables: 1-attack P1; 2-get item; 3-evade; 4-get item3; 5-evade; // 6-attack; 7-get item6; 8-get item2; /**** SETSTATE ****/ void SetState( int *state ) { static int state_count; static int picked; //decided state state_count++; if(state_count>CHANGE_TIME) { picked=(rand()%9); if(picked>0 && picked<9) { state_count=0; *state=picked; //assign pointed-to varible a state } } } //end SETSTATE /**** CPU1REACTION ****/ /**NOTE: There are only 3 types of actions for CPU players. Attack, Evade, or get item. **/ void CPU1Reaction( int *state, int *state2 ) { TestDir( &truck[1].coord, &truck[1].vector, state, 1, truck[0].coord.coord.t[0], truck[0].coord.coord.t[2], &x_dir, &z_dir ); TestDir( &truck[1].coord, &truck[1].vector, state, 2, upg_ico_pos.vx, upg_ico_pos.vz, &x_dir, &z_dir ); TestDir( &truck[1].coord, &truck[1].vector, state, 3, tbo_ico_pos.vx, tbo_ico_pos.vz, &x_dir, &z_dir ); TestDir( &truck[1].coord, &truck[1].vector, state, 4, truck[0].coord.coord.t[0], truck[0].coord.coord.t[2], &x_dir, &z_dir ); TestDir( &truck[1].coord, &truck[1].vector, state, 5, truck[0].coord.coord.t[0], truck[0].coord.coord.t[2], &x_dir, &z_dir ); TestDir( &truck[1].coord, &truck[1].vector, state, 6, fire_ico_pos.vx, fire_ico_pos.vz, &x_dir, &z_dir ); TestDir( &truck[1].coord, &truck[1].vector, state, 7, truck[0].coord.coord.t[0], truck[0].coord.coord.t[2], &x_dir, &z_dir ); TestDir( &truck[1].coord, &truck[1].vector, state, 8, pow_ico_pos.vx, pow_ico_pos.vz, &x_dir, &z_dir ); TestDir( &truck[2].coord, &truck[2].vector, state2, 8, truck[0].coord.coord.t[0], truck[0].coord.coord.t[2], &x_dir2, &z_dir2 ); TestDir( &truck[2].coord, &truck[2].vector, state2, 7, rico_ico_pos.vx, rico_ico_pos.vz, &x_dir2, &z_dir2 ); TestDir( &truck[2].coord, &truck[2].vector, state2, 6, truck[0].coord.coord.t[0], truck[0].coord.coord.t[2], &x_dir2, &z_dir2 ); TestDir( &truck[2].coord, &truck[2].vector, state2, 5, upg_ico_pos.vx, upg_ico_pos.vz, &x_dir2, &z_dir2 ); TestDir( &truck[2].coord, &truck[2].vector, state2, 4, hlt_ico_pos.vx, hlt_ico_pos.vz, &x_dir2, &z_dir2 ); TestDir( &truck[2].coord, &truck[2].vector, state2, 3, truck[0].coord.coord.t[0], truck[0].coord.coord.t[2], &x_dir2, &z_dir2 ); TestDir( &truck[2].coord, &truck[2].vector, state2, 2, truck[0].coord.coord.t[0], truck[0].coord.coord.t[2], &x_dir2, &z_dir2 ); TestDir( &truck[2].coord, &truck[2].vector, state2, 1, pow_ico_pos.vx, pow_ico_pos.vz, &x_dir2, &z_dir2 ); /** MOVE CPU 1 ACCORDING TO Z_DIR AND X_DIR **/ switch(x_dir) { case 1: { truck[1].coord.coord.t[0]+=CPU_SPEED; break; } case -1: { truck[1].coord.coord.t[0]-=CPU_SPEED; break; } } switch(z_dir) { case 1: { truck[1].coord.coord.t[2]+=CPU_SPEED; break; } case -1: { truck[1].coord.coord.t[2]-=CPU_SPEED; break; } } /**** MOVE CPU 2 ACCORDING TO x_dir2, z_dir2 ****/ switch(x_dir2) { case 1: { truck[2].coord.coord.t[0]+=CPU_SPEED; break; } case -1: { truck[2].coord.coord.t[0]-=CPU_SPEED; break; } } switch(z_dir2) { case 1: { truck[2].coord.coord.t[2]+=CPU_SPEED; break; } case -1: { truck[2].coord.coord.t[2]-=CPU_SPEED; break; } } } //end CPU1REACTION /**** RANDOM ****/ //adapted from Judge of Ultimate (SCEI game) int Random( int max, int min ) { return (rand()%(max-min))+min; } /**** SMOOTHROT ****/ void SmoothRot( int *id, int *counter, SVECTOR *vector, int rot ) { *id=1; if(*id>0) { *counter++; if(*counter>10) { *counter=0; *id=0; vector->vy=rot; } } } //end SMOOTHROT /**** TESTDIR ****/ //simple pathfinding routine void TestDir( GsCOORDINATE2 *coord, SVECTOR *vec, int *curr_state, int value, int x, int z, int *dirx, int *dirz ) { if(*curr_state==value ) { if(coord->coord.t[0]<(x-AREA_BUFF) ) { *dirx=1; if(coord->coord.t[2]>(z+AREA_BUFF) ) vec->vy=512; else if(coord->coord.t[2]<(z-AREA_BUFF) ) vec->vy=3584; else vec->vy=0; } else if(coord->coord.t[0]>(x+AREA_BUFF) ) { *dirx=-1; if(coord->coord.t[2]>(z+AREA_BUFF) ) vec->vy=1536; else if(coord->coord.t[2]<(z-AREA_BUFF)) vec->vy=2560; else vec->vy=2048; } else *dirx=0; if(coord->coord.t[2]<(z-AREA_BUFF) ) { *dirz=1; vec->vy=3072; } else if(coord->coord.t[2]>(z+AREA_BUFF) ) { *dirz=-1; vec->vy=1024; } else *dirz=0; } //end if(*state==1) } //end TESTDIR /**** CPUATTACK ****/ // int attack1-attack4 specify which states trigger an attack. values greater than 8 are always false void CPUAttack( GsCOORDINATE2 *attacker, GsCOORDINATE2 *target, int *flag_p, int *flag_f, int *flag_h, int *state, int attack1, int attack2, int attack3, int attack4 ) { static int limit; static int random; if(limit>0) limit++; if(limit>200) limit=0; //attack only if in the proper state if(*state==attack1 || *state==attack2 || *state==attack3 || *state==attack4) { if(attacker->coord.t[0]>(target->coord.t[0]-700)) { if(attacker->coord.t[0]<(target->coord.t[0]+700)) { if(attacker->coord.t[2]>(target->coord.t[2]-700)) { if(attacker->coord.t[2]<(target->coord.t[2]+700)) { if( limit==0 || limit==1 ) { random=Random( 4, 1 ); switch(random){ case 1: { *flag_p=1; break; } case 2: { *flag_f=1; break; } case 3: { *flag_h=1; break; } } //end switch-case limit=1; } } } } } } //end if(*state==...) } //end CPUATTACK /**** FIRECPUMISSILE ****/ void FireCPUMissile( void ) { static int counter; static int closest; //calculating which CPU opponent is closest static int homing_c; //counter for homing missile as it follows opponent static int p1_d, cpu_d; //distance from CPU enemies static int homing_count, fire_h_count; if( fired1_f==1 ) { fire2_m_status=1; fire2_m_dir=rot; fire2_h_status=1; //decide which enemy is closest to home missile to p1_d=( (abs(truck[1].coord.coord.t[0]-truck[0].coord.coord.t[0])) + (abs(truck[1].coord.coord.t[2]-truck[0].coord.coord.t[2])) ); cpu_d=( (abs(truck[1].coord.coord.t[0]-truck[2].coord.coord.t[0])) + (abs(truck[1].coord.coord.t[2]-truck[2].coord.coord.t[2])) ); if(p1_dFIRE_H_TIME) { fire2_h_count=0; fire2_h_status=0; fire2_m_status=0; } if(fire2_h_status==1) { //limit how long fire missile homes if(closest==1) { if(fire2_m.coord.coord.t[0]truck[0].coord.coord.t[0]) fire2_m.coord.coord.t[0]-=FIRE_H_SPEED; if(fire2_m.coord.coord.t[2]>truck[0].coord.coord.t[2]) fire2_m.coord.coord.t[2]-=FIRE_H_SPEED; if(fire2_m.coord.coord.t[2]truck[2].coord.coord.t[0]) fire2_m.coord.coord.t[0]-=FIRE_H_SPEED; if(fire2_m.coord.coord.t[2]>truck[2].coord.coord.t[2]) fire2_m.coord.coord.t[2]-=FIRE_H_SPEED; if(fire2_m.coord.coord.t[2]3250 || fire2_m.coord.coord.t[0]<-3250 || fire2_m.coord.coord.t[2]>2935 || fire2_m.coord.coord.t[2]<-3200) { fire2_m_status=0; fire2_m.obj.attribute &=~(1<<31); fire2_m.obj.attribute|=(0<<31); } } if(homing2_m_status==1) { if(homing2_status==1) homing2_count++; if(homing2_count>HOMING_TIME) { homing2_count=0; homing2_status=0; homing2_m_status=0; } if(homing2_status==1) { //limit how long the missile homes if(closest==1) { if(homing2_m.coord.coord.t[0]truck[0].coord.coord.t[0]) homing2_m.coord.coord.t[0]-=HOMING_H_SPEED; if(homing2_m.coord.coord.t[2]>truck[0].coord.coord.t[2]) homing2_m.coord.coord.t[2]-=HOMING_H_SPEED; if(homing2_m.coord.coord.t[2]truck[2].coord.coord.t[0]) homing2_m.coord.coord.t[0]-=HOMING_H_SPEED; if(homing2_m.coord.coord.t[2]>truck[2].coord.coord.t[2]) homing2_m.coord.coord.t[2]-=HOMING_H_SPEED; if(homing2_m.coord.coord.t[2]3250 || homing2_m.coord.coord.t[0]<-3250 || homing2_m.coord.coord.t[2]>2935 || homing2_m.coord.coord.t[2]<-3200) { homing2_m_status=0; homing2_m.obj.attribute &=~(1<<31); homing2_m.obj.attribute|=(0<<31); } } if(power2_m_status==1) { power2_m.coord.coord.t[0] +=((MISSILE_SPEED*SIN[power2_m_dir>>12])>>12); power2_m.coord.coord.t[2] +=((MISSILE_SPEED*COS[power2_m_dir>>12])>>12); if(power2_m.coord.coord.t[0]>3250 || power2_m.coord.coord.t[0]<-3250 || power2_m.coord.coord.t[2]>2935 || power2_m.coord.coord.t[2]<-3200) { power2_m_status=0; power2_m.obj.attribute &=~(1<<31); power2_m.obj.attribute|=(0<<31); } } if(fire2_m_status==0) { fire2_h_status=0; fire2_h_count=0; } if(homing2_m_status==0) { homing2_count=0; homing2_status=0; } } //end FIREMISSILE /**** FIRECPUMISSILE2 ****/ void FireCPUMissile2( void ) { static int counter; static int closest; //calculating which CPU opponent is closest static int homing_c; //counter for homing missile as it follows opponent static int p1_d, cpu_d; //distance from CPU enemies static int fire_h_count; if( fired2_f==1 ) { fire3_m_status=1; fire3_m_dir=rot; fire3_h_status=1; //decide which enemy is closest to home missile to p1_d=( (abs(truck[2].coord.coord.t[0]-truck[0].coord.coord.t[0])) + (abs(truck[2].coord.coord.t[2]-truck[0].coord.coord.t[2])) ); cpu_d=( (abs(truck[2].coord.coord.t[0]-truck[1].coord.coord.t[0])) + (abs(truck[2].coord.coord.t[2]-truck[1].coord.coord.t[2])) ); if(p1_dFIRE_H_TIME) { fire3_h_count=0; fire3_h_status=0; fire3_m_status=0; } if(fire3_h_status==1) { //limit how long fire missile homes if(closest==1) { if(fire3_m.coord.coord.t[0]truck[0].coord.coord.t[0]) fire3_m.coord.coord.t[0]-=FIRE_H_SPEED; if(fire3_m.coord.coord.t[2]>truck[0].coord.coord.t[2]) fire2_m.coord.coord.t[2]-=FIRE_H_SPEED; if(fire3_m.coord.coord.t[2]truck[1].coord.coord.t[0]) fire3_m.coord.coord.t[0]-=FIRE_H_SPEED; if(fire3_m.coord.coord.t[2]>truck[1].coord.coord.t[2]) fire3_m.coord.coord.t[2]-=FIRE_H_SPEED; if(fire3_m.coord.coord.t[2]3250 || fire3_m.coord.coord.t[0]<-3250 || fire3_m.coord.coord.t[2]>2935 || fire3_m.coord.coord.t[2]<-3200) { fire3_m_status=0; fire3_m.obj.attribute &=~(1<<31); fire3_m.obj.attribute|=(0<<31); } } if(homing3_m_status==1) { if(homing3_status==1) homing3_count++; if(homing3_count>HOMING_TIME) { homing3_count=0; homing3_status=0; homing3_m_status=0; } if(homing3_status==1) { //limit how long the missile homes if(closest==1) { if(homing3_m.coord.coord.t[0]truck[0].coord.coord.t[0]) homing3_m.coord.coord.t[0]-=HOMING_H_SPEED; if(homing3_m.coord.coord.t[2]>truck[0].coord.coord.t[2]) homing3_m.coord.coord.t[2]-=HOMING_H_SPEED; if(homing3_m.coord.coord.t[2]truck[1].coord.coord.t[0]) homing3_m.coord.coord.t[0]-=HOMING_H_SPEED; if(homing3_m.coord.coord.t[2]>truck[1].coord.coord.t[2]) homing3_m.coord.coord.t[2]-=HOMING_H_SPEED; if(homing3_m.coord.coord.t[2]3250 || homing3_m.coord.coord.t[0]<-3250 || homing3_m.coord.coord.t[2]>2935 || homing3_m.coord.coord.t[2]<-3200) { homing3_m_status=0; homing3_m.obj.attribute &=~(1<<31); homing3_m.obj.attribute|=(0<<31); } } if(power3_m_status==1) { power3_m.coord.coord.t[0] +=((MISSILE_SPEED*SIN[power3_m_dir>>12])>>12); power3_m.coord.coord.t[2] +=((MISSILE_SPEED*COS[power3_m_dir>>12])>>12); if(power3_m.coord.coord.t[0]>3250 || power3_m.coord.coord.t[0]<-3250 || power3_m.coord.coord.t[2]>2935 || power3_m.coord.coord.t[2]<-3200) { power3_m_status=0; power3_m.obj.attribute &=~(1<<31); power3_m.obj.attribute|=(0<<31); } } if(fire3_m_status==0) { fire3_h_status=0; fire3_h_count=0; } if(homing3_m_status==0) { homing3_count=0; homing3_status=0; } } //end FIREMISSILE2