Files
source/TacticalAI/Medical.cpp
T
Overhaul 7a27e7105d New attack busy system
Added missing #includes

Added LUA scripting and console

Changed the way tracers are visualized, so they work more like real tracers instead of a light fountain

Fixed signedness of many grid variables used in GetMouseMapPos calls

Fixed enemy weapon choosing:  Sometimes a mortar is chosen but later rejected, but the grenade class was not reset.  Caused assertion failure

Added checks so enemies don't try to chuck RPG grenades with their hands

Now possible to shoot a someone in the head if he's in water

Fixed InitSightArrays to also clear soldier interrupt duel points.  This was causing an assertion failure elsewhere in the code because the interrupt list still had soldiers on it sometimes when this function was called.

Soldiers are much less willing to forfeit their turn over an attempt to use more APs than they have

Removed early setting of muzzle flash.  This would allow enemies to get an interrupt before you even fired.

Fixed item dropping by AI.  If AI tried to drop something while standing it would cause deadlock

Change to greatly speed up closing the sector inventory window in an unloaded sector

Added conditional compile flag to always give robot weapon ready advantage, even for 360 degree sighting


Check builddefines.h for the conditional flags.  Of greatest interest might be LUA_CONSOLE.  This will cause the game to bring up a command console when run.  However this console is severely lacking in many areas.  If anybody knows of an open-source terminal/console that could be used instead, it would be appreciated.  The existing console does bad things when you try to close it, and since it counts as a separate app, it pauses the game while it has focus.

LUA scripting is very limited, basically just proof of concept.  There is one global variable, the Soldiers array.  An array index gives you the soldier in that slot in the currently loaded sector.  The soldier has a few things that can be accessed:  name (short name); fullname (long name); grid (current grid #, can be changed); walkto(grid) (function to walk to another grid); runto(grid) (function to run to another grid)


git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@924 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2007-06-09 09:01:04 +00:00

462 lines
12 KiB
C++

#ifdef PRECOMPILEDHEADERS
#include "AI All.h"
#else
#include "types.h"
#include "wcheck.h"
#include "Soldier Control.h"
#include "Soldier Functions.h"
#include "ai.h"
#include "AIInternals.h"
#include "Animation Control.h"
#include "pathai.h"
#include "overhead.h"
#include "items.h"
#include "Message.h"
#include "Buildings.h"
#include "worldman.h"
#include "Assignments.h"
#endif
extern BOOLEAN gfAutoBandageFailed;
//
// This file contains code devoted to the player AI-controlled medical system. Maybe it
// can be used or adapted for the enemies too...
//
#define NOT_GOING_TO_DIE -1
#define NOT_GOING_TO_COLLAPSE -1
// can this grunt be bandaged by a teammate?
BOOLEAN CanCharacterBeAutoBandagedByTeammate( SOLDIERTYPE *pSoldier );
//c an this grunt help anyone else out?
BOOLEAN CanCharacterAutoBandageTeammate( SOLDIERTYPE *pSoldier );
BOOLEAN FindAutobandageClimbPoint( INT16 sDesiredGridNo, BOOLEAN fClimbUp )
{
// checks for existance of location to climb up to building, not occupied by a medic
BUILDING * pBuilding;
UINT8 ubNumClimbSpots;
UINT8 ubLoop;
UINT8 ubWhoIsThere;
pBuilding = FindBuilding( sDesiredGridNo );
if (!pBuilding)
{
return( FALSE );
}
ubNumClimbSpots = pBuilding->ubNumClimbSpots;
for ( ubLoop = 0; ubLoop < ubNumClimbSpots; ubLoop++ )
{
ubWhoIsThere = WhoIsThere2( pBuilding->sUpClimbSpots[ ubLoop ], 1 );
if ( ubWhoIsThere != NOBODY && !CanCharacterAutoBandageTeammate( MercPtrs[ ubWhoIsThere ] ) )
{
continue;
}
ubWhoIsThere = WhoIsThere2( pBuilding->sDownClimbSpots[ ubLoop ], 0 );
if ( ubWhoIsThere != NOBODY && !CanCharacterAutoBandageTeammate( MercPtrs[ ubWhoIsThere ] ) )
{
continue;
}
return( TRUE );
}
return( FALSE );
}
BOOLEAN FullPatientCheck( SOLDIERTYPE * pPatient )
{
UINT8 cnt;
SOLDIERTYPE * pSoldier;
if ( CanCharacterAutoBandageTeammate( pPatient ) )
{
// can bandage self!
return( TRUE );
}
if ( pPatient->bLevel != 0 )
{ // look for a clear spot for jumping up
// special "closest" search that ignores climb spots IF they are occupied by non-medics
return( FindAutobandageClimbPoint( pPatient->sGridNo, TRUE ) );
}
else
{
// run though the list of chars on team
cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++,pSoldier++)
{
// can this character help out?
if ( CanCharacterAutoBandageTeammate( pSoldier ) == TRUE )
{
// can this guy path to the patient?
if ( pSoldier->bLevel == 0 )
{
// do a regular path check
if ( FindBestPath( pSoldier, pPatient->sGridNo, 0, WALKING, NO_COPYROUTE, PATH_THROUGH_PEOPLE ) )
{
return( TRUE );
}
}
else
{
// if on different levels, assume okay
return( TRUE );
}
}
}
}
return( FALSE );
}
BOOLEAN CanAutoBandage( BOOLEAN fDoFullCheck )
{
// returns false if we should stop being in auto-bandage mode
UINT8 cnt;
UINT8 ubMedics = 0, ubPatients = 0;
SOLDIERTYPE * pSoldier;
static UINT8 ubIDForFullCheck = NOBODY;
// run though the list of chars on team
cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++,pSoldier++)
{
// can this character help out?
if( CanCharacterAutoBandageTeammate( pSoldier ) == TRUE )
{
// yep, up the number of medics in sector
ubMedics++;
}
}
if ( ubMedics == 0 )
{
// no one that can help
return( FALSE );
}
cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++,pSoldier++)
{
// can this character be helped out by a teammate?
if ( CanCharacterBeAutoBandagedByTeammate( pSoldier ) == TRUE )
{
// yep, up the number of patients awaiting treatment in sector
ubPatients++;
if (fDoFullCheck)
{
if ( ubIDForFullCheck == NOBODY )
{
// do this guy NEXT time around
ubIDForFullCheck = cnt;
}
else if ( cnt == ubIDForFullCheck )
{
// test this guy
if ( FullPatientCheck( pSoldier ) == FALSE )
{
// shit!
gfAutoBandageFailed = TRUE;
return( FALSE );
}
// set ID for full check to NOBODY; will be set to someone later in loop, or to
// the first guy on the next pass
ubIDForFullCheck = NOBODY;
}
}
}
// is this guy REACHABLE??
}
if ( ubPatients == 0 )
{
// there is no one to help
return( FALSE );
}
else
{
// got someone that can help and help wanted
return( TRUE );
}
}
BOOLEAN CanCharacterAutoBandageTeammate( SOLDIERTYPE *pSoldier )
// can this soldier autobandage others in sector
{
// if the soldier isn't active or in sector, we have problems..leave
if ( !(pSoldier->bActive) || !(pSoldier->bInSector) || ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) || (pSoldier->bAssignment == VEHICLE ) )
{
return( FALSE );
}
// they must have oklife or more, not be collapsed, have some level of medical competence, and have a med kit of some sort
if ( (pSoldier->bLife >= OKLIFE) && !(pSoldier->bCollapsed) && (pSoldier->bMedical > 0) && (FindObjClass( pSoldier, IC_MEDKIT ) != NO_SLOT) )
{
return( TRUE );
}
return( FALSE );
}
// can this soldier autobandage others in sector
BOOLEAN CanCharacterBeAutoBandagedByTeammate( SOLDIERTYPE *pSoldier )
{
// if the soldier isn't active or in sector, we have problems..leave
if ( !(pSoldier->bActive) || !(pSoldier->bInSector) || ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) || (pSoldier->bAssignment == VEHICLE ) )
{
return( FALSE );
}
if ( (pSoldier->bLife > 0) && (pSoldier->bBleeding > 0) )
{
// someone's bleeding and not being given first aid!
return( TRUE );
}
return( FALSE );
}
INT8 FindBestPatient( SOLDIERTYPE * pSoldier, BOOLEAN * pfDoClimb )
{
UINT8 cnt, cnt2;
INT16 bBestPriority = 0, sBestAdjGridNo;
INT16 sPatientGridNo, sBestPatientGridNo;
INT16 sShortestPath = 1000, sPathCost, sOtherMedicPathCost;
SOLDIERTYPE * pPatient;
SOLDIERTYPE * pBestPatient = NULL;
SOLDIERTYPE * pOtherMedic;
INT8 bPatientPriority;
UINT8 ubDirection;
INT16 sAdjustedGridNo, sAdjacentGridNo, sOtherAdjacentGridNo;
INT16 sClimbGridNo, sBestClimbGridNo = NOWHERE, sShortestClimbPath = 1000;
BOOLEAN fClimbingNecessary;
gubGlobalPathFlags = PATH_THROUGH_PEOPLE;
// search for someone who needs aid
cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID;
for ( pPatient = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; cnt++,pPatient++)
{
if ( !(pPatient->bActive) || !(pPatient->bInSector) )
{
continue; // NEXT!!!
}
if (pPatient->bLife > 0 && pPatient->bBleeding && pPatient->ubServiceCount == 0)
{
if (pPatient->bLife < OKLIFE)
{
bPatientPriority = 3;
}
else if (pPatient->bLife < OKLIFE * 2)
{
bPatientPriority = 2;
}
else
{
bPatientPriority = 1;
}
if (bPatientPriority >= bBestPriority)
{
if ( !ClimbingNecessary( pSoldier, pPatient->sGridNo, pPatient->bLevel ) )
{
sPatientGridNo = pPatient->sGridNo;
sAdjacentGridNo = FindAdjacentGridEx( pSoldier, sPatientGridNo, &ubDirection, &sAdjustedGridNo, FALSE, FALSE );
if ( sAdjacentGridNo == -1 && gAnimControl[ pPatient->usAnimState ].ubEndHeight == ANIM_PRONE )
{
// prone; could be the base tile is inaccessible but the rest isn't...
for ( cnt2 = 0; cnt2 < NUM_WORLD_DIRECTIONS; cnt2++ )
{
sPatientGridNo = pPatient->sGridNo + DirectionInc( cnt2 );
if ( WhoIsThere2( sPatientGridNo, pPatient->bLevel ) == pPatient->ubID )
{
// patient is also here, try this location
sAdjacentGridNo = FindAdjacentGridEx( pSoldier, sPatientGridNo, &ubDirection, &sAdjustedGridNo, FALSE, FALSE );
if ( sAdjacentGridNo != -1 )
{
break;
}
}
}
}
if (sAdjacentGridNo != -1)
{
if (sAdjacentGridNo == pSoldier->sGridNo)
{
sPathCost = 1;
}
else
{
sPathCost = PlotPath( pSoldier, sAdjacentGridNo, FALSE, FALSE, FALSE, RUNNING, FALSE, FALSE, 0);
}
if ( sPathCost != 0 )
{
// we can get there... can anyone else?
if ( pPatient->ubAutoBandagingMedic != NOBODY && pPatient->ubAutoBandagingMedic != pSoldier->ubID )
{
// only switch to this patient if our distance is closer than
// the other medic's
pOtherMedic = MercPtrs[ pPatient->ubAutoBandagingMedic ];
sOtherAdjacentGridNo = FindAdjacentGridEx( pOtherMedic, sPatientGridNo, &ubDirection, &sAdjustedGridNo, FALSE, FALSE );
if (sOtherAdjacentGridNo != -1)
{
if (sOtherAdjacentGridNo == pOtherMedic->sGridNo)
{
sOtherMedicPathCost = 1;
}
else
{
sOtherMedicPathCost = PlotPath( pOtherMedic, sOtherAdjacentGridNo, FALSE, FALSE, FALSE, RUNNING, FALSE, FALSE, 0);
}
if (sPathCost >= sOtherMedicPathCost)
{
// this patient is best served by the merc moving to them now
continue;
}
}
}
if (bPatientPriority == bBestPriority)
{
// compare path distances
if ( sPathCost > sShortestPath )
{
continue;
}
}
sShortestPath = sPathCost;
pBestPatient = pPatient;
sBestPatientGridNo = sPatientGridNo;
bBestPriority = bPatientPriority;
sBestAdjGridNo = sAdjacentGridNo;
}
}
}
else
{
sClimbGridNo = NOWHERE;
// see if guy on another building etc and we need to climb somewhere
sPathCost = EstimatePathCostToLocation( pSoldier, pPatient->sGridNo, pPatient->bLevel, FALSE, &fClimbingNecessary, &sClimbGridNo );
// if we can get there
if ( sPathCost != 0 && fClimbingNecessary && sPathCost < sShortestClimbPath )
{
sBestClimbGridNo = sClimbGridNo;
sShortestClimbPath = sPathCost;
}
}
}
}
}
gubGlobalPathFlags = 0;
if (pBestPatient)
{
if (pBestPatient->ubAutoBandagingMedic != NOBODY)
{
// cancel that medic
CancelAIAction( MercPtrs[ pBestPatient->ubAutoBandagingMedic ], TRUE );
}
pBestPatient->ubAutoBandagingMedic = pSoldier->ubID;
*pfDoClimb = FALSE;
if ( CardinalSpacesAway( pSoldier->sGridNo, sBestPatientGridNo ) == 1 )
{
pSoldier->usActionData = sBestPatientGridNo;
return( AI_ACTION_GIVE_AID );
}
else
{
pSoldier->usActionData = sBestAdjGridNo;
return( AI_ACTION_GET_CLOSER );
}
}
else if (sBestClimbGridNo != NOWHERE )
{
*pfDoClimb = TRUE;
pSoldier->usActionData = sBestClimbGridNo;
return( AI_ACTION_MOVE_TO_CLIMB );
}
else
{
return( AI_ACTION_NONE );
}
}
INT8 DecideAutoBandage( SOLDIERTYPE * pSoldier )
{
INT8 bSlot;
BOOLEAN fDoClimb;
if (pSoldier->bMedical == 0 || pSoldier->ubServicePartner != NOBODY)
{
// don't/can't make decision
return( AI_ACTION_NONE );
}
bSlot = FindObjClass( pSoldier, IC_MEDKIT );
if (bSlot == NO_SLOT)
{
// no medical kit!
return( AI_ACTION_NONE );
}
if (pSoldier->bBleeding)
{
// heal self first!
pSoldier->usActionData = pSoldier->sGridNo;
if (bSlot != HANDPOS)
{
pSoldier->bSlotItemTakenFrom = bSlot;
SwapObjs( &(pSoldier->inv[HANDPOS]), &(pSoldier->inv[bSlot]) );
/*
memset( &TempObj, 0, sizeof( OBJECTTYPE ) );
// move the med kit out to temp obj
SwapObjs( &TempObj, &(pSoldier->inv[bSlot]) );
// swap the med kit with whatever was in the hand
SwapObjs( &TempObj, &(pSoldier->inv[HANDPOS]) );
// replace whatever was in the hand somewhere in inventory
AutoPlaceObject( pSoldier, &TempObj, FALSE );
*/
}
return( AI_ACTION_GIVE_AID );
}
// pSoldier->usActionData = FindClosestPatient( pSoldier );
pSoldier->bAction = FindBestPatient( pSoldier, &fDoClimb );
if (pSoldier->bAction != AI_ACTION_NONE)
{
pSoldier->usUIMovementMode = RUNNING;
if (bSlot != HANDPOS)
{
pSoldier->bSlotItemTakenFrom = bSlot;
SwapObjs( &(pSoldier->inv[HANDPOS]), &(pSoldier->inv[bSlot]) );
}
return( pSoldier->bAction );
}
// do nothing
return( AI_ACTION_NONE );
}