#include "AIEvents.h" #include "AITeam.h" #include "memory.h" #include "AIPlayer.h" #include "blobs.h" // // handle any events applicable for the // curMove of the given team // // these handlers can do pretty much anything, // including changing curMove, etc. // void aieExecute(struct AITeam *team) { AITeamMove *curMove = team->curMove; SelectCommand *ships = NULL; ShipPtr ship; if (!curMove || curMove->type == MOVE_DONE) return; // we're just going to allow one event to fire per cycle through here // for now // IMPORTANT: a handler can do pretty much anything it wants to, // so after calling a handler, don't assume that the previous // curMove is still the current one or even if it still exists // team died if (curMove->events.teamDied.handler && aieCheckTeamDied(team)) { aiplayerLog((aiIndex,"eventhandler: teamDied")); curMove->events.teamDied.handler(team); } // ship died else if (curMove->events.shipDied.handler && (!curMove->events.shipDied.oneShot || !curMove->events.shipDied.triggered) && aieCheckShipDied(team, &ship)) { aiplayerLog((aiIndex,"eventhandler: shipDied")); curMove->events.shipDied.triggered = TRUE; curMove->events.shipDied.handler(team, ship); } // gettingRocked else if (curMove->events.gettingRocked.handler && (!curMove->events.gettingRocked.oneShot || !curMove->events.gettingRocked.triggered) && aieCheckGettingRocked(team, &ships)) // any of the team's ships are getting rocked { aiplayerLog((aiIndex,"eventhandler: gettingRocked")); curMove->events.gettingRocked.triggered = TRUE; curMove->events.gettingRocked.handler(team, ships); } // enemyNearby else if (curMove->events.enemyNearby.handler && (!curMove->events.enemyNearby.oneShot || !curMove->events.enemyNearby.triggered) && aieCheckEnemyNearby(team, &ships)) { aiplayerLog((aiIndex,"eventhandler: enemyNearby")); curMove->events.enemyNearby.triggered = TRUE; curMove->events.enemyNearby.handler(team, ships); } // enemy not nearby else if (curMove->events.enemyNotNearby.handler && (!curMove->events.enemyNotNearby.oneShot || !curMove->events.enemyNotNearby.triggered) && aieCheckEnemyNotNearby(team)) { aiplayerLog((aiIndex,"eventhandler: enemyNotNearby")); curMove->events.enemyNotNearby.triggered = TRUE; curMove->events.enemyNotNearby.handler(team); } // firing else if (curMove->events.firing.handler && (!curMove->events.firing.oneShot || !curMove->events.firing.triggered) && aieCheckFiring(team)) { aiplayerLog((aiIndex,"eventhandler: firing")); curMove->events.firing.triggered = TRUE; curMove->events.firing.handler(team); } // disengaging else if (curMove->events.disengage.handler && curMove->events.firing.triggered && //<----- note that this event is only triggered (!curMove->events.disengage.oneShot || //if the "firing" event has already been triggered !curMove->events.disengage.triggered) && aieCheckDisengage(team)) { aiplayerLog((aiIndex,"eventhandler: disengaging")); curMove->events.disengage.triggered = TRUE; curMove->events.disengage.handler(team); } // health low else if (curMove->events.healthLow.handler && (!curMove->events.healthLow.oneShot || !curMove->events.healthLow.triggered) && aieCheckHealthLow(team)) { aiplayerLog((aiIndex,"eventhandler: healthLow")); curMove->events.healthLow.triggered = TRUE; curMove->events.healthLow.handler(team); } // health high else if (curMove->events.healthHigh.handler && (!curMove->events.healthHigh.oneShot || !curMove->events.healthHigh.triggered) && aieCheckHealthHigh(team)) { aiplayerLog((aiIndex,"eventhandler: healthHigh")); curMove->events.healthHigh.triggered = TRUE; curMove->events.healthHigh.handler(team); } // numbers low else if (curMove->events.numbersLow.handler && (!curMove->events.numbersLow.oneShot || !curMove->events.numbersLow.triggered) && aieCheckNumbersLow(team)) { aiplayerLog((aiIndex,"eventhandler: numbersLow")); curMove->events.numbersLow.triggered = TRUE; curMove->events.numbersLow.handler(team); } // numbers high else if (curMove->events.numbersHigh.handler && (!curMove->events.numbersHigh.oneShot || !curMove->events.numbersHigh.triggered) && aieCheckNumbersHigh(team)) { aiplayerLog((aiIndex,"eventhandler: numbersHigh")); curMove->events.numbersHigh.triggered = TRUE; curMove->events.numbersHigh.handler(team); } // fuel low else if (curMove->events.fuelLow.handler && (!curMove->events.fuelLow.oneShot || !curMove->events.fuelLow.triggered) && aieCheckFuelLow(team)) { aiplayerLog((aiIndex,"eventhandler: fuelLow")); curMove->events.fuelLow.triggered = TRUE; curMove->events.fuelLow.handler(team); } // fuel high else if (curMove->events.fuelHigh.handler && (!curMove->events.fuelHigh.oneShot || !curMove->events.fuelHigh.triggered) && aieCheckFuelHigh(team)) { aiplayerLog((aiIndex,"eventhandler: fuelHigh")); curMove->events.fuelHigh.triggered = TRUE; curMove->events.fuelHigh.handler(team); } //interrupt else if (curMove->events.interrupt.handler && (!curMove->events.interrupt.oneShot || !curMove->events.interrupt.triggered) && aieCheckInterrupt(team)) { if ((!curMove->events.interrupt.interval) || ((curMove->events.interrupt.interval) && (!curMove->events.interrupt.intervalcnt))) { // aiplayerLog((aiIndex,"eventhandler: interrupt")); curMove->events.interrupt.triggered = TRUE; curMove->events.interrupt.handler(team, curMove->events.interrupt.intvar); curMove->events.interrupt.intervalcnt = curMove->events.interrupt.interval; } else { curMove->events.interrupt.intervalcnt--; } } if (ships) // may have been allocated in some checks memFree(ships); } // // clear all event handlers // (ie, don't handle events) // void aieHandlersClear (AITeamMove *move) { move->events.gettingRocked.handler = NULL; move->events.enemyNearby.handler = NULL; move->events.enemyNotNearby.handler = NULL; move->events.firing.handler = NULL; move->events.disengage.handler = NULL; move->events.healthLow.handler = NULL; move->events.healthHigh.handler = NULL; move->events.numbersLow.handler = NULL; move->events.numbersHigh.handler = NULL; move->events.fuelLow.handler = NULL; move->events.fuelHigh.handler = NULL; move->events.shipDied.handler = NULL; move->events.teamDied.handler = NULL; move->events.interrupt.handler = NULL; } void aieHandlerSetGettingRocked (struct AITeamMove *move, bool8 oneShot, aieHandlerShips handler) { move->events.gettingRocked.oneShot = oneShot; move->events.gettingRocked.triggered = 0; move->events.gettingRocked.handler = handler; } void aieHandlerSetEnemyNearby (AITeamMove *move, real32 watchRadius, bool8 oneShot, aieHandlerShips handler) { move->events.enemyNearby.watchRadius = watchRadius; move->events.enemyNearby.oneShot = oneShot; move->events.enemyNearby.triggered = 0; move->events.enemyNearby.handler = handler; } void aieHandlerSetEnemyNotNearby (AITeamMove *move, real32 watchRadius, bool8 oneShot, aieHandlerSimple handler) { move->events.enemyNotNearby.watchRadius = watchRadius; move->events.enemyNotNearby.oneShot = oneShot; move->events.enemyNotNearby.triggered = 0; move->events.enemyNotNearby.handler = handler; } void aieHandlerSetFiring (AITeamMove *move, bool8 oneShot, aieHandlerSimple handler) { move->events.firing.oneShot = oneShot; move->events.firing.triggered = 0; move->events.firing.handler = handler; } void aieHandlerSetDisengage(AITeamMove *move, bool8 oneShot, aieHandlerSimple handler) { move->events.disengage.oneShot = oneShot; move->events.disengage.triggered = 0; move->events.disengage.handler = handler; } void aieHandlerSetHealthLow (AITeamMove *move, sdword watchPercentage, bool8 watchIndividual, bool8 oneShot, aieHandlerSimple handler) { move->events.healthLow.watchPercentage = watchPercentage; move->events.healthLow.watchIndividual = watchIndividual; move->events.healthLow.oneShot = oneShot; move->events.healthLow.triggered = 0; move->events.healthLow.handler = handler; } void aieHandlerSetHealthHigh (AITeamMove *move, sdword watchPercentage, bool8 watchIndividual, bool8 oneShot, aieHandlerSimple handler) { move->events.healthHigh.watchPercentage = watchPercentage; move->events.healthHigh.watchIndividual = watchIndividual; move->events.healthHigh.oneShot = oneShot; move->events.healthHigh.triggered = 0; move->events.healthHigh.handler = handler; } void aieHandlerSetNumbersLow (AITeamMove *move, sdword watchPercentage, sdword watchBaseCount, bool8 oneShot, aieHandlerSimple handler) { move->events.numbersLow.watchPercentage = watchPercentage; move->events.numbersLow.watchBaseCount = watchBaseCount; move->events.numbersLow.oneShot = oneShot; move->events.numbersLow.triggered = 0; move->events.numbersLow.handler = handler; } void aieHandlerSetNumbersHigh (AITeamMove *move, sdword watchPercentage, sdword watchBaseCount, bool8 oneShot, aieHandlerSimple handler) { move->events.numbersHigh.watchPercentage = watchPercentage; move->events.numbersHigh.watchBaseCount = watchBaseCount; move->events.numbersHigh.oneShot = oneShot; move->events.numbersHigh.triggered = 0; move->events.numbersHigh.handler = handler; } void aieHandlerSetFuelLow (AITeamMove *move, sdword watchPercentage, bool8 watchIndividual, bool8 oneShot, aieHandlerSimple handler) { move->events.fuelLow.watchPercentage = watchPercentage; move->events.fuelLow.watchIndividual = watchIndividual; move->events.fuelLow.oneShot = oneShot; move->events.fuelLow.triggered = 0; move->events.fuelLow.handler = handler; } void aieHandlerSetFuelHigh (AITeamMove *move, sdword watchPercentage, bool8 watchIndividual, bool8 oneShot, aieHandlerSimple handler) { move->events.fuelHigh.watchPercentage = watchPercentage; move->events.fuelHigh.watchIndividual = watchIndividual; move->events.fuelHigh.oneShot = oneShot; move->events.fuelHigh.triggered = 0; move->events.fuelHigh.handler = handler; } void aieHandlerSetShipDied (AITeamMove *move, bool8 oneShot, aieHandlerShip handler) { move->events.shipDied.oneShot = oneShot; move->events.shipDied.triggered = 0; move->events.shipDied.handler = handler; } void aieHandlerSetTeamDied (AITeamMove *move, aieHandlerSimple handler) { move->events.teamDied.handler = handler; } void aieHandlerSetInterrupt (AITeamMove *move, udword *intvar, udword interval, bool8 oneShot, aieHandlerInt handler) { move->events.interrupt.intvar = intvar; move->events.interrupt.interval = interval; move->events.interrupt.intervalcnt = interval; move->events.interrupt.oneShot = oneShot; move->events.interrupt.triggered = 0; move->events.interrupt.handler = handler; } // ------------------------------------------- // "CHECK" functions for events // // // return true iff someone in the team is getting rocked. // NOTE: this will allocate memory for ships iff true. // sdword aieCheckGettingRocked(AITeam *team, SelectCommand **ships) { sdword i; SelectCommand *selection = team->shipList.selection; for (i = 0; i < selection->numShips; ++i) if (selection->ShipPtr[i]->gettingrocked && (selection->ShipPtr[i]->gettingrocked->playerowner != selection->ShipPtr[i]->playerowner)) { *ships = memAlloc(sizeofSelectCommand(1), "rockedby", 0); (*ships)->numShips = 1; (*ships)->ShipPtr[0] = selection->ShipPtr[i]->gettingrocked; // an enhancement would keep checking subsequent ships // in the team, adding their rockers to this selection return TRUE; } return FALSE; } // // return true iff there are enemy ships within a certain radius of any of // the given team's ships. // NOTE: this will allocate memory for ships iff true. // sdword aieCheckEnemyNearby(AITeam *team, SelectCommand **ships) { // blob *myblob; if (team->shipList.selection->numShips) { // must use less efficient, but guaranteed, enemy nearby *ships = aiuFindNearbyEnemyShips(team->shipList.selection->ShipPtr[0],team->curMove->events.enemyNearby.watchRadius); // for efficiency, we assume all ships in the team and all // potential enemies are in the same blob -- an enhancement // later could be to select the most "central" ship instead // of the first one // NOTE: this method didn't work because blobs were too small. // myblob = aiuWrapGetCollBlob(team->shipList.selection); // if (!myblob) goto end; // // *ships = selectDupSelection(myblob->blobShips); // aiuMakeShipsOnlyPrimaryEnemyShips((*ships)); // MakeTargetsOnlyBeWithinRangeAndNotIncludeMe((SelectAnyCommand *)(*ships), (SpaceObjRotImpTarg *)(*ships)->ShipPtr[0], team->curMove->events.enemyNearby.watchRadius); if ((*ships)->numShips) { if (((*ships)->numShips <= 2) && ((*ships)->ShipPtr[0]->shiptype == LightInterceptor)) { goto end; } return TRUE; } } end: aiumemFree(*ships); return FALSE; } // // return true iff there are no enemy ships within a certain radius of any of // the given team's ships. // sdword aieCheckEnemyNotNearby(AITeam *team) { // blob *myblob; SelectCommand *ships; if (team->shipList.selection->numShips) { // must use less efficient, but guaranteed, enemy nearby ships = aiuFindNearbyEnemyShips(team->shipList.selection->ShipPtr[0],team->curMove->events.enemyNearby.watchRadius); // for efficiency, we assume all ships in the team and all // potential enemies are in the same blob -- an enhancement // later could be to select the most "central" ship instead // of the first one // myblob = aiuWrapGetCollBlob(team->shipList.selection); // if (!myblob) goto end; // // ships.numShips = 0; // selSelectionCopy((MaxAnySelection *)&ships, (MaxAnySelection *)myblob->blobShips); // MakeTargetsOnlyEnemyShips((SelectAnyCommand *)&ships, aiCurrentAIPlayer->player); // MakeTargetsOnlyBeWithinRangeAndNotIncludeMe((SelectAnyCommand *)&ships, (SpaceObjRotImpTarg *)team->shipList.selection->ShipPtr[0], team->curMove->events.enemyNotNearby.watchRadius); if (ships->numShips) { aiumemFree(ships); return FALSE; } } end: aiumemFree(ships); return TRUE; } // // return true iff one of the ships is firing (indicated by the shipisattacking // boolean variable // sdword aieCheckFiring(AITeam *team) { sdword i; SelectCommand *selection = team->shipList.selection; for (i=0;i < selection->numShips; ++i) { if (selection->ShipPtr[i]->shipisattacking) { return TRUE; } } return FALSE; } // // returns true iff all the ships stop firing (indicated by the shipisattacking // boolean variable // sdword aieCheckDisengage(AITeam *team) { sdword i; SelectCommand *selection = team->shipList.selection; for (i=0;i < selection->numShips; ++i) { if (selection->ShipPtr[i]->shipisattacking) { return FALSE; } } return TRUE; } // // return true iff the entire group (or one individual, if specified) // is below a certain level of health // sdword aieCheckHealthLow(AITeam *team) { sdword i; real32 maxHealth = 0.0; real32 actualHealth = 0.0; bool watchIndividual = team->curMove->events.healthLow.watchIndividual; for (i = 0; i < team->shipList.selection->numShips; ++i) { if (watchIndividual) { if ((100 * team->shipList.selection->ShipPtr[i]->health) < (team->curMove->events.healthLow.watchPercentage * team->shipList.selection->ShipPtr[i]->staticinfo->maxhealth)) return TRUE; } else // accummulate { maxHealth += team->shipList.selection->ShipPtr[i]->staticinfo->maxhealth; actualHealth += team->shipList.selection->ShipPtr[i]->health; } } if (!watchIndividual) { if ((100 * actualHealth) < (team->curMove->events.healthLow.watchPercentage * maxHealth)) return TRUE; } return FALSE; } // // return true iff the entire group (or one individual, if specified) // is above a certain level of health // sdword aieCheckHealthHigh(AITeam *team) { sdword i; real32 maxHealth = 0; real32 actualHealth = 0; bool watchIndividual = team->curMove->events.healthHigh.watchIndividual; for (i = 0; i < team->shipList.selection->numShips; ++i) { if (watchIndividual) { if ((100 * team->shipList.selection->ShipPtr[i]->health) > (team->curMove->events.healthHigh.watchPercentage * team->shipList.selection->ShipPtr[i]->staticinfo->maxhealth)) return TRUE; } else // accummulate { maxHealth += team->shipList.selection->ShipPtr[i]->staticinfo->maxhealth; actualHealth += team->shipList.selection->ShipPtr[i]->health; } } if (!watchIndividual) { if ((100 * actualHealth) > (team->curMove->events.healthHigh.watchPercentage * maxHealth)) return TRUE; } return FALSE; } // // return true iff the size of the team is below a certain number of ships // (expressed as a % of original team size) // sdword aieCheckNumbersLow(AITeam *team) { if (!team->curMove->events.numbersLow.watchBaseCount) { team->curMove->events.numbersLow.watchBaseCount = team->shipList.selection->numShips; return FALSE; } return ((100 * team->shipList.selection->numShips) < (team->curMove->events.numbersLow.watchPercentage * team->curMove->events.numbersLow.watchBaseCount)); } // // return true iff the size of the team is above a certain number of ships // (expressed as a % of original team size) // sdword aieCheckNumbersHigh(AITeam *team) { if (!team->curMove->events.numbersHigh.watchBaseCount) { team->curMove->events.numbersHigh.watchBaseCount = team->shipList.selection->numShips; return FALSE; } return ((100 * team->shipList.selection->numShips) > (team->curMove->events.numbersHigh.watchPercentage * team->curMove->events.numbersHigh.watchBaseCount)); } // // return true iff the entire group (or one individual, if specified) // is below a certain level of fuel // sdword aieCheckFuelLow(AITeam *team) { sdword i; real32 maxFuel = 0.0; real32 actualFuel = 0.0; bool watchIndividual = team->curMove->events.fuelLow.watchIndividual; for (i = 0; i < team->shipList.selection->numShips; ++i) { if (watchIndividual) { if ((100.0 * team->shipList.selection->ShipPtr[i]->fuel) < (team->curMove->events.fuelLow.watchPercentage * team->shipList.selection->ShipPtr[i]->staticinfo->maxfuel)) return TRUE; } else // accummulate { maxFuel += team->shipList.selection->ShipPtr[i]->staticinfo->maxfuel; actualFuel += team->shipList.selection->ShipPtr[i]->fuel; } } if (!watchIndividual) { if ((100.0 * actualFuel) < (team->curMove->events.fuelLow.watchPercentage * maxFuel)) return TRUE; } return FALSE; } // // return true iff the entire group (or one individual, if specified) // is below a certain level of fuel // sdword aieCheckFuelHigh(AITeam *team) { sdword i; real32 maxFuel = 0.0; real32 actualFuel = 0.0; bool watchIndividual = team->curMove->events.fuelHigh.watchIndividual; for (i = 0; i < team->shipList.selection->numShips; ++i) { if (watchIndividual) { if ((100.0 * team->shipList.selection->ShipPtr[i]->fuel) > (team->curMove->events.fuelHigh.watchPercentage * team->shipList.selection->ShipPtr[i]->staticinfo->maxfuel)) return TRUE; } else // accummulate { maxFuel += team->shipList.selection->ShipPtr[i]->staticinfo->maxfuel; actualFuel += team->shipList.selection->ShipPtr[i]->fuel; } } if (!watchIndividual) { if ((100.0 * actualFuel) > (team->curMove->events.fuelHigh.watchPercentage * maxFuel)) return TRUE; } return FALSE; } sdword aieCheckShipDied(AITeam *team, ShipPtr *ship) { return FALSE; } sdword aieCheckTeamDied(AITeam *team) { if (team->shipList.selection->numShips) { return FALSE; } return TRUE; } sdword aieCheckInterrupt(AITeam *team) { if (*(team->curMove->events.interrupt.intvar)) { return TRUE; } return FALSE; } /*============================================================================= Save Game Stuff =============================================================================*/ #pragma warning( 4 : 4047) // turns off "different levels of indirection warning" void aiePreFixAIEvents(struct AITeamMove *move) { if (move->events.interrupt.handler) { // convert pointer to offset into AIPlayer structure dbgAssert(move->events.interrupt.intvar); move->events.interrupt.intvar = ((ubyte *)move->events.interrupt.intvar) - ((ubyte *)fixingThisAIPlayer); dbgAssert(move->events.interrupt.intvar < sizeof(AIPlayer)); } move->events.gettingRocked.handler = aieHandlerToNum((aieHandlerSimple)move->events.gettingRocked.handler); move->events.enemyNearby.handler = aieHandlerToNum((aieHandlerSimple)move->events.enemyNearby.handler); move->events.enemyNotNearby.handler = aieHandlerToNum(move->events.enemyNotNearby.handler); move->events.firing.handler = aieHandlerToNum(move->events.firing.handler); move->events.disengage.handler = aieHandlerToNum(move->events.disengage.handler); move->events.healthLow.handler = aieHandlerToNum(move->events.healthLow.handler); move->events.healthHigh.handler = aieHandlerToNum(move->events.healthHigh.handler); move->events.numbersLow.handler = aieHandlerToNum(move->events.numbersLow.handler); move->events.numbersHigh.handler = aieHandlerToNum(move->events.numbersHigh.handler); move->events.fuelLow.handler = aieHandlerToNum(move->events.fuelLow.handler); move->events.fuelHigh.handler = aieHandlerToNum(move->events.fuelHigh.handler); move->events.shipDied.handler = aieHandlerToNum((aieHandlerSimple)move->events.shipDied.handler); move->events.teamDied.handler = aieHandlerToNum(move->events.teamDied.handler); move->events.interrupt.handler = aieHandlerToNum((aieHandlerSimple)move->events.interrupt.handler); } void aieFixAIEvents(struct AITeamMove *move) { move->events.gettingRocked.handler = (aieHandlerShips)aieNumToHandler((sdword)move->events.gettingRocked.handler); move->events.enemyNearby.handler = (aieHandlerShips)aieNumToHandler((sdword)move->events.enemyNearby.handler); move->events.enemyNotNearby.handler = aieNumToHandler((sdword)move->events.enemyNotNearby.handler); move->events.firing.handler = aieNumToHandler((sdword)move->events.firing.handler); move->events.disengage.handler = aieNumToHandler((sdword)move->events.disengage.handler); move->events.healthLow.handler = aieNumToHandler((sdword)move->events.healthLow.handler); move->events.healthHigh.handler = aieNumToHandler((sdword)move->events.healthHigh.handler); move->events.numbersLow.handler = aieNumToHandler((sdword)move->events.numbersLow.handler); move->events.numbersHigh.handler = aieNumToHandler((sdword)move->events.numbersHigh.handler); move->events.fuelLow.handler = aieNumToHandler((sdword)move->events.fuelLow.handler); move->events.fuelHigh.handler = aieNumToHandler((sdword)move->events.fuelHigh.handler); move->events.shipDied.handler = (aieHandlerShip)aieNumToHandler((sdword)move->events.shipDied.handler); move->events.teamDied.handler = aieNumToHandler((sdword)move->events.teamDied.handler); move->events.interrupt.handler = (aieHandlerInt)aieNumToHandler((sdword)move->events.interrupt.handler); if (move->events.interrupt.handler) { dbgAssert(move->events.interrupt.intvar < sizeof(AIPlayer)); move->events.interrupt.intvar = (udword *)( ((ubyte *)fixingThisAIPlayer) + ((sdword)move->events.interrupt.intvar) ); } } #pragma warning( 2 : 4047) // turn back on "different levels of indirection warning"