/*******************************************************************
/*  AlignmentUnit.cpp
/*  Author: Vadim Berman
/*
/*  Description:
/*  implementation for Agent class and some Agent-related non-member
/*  functions
/*
/*  The contents of this file are subject to the Brainiac Public License.
/*  Version 1.0 (the "License"); you may not use this file except in
/*  compliance with the License. You may obtain a copy of the License at
/*  http://www.twilightminds.com
/*
/*  Software distributed under the License is distributed on an "AS IS"
/*  basis WITHOUT WARRANTY OF ANY KIND, either express or implied. See
/*  the License for the specific language governing rights and limitations
/*  under the License.
/*
/*  Copyright (C) 1999 Twilight Minds. All rights reserved.
/********************************************************************/

#include "AlignmentUnit.h"
#include <string.h>

#define INIT_TYPE(a) \
    at = (AnarchyType)(a.Anarchy / 50); \
	if( a.Wickedness / 45 > 1)          \
		a.alType = alUltimateEvil;      \
	else                                \
		if( a.Wickedness / 45 < -1)     \
			a.alType = alPureGood;      \
	wt = (WickednessType)(a.Wickedness / 50); \
	switch (wt){                              \
		case wGood:                           \
			switch (at){                      \
				case aLawful: 	a.alType = alLawfulGood; \
						break;                           \
				case aNeutral:	a.alType = alNeutralGood;\
						break;                           \
				case aChaotic:	a.alType = alChaoticGood;\
						break;                           \
			}                                            \
            break;                                       \
		case wNeutral:                                   \
			switch (at){                                 \
				case aLawful: 	a.alType = alLawfulNeutral;\
						break;                             \
				case aNeutral:	a.alType = alTrueNeutral;  \
						break;                             \
				case aChaotic:	a.alType = alChaoticNeutral;\
						break;                              \
			}                                               \
            break;                                          \
		case wEvil:                                         \
			switch (at){                                    \
				case aLawful: 	a.alType = alLawfulEvil;    \
						break;                              \
				case aNeutral:	a.alType = alNeutralEvil;   \
						break;                              \
				case aChaotic:	a.alType = alChaoticEvil;   \
						break;                              \
			}                                               \
	}

//-------------------------------------- Global variables -----------------------
extern GoalDefNodeP 		GoalListHead;
extern ActionDefNodeP		ActionListHead;
extern StrategyDefNodeP		StrategyListHead;
BOOL SoloAssumptionMode = TRUE, ActionsSoloMode = TRUE, UseSuddenChange = FALSE,
    UseHistory = TRUE;
extern char VerbalOutput[MAX_VERBAL_LEN];
char LastThought[MAX_VERBAL_LEN];
unsigned short nMaxFailures = 3, LastSelected = 0;
ID gItemAcquisition = GOAL_ITEM_ACQUISITION;
clock_t start_time;
EventsRegistry History;

unsigned short Agent::AutoNumber = 0;//autoincrementing counter
//--------------------------------------
//------Alternate values to refer ------
//--------------------------------------
Core *pbsAlternateInitiator = NULL;
Strategy    *pAlternateStrategy = NULL;
//--------------------------------------
Agent**   pbspCompPopulation = NULL;
unsigned short  nCompPopSize = 0;
ItemPtrArr*     ipapCompAllInv = NULL;
unsigned short* nCompInvSizes = NULL;
//--------------------------------------
BBECALL Agent::Agent()
{
    Init(0, 0);	AutoNumber++; LastThought[0] = '\0';
    Original.Dead = core.Dead = FALSE;
  	AutoNumber++;
    Original.attId = core.attId = AutoNumber;
    UseTrueStats = 0x7F;
    UseTrueAttitude = TRUE;
    core.LastAccessedBy = NULL;   Original.LastAccessedBy = NULL;
    core.CurrentEnvironment = NEUTRAL_ENV;
    core.LastTargettedInAction = 0;
    core.LastCommittedAction = 0;
    Original.CurrentEnvironment = NEUTRAL_ENV;
    Original.LastTargettedInAction = 0;
    Original.LastCommittedAction = 0;
    core.attPersonal = NULL;
    Original.attPersonal = NULL;
    actAssignTargetsTo = NULL;
    pbsAlternateInitiator = NULL;
    bBypassCheckExecute = FALSE;
    Initialized = FALSE;
}

//***************************************************************************
//Agent::Agent(percentage , percentage )
//********** Description: **************
//construct Agent object by the given Wickedness and Anarchy
//********** Parameters:  **************
//percentage WickednessArg - wickedness
//percentage AnarchyArg    - anarchy
//--------------------------------------
BBECALL Agent::Agent(percentage WickednessArg, percentage AnarchyArg)
{
	Init(WickednessArg, AnarchyArg); AutoNumber++; LastThought[0] = '\0';
    Original.Dead = core.Dead = FALSE;
    UseTrueStats = 0x7F;
    UseTrueAttitude = TRUE;
    core.LastAccessedBy = NULL;   Original.LastAccessedBy = NULL;
    Original.CurrentEnvironment = NEUTRAL_ENV;
    Original.LastTargettedInAction = 0;
    Original.LastCommittedAction = 0;
    actAssignTargetsTo = NULL;
    Original.attPersonal = NULL;
    core.attPersonal = NULL;
    pbsAlternateInitiator = NULL;
    pAlternateStrategy = NULL;
    bBypassCheckExecute = FALSE;
    Initialized = FALSE;
}

//--------------------------------------
BBECALL Agent::~Agent()
{
    CurrentStrategy.End();
	DelAttitudeTree(&(core.attPersonal));
}

//-----------------------------------------------------------------------
void BBECALL Agent::Init(percentage WickednessArg, percentage AnarchyArg)
{
    Original.Dead = FALSE;    //I hope...
	Original.Wickedness = WickednessArg; Original.Anarchy = AnarchyArg;
    core.Dead = FALSE;    //I hope...
	core.Wickedness = WickednessArg; core.Anarchy = AnarchyArg;
    InitType();
    UpdateSecondaryVars();
    FailuresCount = 0;
}

//***************************************************************************
//Agent::InitType
//********** Description: **************
//initialize Agent Core attributes by the selected type
//********** Parameters:  **************
//none
//--------------------------------------
void BBECALL Agent::InitType()
{
	WickednessType wt;
	AnarchyType    at;
    MindDefNodeP   mp;
    if(core.alType > CUSTOM_MINDSET_START)
    {
        mp = GetMindNode((ID)(core.alType - CUSTOM_MINDSET_START));
        if(mp != NULL )
        {
	        core.SuddenChange = mp->SuddenChange;
	        core.AttitudeChangeUnit = mp->AttitudeChangeUnit;
            core.InitialAttitude = mp->InitialAttitude;
            if(!Initialized)
            {
	            Original.SuddenChange = mp->SuddenChange;
	            Original.AttitudeChangeUnit = mp->AttitudeChangeUnit;
                Original.InitialAttitude = mp->InitialAttitude;
            }
            return;
        }
    }

    INIT_TYPE(core)
    //these are macros. Forget about ";" but don't delete "{ }"
    if(!Initialized)
    {
        INIT_TYPE(Original)
    }
}

//***************************************************************************
//Agent::GetAlName
//********** Description: **************
//return a character string containing the name
//********** Parameters:  **************
//none
//--------------------------------------
char* BBECALL Agent::GetAlName()
{
    AlignmentType at = GetAlType();
	switch (at){
		case alUltimateEvil:
            return "ultimate evil";
		case alPureGood:
            return "pure good";
		case alLawfulGood:
            return "lawful good";
		case alNeutralGood:
            return "neutral good";
		case alChaoticGood:
            return "chaotic good";
		case alLawfulNeutral:
            return "lawful neutral";
		case alTrueNeutral:
            return "true neutral";
		case alChaoticNeutral:
            return "chaotic neutral";
		case alLawfulEvil:
            return "lawful evil";
		case alNeutralEvil:
            return "neutral evil";
		case alChaoticEvil:
            return "chaotic evil";
        default:
            return GetMindDesc((ID)(at - CUSTOM_MINDSET_START));
	}
}

//***************************************************************************
//Agent::UpdateSecondaryVars
//********** Description: **************
//calculate some of the Core attributes according to the basic ones
//********** Parameters:  **************
//none
//--------------------------------------
void BBECALL Agent::UpdateSecondaryVars()
{
    MindDefNodeP pThisMindset = NULL;
    //lie is the evil weapon of those who has no respect for the law...
    //...and it doesn't depend on custom mindset definition
	if(core.Anarchy > 0)
        core.LieProbability = (percentage)(core.Wickedness > 0 ? core.Wickedness/4 + 10 : 10);
	else
        core.LieProbability = 10;

    if(core.alType > CUSTOM_MINDSET_START)
    //try to find the matching node
        pThisMindset = GetMindNode(core.alType);
    if(pThisMindset != NULL)
    {
	    core.SuddenChange = pThisMindset->SuddenChange;
	    core.AttitudeChangeUnit = pThisMindset->AttitudeChangeUnit;
        core.InitialAttitude = pThisMindset->InitialAttitude;
    }
    else
    {
	    core.SuddenChange
            = (percentage)(core.Anarchy > 0 ? core.Anarchy / 3 + 10 : 6);
	    core.AttitudeChangeUnit
            = (percentage)(core.Anarchy > 0 ? core.Anarchy / 3 + 10 : 10);
        core.InitialAttitude = (percentage)(-(float)core.Wickedness/1.75);
	    if (core.InitialAttitude > 70)
		    core.InitialAttitude = 70;
	    if (core.InitialAttitude < -90)
		    core.InitialAttitude = -90;
    }
    SetCoreDefaults(core);

    Original.LieProbability = core.LieProbability;
	Original.SuddenChange = core.SuddenChange;
	Original.AttitudeChangeUnit = core.AttitudeChangeUnit;
    Original.InitialAttitude = core.InitialAttitude;
    SetCoreDefaults(Original);
}

//***************************************************************************
//Agent::InitialAttitudeModification
//********** Description: **************
//calculate Agent's initial attitude modification by the given other Agent's Core
//********** Parameters:  **************
//Core& OtherGuy         - reference to the evaluated Agent's Core
//unsigned char UseWhich - bitmap containing the info on which attributes to use
//----------------------------------------------------------------------
percentage BBECALL Agent::InitialAttitudeModification(Core& OtherGuy,
        unsigned char UseWhich)
{
    MindDefNodeP pThisMindset = NULL;
	short AttitudeModify;
    unsigned char ImprOffset[3]; //which score to choose, Seemed (0) or True (1)
    ImprOffset[0] = (unsigned char)(1 & UseWhich);  //this one is for Meanness
    ImprOffset[1] = (unsigned char)(2 & UseWhich);  //... for Wealth
//        ImprOffset[2] = 4 & UseWhich;  //... for Foreignness

    if(core.alType > CUSTOM_MINDSET_START)
        pThisMindset = GetMindNode(core.alType);
    if(pThisMindset != NULL)
    {
        AttitudeModify =
                 (short)((
                 (OtherGuy.Meanness[ ImprOffset[0] ] - core.Meanness[0])
                    * pThisMindset->Meanness/100
                 + ABS(OtherGuy.Meanness[ ImprOffset[0] ] - core.Meanness[0])
                    * pThisMindset->AbsMeanness/100
                 + (OtherGuy.Wealth[ ImprOffset[1] ] - core.Wealth[ ImprOffset[1] ])
                    * pThisMindset->Wealth/100
                 + ABS(OtherGuy.Wealth[ ImprOffset[1] ] - core.Wealth[ ImprOffset[1] ])
                    * pThisMindset->AbsWealth/100
                 + (OtherGuy.Beauty - core.Beauty)
                    * pThisMindset->Beauty/100
                 + ABS(OtherGuy.Beauty - core.Beauty)
                    * pThisMindset->AbsBeauty/100
                 + ABS(OtherGuy.Foreignness - core.Foreignness)
                    * pThisMindset->Foreignness/100
                 + (OtherGuy.Intelligence - core.Intelligence)
                    * pThisMindset->Intelligence/100
                 + ABS(OtherGuy.Intelligence - core.Intelligence)
                    * pThisMindset->AbsIntelligence/100
                 ) * core.AttitudeChangeUnit / 100);
    }
    else
	    switch (core.alType){
        case alPureGood:
            AttitudeModify =
                 (- OtherGuy.Meanness[ ImprOffset[0] ] * 0.2 * core.AttitudeChangeUnit + OtherGuy.Beauty * 0.3 * core.AttitudeChangeUnit + OtherGuy.Intelligence[0] * 0.5 * core.AttitudeChangeUnit)/100;
				break;
		case alLawfulGood:
            AttitudeModify =
                 (- OtherGuy.Meanness[ ImprOffset[0] ] * 0.2 * core.AttitudeChangeUnit + OtherGuy.Beauty * 0.3 * core.AttitudeChangeUnit + OtherGuy.Intelligence[0] * 0.5 * core.AttitudeChangeUnit)/100;
				break;
		case alNeutralGood:
            AttitudeModify =
                 (- OtherGuy.Meanness[ ImprOffset[0] ] * 0.2 * core.AttitudeChangeUnit + OtherGuy.Beauty * 0.3 * core.AttitudeChangeUnit + OtherGuy.Intelligence[0] * 0.5 * core.AttitudeChangeUnit)/100;
				break;
		case alChaoticGood:
            AttitudeModify =
                 (-OtherGuy.Meanness[ ImprOffset[0] ] * 0.5 * core.AttitudeChangeUnit
                 - OtherGuy.Wealth[ ImprOffset[1] ] * 0.2 * core.AttitudeChangeUnit
                 - ABS(OtherGuy.Foreignness - core.Foreignness) * 0.1 * core.AttitudeChangeUnit
                 + OtherGuy.Beauty * 0.1 * core.AttitudeChangeUnit)/100;
				break;
		case alLawfulNeutral:
            AttitudeModify =
               (- OtherGuy.Meanness[ ImprOffset[0] ] * 0.2 * core.AttitudeChangeUnit + OtherGuy.Wealth[ ImprOffset[1] ] * 0.4 * core.AttitudeChangeUnit + OtherGuy.Beauty * 0.3 * core.AttitudeChangeUnit + OtherGuy.Intelligence[0] * 0.1 * core.AttitudeChangeUnit) / 100;
				break;
		case alTrueNeutral:
            AttitudeModify = 0;
				break;
		case alChaoticNeutral:
            AttitudeModify =
            	(- OtherGuy.Meanness[ ImprOffset[0] ] * 1.2 * core.AttitudeChangeUnit - OtherGuy.Wealth[ ImprOffset[1] ] * 0.4 * core.AttitudeChangeUnit + OtherGuy.Beauty * 0.3 * core.AttitudeChangeUnit + OtherGuy.Intelligence[0] * 0.1 * core.AttitudeChangeUnit - (OtherGuy.Foreignness - core.Foreignness) * core.AttitudeChangeUnit) / 100;
				break;
		case alLawfulEvil:
            AttitudeModify =
                 (- ABS(OtherGuy.Meanness[ ImprOffset[0] ]
                            - core.Meanness[0]) * 0.2
                 - ABS(OtherGuy.Wealth[ ImprOffset[1] ]
                            - core.Wealth[ ImprOffset[1] ]) * 0.4
                 - ABS(OtherGuy.Foreignness - core.Foreignness) * 0.1
                 - ABS(OtherGuy.Beauty - core.Beauty) * 0.1) * core.AttitudeChangeUnit / 100;
				break;
		case alNeutralEvil:
            AttitudeModify =
                 (- ABS(OtherGuy.Meanness[ ImprOffset[0] ] - core.Meanness[0]) * 0.2
                 - ABS(OtherGuy.Wealth[ ImprOffset[1] ] - core.Wealth[ ImprOffset[1] ]) * 0.1
                 - ABS(OtherGuy.Foreignness - core.Foreignness) * 0.1
                 - ABS(OtherGuy.Beauty - core.Beauty) * 0.1)
                 * core.AttitudeChangeUnit / 100;
				break;
		case alChaoticEvil:
            AttitudeModify =
                 (- ABS(OtherGuy.Meanness[ ImprOffset[0] ] - core.Meanness[0]) * 0.2
                 - ABS(OtherGuy.Wealth[ ImprOffset[1] ] - core.Wealth[ ImprOffset[1] ]) * 0.4
                 - ABS(OtherGuy.Foreignness - core.Foreignness) * 0.2
                 - ABS(OtherGuy.Beauty - core.Beauty) * 0.1) * core.AttitudeChangeUnit / 100;
				break;
		case alUltimateEvil:
            AttitudeModify =
                 (- ABS(OtherGuy.Meanness[ ImprOffset[0] ] - core.Meanness[0]) * 0.2
                 - ABS(OtherGuy.Wealth[ ImprOffset[1] ] - core.Wealth[ ImprOffset[1] ]) * 0.4
                 - ABS(OtherGuy.Foreignness - core.Foreignness) * 0.1
                 - ABS(OtherGuy.Beauty - core.Beauty) * 0.1) * core.AttitudeChangeUnit/100;
				break;
	    }
    if(core.InitialAttitude + AttitudeModify < -EXTREME_PER_VAL)
        AttitudeModify = (short)(-EXTREME_PER_VAL - core.InitialAttitude);
    if(core.InitialAttitude + AttitudeModify > EXTREME_PER_VAL)
        AttitudeModify = (short)(EXTREME_PER_VAL - core.InitialAttitude);

	return (percentage)AttitudeModify;
}

//***************************************************************************
//Agent::AttitudeToItem
//********** Description: **************
//calculate Agent's attitude to an inventory item
//********** Parameters:  **************
//ItemScores& EvalItem - reference to the evaluated item
//----------------------------------------------------------------------
percentage BBECALL Agent::AttitudeToItem(ItemScores& EvalItem)
{
    return ItemPersonalEval(core,EvalItem);
}

//***************************************************************************
//Agent::Scan
//********** Description: **************
//scan another agent for possible strategy engagement / action target assignment.
//Return TRUE if the scanned person or his/her items cause to adopt a new strategy
//or to initiate an action, FALSE otherwise.
//********** Parameters:  **************
//Agent *paScanned            - pointer to the scanned Agent
//ItemPtrArr Items            - pointer to the array of the Agent's item pointers
//unsigned short ItemArrayLen - length of the above Items array
//ID ScanGoalItselfArg        - scan goal ID. Default is GOAL_NONE
//------------------------------------------------------------------------------
BOOL BBECALL Agent::Scan(Agent *paScanned,ItemPtrArr Items,
		unsigned short ItemArrayLen, ID ScanGoalItselfArg)
{
    percentage NewImportance,ScannedAtt,EvalAtt;
    Goal NewGoal, ScanGoal;
    GoalDefNodeP	gp;
    Strategy *NewStrategy = NULL;
    BOOL byNotFamiliar, bRetVal = FALSE;
    Core bsScanned,*pbsScanned;
    unsigned short i;

    PostError(0);//for now, everything is OK

    if(paScanned == NULL)
    {
        PostError(INVALID_PARAM);
        return FALSE;
    }

    pbsScanned = paScanned->GetCoreAddress();//we need only core

    ScanGoal.Itself = ScanGoalItselfArg;

    if (ScanGoal.Itself == GOAL_NONE)
        ScanGoal.Importance = 0;
    else
    {
        gp = GetGoalNode(ScanGoal.Itself);
        if (gp == NULL)
            ScanGoal.Importance = 0;
        else
            ScanGoal.Importance = gp->goal.Importance;
    }

    bsScanned = *pbsScanned; //Make local copy of the original struct
                            //so nobody would get hurt...
    if(CreateNewAttitude(bsScanned))
        byNotFamiliar = TRUE;
    else
        byNotFamiliar = FALSE;

   //get the mutual attitudes
    ScannedAtt = GetAttitude(core.attId,pbsScanned->attPersonal);
    EvalAtt    = AttitudeTo(bsScanned);
   //bsScanned.InitialAttitude and bsScanned.AttitudeChangeUnit
   // are used to pass attitudes.
   // if the "not politically correct" problem persists,
   // I'll change it to flags or something more appropriate...
    bsScanned.InitialAttitude = EvalAtt;
    bsScanned.AttitudeChangeUnit = ScannedAtt;

    if(ScanGoal.Itself != GOAL_NONE)
    {
        if (ScanGoal.Importance == 0 && gp != NULL)
            bRetVal = ScanForCriterion(paScanned,Items,
                ItemArrayLen, gp);
        else
                //this part is used for manually operated scan
                if(PickStrategy(gp->goal,pbsScanned))
                    return TRUE;
    }

    switch(ScanGoal.Itself){
      case GOAL_NONE:
        ////////// the most difficult and interesting case
      	for (gp = GoalListHead; gp != NULL; gp = gp->next)
        {
            if ( gp->goal.Importance == 0 )   //skip criteria records
                continue;
        	if ( VerifyCriteria(gp, &bsScanned) )
            {
                if(PickStrategy(gp->goal,pbsScanned))
                {
                    bRetVal = TRUE;
                }
            }
        }

        bsScanned.InitialAttitude = pbsScanned->InitialAttitude;  //return original attitude

        NewGoal.Itself = gItemAcquisition;
         //scanning item
        for ( i = 0; i < ItemArrayLen; i++)
        {
            if(Items[i] == NULL)    //can it fail here?
                continue;
            NewImportance = ItemAcquisitionImportance(*(Items[i]));
         	if ( IsNewStrategyMoreImportant(NewImportance)
                && (NewImportance > MIN_IMPORTANCE)
                )
            //so a character wouldn't want to buy everything in sight
            //once s/he has no active goal
            {
                NewGoal.Importance = NewImportance;
                //ItemAcquisitionImportance returns importance of this item
                if(PickStrategy(NewGoal,pbsScanned,Items[i]))
                {
                    bRetVal = TRUE;
                }
            }
        }

        break;
    }//end switch

    if(!bRetVal)
        sprintf(LastThought,"I feel %s towards this person. Nothing I could or wanted to do with that. ",
            GetAttitudeDesc(GetAttitude(pbsScanned->attId,core.attPersonal)));

   //arbitrary: should the attitudes leading nowhere be deleted?
    if(byNotFamiliar && !bRetVal)    //delete the temporary node
        DelAttitude(pbsScanned->attId, &(core.attPersonal));

    if(NewStrategy != NULL)
    {
        NewStrategy->End();
        delete NewStrategy;
    }

    return bRetVal;
}

//***************************************************************************
//Agent::PickStrategy
//********** Description: **************
//attempt to find a Strategy to accomplish the given goal, suitable for the current
//Agent
//Return TRUE if the scanned person or his/her items cause to adopt a new strategy
//or to initiate an action, FALSE otherwise.
//********** Parameters:  **************
//Goal StrGoal       - goal to search Strategy for
//Core *pbsObj       - pointer to the Objective's Core
//ItemScores *pisObj - pointer to the Objective Item
//----------------------------------------------------------------------------
BOOL BBECALL Agent::PickStrategy(Goal StrGoal, Core *pbsObj,
        ItemScores *pisObj)
{
    Strategy *NewStrategy = NULL;
    StrategyDefNodeP sp;
    percentage NewImportance;

    for( sp = StrategyListHead ; sp != NULL ; sp = sp->next )
    {
        if (sp->TheGoal.Itself == StrGoal.Itself
                && sp->_Type == STRTYPE_REGULAR)
        {
            if(sp->MaxIntel < core.Intelligence[1])
                continue;
            NewStrategy = CreateStrategy( sp );
            if(NewStrategy == NULL)
                continue;
            NewStrategy->SetObjective( pbsObj );
            if(pisObj != NULL)
                NewStrategy->SetObjItem( pisObj );
            //if NULL... so be it
            //important to set item there as actions
            //might use it as reference
            NewImportance = NewStrategy->GetImportance();

        //to use this strategy, 3 conditions must be fulfilled:
        //(1) the cached population must contain needed candidates
        //(2) the strategy must be more important than the current
        //(3) it must be theoretically possible, from the initiator's
        //point of view, to complete it
            if(!WasStrategyFailure(NewStrategy)
                    && PrimeAllActionTargets(NewStrategy)
                    && IsNewStrategyMoreImportant(NewImportance)
                    && NewStrategy->PossibilityCheck())
            {
                CurrentStrategy.End();
                CurrentStrategy = *NewStrategy;
                CurrentStrategy.ResetAllActionTargets();
                delete NewStrategy;
                if(pisObj == NULL)
                    sprintf(LastThought,"I think I'll try %s on this person. ",
                        sp->Name);
                else
                    sprintf(LastThought,"I think I'll try %s to obtain an item in this person's possession. ",
                        sp->Name);
                //tests must be done to find out if there is memory leak here
                return TRUE;
            }
            else
            {
                NewStrategy->End();
                delete NewStrategy;
                NewStrategy = NULL;
            }
        }
    }

    if(NewStrategy != NULL)
    {
        NewStrategy->End();
        delete NewStrategy;
    }
    return FALSE;
}

//***************************************************************************
//Agent::WasStrategyFailure
//********** Description: **************
//check whether the given Strategy was attempted to adopt and failed in the past
//********** Parameters:  **************
//Strategy *ToCheck - pointer to the strategy to check
//----------------------------------------------------------------------------
BOOL BBECALL Agent::WasStrategyFailure(Strategy *ToCheck)
{
    if(!UseHistory)
        return FALSE;
    if(ToCheck != NULL && ToCheck->GetObjective() != NULL)
    {
        //check history: whether the strategy was accomplished before
        //and failed
        if(History.IsEventRegistered(core.attId,ToCheck->GetID(),
                    ToCheck->GetObjective()->attId,
                    STRATEGY_ITEMID(*ToCheck),
                    STRATEGY_FAILURE))
            return TRUE;

    }
    return FALSE;
}

//***************************************************************************
//Agent::ScanForCriterion
//********** Description: **************
//perform scan for the specified criterion
//********** Parameters:  **************
//Agent *paScanned            - pointer to the scanned Agent
//ItemPtrArr Items            - pointer to the array of the Agent's item pointers
//unsigned short ItemArrayLen - length of the above Items array
//GoalDefNodeP gp             - pointer to the criterion definition node
//----------------------------------------------------------------------------
BOOL BBECALL Agent::ScanForCriterion(Agent *paScanned,
        ItemPtrArr Items,
		unsigned short ItemArrayLen, GoalDefNodeP gp)
{
    BOOL bRetVal = FALSE;
    unsigned short nVerCritRV;
    Core bsScanned, *pbsScanned,*ActToAssignInit;
    ActionError aeCheck;

    PostError(0);
    if(gp == NULL || paScanned == NULL || actAssignTargetsTo == NULL)
    {
        PostError(INVALID_PARAM);
        return FALSE;
    }

    pbsScanned = paScanned->GetCoreAddress();
    bsScanned = *pbsScanned;
    if(pbsAlternateInitiator != NULL)
        ActToAssignInit = pbsAlternateInitiator;
    else
        ActToAssignInit = &core;

    //we're looking for target suiting our criteria
    nVerCritRV = VerifyCriteria(gp, &bsScanned, Items, ItemArrayLen);
    if ( nVerCritRV )
    {
        if(actAssignTargetsTo != NULL)
        {
        //targets are set here. That's the easiest way
            if(gp->goal.Itself < ITEM_CRITERIA_RECORDS_START)
                actAssignTargetsTo->InitTargets(ActToAssignInit,
                                    pbsScanned,
                                    NULL);
            else
                actAssignTargetsTo->InitTargets(ActToAssignInit,
                                    pbsScanned,
                                    Items[nVerCritRV-1]);

            if(!bBypassCheckExecute)
            {
                aeCheck = actAssignTargetsTo->Execute(NULL, TRUE,
                                UseTrueStats);
                if ( aeCheck ==
                        ACTION_SUCCESS || aeCheck == ACTION_NOT_PRIMED )
                    bRetVal = TRUE;
                else
                    bRetVal = FALSE;
            }
            else
                bRetVal = TRUE;
        }
    }

    return bRetVal;

}

//***************************************************************************
//Agent::FindCriterionMatch
//********** Description: **************
//perform scan of the given population for the specified criterion
//********** Parameters:  **************
//Agent **Candidates           - array of pointers to the Agents being scanned
//unsigned short nArrLen       - the above array length
//ItemPtrArr *Items            - pointer to the arrays of the Agents' item pointers
//unsigned short *ItemArrayLen - lengths of the above Items arrays
//ID ScanGoal                  - scanned goal ID
//percentage PassedRestrict    - candidate's restrictions
//----------------------------------------------------------------------------
long BBECALL Agent::FindCriterionMatch(Agent **Candidates,unsigned short nArrLen,
        ItemPtrArr *Items,
		unsigned short *ItemArrayLen, ID ScanGoal, percentage PassedRestrict)
{
    unsigned short i;
    unsigned char Restrict;
    Core *ObjBs;
    GoalDefNodeP gp;

    gp = GetGoalNode(ScanGoal);
    if(gp == NULL)
    {
        PostError(INVALID_PARAM);
        return -1;
    }

    if(PassedRestrict == -1)
    {
        if(actAssignTargetsTo)
            Restrict = actAssignTargetsTo->GetRefTAC();
        else
            Restrict = ALL_BUT_SELF;
    }
    else
        Restrict = PassedRestrict;

    for(i = 0; i < nArrLen; i++ )
    {
        if(Candidates[i] == NULL)
        //one or more of the candidates could be NULL - why not?
            continue;
        if(Restrict == ALL_BUT_SELF || Restrict == SELF_ONLY)
        {
            ObjBs = Candidates[i]->GetCoreAddress();
            if(ObjBs != NULL)
            {
                if(((ObjBs->attId == core.attId) && (Restrict == ALL_BUT_SELF)) ||
                        ((ObjBs->attId != core.attId) && (Restrict == SELF_ONLY)))
                    continue;
            }
        }

        if(Restrict == NOT_STR_OBJ)
    //Why do we need restrictions like that? Well, it's pretty obvious.
    //For example, what technically prevents from someone ordering a kill
    // to come down to the potential victim and ask: "You see, I'd like you
    // to eliminate this guy..." and point to him/herself?.. That's right,
    // NOTHING. That's what the restrictions are for
        {
            ObjBs = CurrentStrategy.GetObjective();
            if(ObjBs != NULL)
            {
                if(Candidates[i]->GetCore().attId == ObjBs->attId
                        ||
                        //don't forget about "disallow self"
                        Candidates[i]->GetCore().attId == core.attId)
                    continue;
            }
        }

        if(Restrict == NOT_LISTED_IN_STR)
        {
            ObjBs = Candidates[i]->GetCoreAddress();
            if(ObjBs != NULL)
                if(ObjBs->attId == core.attId
                //'cause we can't allow self too
                        || CurrentStrategy.IsListed(ObjBs->attId))
                    continue;
        }

        if(Items == NULL || ItemArrayLen == NULL)
        {
            if(ScanForCriterion(Candidates[i],NULL,0,
                    gp))
                return i;
        }
        else
            if(ScanForCriterion(Candidates[i],Items[i],ItemArrayLen[i],
                    gp))
                return i;
    }

    return -1;
}

//***************************************************************************
//Agent::ExecuteNextAction
//********** Description: **************
//execute next scheduled action, finding the needed targets and updating history,
//if used
//********** Parameters:  **************
//void *GenericArg               - parameter to the user-defined Action handler
//Agent **Candidates           - array of pointers to the Agents being scanned
//unsigned short nArrLen       - the above array length
//ItemPtrArr *Items            - pointer to the arrays of the Agents' item pointers
//unsigned short *ItemArrayLen - lengths of the above Items arrays
//--------------------------------------
ActionError BBECALL Agent::ExecuteNextAction(void *GenericArg,
        Agent **Candidates,unsigned short nArrLen,
        ItemPtrArr *Items,
        unsigned short *ItemArrayLen)
{
    ActionError ErrCode = NO_CANDIDATES;
    percentage ActWick, ActAnar, ActDiff, ActAttChg,
        NeutralSideMeanness,AttVal;
    ActionNodeP fail_goto;
    Action *pNextA;
    unsigned short i;

    VerbalOutput[0] = '\0';
    LastThought[0] = '\0';
    //catch this chance to cache TAC assumtion population, if none so far
    if(pbspCompPopulation == NULL && Candidates != NULL)
        SetDynamicTACAssumptionPopulation(Candidates,nArrLen,
            Items,ItemArrayLen);

    if ( CurrentStrategy.GetGoal().Itself == GOAL_NONE )
    {
    	return ACTION_NOT_PRIMED;
    }
    actAssignTargetsTo = CurrentStrategy.GetNextAction();
    pAlternateStrategy = NULL;

    if(actAssignTargetsTo == NULL || IsStrategyStillRelevant(Candidates,nArrLen,
        Items,ItemArrayLen) == FALSE)
    {
        CurrentStrategy.End();    //in case it wasn't called yet
    	return ACTION_NOT_PRIMED;
    }
    pNextA = actAssignTargetsTo;

    if(AssignTargetForNextAction(Candidates,nArrLen,Items,ItemArrayLen))
    {
        pNextA->GetActualScores(ActDiff, ActWick,
    	    ActAnar, ActAttChg);
        NeutralSideMeanness = CalcNeutralSideMeanness(Candidates,
            nArrLen,pNextA);
        ErrCode = CurrentStrategy.ExecuteNextAction(GenericArg, UseTrueStats,
            NeutralSideMeanness);
    }

    if(!(ErrCode == ACTION_SUCCESS || ErrCode == ACTION_NOT_PRIMED
        //end strategy on success means also go on in case of failure
            || pNextA->GetFailureGoto() == END_STR_ON_SUCCESS))
    {
        FailuresCount++;
        if(UseHistory)
        {
            //now register failure
            History.Register(core.attId,
                    pNextA->GetID(),
                    pNextA->GetObjective()->attId,
                    ACTION_ITEMID(pNextA),
                    ACTION_FAILURE);
        }
    }

    if((ErrCode == ACTION_SUCCESS || ErrCode == ACTION_FAILURE)
            && !ActionsSoloMode)
    {
        //3rd party attitude update
        for(i = 0;i < nArrLen; i++)
        {
            if(Candidates[i] == NULL)           continue;
            AttVal = Candidates[i]->AttitudeTo(
                    pNextA->GetObjective()->attId);
            if(AttVal == ERROR_PERCENTAGE)      continue;
            AttVal = Candidates[i]->AttitudeTo(core);
            if(AttVal == ERROR_PERCENTAGE)
                Candidates[i]->CreateNewAttitude(core);
            PersonalAttitude2Action(*pNextA,
                *(Candidates[i]->GetCoreAddress()),
                TRUE);
        }
    }

    if(ErrCode == ACTION_SUCCESS)
    {
        FailuresCount = 0;
        LoopFailuresCount = 0;
        if(pNextA->GetFailureGoto() == END_STR_ON_SUCCESS)
            CurrentStrategy.End();
        if(ABS(ActWick) > ABS(core.Wickedness))
        {
            core.Wickedness = (percentage)((1-SINGLE_ACTION_INFLUENCE) * core.Wickedness
                + SINGLE_ACTION_INFLUENCE * ActWick);
        }
        if(ABS(ActAnar) > ABS(core.Anarchy))
        {
            core.Anarchy = (percentage)((1-SINGLE_ACTION_INFLUENCE) * core.Anarchy
                + SINGLE_ACTION_INFLUENCE * ActAnar);
        }
    }

    if(FailuresCount >= nMaxFailures)
    {
        if(pNextA->GetFailureGoto() && (LoopFailuresCount < nMaxFailures))
        {
            fail_goto = CurrentStrategy.FindAction(pNextA->GetFailureGoto());
            if(fail_goto)
            {
                LoopFailuresCount++;
                CurrentStrategy.SetNextActionAddress(fail_goto);
                sprintf(LastThought,"I can't do it like that. I must act another way... ");
            }
            else
            {
                sprintf(LastThought,"I'll never be able to complete this plan. I better forget about it... ");
                if(UseHistory)
                {
                    History.Register(core.attId,
                        CurrentStrategy.GetID(),
                        CurrentStrategy.GetObjective()->attId,
                        STRATEGY_ITEMID(CurrentStrategy),
                        STRATEGY_FAILURE);
                        //so next time the initiator will be smarter;)
                }
                CurrentStrategy.End();
                ErrCode = STRATEGY_ABORTED;
            }
        }
        else
        {
            sprintf(LastThought,"I'll never be able to complete this plan. I better forget about it... ");
            if(UseHistory)
            {
                History.Register(core.attId,
                    CurrentStrategy.GetID(),
                    CurrentStrategy.GetObjective()->attId,
                    STRATEGY_ITEMID(CurrentStrategy),
                    STRATEGY_FAILURE);
            }
                    //so next time the initiator will be smarter
            CurrentStrategy.End();
            ErrCode = STRATEGY_ABORTED;
        }
        FailuresCount = 0;
    }
    return ErrCode;
}

//***************************************************************************
//Agent::IsStrategyStillRelevant
//********** Description: **************
//verifies if the objective still has the item, if objective item is not NULL
//and still suits the criteria
//********** Parameters:  **************
//Agent **Candidates           - array of pointers to the Agents being scanned
//unsigned short nArrLen       - the above array length
//ItemPtrArr *Items            - pointer to the arrays of the Agents' item pointers
//unsigned short *ItemArrayLen - lengths of the above Items arrays
//----------------------------------------------------------------------------------------
BOOL BBECALL Agent::IsStrategyStillRelevant(Agent **Candidates,unsigned short nArrLen,
        ItemPtrArr* Items,
		unsigned short *ItemArrayLen)
{
    Core StrObj, *pbsStrObj;
    ItemScores *StrObjItem;
    GoalDefNodeP gp;

    pbsStrObj = CurrentStrategy.GetObjective();
    if(pbsStrObj == NULL)
        return FALSE;
    StrObj = *pbsStrObj;

    StrObjItem = CurrentStrategy.GetObjItem();

    if(CurrentStrategy.GetGoal().Itself != MANUAL_GOAL)
    {
        gp = GetGoalNode(CurrentStrategy.GetGoal().Itself);
        //check the objective parameters versus criteria
        if(gp == NULL)
        {
            return FALSE;
        }

            /*check that the strategy objective
            still fits in the criterion
            2 problems remain:
            (1) Checking all the action objectives (and initiators, possibly)
            (2) Checking items criteria consistency
            */

        //initial attitude stores evaluator's attitude to target,
        //AttitudeChangeUnit stores the opposite
        StrObj.InitialAttitude = AttitudeTo(StrObj.attId);
        StrObj.AttitudeChangeUnit = GetAttitude(core.attId,StrObj.attPersonal);

        if(!StrObjItem && !VerifyCriteria(gp, &StrObj))
        {
            return FALSE;
        }
    }

    if(StrObjItem == NULL)
    {
        return TRUE;    //nothing to check
    }

    if(StrObjItem->Owner == StrObj.attId)
        return TRUE;
    else
        return FALSE;
}

//***************************************************************************
//Agent::AssignTargetForNextAction
//********** Description: **************
//attempt to find agents suitable to be the next action's targets, and prime the
//next action with these targets, if successful.
//********** Parameters:  **************
//Agent **Candidates           - array of pointers to the Agents being scanned
//unsigned short nArrLen       - the above array length
//ItemPtrArr *Items            - pointer to the arrays of the Agents' item pointers
//unsigned short *ItemArrayLen - lengths of the above Items arrays
//----------------------------------------------------------------------------------------
BOOL BBECALL Agent::AssignTargetForNextAction(Agent **Candidates,unsigned short nArrLen,
        ItemPtrArr* Items,
		unsigned short *ItemArrayLen)
{
    Action *NextA, *Rep1, *Rep2;
    BOOL bRetVal = TRUE;

    NextA = CurrentStrategy.GetNextAction();

    if(NextA == NULL)
    {
        PostError(INVALID_PROP);
        return FALSE;
    }

    if(AssignTargetsForAction(NextA,
            Candidates,
            nArrLen,
            Items,
		    ItemArrayLen) == FALSE)
        return FALSE;

    Rep1 = NextA->GetReported1Ptr();
    if(Rep1 == NULL)
        return TRUE;
    Rep2 = NextA->GetReported2Ptr();
    if(Rep2 != NULL)
    {
        bRetVal = AssignTargetsForAction(Rep2,
                Candidates,
                nArrLen,
                Items,
		        ItemArrayLen);

        if((Rep2->GetID() == SAME_ACTION) && bRetVal)
            pbsAlternateInitiator = Rep2->GetObjective();
        else
        {
        //Warning: dangerous and possibly erroneous assumption.
        // In "double" verbal actions (only deal for now) the objective
        // of the master action is usually initiator in Reported2
            Rep2->InitTargets(NextA->GetObjective());
        }
    }

    if(!bRetVal)
        return FALSE;

    bRetVal = AssignTargetsForAction(Rep1,
            Candidates,
            nArrLen,
            Items,
		    ItemArrayLen);

    pbsAlternateInitiator = NULL;

    return bRetVal;
}

//***************************************************************************
//Agent::AssignTargetsForAction
//********** Description: **************
//attempt to find agents suitable to be the specified action's targets, and prime
//the next action with these targets, if successful
//********** Parameters:  **************
//Action *aArg                 - Action to set targets to
//Agent **Candidates           - array of pointers to the Agents being scanned
//unsigned short nArrLen       - the above array length
//ItemPtrArr *Items            - pointer to the arrays of the Agents' item pointers
//unsigned short *ItemArrayLen - lengths of the above Items arrays
//Strategy *pStrategy          - alternate Strategy to refer
//----------------------------------------------------------------------------------------
BOOL BBECALL Agent::AssignTargetsForAction(Action *aArg,
        Agent **Candidates,
        unsigned short nArrLen,
        ItemPtrArr* Items,
		unsigned short *ItemArrayLen,
        Strategy *pStrategy)
{
    Action *RefA = NULL;
    ActionNodeP tmp;
    long ChosenOne;
    unsigned char actRefTAC;
    Core *pbsInitiator;

    if(aArg == NULL)
    {
        PostError(INVALID_PARAM);
        return FALSE;
    }

    if(pbsAlternateInitiator)
        pbsInitiator = pbsAlternateInitiator;
    else
        pbsInitiator = &core;

    if(aArg->GetID() == SAME_ACTION || aArg->GetType() == STRATEGY_CALL)
        bBypassCheckExecute = TRUE;
    else
        bBypassCheckExecute = FALSE;

    //if NULL... then it's NULL, there's nothing wrong in it
    pAlternateStrategy = pStrategy;
    if(pStrategy == NULL)
        pStrategy = &CurrentStrategy;

    if(pStrategy->GetGoal().Itself == MANUAL_GOAL)
    {
        aArg->InitTargets(pbsInitiator);
    //it's all ready
        bBypassCheckExecute = FALSE;
        return TRUE;
    }

    actAssignTargetsTo = aArg;
    /*
    The algorithm is:
    (1) Check if the dynamic target assignment criterion (TAC) is non-zero.
        If it is, go and FindCriterionMatch.
    (2) If dynamic TAC is zero, check the referenced TAC. If it is zero,
        assign default strategy target(s). If not, look for suitable action.
        If the action was not found, assign the strategy targets.
    */
    if(aArg->GetTargetAssignmentCriteria() == 0)
    {
        if(aArg->GetRefTAC() == 0)
        {
            aArg->InitTargets(
                pbsInitiator,
                pStrategy->GetObjective(),
                pStrategy->GetObjItem());

            pAlternateStrategy = NULL;
            bBypassCheckExecute = FALSE;
            return TRUE;
        }
        else
        {
            actRefTAC = aArg->GetRefTAC();
            tmp = pStrategy->FindAction(actRefTAC);
            if(tmp)
            {
                if(IS_REF_TO_REP(actRefTAC))
                    if(IS_REP1(actRefTAC))
                        RefA = tmp->action.GetReported1Ptr();
                    else
                        RefA = tmp->action.GetReported2Ptr();
                else
                    RefA = &(tmp->action);
            }

            if(RefA)
            {
                aArg->InitTargets(
                    pbsInitiator,
                    RefA->GetObjective(),
                    RefA->GetObjItem());
                pAlternateStrategy = NULL;
                bBypassCheckExecute = FALSE;
                return TRUE;
            }
            else
            {
                aArg->InitTargets(
                    pbsInitiator,
                    pStrategy->GetObjective(),
                    pStrategy->GetObjItem());
                pAlternateStrategy = NULL;
                bBypassCheckExecute = FALSE;
                return TRUE;
            }
        }
    }

    if(Candidates == NULL || nArrLen == 0)
    {
        pAlternateStrategy = NULL;
        bBypassCheckExecute = FALSE;
        return FALSE;
    }
    else
    {
        ChosenOne = FindCriterionMatch(Candidates,nArrLen,Items,ItemArrayLen,
            aArg->GetTargetAssignmentCriteria(),
            aArg->GetRefTAC());//RefTAC acts here as restrictions' holder
        LastSelected = (unsigned short)ChosenOne;
        if(ChosenOne == -1)
        {
            pAlternateStrategy = NULL;
            bBypassCheckExecute = FALSE;
            return FALSE;
        }
        else
        {
            pAlternateStrategy = NULL;
            bBypassCheckExecute = FALSE;
            return TRUE;
        }
    }
}

//***************************************************************************
//Agent::ItemAcquisitionImportance
//********** Description: **************
//return the given item acquisition importance (related to the current Agent)
//********** Parameters:  **************
//ItemScores &ScannedItem - the evaluated item
//--------------------------------------
percentage BBECALL Agent::ItemAcquisitionImportance(ItemScores &ScannedItem)
{
	percentage ActualAtt;
    ActualAtt = (percentage)(AttitudeToItem(ScannedItem));
     //after all, item acqisition importance must be a little less
    // than attitude
	return (percentage)(ActualAtt > 30 ?(ActualAtt - 30)/10 :
            ActualAtt/10);
}

//***************************************************************************
//Agent::CalcNeutralSideMeanness
//********** Description: **************
//return the Combat Overall of all the Agents in the given population who are
//neither the objectives nor the initiators of the current Action nor have a
//positive attitude towards this Agent
//********** Parameters:  **************
//Agent **pbspPopulation  - the population
//unsigned short nPopSize - number of Agents in the population
//Action *actToExecute    - pointer to the referenced Action
//-----------------------------------------------------------------------
percentage BBECALL Agent::CalcNeutralSideMeanness(Agent **pbspPopulation,
        unsigned short nPopSize, Action *actToExecute)
{
    unsigned short i;
    BOOL nUseWhat;
    Core *Obj,*Initi, *Current;
    percentage NeutralSideMeanness = 0;
    if(pbspPopulation == NULL || actToExecute == NULL
            || ActionsSoloMode)
        return 0;
    nUseWhat = UseTrueStats & 1;    //check LSB
    Obj = actToExecute->GetObjective();
    Initi = actToExecute->GetInitiator();
    if(Obj == NULL || Initi == NULL)
        return 0;

    for(i = 0; i < nPopSize ; i++)
        if(pbspPopulation[i] != NULL)
        {
            Current = pbspPopulation[i]->GetCoreAddress();
            if(Current == NULL)
                continue;
            if(Obj != Current
                && Initi != Current
                //if person's attitude to "SELF" is not positive...
                && GetAttitude(Initi->attId,
                    Current->attPersonal,UseTrueAttitude) < 1
                //...the person is neutral
                )
            NeutralSideMeanness += Current->Meanness[nUseWhat];
        }

    return NeutralSideMeanness;
}


//***************************************************************************
//Agent::IsNewStrategyMoreImportant(Strategy&)
//********** Description: **************
//return whether the new given Strategy is more important to the Agent than
//the current one
//********** Parameters:  **************
//Strategy& CheckedStrategy - the Strategy to check
//-----------------------------------------------------------------------
BOOL BBECALL Agent::IsNewStrategyMoreImportant(Strategy& CheckedStrategy)
{
    percentage OldImp, NewImp;
    OldImp = CurrentStrategy.GetImportance();
    NewImp = CheckedStrategy.GetImportance();

    ActionNodeP ListHead;
    ListHead = CurrentStrategy.GetActionList();
    if(ListHead != NULL)
        if(CurrentStrategy.GetNextAction() != &(ListHead->action))
            OldImp = (percentage)(OldImp + ALREADY_STARTED_FACTOR);
            //the character already started something -
            //it's a pity to waste an effort

  	if(NewImp > OldImp)
		return TRUE;
    else
        return FALSE;
}

//***************************************************************************
//Agent::IsNewStrategyMoreImportant(percentage)
//********** Description: **************
//return whether a new Strategy of the given Importance is more important to
//the Agent than the current one
//********** Parameters:  **************
//percentage NewImp - new Strategy importance
//-----------------------------------------------------------------------
BOOL BBECALL Agent::IsNewStrategyMoreImportant(percentage NewImp)
{
    percentage OldImp;
    ActionNodeP ListHead;
    OldImp = CurrentStrategy.GetImportance();
    ListHead = CurrentStrategy.GetActionList();
    if(ListHead != NULL)
        if(CurrentStrategy.GetNextAction() != &(ListHead->action))
            OldImp = (percentage)(OldImp + ALREADY_STARTED_FACTOR);
            //the character already've done something -
            //it's a pity to waste an effort

    if(NewImp > OldImp)
		return TRUE;
    else
        return FALSE;
}

//***************************************************************************
//Agent::SetImpression
//********** Description: **************
//set the impression scores: Meanness (combat overall), Wealth, Beauty,
//Intelligence, Foreignness. Meanness, Wealth and Intelligence can be either
//seemed or true. UseTrue determines which one will be assigned new values
//********** Parameters:  **************
//percentage argMeanness     - Meanness (combat overall)
//percentage argWealth       - Wealth
//percentage argBeauty       - Beauty
//percentage argIntelligence - Intelligence
//percentage argForeignness  - Foreignness
//BOOL UseTrue               - whether to use true or seemed values
//-----------------------------------------------------------------------
void BBECALL Agent::SetImpression(percentage  argMeanness, percentage argWealth,
  	percentage argBeauty, percentage argIntelligence, percentage argForeignness,BOOL UseTrue)
{
  	core.Meanness[UseTrue] = argMeanness;
  	core.Wealth[UseTrue] = argWealth;
  	core.Beauty = argBeauty;
    core.Intelligence[UseTrue] = argIntelligence;
  	core.Foreignness = argForeignness;
    SetCoreDefaults(core);
}

//***************************************************************************
//Agent::SetSolidScores
//********** Description: **************
//set the solid scores: true Intelligence and Courage
//********** Parameters:  **************
//percentage argIntel   - Intelligence (only true)
//percentage argCourage - Courage
//-----------------------------------------------------------------------
void BBECALL Agent::SetSolidScores(percentage argIntel, percentage argCourage)
{
    core.Intelligence[1] = argIntel;
    core.Courage = argCourage;
}

//***************************************************************************
//Agent::GetImpression
//********** Description: **************
//get the impression scores: Meanness (combat overall), Wealth, Beauty,
//Intelligence, Foreignness. Meanness, Wealth and Intelligence can be either
//seemed or true. UseTrue determines which one will be used.
//********** Parameters:  **************
//percentage argMeanness     - Meanness (combat overall) holder
//percentage argWealth       - Wealth holder
//percentage argBeauty       - Beauty holder
//percentage argIntelligence - Intelligence holder
//percentage argForeignness  - Foreignness holder
//BOOL UseTrue               - whether to use true or seemed values
//-----------------------------------------------------------------------
void BBECALL Agent::GetImpression(percentage&  argMeanness, percentage& argWealth,
  	percentage& argBeauty, percentage& argIntelligence, percentage& argForeignness, BOOL UseTrue)
{
  	argMeanness = core.Meanness[UseTrue];
  	argWealth = core.Wealth[UseTrue];
  	argBeauty = core.Beauty;
    argIntelligence = core.Intelligence[UseTrue];
  	argForeignness = core.Foreignness;//[UseTrue];
}

//***************************************************************************
//Agent::GetSolidScores
//********** Description: **************
//get the solid scores: true Intelligence and Courage
//********** Parameters:  **************
//percentage argIntel   - Intelligence holder
//percentage argCourage - Courage holder
//-----------------------------------------------------------------------
void BBECALL Agent::GetSolidScores(percentage& argIntel, percentage& argCourage)
{
    argIntel = core.Intelligence[1];
	argCourage = core.Courage;
}

//***************************************************************************
//Agent::PrimeAllActionTargets
//********** Description: **************
//assign targets to all Actions, usually for testing purposes (whether the Strategy
//can be accomplished)
//********** Parameters:  **************
//Strategy *argStrategy - Strategy to accomplish
//-----------------------------------------------------------------------
BOOL BBECALL Agent::PrimeAllActionTargets(Strategy *argStrategy)
{
	ActionNodeP ap;
    Action *Rep;

	for( ap = argStrategy->GetActionList(); ap != NULL ; ap = ap->next )
    {
        if(SoloAssumptionMode)
        {
            ap->action.InitTargets(&core, argStrategy->GetObjective());
            Rep = ap->action.GetReported1Ptr();
            if(Rep == NULL)
                continue;
            Rep->InitTargets(argStrategy->GetObjective());

            Rep = ap->action.GetReported2Ptr();
            if(Rep == NULL)
                continue;
            Rep->InitTargets(argStrategy->GetObjective());
        }
        else
        {
            pbsAlternateInitiator = NULL;
            //use AssignTargetsForAction with population
            if(!AssignTargetsForAction(&(ap->action),pbspCompPopulation,
                    nCompPopSize,ipapCompAllInv,nCompInvSizes,
                    argStrategy))
            {
                return FALSE;
            }

            Rep = ap->action.GetReported2Ptr();
            if(Rep != NULL)
            {
                if(Rep->GetID() == SAME_ACTION)
                    bBypassCheckExecute = TRUE;
                if(!AssignTargetsForAction(Rep,pbspCompPopulation,
                        nCompPopSize,ipapCompAllInv,nCompInvSizes,
                        argStrategy))
                {
                    bBypassCheckExecute = FALSE;
                    return FALSE;
                }

                if(Rep->GetID() == SAME_ACTION)
                    pbsAlternateInitiator = Rep->GetObjective();
                else
        //Warning: dangerous and possibly erroneous assumption.
        // In "double" verbal actions (only DEAL for now) the objective
        // of the master action is usually initiator in Reported2
                    Rep->InitTargets(ap->action.GetObjective());

                bBypassCheckExecute = FALSE;
            }

            Rep = ap->action.GetReported1Ptr();
            if(Rep != NULL)
            {
                if(!AssignTargetsForAction(Rep,pbspCompPopulation,
                        nCompPopSize,ipapCompAllInv,nCompInvSizes,
                        argStrategy))
                {
                    pbsAlternateInitiator = NULL;
                    return FALSE;
                }
            }
            pbsAlternateInitiator = NULL;
        }
    }

    return TRUE;
}

//***************************************************************************
//Agent::CalcResponse
//********** Description: **************
//evaluate the situation and initiate response, if needed
//********** Parameters:  **************
//Action& ActionToRespond - Action to evaluate
//-----------------------------------------------------------------------
BOOL BBECALL Agent::CalcResponse(Action& ActionToRespond)
{
   percentage ActDiff, ActWick, ActAnar, AttChgInACU, SavedAttitude, NewAttitude;
   signed short ActAttChg;
   Core* RespondTo = ActionToRespond.GetInitiator(), bsPartial;
   GoalDefNodeP	gp;
   StrategyDefNodeP sp;
   percentage SuddenChangeRoll;
   Strategy *NewStrategy;

   if((RespondTo == NULL) || (RespondTo == &core))
   {
        PostError(INVALID_PARAM);
        return FALSE;
   }

   ActionToRespond.GetActualScores(ActDiff, ActWick, ActAnar, AttChgInACU);
   ActAttChg = (short)(core.AttitudeChangeUnit * AttChgInACU);
   if (ActAttChg > EXTREME_PER_VAL*2)
   		ActAttChg = EXTREME_PER_VAL*2;
   if (ActAttChg < -EXTREME_PER_VAL*2)
   		ActAttChg = -EXTREME_PER_VAL*2;
   SavedAttitude = AttitudeTo(RespondTo->attId);
   if(SavedAttitude == ERROR_PERCENTAGE)
   // if we are not familiar with the subject, get a new attitude
        CreateNewAttitude(*RespondTo);

    if(SavedAttitude == ERROR_PERCENTAGE)
        SavedAttitude = GetAttitude(RespondTo->attId,core.attPersonal,TRUE);
   //modify both attitudes
    ModifyAttitude(RespondTo->attId, ActAttChg, core.attPersonal, BOTH);
    //"partial" is because it is partial Core structure

    memset(&bsPartial, 0, sizeof(Core));
    bsPartial.Dead = RespondTo->Dead;
    bsPartial.CurrentEnvironment = RespondTo->CurrentEnvironment;

    bsPartial.attPersonal    = NULL;
    bsPartial.LastAccessedBy = NULL;
    bsPartial.AttitudeChangeUnit = GetAttitude(core.attId,RespondTo->attPersonal,
        TRUE);
    if(bsPartial.AttitudeChangeUnit == ERROR_PERCENTAGE)
        bsPartial.AttitudeChangeUnit = 0;
    bsPartial.InitialAttitude = VALID_PERCENTAGE(SavedAttitude
        + ActAttChg);   //this is the new attitude
    if(UseSuddenChange)
        SuddenChangeRoll = (percentage)Random(0,100);
    else
        SuddenChangeRoll = 100;
    if(SuddenChangeRoll <= core.SuddenChange)
    //sudden change roll succeeded
        bsPartial.InitialAttitude = VALID_PERCENTAGE(bsPartial.InitialAttitude
                + ActAttChg);//give extra change
    NewAttitude = bsPartial.InitialAttitude;
    ActAttChg = (short)(NewAttitude - SavedAttitude);

    if(ABS(ActAttChg) > MIN_SIGNIFICANT_ATT)
    //otherwise, we don't need to do anything
    for(gp = GoalListHead; gp != NULL; gp = gp->next)
    {
        if( gp->LoReqs.InitialAttitude == ERROR_PERCENTAGE )
            continue;
        //find only attitude-related goals
      	if ( VerifyCriteria(gp, &bsPartial) )
        {
        	    for( sp = StrategyListHead ; sp != NULL ; sp = sp->next )
                {
        	    	if (sp->TheGoal.Itself == gp->goal.Itself
                            && sp->_Type == STRTYPE_REGULAR)
                    {
                        if(sp->MaxIntel < core.Intelligence[1])
                            continue;
	            		NewStrategy = CreateStrategy( sp );
                        if(NewStrategy == NULL)
                            continue;
    	            	NewStrategy->SetObjective( RespondTo );

    	        		if (!WasStrategyFailure(NewStrategy)
                          && PrimeAllActionTargets(NewStrategy)
                          && NewStrategy->PossibilityCheck() )
        	        	{
                    		CurrentStrategy.End();
            	       		CurrentStrategy = *NewStrategy;
                            CurrentStrategy.ResetAllActionTargets();
	            	        delete NewStrategy;
                            sprintf(LastThought,
            "I must respond. I think I'll try %s on this person.",
                                sp->Name);
         //tests must be done to find out if there is memory leak here
        	        		return TRUE;
	            	    }
                        else
                        {
                            NewStrategy->End();
                            delete NewStrategy;
                        }
         		     }
         	}
        }
   }

   if (ABS(ActAttChg) > MIN_SIGNIFICANT_ATT)
   {
        if(NewAttitude < SavedAttitude)
            sprintf(LastThought,"I won't respond, but my attitude to this person became worse. It is now %s. ",
                GetAttitudeDesc(NewAttitude));
        else
            sprintf(LastThought,"I won't respond, but my attitude to this person improved. It is now %s. ",
                GetAttitudeDesc(NewAttitude));
   }
   else
        sprintf(LastThought,"Currently I feel %s towards this person. ",
            GetAttitudeDesc(NewAttitude));
   return FALSE;
}

//***************************************************************************
//Agent::CreateNewAttitude
//********** Description: **************
//create new Attitude node by the given Core
//********** Parameters:  **************
//Core& NewPerson - the Core of the Agent to create new Attitude for
//-----------------------------------------------------------------------
BOOL BBECALL Agent::CreateNewAttitude(Core& NewPerson)
{
	return (AddAttitude(NewPerson.attId,(percentage)(core.InitialAttitude +
    	InitialAttitudeModification(NewPerson)),&core.attPersonal));
}

//***************************************************************************
//Agent::AttitudeTo
//********** Description: **************
//return attitude to the given Agent by the given Core
//********** Parameters:  **************
//Core& argBs - the Agent's Core
//-----------------------------------------------------------------------
percentage BBECALL Agent::AttitudeTo(Core& argBs)
{
    return GetAttitude(argBs.attId,core.attPersonal,UseTrueAttitude);
}

//***************************************************************************
//Agent::AttitudeTo
//********** Description: **************
//return attitude to the given Agent by the given ID
//********** Parameters:  **************
//ID argId - the Agent's Core ID
//-----------------------------------------------------------------------
percentage BBECALL Agent::AttitudeTo(ID argId)
{
    return GetAttitude(argId,core.attPersonal,UseTrueAttitude);
}

//***************************************************************************
//Agent::SetAttitudeTo
//********** Description: **************
//set attitude to the given Agent by the given ID
//********** Parameters:  **************
//ID argId               - the Agent's Core ID
//percentage NewAttitude - value to set
//unsigned char WhichOne - which attitude to set
//-----------------------------------------------------------------------
BOOL BBECALL Agent::SetAttitudeTo(ID argId, percentage NewAttitude,
        unsigned char WhichOne)
{
    return UpdateAttitude(argId, NewAttitude, &(core.attPersonal), WhichOne );
}

//***************************************************************************
//Agent::GetCore
//********** Description: **************
//return Core
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------
Core BBECALL Agent::GetCore() const
{ return core; }

//***************************************************************************
//Agent::GetCoreAddress
//********** Description: **************
//return pointer to Core
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------
Core *BBECALL Agent::GetCoreAddress()
{ return &core; }

//***************************************************************************
//Agent::SetCore
//********** Description: **************
//set Core; the function does not just overwrite everything - only the needed
//attributes
//********** Parameters:  **************
//Core &argBs - Core to copy
//-----------------------------------------------------------------------
void BBECALL Agent::SetCore(Core &argBs)
{
    if(!Initialized)
    {
        SetOriginalCore(argBs);
        Initialized = TRUE;
    }
//    core = argBs; IN YOUR WILDEST DREAMS!!!
    core.Wickedness = argBs.Wickedness;
    core.Anarchy = argBs.Anarchy;
    core.alType = argBs.alType;
    InitType();

    //Impression values
    core.Meanness[0] = argBs.Meanness[0];
    core.Meanness[1] = argBs.Meanness[1];
    core.Wealth[0] = argBs.Wealth[0];
    core.Wealth[1] = argBs.Wealth[1];
    core.Beauty = argBs.Beauty;
    core.Foreignness = argBs.Foreignness;
    core.Intelligence[0] = argBs.Intelligence[0];
    core.Intelligence[1] = argBs.Intelligence[1];
    core.Courage = argBs.Courage;
    core.UserDefined[0] = argBs.UserDefined[0];
    core.UserDefined[1] = argBs.UserDefined[1];
    core.UserDefined[2] = argBs.UserDefined[2];

    if(core.attPersonal == NULL)
        core.attPersonal = argBs.attPersonal;
    UpdateSecondaryVars();
}

//***************************************************************************
//Agent::GetOriginalCore
//********** Description: **************
//return original Core
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------
Core BBECALL Agent::GetOriginalCore() const
{ return Original; }

//***************************************************************************
//Agent::SetOriginalCore
//********** Description: **************
//set original Core; the function does not just overwrite everything -
//only the needed attributes
//********** Parameters:  **************
//Core &argBs - Core to copy
//-----------------------------------------------------------------------
void BBECALL Agent::SetOriginalCore(Core &argBs)
{
  Original.Wickedness = argBs.Wickedness;
  Original.Anarchy = argBs.Anarchy;
  InitType();

  //Impression values
  Original.Meanness[0] = argBs.Meanness[0];
  Original.Meanness[1] = argBs.Meanness[1];
  Original.Wealth[0] = argBs.Wealth[0];
  Original.Wealth[1] = argBs.Wealth[1];
  Original.Beauty = argBs.Beauty;
  Original.Foreignness = argBs.Foreignness;
  Original.Intelligence[0] = argBs.Intelligence[0];
  Original.Intelligence[1] = argBs.Intelligence[1];
  Original.Courage = argBs.Courage;
  Original.UserDefined[0] = argBs.UserDefined[0];
  Original.UserDefined[1] = argBs.UserDefined[1];
  Original.UserDefined[2] = argBs.UserDefined[2];
  UpdateSecondaryVars();
}

//***************************************************************************
//Agent::ResetFromOriginal
//********** Description: **************
//restore all the modified attributes in the Core from Original Core
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------
void BBECALL Agent::ResetFromOriginal()
{
  core.Wickedness = Original.Wickedness;
  core.Anarchy = Original.Anarchy;
  InitType();

  //Impression values
  core.Meanness[0] = Original.Meanness[0];
  core.Meanness[1] = Original.Meanness[1];
  core.Wealth[0] = Original.Wealth[0];
  core.Wealth[1] = Original.Wealth[1];
  core.Beauty = Original.Beauty;
  core.Foreignness = Original.Foreignness;
  core.Intelligence[0] = Original.Intelligence[0];
  core.Intelligence[1] = Original.Intelligence[1];
  core.Courage = Original.Courage;
  core.Dead = Original.Dead;
  UpdateSecondaryVars();
}

//***************************************************************************
//Agent::ResetFromOriginal
//********** Description: **************
//save all the modified attributes in the Core to the Original Core
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------
void BBECALL Agent::UpdateOriginalCore()
{
    Original.Wickedness = core.Wickedness;
    Original.Anarchy = core.Anarchy;

  //Impression values
    Original.Meanness[0] = core.Meanness[0];
    Original.Meanness[1] = core.Meanness[1];
    Original.Wealth[0] = core.Wealth[0];
    Original.Wealth[1] = core.Wealth[1];
    Original.Beauty = core.Beauty;
    Original.Foreignness = core.Foreignness;
    Original.Intelligence[0] = core.Intelligence[0];
    Original.Intelligence[1] = core.Intelligence[1];
    Original.Courage = core.Courage;
    Original.Dead = core.Dead;
}

//***************************************************************************
//Agent::GetAlType
//********** Description: **************
//return the Agent's Alignment Type
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------
const AlignmentType BBECALL Agent::GetAlType()
{ return core.alType; }

//***************************************************************************
//Agent::GetID
//********** Description: **************
//return the Agent's unique ID
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------
ID BBECALL Agent::GetID()
{  return core.attId;  }

//***************************************************************************
//Agent::SetSeemed_TrueToggles
//********** Description: **************
//set the defaults for which attributes to use, true (1) or seemed (0)
//********** Parameters:  **************
//BOOL UseTrueMeanness     - whether to use true Meanness
//BOOL UseTrueWealth       - whether to use true Wealth
//BOOL UseTrueIntelligence - whether to use true Intelligence
//BOOL argUseTrueAttitude  - whether to use true Attitudes
//-----------------------------------------------------------------------
void BBECALL Agent::SetSeemed_TrueToggles(BOOL UseTrueMeanness,
        BOOL UseTrueWealth, BOOL UseTrueIntelligence,
        BOOL argUseTrueAttitude)
{
    UseTrueStats = (unsigned char)((1 & UseTrueMeanness + 2 & UseTrueWealth
        + 4 & UseTrueIntelligence) | 0x80 );
    UseTrueAttitude = (unsigned char)argUseTrueAttitude;
    //MSB set to 1 to notify that the user is aware of the feature and forces us to
    //use his settings
}

//***************************************************************************
//Agent::IsDead
//********** Description: **************
//return if the Agent is permanently inactive (dead)
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------
BOOL BBECALL Agent::IsDead()
{
    if(core.Dead)    // forget about Original scores
        return TRUE;
    else
        return FALSE;
}

//***************************************************************************
//Agent::Die
//********** Description: **************
//switch the Agent's state to permanently inactive (dead)
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------
void BBECALL Agent::Die()
{
    core.Dead = TRUE;
    Original.Dead = TRUE;
}

//***************************************************************************
//Agent::FillCritAttitudesArray
//********** Description: **************
//fills the given buffer with the needed attitudes by the given goal definition
//********** Parameters:  **************
//percentage *pAtt - byte buffer to fill
//Core *bsChk      - Core being examined
//GoalDefNodeP gp  - goal definition node pointer
//-----------------------------------------------------------------------
void BBECALL Agent::FillCritAttitudesArray(percentage *pAtt,Core *bsChk,
    GoalDefNodeP gp)
{
    Core *bsObj,*bsRefActObj,*bsLastAcc;
    ActionNodeP anpRefAct;
    Strategy *pStrReferTo;
    percentage Att2StrObj = ERROR_PERCENTAGE,AttByStrObj = ERROR_PERCENTAGE,
        Att2LastInit = ERROR_PERCENTAGE,AttByLastInit = ERROR_PERCENTAGE,
        Att2RefActObj = ERROR_PERCENTAGE,AttByRefActObj = ERROR_PERCENTAGE,
        Att2UserDef;
    //this should fix the assign failure when referring to another strategy,
    //as in PrimeAllActionTargets
    if(pAlternateStrategy != NULL)
        pStrReferTo = pAlternateStrategy;
    else
    {
        pStrReferTo = &CurrentStrategy;
    }

    PostError(0);

    if(pAtt == NULL || bsChk == NULL || gp == NULL)
    {
        PostError(INVALID_PARAM);
        return;
    }

    bsObj = pStrReferTo->GetObjective();
    if(bsObj != NULL)
    {   //strategy objective
        Att2StrObj  = GetAttitude(bsObj->attId,bsChk->attPersonal);
        AttByStrObj = GetAttitude(bsChk->attId,bsObj->attPersonal);
    }
    bsLastAcc = bsChk->LastAccessedBy;
    if(bsLastAcc != NULL)
    {   //last action initiator. Probably unneeded
        Att2LastInit    = GetAttitude(bsLastAcc->attId,bsChk->attPersonal);
        AttByLastInit   = GetAttitude(bsChk->attId,bsLastAcc->attPersonal);
    }

    Att2UserDef = GetAttitude(gp->UserDefAttID,bsChk->attPersonal);
    if(gp->AttRefAct)
    {
        anpRefAct = pStrReferTo->FindAction(gp->AttRefAct);
        if(anpRefAct)
        {
            bsRefActObj = anpRefAct->action.GetObjective();
            if(bsRefActObj)
            {
                Att2RefActObj   = GetAttitude(bsRefActObj->attId,
                    bsChk->attPersonal);
                AttByRefActObj  = GetAttitude(bsChk->attId,
                    bsRefActObj->attPersonal);
            }
        }
    }

    pAtt[CRIT_ATT2STROBJ - CRIT_ATT_OFFSET] = Att2StrObj;
    pAtt[CRIT_ATT_BY_STROBJ - CRIT_ATT_OFFSET] = AttByStrObj;
    pAtt[CRIT_ATT2LASTINIT - CRIT_ATT_OFFSET] = Att2LastInit;
    pAtt[CRIT_ATT_BY_LASTINIT - CRIT_ATT_OFFSET] = AttByLastInit;
    pAtt[CRIT_ATT2REFACTOBJ - CRIT_ATT_OFFSET] = Att2RefActObj;
    pAtt[CRIT_ATT_BY_REFACTOBJ - CRIT_ATT_OFFSET] = AttByRefActObj;
    pAtt[CRIT_ATT_BY_REFACTOBJ - CRIT_ATT_OFFSET+1] = Att2UserDef;
}

//***************************************************************************
//Agent::VerifyCriteria
//********** Description: **************
//check if the given Core suits the given criterion. Return 0 if checked
//subject doesn't fit, 1 if he does - except for inventory related cases
//(criterion ID >= 200) where the matching item's index is returned
//********** Parameters:  **************
//GoalDefNodeP gpCrit     - criterion definition node pointer
//Core *bsChk             - Core being examined
//ItemPtrArr ipaInventory - pointer to the Agent's inventory
//unsigned short nInvSize - Agent's inventory length
//-----------------------------------------------------------------------
unsigned short BBECALL Agent::VerifyCriteria(GoalDefNodeP gpCrit, Core *bsChk,
        ItemPtrArr ipaInventory, unsigned short nInvSize)
{
    GoalDefNodeP gpIncluded;
    Core *bsRel;
    ItemScores *isRel;
    percentage AttArr[10];
    unsigned short i,nIncRetVal;
    Strategy *strActive;

    PostError(0);
    if(pAlternateStrategy == NULL)
        strActive = &CurrentStrategy;
    else
        strActive = pAlternateStrategy;

    if(gpCrit == NULL)
    {
        PostError(INVALID_PARAM);
        return FALSE;
    }

    if(gpCrit->IncGoalID != 0)
    {
        gpIncluded = GetGoalNode(gpCrit->IncGoalID);
        //recursive call
        nIncRetVal = VerifyCriteria(gpIncluded,bsChk,
                ipaInventory,nInvSize);
        if(nIncRetVal == 0)
        {
            return FALSE;
        }
    }

    if(gpCrit->goal.Itself >= ITEM_CRITERIA_RECORDS_START)
    // the criteria is inventory related
    {
        if(ipaInventory == NULL || nInvSize == 0)
        {
            PostError(INVALID_PARAM);
            return FALSE;
        }
        switch(gpCrit->CompType)
        {
            case COMP_ABS:
                isRel = NULL;
                break;
            case COMP_REL2SOBJ:
                isRel = strActive->GetObjItem();
                break;
            default:
                isRel = NULL;
        }
        for(i = 0; i < nInvSize; i++)
        {
            if(CheckItemCriteria(gpCrit,ipaInventory[i],isRel))
            {
                //0 means failure; to obtain the index, the caller should
                //subtract 1
                return (unsigned short)(i+1);
            }
        }
        return 0;
    }

    switch(gpCrit->CompType)
    {
        case COMP_ABS:
            bsRel = NULL;
            break;
        case COMP_REL2EVAL:
            bsRel = &core;
            break;
        case COMP_REL2SOBJ:
            bsRel = strActive->GetObjective();
            break;
        case COMP_REL2LINI:
            bsRel = bsChk->LastAccessedBy;
            break;
        case COMP_ABS_EVAL:
            bsChk = &core;  //override the examined agent with evaluator
        default:
            bsRel = NULL;
    }

    FillCritAttitudesArray(AttArr,bsChk,
        gpCrit);

    if(!CheckCriteria(gpCrit,bsChk,bsRel,AttArr))
        return 0;
    else
        return 1;

}

//***************************************************************************
//Agent::AdoptStrategyFromOneAction
//********** Description: **************
//create and adopt a custom Strategy consisting from only one given Action
//********** Parameters:  **************
//Action *ToBeExecuted        - Action to be executed (unexpected, isn't it:) )
//unsigned char NewImportance - importance of the Strategy to be created
//-----------------------------------------------------------------------
BOOL BBECALL Agent::AdoptStrategyFromOneAction(Action *ToBeExecuted,
        unsigned char NewImportance)
{
    Action LocalCopyOfAction;
    Strategy *NewStrategy;
    Goal DummyGoal;

    if(ToBeExecuted == NULL)
    {
        PostError(INVALID_PARAM);
        return FALSE;
    }
    if(NewImportance < UNSURPASSED_IMPORTANCE)//that means, no checks
        if((NewImportance < CurrentStrategy.GetImportance())
            && (CurrentStrategy.GetNextAction() != NULL))
            return FALSE;

    LocalCopyOfAction.Init(ToBeExecuted);
    LocalCopyOfAction.SetTargetAssignmentCriteria(0);
    LocalCopyOfAction.SetRefTAC(0);
    LocalCopyOfAction.SetFailureGoto(0);

    DummyGoal.Importance = NewImportance;
    DummyGoal.Itself = MANUAL_GOAL;

    NewStrategy = new Strategy;
    NewStrategy->Init(DummyGoal);
    // the new strategy is manually constructed from single action
    NewStrategy->AppendAction(&LocalCopyOfAction);
    NewStrategy->SetObjective(ToBeExecuted->GetObjective());
    NewStrategy->SetObjItem(ToBeExecuted->GetObjItem());
    if ( NewStrategy->PossibilityCheck()
        || NewImportance >= UNSURPASSED_IMPORTANCE)
    {
        CurrentStrategy.End();
        CurrentStrategy = *NewStrategy;
        delete NewStrategy;
        return TRUE;
    }
    else
    {
        NewStrategy->End();
        delete NewStrategy;
        return FALSE;
    }
}

//***************************************************************************
//Agent::SetAttitudesRoot
//********** Description: **************
//make Agent's attitude tree root point to the given address
//********** Parameters:  **************
//Action *ToBeExecuted        - Action to be executed (unexpected, isn't it:) )
//unsigned char NewImportance - importance of the Strategy to be created
//-----------------------------------------------------------------------
void BBECALL Agent::SetAttitudesRoot(Attitude *TreeRoot, BOOL bDoNotDelele)
{
    if(core.attPersonal != NULL && !bDoNotDelele)
	    DelAttitudeTree(&(core.attPersonal));
    core.attPersonal = TreeRoot;
}

//***************************************************************************
//Agent::FillSaveBuffer
//********** Description: **************
//encode the current Agent instance to the given buffer, replacing pointers with IDs
//********** Parameters:  **************
//BYTE* SaveBuffer - buffer to fill
//-----------------------------------------------------------------------
unsigned short BBECALL Agent::FillSaveBuffer(BYTE* SaveBuffer)
{
    unsigned short nAllocated = 0;

    if(this == NULL || SaveBuffer == NULL)
        return 0;

    SaveBuffer[nAllocated++] = (BYTE)UseTrueStats;
    SaveBuffer[nAllocated++] = (BYTE)UseTrueAttitude;
    SaveBuffer[nAllocated++] = (BYTE)Initialized;
    SaveBuffer[nAllocated++] = (BYTE)bBypassCheckExecute;

    nAllocated += BSFillSaveBuffer(Original, SaveBuffer + nAllocated);
    nAllocated += BSFillSaveBuffer(core, SaveBuffer + nAllocated);
    memcpy(SaveBuffer + nAllocated,"AEND",4);
    nAllocated += (unsigned short)4;
    nAllocated += CurrentStrategy.FillSaveBuffer(SaveBuffer + nAllocated);
    return nAllocated;
}

//***************************************************************************
//Agent::RestoreFromBufferStep1
//********** Description: **************
//decode the current Agent instance from the given buffer, step 1
//Why two steps? Because we cannot assume that the unique attId's will
//be the same if you generate these characters in the same order and
//quantity.That's why first we must restore all their scores, including
//IDs, and then recover all the pointers through these IDs
//********** Parameters:  **************
//BYTE* pbBuffer         - buffer containing the saved info
//unsigned short nBufLen - buffer length
//-----------------------------------------------------------------------
void BBECALL Agent::RestoreFromBufferStep1(BYTE *pbBuffer,unsigned short nBufLen)
{
    unsigned short nStrategyStart, nRead = 0;

    UseTrueStats = pbBuffer[nRead++];
    UseTrueAttitude = pbBuffer[nRead++];
    Initialized = pbBuffer[nRead++];
    bBypassCheckExecute = pbBuffer[nRead++];

    BSRestoreFromBufferStep1(pbBuffer + nRead,
        sizeof(Core),
        &Original);
    nRead += (unsigned short)sizeof(Core);

    for(nStrategyStart = 2*sizeof(Core);
            nStrategyStart < nBufLen; nStrategyStart++)
        if(pbBuffer[nStrategyStart] == 'A'
                && pbBuffer[nStrategyStart + 1] == 'E'
                && pbBuffer[nStrategyStart + 2] == 'N'
                && pbBuffer[nStrategyStart + 3] == 'D')
            break;

    //original scores do not contain attitudes. Otherwise, we would be
    //in big trouble
    BSRestoreFromBufferStep1(
        pbBuffer + nRead,
        (unsigned short)( nStrategyStart - nRead ),
        &core);
    pbsAlternateInitiator = NULL;
    actAssignTargetsTo = NULL;
}

//***************************************************************************
//Agent::RestoreFromBufferStep2
//********** Description: **************
//decode the current Agent instance from the given buffer, step 2. At this point,
//Agent's Strategies and Actions are restored, too.
//********** Parameters:  **************
//BYTE* pbBuffer             - buffer containing the saved info
//unsigned short nBufLen     - buffer length
//Core **papPopulation       - current population
//unsigned short nPopSize    - population size
//ItemPtrArr* ipapItems      - all the inventories
//unsigned short *npInvSizes - array of the inventory sizes. The array length is
//                            nPopSize
//-----------------------------------------------------------------------
void BBECALL Agent::RestoreFromBufferStep2(BYTE *pbBuffer,
        unsigned short nBufLen,
        Agent** papPopulation, unsigned short nPopSize,
        ItemPtrArr* ipapItems, unsigned short *npInvSizes)
{
    unsigned short nStrategyStart;
    Core **pbspPopulation;

    pbspPopulation = new Core*[nPopSize];
    if(pbspPopulation == NULL)
        return;

    Agent2CorePtrArray(papPopulation,&pbspPopulation,nPopSize);
    BSRestoreFromBufferStep2(&Original,pbspPopulation,nPopSize);
    BSRestoreFromBufferStep2(&core,pbspPopulation,nPopSize);

    for(nStrategyStart = 2*sizeof(Core);
            nStrategyStart < nBufLen; nStrategyStart++)
    //look for record end delimiter
        if(pbBuffer[nStrategyStart] == 'A'
                && pbBuffer[nStrategyStart + 1] == 'E'
                && pbBuffer[nStrategyStart + 2] == 'N'
                && pbBuffer[nStrategyStart + 3] == 'D')
            break;

    if(nBufLen - nStrategyStart < sizeof(Strategy))
    {
        delete pbspPopulation;
        return;
    }

    CurrentStrategy.End();    //1st, clean up strategy
    CurrentStrategy.RestoreFromBuffer(pbBuffer + nStrategyStart + 4,
        pbspPopulation, nPopSize,
        ipapItems, npInvSizes);
    actAssignTargetsTo = CurrentStrategy.GetNextAction();
    delete pbspPopulation;
}

//***************************************************************************
//Agent::GetTrueStatsByte
//********** Description: **************
//return bitmap attribute containing the info which attributes to use, true or
//seemed
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------
unsigned char BBECALL Agent::GetTrueStatsByte()
{ return UseTrueStats; }

//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
//------------ this functions handle dynamic TAC populations ------------
//-----------------------------------------------------------------------
//***************************************************************************
//SetDynamicTACAssumptionPopulation
//********** Description: **************
//set "sample" population used for Strategy possibility checks. If this population
//is NULL, then on the next call to Agent::ExecuteNextAction, if SoloAssumptionMode
//is FALSE, this function will be called
//********** Parameters:  **************
//Core **pbspPopulation     - current population
//unsigned short nPopSize   - population size
//ItemPtrArr* ipapAllItems  - all the inventories
//unsigned short *nInvSizes - array of the inventory sizes. The array length is
//                            nPopSize
//-----------------------------------------------------------------------
void BBECALL SetDynamicTACAssumptionPopulation(Agent** pbspPopulation,
        unsigned short nPopSize,
        ItemPtrArr* ipapAllInv,unsigned short *nInvSizes)
{
    unsigned short i;

    PostError(0);
    FreeDynamicTACAssumptionPopulation();
    if(pbspPopulation == NULL || nPopSize == 0)
    {
        return;
    }

    pbspCompPopulation = (Agent**)GlobalAlloc(
        GPTR,nPopSize * sizeof(Agent*));
    if(pbspCompPopulation == NULL)
    {
        PostError(MEM_ALLOC_FAILED);
        return;
    }
    nCompPopSize = nPopSize;
    memcpy(pbspCompPopulation,pbspPopulation,
        nCompPopSize * sizeof(Agent*));

    nCompInvSizes = (unsigned short*)GlobalAlloc(
        GPTR,nPopSize * sizeof(unsigned short));
    if(nCompInvSizes == NULL)
    {
        PostError(MEM_ALLOC_FAILED);
        return;
    }
    if(nInvSizes == NULL || ipapAllInv == NULL)
    {
        ipapCompAllInv = NULL;
        nCompInvSizes = NULL;
        return;
    }

    ipapCompAllInv = (ItemPtrArr*)GlobalAlloc(
        GPTR,nPopSize * sizeof(ItemPtrArr));
    if(ipapCompAllInv == NULL)
    {
        PostError(MEM_ALLOC_FAILED);
        return;
    }

    //allocate 'n' copy
    for(i = 0; i < nCompPopSize; i++)
    {
        nCompInvSizes[i] = nInvSizes[i];
        if(nCompInvSizes[i])
        {
            ipapCompAllInv[i] =
                (ItemPtrArr)GlobalAlloc(GPTR,nCompInvSizes[i] *
                    sizeof(ItemPtr));
            if(ipapCompAllInv[i] == NULL)
            {
                PostError(MEM_ALLOC_FAILED);
                return;
            }
            memcpy(ipapCompAllInv[i],ipapAllInv[i],
                    nCompInvSizes[i]*sizeof(ItemPtr));
        }
        else
            ipapCompAllInv[i] = NULL;
    }

}

//***************************************************************************
//FreeDynamicTACAssumptionPopulation
//********** Description: **************
//delete "sample" population used for Strategy possibility checks
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------
void BBECALL FreeDynamicTACAssumptionPopulation()
{
    unsigned short i;
    if(pbspCompPopulation != NULL)
        GlobalFree((HGLOBAL)pbspCompPopulation);
    pbspCompPopulation = NULL;

    if(nCompPopSize != 0 && ipapCompAllInv != NULL && nCompInvSizes != NULL)
    {
        for(i = 0; i < nCompPopSize; i++)
            if(ipapCompAllInv[i] != NULL)
            {
                GlobalFree((HGLOBAL)ipapCompAllInv[i]);
                ipapCompAllInv[i] = NULL;
            }

        GlobalFree((HGLOBAL)ipapCompAllInv);
        ipapCompAllInv = NULL;
        GlobalFree((HGLOBAL)nCompInvSizes);
        nCompInvSizes = NULL;
    }
    nCompPopSize = 0;
}

//***************************************************************************
//SetSoloAssumptionMode
//********** Description: **************
//set SoloAssumptionMode parameter (whether to check the current population
//on Strategy possibility checks)
//********** Parameters:  **************
//BOOL bNewSoloAssumptionMode - new value of SoloAssumptionMode
//-----------------------------------------------------------------------
void BBECALL SetSoloAssumptionMode(BOOL bNewSoloAssumptionMode)
{ SoloAssumptionMode = bNewSoloAssumptionMode; }

//***************************************************************************
//SetSoloActionMode
//********** Description: **************
//set ActionsSoloMode parameter (whether to perform checks on the entire population)
//********** Parameters:  **************
//BOOL bNewActionsSoloMode - new value of ActionsSoloMode
//-----------------------------------------------------------------------
void BBECALL SetSoloActionMode(BOOL bNewActionsSoloMode)
{ ActionsSoloMode = bNewActionsSoloMode; }

//***************************************************************************
//SetSuddenChangeUse
//********** Description: **************
//set UseSuddenChange parameter (whether to allow sudden random changes in behavior)
//********** Parameters:  **************
//BOOL bNewUseSuddenChange - new value of UseSuddenChange
//-----------------------------------------------------------------------
void BBECALL SetSuddenChangeUse(BOOL bNewUseSuddenChange)
{ UseSuddenChange = bNewUseSuddenChange; }

//***************************************************************************
//SetMaxFailures
//********** Description: **************
//set maximum number of consequent failures of the same Action before aborting
//the Strategy
//********** Parameters:  **************
//unsigned short nNewMaxFailures - new maximum failures
//-----------------------------------------------------------------------
void BBECALL SetMaxFailures(unsigned short nNewMaxFailures)
{ nMaxFailures = nNewMaxFailures ; }

//***************************************************************************
//SetHistoryUse
//********** Description: **************
//set UseHistory parameter (whether to use learning / history feature)
//********** Parameters:  **************
//unsigned short nNewUseHistory - new UseHistory value
//-----------------------------------------------------------------------
void BBECALL SetHistoryUse(BOOL bNewUseHistory)
{ UseHistory = bNewUseHistory ; }

//***************************************************************************
//GetLastThought
//********** Description: **************
//return last verbose message
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------
char *BBECALL GetLastThought()
{ return &(LastThought[0]); }

//***************************************************************************
//Agent2CorePtrArray
//********** Description: **************
//fill the given array of Core pointers with pointers extracted from the given
//array of Agent pointers
//********** Parameters:  **************
//Agent **alpArr      - array of Agent pointers
//Core ***cppArr      - array of Core pointers to fill
//unsigned short nLen - length of alpArr array
//-----------------------------------------------------------------------
void BBECALL Agent2CorePtrArray(Agent **alpArr,Core ***cppArr,
    unsigned short nLen)
{
    unsigned short i;
    for(i = 0; i < nLen; i++)
        if(alpArr[i] != NULL)
            (*cppArr)[i] = alpArr[i]->GetCoreAddress();
}

//***************************************************************************
//ClearHistory
//********** Description: **************
//delete all events in the global default EventsRegistry History object
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------
void BBECALL ClearHistory()
{
    History.Clear();
}

//***************************************************************************
//GetHistoryAddress
//********** Description: **************
//return pointer to the global default EventsRegistry History object
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------
EventsRegistry *BBECALL GetHistoryAddress()
{
    return &History;
}

//***************************************************************************
//SetGoalItemAcquisitionID
//********** Description: **************
//set new item acquisition node
//********** Parameters:  **************
//ID idNewAcquisitionGoal - new item acquisition node ID
//-----------------------------------------------------------------------
void BBECALL SetGoalItemAcquisitionID(ID idNewAcquisitionGoal)
{
    gItemAcquisition = idNewAcquisitionGoal;
}

//-----------------------------------------------------------------------

