/*============================================================================= Name : AIPlayer.c Purpose : All Computer Player (AIPlayer) interfaces with game happen here Created 5/31/1998 by gshaw Copyright Relic Entertainment, Inc. All rights reserved. =============================================================================*/ #include #include #include "types.h" #include "Objtypes.h" #include "AIPlayer.h" #include "file.h" #include "universe.h" #include "select.h" #include "AIFleetMan.h" #include "AIAttackMan.h" #include "AIDefenseMan.h" #include "AIUtilities.h" #include "AITeam.h" #include "stats.h" #include "randy.h" #include "nis.h" #include "SaveGame.h" #include "SinglePlayer.h" #define AIPLAYER_LOG_FILE_NAME "aiplayerlog.txt" bool aiplayerLogEnable = FALSE; AIPlayer *aiCurrentAIPlayer; uword aiIndex; udword AIPLAYER_UPDATE_RATE[AI_NUM_LEVELS] = { 63, 31, 15 }; real32 ATTMAN_BUILDPRIORITY_RATIO = 0.3f; real32 DEFMAN_BUILDPRIORITY_RATIO = 0.3f; real32 RESMAN_BUILDPRIORITY_RATIO = 0.4f; // these 3 numbers must add to 1.0 real32 AIPLAYER_OVERKILL_FACTOR = 1.4f; udword AIPLAYER_BIGOTRY[MAX_MULTIPLAYER_PLAYERS] = {0,0,0,0,0,0,0,0}; ubyte AIF_RESEARCH_DELAY[AI_NUM_LEVELS] = { 2, 1, 0}; //attackman tweak variables //out of 255 udword AIA_ATTACK_FLEET_FAST_PROB[AI_NUM_LEVELS] = { 130, 130, 130}; udword AIA_ATTACK_FLEET_FAST_RANGE[AI_NUM_LEVELS] = { 50, 50, 50}; udword AIA_ATTACK_FLEET_GUARD_PROB[AI_NUM_LEVELS] = { 120, 120, 120}; udword AIA_ATTACK_FLEET_GUARD_RANGE[AI_NUM_LEVELS] = { 50, 50, 50}; udword AIA_ATTACK_FLEET_BIG_PROB[AI_NUM_LEVELS] = { 100, 100, 100}; udword AIA_ATTACK_FLEET_BIG_RANGE[AI_NUM_LEVELS] = { 50, 50, 50}; udword AIA_ATTACK_FLEET_HUGE_PROB[AI_NUM_LEVELS] = { 0, 0, 70}; udword AIA_ATTACK_FLEET_HUGE_RANGE[AI_NUM_LEVELS] = { 0, 0, 40}; udword AIA_TAKEOUT_TARGET_PROB[AI_NUM_LEVELS] = { 100, 100, 100}; udword AIA_TAKEOUT_TARGET_RANGE[AI_NUM_LEVELS] = { 50, 50, 50}; udword AIA_FANCY_TAKEOUT_TARGET_PROB[AI_NUM_LEVELS] = { 50, 50, 50}; udword AIA_FANCY_TAKEOUT_TARGET_RANGE[AI_NUM_LEVELS] = { 40, 40, 40}; udword AIA_FIGHTER_STRIKE_PROB[AI_NUM_LEVELS] = { 15, 15, 15}; udword AIA_FIGHTER_STRIKE_RANGE[AI_NUM_LEVELS] = { 15, 15, 15}; udword AIA_CORVETTE_STRIKE_PROB[AI_NUM_LEVELS] = { 100, 100, 100}; udword AIA_CORVETTE_STRIKE_RANGE[AI_NUM_LEVELS] = { 60, 60, 60}; udword AIA_FRIGATE_STRIKE_PROB[AI_NUM_LEVELS] = { 115, 115, 115}; udword AIA_FRIGATE_STRIKE_RANGE[AI_NUM_LEVELS] = { 70, 70, 70}; udword AIA_HARASS_BIG_PROB[AI_NUM_LEVELS] = { 60, 60, 60}; udword AIA_HARASS_BIG_RANGE[AI_NUM_LEVELS] = { 30, 30, 30}; udword AIA_HARASS_SMALL_PROB[AI_NUM_LEVELS] = { 0, 0, 0}; udword AIA_HARASS_SMALL_RANGE[AI_NUM_LEVELS] = { 0, 0, 0}; udword AIA_CAPTURE_PROB[AI_NUM_LEVELS] = { 80, 80, 80}; udword AIA_CAPTURE_RANGE[AI_NUM_LEVELS] = { 40, 40, 40}; udword AIA_MINE_PROB[AI_NUM_LEVELS] = { 0, 0, 55}; udword AIA_MINE_RANGE[AI_NUM_LEVELS] = { 0, 0, 45}; udword AIA_CLOAKGEN_PROB[AI_NUM_LEVELS] = { 0, 0, 55}; udword AIA_CLOAKGEN_RANGE[AI_NUM_LEVELS] = { 0, 0, 35}; udword AIA_GRAVWELL_PROB[AI_NUM_LEVELS] = { 0, 0, 55}; udword AIA_GRAVWELL_RANGE[AI_NUM_LEVELS] = { 0, 0, 35}; //out of 100 udword AIA_KAMIKAZE_PROB[AI_NUM_LEVELS] = { 0, 0, 25}; udword AIA_KAMIKAZE_RANGE[AI_NUM_LEVELS] = { 0, 0, 20}; ubyte AIA_NUM_ARMADA_TEAMS[AI_NUM_LEVELS] = { 0, 1, 2}; udword AIA_ENEMYBLOBS_PER_RECON = 5; //defenseman tweak variables real32 AID_MOTHERSHIP_DEFENSE_FLEET_STRENGTH = 1.4f; udword AID_RESCON_ASF_DEFENDABLE_MULTIPLIER = 2; udword AID_SPHERE_OF_INFLUENCE_INTERVAL = 15; udword AID_ROVING_DEFENSE_LOW_COUNT_CUTOFF = 3; udword AID_ROVING_DEFENSE_LOW_COUNT_DIVISOR = 4; udword AID_ROVING_DEFENSE_HIGH_COUNT_DIVISOR = 2; udword AID_DEFEND_MOTHERSHIP_FUEL_LOW = 5; real32 AID_MOTHERSHIP_ATTACKER_FLEET_RADIUS = 1000.0f; //team tweaks udword AIT_TEAM_MOVE_DELAY[NUM_TEAMLEVEL_TYPES] = {2,1,1,0,0}; TypeOfFormation AIT_DEFAULT_FORMATION = BROAD_FORMATION; real32 AIT_DEFTEAM_NEEDS_HELP_STRENGTH = 1.2f; //order tweaks udword AIO_GUARD_SHIPS_NUM_CAPITAL = 12; udword AIO_GUARD_SHIPS_NUM_NONCAPITAL = 6; udword AIO_GUARD_SHIPS_FUEL_LOW = 15; TypeOfFormation AIO_HARASS_INITIAL_MOVE_FORMATION = DELTA_FORMATION; udword AIO_HARASS_FUEL_LOW = 15; udword AIO_HARASS_NUMBERS_LOW = 40; udword AIO_DEFMOTHERSHIP_FUEL_LOW = 5; udword AIO_PATROL_FUEL_LOW = 15; udword AIO_PATROL_NUMBERS_LOW = 50; udword AIO_FASTROVING_FUEL_LOW = 15; udword AIO_FASTROVING_NUMBERS_LOW = 80; udword AIO_SLOWROVING_FUEL_LOW = 15; udword AIO_SLOWROVING_NUMBERS_LOW = 50; TypeOfFormation AIO_TOUT_MSHIP_FAST_TGRD_FORMATION = BROAD_FORMATION; TacticsType AIO_TOUT_MSHIP_FAST_TGRD_TACTICS = Aggressive; TypeOfFormation AIO_TOUT_MSHIP_BIG_TGRD_FORMATION = BROAD_FORMATION; TacticsType AIO_TOUT_MSHIP_BIG_TGRD_TACTICS = Aggressive; TypeOfFormation AIO_TOUT_MSHIP_GUARD_TGRD_FORMATION= BROAD_FORMATION; TacticsType AIO_TOUT_MSHIP_GUARD_TGRD_TACTICS = Aggressive; TypeOfFormation AIO_TOUT_TARG_WCUR_FORMATION = WALL_FORMATION; TacticsType AIO_TOUT_TARG_WCUR_TACTICS = Aggressive; TypeOfFormation AIO_TOUT_TARG_TGUARD_FORMATION = BROAD_FORMATION; TacticsType AIO_TOUT_TARG_TGUARD_TACTICS = Aggressive; TypeOfFormation AIO_TOUT_TARG_FANCY_TGRD_FORMATION = BROAD_FORMATION; TacticsType AIO_TOUT_TARG_FANCY_TGRD_TACTICS = Aggressive; udword AIO_FIGHTER_KAMIKAZE_HEALTH = 45; udword AIO_CORVETTE_KAMIKAZE_HEALTH = 25; //move and handler tweak variables real32 AIH_GENERIC_GETTINGROCKED_STRENGTH = 1.5f; TypeOfFormation AIH_GENERIC_GETTINGROCKED_FORMATION = BROAD_FORMATION; real32 AIM_DIST_MOVE_END = 1300.0f; udword AIM_SQUADRON_NUM_SHIPS = 5; real32 AIM_INTERCEPT_FINISH_DISTSQ = 4000000.0f; real32 AIM_INTERCEPT_RECALC_DISTSQ = 2000000.0f; //harass move tweaks real32 AIM_MIN_HARASS_ATTACK_DIST_SQ = 4000000.0f; real32 AIM_HARASS_STANDOFF_DIST = 16000.0f; real32 AIM_HARASS_PROTECTION_RADIUS = 2500.0f; TypeOfFormation AIM_HARASS_MOVE_FORMATION = DELTA3D_FORMATION; TypeOfFormation AIM_HARASS_SINGLETARGET_FORMATION = SPHERE_FORMATION; TypeOfFormation AIM_HARASS_MULTITARGET_FORMATION = CLAW_FORMATION; TypeOfFormation AIH_HARASS_SINGLEATTACK_FORMATION = SPHERE_FORMATION; real32 AIH_HARASS_NUMLOW_STANDOFF_DIST = 7500.0f; TypeOfFormation AIH_HARASS_NUMLOW_FORMATION = BROAD_FORMATION; //flank move tweaks real32 AIM_FLANK_ATTACK_RADIUS = 16000.0f; TypeOfFormation AIM_FLANK_MOVE_FORMATION = WALL_FORMATION; TypeOfFormation AIM_FLANK_ATTACK_FORMATION = BROAD_FORMATION; //dock move tweaks udword AIM_DOCK_REPAIRHEALTH_TRIGGER = 80; real32 AIM_DOCK_FINISH_FUEL_THRESHOLD = 0.95f; TypeOfFormation AIM_DOCK_DEFAULT_FORMATION = SPHERE_FORMATION; //defend mothership move tweaks TypeOfFormation AIM_DEF_MOTHERSHIP_ATTACK_FORMATION = WALL_FORMATION; //active patrol move tweaks TypeOfFormation AIM_PATROL_FIRST_FORMATION = DELTA_FORMATION; TypeOfFormation AIM_PATROL_GENERAL_FORMATION = DELTA3D_FORMATION; real32 AIH_PATROL_ENEMYNEARBY_STRENGTH = 1.5f; TypeOfFormation AIH_PATROL_ENEMYNEARBY_FORMATION = BROAD_FORMATION; real32 AIH_FASTDEF_NUMLOW_STANDOFF_DIST = 7500.0f; TypeOfFormation AIH_FASTDEF_NUMLOW_FORMATION = BROAD_FORMATION; real32 AIH_FASTDEF_INVADER_ENEMYDIST = 1500.0f; udword AIH_FASTDEF_INVADER_LOTSFEW_LIMIT = 1; TypeOfFormation AIH_FASTDEF_INVADER_LOTS_FORMATION = CLAW_FORMATION; TypeOfFormation AIH_FASTDEF_INVADER_FEW_FORMATION = SPHERE_FORMATION; real32 AIH_SLOWDEF_NUMLOW_STANDOFF_DIST = 7000.0f; TypeOfFormation AIH_SLOWDEF_NUMLOW_FORMATION = BROAD_FORMATION; real32 AIH_SLOWDEF_RESPONSE_MAXDISTSQ = 100000000.0f; real32 AIH_SLOWDEF_INVADER_ENEMYDIST = 1500.0f; udword AIH_SLOWDEF_INVADER_LOTSFEW_LIMIT = 1; TypeOfFormation AIH_SLOWDEF_INVADER_LOTS_FORMATION = BROAD_FORMATION; TypeOfFormation AIH_SLOWDEF_INVADER_FEW_FORMATION = SPHERE_FORMATION; //temp guard move tweaks real32 AIM_TEMP_GUARD_MOVE_LOCATION_MULT = 0.6666f; TypeOfFormation AIM_TEMP_GUARD_MOVE_FORMATION = BROAD_FORMATION; TacticsType AIM_TEMP_GUARD_MOVE_TACTICS = Aggressive; TypeOfFormation AIM_TEMP_GUARD_INVADER_FORMATION = BROAD_FORMATION; //support move tweaks real32 AIM_SUPPORT_SAFE_DISTANCE = 16000; //utilities tweak variables real32 AIU_STANDOFF_NEG_ERROR_ANGLE = -10.0f; real32 AIU_STANDOFF_POS_ERROR_ANGLE = 10.0f; real32 AIU_STANDOFF_DIST_ERROR_LOW = 0.95f; real32 AIU_STANDOFF_DIST_ERROR_HIGH = 1.05f; udword AIU_RESCUE_MULTIPLE_MINSHIPS = 4; udword AIU_RESCUE_ENEMYMULTIPLE_MINSHIPS = 2; TypeOfFormation AIU_RESCUE_NOMULTIPLE_FORMATION = BROAD_FORMATION; TypeOfFormation AIU_RESCUE_TEAMMULTIPLE_FORMATION = SPHERE_FORMATION; TypeOfFormation AIU_RESCUE_BOTHMULTIPLE_FORMATION = WALL_FORMATION; real32 AIU_TAKEOUT_ENEMYFLEET_RADIUS = 1500.0f; udword AIU_TAKEOUT_MULTIPLE_MINSHIPS = 4; udword AIU_TAKEOUT_ENEMYMULTIPLE_MINSHIPS = 2; TypeOfFormation AIU_TAKEOUT_NOMULTIPLE_FORMATION = BROAD_FORMATION; TypeOfFormation AIU_TAKEOUT_TEAMMULTIPLE_FORMATION = SPHERE_FORMATION; TypeOfFormation AIU_TAKEOUT_BOTHMULTIPLE_FORMATION = WALL_FORMATION; real32 AIU_SAFEST_STANDOFF_DIST_MULT = 2.2f; real32 AIU_FINDUNARMED_PROTECTION_STRENGTH = 2.0f; real32 AIU_FINDATTACKING_ENEMYFLEET_RADIUS = 1000.0f; real32 AIU_HARASS_PROTECTION_RADIUS = 1500.0f; real32 AIU_UNARMED_UNDEFENDED_MODIFIER = 1.0f; real32 AIU_UNARMED_MODIFIER = 1.2f; real32 AIU_FIGHTER_VULNERABLE_MODIFIER = 0.8f; scriptEntry AIPlayerTweaks[] = { makeEntry(AIPLAYER_UPDATE_RATE[AI_BEG], scriptSetUdwordCB), makeEntry(AIPLAYER_UPDATE_RATE[AI_INT], scriptSetUdwordCB), makeEntry(AIPLAYER_UPDATE_RATE[AI_ADV], scriptSetUdwordCB), makeEntry(ATTMAN_BUILDPRIORITY_RATIO, scriptSetReal32CB), makeEntry(DEFMAN_BUILDPRIORITY_RATIO, scriptSetReal32CB), makeEntry(RESMAN_BUILDPRIORITY_RATIO, scriptSetReal32CB), makeEntry(AIPLAYER_OVERKILL_FACTOR, scriptSetReal32CB), makeEntry(AIPLAYER_BIGOTRY[0], scriptSetUdwordCB), makeEntry(AIPLAYER_BIGOTRY[1], scriptSetUdwordCB), makeEntry(AIPLAYER_BIGOTRY[2], scriptSetUdwordCB), makeEntry(AIPLAYER_BIGOTRY[3], scriptSetUdwordCB), makeEntry(AIPLAYER_BIGOTRY[4], scriptSetUdwordCB), makeEntry(AIPLAYER_BIGOTRY[5], scriptSetUdwordCB), makeEntry(AIPLAYER_BIGOTRY[6], scriptSetUdwordCB), makeEntry(AIPLAYER_BIGOTRY[7], scriptSetUdwordCB), makeEntry(AIF_RESEARCH_DELAY[AI_BEG], scriptSetUbyteCB), makeEntry(AIF_RESEARCH_DELAY[AI_INT], scriptSetUbyteCB), makeEntry(AIF_RESEARCH_DELAY[AI_ADV], scriptSetUbyteCB), //attackman tweaks makeEntry(AIA_ATTACK_FLEET_FAST_PROB[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_FAST_RANGE[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_FAST_PROB[AI_INT], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_FAST_RANGE[AI_INT], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_FAST_PROB[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_FAST_RANGE[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_GUARD_PROB[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_GUARD_RANGE[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_GUARD_PROB[AI_INT], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_GUARD_RANGE[AI_INT], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_GUARD_PROB[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_GUARD_RANGE[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_BIG_PROB[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_BIG_RANGE[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_BIG_PROB[AI_INT], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_BIG_RANGE[AI_INT], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_BIG_PROB[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_BIG_RANGE[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_HUGE_PROB[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_HUGE_RANGE[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_HUGE_PROB[AI_INT], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_HUGE_RANGE[AI_INT], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_HUGE_PROB[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_ATTACK_FLEET_HUGE_RANGE[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_TAKEOUT_TARGET_PROB[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_TAKEOUT_TARGET_RANGE[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_TAKEOUT_TARGET_PROB[AI_INT], scriptSetUdwordCB), makeEntry(AIA_TAKEOUT_TARGET_RANGE[AI_INT], scriptSetUdwordCB), makeEntry(AIA_TAKEOUT_TARGET_PROB[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_TAKEOUT_TARGET_RANGE[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_FANCY_TAKEOUT_TARGET_PROB[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_FANCY_TAKEOUT_TARGET_RANGE[AI_BEG],scriptSetUdwordCB), makeEntry(AIA_FANCY_TAKEOUT_TARGET_PROB[AI_INT], scriptSetUdwordCB), makeEntry(AIA_FANCY_TAKEOUT_TARGET_RANGE[AI_INT],scriptSetUdwordCB), makeEntry(AIA_FANCY_TAKEOUT_TARGET_PROB[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_FANCY_TAKEOUT_TARGET_RANGE[AI_ADV],scriptSetUdwordCB), makeEntry(AIA_FIGHTER_STRIKE_PROB[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_FIGHTER_STRIKE_RANGE[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_FIGHTER_STRIKE_PROB[AI_INT], scriptSetUdwordCB), makeEntry(AIA_FIGHTER_STRIKE_RANGE[AI_INT], scriptSetUdwordCB), makeEntry(AIA_FIGHTER_STRIKE_PROB[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_FIGHTER_STRIKE_RANGE[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_CORVETTE_STRIKE_PROB[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_CORVETTE_STRIKE_RANGE[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_CORVETTE_STRIKE_PROB[AI_INT], scriptSetUdwordCB), makeEntry(AIA_CORVETTE_STRIKE_RANGE[AI_INT], scriptSetUdwordCB), makeEntry(AIA_CORVETTE_STRIKE_PROB[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_CORVETTE_STRIKE_RANGE[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_FRIGATE_STRIKE_PROB[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_FRIGATE_STRIKE_RANGE[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_FRIGATE_STRIKE_PROB[AI_INT], scriptSetUdwordCB), makeEntry(AIA_FRIGATE_STRIKE_RANGE[AI_INT], scriptSetUdwordCB), makeEntry(AIA_FRIGATE_STRIKE_PROB[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_FRIGATE_STRIKE_RANGE[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_HARASS_BIG_PROB[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_HARASS_BIG_RANGE[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_HARASS_BIG_PROB[AI_INT], scriptSetUdwordCB), makeEntry(AIA_HARASS_BIG_RANGE[AI_INT], scriptSetUdwordCB), makeEntry(AIA_HARASS_BIG_PROB[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_HARASS_BIG_RANGE[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_HARASS_SMALL_PROB[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_HARASS_SMALL_RANGE[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_HARASS_SMALL_PROB[AI_INT], scriptSetUdwordCB), makeEntry(AIA_HARASS_SMALL_RANGE[AI_INT], scriptSetUdwordCB), makeEntry(AIA_HARASS_SMALL_PROB[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_HARASS_SMALL_RANGE[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_CAPTURE_PROB[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_CAPTURE_RANGE[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_CAPTURE_PROB[AI_INT], scriptSetUdwordCB), makeEntry(AIA_CAPTURE_RANGE[AI_INT], scriptSetUdwordCB), makeEntry(AIA_CAPTURE_PROB[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_CAPTURE_RANGE[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_MINE_PROB[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_MINE_RANGE[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_MINE_PROB[AI_INT], scriptSetUdwordCB), makeEntry(AIA_MINE_RANGE[AI_INT], scriptSetUdwordCB), makeEntry(AIA_MINE_PROB[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_MINE_RANGE[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_CLOAKGEN_PROB[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_CLOAKGEN_RANGE[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_CLOAKGEN_PROB[AI_INT], scriptSetUdwordCB), makeEntry(AIA_CLOAKGEN_RANGE[AI_INT], scriptSetUdwordCB), makeEntry(AIA_CLOAKGEN_PROB[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_CLOAKGEN_RANGE[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_GRAVWELL_PROB[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_GRAVWELL_RANGE[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_GRAVWELL_PROB[AI_INT], scriptSetUdwordCB), makeEntry(AIA_GRAVWELL_RANGE[AI_INT], scriptSetUdwordCB), makeEntry(AIA_GRAVWELL_PROB[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_GRAVWELL_RANGE[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_KAMIKAZE_PROB[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_KAMIKAZE_RANGE[AI_BEG], scriptSetUdwordCB), makeEntry(AIA_KAMIKAZE_PROB[AI_INT], scriptSetUdwordCB), makeEntry(AIA_KAMIKAZE_RANGE[AI_INT], scriptSetUdwordCB), makeEntry(AIA_KAMIKAZE_PROB[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_KAMIKAZE_RANGE[AI_ADV], scriptSetUdwordCB), makeEntry(AIA_NUM_ARMADA_TEAMS[AI_BEG], scriptSetUbyteCB), makeEntry(AIA_NUM_ARMADA_TEAMS[AI_INT], scriptSetUbyteCB), makeEntry(AIA_NUM_ARMADA_TEAMS[AI_ADV], scriptSetUbyteCB), makeEntry(AIA_ENEMYBLOBS_PER_RECON, scriptSetUdwordCB), //defenseman tweaks makeEntry(AID_MOTHERSHIP_DEFENSE_FLEET_STRENGTH,scriptSetReal32CB), makeEntry(AID_RESCON_ASF_DEFENDABLE_MULTIPLIER, scriptSetUdwordCB), makeEntry(AID_SPHERE_OF_INFLUENCE_INTERVAL, scriptSetUdwordCB), makeEntry(AID_ROVING_DEFENSE_LOW_COUNT_CUTOFF, scriptSetUdwordCB), makeEntry(AID_ROVING_DEFENSE_LOW_COUNT_DIVISOR, scriptSetUdwordCB), makeEntry(AID_ROVING_DEFENSE_HIGH_COUNT_DIVISOR,scriptSetUdwordCB), makeEntry(AID_DEFEND_MOTHERSHIP_FUEL_LOW, scriptSetUdwordCB), makeEntry(AID_MOTHERSHIP_ATTACKER_FLEET_RADIUS, scriptSetReal32CB), //team tweaks makeEntry(AIT_TEAM_MOVE_DELAY[TEAM_BEGINNER], scriptSetUdwordCB), makeEntry(AIT_TEAM_MOVE_DELAY[TEAM_BEGINNER_INTERMEDIATE], scriptSetUdwordCB), makeEntry(AIT_TEAM_MOVE_DELAY[TEAM_INTERMEDIATE], scriptSetUdwordCB), makeEntry(AIT_TEAM_MOVE_DELAY[TEAM_INTERMEDIATE_ADVANCED], scriptSetUdwordCB), makeEntry(AIT_TEAM_MOVE_DELAY[TEAM_ADVANCED], scriptSetUdwordCB), makeEntry(AIT_DEFAULT_FORMATION, scriptSetFormationCB), makeEntry(AIT_DEFTEAM_NEEDS_HELP_STRENGTH, scriptSetReal32CB), //order tweaks makeEntry(AIO_GUARD_SHIPS_NUM_CAPITAL, scriptSetUdwordCB), makeEntry(AIO_GUARD_SHIPS_NUM_NONCAPITAL, scriptSetUdwordCB), makeEntry(AIO_GUARD_SHIPS_FUEL_LOW, scriptSetUdwordCB), makeEntry(AIO_HARASS_INITIAL_MOVE_FORMATION, scriptSetFormationCB), makeEntry(AIO_HARASS_FUEL_LOW, scriptSetUdwordCB), makeEntry(AIO_HARASS_NUMBERS_LOW, scriptSetUdwordCB), makeEntry(AIO_DEFMOTHERSHIP_FUEL_LOW, scriptSetUdwordCB), makeEntry(AIO_PATROL_FUEL_LOW, scriptSetUdwordCB), makeEntry(AIO_PATROL_NUMBERS_LOW, scriptSetUdwordCB), makeEntry(AIO_FASTROVING_FUEL_LOW, scriptSetUdwordCB), makeEntry(AIO_FASTROVING_NUMBERS_LOW, scriptSetUdwordCB), makeEntry(AIO_SLOWROVING_FUEL_LOW, scriptSetUdwordCB), makeEntry(AIO_SLOWROVING_NUMBERS_LOW, scriptSetUdwordCB), makeEntry(AIO_TOUT_MSHIP_FAST_TGRD_FORMATION, scriptSetFormationCB), makeEntry(AIO_TOUT_MSHIP_FAST_TGRD_TACTICS, scriptSetTacticsCB), makeEntry(AIO_TOUT_MSHIP_BIG_TGRD_FORMATION, scriptSetFormationCB), makeEntry(AIO_TOUT_MSHIP_BIG_TGRD_TACTICS, scriptSetTacticsCB), makeEntry(AIO_TOUT_MSHIP_GUARD_TGRD_FORMATION,scriptSetFormationCB), makeEntry(AIO_TOUT_MSHIP_GUARD_TGRD_TACTICS, scriptSetTacticsCB), makeEntry(AIO_TOUT_TARG_WCUR_FORMATION, scriptSetFormationCB), makeEntry(AIO_TOUT_TARG_WCUR_TACTICS, scriptSetTacticsCB), makeEntry(AIO_TOUT_TARG_TGUARD_FORMATION, scriptSetFormationCB), makeEntry(AIO_TOUT_TARG_TGUARD_TACTICS, scriptSetTacticsCB), makeEntry(AIO_TOUT_TARG_FANCY_TGRD_FORMATION, scriptSetFormationCB), makeEntry(AIO_TOUT_TARG_FANCY_TGRD_TACTICS, scriptSetTacticsCB), makeEntry(AIO_FIGHTER_KAMIKAZE_HEALTH, scriptSetUdwordCB), makeEntry(AIO_CORVETTE_KAMIKAZE_HEALTH, scriptSetUdwordCB), //move tweaks makeEntry(AIH_GENERIC_GETTINGROCKED_STRENGTH, scriptSetReal32CB), makeEntry(AIH_GENERIC_GETTINGROCKED_FORMATION,scriptSetFormationCB), makeEntry(AIM_DIST_MOVE_END, scriptSetReal32CB), makeEntry(AIM_SQUADRON_NUM_SHIPS, scriptSetUdwordCB), makeEntry(AIM_INTERCEPT_FINISH_DISTSQ, scriptSetReal32CB), makeEntry(AIM_INTERCEPT_RECALC_DISTSQ, scriptSetReal32CB), //harass move tweaks makeEntry(AIM_MIN_HARASS_ATTACK_DIST_SQ, scriptSetReal32CB), makeEntry(AIM_HARASS_STANDOFF_DIST, scriptSetReal32CB), makeEntry(AIM_HARASS_PROTECTION_RADIUS, scriptSetReal32CB), makeEntry(AIM_HARASS_MOVE_FORMATION, scriptSetFormationCB), makeEntry(AIM_HARASS_SINGLETARGET_FORMATION, scriptSetFormationCB), makeEntry(AIM_HARASS_MULTITARGET_FORMATION, scriptSetFormationCB), makeEntry(AIH_HARASS_SINGLEATTACK_FORMATION, scriptSetFormationCB), makeEntry(AIH_HARASS_NUMLOW_STANDOFF_DIST, scriptSetReal32CB), makeEntry(AIH_HARASS_NUMLOW_FORMATION, scriptSetFormationCB), //flank attack move tweaks makeEntry(AIM_FLANK_ATTACK_RADIUS, scriptSetReal32CB), makeEntry(AIM_FLANK_MOVE_FORMATION, scriptSetFormationCB), makeEntry(AIM_FLANK_ATTACK_FORMATION, scriptSetFormationCB), //dock move tweaks makeEntry(AIM_DOCK_REPAIRHEALTH_TRIGGER, scriptSetUdwordCB), makeEntry(AIM_DOCK_FINISH_FUEL_THRESHOLD, scriptSetReal32CB), makeEntry(AIM_DOCK_DEFAULT_FORMATION, scriptSetFormationCB), //defend mothership move tweaks makeEntry(AIM_DEF_MOTHERSHIP_ATTACK_FORMATION,scriptSetFormationCB), //active patrol move tweaks makeEntry(AIM_PATROL_FIRST_FORMATION, scriptSetFormationCB), makeEntry(AIM_PATROL_GENERAL_FORMATION, scriptSetFormationCB), makeEntry(AIH_PATROL_ENEMYNEARBY_STRENGTH, scriptSetReal32CB), makeEntry(AIH_PATROL_ENEMYNEARBY_FORMATION, scriptSetFormationCB), makeEntry(AIH_FASTDEF_NUMLOW_STANDOFF_DIST, scriptSetReal32CB), makeEntry(AIH_FASTDEF_NUMLOW_FORMATION, scriptSetFormationCB), makeEntry(AIH_FASTDEF_INVADER_ENEMYDIST, scriptSetReal32CB), makeEntry(AIH_FASTDEF_INVADER_LOTSFEW_LIMIT, scriptSetUdwordCB), makeEntry(AIH_FASTDEF_INVADER_LOTS_FORMATION, scriptSetFormationCB), makeEntry(AIH_FASTDEF_INVADER_FEW_FORMATION, scriptSetFormationCB), makeEntry(AIH_SLOWDEF_NUMLOW_STANDOFF_DIST, scriptSetReal32CB), makeEntry(AIH_SLOWDEF_NUMLOW_FORMATION, scriptSetFormationCB), makeEntry(AIH_SLOWDEF_RESPONSE_MAXDISTSQ, scriptSetReal32CB), makeEntry(AIH_SLOWDEF_INVADER_ENEMYDIST, scriptSetReal32CB), makeEntry(AIH_SLOWDEF_INVADER_LOTSFEW_LIMIT, scriptSetUdwordCB), makeEntry(AIH_SLOWDEF_INVADER_LOTS_FORMATION, scriptSetFormationCB), makeEntry(AIH_SLOWDEF_INVADER_FEW_FORMATION, scriptSetFormationCB), //temp guard move tweaks makeEntry(AIM_TEMP_GUARD_MOVE_LOCATION_MULT, scriptSetReal32CB), makeEntry(AIM_TEMP_GUARD_MOVE_FORMATION, scriptSetFormationCB), makeEntry(AIM_TEMP_GUARD_MOVE_TACTICS, scriptSetTacticsCB), makeEntry(AIM_TEMP_GUARD_INVADER_FORMATION, scriptSetFormationCB), //support move tweaks makeEntry(AIM_SUPPORT_SAFE_DISTANCE, scriptSetReal32CB), //utility tweak variables makeEntry(AIU_STANDOFF_NEG_ERROR_ANGLE, scriptSetReal32CB), makeEntry(AIU_STANDOFF_POS_ERROR_ANGLE, scriptSetReal32CB), makeEntry(AIU_STANDOFF_DIST_ERROR_LOW, scriptSetReal32CB), makeEntry(AIU_STANDOFF_DIST_ERROR_HIGH, scriptSetReal32CB), makeEntry(AIU_RESCUE_MULTIPLE_MINSHIPS, scriptSetUdwordCB), makeEntry(AIU_RESCUE_ENEMYMULTIPLE_MINSHIPS, scriptSetUdwordCB), makeEntry(AIU_RESCUE_NOMULTIPLE_FORMATION, scriptSetFormationCB), makeEntry(AIU_RESCUE_TEAMMULTIPLE_FORMATION, scriptSetFormationCB), makeEntry(AIU_RESCUE_BOTHMULTIPLE_FORMATION, scriptSetFormationCB), makeEntry(AIU_TAKEOUT_ENEMYFLEET_RADIUS, scriptSetReal32CB), makeEntry(AIU_TAKEOUT_MULTIPLE_MINSHIPS, scriptSetUdwordCB), makeEntry(AIU_TAKEOUT_ENEMYMULTIPLE_MINSHIPS, scriptSetUdwordCB), makeEntry(AIU_TAKEOUT_NOMULTIPLE_FORMATION, scriptSetFormationCB), makeEntry(AIU_TAKEOUT_TEAMMULTIPLE_FORMATION, scriptSetFormationCB), makeEntry(AIU_TAKEOUT_BOTHMULTIPLE_FORMATION, scriptSetFormationCB), makeEntry(AIU_SAFEST_STANDOFF_DIST_MULT, scriptSetReal32CB), makeEntry(AIU_FINDUNARMED_PROTECTION_STRENGTH,scriptSetReal32CB), makeEntry(AIU_FINDATTACKING_ENEMYFLEET_RADIUS,scriptSetReal32CB), makeEntry(AIU_HARASS_PROTECTION_RADIUS, scriptSetReal32CB), makeEntry(AIU_UNARMED_UNDEFENDED_MODIFIER, scriptSetReal32CB), makeEntry(AIU_UNARMED_MODIFIER, scriptSetReal32CB), makeEntry(AIU_FIGHTER_VULNERABLE_MODIFIER, scriptSetReal32CB), endEntry }; /*----------------------------------------------------------------------------- Name : aiplayerChooseEnemies Description : Fills in the universe.aiplayerEnemy array so that each computer player has a primary enemy. Sorry about the sheer massiveness of this function. It just got out of hand. Inputs : Outputs : Return : void ----------------------------------------------------------------------------*/ void aiplayerChooseEnemies(udword num_players, udword num_human, udword num_comp) { sdword players[MAX_MULTIPLAYER_PLAYERS][MAX_MULTIPLAYER_PLAYERS], bigots[MAX_MULTIPLAYER_PLAYERS], normcomp[MAX_MULTIPLAYER_PLAYERS]; udword humancnt = 0, bigotcnt = 0, normcnt = 0; udword numbigotsperhuman, numbigotsleftover, numloops, numcompenemies; udword slot, i, j; sdword giveup; if ((!num_comp) || (noDefaultComputerPlayer)) { universe.aiplayerEnemy[0] = 1; universe.aiplayerEnemy[1] = 0; return; } dbgAssert(num_players == num_human+num_comp); for (i=0;i 100) { aiplayerLog((0,"Warning: giving up on loop 1")); break; } //if the player has no enemy if ((enemy != normcomp[i]) && (players[enemy][0] == -1)) { //the player becomes that player's enemy players[enemy][0] = normcomp[i]; done = TRUE; } else { //go to the next player enemy++; //loop around if at end of list if (enemy == num_players) { enemy = 0; } } } //Note: each (human or computer) player should have only 1 normal computer player as enemy } //get some stats on the bigots // num_comp_per_human = num_comp/num_human; // num_comp_leftover = num_comp%num_human; if (!num_human) { //want to avoid divide by zero //if there aren't any human players, then bigotry is irrelevant goto transfer_aiplayerEnemy; } numbigotsperhuman = bigotcnt/num_human; numbigotsleftover = bigotcnt%num_human; numloops = bigotcnt-numbigotsleftover; //determine how many enemy computer players each human player can have if (normcnt) { numcompenemies = numbigotsperhuman+1; } else { numcompenemies = numbigotsperhuman; } //distribute the bigot computers among available human players //numloops is the amount of bigot computer players per human for (i=0;i 100) { aiplayerLog((0,"Warning: giving up on loop 2")); break; } if (players[enemy][slot] == -1) { players[enemy][slot] = bigots[i]; done = TRUE; } else { slot++; if (slot == numcompenemies) { enemy++; slot = 0; if (enemy == num_human) { enemy = 0; } } } } } numcompenemies++; //distribute extra bigot computer players among human players for (i=0;i 100) { aiplayerLog((0,"Warning: giving up on loop 3")); break; } if (players[enemy][slot] == -1) { players[enemy][slot] = bigots[i+(num_human*numloops)]; done = TRUE; } else { slot++; if (slot == numcompenemies) { enemy++; slot = 0; if (enemy == num_human) { enemy = 0; } } } } } transfer_aiplayerEnemy: //initialize enemyplayer array for (i=0;iplayer = player; dbgAssert(aiplayerLevel < AI_NUM_LEVELS); aiplayer->aiplayerDifficultyLevel = aiplayerLevel; aiplayer->aiplayerUpdateRate = AIPLAYER_UPDATE_RATE[aiplayerLevel]; // initialize other stuff here aiplayer->aiplayerFrameCount = 0; aiplayer->enemyPlayerCount = 0; aiplayer->primaryEnemyPlayer = NULL; growSelectInit(&aiplayer->newships); for (i=0;ienemyShipsIAmAwareOf[i]); growSelectInit(&aiplayer->primaryEnemyShipsIAmAwareOf[i]); } //initialize hyperspacing stuff aiplayer->aifHyperSavings = 0; aiplayer->aifHyperSkimming = 0; aiplayer->aifLastRUCount = aiplayer->player->resourceUnits; aiplayer->NumRUDockPoints = 0; aiplayer->NumRCollectorsBeingBuilt = 0; aiplayer->NumRControllersBeingBuilt = 0; aiplayer->NumASFBeingBuilt = 0; aiplayer->NumResearchShipsBeingBuilt = 0; aiplayer->NumRUsSpentOnAttman = 0; aiplayer->NumRUsSpentOnDefman = 0; aiplayer->NumRUsSpentOnResourceman = 0; aiplayer->NumRUsSpentOnScriptman = 0; listInit(&aiplayer->AttackManRequestShipsQ); listInit(&aiplayer->DefenseManRequestShipsQ); listInit(&aiplayer->ScriptManRequestShipsQ); listInit(&aiplayer->AttackManTeamsWaitingForShipsQ); listInit(&aiplayer->DefenseManTeamsWaitingForShipsQ); listInit(&aiplayer->ScriptManTeamsWaitingForShipsQ); memset(&aiplayer->AttackManShipsBeingBuilt,0,sizeof(aiplayer->AttackManShipsBeingBuilt)); memset(&aiplayer->DefenseManShipsBeingBuilt,0,sizeof(aiplayer->DefenseManShipsBeingBuilt)); memset(&aiplayer->ScriptManShipsBeingBuilt,0,sizeof(aiplayer->ScriptManShipsBeingBuilt)); aiplayer->numLeaders = 0; aifInit(aiplayer); airInit(aiplayer); aiaInit(aiplayer); aidInit(aiplayer); aitInit(aiplayer); return aiplayer; } void aiplayerClose(AIPlayer *aiplayer) { udword i; aiCurrentAIPlayer = aiplayer; aiplayerLog((aiplayer->player->playerIndex,"Deleting AIPlayer %x",aiplayer)); growSelectClose(&aiplayer->newships); for (i=0;ienemyShipsIAmAwareOf[i]); growSelectClose(&aiplayer->primaryEnemyShipsIAmAwareOf[i]); } listDeleteAll(&aiplayer->AttackManRequestShipsQ); listDeleteAll(&aiplayer->DefenseManRequestShipsQ); listDeleteAll(&aiplayer->ScriptManRequestShipsQ); listDeleteAll(&aiplayer->AttackManTeamsWaitingForShipsQ); listDeleteAll(&aiplayer->DefenseManTeamsWaitingForShipsQ); listDeleteAll(&aiplayer->ScriptManTeamsWaitingForShipsQ); aifClose(aiplayer); airClose(aiplayer); aiaClose(aiplayer); aidClose(aiplayer); aitClose(aiplayer); memFree(aiplayer); } void aiplayerPlay(AIPlayer *aiplayer) { // if (!singlePlayerGame) aiCurrentAIPlayer = aiplayer; universe.aiplayerProcessing = TRUE; if (!nisIsRunning) { statsSetOverkillfactor(AIPLAYER_OVERKILL_FACTOR); aifFleetCommand(); aiplayer->aiplayerFrameCount++; } if (!singlePlayerGame) aiCurrentAIPlayer = NULL; universe.aiplayerProcessing = FALSE; } extern bool mrNoAI; void aiplayerUpdateAll(void) { uword i; AIPlayer *aiplayer; if (gameIsRunning) { if (singlePlayerGame) { if (singlePlayerGameInfo.hyperspaceState != NO_HYPERSPACE) { return; } if (tutorial!=TUTORIAL_ONLY) { aiCurrentAIPlayer = universe.players[1].aiPlayer; dbgAssert(aiCurrentAIPlayer); if ((universe.univUpdateCounter & aiCurrentAIPlayer->aiplayerUpdateRate) == (SP_KAS_UPDATE_FRAME & aiCurrentAIPlayer->aiplayerUpdateRate)) { universe.aiplayerProcessing = TRUE; kasExecute(); universe.aiplayerProcessing = FALSE; } } } if (!playPackets) { for (i=0;iaiplayerUpdateRate) == (i & aiplayer->aiplayerUpdateRate)) && (aiplayer->player->playerState != PLAYER_DEAD) && (!mrNoAI)) { aiplayerPlay(aiplayer); } } } } if(tutorial==TUTORIAL_ONLY) { // In the tutorial, execute Kas script every other frame - we want REALTIME Kas. if( (universe.univUpdateCounter & TUTORIAL_KAS_UPDATE_MASK) == TUTORIAL_KAS_UPDATE_FRAME) kasExecute(); } } } /*----------------------------------------------------------------------------- Name : Description : Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void ThisAIPlayerShipDiedCB(AIPlayer *aiplayer,Ship *ship) { if (ship->staticinfo->canReceiveResources) { aiplayer->NumRUDockPoints -= NumRUDockPointsOnThisShip(ship); } } /*----------------------------------------------------------------------------- Name : Description : Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void ThisAIPlayerShipCreatedCB(AIPlayer *aiplayer,Ship *ship) { if (ship->staticinfo->canReceiveResources) { aiplayer->NumRUDockPoints += NumRUDockPointsOnThisShip(ship); } } /*----------------------------------------------------------------------------- Name : aiplayerShipLaunchedCallback Description : calls the aiplayer module to indicate a new ship has been built and launched Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void aiplayerShipLaunchedCallback(Ship *ship) { AIPlayer *aiplayer; if ((gameIsRunning) && (!nisIsRunning)) { aiplayer = ship->playerowner->aiPlayer; if (aiplayer != NULL) { if (bitTest(ship->specialFlags, SPECIAL_LaunchedFromKas)) { bitClear(ship->specialFlags, SPECIAL_LaunchedFromKas); } else { unitCapCreateShip(ship, aiplayer->player); growSelectAddShip(&aiplayer->newships, ship); ThisAIPlayerShipCreatedCB(aiplayer,ship); } } } } /*----------------------------------------------------------------------------- Name : aiplayerPlayerDied Description : Checks to see if the player that died is the aiplayer's primary enemy, and changes the primary enemy if needed Inputs : player - the player that died Outputs : Return : void ----------------------------------------------------------------------------*/ void aiplayerPlayerDied(Player *player) { // AIPlayer *aiplayer; // udword DeadPlayerIndex = player->playerIndex, tempIndex,loop, // DeadPlayersEnemy = universe.aiplayerEnemy[DeadPlayerIndex], i,j; sdword players[MAX_MULTIPLAYER_PLAYERS][MAX_MULTIPLAYER_PLAYERS]; Node *shipnode = universe.ShipList.head; Ship *ship; udword DeadPlayerIndex = player->playerIndex, num_living_humans=0, num_humans=0, maxopponents=0, tempcnt=0, num_remaining_players=0; udword slot, newslot, i, j; bool done; sdword giveup; if (DeadPlayerIndex == MAX_MULTIPLAYER_PLAYERS) { // this is the hack player for autoguns and stuff return; } //count the number of remaining players for (i=0;i 100) { aiplayerLog((0,"Warning: giving up on loop 4")); break; } if (players[universe.aiplayerEnemy[i]][slot] == -1) { players[universe.aiplayerEnemy[i]][slot] = i; done = TRUE; } else { slot++; } } } } else { //humans have -1 as their enemies num_humans++; if (universe.players[i].playerState == PLAYER_ALIVE) { num_living_humans++; } } } //find out how many opponents human players have maxopponents = 0; for (i=0;i maxopponents) { maxopponents = tempcnt; } } //now find new opponents for the players who's enemy died slot=0; while (players[DeadPlayerIndex][slot] != -1) { udword bigotchance = randyrandombetween(RAN_AIPlayer, 0, 100); //there're still living humans, and the computer player is a bigot //so he'll have to kill the human, won't he? if ((num_living_humans) && (bigotchance < AIPLAYER_BIGOTRY[players[DeadPlayerIndex][slot]-num_humans])) { udword enemy = randyrandombetween(RAN_AIPlayer, 0, num_humans); done = FALSE; giveup = 0; newslot = 0; //the computer player is a bigot while (!done) { if (giveup++ > 100) { aiplayerLog((0,"Warning: giving up on loop 5")); break; } if ((universe.players[enemy].playerState == PLAYER_ALIVE) && (players[enemy][newslot] == -1)) { players[enemy][newslot] = players[DeadPlayerIndex][slot]; done = TRUE; if (universe.players[players[DeadPlayerIndex][slot]].aiPlayer) { universe.players[players[DeadPlayerIndex][slot]].aiPlayer->primaryEnemyPlayer = &universe.players[enemy]; } } else { newslot++; if ((universe.players[enemy].playerState != PLAYER_ALIVE) || (newslot == maxopponents)) { enemy++; newslot = 0; if (enemy == num_humans) { enemy = 0; } } } } } else { //find the enemy for a non-bigot udword enemy = randyrandombetween(RAN_AIPlayer, 0, (universe.numPlayers-1)); bool done = FALSE, loop = FALSE; giveup = 0; while (!done) { if (giveup++ > 100) { aiplayerLog((0,"Warning: giving up on loop 6")); break; } //if the player has no enemy if ((universe.players[enemy].playerState == PLAYER_ALIVE) && (players[enemy][0] == -1) && (enemy != players[DeadPlayerIndex][slot])) { //the player becomes that player's enemy players[enemy][0] = players[DeadPlayerIndex][slot]; done = TRUE; if (universe.players[players[DeadPlayerIndex][slot]].aiPlayer) { universe.players[players[DeadPlayerIndex][slot]].aiPlayer->primaryEnemyPlayer = &universe.players[enemy]; } } else { //go to the next player enemy++; //loop around if at end of list if (enemy == universe.numPlayers) { //if this while loop happens more than once, then //the random enemies chosen ended up giving the last //computer player to have itself as enemy if (!loop) { loop = TRUE; } else { //loop done twice, switch previous enemy with current enemy enemy--; players[enemy][0] = players[enemy-1][0]; players[enemy-1][0] = players[DeadPlayerIndex][slot]; done = TRUE; } enemy = 0; } } } } slot++; } //initialize enemyplayer array for (i=0;iprimaryEnemyPlayer = &universe.players[j]; aiplayerLog(((uword)i, "Giving Up on this function... assigning the first living player %i as primary enemy", j)); } } } else { //yay! it worked properly universe.players[i].aiPlayer->primaryEnemyPlayer = &universe.players[universe.aiplayerEnemy[i]]; aiplayerLog(((uword)i, "New Primary Enemy %i", universe.aiplayerEnemy[i])); } aiuChangePrimaryEnemy(universe.players[i].aiPlayer); universe.players[i].aiPlayer->recalculateAllies = TRUE; } } shipdiedstuff: //go through the entire shiplist and run the ship died //function for every ship belonging to the dead player while (shipnode != NULL) { ship = (Ship *)listGetStructOfNode(shipnode); if (ship->playerowner == player) { aiplayerShipDied(ship); } shipnode = shipnode->next; } for (i=0;iteamsUsed; ++i) { teamp = aiplayer->teams[i]; for (j = 0; j < teamp->shipList.selection->numShips; ++j) if (teamp->shipList.selection->ShipPtr[j] == ship) return 0; } return 1; } /*----------------------------------------------------------------------------- Name : BuildShipList Description : Goes through the universe shiplist and pulls out the aiplayer's ships Inputs : aiplayer - the current computer player Outputs : fills in the computer players newships shiplist Return : void ----------------------------------------------------------------------------*/ void BuildShipList(AIPlayer *aiplayer) { Node *shipnode = universe.ShipList.head; Ship *ship; while (shipnode != NULL) { ship = (Ship *)listGetStructOfNode(shipnode); if (((ship->flags & SOF_NISShip) == 0) && (ship->playerowner == aiplayer->player) && ShipNotOnTeam(aiplayer, ship)) { growSelectAddShip(&aiplayer->newships, ship); ThisAIPlayerShipCreatedCB(aiplayer,ship); } shipnode = shipnode->next; } } /*----------------------------------------------------------------------------- Name : FindEnemies Description : Counts the number of enemy players and decides who the primary enemy will be Inputs : aiplayer - the current aiplayer Outputs : Return : void ----------------------------------------------------------------------------*/ void FindEnemies(AIPlayer *aiplayer) { udword primaryEnemyIndex = 0; aiplayer->enemyPlayerCount = universe.numPlayers - 1; primaryEnemyIndex = universe.aiplayerEnemy[aiplayer->player->playerIndex]; //randomize the primary Enemy Index if there is more than one opponent /* if (aiplayer->enemyPlayerCount > 1) { primaryEnemyIndex = ranRandom(RAN_AIPlayer) % (aiplayer->enemyPlayerCount); //check to make sure the primaryEnemy isn't the aiplayer itself while (&universe.players[primaryEnemyIndex] == aiplayer->player) { primaryEnemyIndex = ranRandom(RAN_AIPlayer) % (aiplayer->enemyPlayerCount); } } else { if (&universe.players[primaryEnemyIndex] == aiplayer->player) { primaryEnemyIndex = 1; } } */ #ifndef HW_Release if (primaryEnemyIndex == aiplayer->player->playerIndex) { aiplayerLog((aiplayer->player->playerIndex, "Super Heap Big Warning!!! CPU chose itself as primary enemy")); } #endif if (primaryEnemyIndex == -1) { aiplayerLog((aiplayer->player->playerIndex, "Error: CPU Player chose -1th player, setting to 0")); primaryEnemyIndex++; } aiplayer->primaryEnemyPlayer = &universe.players[primaryEnemyIndex]; } /*----------------------------------------------------------------------------- Name : aiplayerGameStart Description : Starting code for aiplayer Inputs : aiplayer - the player to start Outputs : Return : void ----------------------------------------------------------------------------*/ void aiplayerGameStart(AIPlayer *aiplayer) { //put startup code in here //build currentshiplist BuildShipList(aiplayer); //find other players and decide who the primary enemy is FindEnemies(aiplayer); //initializes hyperspacing code aifHyperspaceInit(aiplayer); } /*----------------------------------------------------------------------------- Name : aiplayerShipDied Description : Clears the pointers to a ship that just died from the entire computer player tree Inputs : ship - the ship that just died Outputs : various structures get changed Return : void ----------------------------------------------------------------------------*/ void aiplayerShipDied(ShipPtr ship) { udword i; AIPlayer *aiplayer; uword playerIndex; kasShipDied(ship); // kas runs even when NIS running, so always report ship deaths. if (ship->flags & SOF_NISShip) { return; } aiplayerLog((ship->playerowner->playerIndex,"%s Ship died, ", ShipTypeToStr(ship->shiptype))); for (i = 0; i < universe.numPlayers;i++) { if ((aiplayer = universe.players[i].aiPlayer) != NULL) { aiCurrentAIPlayer = aiplayer; playerIndex = aiplayer->player->playerIndex; if ((ship->playerowner == aiplayer->player) || (ship->attributes & ATTRIBUTES_Defector)) // do all checks for defector { if (ship->playerowner == aiplayer->player) ThisAIPlayerShipDiedCB(aiplayer,ship); if (growSelectRemoveShip(&aiplayer->newships, ship)) { if ((aiplayer->numLeaders) && (bitTest(ship->attributes, ATTRIBUTES_TeamLeader))) { aiplayer->numLeaders--; } aiplayerLog((playerIndex,"removed from aiplayer newships")); } } // do these checks all the time for safety (salcap's capturing ships, defectors, etc.) if (growSelectRemoveShip(&aiplayer->enemyShipsIAmAwareOf[ship->shiptype], ship)) { // aiplayerLog((playerIndex,"removed from aiplayer enemyShipsIAmAwareOf")); } if (growSelectRemoveShip(&aiplayer->primaryEnemyShipsIAmAwareOf[ship->shiptype], ship)) { // aiplayerLog((playerIndex,"removed from aiplayer primaryEnemyShipsIAmAwareOf")); } if (airShipDied(aiplayer,ship)) { aiplayerLog((playerIndex,"removed from Resource Manager of aiplayer %d",aiplayer->player->playerIndex)); } if (aiaShipDied(aiplayer,ship)) { aiplayerLog((playerIndex,"removed from Attack Manager of aiplayer %d",aiplayer->player->playerIndex)); } if (aidShipDied(aiplayer,ship)) { aiplayerLog((playerIndex,"removed from Defense Manager of aiplayer %d",aiplayer->player->playerIndex)); } aitShipDied(aiplayer,ship); } } } /*----------------------------------------------------------------------------- Name : aiplayerResourceDied Description : Clears all pointers in the computer player tree to a resource that died Inputs : resource - the resource that died Outputs : Clears some pointers Return : void ----------------------------------------------------------------------------*/ void aiplayerResourceDied(Resource *resource) { udword i; AIPlayer *aiplayer; for (i=0;inumLeaders++; bitSet(ship->attributes, ATTRIBUTES_TeamLeader); aiplayerLog((aiplayer->player->playerIndex, "Added Leader to aiplayer")); } /*============================================================================= Save Game Stuff =============================================================================*/ #pragma warning( 4 : 4047) // turns off "different levels of indirection warning" AIPlayer *fixingThisAIPlayer; void SavePath(struct Path *path) { SaveChunk *chunk; sdword size; size = sizeof_Path(path->numPoints); chunk = CreateChunk(BASIC_STRUCTURE|PATH,size,path); SaveThisChunk(chunk); memFree(chunk); } struct Path *LoadPath(void) { SaveChunk *chunk; sdword size; Path *path; Path *loadpath; chunk = LoadNextChunk(); VerifyChunkNoSize(chunk,BASIC_STRUCTURE|PATH); path = (Path *)chunkContents(chunk); size = sizeof_Path(path->numPoints); dbgAssert(size == chunk->contentsSize); loadpath = (Path *)memAlloc(size, "newPath", NonVolatile); memcpy(loadpath,path,size); memFree(chunk); return loadpath; } sdword AITeamToTeamIndex(struct AITeam *team) { sdword i; AIPlayer *aiplayer; if (team == NULL) { return -1; } aiplayer = team->aiplayerowner; for (i=0;iteamsUsed;i++) { if (aiplayer->teams[i] == team) { return i; } } dbgAssert(FALSE); return -1; } struct AITeam *AITeamIndexToTeam(AIPlayer *aiplayer,sdword index) { if (index == -1) { return NULL; } dbgAssert(index < aiplayer->teamsUsed); return aiplayer->teams[index]; } void SaveRequestShipsCB(void *stuff) { RequestShips copyOfRequest; memcpy(©OfRequest,stuff,sizeof(RequestShips)); copyOfRequest.creator = (sdword)SpaceObjRegistryGetID((SpaceObj *)copyOfRequest.creator); SaveStructureOfSize(©OfRequest,sizeof(RequestShips)); } void SaveTeamWaitingCB(void *stuff) { SaveChunk *chunk; TeamWaitingForTheseShips *sc; chunk = CreateChunk(BASIC_STRUCTURE,sizeof(TeamWaitingForTheseShips),stuff); sc = (TeamWaitingForTheseShips *)chunkContents(chunk); sc->team = AITeamToTeamIndex(sc->team); SaveThisChunk(chunk); memFree(chunk); } void LoadRequestShipsCB(LinkedList *list) { RequestShips *requestShips = LoadStructureOfSize(sizeof(RequestShips)); requestShips->creator = SpaceObjRegistryGetShip((sdword)requestShips->creator); listAddNode(list,&requestShips->node,requestShips); } void LoadTeamWaitingCB(LinkedList *list) { TeamWaitingForTheseShips *teamWaiting = LoadStructureOfSize(sizeof(TeamWaitingForTheseShips)); listAddNode(list,&teamWaiting->node,teamWaiting); } void FixTeamWaitingCB(void *stuff) { #define teamWaiting ((TeamWaitingForTheseShips *)stuff) teamWaiting->team = AITeamIndexToTeam(fixingThisAIPlayer,(sdword)teamWaiting->team); #undef teamWaiting } void SaveThisAIPlayer(AIPlayer *aiplayer) { SaveChunk *chunk; AIPlayer *sc; sdword i; chunk = CreateChunk(BASIC_STRUCTURE|AIPLAYER,sizeof(AIPlayer),aiplayer); sc = chunkContents(chunk); sc->player = SavePlayerToPlayerIndex(aiplayer->player); sc->primaryEnemyPlayer = SavePlayerToPlayerIndex(aiplayer->primaryEnemyPlayer); for (i=0;inumSupportTeams;i++) { sc->supportTeam[i] = AITeamToTeamIndex(aiplayer->supportTeam[i]); } for (i=0;ireconTeam[i] = AITeamToTeamIndex(aiplayer->reconTeam[i]); } sc->harassTeam = AITeamToTeamIndex(aiplayer->harassTeam); for (i=0;iattackTeam[i] = AITeamToTeamIndex(aiplayer->attackTeam[i]); } for (i=0;inumGuardTeams;i++) { sc->guardTeams[i] = AITeamToTeamIndex(aiplayer->guardTeams[i]); } sc->ScriptCreator = (sdword)SpaceObjRegistryGetID((SpaceObj *)aiplayer->ScriptCreator); sc->AICreator = (sdword)SpaceObjRegistryGetID((SpaceObj *)aiplayer->AICreator); SaveThisChunk(chunk); memFree(chunk); sc = NULL; SaveGrowSelection(&aiplayer->newships); for (i=0;ienemyShipsIAmAwareOf[i]); SaveGrowSelection(&aiplayer->primaryEnemyShipsIAmAwareOf[i]); } SaveLinkedListOfStuff(&aiplayer->AttackManRequestShipsQ,SaveRequestShipsCB); SaveLinkedListOfStuff(&aiplayer->DefenseManRequestShipsQ,SaveRequestShipsCB); SaveLinkedListOfStuff(&aiplayer->ScriptManRequestShipsQ,SaveRequestShipsCB); SaveLinkedListOfStuff(&aiplayer->AttackManTeamsWaitingForShipsQ,SaveTeamWaitingCB); SaveLinkedListOfStuff(&aiplayer->DefenseManTeamsWaitingForShipsQ,SaveTeamWaitingCB); SaveLinkedListOfStuff(&aiplayer->ScriptManTeamsWaitingForShipsQ,SaveTeamWaitingCB); SaveGrowSelection(&aiplayer->airResourceReserves); SaveGrowSelection(&aiplayer->airResourceCollectors); if (aiplayer->shipsattackingmothership) SaveSelection((SpaceObjSelection *)aiplayer->shipsattackingmothership); if (aiplayer->aidProximitySensors) SaveSelection((SpaceObjSelection *)aiplayer->aidProximitySensors); if (aiplayer->aidDefenseTargets) SaveSelection((SpaceObjSelection *)aiplayer->aidDefenseTargets); if (aiplayer->aidInvadingShips) SaveSelection((SpaceObjSelection *)aiplayer->aidInvadingShips); if (aiplayer->aidDistressShips) SaveSelection((SpaceObjSelection *)aiplayer->aidDistressShips); if (aiplayer->aiaArmada.targets) SaveSelection((SpaceObjSelection *)aiplayer->aiaArmada.targets); if (aiplayer->Targets) SaveSelection((SpaceObjSelection *)aiplayer->Targets); // Save all team stuff aitSave(aiplayer); } void FixThisAIPlayer(AIPlayer *aiplayer) { sdword i; aiplayer->player = SavePlayerIndexToPlayer((sdword)aiplayer->player); aiplayer->primaryEnemyPlayer = SavePlayerIndexToPlayer((sdword)aiplayer->primaryEnemyPlayer); for (i=0;inumSupportTeams;i++) { aiplayer->supportTeam[i] = AITeamIndexToTeam(aiplayer,(sdword)aiplayer->supportTeam[i]); } for (i=0;ireconTeam[i] = AITeamIndexToTeam(aiplayer,(sdword)aiplayer->reconTeam[i]); } aiplayer->harassTeam = AITeamIndexToTeam(aiplayer,(sdword)aiplayer->harassTeam); for (i=0;iattackTeam[i] = AITeamIndexToTeam(aiplayer,(sdword)aiplayer->attackTeam[i]); } for (i=0;inumGuardTeams;i++) { aiplayer->guardTeams[i] = AITeamIndexToTeam(aiplayer,(sdword)aiplayer->guardTeams[i]); } //FixLinkedListOfStuff(&aiplayer->AttackManRequestShipsQ,FixRequestShipsCB); not needed //FixLinkedListOfStuff(&aiplayer->DefenseManRequestShipsQ,FixRequestShipsCB); not needed //FixLinkedListOfStuff(&aiplayer->ScriptManRequestShipsQ,FixRequestShipsCB); not needed FixLinkedListOfStuff(&aiplayer->AttackManTeamsWaitingForShipsQ,FixTeamWaitingCB); FixLinkedListOfStuff(&aiplayer->DefenseManTeamsWaitingForShipsQ,FixTeamWaitingCB); FixLinkedListOfStuff(&aiplayer->ScriptManTeamsWaitingForShipsQ,FixTeamWaitingCB); // Fix all team stuff aitFix(aiplayer); } AIPlayer *LoadThisAIPlayer(void) { SaveChunk *chunk; AIPlayer *aiplayer; udword i; chunk = LoadNextChunk(); VerifyChunk(chunk,BASIC_STRUCTURE|AIPLAYER,sizeof(AIPlayer)); aiplayer = memAlloc(sizeof(AIPlayer),"AIPlayer",0); memcpy(aiplayer,chunkContents(chunk),sizeof(AIPlayer)); memFree(chunk); LoadGrowSelectionAndFix(&aiplayer->newships); for (i=0;ienemyShipsIAmAwareOf[i]); LoadGrowSelectionAndFix(&aiplayer->primaryEnemyShipsIAmAwareOf[i]); } LoadLinkedListOfStuff(&aiplayer->AttackManRequestShipsQ,LoadRequestShipsCB); LoadLinkedListOfStuff(&aiplayer->DefenseManRequestShipsQ,LoadRequestShipsCB); LoadLinkedListOfStuff(&aiplayer->ScriptManRequestShipsQ,LoadRequestShipsCB); LoadLinkedListOfStuff(&aiplayer->AttackManTeamsWaitingForShipsQ,LoadTeamWaitingCB); LoadLinkedListOfStuff(&aiplayer->DefenseManTeamsWaitingForShipsQ,LoadTeamWaitingCB); LoadLinkedListOfStuff(&aiplayer->ScriptManTeamsWaitingForShipsQ,LoadTeamWaitingCB); aiplayer->ScriptCreator = SpaceObjRegistryGetShip((sdword)aiplayer->ScriptCreator); aiplayer->AICreator = SpaceObjRegistryGetShip((sdword)aiplayer->AICreator); LoadGrowSelectionAndFix(&aiplayer->airResourceReserves); LoadGrowSelectionAndFix(&aiplayer->airResourceCollectors); if (aiplayer->shipsattackingmothership) aiplayer->shipsattackingmothership = (SelectCommand *)LoadSelectionAndFix(); if (aiplayer->aidProximitySensors) aiplayer->aidProximitySensors = (SelectCommand *)LoadSelectionAndFix(); if (aiplayer->aidDefenseTargets) aiplayer->aidDefenseTargets = (SelectCommand *)LoadSelectionAndFix(); if (aiplayer->aidInvadingShips) aiplayer->aidInvadingShips = (SelectCommand *)LoadSelectionAndFix(); if (aiplayer->aidDistressShips) aiplayer->aidDistressShips = (SelectCommand *)LoadSelectionAndFix(); if (aiplayer->aiaArmada.targets) aiplayer->aiaArmada.targets = (SelectCommand *)LoadSelectionAndFix(); if (aiplayer->Targets) aiplayer->Targets = (SelectCommand *)LoadSelectionAndFix(); // Load all team stuff aitLoad(aiplayer); return aiplayer; } sdword AIPlayerToNumber(AIPlayer *aiplayer) { sdword number; if (aiplayer == NULL) { number = -1; } else { number = aiplayer->player->playerIndex; } return number; } AIPlayer *NumberToAIPlayer(sdword number) { if (number == -1) { return NULL; } else { Player *tmpplayer = SavePlayerIndexToPlayer(number); dbgAssert(tmpplayer->aiPlayer); return tmpplayer->aiPlayer; } } void aiplayerSave(void) { sdword i; AIPlayer *aiplayer; // Save Global Variables SaveInfoNumber(AIPlayerToNumber(aiCurrentAIPlayer)); aivarSave(); // Save each instance of aiplayer structure for (i=0;i